b22934fe0d
Browser SD files are persisted in OPFS (web/sdstore.js) and edited in the Files tab. At boot the browser builds a deterministic 64 MiB FAT32 image (web/fat32.js) with nested directories and VFAT long names, then mounts it as a raw block device, bypassing QEMU's broken populated-vvfat WASM coroutine path. Guest writes mutate only the ephemeral image. Exposes a hidden #frame-generation indicator that increments on every e-ink flush, so automation can wait on real display updates instead of fixed sleeps. Adds tests/ with a Makefile runner: 'make test' for JS + FAT32/mtools checks, 'make browser-test' for a headless-Firefox boot of the official CrossPoint 1.4.1 firmware that navigates to Test/example.txt. Bumps third_party/qemu to the SPI-SD/CMD13 fixes and updates README/ROADMAP/ISSUES for the new architecture.
101 lines
4.5 KiB
Markdown
101 lines
4.5 KiB
Markdown
# xteink-web-emulator
|
||
|
||
An offline, in-browser emulator for the xteink **X3/X4** (ESP32-C3 + e-ink):
|
||
upload a firmware `.bin`, run the real machine code client-side, see the screen,
|
||
press buttons, mount a virtual SD — no server, no proprietary engine.
|
||
|
||
**Status:** native and WebAssembly builds provide one `xteink` machine that
|
||
selects X3 or X4 at launch. Unmodified
|
||
[canonical firmware](https://github.com/crosspoint-reader/crosspoint-reader)
|
||
auto-detects the selected variant: X3 boots with its UC8253 display and SD card;
|
||
X4 reaches its SSD1677 driver through a blank protocol stub. A basic static
|
||
browser UI now loads local firmware, renders the panel, and drives the physical
|
||
buttons. See [ROADMAP.md](./ROADMAP.md) for architecture and plan.
|
||
|
||
## Quick start (native QEMU)
|
||
|
||
```sh
|
||
git clone --recursive https://github.com/crosspoint-reader/crosspoint-reader ../crosspoint-reader
|
||
# Build ../crosspoint-reader's PlatformIO `default` environment first.
|
||
nix develop
|
||
make qemu # clone espressif/qemu @ pinned commit, apply patch, build
|
||
make firmware sdimage # 16 MB flash.bin + FAT32 sd.img from the PlatformIO build
|
||
make run # X3 (default)
|
||
make run VARIANT=x4 # X4 detection + blank SSD1677 stub
|
||
```
|
||
|
||
Point at a firmware build with `make firmware FIRMWARE_DIR=/path/to/.pio/build/gh_release`.
|
||
`make qemu` is a long compile on modest hardware — set `JOBS` to taste.
|
||
|
||
## WebAssembly build
|
||
|
||
Podman or Docker is required. The QEMU source is the `third_party/qemu`
|
||
submodule, so initialize it first:
|
||
|
||
```sh
|
||
git submodule update --init third_party/qemu
|
||
```
|
||
|
||
The build is limited to `riscv32-softmmu` and the X3/X4 device graph;
|
||
networking, audio, virtfs, tools, docs, and unrelated boards are disabled.
|
||
|
||
```sh
|
||
make qemu-wasm
|
||
# dist/wasm/qemu-system-riscv32.{js,wasm,worker.js} + esp32c3-rom.bin
|
||
```
|
||
|
||
The build runs Node smoke checks when Node is available.
|
||
|
||
## Browser interface
|
||
|
||
```sh
|
||
make web
|
||
# Open http://127.0.0.1:8000/web/
|
||
```
|
||
|
||
Choose a CrossPoint firmware `.bin` — either the ~5 MB release app image (it is
|
||
merged in-browser with the bundled bootloader and partition table into a 16 MB
|
||
flash) or a full 16 MB image — select X3 or X4, and boot. The controls
|
||
map to the device's ADC button ladders and active-low Power GPIO. `make web`
|
||
serves the required COOP/COEP headers for Emscripten pthreads; a generic static
|
||
server without those headers will not work.
|
||
|
||
Real firmware boots in WASM and drives the X3 panel (528×792). Browser SD files
|
||
are persisted in OPFS and edited in the Files tab. At boot, the browser builds a
|
||
64 MiB raw FAT32 image with nested-directory and VFAT long-name support; QEMU
|
||
mounts that image directly, avoiding `vvfat`. Choose **Apply & reboot** to expose
|
||
edits to the guest. Guest writes modify only the ephemeral image. The Debug tab
|
||
reports committed WASM heap, visible MEMFS data, and browser JS heap where
|
||
supported. X4 remains a white display stub.
|
||
|
||
## Contents
|
||
|
||
- `qemu/patches/0001-xteink-machine.patch` — the `xteink,variant=x3|x4` machine
|
||
and its device models (I²C fingerprints, ADC, GPIO, SPI2, X3 UC8253, X4 panel
|
||
stub, SD-over-SPI fix), applied onto the pinned espressif/qemu commit by
|
||
`scripts/build-qemu.sh`.
|
||
- `third_party/qemu` — submodule of the [xteink QEMU fork](git@ssh.gitea.va.reichard.io:evan/qemu-xteink.git)
|
||
(espressif/qemu + the machine models + the wasm32 TCG backend), built for the
|
||
browser by `scripts/build-qemu-wasm.sh`. Edit it in place; `make web`
|
||
recompiles incrementally. `qemu/wasm/Dockerfile` is the pinned Emscripten
|
||
dependency environment.
|
||
- `flake.nix` — the espressif-qemu *release* binary as the fast generic baseline
|
||
(`nix run .#run-upstream -- flash.bin`), plus a devShell with QEMU's full build
|
||
environment. `Makefile` — `qemu`, `firmware`, `sdimage`, `run`, `chip`.
|
||
- `web/fat32.js` and `web/sdstore.js` — dependency-free FAT32 image generation
|
||
and persistent OPFS file storage.
|
||
- `scripts/mksd.sh` — build a FAT32 SD backing image for native QEMU.
|
||
- `tests/` — `make test` runs JS/FAT32 checks; `make browser-test` boots the
|
||
official CrossPoint 1.4.1 firmware and navigates to `Test/example.txt` in
|
||
headless Firefox.
|
||
- `chip/eink-x3.chip.c` — the reverse-engineered X3 e-ink protocol, first written
|
||
as a Wokwi chip and now the reference for the QEMU display device.
|
||
`make -C chip test` runs its host self-check.
|
||
|
||
## Why not Wokwi
|
||
|
||
Wokwi runs any bin in a browser and we had the e-ink chip working — but its
|
||
simulation engine is proprietary and gated behind a license/service, so it can't
|
||
be self-hosted or run airgapped. QEMU is the open path; the e-ink protocol work
|
||
carried straight over.
|