feat(desktop): run ROM files in a window with keypad input

Add --rom PATH --window with optional bounded --frames execution. Capture from BIOS boot, pump native events between bounded machine slices, retain frames during STOP, and sample keypad input for wake-up. Share final machine reports with terminal mode.

Add an original file-exportable input ROM, controller and CLI regressions, and opt-in native window tests. Document controls, frame limits, STOP behavior, and compatibility limits. Verified on Darwin arm64: debug/release workspace and core tests, Clippy, rustdoc, native ROM diagnostics, all ten demo smoke tests, and a 60-frame release ROM run.
This commit is contained in:
0xrsydn 2026-10-05 19:12:55 +07:00
commit 109c1ffe81
19 changed files with 919 additions and 57 deletions

View file

@ -39,7 +39,7 @@ The repository is a Cargo workspace with three crates:
| `crates/gba-demos/src/timer_demo.rs` | Original timer-configuration program and IRQ handler |
| `Cargo.toml` | Workspace members and shared package settings |
| `crates/gba-core/src/lib.rs` | Core modules |
| `crates/gba-demos/src/lib.rs` | Demo modules and original instruction/exception demo bytes |
| `crates/gba-demos/src/lib.rs` | Demo modules, original instruction/exception bytes, and raw input-test ROM |
| `crates/gba-core/src/bios.rs` | Original ARM BIOS image builder, minimal boot, IRQ dispatch, waits, and memory services |
| `crates/gba-core/src/bios/reset.rs` | Emitted ARM SoftReset and selective RegisterRamReset; CPU banks, RAM clearing, and supported device-register resets |
| `crates/gba-core/src/bios/arithmetic.rs` | Emitted ARM division and integer-square-root routines |
@ -79,9 +79,13 @@ The repository is a Cargo workspace with three crates:
| `crates/desktop/src/desktop/mosaic.rs` | Mosaic window and 128-frame independent pixel checks |
| `crates/desktop/src/desktop/raster.rs` | Raster window and scanline color-band smoke test |
| `crates/desktop/src/main.rs` | Command-line modes, error exit status, and fixed-length CPU demo |
| `crates/desktop/src/cartridge.rs` | Read-only ROM files, argument checks, bounded machine execution, and terminal reports |
| `crates/desktop/src/cartridge.rs` | Read-only ROM files, mode/limit checks, bounded terminal execution, and shared state reports |
| `crates/desktop/src/cartridge/window.rs` | ROM-window session, bounded slices, capture, STOP/input handling, and progress limits |
| `crates/desktop/src/cartridge/window/tests.rs` | Original ROM pixels, keyboard mapping, STOP wake, HALT, frame budgets, and diagnostics |
| `crates/desktop/src/desktop/rom.rs` | Main-thread ROM window, input polling, presentation pacing, and frame-limited exit |
| `crates/desktop/examples/write_rom_demo.rs` | Non-overwriting export of the original input-test ROM |
| `crates/desktop/src/cartridge/tests.rs` | ROM options, bounded readers, instruction/DMA/IRQ budgets, HALT/STOP, and diagnostic reports |
| `crates/desktop/tests/rom_cli.rs` | Process-level loading and exit-status checks using temporary original programs |
| `crates/desktop/tests/rom_cli.rs` | File loading and exit-status checks; opt-in native ROM-window integration test |
| `crates/gba-core/tests/core.rs` | CPU integration tests and the complete demo |
| `crates/gba-core/tests/memory.rs` | Memory widths, alignment, errors, and mirrors |
| `crates/gba-core/tests/exceptions.rs` | Exception demo, BIOS mapping, and reset-vector execution |

View file

