diff --git a/AGENTS.md b/AGENTS.md index 047f5d2..389c0f6 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -38,6 +38,7 @@ Window smoke tests (`--*-smoke-test`) need a logged-in desktop session. Details | Pinned public memory tests and video-byte coverage limits | [docs/public-memory-tests.md](docs/public-memory-tests.md) | | ROM test suites, assertions, and JSON reports | [docs/rom-tests.md](docs/rom-tests.md) | | ROM files, window execution, and terminal diagnostics | [docs/hardware/cartridge.md](docs/hardware/cartridge.md) | +| Pinned public BIOS read-protection test and firmware limits | [docs/public-bios-tests.md](docs/public-bios-tests.md) | | BIOS services (SWI) | [docs/hardware/bios.md](docs/hardware/bios.md) | | Timers, IE/IF/IME, HALT/STOP | [docs/hardware/timers-irq.md](docs/hardware/timers-irq.md) | | DMA | [docs/hardware/dma.md](docs/hardware/dma.md) | diff --git a/README.md b/README.md index 3ea1b22..b8acc20 100644 --- a/README.md +++ b/README.md @@ -44,6 +44,7 @@ Use `--test-suite suite.json` for [repeatable headless ROM assertions](docs/rom- | [docs/public-arm-tests.md](docs/public-arm-tests.md) | Pinned public ARM test, preparation command, results, and limits | | [docs/public-thumb-tests.md](docs/public-thumb-tests.md) | Pinned public Thumb test, result protocol, and compatibility baseline | | [docs/public-memory-tests.md](docs/public-memory-tests.md) | Pinned public memory test, results, and video-byte coverage limits | +| [docs/public-bios-tests.md](docs/public-bios-tests.md) | Pinned BIOS read-protection test and firmware-specific failure | | [docs/architecture.md](docs/architecture.md) | Crate boundaries and a file-by-file map | | [docs/hardware/cpu.md](docs/hardware/cpu.md) | ARM/Thumb execution rules, modes, exceptions, and timing | | [docs/hardware/cartridge.md](docs/hardware/cartridge.md) | ROM loading, window/input controls, original ROM generator, and diagnostics | diff --git a/docs/architecture.md b/docs/architecture.md index 0ea755f..6f1337e 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -14,7 +14,7 @@ The repository is a Cargo workspace with three crates: | Path | Purpose | | --- | --- | | `flake.nix`, `flake.lock` | Pinned Rust/Python development environment | -| `tools/prepare_gba_tests.py`, `tools/gba-tests-{arm,thumb,memory}.lock.json` | Hash-verified public test source/ROM preparation and suite generation | +| `tools/prepare_gba_tests.py`, `tools/gba-tests-{arm,thumb,memory,bios}.lock.json` | Hash-verified public test source/ROM preparation and suite generation | | `tools/test_prepare_gba_tests.py` | Offline adapter tests using original synthetic data | | `.envrc` | Automatic environment loading through direnv | | `AGENTS.md` | Short orientation for coding agents: crate map, commands, and where docs live | diff --git a/docs/public-bios-tests.md b/docs/public-bios-tests.md new file mode 100644 index 0000000..20196a9 --- /dev/null +++ b/docs/public-bios-tests.md @@ -0,0 +1,74 @@ +# Pinned public BIOS read-protection test + +The headless runner executes the unmodified BIOS test ROM from [jsmolka/gba-tests](https://github.com/jsmolka/gba-tests). +It currently fails test 1 on Darwin arm64 in debug and release builds. +This ROM tests protected BIOS reads against Nintendo firmware opcode values, not the complete BIOS service set. +Our runner uses the original replacement BIOS. It neither supplies nor loads Nintendo firmware. + +## Prepare and run + +```sh +direnv exec . python3 tools/prepare_gba_tests.py /tmp/gba-public-bios --suite bios +direnv exec . cargo run --locked --release -- --test-suite /tmp/gba-public-bios/suite.json > /tmp/gba-public-bios-report.json +``` + +`bios/bios.gba` is a public test cartridge, not a BIOS image. +Use a new directory outside the repository or under ignored `roms/`. Its parent directory must exist. +The adapter checks every selected file's size and SHA-256 digest before creating output. +It retains the upstream license, source, ROM, `suite.json`, and `source-lock.json` without modifying them. +Downloaded binaries and header sources must not enter tracked files. + +Offline preparation uses the same checks: + +```sh +direnv exec . python3 tools/prepare_gba_tests.py /tmp/gba-public-bios-offline --suite bios --source-dir /path/to/gba-tests +``` + +ARM remains the default suite. The Thumb and memory selections remain unchanged. +Preparation is not a passing result. The BIOS run currently exits nonzero and writes a failed assertion report. + +## Provenance and checkpoint + +- Repository: `jsmolka/gba-tests`. +- Revision: `a7113b67e63f83a9b321696ddd7042ccfad6c881`. +- Upstream project license: MIT, copyright Julian Smolka; retained with downloaded files. +- Test ROM: `bios/bios.gba`, 1,908 bytes. +- SHA-256: `9d7b369fa1aa661ff03692b3d79c6f644b623d72983d0fc890e6d87a0409a3c9`. +- Selected file hashes: [`tools/gba-tests-bios.lock.json`](../tools/gba-tests-bios.lock.json). + +The adapter uses the upstream committed binary. No FASMARM rebuild or source-to-binary reproduction was performed on Darwin. +The source initializes r12 to zero. Each failure sets r12 to its test number and branches to `eval`. +Successful execution reaches the same label with r12 still zero. +The source exits and binary ARM branch targets were checked against `eval` at `0x08000248`. +Its first instruction is `0xe92d0003`, the shared `m_vsync` register save. +There is another `m_vsync` inside test 3 at `0x08000194`; that is not the completion checkpoint. + +The manifest requires ARM state at `0x08000248` and `r12 == 0`, within 1,000,000 successful machine steps. +The budget includes original BIOS boot. All test setup executes normally; only final result rendering is excluded. +Test 3's VBlank wait and IRQ callback are part of the test and are not bypassed. +Preparation verifies the checkpoint word in addition to whole-file hashes. +Timeouts, diagnostics, STOP, and nonzero r12 remain failures under the [normal runner rules](rom-tests.md). + +## Observed result on Darwin arm64 + +Before BIOS read protection: + +- Both builds reach `eval` after 64 successful steps and 543 nominal cycles. +- The CPU is in System/ARM state, with CPSR `0x2000001f` and r12 = 1. +- The report says `reason: assertion_failed`, with no emulator diagnostic. +- Test 1 reads address zero after boot. It expects `0xe129f000`, a Nintendo BIOS opcode. +- The emulator instead exposes the replacement BIOS reset-vector instruction at address zero. +- The later post-SWI and IRQ checks are not reached. + +This reveals a missing bus-protection rule and a separate firmware-specific expectation. +Implementing protection must not substitute Nintendo opcode constants merely to pass this ROM. +The original BIOS has a different layout, so its retained prefetch data can differ even with protection implemented. +No public BIOS pass is claimed. The ARM, Thumb, and memory suites remain separate passing results. +Exact fetch-pipeline behavior and full BIOS compatibility remain unverified. + +## References + +- [Pinned BIOS test source and expected opcodes](https://github.com/jsmolka/gba-tests/blob/a7113b67e63f83a9b321696ddd7042ccfad6c881/bios/bios.asm). +- [Shared initialization and result macros](https://github.com/jsmolka/gba-tests/blob/a7113b67e63f83a9b321696ddd7042ccfad6c881/lib/macros.inc). +- [GBATEK BIOS read protection](https://problemkaputt.de/gbatek-gba-unpredictable-things.htm). +- [Pinned license](https://github.com/jsmolka/gba-tests/blob/a7113b67e63f83a9b321696ddd7042ccfad6c881/LICENSE). diff --git a/docs/references.md b/docs/references.md index 2d89c23..adb4e6b 100644 --- a/docs/references.md +++ b/docs/references.md @@ -68,6 +68,7 @@ Hardware references used for the core: - [Pinned jsmolka Thumb entry](https://github.com/jsmolka/gba-tests/blob/a7113b67e63f83a9b321696ddd7042ccfad6c881/thumb/thumb.asm), [branch tests](https://github.com/jsmolka/gba-tests/blob/a7113b67e63f83a9b321696ddd7042ccfad6c881/thumb/branches.asm), and [memory tests](https://github.com/jsmolka/gba-tests/blob/a7113b67e63f83a9b321696ddd7042ccfad6c881/thumb/memory.asm), reviewed for r7 failure IDs, success-marker reset, the Thumb-to-ARM evaluation bridge, and empty-list transfer expectations. - [Pinned jsmolka memory entry](https://github.com/jsmolka/gba-tests/blob/a7113b67e63f83a9b321696ddd7042ccfad6c881/memory/memory.asm), [mirror tests](https://github.com/jsmolka/gba-tests/blob/a7113b67e63f83a9b321696ddd7042ccfad6c881/memory/mirrors.asm), and [video-byte tests](https://github.com/jsmolka/gba-tests/blob/a7113b67e63f83a9b321696ddd7042ccfad6c881/memory/video_strb.asm), reviewed for the r12 checkpoint, binary exit targets, limited byte-write assertions, and mode-selection caveats. - [gbadoc memory layout](https://gbadev.net/gbadoc/memory.html), consulted for VRAM mirror ranges alongside GBATEK's video-memory layout and byte-write rules. +- [Pinned jsmolka BIOS test](https://github.com/jsmolka/gba-tests/blob/a7113b67e63f83a9b321696ddd7042ccfad6c881/bios/bios.asm), reviewed for the r12 evaluation checkpoint, internal VBlank wait, and Nintendo-firmware-specific protected-read expectations after boot, SWI, and IRQ. - [armwrestler-gba-fixed](https://github.com/destoer/armwrestler-gba-fixed) and [arm7wrestler](https://github.com/Arisotura/arm7wrestler), considered as test sources; not integrated. - [SingleStepTests ARM7TDMI](https://github.com/SingleStepTests/ARM7TDMI), considered as an experimental, emulator-generated test source; not integrated. - [GBATEK ARM single data transfers](https://problemkaputt.de/gbatek-arm-opcodes-memory-single-data-transfer-ldr-str-pld.htm), for address calculation, indexing, load extension, and nominal timing. diff --git a/docs/rom-tests.md b/docs/rom-tests.md index 0635699..9d3d076 100644 --- a/docs/rom-tests.md +++ b/docs/rom-tests.md @@ -136,7 +136,8 @@ The first public integration is [jsmolka/gba-tests ARM](public-arm-tests.md), wi The pinned ARM ROM now passes this checkpoint on Darwin arm64. Historical failures remain documented. The [pinned Thumb ROM](public-thumb-tests.md) also passes its verified r7 checkpoint after the empty-list store correction. The [pinned memory ROM](public-memory-tests.md) also passes, without a core change and with explicit coverage limits. -Next, integrate a pinned public BIOS suite with a verified result protocol. +The [pinned BIOS read-protection ROM](public-bios-tests.md) reaches its checkpoint but fails test 1 with the original replacement BIOS. +Its expected values depend on Nintendo firmware. Record that failure rather than changing the assertion or returning guessed constants. For further public ARM7TDMI/GBA tests, inspect their source, license, entry assumptions, and result protocol. Pin source revisions and build instructions before comparing emulator changes. Adapt suites to verified completion addresses and result locations; do not guess them or treat timeouts as passes. diff --git a/docs/status.md b/docs/status.md index 05a5889..e00d04d 100644 --- a/docs/status.md +++ b/docs/status.md @@ -69,6 +69,7 @@ Update this file when a feature lands or a limit is removed. - Headless JSON ROM suites with per-case checkpoints, register/CPSR/memory assertions, bounded budgets, and structured failure reports. - Hash-pinned preparation of public `jsmolka/gba-tests` ARM, Thumb, and memory ROMs, with verified result-register checkpoints. - These pinned ROMs pass their checkpoints on Darwin arm64 in debug and release builds; coverage limits remain documented. +- A pinned public BIOS read-protection ROM has a verified checkpoint and an explicit failing baseline with the original BIOS. - ARM test/compare Rd=15 status restoration, without a PC result write or nominal refill. - ARM unused-memory data reads return a PC+8 word snapshot, with normal byte lanes and load rotation. - Little-endian byte, halfword, and word reads/writes. @@ -119,6 +120,7 @@ The [ROM suite runner](rom-tests.md) has original regressions and a [pinned publ The pinned public ARM ROM passes its result checkpoint in debug and release builds. The [pinned public Thumb ROM](public-thumb-tests.md) also passes after correcting its empty-list store PC value. The [pinned public memory ROM](public-memory-tests.md) passes without a core change; its video-byte assertions have documented limits. +The [pinned BIOS read-protection ROM](public-bios-tests.md) fails test 1; it expects Nintendo firmware opcode values. Other public suites remain unverified. These results do not establish full CPU or memory compatibility. The runner requires known completion addresses and has no debug-port protocol, scripted input, or rendered-image assertions. @@ -132,7 +134,7 @@ Writes to cartridge addresses return errors instead of modeling cartridge hardwa ## Next steps -1. Add a pinned public BIOS suite with a verified result protocol, then address its first compatibility failure. +1. Add BIOS read protection based on supplied firmware instructions, not hard-coded values from the public BIOS test. Extend open-bus support with verified Thumb region/alignment rules and bus history, rather than a blanket unmapped-read fallback. 2. Refine nominal timing with a fetch pipeline, Game Pak prefetch, per-access device updates, and verified timer/IRQ delays. 3. Validate keypad retrigger behavior with hardware tests and add remaining device registers; extend BIOS reset coverage as sound/serial support becomes available. diff --git a/docs/testing.md b/docs/testing.md index b86edeb..dbc4cd8 100644 --- a/docs/testing.md +++ b/docs/testing.md @@ -52,6 +52,7 @@ Additional tests cover DMA/IRQ accounting, HALT/STOP, diagnostic state, case iso These original tests validate the runner. The [public ARM adapter](public-arm-tests.md) provides a separate passing checkpoint result for its pinned ROM. The [public Thumb adapter](public-thumb-tests.md) uses a separate lock and r7 assertion after its return to ARM state. The [public memory adapter](public-memory-tests.md) uses its own lock and an ARM-state r12 checkpoint. +The [public BIOS adapter](public-bios-tests.md) also uses an ARM-state r12 checkpoint, but currently reports test 1 as failed. Preparation tests use only synthetic original bytes and no network: ```sh @@ -59,7 +60,7 @@ direnv exec . python3 -B -m unittest discover -s tools -p 'test_*.py' -v ``` They verify suite selection, pinned URLs, hashes, sizes, checkpoint validation, offline copies, destination safety, and non-overwriting output. -Thumb cases also verify the return-to-ARM bridge. Command-line tests cover default ARM selection, explicit Thumb/memory selection, and errors. +Thumb cases also verify the return-to-ARM bridge. Command-line tests cover default ARM selection, explicit Thumb/memory/BIOS selection, and errors. Compare/status CPU regressions verify saved flags/masks, bank switching, sequential PC/timing, User/System fallback, and atomic diagnostics. Load-alias regressions cover widths, signed/unaligned values, immediate/register offsets, pre/post-indexing, every non-PC register bank, and address wrapping. They verify loaded-value precedence, preserved flags, unchanged stores, nominal timing, I/O reads before device progress, deferred IRQ delivery, and atomic errors. diff --git a/tools/gba-tests-bios.lock.json b/tools/gba-tests-bios.lock.json new file mode 100644 index 0000000..56f4169 --- /dev/null +++ b/tools/gba-tests-bios.lock.json @@ -0,0 +1,46 @@ +{ + "repository": "jsmolka/gba-tests", + "revision": "a7113b67e63f83a9b321696ddd7042ccfad6c881", + "license": "MIT", + "rom": "bios/bios.gba", + "completion_pc": "0x08000248", + "result_register": 12, + "files": { + "LICENSE": { + "sha256": "b59a9ed235d76752c977f04116bc72155a14af48176bdbf50d7e5c29fca35aa7", + "bytes": 1070 + }, + "README.md": { + "sha256": "488f50d9d9031c88cea4161fad69f6460209dd54d965645fa45cf5e972cc9062", + "bytes": 434 + }, + "bios/bios.asm": { + "sha256": "aebc912f7634aca29d926bef61858d59c370a4d96728e53b4cc63d60641dd118", + "bytes": 2211 + }, + "bios/bios.gba": { + "sha256": "9d7b369fa1aa661ff03692b3d79c6f644b623d72983d0fc890e6d87a0409a3c9", + "bytes": 1908 + }, + "lib/constants.inc": { + "sha256": "b4dd3f4760ac1ae7e99cdc7e255f8220eed666e1ab821b150561db58e5d3b7ea", + "bytes": 900 + }, + "lib/macros.inc": { + "sha256": "25b0ff553c4ca99d76ec82c8c9db874e25d6eed58e2e69fa27f1a0fe7a8c46d4", + "bytes": 2864 + }, + "lib/header.asm": { + "sha256": "e9d1e8c7b6382eeb1b7059423ec365e32911f702eadfa51804a2119438a87357", + "bytes": 1720 + }, + "lib/text.asm": { + "sha256": "77e579d6cf0db182fd24e8d420c8c0a81592d78197e8fb06accc8bcfb43db17e", + "bytes": 2288 + }, + "lib/glyphs.asm": { + "sha256": "538beb37f40b1fd56beed9253e0e9eab25444c748adba7440a2093f49e3cbcc9", + "bytes": 2545 + } + } +} diff --git a/tools/prepare_gba_tests.py b/tools/prepare_gba_tests.py index 9672c61..6933b77 100644 --- a/tools/prepare_gba_tests.py +++ b/tools/prepare_gba_tests.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -"""Prepare a pinned public ARM, Thumb, or memory test outside tracked paths. +"""Prepare a pinned public ARM, Thumb, memory, or BIOS test outside tracked paths. This adapter is original. Upstream sources, license, and ROM bytes are downloaded only to the requested local directory. They are not embedded in this repository. @@ -15,7 +15,7 @@ from urllib.request import Request, urlopen ROOT = Path(__file__).resolve().parent.parent LOCKS = {suite: Path(__file__).with_name(f"gba-tests-{suite}.lock.json") - for suite in ("arm", "thumb", "memory")} + for suite in ("arm", "thumb", "memory", "bios")} MAX_FILE_BYTES = 1024 * 1024 @@ -64,7 +64,7 @@ def prepare(output, source=None, download=fetch, suite="arm"): files[name] = data # The source-defined eval label starts with m_vsync's register save. All - # failure exits set the result register (ARM/memory r12, Thumb r7). + # failure exits set the result register (ARM/memory/BIOS r12, Thumb r7). # Thumb tests switch back to ARM before eval. Success leaves the result zero. # Hash pinning is authoritative; these checks catch adapter-offset mistakes. pc = int(lock["completion_pc"], 16) diff --git a/tools/test_prepare_gba_tests.py b/tools/test_prepare_gba_tests.py index 4c8bbb8..ac39afa 100644 --- a/tools/test_prepare_gba_tests.py +++ b/tools/test_prepare_gba_tests.py @@ -26,9 +26,12 @@ class PreparationTests(unittest.TestCase): elif self.suite == "thumb": data = struct.pack("