@ -17,12 +17,13 @@ Verified locally on macOS 15.7.3:
- All ten native window smoke tests pass. The mosaic test submits 128 frames; the other nine submit 60 frames each.
- The eight CPU-driven smoke tests check CPU state, every output pixel, and presentation at VBlank entry.
- The terminal-only CPU and timer IRQ demos run successfully.
- The bounded ROM runner loads original raw programs and reports state without opening a window.
- The bounded terminal ROM runner loads original raw programs and reports state without opening a window.
- The native ROM-window test presents a file-backed original program and reports STOP, CPU, and video failures.
These results cover the CPU core, memory, Mode 0–5 snapshots and row capture, input mapping, and desktop windows.
They do not verify a complete GBA display controller or audio.
Arrow-key movement in the original host display test was confirmed manually on this Mac.
The new CPU graphics demo has automated input-mapping tests; its physical keyboard behavior still needs manual confirmation.
CPU demos and ROM windows have automated input-mapping tests; their physical keyboard behavior still needs manual confirmation.
Enable the direnv hook in your shell if it is not already configured.
For zsh, add this line to `~/.zshrc`:

View file

@ -26,7 +26,7 @@ Minimal boot initializes:
It then enters `0x08000000` in ARM System mode with IRQ/FIQ masks clear.
It does not reproduce Nintendo's logo, cartridge-header checks, RAM clearing, boot delays, or full hardware initialization.
The executable can load raw ROM files for [bounded terminal diagnostics](cartridge.md) using this boot sequence.
The executable can load raw ROM files for [terminal or window execution](cartridge.md) using this boot sequence.
This does not establish game compatibility or complete BIOS behavior.
Supported software interrupt services:

View file

@ -1,14 +1,17 @@
# Cartridge files and terminal execution
# Cartridge files and execution
The desktop executable can load raw ROM bytes and run a bounded diagnostic session.
The desktop executable can load raw ROM bytes for terminal diagnostics or window execution.
This supports original test programs. It does not establish commercial-game compatibility.
```sh
direnv exec . cargo run --locked --release -- --rom path/to/original.gba --steps 100000
direnv exec . cargo run --locked --release -- --rom path/to/original.gba --window
```
Both options are required. Their order does not matter.
`COUNT` must contain decimal digits and be between 1 and 100,000,000 inclusive.
Supply `--rom PATH` and exactly one execution mode: `--steps COUNT` or `--window`.
Option order does not matter. Terminal step counts accept decimal digits from 1 through 100,000,000.
Window mode optionally accepts `--frames COUNT`, from 1 through 100,000, for bounded presentation tests.
Reject zero, signed numbers, duplicate options, and combinations of terminal and window limits.
Do not combine ROM mode with demo or help options. Bare file paths are not accepted.
Quote paths that contain spaces. File extensions do not affect loading.
@ -31,12 +34,12 @@ Reads past supplied cartridge bytes remain explicit memory errors, not open-bus
All three Game Pak ROM windows use the same supplied bytes and existing wait-state rules.
Cartridge writes, save devices, and cartridge peripherals remain unsupported.
The runner calls `bios::boot` with the loaded bytes.
Both modes call `bios::boot` with the loaded bytes.
The [original BIOS replacement](bios.md) executes its minimal boot before entering `0x08000000` in ARM System mode.
There is no command-line option for external BIOS files.
Unsupported services remain diagnostics. In particular, RegisterRamReset sound/serial flags, including `r0=0xff`, still fail.
## Execution limits and results
## Terminal execution limits and results
The budget counts successful machine steps, including BIOS boot instructions.
Each CPU instruction, IRQ entry, DMA unit, or HALT idle batch consumes one step.
@ -58,7 +61,67 @@ It does not mean the program completed or passed a hardware test.
Invalid arguments, loading failures, output failures, and emulation diagnostics produce a nonzero exit status.
For emulation diagnostics, stdout contains the final machine state and stderr contains the core error.
ROM mode has no window, keyboard input, audio, saves, per-step trace, or real-time pacing.
Terminal mode has no window, keyboard input, or real-time pacing.
It does not enable frame capture or validate rendered output.
Use the existing demos for window and input tests.
## Window execution
`--window` opens a native 240×160 window at 4× scale on the main thread.
The window presents [scanline-captured frames](display-timing.md#scanline-frame-capture) at VBlank entry.
Capture starts before BIOS boot. It has no demo-specific readiness checks and does not suppress startup images.
A black buffer appears before the first captured frame.
The host does not set display registers, initialize game RAM, or draw replacement game graphics.
Controls use the same focus correction and key mapping as the existing demos:
| Host key | GBA button |
| --- | --- |
| Arrows | D-pad |
| Z / X | A / B |
| Q / W | L / R |
| Enter | Start |
| Backspace | Select |
Escape or the window close control ends execution. Enter does not reset the emulator.
Focus loss releases all buttons but does not pause execution.
The host samples buttons before each execution slice, including while stopped.
Each slice executes at most 4,096 machine steps or stops at the next VBlank event.
The host processes window events between slices, not only when a frame completes.
A separate 400,000-step limit guards progress between VBlank events and persists across slices and STOP waits.
This limit permits a full frame of one-cycle instructions. It is not a wall-clock deadline.
HALT continues device clocks and frame publication.
Normal presentation targets approximately 59.73 frames per second, without frame skipping or accumulated catch-up work.
Slow hosts can run slower than real time. Host sleeping never advances emulated clocks.
In interactive mode, STOP retains the last image and changes the window title.
The host continues polling input and window-close events while GBA clocks remain frozen.
Only keypad conditions configured by the ROM through KEYCNT and IE can wake STOP.
A key press cannot wake a program that has not enabled a matching wake source.
`--frames COUNT` exits successfully after that many captured frames are submitted to the window.
It is a presentation limit, not proof that the ROM completed or passed a test.
STOP before that limit is an error instead of an indefinite wait for physical input.
Early window closure is also an error in this bounded mode; closure is normal in interactive mode.
CPU, DMA, video, and window errors end the session with a nonzero exit status.
The terminal report includes captured-frame count and the final machine state; stderr contains the error.
Rendering errors do not overwrite the last complete framebuffer.
Reports count captured frames, not idle redraws or window-event updates.
## Try an original ROM
Generate an original headerless input-test file, then open that file through the ROM loader:
```sh
direnv exec . cargo run --locked --example write_rom_demo -- /tmp/gba-input-demo.gba
direnv exec . cargo run --locked --release -- --rom /tmp/gba-input-demo.gba --window
```
The generator refuses to overwrite an existing file. Choose a new path if needed.
The CPU writes the palette: blue at rest, red while Z is held, and green while Right is held.
Right takes priority over Z. Other buttons have no visible effect in this small program.
No game or firmware content is included.
Both ROM modes lack audio, saves, and per-step tracing. Commercial-game compatibility remains unverified and unsupported.
Only load files that you may lawfully use. Do not commit game ROMs or firmware.

View file

@ -44,6 +44,7 @@ When row capture is enabled, the clock advances within those steps also render v
`Memory::set_scanline_rendering(true)` enables row capture without changing clocks or hardware registers.
Capture is disabled by default for CPU-only use. All CPU-driven demo frame runners enable it automatically.
The ROM window runner enables capture before BIOS boot and presents the first complete frame without demo-specific startup checks.
Repeatedly enabling capture preserves progress. Disabling it drops captured buffers and availability state.
For each visible line, the renderer composes the row at HBlank entry, cycle 1,006.

View file

@ -62,7 +62,9 @@ Update this file when a feature lands or a limit is removed.
- `SWP`/`SWPB`, including unaligned word rotation and register aliases.
- 256 KiB external work RAM and 32 KiB internal work RAM, including mirrors.
- Read-only cartridge bytes in the three GBA cartridge windows.
- Raw ROM-file loading for bounded terminal diagnostics, with original BIOS boot and final CPU/step reports.
- Raw ROM-file loading with original BIOS boot, terminal diagnostics, and final CPU/step reports.
- ROM windows with scanline capture, keyboard input, focus-loss release, bounded execution slices, and interactive keypad wake from STOP.
- Optional ROM-window frame limits and an original input-test ROM generator.
- Little-endian byte, halfword, and word reads/writes.
- ARM7TDMI word-load rotation, aligned stores, and odd-address halfword-load behavior.
- Explicit errors for unsupported instructions when their condition passes, and for unsupported memory accesses.
@ -103,8 +105,9 @@ Keypad IRQs use a documented polling model; hardware retrigger edges and asynchr
STOP implements keypad wake only. Serial/Game Pak wake sources, oscillator restart delays, and exact wake timing remain unimplemented or unverified.
The presentation framebuffer is separate from emulated video RAM.
All named graphics demos connect desktop input and rendering to the emulated CPU. The default display test remains host-generated.
ROM files can run in bounded terminal mode with the original BIOS replacement.
This mode has no window, input, audio, saves, or external BIOS option. It does not establish commercial-game compatibility.
ROM files can run in bounded terminal mode or a window with the original BIOS replacement.
ROM windows have no demo-specific startup checks. Input is sampled between bounded execution slices.
Audio, saves, and external BIOS loading remain unavailable. ROM loading does not establish commercial-game compatibility.
See [cartridge loading](hardware/cartridge.md) for file limits, step budgets, and exit status.
Unmapped reads return errors instead of hardware open-bus values.
@ -120,7 +123,7 @@ Writes to cartridge addresses return errors instead of modeling cartridge hardwa
3. Validate keypad retrigger behavior with hardware tests and add remaining device registers; extend BIOS reset coverage as sound/serial support becomes available.
Validate STOP entry/wake edges and add external wake sources and remaining DMA device modes.
4. Replace nominal sprite work limits with verified individual fetch timing; add background fetch timing and per-pixel composition.
5. Connect ROM execution to the window and input; expand audio, cartridge hardware, and saves before testing Emerald compatibility.
5. Validate more original/public test ROMs through the window; add audio, cartridge hardware, and saves before testing Emerald compatibility.
Keep the emulator core independent of window and audio libraries.
Treat Nintendo DS support as a separate project phase.

View file

@ -23,7 +23,16 @@ file target/release/gameboy-rust
./target/release/gameboy-rust --timer-demo
```
All ten window smoke tests need an active desktop session. Ordinary tests do not open windows.
All ten demo window smoke tests need an active desktop session. Ordinary tests do not open windows.
Run the additional file-backed ROM-window test explicitly in that desktop session:
```sh
direnv exec . cargo test --locked -p gameboy-rust-desktop --test rom_cli native_rom_window -- --ignored --test-threads=1
direnv exec . cargo test --locked --release -p gameboy-rust-desktop --test rom_cli native_rom_window -- --ignored --test-threads=1
```
This opt-in test opens native windows in child processes and checks bounded presentation and STOP/CPU/video diagnostics.
Each process has a host-side timeout. It does not require or validate physical keyboard presses.
CPU-driven smoke tests supply scripted buttons; they do not validate physical keyboard events.
To test the core and demo programs without building the window dependency:
@ -42,7 +51,10 @@ They check file-size boundaries, bounded reads, short reads, I/O errors, paths,
CLI tests cover option order, missing/conflicting options, invalid step limits, process exit status, and reports.
Execution tests cover original ARM/Thumb code, BIOS boot, DMA/IRQ step accounting, HALT budgets, and prompt STOP results.
Unsupported instructions, truncated code, and DMA errors retain the failure state for inspection.
See [cartridge execution](hardware/cartridge.md) for usage and limitations.
ROM-window session tests compare every pixel from an original CPU-written palette with expected colors.
They check keyboard-to-ROM input, focus loss, repeated STOP polling, keypad wake, HALT progress, and bounded slices.
Further checks cover frame-budget retention/reset, DMA/IRQ step accounting, video errors, and final reports.
See [cartridge execution](hardware/cartridge.md) for usage, limitations, and a generated original ROM for manual input testing.
Angle-service tests compare results with wide-integer polynomial and signed-sector references, plus floating-point accuracy checks in supported ranges.
They cover a dense ArcTan unit-interval grid, sampled full-domain and seeded inputs, axes, quadrant boundaries, signed extremes, and scale invariance.