Compare commits
4 Commits
d1d924c408
...
6d68a87e4e
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6d68a87e4e | ||
|
|
edb8ed78f3 | ||
|
|
1ad03d5692 | ||
|
|
421c9f4567 |
43
README.md
43
README.md
@@ -30,8 +30,11 @@ To run the newer sidecar protocol and gate/queue analysis tools:
|
||||
|
||||
```powershell
|
||||
.\.venv\Scripts\python.exe h8536_serial_gate.py build\rom_decompiled.json --out build\rom_serial_gate.txt
|
||||
.\.venv\Scripts\python.exe h8536_rx_branch_trace.py build\rom_decompiled.json --out build\rom_rx_branch_trace.txt
|
||||
.\.venv\Scripts\python.exe h8536_report_source_trace.py build\rom_decompiled.json --out build\rom_report_sources.txt
|
||||
.\.venv\Scripts\python.exe h8536_table_xrefs.py --out build\rom_table_xrefs.txt
|
||||
.\.venv\Scripts\python.exe h8536_ccu_seed_hints.py build\rom_decompiled.json --out build\rom_ccu_seed_hints.txt
|
||||
.\.venv\Scripts\python.exe h8536_eeprom_layout.py build\rom_decompiled.json --out build\rom_eeprom_layout.txt
|
||||
.\.venv\Scripts\python.exe h8536_consistency.py build\rom_decompiled.json --out build\rom_consistency.txt
|
||||
.\.venv\Scripts\python.exe h8536_protocol_capture.py ROM\rcp-txd-idle-only.txt
|
||||
```
|
||||
@@ -47,7 +50,11 @@ To start the current emulator harness:
|
||||
.\.venv\Scripts\python.exe scripts\serial_ack_probe.py --ack-frame "05 00 40 00 00 1F"
|
||||
.\.venv\Scripts\python.exe scripts\serial_scenario.py scenarios\ack-race-000-001.json --log captures\ack-race-000-001.txt --result-json captures\ack-race-000-001-result.json
|
||||
.\.venv\Scripts\python.exe scripts\serial_scenario.py scenarios\table-sweep-ack-000-07f.json --log captures\table-sweep-ack-000-07f.txt --result-json captures\table-sweep-ack-000-07f-result.json
|
||||
.\.venv\Scripts\python.exe scripts\state_map_runner.py --preset ok --prompt-screen
|
||||
.\.venv\Scripts\python.exe scripts\state_map_runner.py --analyze-log captures\ack-race-000-001.txt
|
||||
.\.venv\Scripts\python.exe h8536_emulator_state_search.py --preset connect-queue --target ok --first-hit --json-out build\connect-state-search-ok.json
|
||||
.\.venv\Scripts\python.exe h8536_emulator_bench_replay.py captures\bench-connect-lcd-sequence-20260525-214411.txt --assert-bench-parity
|
||||
.\.venv\Scripts\python.exe h8536_emulator.py --max-steps 250000 --p9-fast-path --eeprom-seed blank --eeprom-save build\emulator-eeprom-boot.bin --eeprom-report build\emulator-eeprom-boot.txt --eeprom-report-json build\emulator-eeprom-boot.json
|
||||
```
|
||||
|
||||
The real-device bench helper uses `pyserial`; install repo dependencies with `.\.venv\Scripts\python.exe -m pip install -r requirements.txt` if needed.
|
||||
@@ -74,8 +81,11 @@ The real-device bench helper uses `pyserial`; install repo dependencies with `.\
|
||||
- Decodes observed serial byte captures into six-byte frames, validates checksums, labels capture-observed heartbeat/call/camera-power candidates, and summarizes heartbeat cadence.
|
||||
- Accepts both analyzer-style lines such as `RX 006 bytes ...` and the idle reference `frame 006 ...` format in `ROM/rcp-txd-idle-only.txt`.
|
||||
- Reconstructs the autonomous serial gate/queue state-machine around `loc_3FD3`, `loc_BAF2`, `F9B0/F9B5`, `FAA2/FAA3/FAA5`, the `F9C4`/FRT2 idle heartbeat gate at `loc_4046`, and the resend path through `BE9E/BED5`.
|
||||
- Emits a focused SCI1 RX branch trace covering RXI/ERI byte capture, six-byte checksum validation, selector decode, the `FAA2 == 0` initial dispatcher, the `FAA2 != 0` continuation dispatcher, command `0x00/0x01/0x02/0x04/0x05/0x06/0x07` handlers, table surfaces, retry/error echoes, the separate `BE70/F970` selector-processing and `3E54/F870` serial-report queues, the TXI/RXI continuation-collapse interlock, RX-to-TX feedback loops, and session-timeout side effects.
|
||||
- Traces direct callers to `loc_3E54` to identify report queue sources and conservatively flags whether observed report indexes such as `0x0007` are ROM-proven constants or runtime/capture observations.
|
||||
- Generates table/index cross-reference reports for candidate value/current/secondary/flag tables and LCD text correlations.
|
||||
- Mines ROM-backed CCU seed hints from table xrefs, selector dispatch, LCD text terms, and observed report overlays, then proposes syntactically valid command-0 seed frames and command-1 readback frames for high-value selectors.
|
||||
- Mines the ROM-backed X24164 EEPROM layout, including the factory F400-F4FF shadow defaults, the page-0 EEPROM signature/options header, the fifteen blank-by-default 8-byte record slots loaded into F7B8-F82F, and the serial selector-to-persistent-offset map used by command 0/4 handlers.
|
||||
- Adds a Sony RCP-TX7 board profile that ties H8/536 pin 66 `P95/TXD` and pin 67 `P96/RXD` to the MAX202 RS232 transceiver.
|
||||
- Flags/manual-annotates TEMP-register access ordering for FRT and A/D 16-bit peripheral registers.
|
||||
- Scans unreached ROM ranges for ASCII strings and pointer-table candidates.
|
||||
@@ -89,13 +99,15 @@ The real-device bench helper uses `pyserial`; install repo dependencies with `.\
|
||||
- Handles the E-clock transfer instructions `MOVFPE` and `MOVTPE`.
|
||||
- Recognizes likely LCD E-clock access routines at `H'F200`/`H'F201`, including busy-flag polling and data/control writes.
|
||||
- Generates a separate C-like pseudocode view from the JSON, preserving labels, calls, branches, register names, inferred symbols, metadata comments, optional cycle notes, and simple structured `if`/`do while` patterns.
|
||||
- Provides an early H8/536 emulator harness with ROM/RAM/register memory mapping, reset-vector boot, SCI1 transmit capture, MOV condition-code updates, `SCB/F`, stack/call/return support, indirect `JMP/JSR @Rn` dispatch, scaffolded SCI1 RXI/ERI/TXI and interval timer scheduling, manual-derived FRT1/FRT2 OCIA cycle scheduling, a P9 bit-banged bus model, an X24164 two-wire EEPROM model on traced `P91/SCL` and `P97/SDA`, a 16x4 LCD bus/DDRAM model for `H'F200`/`H'F201`, and an opt-in P9 transfer fast path.
|
||||
- Provides an early H8/536 emulator harness with ROM/RAM/register memory mapping, reset-vector boot, SCI1 transmit capture, MOV condition-code updates, `SCB/F`, stack/call/return support, indirect `JMP/JSR @Rn` dispatch, scaffolded SCI1 RXI/ERI/TXI and interval timer scheduling, manual-derived FRT1/FRT2 OCIA cycle scheduling, a P9 bit-banged bus model, an X24164 two-wire EEPROM model on traced `P91/SCL` and `P97/SDA`, logical EEPROM image load/save/reporting, a 16x4 LCD bus/DDRAM model for `H'F200`/`H'F201`, and an opt-in P9 transfer fast path.
|
||||
- Includes an emulator probe that reports hot PCs, recent P9/SCI accesses, serial report queue/gate traces, RAM lifecycle watches, final SCI1/TXI state, and captured P9 byte candidates while running the real ROM.
|
||||
- Includes an RX command probe that boots until SCI1 RXI is serviceable, injects host six-byte frames through RDR/RDRF, can optionally schedule 38400 8N1 byte arrivals at real UART spacing, listens for device TX frames, and reports serial latch/table/LCD-buffer and emulated-LCD effects.
|
||||
- Includes a bench helper for replaying the emulator-derived CONNECT LCD frame sequence against the real device through COM5, with optional COM6 relay power cycling and timestamped capture logs.
|
||||
- Includes a bench ACK probe that reproduces the `01 00 00...` -> `01 00 01...` visible retry burst, waits for `07 80 40 20 90 2D`, then sends a candidate command-5 ACK and reports whether the target keeps repeating.
|
||||
- Includes a checksum-resynchronizing bench receiver that scans RX byte streams for valid six-byte frames, avoids common shifted-heartbeat false locks, and can fall back to the old fixed six-byte slicer with `--sync fixed`.
|
||||
- Includes a JSON scenario bench runner for repeatable multi-step serial tests, including low-latency ACK-aware command-1 probes that can send the current command-5 ACK candidate immediately after the retry frame appears, with explicit max-ACK/max-target guardrails.
|
||||
- Includes a PT2 state-map-aware bench runner/analyzer for the current CONNECT gate proof: it hunts a fresh device `07...` visible-drain token candidate, sends exactly one selector-zero command-4 force, probes `E000[0]` with command 1, optionally uses command 7 to recover a hidden finalized response, and labels likely token-destroying turns.
|
||||
- Includes a bounded emulator CONNECT state-search tool that patches small ROM-derived RAM/table surfaces, runs either the direct CONNECT branch or the selector-zero queue dispatch path, and classifies LCD outcomes as OK, DXC, NOT ACT, or other.
|
||||
- Includes a bench-log replay harness that feeds recorded host TX frames back into the ROM emulator with bench-style UART byte timing by default and asserts parity against the real device's observed response/LCD state.
|
||||
|
||||
Current serial observations:
|
||||
@@ -110,9 +122,13 @@ Current serial observations:
|
||||
- Emulator LCD finding: the ROM writes the boot/no-active-session message to the LCD bus as ` CONNECT:NOT ACT` on line 0 by the time SCI1 RX is serviceable. Valid and invalid six-byte host frames leave that display active while normal serial replies/heartbeats continue.
|
||||
- Board/P9 finding: traced MCU pin 62 `P91` reaches X24164 pin 6 `SCL`, and MCU pin 68 `P97` reaches the shared X24164 pin 5 `SDA` node. The emulator now treats the ROM's `C121/C08B/C0DB/C10C/C142` P9 routines as an X24164-style two-wire EEPROM bus, with ROM logical addresses `0x000-0x7FF` on the `H'A0/H'A1` control-byte family and `0x800-0xFFF` on `H'E0/H'E1`.
|
||||
- EEPROM role finding: `loc_40BB` checks `P7DR.7` and the `F402 == H'6B6F` signature before defaulting EEPROM/shadow tables; `loc_4103` writes ROM default words through `BFE0`, `loc_41D2` reads sixteen 8-byte records into `F7B0-F82F`, and the command-4 path at `BD2B-BD5F` can persist serial table writes when `F76E.7` is set.
|
||||
- EEPROM layout finding: `build\rom_eeprom_layout.txt` currently identifies the ROM factory table at `H'C964-H'CA63`, the F400 shadow defaults, page 0 offset `0x000-0x007` as the signature/options header (`00 00 6B 6F FE 00 00 00`), pages 1-F offset `0x00-0x07` as blank-by-default record slots, and 89 selector mappings from the `H'C564` table into F400/EEPROM offsets. `F404` defaults to `H'FE00` and is tested as option/feature bits, while `F76E` combines persistence enable, dispatch suppression, and low-nibble EEPROM page selection.
|
||||
- Emulator EEPROM-image finding: `build\emulator-eeprom-boot.txt` captures a blank-EEPROM boot defaulting pass. The ROM writes 2108 words, leaves page 0's signature/options header intact, blanks page 1-F record headers, and the final image matches the ROM factory/default baseline. Use `--eeprom-load`/`--eeprom-save` to persist an emulated EEPROM image across runs and compare command-induced changes.
|
||||
- Emulator board-state finding: P7 now reads external pin state for input bits, so the DIP-off default is modeled as `--p7-input 0xFF`; `--eeprom-seed factory` can pre-seed the X24164 devices and `F400-F4FF` shadow from the ROM default table for already-initialized-state experiments.
|
||||
- RX probe finding: the `--preset connect-lcd` sequence is sensitive to injection timing and modeled external state. With timed UART injection, the emulator can still reach `CONNECT: OK`/`02 00 02 00 00 5A`, while the real bench remains at `CONNECT NOT ACT`; this points to missing session/P9/external-panel context rather than a simple checksum or UART-spacing issue.
|
||||
- Emulator state-search finding: the minimum ROM-visible OK display condition is now reproducible without serial. Direct entry at `loc_2CB9` with `E000[0]=0x8080` and unsuppressed `F730=0` reaches `CONNECT: OK`; the queued selector-zero path also reaches OK when `F970[0]=0`, `F9B9=0`, `F9B4=1`, `E000[0]=0x8080`, and `F730=0`. This makes the bench problem sharper: prove whether serial can retain `E000[0]=0x8080` and enqueue selector zero without the reset/clobber path clearing it first.
|
||||
- Bench follow-up: replaying the emulator CONNECT sequence on the real device did not switch the LCD to OK. The real device answered the `04 00 00 80 00 DE` step with `07 80 C0 60 20 5D` in the captured run and remained at `CONNECT NOT ACT`, so the next mismatch to chase is the missing visible `07 80 C0 60 20 5D` response/session context rather than the LCD OK branch.
|
||||
- CCU seed-hint finding: `build\rom_ccu_seed_hints.txt` currently ranks selector `0x000`, `0x0F6`, `0x003`, and `0x040` as the highest-value fake-CCU stream candidates. The generated seed frames are `00 00 00 80 80 5A`, `00 01 76 20 00 0D`, `00 00 03 80 00 D9`, and `00 00 40 FF FF 1A`, with command-1 readbacks listed beside them.
|
||||
- Observed capture labels such as `cam_power_button_candidate` and `call_button_candidate` are deliberately treated as capture overlays, not protocol facts hard-coded in ROM.
|
||||
|
||||
The generated listing is written to:
|
||||
@@ -135,7 +151,10 @@ build/rom_serial_pseudocode.c
|
||||
build/rom_serial_gate.txt
|
||||
build/rom_report_sources.txt
|
||||
build/rom_table_xrefs.txt
|
||||
build/rom_ccu_seed_hints.txt
|
||||
build/rom_eeprom_layout.txt
|
||||
build/rom_consistency.txt
|
||||
build/emulator-eeprom-boot.txt
|
||||
build/callgraph.dot
|
||||
```
|
||||
|
||||
@@ -196,14 +215,20 @@ For gate/queue and table reports:
|
||||
|
||||
```powershell
|
||||
python h8536_serial_gate.py --help
|
||||
python h8536_rx_branch_trace.py --help
|
||||
python h8536_report_source_trace.py --help
|
||||
python h8536_table_xrefs.py --help
|
||||
python h8536_ccu_seed_hints.py --help
|
||||
python h8536_eeprom_layout.py --help
|
||||
python h8536_consistency.py --help
|
||||
```
|
||||
|
||||
- `h8536_serial_gate.py`: reports the autonomous TX gate and report queue evidence.
|
||||
- `h8536_rx_branch_trace.py`: reports the SCI1 RX branch tree. Current finding: command `0x04/0x05/0x06` are continuation-path commands behind `FAA2 != 0`, so a standalone command-4 force from idle should not reach `BD0E`.
|
||||
- `h8536_report_source_trace.py`: traces direct `loc_3E54` report enqueue sources. Current finding: no direct static `R3 = 0x0007` enqueue in the JSON, so CAM power `0x0007` remains runtime/capture-observed unless a later indirect/table path proves it.
|
||||
- `h8536_table_xrefs.py`: emits candidate table/index xrefs and LCD text correlation hints.
|
||||
- `h8536_ccu_seed_hints.py`: mines table, dispatch, LCD, and observed-report hints for the CCU-side state stream the RCP may expect before active displays/reports.
|
||||
- `h8536_eeprom_layout.py`: mines the X24164 EEPROM layout, ROM factory defaults, persistent record slots, and serial selector-to-EEPROM offset mapping.
|
||||
- `h8536_consistency.py`: flags JSON-to-pseudocode semantic hazards such as byte immediates written to word destinations.
|
||||
|
||||
For the emulator harness:
|
||||
@@ -225,6 +250,9 @@ python h8536_emulator_rx_probe.py --help
|
||||
- `--p9-fast-optimistic-wrapper`: legacy fallback for older wrapper experiments; the known `BFE0/BFFE` EEPROM wrappers now use the X24164 model instead.
|
||||
- `--p7-input 0xFF`: set external P7 input pin state; this matters for the EEPROM defaulting gate at `P7DR.7` and the DIP-switch style inputs.
|
||||
- `--eeprom-seed blank|factory`: choose blank X24164 power-on state or pre-seed the X24164/shadow tables from the ROM defaults before reset.
|
||||
- `--eeprom-load PATH`: load a 0x1000-byte logical X24164 EEPROM image before boot/probe; page 0 is also mirrored into the F400 shadow so the ROM's early `F402` signature check sees the loaded state.
|
||||
- `--eeprom-save PATH`: save the final 0x1000-byte logical EEPROM image after boot/probe.
|
||||
- `--eeprom-report PATH` / `--eeprom-report-json PATH`: write a ROM-layout-aware EEPROM snapshot with page records, write logs, factory diffs, and F400 shadow diffs.
|
||||
- `--trace-report-gates`, `--trace-report-queue`, and `--trace-ram-lifecycle`: inspect the serial report queue, `loc_4046`/`F9C4` gate, and watched RAM byte history.
|
||||
- `--target-frame "00 00 00 00 80 DA"`: compare staged/emitted TX bytes against an expected six-byte frame.
|
||||
- `h8536_emulator_rx_probe.py "04 00 00 80 00"`: append the checksum, inject the host frame through SCI1 RX, and summarize responses.
|
||||
@@ -234,6 +262,10 @@ python h8536_emulator_rx_probe.py --help
|
||||
- `scripts\serial_scenario.py scenarios\ack-race-000-001.json --log captures\ack-race-000-001.txt --result-json captures\ack-race-000-001-result.json`: run the focused `0x000 -> 0x001` retry probe with immediate reactive ACK and a 2 ms poll interval, to test whether command 5 can arrive before the second `07 80 40 20 90 2D` retry.
|
||||
- `scripts\serial_scenario.py scenarios\early-ack-000-001.json --log captures\early-ack-000-001.txt --result-json captures\early-ack-000-001-result.json`: send the same command-1 pair, then send command-5 ACK immediately without waiting for the retry frame.
|
||||
- `scripts\serial_scenario.py scenarios\table-sweep-ack-000-07f.json --log captures\table-sweep-ack-000-07f.txt --result-json captures\table-sweep-ack-000-07f-result.json`: run a repeatable bench scenario that sweeps selectors `0x000-0x07F` and sends `05 00 40 00 00 1F` only after `07 80 40 20 90 2D` appears. The checked-in scenario stops if it reaches 8 ACKs or 32 target hits. Use `--sync fixed` only when comparing against the old non-resyncing receiver.
|
||||
- `scripts\state_map_runner.py --preset ok --prime-frame "01 80 40 40 30 EB" --prime-repeat 1 --prompt-screen`: run the state-map proof sequence against the bench device. The runner waits for a device `07...` visible-drain candidate, guards briefly so TXI can finish, sends the selector-zero force, then probes direct readback and command-7 recovery without inserting a command-0/command-1 destroyer before the force.
|
||||
- `scripts\state_map_runner.py --analyze-log captures\ack-race-000-001.txt --json-out captures\ack-race-000-001-state-map.json`: classify an existing capture using the same state-map rules and report whether the selector-zero `BD0E -> E000[0]` edge was proven.
|
||||
- `h8536_emulator_state_search.py --preset connect-queue --target ok --first-hit --json-out build\connect-state-search-ok.json`: run the bounded emulator state search for the minimum selector-zero queue condition that reaches `CONNECT: OK`. The default matrix varies `E000[0]` and `F730`, seeds `F970[0]=0`, starts at `loc_2806`, and executes real ROM code into the LCD handler.
|
||||
- `h8536_emulator_state_search.py --preset custom --pc 0x2CB9 --word E000=0x8080 --byte F730=0 --target ok`: directly test the CONNECT handler branch with explicit internal state patches.
|
||||
- `scripts\bench_connect_lcd_sequence.py --port COM5 --relay-port COM6 --prompt-screen`: power-cycle the bench device, wait for heartbeat readiness, send `04 00 00 40 00 1E`, `04 00 00 80 00 DE`, `04 00 00 C0 00 9E`, log RX/TX, and prompt for observed LCD text.
|
||||
- `h8536_emulator_bench_replay.py captures\bench-connect-lcd-sequence-20260525-214411.txt --assert-bench-parity`: replay a real bench log into the emulator using timed UART RX by default and intentionally fail while any response/LCD state still diverges from the bench-observed `CONNECT NOT ACT` plus `07 80 C0 60 20 5D` path. Pass `--polite-rx` for the old wait-until-consumed injection mode.
|
||||
- Current status: boots from `H'1000`, initializes SCI1, models the traced X24164 EEPROM bus on P9, captures P9 byte candidates, can optionally fast-path known P9 EEPROM routines, schedules FRT1/FRT2 OCIA from timer registers and `--clock-hz`, captures the ROM-driven LCD line ` CONNECT:NOT ACT`, and emits the observed heartbeat frame `00 00 00 00 80 DA`.
|
||||
@@ -264,12 +296,17 @@ python h8536_emulator_rx_probe.py --help
|
||||
- `h8536/protocol_trace.py`: raw six-byte protocol frame decoder/checksum validator.
|
||||
- `h8536/protocol_capture.py`: timestamped serial capture parser, frame recombiner, and cadence/gate-session analyzer.
|
||||
- `h8536/serial_scenario.py`: JSON-driven bench scenario engine shared by real-device serial scripts.
|
||||
- `h8536/state_map_runner.py`: PT2 state-map proof runner and bench-log analyzer for visible-drain token, selector-zero force, `E000[0]` readback, and command-7 recovery experiments.
|
||||
- `h8536/serial_gate.py`: autonomous TX gate/queue state-machine reconstruction.
|
||||
- `h8536/report_source_trace.py`: direct `loc_3E54` report enqueue source tracer.
|
||||
- `h8536/table_xrefs.py`: table/index xrefs and LCD correlation report generation.
|
||||
- `h8536/ccu_seed_hints.py`: ROM miner for likely fake-CCU state seed selectors and candidate command/readback frames.
|
||||
- `h8536/eeprom_layout.py`: ROM miner for X24164 EEPROM defaults, 8-byte record slots, and serial persistence mapping.
|
||||
- `h8536/consistency.py`: decompiler/pseudocode semantic consistency checks.
|
||||
- `h8536/emulator/`: early H8/536 emulator package split into CPU state, memory map, SCI1 TX capture, 38400 8N1 UART injection timing, P9/X24164 EEPROM bus model, LCD model, manual-derived FRT timer scheduling, runner, probe, CLI, and peripheral scaffolding.
|
||||
- `h8536/emulator/eeprom_image.py`: logical EEPROM image dump/report helpers for emulator runs, including factory diffs and record-slot summaries.
|
||||
- `h8536/emulator/rx_probe.py`: host-frame injection and response/listener probe for SCI1 RX experiments.
|
||||
- `h8536/emulator/state_search.py`: bounded internal-state search for CONNECT LCD outcomes using ROM execution plus explicit RAM/table patches.
|
||||
- `h8536/board_profile.py`: Sony RCP-TX7 board-trace annotations, including the MAX202 RS232 path.
|
||||
- `h8536/peripheral_access.py`: FRT/A-D TEMP-register access analysis.
|
||||
- `h8536/pseudocode.py`: JSON-to-C-like pseudocode generation.
|
||||
@@ -278,8 +315,10 @@ python h8536_emulator_rx_probe.py --help
|
||||
- `h8536_pseudocode.py`: pseudocode CLI wrapper.
|
||||
- `h8536_serial_pseudocode.py`: focused serial pseudocode CLI wrapper.
|
||||
- `h8536_protocol_trace.py`, `h8536_protocol_capture.py`: protocol analysis CLI wrappers.
|
||||
- `h8536_serial_gate.py`, `h8536_report_source_trace.py`, `h8536_table_xrefs.py`, `h8536_consistency.py`: sidecar analysis CLI wrappers.
|
||||
- `h8536_serial_gate.py`, `h8536_report_source_trace.py`, `h8536_table_xrefs.py`, `h8536_ccu_seed_hints.py`, `h8536_eeprom_layout.py`, `h8536_consistency.py`: sidecar analysis CLI wrappers.
|
||||
- `h8536_emulator.py`, `h8536_emulator_probe.py`, `h8536_emulator_rx_probe.py`, `h8536_emulator_bench_replay.py`: emulator CLI wrappers.
|
||||
- `h8536_emulator_state_search.py`: emulator CONNECT state-search CLI wrapper.
|
||||
- `scripts/bench_connect_lcd_sequence.py`: real-device COM5/COM6 bench runner for the CONNECT LCD sequence.
|
||||
- `scripts/serial_table_dump.py`: read-only COM5/COM6 command-1 table sweep for inferring live EEPROM-backed parameter state.
|
||||
- `scripts/serial_scenario.py`: JSON-driven COM5/COM6 bench scenario runner for chained probes, waits, read sweeps, and ACK-on-target experiments.
|
||||
- `scripts/state_map_runner.py`: COM5/COM6 PT2 state-map proof runner and offline bench-log analyzer.
|
||||
|
||||
BIN
build/bench-sync-after-dip-reset.bin
Normal file
BIN
build/bench-sync-after-dip-reset.bin
Normal file
Binary file not shown.
158603
build/bench-sync-after-dip-reset.json
Normal file
158603
build/bench-sync-after-dip-reset.json
Normal file
File diff suppressed because it is too large
Load Diff
111
build/bench-sync-after-dip-reset.txt
Normal file
111
build/bench-sync-after-dip-reset.txt
Normal file
@@ -0,0 +1,111 @@
|
||||
Emulator EEPROM Snapshot
|
||||
|
||||
size=0x1000 sha256=4bed7704e1ea085487ca325c43bd60da75d37b6ae6f8292544e069a8825c64c6
|
||||
writes: bytes=4216 words=2108 factory_diff_words=0
|
||||
|
||||
Persistent Records:
|
||||
- page 0x0 EEPROM 0x000-0x007 bytes=00 00 6B 6F FE 00 00 00 text='..ko....'
|
||||
- page 0x1 EEPROM 0x100-0x107 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x2 EEPROM 0x200-0x207 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x3 EEPROM 0x300-0x307 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x4 EEPROM 0x400-0x407 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x5 EEPROM 0x500-0x507 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x6 EEPROM 0x600-0x607 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x7 EEPROM 0x700-0x707 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x8 EEPROM 0x800-0x807 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x9 EEPROM 0x900-0x907 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0xA EEPROM 0xA00-0xA07 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0xB EEPROM 0xB00-0xB07 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0xC EEPROM 0xC00-0xC07 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0xD EEPROM 0xD00-0xD07 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0xE EEPROM 0xE00-0xE07 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0xF EEPROM 0xF00-0xF07 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
|
||||
EEPROM Word Writes:
|
||||
- 0x0FE page=0x0 offset=0xFE 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x1FE page=0x1 offset=0xFE 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x2FE page=0x2 offset=0xFE 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x3FE page=0x3 offset=0xFE 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x4FE page=0x4 offset=0xFE 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x5FE page=0x5 offset=0xFE 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x6FE page=0x6 offset=0xFE 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x7FE page=0x7 offset=0xFE 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x8FE page=0x8 offset=0xFE 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x9FE page=0x9 offset=0xFE 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xAFE page=0xA offset=0xFE 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xBFE page=0xB offset=0xFE 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xCFE page=0xC offset=0xFE 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xDFE page=0xD offset=0xFE 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xEFE page=0xE offset=0xFE 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xFFE page=0xF offset=0xFE 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x0FC page=0x0 offset=0xFC 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x1FC page=0x1 offset=0xFC 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x2FC page=0x2 offset=0xFC 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x3FC page=0x3 offset=0xFC 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x4FC page=0x4 offset=0xFC 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x5FC page=0x5 offset=0xFC 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x6FC page=0x6 offset=0xFC 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x7FC page=0x7 offset=0xFC 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x8FC page=0x8 offset=0xFC 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x9FC page=0x9 offset=0xFC 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xAFC page=0xA offset=0xFC 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xBFC page=0xB offset=0xFC 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xCFC page=0xC offset=0xFC 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xDFC page=0xD offset=0xFC 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xEFC page=0xE offset=0xFC 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xFFC page=0xF offset=0xFC 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x0FA page=0x0 offset=0xFA 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x1FA page=0x1 offset=0xFA 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x2FA page=0x2 offset=0xFA 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x3FA page=0x3 offset=0xFA 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x4FA page=0x4 offset=0xFA 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x5FA page=0x5 offset=0xFA 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x6FA page=0x6 offset=0xFA 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x7FA page=0x7 offset=0xFA 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x8FA page=0x8 offset=0xFA 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x9FA page=0x9 offset=0xFA 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xAFA page=0xA offset=0xFA 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xBFA page=0xB offset=0xFA 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xCFA page=0xC offset=0xFA 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xDFA page=0xD offset=0xFA 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xEFA page=0xE offset=0xFA 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xFFA page=0xF offset=0xFA 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x0F8 page=0x0 offset=0xF8 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x1F8 page=0x1 offset=0xF8 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x2F8 page=0x2 offset=0xF8 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x3F8 page=0x3 offset=0xF8 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x4F8 page=0x4 offset=0xF8 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x5F8 page=0x5 offset=0xF8 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x6F8 page=0x6 offset=0xF8 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x7F8 page=0x7 offset=0xF8 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x8F8 page=0x8 offset=0xF8 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x9F8 page=0x9 offset=0xF8 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xAF8 page=0xA offset=0xF8 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xBF8 page=0xB offset=0xF8 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xCF8 page=0xC offset=0xF8 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xDF8 page=0xD offset=0xF8 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xEF8 page=0xE offset=0xF8 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xFF8 page=0xF offset=0xF8 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x0F6 page=0x0 offset=0xF6 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x1F6 page=0x1 offset=0xF6 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x2F6 page=0x2 offset=0xF6 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x3F6 page=0x3 offset=0xF6 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x4F6 page=0x4 offset=0xF6 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x5F6 page=0x5 offset=0xF6 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x6F6 page=0x6 offset=0xF6 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x7F6 page=0x7 offset=0xF6 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x8F6 page=0x8 offset=0xF6 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x9F6 page=0x9 offset=0xF6 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xAF6 page=0xA offset=0xF6 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xBF6 page=0xB offset=0xF6 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xCF6 page=0xC offset=0xF6 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xDF6 page=0xD offset=0xF6 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xEF6 page=0xE offset=0xF6 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xFF6 page=0xF offset=0xF6 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- ... 2028 more word writes omitted
|
||||
|
||||
Factory Diffs:
|
||||
- current EEPROM image matches ROM factory/default image
|
||||
|
||||
F400 Shadow Diffs:
|
||||
- F400-F4FF shadow matches ROM factory words or no ROM factory baseline was supplied
|
||||
BIN
build/bench-sync-factory-trial1.bin
Normal file
BIN
build/bench-sync-factory-trial1.bin
Normal file
Binary file not shown.
31
build/bench-sync-factory-trial1.txt
Normal file
31
build/bench-sync-factory-trial1.txt
Normal file
@@ -0,0 +1,31 @@
|
||||
Emulator EEPROM Snapshot
|
||||
|
||||
size=0x1000 sha256=4bed7704e1ea085487ca325c43bd60da75d37b6ae6f8292544e069a8825c64c6
|
||||
writes: bytes=0 words=0 factory_diff_words=0
|
||||
|
||||
Persistent Records:
|
||||
- page 0x0 EEPROM 0x000-0x007 bytes=00 00 6B 6F FE 00 00 00 text='..ko....'
|
||||
- page 0x1 EEPROM 0x100-0x107 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x2 EEPROM 0x200-0x207 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x3 EEPROM 0x300-0x307 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x4 EEPROM 0x400-0x407 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x5 EEPROM 0x500-0x507 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x6 EEPROM 0x600-0x607 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x7 EEPROM 0x700-0x707 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x8 EEPROM 0x800-0x807 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x9 EEPROM 0x900-0x907 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0xA EEPROM 0xA00-0xA07 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0xB EEPROM 0xB00-0xB07 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0xC EEPROM 0xC00-0xC07 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0xD EEPROM 0xD00-0xD07 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0xE EEPROM 0xE00-0xE07 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0xF EEPROM 0xF00-0xF07 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
|
||||
EEPROM Word Writes:
|
||||
- none since EEPROM setup/load
|
||||
|
||||
Factory Diffs:
|
||||
- current EEPROM image matches ROM factory/default image
|
||||
|
||||
F400 Shadow Diffs:
|
||||
- H'F4AA offset=0xAA expected=0x8000 actual=0x5500 (factory_shadow_offset; selectors=0x112)
|
||||
31
build/bench-sync-polite-trial1-eeprom.txt
Normal file
31
build/bench-sync-polite-trial1-eeprom.txt
Normal file
@@ -0,0 +1,31 @@
|
||||
Emulator EEPROM Snapshot
|
||||
|
||||
size=0x1000 sha256=4bed7704e1ea085487ca325c43bd60da75d37b6ae6f8292544e069a8825c64c6
|
||||
writes: bytes=0 words=0 factory_diff_words=0
|
||||
|
||||
Persistent Records:
|
||||
- page 0x0 EEPROM 0x000-0x007 bytes=00 00 6B 6F FE 00 00 00 text='..ko....'
|
||||
- page 0x1 EEPROM 0x100-0x107 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x2 EEPROM 0x200-0x207 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x3 EEPROM 0x300-0x307 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x4 EEPROM 0x400-0x407 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x5 EEPROM 0x500-0x507 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x6 EEPROM 0x600-0x607 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x7 EEPROM 0x700-0x707 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x8 EEPROM 0x800-0x807 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x9 EEPROM 0x900-0x907 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0xA EEPROM 0xA00-0xA07 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0xB EEPROM 0xB00-0xB07 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0xC EEPROM 0xC00-0xC07 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0xD EEPROM 0xD00-0xD07 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0xE EEPROM 0xE00-0xE07 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0xF EEPROM 0xF00-0xF07 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
|
||||
EEPROM Word Writes:
|
||||
- none since EEPROM setup/load
|
||||
|
||||
Factory Diffs:
|
||||
- current EEPROM image matches ROM factory/default image
|
||||
|
||||
F400 Shadow Diffs:
|
||||
- H'F4AA offset=0xAA expected=0x8000 actual=0x5500 (factory_shadow_offset; selectors=0x112)
|
||||
31
build/bench-sync-polite-trial2-eeprom.txt
Normal file
31
build/bench-sync-polite-trial2-eeprom.txt
Normal file
@@ -0,0 +1,31 @@
|
||||
Emulator EEPROM Snapshot
|
||||
|
||||
size=0x1000 sha256=4bed7704e1ea085487ca325c43bd60da75d37b6ae6f8292544e069a8825c64c6
|
||||
writes: bytes=0 words=0 factory_diff_words=0
|
||||
|
||||
Persistent Records:
|
||||
- page 0x0 EEPROM 0x000-0x007 bytes=00 00 6B 6F FE 00 00 00 text='..ko....'
|
||||
- page 0x1 EEPROM 0x100-0x107 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x2 EEPROM 0x200-0x207 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x3 EEPROM 0x300-0x307 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x4 EEPROM 0x400-0x407 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x5 EEPROM 0x500-0x507 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x6 EEPROM 0x600-0x607 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x7 EEPROM 0x700-0x707 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x8 EEPROM 0x800-0x807 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x9 EEPROM 0x900-0x907 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0xA EEPROM 0xA00-0xA07 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0xB EEPROM 0xB00-0xB07 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0xC EEPROM 0xC00-0xC07 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0xD EEPROM 0xD00-0xD07 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0xE EEPROM 0xE00-0xE07 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0xF EEPROM 0xF00-0xF07 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
|
||||
EEPROM Word Writes:
|
||||
- none since EEPROM setup/load
|
||||
|
||||
Factory Diffs:
|
||||
- current EEPROM image matches ROM factory/default image
|
||||
|
||||
F400 Shadow Diffs:
|
||||
- H'F4AA offset=0xAA expected=0x8000 actual=0x5500 (factory_shadow_offset; selectors=0x112)
|
||||
BIN
build/bench-sync-polite-trial2.bin
Normal file
BIN
build/bench-sync-polite-trial2.bin
Normal file
Binary file not shown.
BIN
build/bench-sync-pololite-trial1.bin
Normal file
BIN
build/bench-sync-pololite-trial1.bin
Normal file
Binary file not shown.
50
build/bench-sync-state-search-direct-ok.json
Normal file
50
build/bench-sync-state-search-direct-ok.json
Normal file
@@ -0,0 +1,50 @@
|
||||
{
|
||||
"case_count": 1,
|
||||
"description": "Use only --byte/--word/--matrix-* patches and --pc.",
|
||||
"hits": [
|
||||
{
|
||||
"case_index": 0,
|
||||
"display": " CONNECT: OK | | | ",
|
||||
"e000": "0x8080",
|
||||
"f730": "0x81",
|
||||
"f9b4": "0x00",
|
||||
"f9b9": "0x00",
|
||||
"final_pc": "H'FFFF",
|
||||
"line0": " CONNECT: OK ",
|
||||
"outcome": "ok",
|
||||
"patches": [
|
||||
"byte:H'F730=0x00",
|
||||
"word:H'E000=0x8080"
|
||||
],
|
||||
"pc": "H'2CB9",
|
||||
"steps": 37097,
|
||||
"stopped_reason": "stop_pc",
|
||||
"unsupported": null
|
||||
}
|
||||
],
|
||||
"kind": "h8536_emulator_state_search",
|
||||
"preset": "custom",
|
||||
"result_count": 1,
|
||||
"results": [
|
||||
{
|
||||
"case_index": 0,
|
||||
"display": " CONNECT: OK | | | ",
|
||||
"e000": "0x8080",
|
||||
"f730": "0x81",
|
||||
"f9b4": "0x00",
|
||||
"f9b9": "0x00",
|
||||
"final_pc": "H'FFFF",
|
||||
"line0": " CONNECT: OK ",
|
||||
"outcome": "ok",
|
||||
"patches": [
|
||||
"byte:H'F730=0x00",
|
||||
"word:H'E000=0x8080"
|
||||
],
|
||||
"pc": "H'2CB9",
|
||||
"steps": 37097,
|
||||
"stopped_reason": "stop_pc",
|
||||
"unsupported": null
|
||||
}
|
||||
],
|
||||
"target": "ok"
|
||||
}
|
||||
56
build/bench-sync-state-search-queue-ok-exact.json
Normal file
56
build/bench-sync-state-search-queue-ok-exact.json
Normal file
@@ -0,0 +1,56 @@
|
||||
{
|
||||
"case_count": 1,
|
||||
"description": "Use only --byte/--word/--matrix-* patches and --pc.",
|
||||
"hits": [
|
||||
{
|
||||
"case_index": 0,
|
||||
"display": " CONNECT: OK | | | ",
|
||||
"e000": "0x8080",
|
||||
"f730": "0x81",
|
||||
"f9b4": "0x01",
|
||||
"f9b9": "0x01",
|
||||
"final_pc": "H'FFFF",
|
||||
"line0": " CONNECT: OK ",
|
||||
"outcome": "ok",
|
||||
"patches": [
|
||||
"byte:H'F9B9=0x00",
|
||||
"byte:H'F9B4=0x01",
|
||||
"byte:H'F730=0x00",
|
||||
"word:H'F970=0x0000",
|
||||
"word:H'E000=0x8080"
|
||||
],
|
||||
"pc": "H'2806",
|
||||
"steps": 37121,
|
||||
"stopped_reason": "stop_pc",
|
||||
"unsupported": null
|
||||
}
|
||||
],
|
||||
"kind": "h8536_emulator_state_search",
|
||||
"preset": "custom",
|
||||
"result_count": 1,
|
||||
"results": [
|
||||
{
|
||||
"case_index": 0,
|
||||
"display": " CONNECT: OK | | | ",
|
||||
"e000": "0x8080",
|
||||
"f730": "0x81",
|
||||
"f9b4": "0x01",
|
||||
"f9b9": "0x01",
|
||||
"final_pc": "H'FFFF",
|
||||
"line0": " CONNECT: OK ",
|
||||
"outcome": "ok",
|
||||
"patches": [
|
||||
"byte:H'F9B9=0x00",
|
||||
"byte:H'F9B4=0x01",
|
||||
"byte:H'F730=0x00",
|
||||
"word:H'F970=0x0000",
|
||||
"word:H'E000=0x8080"
|
||||
],
|
||||
"pc": "H'2806",
|
||||
"steps": 37121,
|
||||
"stopped_reason": "stop_pc",
|
||||
"unsupported": null
|
||||
}
|
||||
],
|
||||
"target": "ok"
|
||||
}
|
||||
31
build/bench-sync-timed-trial1-eeprom.txt
Normal file
31
build/bench-sync-timed-trial1-eeprom.txt
Normal file
@@ -0,0 +1,31 @@
|
||||
Emulator EEPROM Snapshot
|
||||
|
||||
size=0x1000 sha256=4bed7704e1ea085487ca325c43bd60da75d37b6ae6f8292544e069a8825c64c6
|
||||
writes: bytes=0 words=0 factory_diff_words=0
|
||||
|
||||
Persistent Records:
|
||||
- page 0x0 EEPROM 0x000-0x007 bytes=00 00 6B 6F FE 00 00 00 text='..ko....'
|
||||
- page 0x1 EEPROM 0x100-0x107 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x2 EEPROM 0x200-0x207 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x3 EEPROM 0x300-0x307 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x4 EEPROM 0x400-0x407 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x5 EEPROM 0x500-0x507 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x6 EEPROM 0x600-0x607 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x7 EEPROM 0x700-0x707 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x8 EEPROM 0x800-0x807 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x9 EEPROM 0x900-0x907 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0xA EEPROM 0xA00-0xA07 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0xB EEPROM 0xB00-0xB07 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0xC EEPROM 0xC00-0xC07 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0xD EEPROM 0xD00-0xD07 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0xE EEPROM 0xE00-0xE07 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0xF EEPROM 0xF00-0xF07 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
|
||||
EEPROM Word Writes:
|
||||
- none since EEPROM setup/load
|
||||
|
||||
Factory Diffs:
|
||||
- current EEPROM image matches ROM factory/default image
|
||||
|
||||
F400 Shadow Diffs:
|
||||
- H'F4AA offset=0xAA expected=0x8000 actual=0x5500 (factory_shadow_offset; selectors=0x112)
|
||||
BIN
build/bench-sync-timed-trial1.bin
Normal file
BIN
build/bench-sync-timed-trial1.bin
Normal file
Binary file not shown.
31
build/bench-sync-timed-trial2-eeprom.txt
Normal file
31
build/bench-sync-timed-trial2-eeprom.txt
Normal file
@@ -0,0 +1,31 @@
|
||||
Emulator EEPROM Snapshot
|
||||
|
||||
size=0x1000 sha256=4bed7704e1ea085487ca325c43bd60da75d37b6ae6f8292544e069a8825c64c6
|
||||
writes: bytes=0 words=0 factory_diff_words=0
|
||||
|
||||
Persistent Records:
|
||||
- page 0x0 EEPROM 0x000-0x007 bytes=00 00 6B 6F FE 00 00 00 text='..ko....'
|
||||
- page 0x1 EEPROM 0x100-0x107 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x2 EEPROM 0x200-0x207 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x3 EEPROM 0x300-0x307 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x4 EEPROM 0x400-0x407 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x5 EEPROM 0x500-0x507 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x6 EEPROM 0x600-0x607 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x7 EEPROM 0x700-0x707 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x8 EEPROM 0x800-0x807 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x9 EEPROM 0x900-0x907 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0xA EEPROM 0xA00-0xA07 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0xB EEPROM 0xB00-0xB07 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0xC EEPROM 0xC00-0xC07 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0xD EEPROM 0xD00-0xD07 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0xE EEPROM 0xE00-0xE07 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0xF EEPROM 0xF00-0xF07 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
|
||||
EEPROM Word Writes:
|
||||
- none since EEPROM setup/load
|
||||
|
||||
Factory Diffs:
|
||||
- current EEPROM image matches ROM factory/default image
|
||||
|
||||
F400 Shadow Diffs:
|
||||
- H'F4AA offset=0xAA expected=0x8000 actual=0x5500 (factory_shadow_offset; selectors=0x112)
|
||||
BIN
build/bench-sync-timed-trial2.bin
Normal file
BIN
build/bench-sync-timed-trial2.bin
Normal file
Binary file not shown.
386
build/connect-state-search-ok.json
Normal file
386
build/connect-state-search-ok.json
Normal file
@@ -0,0 +1,386 @@
|
||||
{
|
||||
"case_count": 25,
|
||||
"description": "Queue selector zero in F970, start at loc_2806, then enter loc_2CB9 through the ROM dispatch.",
|
||||
"hits": [
|
||||
{
|
||||
"case_index": 15,
|
||||
"display": " CONNECT: OK | | | ",
|
||||
"e000": "0x8080",
|
||||
"f730": "0x81",
|
||||
"f9b4": "0x01",
|
||||
"f9b9": "0x01",
|
||||
"final_pc": "H'FFFF",
|
||||
"line0": " CONNECT: OK ",
|
||||
"outcome": "ok",
|
||||
"patches": [
|
||||
"byte:H'F9B9=0x00",
|
||||
"byte:H'F9B4=0x01",
|
||||
"word:H'F970=0x0000",
|
||||
"word:H'E000=0x8080",
|
||||
"byte:H'F730=0x00"
|
||||
],
|
||||
"pc": "H'2806",
|
||||
"steps": 37121,
|
||||
"stopped_reason": "stop_pc",
|
||||
"unsupported": null
|
||||
}
|
||||
],
|
||||
"kind": "h8536_emulator_state_search",
|
||||
"preset": "connect-queue",
|
||||
"result_count": 16,
|
||||
"results": [
|
||||
{
|
||||
"case_index": 0,
|
||||
"display": " CONNECT:NOT ACT | | | ",
|
||||
"e000": "0x0080",
|
||||
"f730": "0x00",
|
||||
"f9b4": "0x01",
|
||||
"f9b9": "0x01",
|
||||
"final_pc": "H'2D37",
|
||||
"line0": " CONNECT:NOT ACT",
|
||||
"outcome": "not-act",
|
||||
"patches": [
|
||||
"byte:H'F9B9=0x00",
|
||||
"byte:H'F9B4=0x01",
|
||||
"word:H'F970=0x0000",
|
||||
"word:H'E000=0x0000",
|
||||
"byte:H'F730=0x00"
|
||||
],
|
||||
"pc": "H'2806",
|
||||
"steps": 5109,
|
||||
"stopped_reason": "unsupported_instruction",
|
||||
"unsupported": "unsupported instruction at H'2D37: 6E C6 MOV:L.W @H'00C6, R6"
|
||||
},
|
||||
{
|
||||
"case_index": 1,
|
||||
"display": " CONNECT:NOT ACT | | | ",
|
||||
"e000": "0x0080",
|
||||
"f730": "0x00",
|
||||
"f9b4": "0x01",
|
||||
"f9b9": "0x01",
|
||||
"final_pc": "H'2D37",
|
||||
"line0": " CONNECT:NOT ACT",
|
||||
"outcome": "not-act",
|
||||
"patches": [
|
||||
"byte:H'F9B9=0x00",
|
||||
"byte:H'F9B4=0x01",
|
||||
"word:H'F970=0x0000",
|
||||
"word:H'E000=0x0000",
|
||||
"byte:H'F730=0x01"
|
||||
],
|
||||
"pc": "H'2806",
|
||||
"steps": 5109,
|
||||
"stopped_reason": "unsupported_instruction",
|
||||
"unsupported": "unsupported instruction at H'2D37: 6E C6 MOV:L.W @H'00C6, R6"
|
||||
},
|
||||
{
|
||||
"case_index": 2,
|
||||
"display": " CONNECT:NOT ACT | | | ",
|
||||
"e000": "0x0080",
|
||||
"f730": "0x00",
|
||||
"f9b4": "0x01",
|
||||
"f9b9": "0x01",
|
||||
"final_pc": "H'2D37",
|
||||
"line0": " CONNECT:NOT ACT",
|
||||
"outcome": "not-act",
|
||||
"patches": [
|
||||
"byte:H'F9B9=0x00",
|
||||
"byte:H'F9B4=0x01",
|
||||
"word:H'F970=0x0000",
|
||||
"word:H'E000=0x0000",
|
||||
"byte:H'F730=0x41"
|
||||
],
|
||||
"pc": "H'2806",
|
||||
"steps": 5109,
|
||||
"stopped_reason": "unsupported_instruction",
|
||||
"unsupported": "unsupported instruction at H'2D37: 6E C6 MOV:L.W @H'00C6, R6"
|
||||
},
|
||||
{
|
||||
"case_index": 3,
|
||||
"display": " CONNECT:NOT ACT | | | ",
|
||||
"e000": "0x0080",
|
||||
"f730": "0x00",
|
||||
"f9b4": "0x01",
|
||||
"f9b9": "0x01",
|
||||
"final_pc": "H'2D37",
|
||||
"line0": " CONNECT:NOT ACT",
|
||||
"outcome": "not-act",
|
||||
"patches": [
|
||||
"byte:H'F9B9=0x00",
|
||||
"byte:H'F9B4=0x01",
|
||||
"word:H'F970=0x0000",
|
||||
"word:H'E000=0x0000",
|
||||
"byte:H'F730=0x81"
|
||||
],
|
||||
"pc": "H'2806",
|
||||
"steps": 5109,
|
||||
"stopped_reason": "unsupported_instruction",
|
||||
"unsupported": "unsupported instruction at H'2D37: 6E C6 MOV:L.W @H'00C6, R6"
|
||||
},
|
||||
{
|
||||
"case_index": 4,
|
||||
"display": " CONNECT:NOT ACT | | | ",
|
||||
"e000": "0x0080",
|
||||
"f730": "0x00",
|
||||
"f9b4": "0x01",
|
||||
"f9b9": "0x01",
|
||||
"final_pc": "H'2D37",
|
||||
"line0": " CONNECT:NOT ACT",
|
||||
"outcome": "not-act",
|
||||
"patches": [
|
||||
"byte:H'F9B9=0x00",
|
||||
"byte:H'F9B4=0x01",
|
||||
"word:H'F970=0x0000",
|
||||
"word:H'E000=0x0000",
|
||||
"byte:H'F730=0xC1"
|
||||
],
|
||||
"pc": "H'2806",
|
||||
"steps": 5109,
|
||||
"stopped_reason": "unsupported_instruction",
|
||||
"unsupported": "unsupported instruction at H'2D37: 6E C6 MOV:L.W @H'00C6, R6"
|
||||
},
|
||||
{
|
||||
"case_index": 5,
|
||||
"display": " CONNECT:NOT ACT | | | ",
|
||||
"e000": "0x0080",
|
||||
"f730": "0x00",
|
||||
"f9b4": "0x01",
|
||||
"f9b9": "0x01",
|
||||
"final_pc": "H'2D37",
|
||||
"line0": " CONNECT:NOT ACT",
|
||||
"outcome": "not-act",
|
||||
"patches": [
|
||||
"byte:H'F9B9=0x00",
|
||||
"byte:H'F9B4=0x01",
|
||||
"word:H'F970=0x0000",
|
||||
"word:H'E000=0x0080",
|
||||
"byte:H'F730=0x00"
|
||||
],
|
||||
"pc": "H'2806",
|
||||
"steps": 5109,
|
||||
"stopped_reason": "unsupported_instruction",
|
||||
"unsupported": "unsupported instruction at H'2D37: 6E C6 MOV:L.W @H'00C6, R6"
|
||||
},
|
||||
{
|
||||
"case_index": 6,
|
||||
"display": " CONNECT:NOT ACT | | | ",
|
||||
"e000": "0x0080",
|
||||
"f730": "0x00",
|
||||
"f9b4": "0x01",
|
||||
"f9b9": "0x01",
|
||||
"final_pc": "H'2D37",
|
||||
"line0": " CONNECT:NOT ACT",
|
||||
"outcome": "not-act",
|
||||
"patches": [
|
||||
"byte:H'F9B9=0x00",
|
||||
"byte:H'F9B4=0x01",
|
||||
"word:H'F970=0x0000",
|
||||
"word:H'E000=0x0080",
|
||||
"byte:H'F730=0x01"
|
||||
],
|
||||
"pc": "H'2806",
|
||||
"steps": 5109,
|
||||
"stopped_reason": "unsupported_instruction",
|
||||
"unsupported": "unsupported instruction at H'2D37: 6E C6 MOV:L.W @H'00C6, R6"
|
||||
},
|
||||
{
|
||||
"case_index": 7,
|
||||
"display": " CONNECT:NOT ACT | | | ",
|
||||
"e000": "0x0080",
|
||||
"f730": "0x00",
|
||||
"f9b4": "0x01",
|
||||
"f9b9": "0x01",
|
||||
"final_pc": "H'2D37",
|
||||
"line0": " CONNECT:NOT ACT",
|
||||
"outcome": "not-act",
|
||||
"patches": [
|
||||
"byte:H'F9B9=0x00",
|
||||
"byte:H'F9B4=0x01",
|
||||
"word:H'F970=0x0000",
|
||||
"word:H'E000=0x0080",
|
||||
"byte:H'F730=0x41"
|
||||
],
|
||||
"pc": "H'2806",
|
||||
"steps": 5109,
|
||||
"stopped_reason": "unsupported_instruction",
|
||||
"unsupported": "unsupported instruction at H'2D37: 6E C6 MOV:L.W @H'00C6, R6"
|
||||
},
|
||||
{
|
||||
"case_index": 8,
|
||||
"display": " CONNECT:NOT ACT | | | ",
|
||||
"e000": "0x0080",
|
||||
"f730": "0x00",
|
||||
"f9b4": "0x01",
|
||||
"f9b9": "0x01",
|
||||
"final_pc": "H'2D37",
|
||||
"line0": " CONNECT:NOT ACT",
|
||||
"outcome": "not-act",
|
||||
"patches": [
|
||||
"byte:H'F9B9=0x00",
|
||||
"byte:H'F9B4=0x01",
|
||||
"word:H'F970=0x0000",
|
||||
"word:H'E000=0x0080",
|
||||
"byte:H'F730=0x81"
|
||||
],
|
||||
"pc": "H'2806",
|
||||
"steps": 5109,
|
||||
"stopped_reason": "unsupported_instruction",
|
||||
"unsupported": "unsupported instruction at H'2D37: 6E C6 MOV:L.W @H'00C6, R6"
|
||||
},
|
||||
{
|
||||
"case_index": 9,
|
||||
"display": " CONNECT:NOT ACT | | | ",
|
||||
"e000": "0x0080",
|
||||
"f730": "0x00",
|
||||
"f9b4": "0x01",
|
||||
"f9b9": "0x01",
|
||||
"final_pc": "H'2D37",
|
||||
"line0": " CONNECT:NOT ACT",
|
||||
"outcome": "not-act",
|
||||
"patches": [
|
||||
"byte:H'F9B9=0x00",
|
||||
"byte:H'F9B4=0x01",
|
||||
"word:H'F970=0x0000",
|
||||
"word:H'E000=0x0080",
|
||||
"byte:H'F730=0xC1"
|
||||
],
|
||||
"pc": "H'2806",
|
||||
"steps": 5109,
|
||||
"stopped_reason": "unsupported_instruction",
|
||||
"unsupported": "unsupported instruction at H'2D37: 6E C6 MOV:L.W @H'00C6, R6"
|
||||
},
|
||||
{
|
||||
"case_index": 10,
|
||||
"display": " CONNECT:NOT ACT | | | ",
|
||||
"e000": "0x4080",
|
||||
"f730": "0x00",
|
||||
"f9b4": "0x01",
|
||||
"f9b9": "0x01",
|
||||
"final_pc": "H'A9E8",
|
||||
"line0": " CONNECT:NOT ACT",
|
||||
"outcome": "not-act",
|
||||
"patches": [
|
||||
"byte:H'F9B9=0x00",
|
||||
"byte:H'F9B4=0x01",
|
||||
"word:H'F970=0x0000",
|
||||
"word:H'E000=0x4080",
|
||||
"byte:H'F730=0x00"
|
||||
],
|
||||
"pc": "H'2806",
|
||||
"steps": 5366,
|
||||
"stopped_reason": "unsupported_instruction",
|
||||
"unsupported": "unsupported instruction at H'A9E8: 7E 58 MOV:S.W R6, @H'0058"
|
||||
},
|
||||
{
|
||||
"case_index": 11,
|
||||
"display": " CONNECT:NOT ACT | | | ",
|
||||
"e000": "0x4080",
|
||||
"f730": "0x01",
|
||||
"f9b4": "0x01",
|
||||
"f9b9": "0x01",
|
||||
"final_pc": "H'A9E8",
|
||||
"line0": " CONNECT:NOT ACT",
|
||||
"outcome": "not-act",
|
||||
"patches": [
|
||||
"byte:H'F9B9=0x00",
|
||||
"byte:H'F9B4=0x01",
|
||||
"word:H'F970=0x0000",
|
||||
"word:H'E000=0x4080",
|
||||
"byte:H'F730=0x01"
|
||||
],
|
||||
"pc": "H'2806",
|
||||
"steps": 5366,
|
||||
"stopped_reason": "unsupported_instruction",
|
||||
"unsupported": "unsupported instruction at H'A9E8: 7E 58 MOV:S.W R6, @H'0058"
|
||||
},
|
||||
{
|
||||
"case_index": 12,
|
||||
"display": " CONNECT:NOT ACT | | | ",
|
||||
"e000": "0x4080",
|
||||
"f730": "0x41",
|
||||
"f9b4": "0x01",
|
||||
"f9b9": "0x01",
|
||||
"final_pc": "H'FFFF",
|
||||
"line0": " CONNECT:NOT ACT",
|
||||
"outcome": "not-act",
|
||||
"patches": [
|
||||
"byte:H'F9B9=0x00",
|
||||
"byte:H'F9B4=0x01",
|
||||
"word:H'F970=0x0000",
|
||||
"word:H'E000=0x4080",
|
||||
"byte:H'F730=0x41"
|
||||
],
|
||||
"pc": "H'2806",
|
||||
"steps": 45,
|
||||
"stopped_reason": "stop_pc",
|
||||
"unsupported": null
|
||||
},
|
||||
{
|
||||
"case_index": 13,
|
||||
"display": " CONNECT:NOT ACT | | | ",
|
||||
"e000": "0x4080",
|
||||
"f730": "0x81",
|
||||
"f9b4": "0x01",
|
||||
"f9b9": "0x01",
|
||||
"final_pc": "H'A9E8",
|
||||
"line0": " CONNECT:NOT ACT",
|
||||
"outcome": "not-act",
|
||||
"patches": [
|
||||
"byte:H'F9B9=0x00",
|
||||
"byte:H'F9B4=0x01",
|
||||
"word:H'F970=0x0000",
|
||||
"word:H'E000=0x4080",
|
||||
"byte:H'F730=0x81"
|
||||
],
|
||||
"pc": "H'2806",
|
||||
"steps": 5366,
|
||||
"stopped_reason": "unsupported_instruction",
|
||||
"unsupported": "unsupported instruction at H'A9E8: 7E 58 MOV:S.W R6, @H'0058"
|
||||
},
|
||||
{
|
||||
"case_index": 14,
|
||||
"display": " CONNECT:NOT ACT | | | ",
|
||||
"e000": "0x4080",
|
||||
"f730": "0xC1",
|
||||
"f9b4": "0x01",
|
||||
"f9b9": "0x01",
|
||||
"final_pc": "H'FFFF",
|
||||
"line0": " CONNECT:NOT ACT",
|
||||
"outcome": "not-act",
|
||||
"patches": [
|
||||
"byte:H'F9B9=0x00",
|
||||
"byte:H'F9B4=0x01",
|
||||
"word:H'F970=0x0000",
|
||||
"word:H'E000=0x4080",
|
||||
"byte:H'F730=0xC1"
|
||||
],
|
||||
"pc": "H'2806",
|
||||
"steps": 45,
|
||||
"stopped_reason": "stop_pc",
|
||||
"unsupported": null
|
||||
},
|
||||
{
|
||||
"case_index": 15,
|
||||
"display": " CONNECT: OK | | | ",
|
||||
"e000": "0x8080",
|
||||
"f730": "0x81",
|
||||
"f9b4": "0x01",
|
||||
"f9b9": "0x01",
|
||||
"final_pc": "H'FFFF",
|
||||
"line0": " CONNECT: OK ",
|
||||
"outcome": "ok",
|
||||
"patches": [
|
||||
"byte:H'F9B9=0x00",
|
||||
"byte:H'F9B4=0x01",
|
||||
"word:H'F970=0x0000",
|
||||
"word:H'E000=0x8080",
|
||||
"byte:H'F730=0x00"
|
||||
],
|
||||
"pc": "H'2806",
|
||||
"steps": 37121,
|
||||
"stopped_reason": "stop_pc",
|
||||
"unsupported": null
|
||||
}
|
||||
],
|
||||
"target": "ok"
|
||||
}
|
||||
BIN
build/emulator-eeprom-boot.bin
Normal file
BIN
build/emulator-eeprom-boot.bin
Normal file
Binary file not shown.
158603
build/emulator-eeprom-boot.json
Normal file
158603
build/emulator-eeprom-boot.json
Normal file
File diff suppressed because it is too large
Load Diff
111
build/emulator-eeprom-boot.txt
Normal file
111
build/emulator-eeprom-boot.txt
Normal file
@@ -0,0 +1,111 @@
|
||||
Emulator EEPROM Snapshot
|
||||
|
||||
size=0x1000 sha256=4bed7704e1ea085487ca325c43bd60da75d37b6ae6f8292544e069a8825c64c6
|
||||
writes: bytes=4216 words=2108 factory_diff_words=0
|
||||
|
||||
Persistent Records:
|
||||
- page 0x0 EEPROM 0x000-0x007 bytes=00 00 6B 6F FE 00 00 00 text='..ko....'
|
||||
- page 0x1 EEPROM 0x100-0x107 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x2 EEPROM 0x200-0x207 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x3 EEPROM 0x300-0x307 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x4 EEPROM 0x400-0x407 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x5 EEPROM 0x500-0x507 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x6 EEPROM 0x600-0x607 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x7 EEPROM 0x700-0x707 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x8 EEPROM 0x800-0x807 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x9 EEPROM 0x900-0x907 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0xA EEPROM 0xA00-0xA07 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0xB EEPROM 0xB00-0xB07 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0xC EEPROM 0xC00-0xC07 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0xD EEPROM 0xD00-0xD07 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0xE EEPROM 0xE00-0xE07 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0xF EEPROM 0xF00-0xF07 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
|
||||
EEPROM Word Writes:
|
||||
- 0x0FE page=0x0 offset=0xFE 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x1FE page=0x1 offset=0xFE 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x2FE page=0x2 offset=0xFE 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x3FE page=0x3 offset=0xFE 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x4FE page=0x4 offset=0xFE 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x5FE page=0x5 offset=0xFE 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x6FE page=0x6 offset=0xFE 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x7FE page=0x7 offset=0xFE 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x8FE page=0x8 offset=0xFE 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x9FE page=0x9 offset=0xFE 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xAFE page=0xA offset=0xFE 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xBFE page=0xB offset=0xFE 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xCFE page=0xC offset=0xFE 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xDFE page=0xD offset=0xFE 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xEFE page=0xE offset=0xFE 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xFFE page=0xF offset=0xFE 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x0FC page=0x0 offset=0xFC 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x1FC page=0x1 offset=0xFC 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x2FC page=0x2 offset=0xFC 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x3FC page=0x3 offset=0xFC 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x4FC page=0x4 offset=0xFC 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x5FC page=0x5 offset=0xFC 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x6FC page=0x6 offset=0xFC 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x7FC page=0x7 offset=0xFC 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x8FC page=0x8 offset=0xFC 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x9FC page=0x9 offset=0xFC 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xAFC page=0xA offset=0xFC 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xBFC page=0xB offset=0xFC 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xCFC page=0xC offset=0xFC 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xDFC page=0xD offset=0xFC 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xEFC page=0xE offset=0xFC 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xFFC page=0xF offset=0xFC 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x0FA page=0x0 offset=0xFA 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x1FA page=0x1 offset=0xFA 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x2FA page=0x2 offset=0xFA 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x3FA page=0x3 offset=0xFA 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x4FA page=0x4 offset=0xFA 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x5FA page=0x5 offset=0xFA 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x6FA page=0x6 offset=0xFA 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x7FA page=0x7 offset=0xFA 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x8FA page=0x8 offset=0xFA 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x9FA page=0x9 offset=0xFA 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xAFA page=0xA offset=0xFA 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xBFA page=0xB offset=0xFA 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xCFA page=0xC offset=0xFA 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xDFA page=0xD offset=0xFA 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xEFA page=0xE offset=0xFA 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xFFA page=0xF offset=0xFA 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x0F8 page=0x0 offset=0xF8 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x1F8 page=0x1 offset=0xF8 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x2F8 page=0x2 offset=0xF8 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x3F8 page=0x3 offset=0xF8 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x4F8 page=0x4 offset=0xF8 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x5F8 page=0x5 offset=0xF8 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x6F8 page=0x6 offset=0xF8 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x7F8 page=0x7 offset=0xF8 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x8F8 page=0x8 offset=0xF8 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x9F8 page=0x9 offset=0xF8 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xAF8 page=0xA offset=0xF8 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xBF8 page=0xB offset=0xF8 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xCF8 page=0xC offset=0xF8 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xDF8 page=0xD offset=0xF8 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xEF8 page=0xE offset=0xF8 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xFF8 page=0xF offset=0xF8 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x0F6 page=0x0 offset=0xF6 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x1F6 page=0x1 offset=0xF6 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x2F6 page=0x2 offset=0xF6 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x3F6 page=0x3 offset=0xF6 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x4F6 page=0x4 offset=0xF6 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x5F6 page=0x5 offset=0xF6 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x6F6 page=0x6 offset=0xF6 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x7F6 page=0x7 offset=0xF6 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x8F6 page=0x8 offset=0xF6 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x9F6 page=0x9 offset=0xF6 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xAF6 page=0xA offset=0xF6 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xBF6 page=0xB offset=0xF6 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xCF6 page=0xC offset=0xF6 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xDF6 page=0xD offset=0xF6 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xEF6 page=0xE offset=0xF6 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xFF6 page=0xF offset=0xF6 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- ... 2028 more word writes omitted
|
||||
|
||||
Factory Diffs:
|
||||
- current EEPROM image matches ROM factory/default image
|
||||
|
||||
F400 Shadow Diffs:
|
||||
- F400-F4FF shadow matches ROM factory words or no ROM factory baseline was supplied
|
||||
300
build/emulator-eeprom-loaded-boot.json
Normal file
300
build/emulator-eeprom-loaded-boot.json
Normal file
@@ -0,0 +1,300 @@
|
||||
{
|
||||
"factory_diffs": [],
|
||||
"kind": "emulator_eeprom_snapshot",
|
||||
"records": [
|
||||
{
|
||||
"address": 0,
|
||||
"address_hex": "0x000",
|
||||
"ascii": "..ko....",
|
||||
"bytes_hex": "00 00 6B 6F FE 00 00 00",
|
||||
"is_blank_spaces": false,
|
||||
"page": 0,
|
||||
"page_hex": "0x0",
|
||||
"range_hex": "0x000-0x007",
|
||||
"words_hex": [
|
||||
"0x0000",
|
||||
"0x6B6F",
|
||||
"0xFE00",
|
||||
"0x0000"
|
||||
]
|
||||
},
|
||||
{
|
||||
"address": 256,
|
||||
"address_hex": "0x100",
|
||||
"ascii": " ",
|
||||
"bytes_hex": "20 20 20 20 20 20 20 20",
|
||||
"is_blank_spaces": true,
|
||||
"page": 1,
|
||||
"page_hex": "0x1",
|
||||
"range_hex": "0x100-0x107",
|
||||
"words_hex": [
|
||||
"0x2020",
|
||||
"0x2020",
|
||||
"0x2020",
|
||||
"0x2020"
|
||||
]
|
||||
},
|
||||
{
|
||||
"address": 512,
|
||||
"address_hex": "0x200",
|
||||
"ascii": " ",
|
||||
"bytes_hex": "20 20 20 20 20 20 20 20",
|
||||
"is_blank_spaces": true,
|
||||
"page": 2,
|
||||
"page_hex": "0x2",
|
||||
"range_hex": "0x200-0x207",
|
||||
"words_hex": [
|
||||
"0x2020",
|
||||
"0x2020",
|
||||
"0x2020",
|
||||
"0x2020"
|
||||
]
|
||||
},
|
||||
{
|
||||
"address": 768,
|
||||
"address_hex": "0x300",
|
||||
"ascii": " ",
|
||||
"bytes_hex": "20 20 20 20 20 20 20 20",
|
||||
"is_blank_spaces": true,
|
||||
"page": 3,
|
||||
"page_hex": "0x3",
|
||||
"range_hex": "0x300-0x307",
|
||||
"words_hex": [
|
||||
"0x2020",
|
||||
"0x2020",
|
||||
"0x2020",
|
||||
"0x2020"
|
||||
]
|
||||
},
|
||||
{
|
||||
"address": 1024,
|
||||
"address_hex": "0x400",
|
||||
"ascii": " ",
|
||||
"bytes_hex": "20 20 20 20 20 20 20 20",
|
||||
"is_blank_spaces": true,
|
||||
"page": 4,
|
||||
"page_hex": "0x4",
|
||||
"range_hex": "0x400-0x407",
|
||||
"words_hex": [
|
||||
"0x2020",
|
||||
"0x2020",
|
||||
"0x2020",
|
||||
"0x2020"
|
||||
]
|
||||
},
|
||||
{
|
||||
"address": 1280,
|
||||
"address_hex": "0x500",
|
||||
"ascii": " ",
|
||||
"bytes_hex": "20 20 20 20 20 20 20 20",
|
||||
"is_blank_spaces": true,
|
||||
"page": 5,
|
||||
"page_hex": "0x5",
|
||||
"range_hex": "0x500-0x507",
|
||||
"words_hex": [
|
||||
"0x2020",
|
||||
"0x2020",
|
||||
"0x2020",
|
||||
"0x2020"
|
||||
]
|
||||
},
|
||||
{
|
||||
"address": 1536,
|
||||
"address_hex": "0x600",
|
||||
"ascii": " ",
|
||||
"bytes_hex": "20 20 20 20 20 20 20 20",
|
||||
"is_blank_spaces": true,
|
||||
"page": 6,
|
||||
"page_hex": "0x6",
|
||||
"range_hex": "0x600-0x607",
|
||||
"words_hex": [
|
||||
"0x2020",
|
||||
"0x2020",
|
||||
"0x2020",
|
||||
"0x2020"
|
||||
]
|
||||
},
|
||||
{
|
||||
"address": 1792,
|
||||
"address_hex": "0x700",
|
||||
"ascii": " ",
|
||||
"bytes_hex": "20 20 20 20 20 20 20 20",
|
||||
"is_blank_spaces": true,
|
||||
"page": 7,
|
||||
"page_hex": "0x7",
|
||||
"range_hex": "0x700-0x707",
|
||||
"words_hex": [
|
||||
"0x2020",
|
||||
"0x2020",
|
||||
"0x2020",
|
||||
"0x2020"
|
||||
]
|
||||
},
|
||||
{
|
||||
"address": 2048,
|
||||
"address_hex": "0x800",
|
||||
"ascii": " ",
|
||||
"bytes_hex": "20 20 20 20 20 20 20 20",
|
||||
"is_blank_spaces": true,
|
||||
"page": 8,
|
||||
"page_hex": "0x8",
|
||||
"range_hex": "0x800-0x807",
|
||||
"words_hex": [
|
||||
"0x2020",
|
||||
"0x2020",
|
||||
"0x2020",
|
||||
"0x2020"
|
||||
]
|
||||
},
|
||||
{
|
||||
"address": 2304,
|
||||
"address_hex": "0x900",
|
||||
"ascii": " ",
|
||||
"bytes_hex": "20 20 20 20 20 20 20 20",
|
||||
"is_blank_spaces": true,
|
||||
"page": 9,
|
||||
"page_hex": "0x9",
|
||||
"range_hex": "0x900-0x907",
|
||||
"words_hex": [
|
||||
"0x2020",
|
||||
"0x2020",
|
||||
"0x2020",
|
||||
"0x2020"
|
||||
]
|
||||
},
|
||||
{
|
||||
"address": 2560,
|
||||
"address_hex": "0xA00",
|
||||
"ascii": " ",
|
||||
"bytes_hex": "20 20 20 20 20 20 20 20",
|
||||
"is_blank_spaces": true,
|
||||
"page": 10,
|
||||
"page_hex": "0xA",
|
||||
"range_hex": "0xA00-0xA07",
|
||||
"words_hex": [
|
||||
"0x2020",
|
||||
"0x2020",
|
||||
"0x2020",
|
||||
"0x2020"
|
||||
]
|
||||
},
|
||||
{
|
||||
"address": 2816,
|
||||
"address_hex": "0xB00",
|
||||
"ascii": " ",
|
||||
"bytes_hex": "20 20 20 20 20 20 20 20",
|
||||
"is_blank_spaces": true,
|
||||
"page": 11,
|
||||
"page_hex": "0xB",
|
||||
"range_hex": "0xB00-0xB07",
|
||||
"words_hex": [
|
||||
"0x2020",
|
||||
"0x2020",
|
||||
"0x2020",
|
||||
"0x2020"
|
||||
]
|
||||
},
|
||||
{
|
||||
"address": 3072,
|
||||
"address_hex": "0xC00",
|
||||
"ascii": " ",
|
||||
"bytes_hex": "20 20 20 20 20 20 20 20",
|
||||
"is_blank_spaces": true,
|
||||
"page": 12,
|
||||
"page_hex": "0xC",
|
||||
"range_hex": "0xC00-0xC07",
|
||||
"words_hex": [
|
||||
"0x2020",
|
||||
"0x2020",
|
||||
"0x2020",
|
||||
"0x2020"
|
||||
]
|
||||
},
|
||||
{
|
||||
"address": 3328,
|
||||
"address_hex": "0xD00",
|
||||
"ascii": " ",
|
||||
"bytes_hex": "20 20 20 20 20 20 20 20",
|
||||
"is_blank_spaces": true,
|
||||
"page": 13,
|
||||
"page_hex": "0xD",
|
||||
"range_hex": "0xD00-0xD07",
|
||||
"words_hex": [
|
||||
"0x2020",
|
||||
"0x2020",
|
||||
"0x2020",
|
||||
"0x2020"
|
||||
]
|
||||
},
|
||||
{
|
||||
"address": 3584,
|
||||
"address_hex": "0xE00",
|
||||
"ascii": " ",
|
||||
"bytes_hex": "20 20 20 20 20 20 20 20",
|
||||
"is_blank_spaces": true,
|
||||
"page": 14,
|
||||
"page_hex": "0xE",
|
||||
"range_hex": "0xE00-0xE07",
|
||||
"words_hex": [
|
||||
"0x2020",
|
||||
"0x2020",
|
||||
"0x2020",
|
||||
"0x2020"
|
||||
]
|
||||
},
|
||||
{
|
||||
"address": 3840,
|
||||
"address_hex": "0xF00",
|
||||
"ascii": " ",
|
||||
"bytes_hex": "20 20 20 20 20 20 20 20",
|
||||
"is_blank_spaces": true,
|
||||
"page": 15,
|
||||
"page_hex": "0xF",
|
||||
"range_hex": "0xF00-0xF07",
|
||||
"words_hex": [
|
||||
"0x2020",
|
||||
"0x2020",
|
||||
"0x2020",
|
||||
"0x2020"
|
||||
]
|
||||
}
|
||||
],
|
||||
"shadow_f400": {
|
||||
"diff_count": 1,
|
||||
"diffs": [
|
||||
{
|
||||
"actual_word": 21760,
|
||||
"actual_word_hex": "0x5500",
|
||||
"address": 62634,
|
||||
"address_hex": "H'F4AA",
|
||||
"aligned_offset": 170,
|
||||
"aligned_offset_hex": "0xAA",
|
||||
"expected_word": 32768,
|
||||
"expected_word_hex": "0x8000",
|
||||
"mapped_selectors": [
|
||||
274
|
||||
],
|
||||
"mapped_selectors_hex": [
|
||||
"0x112"
|
||||
],
|
||||
"offset": 170,
|
||||
"offset_hex": "0xAA",
|
||||
"page": 0,
|
||||
"page_hex": "0x0",
|
||||
"record_byte": null,
|
||||
"role": "factory_shadow_offset"
|
||||
}
|
||||
]
|
||||
},
|
||||
"summary": {
|
||||
"factory_diff_word_count": 0,
|
||||
"logical_size": 4096,
|
||||
"logical_size_hex": "0x1000",
|
||||
"record_count": 16,
|
||||
"sha256": "4bed7704e1ea085487ca325c43bd60da75d37b6ae6f8292544e069a8825c64c6",
|
||||
"write_byte_count": 0,
|
||||
"write_word_count": 0
|
||||
},
|
||||
"write_events": [],
|
||||
"write_word_events": []
|
||||
}
|
||||
31
build/emulator-eeprom-loaded-boot.txt
Normal file
31
build/emulator-eeprom-loaded-boot.txt
Normal file
@@ -0,0 +1,31 @@
|
||||
Emulator EEPROM Snapshot
|
||||
|
||||
size=0x1000 sha256=4bed7704e1ea085487ca325c43bd60da75d37b6ae6f8292544e069a8825c64c6
|
||||
writes: bytes=0 words=0 factory_diff_words=0
|
||||
|
||||
Persistent Records:
|
||||
- page 0x0 EEPROM 0x000-0x007 bytes=00 00 6B 6F FE 00 00 00 text='..ko....'
|
||||
- page 0x1 EEPROM 0x100-0x107 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x2 EEPROM 0x200-0x207 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x3 EEPROM 0x300-0x307 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x4 EEPROM 0x400-0x407 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x5 EEPROM 0x500-0x507 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x6 EEPROM 0x600-0x607 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x7 EEPROM 0x700-0x707 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x8 EEPROM 0x800-0x807 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x9 EEPROM 0x900-0x907 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0xA EEPROM 0xA00-0xA07 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0xB EEPROM 0xB00-0xB07 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0xC EEPROM 0xC00-0xC07 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0xD EEPROM 0xD00-0xD07 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0xE EEPROM 0xE00-0xE07 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0xF EEPROM 0xF00-0xF07 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
|
||||
EEPROM Word Writes:
|
||||
- none since EEPROM setup/load
|
||||
|
||||
Factory Diffs:
|
||||
- current EEPROM image matches ROM factory/default image
|
||||
|
||||
F400 Shadow Diffs:
|
||||
- H'F4AA offset=0xAA expected=0x8000 actual=0x5500 (factory_shadow_offset; selectors=0x112)
|
||||
191
build/pin50-p77-high-blank-eeprom.txt
Normal file
191
build/pin50-p77-high-blank-eeprom.txt
Normal file
@@ -0,0 +1,191 @@
|
||||
Emulator EEPROM Snapshot
|
||||
|
||||
size=0x1000 sha256=f47a8ec3e9aff2318d896942282ad4fe37d6391c82914f54a5da8a37de1300c6
|
||||
writes: bytes=0 words=0 factory_diff_words=2048
|
||||
|
||||
Persistent Records:
|
||||
- page 0x0 EEPROM 0x000-0x007 bytes=FF FF FF FF FF FF FF FF text='........'
|
||||
- page 0x1 EEPROM 0x100-0x107 bytes=FF FF FF FF FF FF FF FF text='........'
|
||||
- page 0x2 EEPROM 0x200-0x207 bytes=FF FF FF FF FF FF FF FF text='........'
|
||||
- page 0x3 EEPROM 0x300-0x307 bytes=FF FF FF FF FF FF FF FF text='........'
|
||||
- page 0x4 EEPROM 0x400-0x407 bytes=FF FF FF FF FF FF FF FF text='........'
|
||||
- page 0x5 EEPROM 0x500-0x507 bytes=FF FF FF FF FF FF FF FF text='........'
|
||||
- page 0x6 EEPROM 0x600-0x607 bytes=FF FF FF FF FF FF FF FF text='........'
|
||||
- page 0x7 EEPROM 0x700-0x707 bytes=FF FF FF FF FF FF FF FF text='........'
|
||||
- page 0x8 EEPROM 0x800-0x807 bytes=FF FF FF FF FF FF FF FF text='........'
|
||||
- page 0x9 EEPROM 0x900-0x907 bytes=FF FF FF FF FF FF FF FF text='........'
|
||||
- page 0xA EEPROM 0xA00-0xA07 bytes=FF FF FF FF FF FF FF FF text='........'
|
||||
- page 0xB EEPROM 0xB00-0xB07 bytes=FF FF FF FF FF FF FF FF text='........'
|
||||
- page 0xC EEPROM 0xC00-0xC07 bytes=FF FF FF FF FF FF FF FF text='........'
|
||||
- page 0xD EEPROM 0xD00-0xD07 bytes=FF FF FF FF FF FF FF FF text='........'
|
||||
- page 0xE EEPROM 0xE00-0xE07 bytes=FF FF FF FF FF FF FF FF text='........'
|
||||
- page 0xF EEPROM 0xF00-0xF07 bytes=FF FF FF FF FF FF FF FF text='........'
|
||||
|
||||
EEPROM Word Writes:
|
||||
- none since EEPROM setup/load
|
||||
|
||||
Factory Diffs:
|
||||
- 0x000 page=0x0 offset=0x00 expected=0x0000 actual=0xFFFF (record_header_or_label)
|
||||
- 0x002 page=0x0 offset=0x02 expected=0x6B6F actual=0xFFFF (record_header_or_label)
|
||||
- 0x004 page=0x0 offset=0x04 expected=0xFE00 actual=0xFFFF (record_header_or_label)
|
||||
- 0x006 page=0x0 offset=0x06 expected=0x0000 actual=0xFFFF (record_header_or_label)
|
||||
- 0x008 page=0x0 offset=0x08 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x004)
|
||||
- 0x00A page=0x0 offset=0x0A expected=0x0000 actual=0xFFFF (factory_shadow_offset; selectors=0x012)
|
||||
- 0x00C page=0x0 offset=0x0C expected=0x0000 actual=0xFFFF (factory_shadow_offset; selectors=0x013)
|
||||
- 0x00E page=0x0 offset=0x0E expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x017)
|
||||
- 0x010 page=0x0 offset=0x10 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x018)
|
||||
- 0x012 page=0x0 offset=0x12 expected=0x0808 actual=0xFFFF (factory_shadow_offset; selectors=0x01A)
|
||||
- 0x014 page=0x0 offset=0x14 expected=0x0000 actual=0xFFFF (factory_shadow_offset; selectors=0x01F)
|
||||
- 0x016 page=0x0 offset=0x16 expected=0x0000 actual=0xFFFF (factory_shadow_offset; selectors=0x020)
|
||||
- 0x018 page=0x0 offset=0x18 expected=0x0000 actual=0xFFFF (factory_shadow_offset; selectors=0x023)
|
||||
- 0x01A page=0x0 offset=0x1A expected=0x0000 actual=0xFFFF (factory_shadow_offset; selectors=0x037)
|
||||
- 0x01C page=0x0 offset=0x1C expected=0x0000 actual=0xFFFF (factory_shadow_offset; selectors=0x038)
|
||||
- 0x01E page=0x0 offset=0x1E expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x080)
|
||||
- 0x020 page=0x0 offset=0x20 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x081)
|
||||
- 0x022 page=0x0 offset=0x22 expected=0x0020 actual=0xFFFF (factory_shadow_offset; selectors=0x083)
|
||||
- 0x024 page=0x0 offset=0x24 expected=0x0000 actual=0xFFFF (factory_shadow_offset; selectors=0x088)
|
||||
- 0x026 page=0x0 offset=0x26 expected=0x0400 actual=0xFFFF (factory_shadow_offset; selectors=0x089)
|
||||
- 0x028 page=0x0 offset=0x28 expected=0x0800 actual=0xFFFF (factory_shadow_offset; selectors=0x08B)
|
||||
- 0x02A page=0x0 offset=0x2A expected=0x0040 actual=0xFFFF (factory_shadow_offset; selectors=0x08D)
|
||||
- 0x02C page=0x0 offset=0x2C expected=0x0000 actual=0xFFFF (factory_shadow_offset; selectors=0x08F)
|
||||
- 0x02E page=0x0 offset=0x2E expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x091)
|
||||
- 0x030 page=0x0 offset=0x30 expected=0xFF80 actual=0xFFFF (factory_shadow_offset; selectors=0x092)
|
||||
- 0x032 page=0x0 offset=0x32 expected=0x4040 actual=0xFFFF (factory_shadow_offset; selectors=0x093)
|
||||
- 0x034 page=0x0 offset=0x34 expected=0x0000 actual=0xFFFF (factory_shadow_offset; selectors=0x095)
|
||||
- 0x036 page=0x0 offset=0x36 expected=0x0000 actual=0xFFFF (factory_shadow_offset; selectors=0x098)
|
||||
- 0x038 page=0x0 offset=0x38 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x09A)
|
||||
- 0x03A page=0x0 offset=0x3A expected=0x0000 actual=0xFFFF (factory_shadow_offset; selectors=0x09D)
|
||||
- 0x03C page=0x0 offset=0x3C expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x09E)
|
||||
- 0x03E page=0x0 offset=0x3E expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x09F)
|
||||
- 0x040 page=0x0 offset=0x40 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0A3)
|
||||
- 0x042 page=0x0 offset=0x42 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0A4)
|
||||
- 0x044 page=0x0 offset=0x44 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0A5)
|
||||
- 0x046 page=0x0 offset=0x46 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0A6)
|
||||
- 0x048 page=0x0 offset=0x48 expected=0xF000 actual=0xFFFF (factory_shadow_offset; selectors=0x0A7)
|
||||
- 0x04A page=0x0 offset=0x4A expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0A9)
|
||||
- 0x04C page=0x0 offset=0x4C expected=0x2000 actual=0xFFFF (factory_shadow_offset; selectors=0x0AA)
|
||||
- 0x04E page=0x0 offset=0x4E expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0AC)
|
||||
- 0x050 page=0x0 offset=0x50 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0AD)
|
||||
- 0x052 page=0x0 offset=0x52 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0AE)
|
||||
- 0x054 page=0x0 offset=0x54 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0AF)
|
||||
- 0x056 page=0x0 offset=0x56 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0B0)
|
||||
- 0x058 page=0x0 offset=0x58 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0B2)
|
||||
- 0x05A page=0x0 offset=0x5A expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0B3)
|
||||
- 0x05C page=0x0 offset=0x5C expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0B4)
|
||||
- 0x05E page=0x0 offset=0x5E expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0B6)
|
||||
- 0x060 page=0x0 offset=0x60 expected=0xF800 actual=0xFFFF (factory_shadow_offset; selectors=0x0B7)
|
||||
- 0x062 page=0x0 offset=0x62 expected=0x4000 actual=0xFFFF (factory_shadow_offset; selectors=0x0B9)
|
||||
- 0x064 page=0x0 offset=0x64 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0BC)
|
||||
- 0x066 page=0x0 offset=0x66 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0BD)
|
||||
- 0x068 page=0x0 offset=0x68 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0C0)
|
||||
- 0x06A page=0x0 offset=0x6A expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0C1)
|
||||
- 0x06C page=0x0 offset=0x6C expected=0x4000 actual=0xFFFF (factory_shadow_offset; selectors=0x0C3)
|
||||
- 0x06E page=0x0 offset=0x6E expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0C4)
|
||||
- 0x070 page=0x0 offset=0x70 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0C5)
|
||||
- 0x072 page=0x0 offset=0x72 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0C6)
|
||||
- 0x074 page=0x0 offset=0x74 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0C7)
|
||||
- 0x076 page=0x0 offset=0x76 expected=0x0000 actual=0xFFFF (factory_shadow_offset; selectors=0x0C8)
|
||||
- 0x078 page=0x0 offset=0x78 expected=0x0000 actual=0xFFFF (factory_shadow_offset; selectors=0x0C9)
|
||||
- 0x07A page=0x0 offset=0x7A expected=0x0000 actual=0xFFFF (factory_shadow_offset; selectors=0x0CA)
|
||||
- 0x07C page=0x0 offset=0x7C expected=0x0000 actual=0xFFFF (factory_shadow_offset; selectors=0x0CB)
|
||||
- 0x07E page=0x0 offset=0x7E expected=0x0000 actual=0xFFFF (factory_shadow_offset; selectors=0x0CC)
|
||||
- 0x080 page=0x0 offset=0x80 expected=0x0000 actual=0xFFFF (factory_shadow_offset; selectors=0x0CD)
|
||||
- 0x082 page=0x0 offset=0x82 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0D4)
|
||||
- 0x084 page=0x0 offset=0x84 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0D5)
|
||||
- 0x086 page=0x0 offset=0x86 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0D6)
|
||||
- 0x088 page=0x0 offset=0x88 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0D7)
|
||||
- 0x08A page=0x0 offset=0x8A expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0D8)
|
||||
- 0x08C page=0x0 offset=0x8C expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0D9)
|
||||
- 0x08E page=0x0 offset=0x8E expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0DA)
|
||||
- 0x090 page=0x0 offset=0x90 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0F6)
|
||||
- 0x092 page=0x0 offset=0x92 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0F9)
|
||||
- 0x094 page=0x0 offset=0x94 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0FA)
|
||||
- 0x096 page=0x0 offset=0x96 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0FB)
|
||||
- 0x098 page=0x0 offset=0x98 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0FC)
|
||||
- 0x09A page=0x0 offset=0x9A expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0FD)
|
||||
- 0x09C page=0x0 offset=0x9C expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0FE)
|
||||
- 0x09E page=0x0 offset=0x9E expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0FF)
|
||||
- ... 1968 more factory diffs omitted
|
||||
|
||||
F400 Shadow Diffs:
|
||||
- H'F400 offset=0x00 expected=0x0000 actual=0xFFFF (record_header_or_label)
|
||||
- H'F402 offset=0x02 expected=0x6B6F actual=0xFFFF (record_header_or_label)
|
||||
- H'F404 offset=0x04 expected=0xFE00 actual=0xFFFF (record_header_or_label)
|
||||
- H'F406 offset=0x06 expected=0x0000 actual=0xFFFF (record_header_or_label)
|
||||
- H'F408 offset=0x08 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x004)
|
||||
- H'F40A offset=0x0A expected=0x0000 actual=0xFFFF (factory_shadow_offset; selectors=0x012)
|
||||
- H'F40C offset=0x0C expected=0x0000 actual=0xFFFF (factory_shadow_offset; selectors=0x013)
|
||||
- H'F40E offset=0x0E expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x017)
|
||||
- H'F410 offset=0x10 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x018)
|
||||
- H'F412 offset=0x12 expected=0x0808 actual=0xFFFF (factory_shadow_offset; selectors=0x01A)
|
||||
- H'F414 offset=0x14 expected=0x0000 actual=0xFFFF (factory_shadow_offset; selectors=0x01F)
|
||||
- H'F416 offset=0x16 expected=0x0000 actual=0xFFFF (factory_shadow_offset; selectors=0x020)
|
||||
- H'F418 offset=0x18 expected=0x0000 actual=0xFFFF (factory_shadow_offset; selectors=0x023)
|
||||
- H'F41A offset=0x1A expected=0x0000 actual=0xFFFF (factory_shadow_offset; selectors=0x037)
|
||||
- H'F41C offset=0x1C expected=0x0000 actual=0xFFFF (factory_shadow_offset; selectors=0x038)
|
||||
- H'F41E offset=0x1E expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x080)
|
||||
- H'F420 offset=0x20 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x081)
|
||||
- H'F422 offset=0x22 expected=0x0020 actual=0xFFFF (factory_shadow_offset; selectors=0x083)
|
||||
- H'F424 offset=0x24 expected=0x0000 actual=0xFFFF (factory_shadow_offset; selectors=0x088)
|
||||
- H'F426 offset=0x26 expected=0x0400 actual=0xFFFF (factory_shadow_offset; selectors=0x089)
|
||||
- H'F428 offset=0x28 expected=0x0800 actual=0xFFFF (factory_shadow_offset; selectors=0x08B)
|
||||
- H'F42A offset=0x2A expected=0x0040 actual=0xFFFF (factory_shadow_offset; selectors=0x08D)
|
||||
- H'F42C offset=0x2C expected=0x0000 actual=0xFFFF (factory_shadow_offset; selectors=0x08F)
|
||||
- H'F42E offset=0x2E expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x091)
|
||||
- H'F430 offset=0x30 expected=0xFF80 actual=0xFFFF (factory_shadow_offset; selectors=0x092)
|
||||
- H'F432 offset=0x32 expected=0x4040 actual=0xFFFF (factory_shadow_offset; selectors=0x093)
|
||||
- H'F434 offset=0x34 expected=0x0000 actual=0xFFFF (factory_shadow_offset; selectors=0x095)
|
||||
- H'F436 offset=0x36 expected=0x0000 actual=0xFFFF (factory_shadow_offset; selectors=0x098)
|
||||
- H'F438 offset=0x38 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x09A)
|
||||
- H'F43A offset=0x3A expected=0x0000 actual=0xFFFF (factory_shadow_offset; selectors=0x09D)
|
||||
- H'F43C offset=0x3C expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x09E)
|
||||
- H'F43E offset=0x3E expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x09F)
|
||||
- H'F440 offset=0x40 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0A3)
|
||||
- H'F442 offset=0x42 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0A4)
|
||||
- H'F444 offset=0x44 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0A5)
|
||||
- H'F446 offset=0x46 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0A6)
|
||||
- H'F448 offset=0x48 expected=0xF000 actual=0xFFFF (factory_shadow_offset; selectors=0x0A7)
|
||||
- H'F44A offset=0x4A expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0A9)
|
||||
- H'F44C offset=0x4C expected=0x2000 actual=0xFFFF (factory_shadow_offset; selectors=0x0AA)
|
||||
- H'F44E offset=0x4E expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0AC)
|
||||
- H'F450 offset=0x50 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0AD)
|
||||
- H'F452 offset=0x52 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0AE)
|
||||
- H'F454 offset=0x54 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0AF)
|
||||
- H'F456 offset=0x56 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0B0)
|
||||
- H'F458 offset=0x58 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0B2)
|
||||
- H'F45A offset=0x5A expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0B3)
|
||||
- H'F45C offset=0x5C expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0B4)
|
||||
- H'F45E offset=0x5E expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0B6)
|
||||
- H'F460 offset=0x60 expected=0xF800 actual=0xFFFF (factory_shadow_offset; selectors=0x0B7)
|
||||
- H'F462 offset=0x62 expected=0x4000 actual=0xFFFF (factory_shadow_offset; selectors=0x0B9)
|
||||
- H'F464 offset=0x64 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0BC)
|
||||
- H'F466 offset=0x66 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0BD)
|
||||
- H'F468 offset=0x68 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0C0)
|
||||
- H'F46A offset=0x6A expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0C1)
|
||||
- H'F46C offset=0x6C expected=0x4000 actual=0xFFFF (factory_shadow_offset; selectors=0x0C3)
|
||||
- H'F46E offset=0x6E expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0C4)
|
||||
- H'F470 offset=0x70 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0C5)
|
||||
- H'F472 offset=0x72 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0C6)
|
||||
- H'F474 offset=0x74 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0C7)
|
||||
- H'F476 offset=0x76 expected=0x0000 actual=0xFFFF (factory_shadow_offset; selectors=0x0C8)
|
||||
- H'F478 offset=0x78 expected=0x0000 actual=0xFFFF (factory_shadow_offset; selectors=0x0C9)
|
||||
- H'F47A offset=0x7A expected=0x0000 actual=0xFFFF (factory_shadow_offset; selectors=0x0CA)
|
||||
- H'F47C offset=0x7C expected=0x0000 actual=0xFFFF (factory_shadow_offset; selectors=0x0CB)
|
||||
- H'F47E offset=0x7E expected=0x0000 actual=0xFFFF (factory_shadow_offset; selectors=0x0CC)
|
||||
- H'F480 offset=0x80 expected=0x0000 actual=0xFFFF (factory_shadow_offset; selectors=0x0CD)
|
||||
- H'F482 offset=0x82 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0D4)
|
||||
- H'F484 offset=0x84 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0D5)
|
||||
- H'F486 offset=0x86 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0D6)
|
||||
- H'F488 offset=0x88 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0D7)
|
||||
- H'F48A offset=0x8A expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0D8)
|
||||
- H'F48C offset=0x8C expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0D9)
|
||||
- H'F48E offset=0x8E expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0DA)
|
||||
- H'F490 offset=0x90 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0F6)
|
||||
- H'F492 offset=0x92 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0F9)
|
||||
- H'F494 offset=0x94 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0FA)
|
||||
- H'F496 offset=0x96 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0FB)
|
||||
- H'F498 offset=0x98 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0FC)
|
||||
- H'F49A offset=0x9A expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0FD)
|
||||
- H'F49C offset=0x9C expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0FE)
|
||||
- H'F49E offset=0x9E expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0FF)
|
||||
- ... 47 more shadow diffs omitted
|
||||
111
build/pin50-p77-high-fast-blank-eeprom.txt
Normal file
111
build/pin50-p77-high-fast-blank-eeprom.txt
Normal file
@@ -0,0 +1,111 @@
|
||||
Emulator EEPROM Snapshot
|
||||
|
||||
size=0x1000 sha256=4bed7704e1ea085487ca325c43bd60da75d37b6ae6f8292544e069a8825c64c6
|
||||
writes: bytes=4216 words=2108 factory_diff_words=0
|
||||
|
||||
Persistent Records:
|
||||
- page 0x0 EEPROM 0x000-0x007 bytes=00 00 6B 6F FE 00 00 00 text='..ko....'
|
||||
- page 0x1 EEPROM 0x100-0x107 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x2 EEPROM 0x200-0x207 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x3 EEPROM 0x300-0x307 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x4 EEPROM 0x400-0x407 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x5 EEPROM 0x500-0x507 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x6 EEPROM 0x600-0x607 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x7 EEPROM 0x700-0x707 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x8 EEPROM 0x800-0x807 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x9 EEPROM 0x900-0x907 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0xA EEPROM 0xA00-0xA07 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0xB EEPROM 0xB00-0xB07 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0xC EEPROM 0xC00-0xC07 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0xD EEPROM 0xD00-0xD07 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0xE EEPROM 0xE00-0xE07 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0xF EEPROM 0xF00-0xF07 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
|
||||
EEPROM Word Writes:
|
||||
- 0x0FE page=0x0 offset=0xFE 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x1FE page=0x1 offset=0xFE 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x2FE page=0x2 offset=0xFE 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x3FE page=0x3 offset=0xFE 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x4FE page=0x4 offset=0xFE 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x5FE page=0x5 offset=0xFE 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x6FE page=0x6 offset=0xFE 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x7FE page=0x7 offset=0xFE 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x8FE page=0x8 offset=0xFE 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x9FE page=0x9 offset=0xFE 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xAFE page=0xA offset=0xFE 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xBFE page=0xB offset=0xFE 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xCFE page=0xC offset=0xFE 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xDFE page=0xD offset=0xFE 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xEFE page=0xE offset=0xFE 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xFFE page=0xF offset=0xFE 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x0FC page=0x0 offset=0xFC 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x1FC page=0x1 offset=0xFC 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x2FC page=0x2 offset=0xFC 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x3FC page=0x3 offset=0xFC 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x4FC page=0x4 offset=0xFC 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x5FC page=0x5 offset=0xFC 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x6FC page=0x6 offset=0xFC 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x7FC page=0x7 offset=0xFC 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x8FC page=0x8 offset=0xFC 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x9FC page=0x9 offset=0xFC 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xAFC page=0xA offset=0xFC 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xBFC page=0xB offset=0xFC 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xCFC page=0xC offset=0xFC 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xDFC page=0xD offset=0xFC 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xEFC page=0xE offset=0xFC 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xFFC page=0xF offset=0xFC 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x0FA page=0x0 offset=0xFA 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x1FA page=0x1 offset=0xFA 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x2FA page=0x2 offset=0xFA 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x3FA page=0x3 offset=0xFA 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x4FA page=0x4 offset=0xFA 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x5FA page=0x5 offset=0xFA 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x6FA page=0x6 offset=0xFA 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x7FA page=0x7 offset=0xFA 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x8FA page=0x8 offset=0xFA 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x9FA page=0x9 offset=0xFA 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xAFA page=0xA offset=0xFA 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xBFA page=0xB offset=0xFA 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xCFA page=0xC offset=0xFA 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xDFA page=0xD offset=0xFA 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xEFA page=0xE offset=0xFA 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xFFA page=0xF offset=0xFA 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x0F8 page=0x0 offset=0xF8 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x1F8 page=0x1 offset=0xF8 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x2F8 page=0x2 offset=0xF8 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x3F8 page=0x3 offset=0xF8 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x4F8 page=0x4 offset=0xF8 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x5F8 page=0x5 offset=0xF8 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x6F8 page=0x6 offset=0xF8 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x7F8 page=0x7 offset=0xF8 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x8F8 page=0x8 offset=0xF8 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x9F8 page=0x9 offset=0xF8 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xAF8 page=0xA offset=0xF8 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xBF8 page=0xB offset=0xF8 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xCF8 page=0xC offset=0xF8 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xDF8 page=0xD offset=0xF8 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xEF8 page=0xE offset=0xF8 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xFF8 page=0xF offset=0xF8 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x0F6 page=0x0 offset=0xF6 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x1F6 page=0x1 offset=0xF6 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x2F6 page=0x2 offset=0xF6 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x3F6 page=0x3 offset=0xF6 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x4F6 page=0x4 offset=0xF6 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x5F6 page=0x5 offset=0xF6 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x6F6 page=0x6 offset=0xF6 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x7F6 page=0x7 offset=0xF6 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x8F6 page=0x8 offset=0xF6 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x9F6 page=0x9 offset=0xF6 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xAF6 page=0xA offset=0xF6 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xBF6 page=0xB offset=0xF6 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xCF6 page=0xC offset=0xF6 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xDF6 page=0xD offset=0xF6 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xEF6 page=0xE offset=0xF6 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xFF6 page=0xF offset=0xF6 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- ... 2028 more word writes omitted
|
||||
|
||||
Factory Diffs:
|
||||
- current EEPROM image matches ROM factory/default image
|
||||
|
||||
F400 Shadow Diffs:
|
||||
- F400-F4FF shadow matches ROM factory words or no ROM factory baseline was supplied
|
||||
31
build/pin50-p77-high-fast-factory-eeprom.txt
Normal file
31
build/pin50-p77-high-fast-factory-eeprom.txt
Normal file
@@ -0,0 +1,31 @@
|
||||
Emulator EEPROM Snapshot
|
||||
|
||||
size=0x1000 sha256=4bed7704e1ea085487ca325c43bd60da75d37b6ae6f8292544e069a8825c64c6
|
||||
writes: bytes=0 words=0 factory_diff_words=0
|
||||
|
||||
Persistent Records:
|
||||
- page 0x0 EEPROM 0x000-0x007 bytes=00 00 6B 6F FE 00 00 00 text='..ko....'
|
||||
- page 0x1 EEPROM 0x100-0x107 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x2 EEPROM 0x200-0x207 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x3 EEPROM 0x300-0x307 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x4 EEPROM 0x400-0x407 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x5 EEPROM 0x500-0x507 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x6 EEPROM 0x600-0x607 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x7 EEPROM 0x700-0x707 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x8 EEPROM 0x800-0x807 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x9 EEPROM 0x900-0x907 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0xA EEPROM 0xA00-0xA07 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0xB EEPROM 0xB00-0xB07 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0xC EEPROM 0xC00-0xC07 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0xD EEPROM 0xD00-0xD07 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0xE EEPROM 0xE00-0xE07 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0xF EEPROM 0xF00-0xF07 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
|
||||
EEPROM Word Writes:
|
||||
- none since EEPROM setup/load
|
||||
|
||||
Factory Diffs:
|
||||
- current EEPROM image matches ROM factory/default image
|
||||
|
||||
F400 Shadow Diffs:
|
||||
- H'F4AA offset=0xAA expected=0x8000 actual=0x5500 (factory_shadow_offset; selectors=0x112)
|
||||
191
build/pin50-p77-low-blank-eeprom.txt
Normal file
191
build/pin50-p77-low-blank-eeprom.txt
Normal file
@@ -0,0 +1,191 @@
|
||||
Emulator EEPROM Snapshot
|
||||
|
||||
size=0x1000 sha256=f47a8ec3e9aff2318d896942282ad4fe37d6391c82914f54a5da8a37de1300c6
|
||||
writes: bytes=0 words=0 factory_diff_words=2048
|
||||
|
||||
Persistent Records:
|
||||
- page 0x0 EEPROM 0x000-0x007 bytes=FF FF FF FF FF FF FF FF text='........'
|
||||
- page 0x1 EEPROM 0x100-0x107 bytes=FF FF FF FF FF FF FF FF text='........'
|
||||
- page 0x2 EEPROM 0x200-0x207 bytes=FF FF FF FF FF FF FF FF text='........'
|
||||
- page 0x3 EEPROM 0x300-0x307 bytes=FF FF FF FF FF FF FF FF text='........'
|
||||
- page 0x4 EEPROM 0x400-0x407 bytes=FF FF FF FF FF FF FF FF text='........'
|
||||
- page 0x5 EEPROM 0x500-0x507 bytes=FF FF FF FF FF FF FF FF text='........'
|
||||
- page 0x6 EEPROM 0x600-0x607 bytes=FF FF FF FF FF FF FF FF text='........'
|
||||
- page 0x7 EEPROM 0x700-0x707 bytes=FF FF FF FF FF FF FF FF text='........'
|
||||
- page 0x8 EEPROM 0x800-0x807 bytes=FF FF FF FF FF FF FF FF text='........'
|
||||
- page 0x9 EEPROM 0x900-0x907 bytes=FF FF FF FF FF FF FF FF text='........'
|
||||
- page 0xA EEPROM 0xA00-0xA07 bytes=FF FF FF FF FF FF FF FF text='........'
|
||||
- page 0xB EEPROM 0xB00-0xB07 bytes=FF FF FF FF FF FF FF FF text='........'
|
||||
- page 0xC EEPROM 0xC00-0xC07 bytes=FF FF FF FF FF FF FF FF text='........'
|
||||
- page 0xD EEPROM 0xD00-0xD07 bytes=FF FF FF FF FF FF FF FF text='........'
|
||||
- page 0xE EEPROM 0xE00-0xE07 bytes=FF FF FF FF FF FF FF FF text='........'
|
||||
- page 0xF EEPROM 0xF00-0xF07 bytes=FF FF FF FF FF FF FF FF text='........'
|
||||
|
||||
EEPROM Word Writes:
|
||||
- none since EEPROM setup/load
|
||||
|
||||
Factory Diffs:
|
||||
- 0x000 page=0x0 offset=0x00 expected=0x0000 actual=0xFFFF (record_header_or_label)
|
||||
- 0x002 page=0x0 offset=0x02 expected=0x6B6F actual=0xFFFF (record_header_or_label)
|
||||
- 0x004 page=0x0 offset=0x04 expected=0xFE00 actual=0xFFFF (record_header_or_label)
|
||||
- 0x006 page=0x0 offset=0x06 expected=0x0000 actual=0xFFFF (record_header_or_label)
|
||||
- 0x008 page=0x0 offset=0x08 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x004)
|
||||
- 0x00A page=0x0 offset=0x0A expected=0x0000 actual=0xFFFF (factory_shadow_offset; selectors=0x012)
|
||||
- 0x00C page=0x0 offset=0x0C expected=0x0000 actual=0xFFFF (factory_shadow_offset; selectors=0x013)
|
||||
- 0x00E page=0x0 offset=0x0E expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x017)
|
||||
- 0x010 page=0x0 offset=0x10 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x018)
|
||||
- 0x012 page=0x0 offset=0x12 expected=0x0808 actual=0xFFFF (factory_shadow_offset; selectors=0x01A)
|
||||
- 0x014 page=0x0 offset=0x14 expected=0x0000 actual=0xFFFF (factory_shadow_offset; selectors=0x01F)
|
||||
- 0x016 page=0x0 offset=0x16 expected=0x0000 actual=0xFFFF (factory_shadow_offset; selectors=0x020)
|
||||
- 0x018 page=0x0 offset=0x18 expected=0x0000 actual=0xFFFF (factory_shadow_offset; selectors=0x023)
|
||||
- 0x01A page=0x0 offset=0x1A expected=0x0000 actual=0xFFFF (factory_shadow_offset; selectors=0x037)
|
||||
- 0x01C page=0x0 offset=0x1C expected=0x0000 actual=0xFFFF (factory_shadow_offset; selectors=0x038)
|
||||
- 0x01E page=0x0 offset=0x1E expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x080)
|
||||
- 0x020 page=0x0 offset=0x20 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x081)
|
||||
- 0x022 page=0x0 offset=0x22 expected=0x0020 actual=0xFFFF (factory_shadow_offset; selectors=0x083)
|
||||
- 0x024 page=0x0 offset=0x24 expected=0x0000 actual=0xFFFF (factory_shadow_offset; selectors=0x088)
|
||||
- 0x026 page=0x0 offset=0x26 expected=0x0400 actual=0xFFFF (factory_shadow_offset; selectors=0x089)
|
||||
- 0x028 page=0x0 offset=0x28 expected=0x0800 actual=0xFFFF (factory_shadow_offset; selectors=0x08B)
|
||||
- 0x02A page=0x0 offset=0x2A expected=0x0040 actual=0xFFFF (factory_shadow_offset; selectors=0x08D)
|
||||
- 0x02C page=0x0 offset=0x2C expected=0x0000 actual=0xFFFF (factory_shadow_offset; selectors=0x08F)
|
||||
- 0x02E page=0x0 offset=0x2E expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x091)
|
||||
- 0x030 page=0x0 offset=0x30 expected=0xFF80 actual=0xFFFF (factory_shadow_offset; selectors=0x092)
|
||||
- 0x032 page=0x0 offset=0x32 expected=0x4040 actual=0xFFFF (factory_shadow_offset; selectors=0x093)
|
||||
- 0x034 page=0x0 offset=0x34 expected=0x0000 actual=0xFFFF (factory_shadow_offset; selectors=0x095)
|
||||
- 0x036 page=0x0 offset=0x36 expected=0x0000 actual=0xFFFF (factory_shadow_offset; selectors=0x098)
|
||||
- 0x038 page=0x0 offset=0x38 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x09A)
|
||||
- 0x03A page=0x0 offset=0x3A expected=0x0000 actual=0xFFFF (factory_shadow_offset; selectors=0x09D)
|
||||
- 0x03C page=0x0 offset=0x3C expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x09E)
|
||||
- 0x03E page=0x0 offset=0x3E expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x09F)
|
||||
- 0x040 page=0x0 offset=0x40 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0A3)
|
||||
- 0x042 page=0x0 offset=0x42 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0A4)
|
||||
- 0x044 page=0x0 offset=0x44 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0A5)
|
||||
- 0x046 page=0x0 offset=0x46 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0A6)
|
||||
- 0x048 page=0x0 offset=0x48 expected=0xF000 actual=0xFFFF (factory_shadow_offset; selectors=0x0A7)
|
||||
- 0x04A page=0x0 offset=0x4A expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0A9)
|
||||
- 0x04C page=0x0 offset=0x4C expected=0x2000 actual=0xFFFF (factory_shadow_offset; selectors=0x0AA)
|
||||
- 0x04E page=0x0 offset=0x4E expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0AC)
|
||||
- 0x050 page=0x0 offset=0x50 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0AD)
|
||||
- 0x052 page=0x0 offset=0x52 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0AE)
|
||||
- 0x054 page=0x0 offset=0x54 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0AF)
|
||||
- 0x056 page=0x0 offset=0x56 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0B0)
|
||||
- 0x058 page=0x0 offset=0x58 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0B2)
|
||||
- 0x05A page=0x0 offset=0x5A expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0B3)
|
||||
- 0x05C page=0x0 offset=0x5C expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0B4)
|
||||
- 0x05E page=0x0 offset=0x5E expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0B6)
|
||||
- 0x060 page=0x0 offset=0x60 expected=0xF800 actual=0xFFFF (factory_shadow_offset; selectors=0x0B7)
|
||||
- 0x062 page=0x0 offset=0x62 expected=0x4000 actual=0xFFFF (factory_shadow_offset; selectors=0x0B9)
|
||||
- 0x064 page=0x0 offset=0x64 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0BC)
|
||||
- 0x066 page=0x0 offset=0x66 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0BD)
|
||||
- 0x068 page=0x0 offset=0x68 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0C0)
|
||||
- 0x06A page=0x0 offset=0x6A expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0C1)
|
||||
- 0x06C page=0x0 offset=0x6C expected=0x4000 actual=0xFFFF (factory_shadow_offset; selectors=0x0C3)
|
||||
- 0x06E page=0x0 offset=0x6E expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0C4)
|
||||
- 0x070 page=0x0 offset=0x70 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0C5)
|
||||
- 0x072 page=0x0 offset=0x72 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0C6)
|
||||
- 0x074 page=0x0 offset=0x74 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0C7)
|
||||
- 0x076 page=0x0 offset=0x76 expected=0x0000 actual=0xFFFF (factory_shadow_offset; selectors=0x0C8)
|
||||
- 0x078 page=0x0 offset=0x78 expected=0x0000 actual=0xFFFF (factory_shadow_offset; selectors=0x0C9)
|
||||
- 0x07A page=0x0 offset=0x7A expected=0x0000 actual=0xFFFF (factory_shadow_offset; selectors=0x0CA)
|
||||
- 0x07C page=0x0 offset=0x7C expected=0x0000 actual=0xFFFF (factory_shadow_offset; selectors=0x0CB)
|
||||
- 0x07E page=0x0 offset=0x7E expected=0x0000 actual=0xFFFF (factory_shadow_offset; selectors=0x0CC)
|
||||
- 0x080 page=0x0 offset=0x80 expected=0x0000 actual=0xFFFF (factory_shadow_offset; selectors=0x0CD)
|
||||
- 0x082 page=0x0 offset=0x82 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0D4)
|
||||
- 0x084 page=0x0 offset=0x84 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0D5)
|
||||
- 0x086 page=0x0 offset=0x86 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0D6)
|
||||
- 0x088 page=0x0 offset=0x88 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0D7)
|
||||
- 0x08A page=0x0 offset=0x8A expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0D8)
|
||||
- 0x08C page=0x0 offset=0x8C expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0D9)
|
||||
- 0x08E page=0x0 offset=0x8E expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0DA)
|
||||
- 0x090 page=0x0 offset=0x90 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0F6)
|
||||
- 0x092 page=0x0 offset=0x92 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0F9)
|
||||
- 0x094 page=0x0 offset=0x94 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0FA)
|
||||
- 0x096 page=0x0 offset=0x96 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0FB)
|
||||
- 0x098 page=0x0 offset=0x98 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0FC)
|
||||
- 0x09A page=0x0 offset=0x9A expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0FD)
|
||||
- 0x09C page=0x0 offset=0x9C expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0FE)
|
||||
- 0x09E page=0x0 offset=0x9E expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0FF)
|
||||
- ... 1968 more factory diffs omitted
|
||||
|
||||
F400 Shadow Diffs:
|
||||
- H'F400 offset=0x00 expected=0x0000 actual=0xFFFF (record_header_or_label)
|
||||
- H'F402 offset=0x02 expected=0x6B6F actual=0xFFFF (record_header_or_label)
|
||||
- H'F404 offset=0x04 expected=0xFE00 actual=0xFFFF (record_header_or_label)
|
||||
- H'F406 offset=0x06 expected=0x0000 actual=0xFFFF (record_header_or_label)
|
||||
- H'F408 offset=0x08 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x004)
|
||||
- H'F40A offset=0x0A expected=0x0000 actual=0xFFFF (factory_shadow_offset; selectors=0x012)
|
||||
- H'F40C offset=0x0C expected=0x0000 actual=0xFFFF (factory_shadow_offset; selectors=0x013)
|
||||
- H'F40E offset=0x0E expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x017)
|
||||
- H'F410 offset=0x10 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x018)
|
||||
- H'F412 offset=0x12 expected=0x0808 actual=0xFFFF (factory_shadow_offset; selectors=0x01A)
|
||||
- H'F414 offset=0x14 expected=0x0000 actual=0xFFFF (factory_shadow_offset; selectors=0x01F)
|
||||
- H'F416 offset=0x16 expected=0x0000 actual=0xFFFF (factory_shadow_offset; selectors=0x020)
|
||||
- H'F418 offset=0x18 expected=0x0000 actual=0xFFFF (factory_shadow_offset; selectors=0x023)
|
||||
- H'F41A offset=0x1A expected=0x0000 actual=0xFFFF (factory_shadow_offset; selectors=0x037)
|
||||
- H'F41C offset=0x1C expected=0x0000 actual=0xFFFF (factory_shadow_offset; selectors=0x038)
|
||||
- H'F41E offset=0x1E expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x080)
|
||||
- H'F420 offset=0x20 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x081)
|
||||
- H'F422 offset=0x22 expected=0x0020 actual=0xFFFF (factory_shadow_offset; selectors=0x083)
|
||||
- H'F424 offset=0x24 expected=0x0000 actual=0xFFFF (factory_shadow_offset; selectors=0x088)
|
||||
- H'F426 offset=0x26 expected=0x0400 actual=0xFFFF (factory_shadow_offset; selectors=0x089)
|
||||
- H'F428 offset=0x28 expected=0x0800 actual=0xFFFF (factory_shadow_offset; selectors=0x08B)
|
||||
- H'F42A offset=0x2A expected=0x0040 actual=0xFFFF (factory_shadow_offset; selectors=0x08D)
|
||||
- H'F42C offset=0x2C expected=0x0000 actual=0xFFFF (factory_shadow_offset; selectors=0x08F)
|
||||
- H'F42E offset=0x2E expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x091)
|
||||
- H'F430 offset=0x30 expected=0xFF80 actual=0xFFFF (factory_shadow_offset; selectors=0x092)
|
||||
- H'F432 offset=0x32 expected=0x4040 actual=0xFFFF (factory_shadow_offset; selectors=0x093)
|
||||
- H'F434 offset=0x34 expected=0x0000 actual=0xFFFF (factory_shadow_offset; selectors=0x095)
|
||||
- H'F436 offset=0x36 expected=0x0000 actual=0xFFFF (factory_shadow_offset; selectors=0x098)
|
||||
- H'F438 offset=0x38 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x09A)
|
||||
- H'F43A offset=0x3A expected=0x0000 actual=0xFFFF (factory_shadow_offset; selectors=0x09D)
|
||||
- H'F43C offset=0x3C expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x09E)
|
||||
- H'F43E offset=0x3E expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x09F)
|
||||
- H'F440 offset=0x40 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0A3)
|
||||
- H'F442 offset=0x42 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0A4)
|
||||
- H'F444 offset=0x44 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0A5)
|
||||
- H'F446 offset=0x46 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0A6)
|
||||
- H'F448 offset=0x48 expected=0xF000 actual=0xFFFF (factory_shadow_offset; selectors=0x0A7)
|
||||
- H'F44A offset=0x4A expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0A9)
|
||||
- H'F44C offset=0x4C expected=0x2000 actual=0xFFFF (factory_shadow_offset; selectors=0x0AA)
|
||||
- H'F44E offset=0x4E expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0AC)
|
||||
- H'F450 offset=0x50 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0AD)
|
||||
- H'F452 offset=0x52 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0AE)
|
||||
- H'F454 offset=0x54 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0AF)
|
||||
- H'F456 offset=0x56 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0B0)
|
||||
- H'F458 offset=0x58 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0B2)
|
||||
- H'F45A offset=0x5A expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0B3)
|
||||
- H'F45C offset=0x5C expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0B4)
|
||||
- H'F45E offset=0x5E expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0B6)
|
||||
- H'F460 offset=0x60 expected=0xF800 actual=0xFFFF (factory_shadow_offset; selectors=0x0B7)
|
||||
- H'F462 offset=0x62 expected=0x4000 actual=0xFFFF (factory_shadow_offset; selectors=0x0B9)
|
||||
- H'F464 offset=0x64 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0BC)
|
||||
- H'F466 offset=0x66 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0BD)
|
||||
- H'F468 offset=0x68 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0C0)
|
||||
- H'F46A offset=0x6A expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0C1)
|
||||
- H'F46C offset=0x6C expected=0x4000 actual=0xFFFF (factory_shadow_offset; selectors=0x0C3)
|
||||
- H'F46E offset=0x6E expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0C4)
|
||||
- H'F470 offset=0x70 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0C5)
|
||||
- H'F472 offset=0x72 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0C6)
|
||||
- H'F474 offset=0x74 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0C7)
|
||||
- H'F476 offset=0x76 expected=0x0000 actual=0xFFFF (factory_shadow_offset; selectors=0x0C8)
|
||||
- H'F478 offset=0x78 expected=0x0000 actual=0xFFFF (factory_shadow_offset; selectors=0x0C9)
|
||||
- H'F47A offset=0x7A expected=0x0000 actual=0xFFFF (factory_shadow_offset; selectors=0x0CA)
|
||||
- H'F47C offset=0x7C expected=0x0000 actual=0xFFFF (factory_shadow_offset; selectors=0x0CB)
|
||||
- H'F47E offset=0x7E expected=0x0000 actual=0xFFFF (factory_shadow_offset; selectors=0x0CC)
|
||||
- H'F480 offset=0x80 expected=0x0000 actual=0xFFFF (factory_shadow_offset; selectors=0x0CD)
|
||||
- H'F482 offset=0x82 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0D4)
|
||||
- H'F484 offset=0x84 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0D5)
|
||||
- H'F486 offset=0x86 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0D6)
|
||||
- H'F488 offset=0x88 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0D7)
|
||||
- H'F48A offset=0x8A expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0D8)
|
||||
- H'F48C offset=0x8C expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0D9)
|
||||
- H'F48E offset=0x8E expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0DA)
|
||||
- H'F490 offset=0x90 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0F6)
|
||||
- H'F492 offset=0x92 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0F9)
|
||||
- H'F494 offset=0x94 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0FA)
|
||||
- H'F496 offset=0x96 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0FB)
|
||||
- H'F498 offset=0x98 expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0FC)
|
||||
- H'F49A offset=0x9A expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0FD)
|
||||
- H'F49C offset=0x9C expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0FE)
|
||||
- H'F49E offset=0x9E expected=0x8000 actual=0xFFFF (factory_shadow_offset; selectors=0x0FF)
|
||||
- ... 47 more shadow diffs omitted
|
||||
31
build/pin50-p77-low-eeprom.txt
Normal file
31
build/pin50-p77-low-eeprom.txt
Normal file
@@ -0,0 +1,31 @@
|
||||
Emulator EEPROM Snapshot
|
||||
|
||||
size=0x1000 sha256=4bed7704e1ea085487ca325c43bd60da75d37b6ae6f8292544e069a8825c64c6
|
||||
writes: bytes=0 words=0 factory_diff_words=0
|
||||
|
||||
Persistent Records:
|
||||
- page 0x0 EEPROM 0x000-0x007 bytes=00 00 6B 6F FE 00 00 00 text='..ko....'
|
||||
- page 0x1 EEPROM 0x100-0x107 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x2 EEPROM 0x200-0x207 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x3 EEPROM 0x300-0x307 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x4 EEPROM 0x400-0x407 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x5 EEPROM 0x500-0x507 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x6 EEPROM 0x600-0x607 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x7 EEPROM 0x700-0x707 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x8 EEPROM 0x800-0x807 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x9 EEPROM 0x900-0x907 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0xA EEPROM 0xA00-0xA07 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0xB EEPROM 0xB00-0xB07 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0xC EEPROM 0xC00-0xC07 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0xD EEPROM 0xD00-0xD07 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0xE EEPROM 0xE00-0xE07 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0xF EEPROM 0xF00-0xF07 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
|
||||
EEPROM Word Writes:
|
||||
- none since EEPROM setup/load
|
||||
|
||||
Factory Diffs:
|
||||
- current EEPROM image matches ROM factory/default image
|
||||
|
||||
F400 Shadow Diffs:
|
||||
- H'F4AA offset=0xAA expected=0x8000 actual=0x5500 (factory_shadow_offset; selectors=0x112)
|
||||
111
build/pin50-p77-low-fast-blank-eeprom.txt
Normal file
111
build/pin50-p77-low-fast-blank-eeprom.txt
Normal file
@@ -0,0 +1,111 @@
|
||||
Emulator EEPROM Snapshot
|
||||
|
||||
size=0x1000 sha256=4bed7704e1ea085487ca325c43bd60da75d37b6ae6f8292544e069a8825c64c6
|
||||
writes: bytes=4216 words=2108 factory_diff_words=0
|
||||
|
||||
Persistent Records:
|
||||
- page 0x0 EEPROM 0x000-0x007 bytes=00 00 6B 6F FE 00 00 00 text='..ko....'
|
||||
- page 0x1 EEPROM 0x100-0x107 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x2 EEPROM 0x200-0x207 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x3 EEPROM 0x300-0x307 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x4 EEPROM 0x400-0x407 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x5 EEPROM 0x500-0x507 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x6 EEPROM 0x600-0x607 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x7 EEPROM 0x700-0x707 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x8 EEPROM 0x800-0x807 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x9 EEPROM 0x900-0x907 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0xA EEPROM 0xA00-0xA07 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0xB EEPROM 0xB00-0xB07 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0xC EEPROM 0xC00-0xC07 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0xD EEPROM 0xD00-0xD07 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0xE EEPROM 0xE00-0xE07 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0xF EEPROM 0xF00-0xF07 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
|
||||
EEPROM Word Writes:
|
||||
- 0x0FE page=0x0 offset=0xFE 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x1FE page=0x1 offset=0xFE 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x2FE page=0x2 offset=0xFE 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x3FE page=0x3 offset=0xFE 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x4FE page=0x4 offset=0xFE 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x5FE page=0x5 offset=0xFE 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x6FE page=0x6 offset=0xFE 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x7FE page=0x7 offset=0xFE 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x8FE page=0x8 offset=0xFE 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x9FE page=0x9 offset=0xFE 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xAFE page=0xA offset=0xFE 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xBFE page=0xB offset=0xFE 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xCFE page=0xC offset=0xFE 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xDFE page=0xD offset=0xFE 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xEFE page=0xE offset=0xFE 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xFFE page=0xF offset=0xFE 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x0FC page=0x0 offset=0xFC 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x1FC page=0x1 offset=0xFC 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x2FC page=0x2 offset=0xFC 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x3FC page=0x3 offset=0xFC 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x4FC page=0x4 offset=0xFC 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x5FC page=0x5 offset=0xFC 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x6FC page=0x6 offset=0xFC 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x7FC page=0x7 offset=0xFC 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x8FC page=0x8 offset=0xFC 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x9FC page=0x9 offset=0xFC 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xAFC page=0xA offset=0xFC 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xBFC page=0xB offset=0xFC 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xCFC page=0xC offset=0xFC 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xDFC page=0xD offset=0xFC 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xEFC page=0xE offset=0xFC 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xFFC page=0xF offset=0xFC 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x0FA page=0x0 offset=0xFA 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x1FA page=0x1 offset=0xFA 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x2FA page=0x2 offset=0xFA 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x3FA page=0x3 offset=0xFA 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x4FA page=0x4 offset=0xFA 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x5FA page=0x5 offset=0xFA 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x6FA page=0x6 offset=0xFA 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x7FA page=0x7 offset=0xFA 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x8FA page=0x8 offset=0xFA 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x9FA page=0x9 offset=0xFA 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xAFA page=0xA offset=0xFA 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xBFA page=0xB offset=0xFA 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xCFA page=0xC offset=0xFA 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xDFA page=0xD offset=0xFA 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xEFA page=0xE offset=0xFA 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xFFA page=0xF offset=0xFA 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x0F8 page=0x0 offset=0xF8 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x1F8 page=0x1 offset=0xF8 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x2F8 page=0x2 offset=0xF8 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x3F8 page=0x3 offset=0xF8 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x4F8 page=0x4 offset=0xF8 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x5F8 page=0x5 offset=0xF8 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x6F8 page=0x6 offset=0xF8 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x7F8 page=0x7 offset=0xF8 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x8F8 page=0x8 offset=0xF8 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x9F8 page=0x9 offset=0xF8 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xAF8 page=0xA offset=0xF8 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xBF8 page=0xB offset=0xF8 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xCF8 page=0xC offset=0xF8 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xDF8 page=0xD offset=0xF8 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xEF8 page=0xE offset=0xF8 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xFF8 page=0xF offset=0xF8 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x0F6 page=0x0 offset=0xF6 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x1F6 page=0x1 offset=0xF6 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x2F6 page=0x2 offset=0xF6 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x3F6 page=0x3 offset=0xF6 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x4F6 page=0x4 offset=0xF6 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x5F6 page=0x5 offset=0xF6 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x6F6 page=0x6 offset=0xF6 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x7F6 page=0x7 offset=0xF6 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x8F6 page=0x8 offset=0xF6 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x9F6 page=0x9 offset=0xF6 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xAF6 page=0xA offset=0xF6 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xBF6 page=0xB offset=0xF6 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xCF6 page=0xC offset=0xF6 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xDF6 page=0xD offset=0xF6 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xEF6 page=0xE offset=0xF6 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xFF6 page=0xF offset=0xF6 0xFFFF->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- ... 2028 more word writes omitted
|
||||
|
||||
Factory Diffs:
|
||||
- current EEPROM image matches ROM factory/default image
|
||||
|
||||
F400 Shadow Diffs:
|
||||
- F400-F4FF shadow matches ROM factory words or no ROM factory baseline was supplied
|
||||
111
build/pin50-p77-low-fast-factory-eeprom.txt
Normal file
111
build/pin50-p77-low-fast-factory-eeprom.txt
Normal file
@@ -0,0 +1,111 @@
|
||||
Emulator EEPROM Snapshot
|
||||
|
||||
size=0x1000 sha256=4bed7704e1ea085487ca325c43bd60da75d37b6ae6f8292544e069a8825c64c6
|
||||
writes: bytes=4216 words=2108 factory_diff_words=0
|
||||
|
||||
Persistent Records:
|
||||
- page 0x0 EEPROM 0x000-0x007 bytes=00 00 6B 6F FE 00 00 00 text='..ko....'
|
||||
- page 0x1 EEPROM 0x100-0x107 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x2 EEPROM 0x200-0x207 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x3 EEPROM 0x300-0x307 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x4 EEPROM 0x400-0x407 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x5 EEPROM 0x500-0x507 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x6 EEPROM 0x600-0x607 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x7 EEPROM 0x700-0x707 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x8 EEPROM 0x800-0x807 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0x9 EEPROM 0x900-0x907 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0xA EEPROM 0xA00-0xA07 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0xB EEPROM 0xB00-0xB07 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0xC EEPROM 0xC00-0xC07 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0xD EEPROM 0xD00-0xD07 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0xE EEPROM 0xE00-0xE07 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
- page 0xF EEPROM 0xF00-0xF07 bytes=20 20 20 20 20 20 20 20 text=' '
|
||||
|
||||
EEPROM Word Writes:
|
||||
- 0x0FE page=0x0 offset=0xFE 0x0000->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x1FE page=0x1 offset=0xFE 0x0000->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x2FE page=0x2 offset=0xFE 0x0000->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x3FE page=0x3 offset=0xFE 0x0000->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x4FE page=0x4 offset=0xFE 0x0000->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x5FE page=0x5 offset=0xFE 0x0000->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x6FE page=0x6 offset=0xFE 0x0000->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x7FE page=0x7 offset=0xFE 0x0000->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x8FE page=0x8 offset=0xFE 0x0000->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x9FE page=0x9 offset=0xFE 0x0000->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xAFE page=0xA offset=0xFE 0x0000->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xBFE page=0xB offset=0xFE 0x0000->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xCFE page=0xC offset=0xFE 0x0000->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xDFE page=0xD offset=0xFE 0x0000->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xEFE page=0xE offset=0xFE 0x0000->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xFFE page=0xF offset=0xFE 0x0000->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x0FC page=0x0 offset=0xFC 0x0000->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x1FC page=0x1 offset=0xFC 0x0000->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x2FC page=0x2 offset=0xFC 0x0000->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x3FC page=0x3 offset=0xFC 0x0000->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x4FC page=0x4 offset=0xFC 0x0000->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x5FC page=0x5 offset=0xFC 0x0000->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x6FC page=0x6 offset=0xFC 0x0000->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x7FC page=0x7 offset=0xFC 0x0000->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x8FC page=0x8 offset=0xFC 0x0000->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x9FC page=0x9 offset=0xFC 0x0000->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xAFC page=0xA offset=0xFC 0x0000->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xBFC page=0xB offset=0xFC 0x0000->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xCFC page=0xC offset=0xFC 0x0000->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xDFC page=0xD offset=0xFC 0x0000->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xEFC page=0xE offset=0xFC 0x0000->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xFFC page=0xF offset=0xFC 0x0000->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x0FA page=0x0 offset=0xFA 0x0000->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x1FA page=0x1 offset=0xFA 0x0000->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x2FA page=0x2 offset=0xFA 0x0000->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x3FA page=0x3 offset=0xFA 0x0000->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x4FA page=0x4 offset=0xFA 0x0000->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x5FA page=0x5 offset=0xFA 0x0000->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x6FA page=0x6 offset=0xFA 0x0000->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x7FA page=0x7 offset=0xFA 0x0000->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x8FA page=0x8 offset=0xFA 0x0000->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x9FA page=0x9 offset=0xFA 0x0000->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xAFA page=0xA offset=0xFA 0x0000->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xBFA page=0xB offset=0xFA 0x0000->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xCFA page=0xC offset=0xFA 0x0000->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xDFA page=0xD offset=0xFA 0x0000->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xEFA page=0xE offset=0xFA 0x0000->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xFFA page=0xF offset=0xFA 0x0000->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x0F8 page=0x0 offset=0xF8 0x0000->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x1F8 page=0x1 offset=0xF8 0x0000->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x2F8 page=0x2 offset=0xF8 0x0000->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x3F8 page=0x3 offset=0xF8 0x0000->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x4F8 page=0x4 offset=0xF8 0x0000->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x5F8 page=0x5 offset=0xF8 0x0000->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x6F8 page=0x6 offset=0xF8 0x0000->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x7F8 page=0x7 offset=0xF8 0x0000->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x8F8 page=0x8 offset=0xF8 0x0000->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x9F8 page=0x9 offset=0xF8 0x0000->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xAF8 page=0xA offset=0xF8 0x0000->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xBF8 page=0xB offset=0xF8 0x0000->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xCF8 page=0xC offset=0xF8 0x0000->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xDF8 page=0xD offset=0xF8 0x0000->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xEF8 page=0xE offset=0xF8 0x0000->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xFF8 page=0xF offset=0xF8 0x0000->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x0F6 page=0x0 offset=0xF6 0x0000->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x1F6 page=0x1 offset=0xF6 0x0000->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x2F6 page=0x2 offset=0xF6 0x0000->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x3F6 page=0x3 offset=0xF6 0x0000->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x4F6 page=0x4 offset=0xF6 0x0000->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x5F6 page=0x5 offset=0xF6 0x0000->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x6F6 page=0x6 offset=0xF6 0x0000->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x7F6 page=0x7 offset=0xF6 0x0000->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x8F6 page=0x8 offset=0xF6 0x0000->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0x9F6 page=0x9 offset=0xF6 0x0000->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xAF6 page=0xA offset=0xF6 0x0000->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xBF6 page=0xB offset=0xF6 0x0000->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xCF6 page=0xC offset=0xF6 0x0000->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xDF6 page=0xD offset=0xF6 0x0000->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xEF6 page=0xE offset=0xF6 0x0000->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- 0xFF6 page=0xF offset=0xF6 0x0000->0x0000 source=linear_word (factory_shadow_offset)
|
||||
- ... 2028 more word writes omitted
|
||||
|
||||
Factory Diffs:
|
||||
- current EEPROM image matches ROM factory/default image
|
||||
|
||||
F400 Shadow Diffs:
|
||||
- F400-F4FF shadow matches ROM factory words or no ROM factory baseline was supplied
|
||||
1981
build/rom_ccu_seed_hints.json
Normal file
1981
build/rom_ccu_seed_hints.json
Normal file
File diff suppressed because it is too large
Load Diff
168
build/rom_ccu_seed_hints.txt
Normal file
168
build/rom_ccu_seed_hints.txt
Normal file
@@ -0,0 +1,168 @@
|
||||
H8/536 CCU Seed Hint Report
|
||||
|
||||
Summary: The RCP likely waits for the CCU to seed mirrored state tables, then uses those selector values to update LCD text, panel lamps, and report state changes.
|
||||
Confidence: medium
|
||||
|
||||
Table Model:
|
||||
- primary_value_table_candidate: H'E000-H'E3FF; accesses=31 static selectors=0x000, 0x002, 0x003, 0x023, 0x040, 0x081, 0x092, 0x093, 0x0A7, 0x0B7, 0x0B9, 0x0F6
|
||||
- secondary_value_table_candidate: H'E400-H'E7FF; accesses=8 static selectors=none
|
||||
- current_value_table_candidate: H'E800-H'EBFF; accesses=14 static selectors=0x000, 0x003, 0x040, 0x081, 0x092, 0x0F6
|
||||
- flag_table_candidate: H'EC00-H'EFFF; accesses=6 static selectors=0x000
|
||||
|
||||
Highest-Value Selector Candidates:
|
||||
- 0x000 heartbeat_or_idle_report_candidate: score=18 tables=primary_value_table_candidate, current_value_table_candidate, flag_table_candidate
|
||||
- primary_value_table_candidate write in loc_4096: MOV:G.W #H'0080, @H'E000
|
||||
- current_value_table_candidate write in loc_4096: MOV:G.W #H'0080, @H'E800
|
||||
- flag_table_candidate write in loc_4075: CLR.W @(-H'1400,R0)
|
||||
- idle report selector and CONNECT OK emulator condition both center on selector zero
|
||||
seed frames: 0x0080 -> 00 00 00 00 80 DA; 0x8080 -> 00 00 00 80 80 5A
|
||||
readback frame: 01 00 00 00 00 5B
|
||||
- 0x093 state_selector_candidate: score=15 tables=primary_value_table_candidate
|
||||
- primary_value_table_candidate read in loc_17C9: BTST.W #12, @H'E126
|
||||
- primary_value_table_candidate read in loc_17FB: BTST.W #12, @H'E126
|
||||
- primary_value_table_candidate read in loc_182D: BTST.W #5, @H'E126
|
||||
- primary_value_table_candidate read in loc_1891: BTST.W #5, @H'E126
|
||||
readback frame: 01 01 13 00 00 49
|
||||
- 0x0F6 active_status_bridge_candidate: score=14 tables=primary_value_table_candidate, current_value_table_candidate
|
||||
- primary_value_table_candidate read in loc_48FA: BTST.W #13, @H'E1EC
|
||||
- primary_value_table_candidate read in loc_48FA: MOV:G.W @H'E1EC, R0
|
||||
- current_value_table_candidate write in loc_48FA: MOV:G.W R0, @H'E9EC
|
||||
- loc_48FA tests E1EC bit13 and can enqueue report selector 0x00F6
|
||||
seed frames: 0x2000 -> 00 01 76 20 00 0D
|
||||
readback frame: 01 01 76 00 00 2C
|
||||
- 0x003 default_enabled_bit_candidate: score=11 tables=primary_value_table_candidate, current_value_table_candidate
|
||||
- primary_value_table_candidate write in loc_4096: MOV:G.W #H'8000, @H'E006
|
||||
- current_value_table_candidate write in loc_4096: MOV:G.W #H'8000, @H'E806
|
||||
- ROM default table writes E000/E800 selector 0x003 to 0x8000
|
||||
seed frames: 0x8000 -> 00 00 03 80 00 D9
|
||||
readback frame: 01 00 03 00 00 58
|
||||
- 0x040 default_all_ones_or_status_block_candidate: score=11 tables=primary_value_table_candidate, current_value_table_candidate
|
||||
- primary_value_table_candidate write in loc_4096: MOV:G.W #H'FFFF, @H'E080
|
||||
- current_value_table_candidate write in loc_4096: MOV:G.W #H'FFFF, @H'E880
|
||||
- ROM default table writes E000/E800 selector 0x040 to 0xFFFF and bench tests repeatedly touched the 0x40 family
|
||||
seed frames: 0xFFFF -> 00 00 40 FF FF 1A; 0x4030 -> 00 00 40 40 30 6A
|
||||
readback frame: 01 00 40 00 00 1B
|
||||
- 0x081 state_selector_candidate: score=9 tables=primary_value_table_candidate, current_value_table_candidate
|
||||
- primary_value_table_candidate read in vec_ad_adi_3D99: MOV:G.W @H'E102, R0
|
||||
- primary_value_table_candidate read in vec_ad_adi_3D99: CMP:G.W @H'E102, R1
|
||||
- current_value_table_candidate write in loc_15E0: MOV:G.W R1, @H'E902
|
||||
readback frame: 01 01 01 00 00 5B
|
||||
- 0x092 state_selector_candidate: score=9 tables=primary_value_table_candidate, current_value_table_candidate
|
||||
- primary_value_table_candidate read in loc_2650: MOV:G.W @H'E124, R0
|
||||
- primary_value_table_candidate read in loc_2650: CMP:G.W @H'E124, R0
|
||||
- current_value_table_candidate write in loc_2650: MOV:G.W R0, @H'E924
|
||||
readback frame: 01 01 12 00 00 48
|
||||
- 0x06B connection_latch_clear_candidate: score=7 tables=none
|
||||
- when F731.7 is set, command 5 on this selector clears F731.7/F790.7
|
||||
- selector dispatches to H'2F72
|
||||
readback frame: 01 00 6B 00 00 30
|
||||
- 0x06C command5_be70_candidate: score=7 tables=none
|
||||
- continuation command 5 calls BE70 for selector 0x006C
|
||||
- selector dispatches to H'2FAF
|
||||
readback frame: 01 00 6C 00 00 37
|
||||
- 0x06D command5_be70_candidate: score=7 tables=none
|
||||
- continuation command 5 calls BE70 for selector 0x006D
|
||||
- selector dispatches to H'3015
|
||||
readback frame: 01 00 6D 00 00 36
|
||||
- 0x007 camera_power_report_candidate: score=5 tables=none
|
||||
- observed RCP autonomous report frame(s): 00 00 07 80 00 DD
|
||||
- selector dispatches to H'2DC3
|
||||
readback frame: 01 00 07 00 00 5C
|
||||
- 0x015 call_button_report_candidate: score=5 tables=none
|
||||
- observed RCP autonomous report frame(s): 00 00 15 80 00 CF, 00 00 15 00 00 4F
|
||||
- selector dispatches to H'2E39
|
||||
readback frame: 01 00 15 00 00 4E
|
||||
- 0x023 state_selector_candidate: score=5 tables=primary_value_table_candidate
|
||||
- primary_value_table_candidate write in loc_400C: CLR.W @H'E046
|
||||
- selector dispatches to H'2EE6
|
||||
readback frame: 01 00 23 00 00 78
|
||||
- 0x06E command5_be70_candidate: score=5 tables=none
|
||||
- continuation command 5 calls BE70 for selector 0x006E
|
||||
readback frame: 01 00 6E 00 00 35
|
||||
- 0x096 connection_latch_clear_candidate: score=5 tables=none
|
||||
- when F731.7 is set, command 5 on this selector clears F731.7/F790.7
|
||||
readback frame: 01 01 16 00 00 4C
|
||||
- 0x097 connection_latch_clear_candidate: score=5 tables=none
|
||||
- when F731.7 is set, command 5 on this selector clears F731.7/F790.7
|
||||
readback frame: 01 01 17 00 00 4D
|
||||
- 0x0C6 connection_latch_clear_candidate: score=5 tables=none
|
||||
- when F731.7 is set, command 5 on this selector clears F731.7/F790.7
|
||||
readback frame: 01 01 46 00 00 1C
|
||||
- 0x0F8 connection_latch_clear_candidate: score=5 tables=none
|
||||
- when F731.7 is set, command 5 on this selector clears F731.7/F790.7
|
||||
readback frame: 01 01 78 00 00 22
|
||||
- 0x002 state_selector_candidate: score=3 tables=primary_value_table_candidate
|
||||
- primary_value_table_candidate read in loc_2650: BTST.W #13, @H'E004
|
||||
readback frame: 01 00 02 00 00 59
|
||||
- 0x0A7 state_selector_candidate: score=3 tables=primary_value_table_candidate
|
||||
- primary_value_table_candidate read in loc_1705: BTST.W #15, @H'E14E
|
||||
readback frame: 01 01 27 00 00 7D
|
||||
- 0x0B7 state_selector_candidate: score=3 tables=primary_value_table_candidate
|
||||
- primary_value_table_candidate read in loc_174D: BTST.W #13, @H'E16E
|
||||
readback frame: 01 01 37 00 00 6D
|
||||
- 0x0B9 state_selector_candidate: score=3 tables=primary_value_table_candidate
|
||||
- primary_value_table_candidate read in loc_1795: BTST.W #13, @H'E172
|
||||
readback frame: 01 01 39 00 00 63
|
||||
- 0x110 state_selector_candidate: score=3 tables=primary_value_table_candidate
|
||||
- primary_value_table_candidate read in loc_1795: BTST.W #15, @H'E220
|
||||
readback frame: 01 01 90 00 00 CA
|
||||
- 0x012 state_selector_candidate: score=2 tables=none
|
||||
- selector dispatches to H'2E03
|
||||
readback frame: 01 00 12 00 00 49
|
||||
|
||||
Display Text Hints:
|
||||
- CONNECT: 0 hit(s)
|
||||
- COMM LINK: 4 hit(s) - H'77F4 'literal COMM LINK', H'78F4 'literal COMM LINK', H'77F4 'COMM LINK ITEM-1Xw'
|
||||
- COMPLETED: 2 hit(s) - H'A027 'literal COMPLETED', H'A025 'COMPLETED'
|
||||
- CAM: 6 hit(s) - H'7149 'literal CAM', H'71FC 'literal CAM', H'72C7 'literal CAM'
|
||||
- BARS: 12 hit(s) - H'72D1 'literal BARS', H'757D 'literal BARS', H'9C61 'literal BARS'
|
||||
- BLACK: 22 hit(s) - H'65CC 'literal BLACK', H'6647 'literal BLACK', H'6709 'literal BLACK'
|
||||
- IRIS: 6 hit(s) - H'6461 'literal IRIS', H'6A92 'literal IRIS', H'A5CA 'literal IRIS'
|
||||
- GAIN: 10 hit(s) - H'6825 'literal GAIN', H'7813 'literal GAIN', H'98A1 'literal GAIN'
|
||||
- SHUTTER: 4 hit(s) - H'6FB2 'literal SHUTTER', H'781A 'literal SHUTTER', H'6FAE 'SHUTTER Xo'
|
||||
- CALL: 8 hit(s) - H'B53E 'literal CALL', H'B563 'literal CALL', H'B62F 'literal CALL'
|
||||
- POWER: 0 hit(s)
|
||||
- AUTO: 34 hit(s) - H'693E 'literal AUTO', H'6A52 'literal AUTO', H'6B40 'literal AUTO'
|
||||
- DIAG: 6 hit(s) - H'6BF5 'literal DIAG', H'6C19 'literal DIAG', H'6E46 'literal DIAG'
|
||||
- DXC: 0 hit(s)
|
||||
|
||||
Selector Dispatch Hints:
|
||||
- table H'28A6: 25 non-default/interesting entries
|
||||
- selector 0x000 -> H'2CB9 (dispatch index 0x000)
|
||||
- selector 0x007 -> H'2DC3 (dispatch index 0x007)
|
||||
- selector 0x012 -> H'2E03 (dispatch index 0x012)
|
||||
- selector 0x013 -> H'2E06 (dispatch index 0x013)
|
||||
- selector 0x015 -> H'2E39 (dispatch index 0x015)
|
||||
- selector 0x016 -> H'2E5A (dispatch index 0x016)
|
||||
- selector 0x017 -> H'2E85 (dispatch index 0x017)
|
||||
- selector 0x018 -> H'2E6F (dispatch index 0x018)
|
||||
- selector 0x01A -> H'2EC4 (dispatch index 0x01A)
|
||||
- selector 0x023 -> H'2EE6 (dispatch index 0x023)
|
||||
- selector 0x024 -> H'2F0C (dispatch index 0x024)
|
||||
- selector 0x025 -> H'2F1C (dispatch index 0x025)
|
||||
- selector 0x043 -> H'2F4A (dispatch index 0x043)
|
||||
- selector 0x04A -> H'2F5C (dispatch index 0x04A)
|
||||
- selector 0x04E -> H'2F5C (dispatch index 0x04E)
|
||||
- selector 0x052 -> H'2F5C (dispatch index 0x052)
|
||||
|
||||
Candidate Fake-CCU Seed Plan:
|
||||
- cmd0 seed selector 0x000 = 0x8080: 00 00 00 80 80 5A
|
||||
selector zero active/connect candidate from emulator state search
|
||||
- cmd0 seed selector 0x003 = 0x8000: 00 00 03 80 00 D9
|
||||
ROM default state also sets selector 0x003 high bit
|
||||
- cmd0 seed selector 0x040 = 0xFFFF: 00 00 40 FF FF 1A
|
||||
ROM default all-ones/status candidate touched by bench 0x40 family
|
||||
- cmd0 seed selector 0x0F6 = 0x2000: 00 01 76 20 00 0D
|
||||
sets E1EC bit13 candidate used by loc_48FA report bridge
|
||||
|
||||
Bench Implications:
|
||||
- Do not wait for non-heartbeat reports as the only activation source; the CCU may be expected to push initial table state first.
|
||||
- Use command 0 writes for initial seeding, then command 1 readbacks for verification. Treat command 4/5/6 as continuation-only until a live report proves otherwise.
|
||||
- Selector zero remains the highest-value activation candidate because the emulator reaches CONNECT OK when E000[0]=0x8080 and the selector-zero processing queue runs.
|
||||
- E1EC/selector 0x00F6 is a strong follow-up candidate because loc_48FA tests bit13 there and can enqueue report 0x00F6.
|
||||
- LCD text terms such as CAM/BARS/BLACK/COMM LINK appear in ROM records, but they are not direct serial payload strings; they point to selector-driven display builders.
|
||||
|
||||
Caveats:
|
||||
- Selector names are candidates, not confirmed protocol labels.
|
||||
- Static table xrefs prove that firmware reads/writes a selector; they do not prove the external CCU must seed it on boot.
|
||||
- Generated frames are syntactically valid six-byte host frames; bench safety still depends on timing and current RCP state.
|
||||
8903
build/rom_eeprom_layout.json
Normal file
8903
build/rom_eeprom_layout.json
Normal file
File diff suppressed because it is too large
Load Diff
179
build/rom_eeprom_layout.txt
Normal file
179
build/rom_eeprom_layout.txt
Normal file
@@ -0,0 +1,179 @@
|
||||
H8/536 EEPROM Layout Report
|
||||
|
||||
Summary: The ROM treats the traced P9 bus as two X24164-style EEPROM banks, mirrors a 0x100-byte factory/default block into F400-F4FF, and loads sixteen 8-byte persistent records into F7B0-F82F at boot.
|
||||
Confidence: medium-high
|
||||
|
||||
Physical / Logical Model:
|
||||
- lower_x24164_candidate: logical 0x000-0x7FF control A0/A1
|
||||
- upper_x24164_candidate: logical 0x800-0xFFF control E0/E1
|
||||
- bus: P91/SCL and P97/SDA bit-banged two-wire bus through ROM routines C121/C08B/C0DB/C10C/C142
|
||||
- page model: 16 logical pages of 0x100 bytes; low 8 address bits are sent as the EEPROM word address
|
||||
|
||||
Boot Flow:
|
||||
- H'40BB eeprom_boot_gate: initializes queue/table scratch, checks P7DR.7, then checks F402 == H'6B6F before trusting persisted state
|
||||
- H'4103 factory_default_fill: copies ROM C964-CA63 into F400-F4FF and writes the same 0x100-byte defaults to each EEPROM page
|
||||
- H'4187 record_header_blank: overwrites page offsets 0x00-0x07 on pages 0x1-0xF with four H'2020 words after factory replication; page 0 keeps the signature/options header
|
||||
- H'41D2 persistent_record_load: reads page offsets 0x00-0x07 from pages 0x0-0xF into F7B0-F82F; record 0 is a signature/options header, records 1-F are label/identity-like slots
|
||||
- H'BD2B serial_persist_path: command-4 continuation writes the live value into F400+map[selector] and persists it through BFE0 when F76E.7 is set
|
||||
|
||||
Factory Shadow Block:
|
||||
- ROM H'C964 length 0x100 mirrors to H'F400-H'F4FF
|
||||
- H'F402 offset 0x02 default 0x6B6F (ascii='ko'; xrefs=1)
|
||||
- H'F404 offset 0x04 default 0xFE00 (xrefs=8)
|
||||
- H'F408 offset 0x08 default 0x8000 (selectors=0x004)
|
||||
- H'F40A offset 0x0A default 0x0000 (selectors=0x012)
|
||||
- H'F40C offset 0x0C default 0x0000 (selectors=0x013)
|
||||
- H'F40E offset 0x0E default 0x8000 (selectors=0x017)
|
||||
- H'F410 offset 0x10 default 0x8000 (selectors=0x018)
|
||||
- H'F412 offset 0x12 default 0x0808 (selectors=0x01A)
|
||||
- H'F414 offset 0x14 default 0x0000 (selectors=0x01F)
|
||||
- H'F416 offset 0x16 default 0x0000 (selectors=0x020)
|
||||
- H'F418 offset 0x18 default 0x0000 (selectors=0x023)
|
||||
- H'F41A offset 0x1A default 0x0000 (selectors=0x037)
|
||||
- H'F41C offset 0x1C default 0x0000 (selectors=0x038)
|
||||
- H'F41E offset 0x1E default 0x8000 (selectors=0x080)
|
||||
- H'F420 offset 0x20 default 0x8000 (selectors=0x081)
|
||||
- H'F422 offset 0x22 default 0x0020 (ascii='. '; selectors=0x083)
|
||||
- H'F424 offset 0x24 default 0x0000 (selectors=0x088)
|
||||
- H'F426 offset 0x26 default 0x0400 (selectors=0x089)
|
||||
- H'F428 offset 0x28 default 0x0800 (selectors=0x08B)
|
||||
- H'F42A offset 0x2A default 0x0040 (ascii='.@'; selectors=0x08D)
|
||||
- H'F42C offset 0x2C default 0x0000 (selectors=0x08F)
|
||||
- H'F42E offset 0x2E default 0x8000 (selectors=0x091)
|
||||
- H'F430 offset 0x30 default 0xFF80 (selectors=0x092)
|
||||
- H'F432 offset 0x32 default 0x4040 (ascii='@@'; selectors=0x093)
|
||||
- H'F434 offset 0x34 default 0x0000 (selectors=0x095)
|
||||
- H'F436 offset 0x36 default 0x0000 (selectors=0x098)
|
||||
- H'F438 offset 0x38 default 0x8000 (selectors=0x09A)
|
||||
- H'F43A offset 0x3A default 0x0000 (selectors=0x09D)
|
||||
- H'F43C offset 0x3C default 0x8000 (selectors=0x09E)
|
||||
- H'F43E offset 0x3E default 0x8000 (selectors=0x09F)
|
||||
- H'F440 offset 0x40 default 0x8000 (selectors=0x0A3)
|
||||
- H'F442 offset 0x42 default 0x8000 (selectors=0x0A4)
|
||||
|
||||
Persistent 8-Byte Records:
|
||||
- 16 records: EEPROM 0x000-0x007 .. 0xF00-0xF07 load into RAM H'F7B0-H'F7B7 .. H'F828-H'F82F
|
||||
- record 0x0: EEPROM 0x000-0x007 -> RAM H'F7B0-H'F7B7 default '..ko....'
|
||||
- record 0x1: EEPROM 0x100-0x107 -> RAM H'F7B8-H'F7BF default ' '
|
||||
- record 0x2: EEPROM 0x200-0x207 -> RAM H'F7C0-H'F7C7 default ' '
|
||||
- record 0x3: EEPROM 0x300-0x307 -> RAM H'F7C8-H'F7CF default ' '
|
||||
- record 0x4: EEPROM 0x400-0x407 -> RAM H'F7D0-H'F7D7 default ' '
|
||||
- record 0x5: EEPROM 0x500-0x507 -> RAM H'F7D8-H'F7DF default ' '
|
||||
- record 0x6: EEPROM 0x600-0x607 -> RAM H'F7E0-H'F7E7 default ' '
|
||||
- record 0x7: EEPROM 0x700-0x707 -> RAM H'F7E8-H'F7EF default ' '
|
||||
- record 0x8: EEPROM 0x800-0x807 -> RAM H'F7F0-H'F7F7 default ' '
|
||||
- record 0x9: EEPROM 0x900-0x907 -> RAM H'F7F8-H'F7FF default ' '
|
||||
- record 0xA: EEPROM 0xA00-0xA07 -> RAM H'F800-H'F807 default ' '
|
||||
- record 0xB: EEPROM 0xB00-0xB07 -> RAM H'F808-H'F80F default ' '
|
||||
- record 0xC: EEPROM 0xC00-0xC07 -> RAM H'F810-H'F817 default ' '
|
||||
- record 0xD: EEPROM 0xD00-0xD07 -> RAM H'F818-H'F81F default ' '
|
||||
- record 0xE: EEPROM 0xE00-0xE07 -> RAM H'F820-H'F827 default ' '
|
||||
- record 0xF: EEPROM 0xF00-0xF07 -> RAM H'F828-H'F82F default ' '
|
||||
|
||||
Serial Selector -> Shadow/EEPROM Mapping:
|
||||
- table H'C564 has 89 nonzero low-byte mappings
|
||||
- formula: command 4 persists to EEPROM address (((F76E & 0x0F) << 8) | (mapping_low_byte & 0xFE)) when F76E.7 is set
|
||||
- selector 0x004 map_word=0x4808 -> H'F408 page+0x08 default=0x8000
|
||||
- selector 0x012 map_word=0x080A -> H'F40A page+0x0A default=0x0000
|
||||
- selector 0x013 map_word=0x080C -> H'F40C page+0x0C default=0x0000
|
||||
- selector 0x017 map_word=0x600E -> H'F40E page+0x0E default=0x8000
|
||||
- selector 0x018 map_word=0x6010 -> H'F410 page+0x10 default=0x8000
|
||||
- selector 0x01A map_word=0x1012 -> H'F412 page+0x12 default=0x0808
|
||||
- selector 0x01F map_word=0x4414 -> H'F414 page+0x14 default=0x0000
|
||||
- selector 0x020 map_word=0x4416 -> H'F416 page+0x16 default=0x0000
|
||||
- selector 0x023 map_word=0x0418 -> H'F418 page+0x18 default=0x0000
|
||||
- selector 0x037 map_word=0x641A -> H'F41A page+0x1A default=0x0000
|
||||
- selector 0x038 map_word=0x641C -> H'F41C page+0x1C default=0x0000
|
||||
- selector 0x080 map_word=0xE01E -> H'F41E page+0x1E default=0x8000
|
||||
- selector 0x081 map_word=0x6020 -> H'F420 page+0x20 default=0x8000
|
||||
- selector 0x083 map_word=0x6022 -> H'F422 page+0x22 default=0x0020
|
||||
- selector 0x088 map_word=0x4024 -> H'F424 page+0x24 default=0x0000
|
||||
- selector 0x089 map_word=0x4426 -> H'F426 page+0x26 default=0x0400
|
||||
- selector 0x08B map_word=0x4428 -> H'F428 page+0x28 default=0x0800
|
||||
- selector 0x08D map_word=0x442A -> H'F42A page+0x2A default=0x0040
|
||||
- selector 0x08F map_word=0x602C -> H'F42C page+0x2C default=0x0000
|
||||
- selector 0x091 map_word=0x602E -> H'F42E page+0x2E default=0x8000
|
||||
- selector 0x092 map_word=0x6030 -> H'F430 page+0x30 default=0xFF80
|
||||
- selector 0x093 map_word=0x6032 -> H'F432 page+0x32 default=0x4040
|
||||
- selector 0x095 map_word=0x6034 -> H'F434 page+0x34 default=0x0000
|
||||
- selector 0x098 map_word=0x4036 -> H'F436 page+0x36 default=0x0000
|
||||
- selector 0x09A map_word=0x6038 -> H'F438 page+0x38 default=0x8000
|
||||
- selector 0x09D map_word=0x443A -> H'F43A page+0x3A default=0x0000
|
||||
- selector 0x09E map_word=0x603C -> H'F43C page+0x3C default=0x8000
|
||||
- selector 0x09F map_word=0x603E -> H'F43E page+0x3E default=0x8000
|
||||
- selector 0x0A3 map_word=0x6040 -> H'F440 page+0x40 default=0x8000
|
||||
- selector 0x0A4 map_word=0x6042 -> H'F442 page+0x42 default=0x8000
|
||||
- selector 0x0A5 map_word=0x6044 -> H'F444 page+0x44 default=0x8000
|
||||
- selector 0x0A6 map_word=0x6046 -> H'F446 page+0x46 default=0x8000
|
||||
- selector 0x0A7 map_word=0x4048 -> H'F448 page+0x48 default=0xF000
|
||||
- selector 0x0A9 map_word=0xE04A -> H'F44A page+0x4A default=0x8000
|
||||
- selector 0x0AA map_word=0xC44C -> H'F44C page+0x4C default=0x2000
|
||||
- selector 0x0AC map_word=0xC44E -> H'F44E page+0x4E default=0x8000
|
||||
- selector 0x0AD map_word=0xC450 -> H'F450 page+0x50 default=0x8000
|
||||
- selector 0x0AE map_word=0xC452 -> H'F452 page+0x52 default=0x8000
|
||||
- selector 0x0AF map_word=0xC454 -> H'F454 page+0x54 default=0x8000
|
||||
- selector 0x0B0 map_word=0xC456 -> H'F456 page+0x56 default=0x8000
|
||||
- selector 0x0B2 map_word=0xC458 -> H'F458 page+0x58 default=0x8000
|
||||
- selector 0x0B3 map_word=0xC45A -> H'F45A page+0x5A default=0x8000
|
||||
- selector 0x0B4 map_word=0xC45C -> H'F45C page+0x5C default=0x8000
|
||||
- selector 0x0B6 map_word=0xC45E -> H'F45E page+0x5E default=0x8000
|
||||
- selector 0x0B7 map_word=0x4060 -> H'F460 page+0x60 default=0xF800
|
||||
- selector 0x0B9 map_word=0x6862 -> H'F462 page+0x62 default=0x4000
|
||||
- selector 0x0BC map_word=0xE064 -> H'F464 page+0x64 default=0x8000
|
||||
- selector 0x0BD map_word=0xC066 -> H'F466 page+0x66 default=0x8000
|
||||
- selector 0x0C0 map_word=0x4468 -> H'F468 page+0x68 default=0x8000
|
||||
- selector 0x0C1 map_word=0xC46A -> H'F46A page+0x6A default=0x8000
|
||||
- selector 0x0C3 map_word=0xE46C -> H'F46C page+0x6C default=0x4000
|
||||
- selector 0x0C4 map_word=0x446E -> H'F46E page+0x6E default=0x8000
|
||||
- selector 0x0C5 map_word=0xC070 -> H'F470 page+0x70 default=0x8000
|
||||
- selector 0x0C6 map_word=0x4472 -> H'F472 page+0x72 default=0x8000
|
||||
- selector 0x0C7 map_word=0xC474 -> H'F474 page+0x74 default=0x8000
|
||||
- selector 0x0C8 map_word=0xC476 -> H'F476 page+0x76 default=0x0000
|
||||
- selector 0x0C9 map_word=0xC478 -> H'F478 page+0x78 default=0x0000
|
||||
- selector 0x0CA map_word=0xC47A -> H'F47A page+0x7A default=0x0000
|
||||
- selector 0x0CB map_word=0xC47C -> H'F47C page+0x7C default=0x0000
|
||||
- selector 0x0CC map_word=0xC47E -> H'F47E page+0x7E default=0x0000
|
||||
- selector 0x0CD map_word=0xC480 -> H'F480 page+0x80 default=0x0000
|
||||
- selector 0x0D4 map_word=0xC482 -> H'F482 page+0x82 default=0x8000
|
||||
- selector 0x0D5 map_word=0xC484 -> H'F484 page+0x84 default=0x8000
|
||||
- selector 0x0D6 map_word=0xC086 -> H'F486 page+0x86 default=0x8000
|
||||
- selector 0x0D7 map_word=0xC088 -> H'F488 page+0x88 default=0x8000
|
||||
- selector 0x0D8 map_word=0x408A -> H'F48A page+0x8A default=0x8000
|
||||
- selector 0x0D9 map_word=0x408C -> H'F48C page+0x8C default=0x8000
|
||||
- selector 0x0DA map_word=0x408E -> H'F48E page+0x8E default=0x8000
|
||||
- selector 0x0F6 map_word=0x4090 -> H'F490 page+0x90 default=0x8000
|
||||
- selector 0x0F9 map_word=0x4492 -> H'F492 page+0x92 default=0x8000
|
||||
- selector 0x0FA map_word=0x4494 -> H'F494 page+0x94 default=0x8000
|
||||
- selector 0x0FB map_word=0x4496 -> H'F496 page+0x96 default=0x8000
|
||||
- selector 0x0FC map_word=0x4498 -> H'F498 page+0x98 default=0x8000
|
||||
- selector 0x0FD map_word=0x409A -> H'F49A page+0x9A default=0x8000
|
||||
- selector 0x0FE map_word=0x449C -> H'F49C page+0x9C default=0x8000
|
||||
- selector 0x0FF map_word=0x449E -> H'F49E page+0x9E default=0x8000
|
||||
- selector 0x100 map_word=0x44A0 -> H'F4A0 page+0xA0 default=0x8000
|
||||
- selector 0x101 map_word=0x44A2 -> H'F4A2 page+0xA2 default=0x8000
|
||||
- selector 0x10F map_word=0xC4A4 -> H'F4A4 page+0xA4 default=0x8000
|
||||
- selector 0x110 map_word=0x48A6 -> H'F4A6 page+0xA6 default=0x0000
|
||||
- ... 9 more mapped selectors omitted
|
||||
|
||||
State Byte Hints:
|
||||
- H'F402 factory_signature_word: factory 0x6B6F; boot accepts persisted state only when this word is H'6B6F (xrefs=1)
|
||||
- H'F404 feature_or_option_flags_candidate: factory 0xFE00; bits 1-4 are tested with F791 gates in display/status routines (xrefs=8)
|
||||
- H'F730 connect_display_state_candidate: volatile/no factory word (xrefs=3)
|
||||
- H'F731 session_latch_candidate: volatile/no factory word (xrefs=22)
|
||||
- H'F732 display_dispatch_selector_candidate: volatile display dispatch selector feeding the 493E pointer table and 48FA report bridge (xrefs=11)
|
||||
- H'F76E eeprom_page_and_persist_flags: bit7 enables command-4 EEPROM persistence, bit6 suppresses 48FA dispatch, low nibble selects EEPROM page (xrefs=4)
|
||||
- H'F790 connection_latch_shadow_candidate: volatile/no factory word (xrefs=1)
|
||||
- H'F791 feature_flag_gate_candidate: volatile gate tested alongside F404 option bits before setting report/display flags (xrefs=12)
|
||||
- H'FB03 report_bridge_suppress_candidate: volatile/no factory word (xrefs=10)
|
||||
|
||||
Bench Implications:
|
||||
- A live EEPROM dump should first compare F400-F4FF shadow-equivalent offsets against the ROM factory table, especially F402/F404 and mapped offsets.
|
||||
- Pages 0x0-0xF offset 0x00-0x07 are loaded into F7B0-F82F; page 0 carries the signature/options header and pages 1-F default to spaces, so non-space bytes there are high-value identity/config data.
|
||||
- Serial command 0/4 can mirror values into F400 offsets selected by the ROM mapping table, but command 4 only persists when the continuation path reaches BD2B and F76E.7 is set.
|
||||
- F76E is not just a page byte: bit7 gates EEPROM persistence, bit6 suppresses the 48FA dispatch path, and only its low nibble survives into the EEPROM page address.
|
||||
- CONNECT OK is still likely gated by volatile table/session state as well as EEPROM-backed defaults; EEPROM differences may explain why bench and emulator diverge, but are unlikely to be the whole protocol by themselves.
|
||||
|
||||
Caveats:
|
||||
- The selector map proves where firmware mirrors/persists serial values, not what every field means to the panel UI.
|
||||
- High bytes in the C564 selector map look structured, but the observed command-0/command-4 paths only use the low byte for F400/EEPROM offsets.
|
||||
- Indexed F7B0-F82F record consumers can be missed by a static xref pass; dynamic emulator traces should be used once interesting record bytes are found.
|
||||
1111
build/rom_rx_branch_trace.json
Normal file
1111
build/rom_rx_branch_trace.json
Normal file
File diff suppressed because it is too large
Load Diff
218
build/rom_rx_branch_trace.txt
Normal file
218
build/rom_rx_branch_trace.txt
Normal file
@@ -0,0 +1,218 @@
|
||||
H8/536 SCI1 RX Branch Trace
|
||||
|
||||
Summary: The ROM captures six SCI1 bytes, validates a 0x5A-seeded XOR checksum, decodes RX[0] & 0x07, then splits into initial commands while FAA2 == 0 and continuation commands while FAA2 != 0.
|
||||
Confidence: high
|
||||
|
||||
Frame Model:
|
||||
- capture buffer: H'F868-H'F86D
|
||||
- validation buffer: H'F860-H'F865
|
||||
- command: RX[0] & 0x07
|
||||
- logical index candidate: loc_622B(RX[1], RX[2])
|
||||
- value candidate: RX[3:4]
|
||||
- checksum: 0x5A XOR RX[0..4] == RX[5]
|
||||
|
||||
Selector Decode:
|
||||
- loc_622B logical selector decode: present
|
||||
The RX handler builds a raw word from RX[1:2], uses RX[1] bit0-bit2 as a page, keeps RX[2] as the low selector byte, and maps the result into a 0x000-0x1FF logical selector.
|
||||
- page 0, or pages 4-7, and low <= 0x7F: selector = 0x000 + low [H'6234, H'6236, H'6244, H'6248, H'6264]
|
||||
- page 1 and low <= 0xFF: selector = 0x080 + low [H'6238, H'623A, H'624D, H'6251, H'6264]
|
||||
- page 2 and low <= 0x7F: selector = 0x180 + low [H'623C, H'623E, H'6256, H'625A, H'6264]
|
||||
- page 3, page/range failure, or page 0/4-7 with low > 0x7F: selector forced to 0x01FF [H'6240, H'6242, H'625F, H'6261]
|
||||
- implication: RX[1].7 is not part of the selector here because the dispatcher rejects byte1 bit7 before command handling.
|
||||
- implication: Commands shaped as 01 80 xx ... are rejected before loc_622B-derived command handling even if the low byte looks useful.
|
||||
- implication: Page 4-7 encodings appear to alias the page-0 decode path unless the low byte is out of range.
|
||||
|
||||
Stages:
|
||||
- SCI1 RXI/ERI byte capture: present
|
||||
ERI latches FAA4.7 and clears physical error flags; RXI clears RDRF, reads SCI1_RDR, stores bytes into F868-F86D, reloads F9C1, and sets F9C5 when six bytes are captured.
|
||||
- ERI taken: set FAA4.7, clear ORER/FER/PER, then fall into RXI byte capture [H'BB57, H'BB5B, H'BB5F, H'BB63]
|
||||
- F9C1 == 0 before byte: clear F9C3 so the byte starts a fresh frame [H'BB71, H'BB75, H'BB77]
|
||||
- F9C1 != 0 and F9C3 <= 5: append byte at F868 + F9C3 and increment F9C3 [H'BB7D, H'BB82, H'BB8A, H'BB90, H'BB96]
|
||||
- F9C1 != 0 and F9C3 > 5: clear FAA4 and skip storing this byte [H'BB7D, H'BB82, H'BB84, H'BB88]
|
||||
- incremented F9C3 == 6: load F9C5 with 0x14 as the RX/session timeout window [H'BB9A, H'BB9C, H'BB9E]
|
||||
- any RXI exit: reload F9C1 with 0x05 as the inter-byte timeout [H'BBA3]
|
||||
- six-byte validation and checksum: present
|
||||
The main-loop processor only runs when F9C3 is six, copies F868-F86D to F860-F865, clears F9C3, rejects physical-error frames, checks the 0x5A XOR checksum, and decodes the index.
|
||||
- F9C3 != 6: return without processing [H'BBAB, H'BBB0]
|
||||
- FAA4.7 set after capture: enter retry/error path at BE29 [H'BBCF, H'BBD3]
|
||||
- checksum mismatch: enter retry/error path at BE29 [H'BBD6, H'BBD8, H'BBDC, H'BBE0, H'BBE4, H'BBE8, H'BBEC, H'BBF0]
|
||||
- checksum valid: clear FAA6, decode selector from RX[1:2] through loc_622B, and dispatch on RX[0] & 0x07 [H'BBF3, H'BBF7, H'BBFD, H'BC01, H'BC08, H'BC0C]
|
||||
- FAA2 split dispatcher: present
|
||||
FAA2 == 0 enters the initial dispatcher for commands 0, 1, 2, and 7. FAA2 != 0 enters the continuation dispatcher; commands 4, 5, and 6 only live there.
|
||||
- FAA2 == 0: set FAA2.7 and test initial commands 0, 1, 2, 7 [H'BC0F, H'BC13, H'BC15, H'BC20, H'BC24, H'BC29, H'BC2E]
|
||||
- FAA2 != 0 and command bit2 set: continuation commands 4, 5, 6, 7 are possible [H'BC0F, H'BC13, H'BC3A, H'BC45, H'BC4A, H'BC4F, H'BC54]
|
||||
- FAA2 != 0 and command bit2 clear: BCLR FAA2.3; if that bit was set, clear FAA3 and re-enter initial dispatcher [H'BC3A, H'BC3C, H'BC5C, H'BC60, H'BC63, H'BC67]
|
||||
- byte1 bit7 set on initial path: branch to BD0B and return without normal command handling [H'BC19, H'BC1D, H'BD0B]
|
||||
- byte1 bit7 set on continuation path: branch to BE27 and return without normal command handling [H'BC3E, H'BC42, H'BE27]
|
||||
- checksum/error retry path: present
|
||||
Physical RX errors or checksum failures can either be ignored, clear the session after two retries, or stage a command-7 retry/error echo of RX[1:4].
|
||||
- FAA5.7 == 0: return after clearing FAA4.7; retry echo is disabled [H'BE29, H'BE2D, H'BE31]
|
||||
- FAA5.7 == 1 and FAA6 < 2: stage F850=0x07 and F851-F854=RX[1:4], then call BA26 [H'BE33, H'BE37, H'BE3C, H'BE4D, H'BE52, H'BE5A, H'BE62, H'BE6A]
|
||||
- FAA5.7 == 1 and FAA6 >= 2: load F9C0=0x1F and clear FAA3/FAA2 [H'BE37, H'BE3C, H'BE3E, H'BE43, H'BE47]
|
||||
- pending selector ring at loc_BE70: present
|
||||
Several write/ACK paths call BE70 with R5 as the logical selector. BE70 deduplicates that selector against a small F970-like ring using cursors F9B9/F9B4, then appends it if absent.
|
||||
- selector already present: exit without appending [H'BE84, H'BE88, H'BE9D]
|
||||
- ring scan reaches F9B4 cursor: store R5 into the pending ring and advance F9B4 [H'BE80, H'BE82, H'BE91, H'BE95, H'BE99]
|
||||
- session timeout and resend side paths: present
|
||||
After any complete RX frame, F9C5 keeps FAA5.7/session-gate state alive for a short window. When that window expires, 3FEF can clear queue cursors and call 400C, while BE9E handles resend countdowns.
|
||||
- F9C5 == 0 at loc_3FEF: clear F9B5/F9B0 and clear FAA5.7; if FAA5.7 was set, call 400C reset/NOT-ACT state clear [H'3FEF, H'3FF3, H'3FF5, H'3FF9, H'3FFD, H'4001, H'4003]
|
||||
- F9C5 != 0 at loc_3FEF: set FAA5.7, allowing retry/resend/session-gated paths [H'3FEF, H'3FF3, H'4007]
|
||||
- BE9E sees no pending FAA5 & FAA3 & 0x80: clear FAA2 and return [H'BE9E, H'BEA5, H'BEA9, H'BEAD, H'BEAF]
|
||||
- BE9E pending and F9C6==0 and F9C8!=0: decrement F9C8, reload F9C6, and possibly resend staged TX through BA26 [H'BEB5, H'BEBB, H'BEC1, H'BEC5, H'BECB, H'BED1, H'BED5]
|
||||
- BE9E pending but F9C8==0: clear F9C5, which lets 3FEF collapse the session gate later [H'BEBB, H'BEBF, H'BEE4]
|
||||
|
||||
Command Branches:
|
||||
- cmd 0x00 set_value_acked_candidate: initial path only: checksum valid, FAA2 == 0, RX[1].7 == 0; handler H'BC69; writes RX[3:4] into primary/current tables, flags the selector, calls BE70, and sends an echo-style 0x04 response
|
||||
response: F850=0x04; F851-F854 mostly echo RX[1:4]; BA26 sends it
|
||||
- selector zero is special: the low byte is forced to 0x80 after the high byte is taken from RX[3]
|
||||
- nonzero selectors can mirror into an auxiliary table via a mapping table
|
||||
- clears FAA2.7 before exit
|
||||
- cmd 0x01 read_value_candidate: initial path only: checksum valid, FAA2 == 0, RX[1].7 == 0; handler H'BCD7; reads primary table E000 + 2*selector and stages a 0x04 response
|
||||
response: F850=0x04; F851 is overwritten with RX[2]; F853/F854 receive table high/low; F852 is not freshly written here
|
||||
- does not enter continuation command handling
|
||||
- clears FAA2.7 before exit
|
||||
- cmd 0x02 initial_clear_or_noop_candidate: initial path only: checksum valid, FAA2 == 0, RX[1].7 == 0; handler H'BD04; clears FAA2.7 and returns without staging a response
|
||||
response: no immediate serial response
|
||||
- likely a quiet/session-clear style command on the initial path
|
||||
- cmd 0x04 continuation_set_value_candidate: continuation path only: checksum valid, FAA2 != 0, command bit2 set, RX[1].7 == 0; handler H'BD0E; writes a value into the primary table without an immediate serial response; selector zero also updates the current/report table
|
||||
response: no immediate serial response
|
||||
- selector zero is special: RX[3] becomes the high byte and low byte is forced to 0x80
|
||||
- nonzero selectors write E000 and flag EC00.7; the matching E800 current/report write is not present in this handler
|
||||
- nonzero selectors can mirror/persist through F400/BFE0 when mapping and F76E.7 allow it
|
||||
- if FAA2.3 was set from a queued report, advances F9B5 to consume that report
|
||||
- clears FAA3 and FAA2 before exit
|
||||
- cmd 0x05 continuation_ack_or_clear_pending_candidate: continuation path only: checksum valid, FAA2 != 0, command bit2 set, RX[1].7 == 0; handler H'BD80; ACK/session-clear path; usually no response, but selected logical indexes feed BE70 or clear connection latches
|
||||
response: no immediate serial response
|
||||
- selectors 0x006C, 0x006D, and 0x006E call BE70
|
||||
- with F731.7 set, selectors 0x006B, 0x0096, 0x0097, 0x00C6, and 0x00F8 clear F731.7/F790.7
|
||||
- if FAA2.3 was set from a queued report, advances F9B5
|
||||
- clears FAA3 and FAA2 before exit
|
||||
- cmd 0x06 continuation_set_secondary_candidate: continuation path only: checksum valid, FAA2 != 0, command bit2 set, RX[1].7 == 0; handler H'BDDB; writes RX[3:4] into the secondary table and sets flag-table bit 6
|
||||
response: no immediate serial response
|
||||
- if FAA2.3 was set from a queued report, advances F9B5
|
||||
- clears FAA3 and FAA2 before exit
|
||||
- cmd 0x07 retransmit_previous_tx_candidate: initial or continuation path; handler H'BE05; copies the previous finalized TX frame bytes back into staging and sends them again
|
||||
response: previous TX frame retransmitted through BA26
|
||||
- loads F9C0 with 0x1F before sending
|
||||
|
||||
Table Surfaces:
|
||||
- primary_value_table H'E000-H'E3FF: present; logical selector word table; command 0 and continuation command 4 write it, command 1 reads it [H'BC75, H'BC95, H'BCEC, H'BD1A, H'BD35]
|
||||
- indexed as E000 + 2*selector after loc_622B
|
||||
- selector zero writes force the low byte to 0x80 on commands 0 and 4
|
||||
- this is the table that must contain E000[0]=0x8080 for the emulator-correlated CONNECT OK branch
|
||||
- current_report_value_table H'E800-H'EBFF: present; current/report value table used when queued serial reports are converted into TX frames [H'BC79, H'BC99, H'BD1E, H'BB35, H'BB39, H'BB3F]
|
||||
- command 0 writes both primary E000 and current E800 for zero and nonzero selectors
|
||||
- command 4 writes E800 only on the selector-zero special path; the nonzero command-4 path does not show a matching E800 write here
|
||||
- loc_BAF2 reads E800 + 2*queued_selector when building autonomous report frames
|
||||
- secondary_value_table H'E400-H'E7FF: present; secondary logical selector word table written by continuation command 6 [H'BDE5, H'BDE9]
|
||||
- command 6 writes RX[3:4] to E400 + 2*selector
|
||||
- the matching EC00 flag bit is bit6 rather than bit7
|
||||
- dirty_flag_table H'EC00-H'EDFF: present; per-selector flag bytes; command 0/4 set bit7 and command 6 sets bit6 [H'BC82, H'BC9D, H'BD22, H'BD39, H'BDE9]
|
||||
- the same logical selector indexes this byte table directly, not as a word offset
|
||||
- loc_48FA and other consumers test these table bits before raising follow-on reports
|
||||
- mapped_shadow_or_eeprom_surface H'F400-H'F4FF: present; optional mapped mirror/persistence surface selected through ROM tables around C564/C565 [H'BCA1, H'BCA9, H'BD3D, H'BD45, H'BD49, H'BD5F]
|
||||
- nonzero command-0 and command-4 writes consult mapping bytes/words before mirroring into F400
|
||||
- command 4 can call BFE0 to persist the mapped word when F76E.7 is set
|
||||
- selector zero bypasses this mapped mirror path
|
||||
|
||||
Downstream Flow Traces:
|
||||
- immediate response staging through loc_BA26: present
|
||||
Command 0, command 1, command 7, and the retry/error path stage F850-F854, call BA26, copy that staging area to F858-F85C, compute F85D, send the first byte, then let TXI send bytes 1-5.
|
||||
- stage command-0 echo: F850=0x04 and F851-F854 mirror the accepted host fields before BA26 [H'BCB0, H'BCB5, H'BCC1, H'BCC9, H'BCCD]
|
||||
- stage command-1 readback: F850=0x04, F853/F854 receive the E000 table word, and BA26 sends it [H'BCD7, H'BCEC, H'BCF0, H'BCF6, H'BCFA]
|
||||
- finalize TX frame: BA26 copies F850-F854 to F858-F85C and computes F85D as 0x5A XOR bytes 0-4 [H'BA36, H'BA3A, H'BA42, H'BA4A, H'BA4E, H'BA64]
|
||||
- start SCI1 transmission: BA26 waits for TDRE, writes F858 to SCI1_TDR, sets F9C2=1, clears TDRE, and enables TIE [H'BA68, H'BA72, H'BA76, H'BA7B, H'BA7F]
|
||||
- finish SCI1 transmission: TXI indexes F858+F9C2 until six bytes are sent, then disables TIE [H'BAAB, H'BAB1, H'BAB5, H'BABF, H'BAC3, H'BACA]
|
||||
- selector-processing queue BE70/F970 into loc_2806: present
|
||||
BE70 appends unique logical selectors into the F970 ring. The main loop later consumes that ring at loc_2806 and dispatches selector-specific behavior through the 28A6 jump table.
|
||||
- append unique selector: BE70 scans from F9B9 to F9B4, skips duplicates, writes R5 to F970+2*cursor, and advances F9B4 [H'BE70, H'BE78, H'BE84, H'BE91, H'BE95, H'BE99]
|
||||
- consume selector: loc_2806 reads F970+2*F9B9, advances F9B9, masks the selector to 0x01FF, and keeps it in R5 [H'2806, H'280C, H'2819, H'281D, H'2822, H'2826, H'282A]
|
||||
- active-selector side path: selectors matching F736/F738/F73A/F73C/F73E/F740/F742/F754 call loc_48FA before the jump table [H'2837, H'285E, H'2878, H'2892, H'2CAB, H'2CAD]
|
||||
- selector jump table: the consumer jumps through table 28A6; selector zero is emulator-correlated with the CONNECT handler window [H'289F, H'28A3]
|
||||
- serial-report queue loc_3E54/F870 into loc_BAF2: present
|
||||
loc_3E54 is a shared enqueue helper. R2.bit7 queues serial-visible report indexes in F870, while R2.bit6 queues selector-processing work in F970. loc_BAF2 is the path that turns F870 entries into outbound 6-byte frames.
|
||||
- enqueue serial report: when R2.7 is set, loc_3E54 deduplicates R3 in F870 and advances F9B0 [H'3E54, H'3E58, H'3E6C, H'3E76, H'3E7A, H'3E7E]
|
||||
- queue backpressure drain: if the serial queue is nearly full, loc_3E54 calls loc_3FD3 until there is space [H'3E82, H'3E8B, H'3E91, H'3E93]
|
||||
- enqueue selector processing: when R2.6 is set, loc_3E54 deduplicates R3 in F970 and advances F9B4 [H'3E9A, H'3E9E, H'3EB2, H'3EBF, H'3EC3, H'3EC7]
|
||||
- dequeue serial report: loc_BAF2 compares F9B5/F9B0, reads F870+2*F9B5, builds a TX report from E800, and calls BA26 [H'BAF2, H'BAF8, H'BB00, H'BB08, H'BB1C, H'BB35, H'BB43]
|
||||
- open continuation latch: after a report send, loc_BAF2 sets FAA2.3, FAA3.7, F9C6, and F9C8; command 4/5/6 can then consume or ACK that report [H'BB00, H'BB46, H'BB4C, H'BB51]
|
||||
- TXI/RXI race and continuation collapse: present
|
||||
The TX interrupt handler has an interlock: if a queued report is awaiting continuation and RX bytes are already arriving, it clears the report/continuation state and stops TXI before the normal frame-finish path.
|
||||
- detect overlap: TXI tests FAA2.3, FAA5.7, and nonzero F9C3 before continuing the TX frame [H'BA84, H'BA8A, H'BA90]
|
||||
- collapse continuation: on overlap it clears FAA2.3 and FAA3, disables TIE, and loads F9C0=0x1F [H'BA96, H'BA9A, H'BA9E, H'BAA2]
|
||||
- normal completion path: without the overlap, TXI sends bytes until F9C2 reaches six and then starts the post-TX delay path [H'BAA9, H'BAB5, H'BAC3, H'BACA, H'BADA, H'BAED]
|
||||
- session expiry into reset/not-active state: present
|
||||
When F9C5 reaches zero, loc_3FEF clears queue cursors and FAA5.7; if FAA5.7 had been set, loc_400C clears connection/session RAM and refreshes the inactive display state.
|
||||
- expire RX session: F9C5==0 clears F9B5/F9B0 and clears FAA5.7 [H'3FEF, H'3FF5, H'3FF9, H'3FFD]
|
||||
- call reset side path: if clearing FAA5.7 changed the bit, loc_3FEF calls loc_400C [H'4001, H'4003]
|
||||
- clear connection state: loc_400C clears F730/F756-F759/F732/F75C/FB03/F791/F795/F76E and calls follow-on display/session refresh routines [H'400C, H'4010, H'4020, H'4028, H'4034, H'403C, H'4040, H'4042]
|
||||
|
||||
RX-to-TX Feedback Loops:
|
||||
- command-0 write echo and selector-processing loop: present
|
||||
trigger: valid command 0 while FAA2 == 0
|
||||
path: RX validation -> BC69 table write -> BE70 appends selector to F970 -> BCB0/BA26 sends immediate 0x04 echo -> later loc_2806 consumes F970
|
||||
TX outcome: immediate command-4-style echo frame plus possible later selector-driven reports/display work
|
||||
timing/gate: BA26 sets F9C0=0x64 and F9C4=0x07, temporarily delaying queued TX and heartbeat enqueue
|
||||
bench read: a command-0 probe is stateful: it both writes table state and spends time in the post-TX delay, so it can disturb the continuation window being tested
|
||||
evidence: H'BC69, H'BC75, H'BC86, H'BCB0, H'BCCD, H'BA2C, H'BA31, H'BE70, H'2819, H'28A3
|
||||
- command-1 readback and previous-frame loop: present
|
||||
trigger: valid command 1 while FAA2 == 0, followed by optional command 7
|
||||
path: RX validation -> BCD7 reads E000[selector] -> BA26 finalizes TX in F858-F85D -> BE05 can copy F858-F85C back to F850-F854
|
||||
TX outcome: direct 0x04 readback, then command 7 can retransmit the exact last finalized TX frame
|
||||
timing/gate: command 1 clears FAA2.7 and never enters continuation handling; it is a readback path, not an ACK path
|
||||
bench read: command 7 after command 1 proves what was last finalized, but does not prove a hidden continuation token by itself
|
||||
evidence: H'BCD7, H'BCEC, H'BCFA, H'BA36, H'BA64, H'BE05, H'BE09, H'BE22
|
||||
- retry/error 07 echo loop: present
|
||||
trigger: physical RX error or checksum mismatch while FAA5.7 is set and retry count FAA6 is below two
|
||||
path: RX validation error -> BE29 retry gate -> BE4D stages F850=0x07 and copies RX[1:4] into F851-F854 -> BA26 sends
|
||||
TX outcome: a 0x07 frame that can echo the host payload bytes, independent of E000/E800 table contents
|
||||
timing/gate: after two retries, the ROM loads F9C0=0x1F and clears FAA3/FAA2 instead of sending another echo
|
||||
bench read: visible 07 frames in a noisy/tight timing run can be retry echoes, not necessarily device status or a continuation token
|
||||
evidence: H'BE29, H'BE2D, H'BE33, H'BE37, H'BE3E, H'BE43, H'BE47, H'BE4D, H'BE6A
|
||||
- autonomous report to host continuation loop: present
|
||||
trigger: firmware enqueues a serial-visible report via loc_3E54 with R2.7 set
|
||||
path: loc_3E54 appends report selector to F870 -> loc_3FD3 allows BAF2 when FAA2/F9C0 gates are clear -> BAF2 sends report -> command 4/5/6 continuation can advance F9B5
|
||||
TX outcome: autonomous 6-byte report frame built from E800[selector], with FAA2.3/FAA3.7 left set to await host continuation or ACK
|
||||
timing/gate: F9C0 must count down before BAF2 can send, and F9C6/F9C8/BE9E control repeated sends while FAA3.7 remains live
|
||||
bench read: the actual ACK/write target is not just the selector; it is the report that is live under FAA2.3 before TXI/RXI or BE9E clears it
|
||||
evidence: H'3E54, H'3E76, H'3E7A, H'3FD3, H'3FE5, H'3FEB, H'BAF2, H'BB00, H'BB35, H'BB43, H'BB46, H'BB51, H'BD67, H'BDC2, H'BDED
|
||||
- selector-processing to report loop: present
|
||||
trigger: command 0/4/selected command 5 calls BE70, or loc_3E54 is called with R2.6 set
|
||||
path: BE70/F970 selector queue -> loc_2806 selector dispatch -> active-selector side path loc_48FA -> loc_3E54 can enqueue report 0x00F6 when E1EC.13 is set
|
||||
TX outcome: possible later serial report produced through the F870/BAF2 loop rather than an immediate response to the original RX frame
|
||||
timing/gate: loc_48FA is gated by FB03.7, F732 values, E1EC.13, and F76E.6 before reaching its indirect table
|
||||
bench read: selector-zero CONNECT work and visible serial reports can be separated in time; lack of immediate TX does not mean the selector queue did nothing
|
||||
evidence: H'BE70, H'BE91, H'2806, H'2819, H'2CAD, H'48FA, H'490F, H'4921, H'4923, H'4926
|
||||
- TXI/RXI overlap cancellation loop: present
|
||||
trigger: host RX bytes begin while a report TX is active, FAA2.3 is set, and FAA5.7 is set
|
||||
path: TXI observes FAA2.3 + FAA5.7 + F9C3 != 0 -> clears FAA2.3/FAA3 -> disables TIE -> loads F9C0=0x1F
|
||||
TX outcome: the pending report/continuation state can be canceled before the host command reaches the continuation dispatcher
|
||||
timing/gate: this depends on byte timing relative to TXI and F9C3, so polite emulator injection can miss it
|
||||
bench read: this is a concrete ROM reason why bench timing might see latches/retries while a too-polite emulator reaches cleaner continuation paths
|
||||
evidence: H'BA84, H'BA8A, H'BA90, H'BA96, H'BA9A, H'BA9E, H'BAA2
|
||||
- session-expiry to heartbeat/not-active loop: present
|
||||
trigger: any complete six-byte RX frame loads F9C5, then FRT2 decrements it to zero
|
||||
path: RX complete -> F9C5=0x14 -> FRT2 decrements F9C5 -> loc_3FEF clears session/queues and calls 400C -> loc_4046 can later enqueue heartbeat selector 0
|
||||
TX outcome: eventual return to idle heartbeat/report behavior and inactive-session display state
|
||||
timing/gate: F9C4 gates heartbeat enqueue; BA26 reloads it to 0x07 after each send, matching the roughly 700 ms heartbeat cadence
|
||||
bench read: the common CONNECT NOT ACT after arbitrary six-byte traffic is consistent with this expiry/reset loop
|
||||
evidence: H'BB9E, H'BF31, H'BF37, H'3FEF, H'3FFD, H'4003, H'400C, H'4046, H'4067, H'BA31
|
||||
|
||||
State Bits:
|
||||
- H'FAA2.7: rx_command_in_progress_candidate - set on initial-path parse; cleared by command 0/1/2 exits or by continuation cleanup
|
||||
- H'FAA2.3: queued_report_ack_needed_candidate - set by the autonomous queue send path at BB00; continuation command 4/5/6 can advance F9B5 only when this bit was set
|
||||
- H'FAA3.7: pending_resend_mask_candidate - set after queued report send; BE9E masks it with FAA5.7 before resend/clear decisions
|
||||
- H'FAA4.7: rx_physical_error_latch_candidate - set by SCI1 ERI and tested before checksum dispatch
|
||||
- H'FAA5.7: rx_session_gate_candidate - set while F9C5 is alive after a complete RX frame; gates retry/resend and heartbeat/report enqueue behavior
|
||||
|
||||
Bench Implications:
|
||||
- A standalone command 4 frame from idle should not hit BD0E; command 4 is continuation-only and initial dispatch does not accept it.
|
||||
- Command 5 is not a generic always-live ACK. It only performs ACK/session-clear work on the continuation path while FAA2 != 0.
|
||||
- A valid six-byte RX frame loads F9C5 with 0x14, which temporarily sets FAA5.7; when that window expires, loc_3FEF can clear queue/session state and call loc_400C.
|
||||
- The observed 07 retry/error family can be produced by BE4D from RX[1:4] after error/checksum retry conditions; it is not automatically proof of a table value or a command-4 continuation token.
|
||||
- To prove CONNECT OK through serial, the bench has to create or preserve FAA2 != 0 at the moment command 4 arrives, and ideally FAA2.3 if it expects queued-report advancement.
|
||||
|
||||
Caveats:
|
||||
- This is an address-driven static branch map, not proof of every runtime predicate value.
|
||||
- Semantic names are candidates; the branch destinations and RAM side effects are ROM evidence.
|
||||
- loc_BE70/F970 and loc_3E54/F870 are separate queues; this report names them separately because mixing them up changes the command-4/5 interpretation.
|
||||
597
h8536/ccu_seed_hints.py
Normal file
597
h8536/ccu_seed_hints.py
Normal file
@@ -0,0 +1,597 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
from collections import Counter, defaultdict
|
||||
from collections.abc import Mapping
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
from .formatting import h16
|
||||
from .lcd_text import analyze_lcd_text
|
||||
from .rom import Rom
|
||||
from .serial_semantics import OBSERVED_TX_REPORT_OVERLAY
|
||||
from .table_xrefs import analyze_table_xrefs
|
||||
|
||||
|
||||
JsonObject = dict[str, Any]
|
||||
|
||||
DEFAULT_INPUT = Path("build/rom_decompiled.json")
|
||||
DEFAULT_ROM = Path("ROM/M27C512@DIP28_1.BIN")
|
||||
CHECKSUM_SEED = 0x5A
|
||||
DISPLAY_TERMS = (
|
||||
"CONNECT",
|
||||
"COMM LINK",
|
||||
"COMPLETED",
|
||||
"CAM",
|
||||
"BARS",
|
||||
"BLACK",
|
||||
"IRIS",
|
||||
"GAIN",
|
||||
"SHUTTER",
|
||||
"CALL",
|
||||
"POWER",
|
||||
"AUTO",
|
||||
"DIAG",
|
||||
"DXC",
|
||||
)
|
||||
|
||||
SPECIAL_SELECTORS: tuple[JsonObject, ...] = (
|
||||
{
|
||||
"selector": 0x000,
|
||||
"name": "connection_or_heartbeat_root_candidate",
|
||||
"reason": "idle report selector and CONNECT OK emulator condition both center on selector zero",
|
||||
"seed_values": [0x0080, 0x8080],
|
||||
},
|
||||
{
|
||||
"selector": 0x003,
|
||||
"name": "default_enabled_bit_candidate",
|
||||
"reason": "ROM default table writes E000/E800 selector 0x003 to 0x8000",
|
||||
"seed_values": [0x8000],
|
||||
},
|
||||
{
|
||||
"selector": 0x040,
|
||||
"name": "default_all_ones_or_status_block_candidate",
|
||||
"reason": "ROM default table writes E000/E800 selector 0x040 to 0xFFFF and bench tests repeatedly touched the 0x40 family",
|
||||
"seed_values": [0xFFFF, 0x4030],
|
||||
},
|
||||
{
|
||||
"selector": 0x0F6,
|
||||
"name": "active_status_bridge_candidate",
|
||||
"reason": "loc_48FA tests E1EC bit13 and can enqueue report selector 0x00F6",
|
||||
"seed_values": [0x2000],
|
||||
},
|
||||
{
|
||||
"selector": 0x006C,
|
||||
"name": "command5_be70_candidate",
|
||||
"reason": "continuation command 5 calls BE70 for selector 0x006C",
|
||||
"seed_values": [],
|
||||
},
|
||||
{
|
||||
"selector": 0x006D,
|
||||
"name": "command5_be70_candidate",
|
||||
"reason": "continuation command 5 calls BE70 for selector 0x006D",
|
||||
"seed_values": [],
|
||||
},
|
||||
{
|
||||
"selector": 0x006E,
|
||||
"name": "command5_be70_candidate",
|
||||
"reason": "continuation command 5 calls BE70 for selector 0x006E",
|
||||
"seed_values": [],
|
||||
},
|
||||
{
|
||||
"selector": 0x006B,
|
||||
"name": "connection_latch_clear_candidate",
|
||||
"reason": "when F731.7 is set, command 5 on this selector clears F731.7/F790.7",
|
||||
"seed_values": [],
|
||||
},
|
||||
{
|
||||
"selector": 0x0096,
|
||||
"name": "connection_latch_clear_candidate",
|
||||
"reason": "when F731.7 is set, command 5 on this selector clears F731.7/F790.7",
|
||||
"seed_values": [],
|
||||
},
|
||||
{
|
||||
"selector": 0x0097,
|
||||
"name": "connection_latch_clear_candidate",
|
||||
"reason": "when F731.7 is set, command 5 on this selector clears F731.7/F790.7",
|
||||
"seed_values": [],
|
||||
},
|
||||
{
|
||||
"selector": 0x00C6,
|
||||
"name": "connection_latch_clear_candidate",
|
||||
"reason": "when F731.7 is set, command 5 on this selector clears F731.7/F790.7",
|
||||
"seed_values": [],
|
||||
},
|
||||
{
|
||||
"selector": 0x00F8,
|
||||
"name": "connection_latch_clear_candidate",
|
||||
"reason": "when F731.7 is set, command 5 on this selector clears F731.7/F790.7",
|
||||
"seed_values": [],
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
def load_seed_hints_input(path: Path) -> JsonObject:
|
||||
with path.open("r", encoding="utf-8") as handle:
|
||||
payload = json.load(handle)
|
||||
if not isinstance(payload, dict) or "instructions" not in payload:
|
||||
raise ValueError(f"{path} does not look like h8536_decompiler JSON output")
|
||||
return payload
|
||||
|
||||
|
||||
def analyze_ccu_seed_hints(payload: Mapping[str, Any], *, rom_path: Path | None = DEFAULT_ROM) -> JsonObject:
|
||||
table_analysis = analyze_table_xrefs(payload)
|
||||
selector_hints = _selector_hints_from_tables(table_analysis)
|
||||
_merge_special_selectors(selector_hints)
|
||||
_merge_observed_reports(selector_hints)
|
||||
|
||||
dispatch = _dispatch_table_summary(payload, rom_path)
|
||||
for entry in dispatch.get("interesting_entries", []):
|
||||
selector = int(entry["selector"])
|
||||
hint = selector_hints.setdefault(selector, _new_selector_hint(selector))
|
||||
hint["score"] += 2
|
||||
hint["reasons"].append(f"selector dispatches to {entry['target_label_or_hex']}")
|
||||
hint["dispatch_target"] = entry
|
||||
|
||||
display = _display_hint_summary(payload, rom_path)
|
||||
seed_plan = _seed_plan(selector_hints)
|
||||
|
||||
candidates = sorted(
|
||||
selector_hints.values(),
|
||||
key=lambda item: (-int(item["score"]), int(item["selector"])),
|
||||
)
|
||||
return {
|
||||
"kind": "ccu_seed_hints",
|
||||
"summary": {
|
||||
"candidate_count": len(candidates),
|
||||
"core_model": (
|
||||
"The RCP likely waits for the CCU to seed mirrored state tables, then uses those "
|
||||
"selector values to update LCD text, panel lamps, and report state changes."
|
||||
),
|
||||
"confidence": "medium",
|
||||
},
|
||||
"table_model": _table_model_summary(table_analysis),
|
||||
"selector_candidates": candidates[:80],
|
||||
"display_text_hints": display,
|
||||
"dispatch_table": dispatch,
|
||||
"seed_plan": seed_plan,
|
||||
"bench_implications": [
|
||||
"Do not wait for non-heartbeat reports as the only activation source; the CCU may be expected to push initial table state first.",
|
||||
"Use command 0 writes for initial seeding, then command 1 readbacks for verification. Treat command 4/5/6 as continuation-only until a live report proves otherwise.",
|
||||
"Selector zero remains the highest-value activation candidate because the emulator reaches CONNECT OK when E000[0]=0x8080 and the selector-zero processing queue runs.",
|
||||
"E1EC/selector 0x00F6 is a strong follow-up candidate because loc_48FA tests bit13 there and can enqueue report 0x00F6.",
|
||||
"LCD text terms such as CAM/BARS/BLACK/COMM LINK appear in ROM records, but they are not direct serial payload strings; they point to selector-driven display builders.",
|
||||
],
|
||||
"caveats": [
|
||||
"Selector names are candidates, not confirmed protocol labels.",
|
||||
"Static table xrefs prove that firmware reads/writes a selector; they do not prove the external CCU must seed it on boot.",
|
||||
"Generated frames are syntactically valid six-byte host frames; bench safety still depends on timing and current RCP state.",
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
def format_text_report(analysis: Mapping[str, Any]) -> str:
|
||||
summary = analysis["summary"]
|
||||
lines = [
|
||||
"H8/536 CCU Seed Hint Report",
|
||||
"",
|
||||
f"Summary: {summary['core_model']}",
|
||||
f"Confidence: {summary['confidence']}",
|
||||
"",
|
||||
"Table Model:",
|
||||
]
|
||||
for table in analysis.get("table_model", []):
|
||||
lines.append(
|
||||
f"- {table['name']}: {table['logical_range_hex']}; accesses={table['access_count']} "
|
||||
f"static selectors={', '.join(table.get('static_selectors_hex', [])[:12]) or 'none'}"
|
||||
)
|
||||
|
||||
lines.extend(["", "Highest-Value Selector Candidates:"])
|
||||
for hint in analysis.get("selector_candidates", [])[:24]:
|
||||
lines.append(
|
||||
f"- {hint['selector_hex']} {hint['name']}: score={hint['score']} "
|
||||
f"tables={', '.join(hint.get('tables', [])) or 'none'}"
|
||||
)
|
||||
for reason in hint.get("reasons", [])[:4]:
|
||||
lines.append(f" - {reason}")
|
||||
frames = hint.get("seed_frames", [])
|
||||
if frames:
|
||||
frame_text = "; ".join(f"{frame['value_hex']} -> {frame['cmd0_frame']}" for frame in frames[:3])
|
||||
lines.append(f" seed frames: {frame_text}")
|
||||
read_frame = hint.get("cmd1_read_frame")
|
||||
if read_frame:
|
||||
lines.append(f" readback frame: {read_frame}")
|
||||
|
||||
display = analysis.get("display_text_hints", {})
|
||||
lines.extend(["", "Display Text Hints:"])
|
||||
for hit in display.get("term_hits", [])[:16]:
|
||||
samples = ", ".join(
|
||||
f"{sample['address_hex']} {sample['text']!r}"
|
||||
for sample in hit.get("samples", [])[:3]
|
||||
)
|
||||
lines.append(f"- {hit['term']}: {hit['hit_count']} hit(s){f' - {samples}' if samples else ''}")
|
||||
|
||||
dispatch = analysis.get("dispatch_table", {})
|
||||
lines.extend(["", "Selector Dispatch Hints:"])
|
||||
lines.append(
|
||||
f"- table {dispatch.get('table_base_hex', 'unknown')}: "
|
||||
f"{dispatch.get('interesting_count', 0)} non-default/interesting entries"
|
||||
)
|
||||
for entry in dispatch.get("interesting_entries", [])[:16]:
|
||||
lines.append(
|
||||
f" - selector {entry['selector_hex']} -> {entry['target_label_or_hex']} "
|
||||
f"(dispatch index {entry['dispatch_index_hex']})"
|
||||
)
|
||||
|
||||
seed_plan = analysis.get("seed_plan", {})
|
||||
lines.extend(["", "Candidate Fake-CCU Seed Plan:"])
|
||||
for step in seed_plan.get("steps", []):
|
||||
lines.append(f"- {step['name']}: {step['frame']}")
|
||||
lines.append(f" {step['why']}")
|
||||
|
||||
lines.extend(["", "Bench Implications:"])
|
||||
for item in analysis.get("bench_implications", []):
|
||||
lines.append(f"- {item}")
|
||||
|
||||
lines.extend(["", "Caveats:"])
|
||||
for item in analysis.get("caveats", []):
|
||||
lines.append(f"- {item}")
|
||||
return "\n".join(lines).rstrip() + "\n"
|
||||
|
||||
|
||||
def write_ccu_seed_hints(
|
||||
input_path: Path,
|
||||
output_path: Path,
|
||||
*,
|
||||
rom_path: Path | None = DEFAULT_ROM,
|
||||
as_json: bool = False,
|
||||
) -> JsonObject:
|
||||
analysis = analyze_ccu_seed_hints(load_seed_hints_input(input_path), rom_path=rom_path)
|
||||
output_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
if as_json:
|
||||
output_path.write_text(json.dumps(analysis, indent=2, sort_keys=True) + "\n", encoding="utf-8")
|
||||
else:
|
||||
output_path.write_text(format_text_report(analysis), encoding="utf-8")
|
||||
return analysis
|
||||
|
||||
|
||||
def main(argv: list[str] | None = None, stdout: Any | None = None) -> int:
|
||||
parser = argparse.ArgumentParser(description="Mine ROM hints for CCU-to-RCP state seeding candidates.")
|
||||
parser.add_argument("input", nargs="?", type=Path, default=DEFAULT_INPUT)
|
||||
parser.add_argument("--rom", type=Path, default=DEFAULT_ROM, help="ROM binary used for LCD text and dispatch-table mining")
|
||||
parser.add_argument("--json", action="store_true", help="emit structured JSON instead of readable text")
|
||||
parser.add_argument("--out", type=Path, default=None, help="write report to this path")
|
||||
args = parser.parse_args(argv)
|
||||
|
||||
stream = stdout
|
||||
if stream is None:
|
||||
import sys
|
||||
|
||||
stream = sys.stdout
|
||||
|
||||
rom_path = args.rom if args.rom and args.rom.exists() else None
|
||||
analysis = analyze_ccu_seed_hints(load_seed_hints_input(args.input), rom_path=rom_path)
|
||||
rendered = json.dumps(analysis, indent=2, sort_keys=True) + "\n" if args.json else format_text_report(analysis)
|
||||
if args.out:
|
||||
args.out.parent.mkdir(parents=True, exist_ok=True)
|
||||
args.out.write_text(rendered, encoding="utf-8")
|
||||
print(f"wrote {args.out}", file=stream)
|
||||
else:
|
||||
print(rendered, end="", file=stream)
|
||||
return 0
|
||||
|
||||
|
||||
def encode_host_frame(command: int, selector: int, value: int = 0) -> list[int]:
|
||||
byte1, byte2 = selector_bytes(selector)
|
||||
frame = [command & 0x07, byte1, byte2, (value >> 8) & 0xFF, value & 0xFF]
|
||||
frame.append(checksum(frame))
|
||||
return frame
|
||||
|
||||
|
||||
def frame_hex(frame: list[int]) -> str:
|
||||
return " ".join(f"{byte & 0xFF:02X}" for byte in frame)
|
||||
|
||||
|
||||
def selector_bytes(selector: int) -> tuple[int, int]:
|
||||
selector &= 0x01FF
|
||||
if selector <= 0x007F:
|
||||
return 0x00, selector
|
||||
if selector <= 0x017F:
|
||||
return 0x01, selector - 0x0080
|
||||
return 0x02, selector - 0x0180
|
||||
|
||||
|
||||
def checksum(frame_without_checksum: list[int]) -> int:
|
||||
value = CHECKSUM_SEED
|
||||
for byte in frame_without_checksum[:5]:
|
||||
value ^= byte & 0xFF
|
||||
return value & 0xFF
|
||||
|
||||
|
||||
def _selector_hints_from_tables(table_analysis: Mapping[str, Any]) -> dict[int, JsonObject]:
|
||||
hints: dict[int, JsonObject] = {}
|
||||
for table in table_analysis.get("tables", []):
|
||||
if not isinstance(table, Mapping):
|
||||
continue
|
||||
table_name = str(table.get("name", "unknown_table"))
|
||||
element = str(table.get("element_candidate", ""))
|
||||
for access in table.get("accesses", []):
|
||||
if not isinstance(access, Mapping) or not isinstance(access.get("offset"), int):
|
||||
continue
|
||||
selector = _selector_from_offset(int(access["offset"]), element)
|
||||
if selector is None:
|
||||
continue
|
||||
hint = hints.setdefault(selector, _new_selector_hint(selector))
|
||||
hint["score"] += _access_score(access, table_name)
|
||||
if table_name not in hint["tables"]:
|
||||
hint["tables"].append(table_name)
|
||||
hint["accesses"].append(
|
||||
{
|
||||
"address_hex": access.get("instruction_address_hex"),
|
||||
"function": access.get("function_label"),
|
||||
"table": table_name,
|
||||
"access": access.get("access"),
|
||||
"instruction": access.get("instruction"),
|
||||
}
|
||||
)
|
||||
reason = _access_reason(access, table_name)
|
||||
if reason not in hint["reasons"]:
|
||||
hint["reasons"].append(reason)
|
||||
return hints
|
||||
|
||||
|
||||
def _merge_special_selectors(hints: dict[int, JsonObject]) -> None:
|
||||
for item in SPECIAL_SELECTORS:
|
||||
selector = int(item["selector"])
|
||||
hint = hints.setdefault(selector, _new_selector_hint(selector))
|
||||
hint["score"] += 5
|
||||
hint["name"] = str(item["name"])
|
||||
hint["reasons"].append(str(item["reason"]))
|
||||
for value in item.get("seed_values", []):
|
||||
_add_seed_value(hint, int(value))
|
||||
|
||||
|
||||
def _merge_observed_reports(hints: dict[int, JsonObject]) -> None:
|
||||
for report in OBSERVED_TX_REPORT_OVERLAY:
|
||||
selector = int(report["logical_index"])
|
||||
hint = hints.setdefault(selector, _new_selector_hint(selector))
|
||||
hint["score"] += 3
|
||||
hint["name"] = str(report["name_candidate"])
|
||||
frames = ", ".join(str(frame) for frame in report.get("observed_frames_hex", []))
|
||||
hint["reasons"].append(f"observed RCP autonomous report frame(s): {frames}")
|
||||
|
||||
|
||||
def _seed_plan(hints: Mapping[int, JsonObject]) -> JsonObject:
|
||||
planned = [
|
||||
(0x000, 0x8080, "selector zero active/connect candidate from emulator state search"),
|
||||
(0x003, 0x8000, "ROM default state also sets selector 0x003 high bit"),
|
||||
(0x040, 0xFFFF, "ROM default all-ones/status candidate touched by bench 0x40 family"),
|
||||
(0x0F6, 0x2000, "sets E1EC bit13 candidate used by loc_48FA report bridge"),
|
||||
]
|
||||
steps: list[JsonObject] = []
|
||||
for selector, value, why in planned:
|
||||
frame = frame_hex(encode_host_frame(0x00, selector, value))
|
||||
readback = frame_hex(encode_host_frame(0x01, selector, 0))
|
||||
hint = hints.get(selector)
|
||||
if hint is not None:
|
||||
_add_seed_value(hint, value)
|
||||
steps.append(
|
||||
{
|
||||
"selector": selector,
|
||||
"selector_hex": f"0x{selector:03X}",
|
||||
"value": value,
|
||||
"value_hex": f"0x{value:04X}",
|
||||
"name": f"cmd0 seed selector 0x{selector:03X} = 0x{value:04X}",
|
||||
"frame": frame,
|
||||
"readback_frame": readback,
|
||||
"why": why,
|
||||
}
|
||||
)
|
||||
return {
|
||||
"model": "candidate initial CCU state push using command 0 writes, verified with command 1 reads",
|
||||
"steps": steps,
|
||||
}
|
||||
|
||||
|
||||
def _table_model_summary(table_analysis: Mapping[str, Any]) -> list[JsonObject]:
|
||||
rows: list[JsonObject] = []
|
||||
for table in table_analysis.get("tables", []):
|
||||
if not isinstance(table, Mapping):
|
||||
continue
|
||||
element = str(table.get("element_candidate", ""))
|
||||
selectors = []
|
||||
for offset in table.get("static_offsets", []):
|
||||
if isinstance(offset, int):
|
||||
selector = _selector_from_offset(offset, element)
|
||||
if selector is not None:
|
||||
selectors.append(selector)
|
||||
rows.append(
|
||||
{
|
||||
"name": table.get("name"),
|
||||
"logical_range_hex": f"{table.get('logical_base_address_hex')}-{table.get('logical_range_end_hex')}",
|
||||
"access_count": table.get("access_count", 0),
|
||||
"static_selectors": sorted(set(selectors)),
|
||||
"static_selectors_hex": [f"0x{selector:03X}" for selector in sorted(set(selectors))],
|
||||
}
|
||||
)
|
||||
return rows
|
||||
|
||||
|
||||
def _display_hint_summary(payload: Mapping[str, Any], rom_path: Path | None) -> JsonObject:
|
||||
del payload
|
||||
if rom_path is None or not rom_path.exists():
|
||||
return {"term_hits": [], "note": "ROM binary was not available for LCD text mining."}
|
||||
rom = Rom(rom_path.read_bytes())
|
||||
text = analyze_lcd_text(rom, None, search_terms=DISPLAY_TERMS, max_candidates=360)
|
||||
term_hits = []
|
||||
for search in text.get("searches", []):
|
||||
if not isinstance(search, Mapping):
|
||||
continue
|
||||
samples = []
|
||||
for address in search.get("literal_hits", [])[:4]:
|
||||
if isinstance(address, int):
|
||||
samples.append({"address_hex": h16(address), "text": f"literal {search.get('term')}"})
|
||||
for hit in search.get("candidate_hits", [])[:6]:
|
||||
if not isinstance(hit, Mapping):
|
||||
continue
|
||||
samples.append(
|
||||
{
|
||||
"address_hex": h16(int(hit.get("address", 0))),
|
||||
"text": str(hit.get("trimmed") or hit.get("text") or ""),
|
||||
}
|
||||
)
|
||||
hit_count = len(search.get("literal_hits", []) or []) + len(search.get("candidate_hits", []) or [])
|
||||
term_hits.append({"term": search.get("term"), "hit_count": hit_count, "samples": samples[:8]})
|
||||
return {
|
||||
"term_hits": term_hits,
|
||||
"regions": text.get("regions", [])[:8],
|
||||
"note": "Text hits are ROM display resources, not literal serial payloads.",
|
||||
}
|
||||
|
||||
|
||||
def _dispatch_table_summary(payload: Mapping[str, Any], rom_path: Path | None) -> JsonObject:
|
||||
table_base = 0x28A6
|
||||
entries = _indirect_entries(payload, table_base)
|
||||
if not entries and rom_path is not None and rom_path.exists():
|
||||
entries = _raw_dispatch_entries(rom_path, table_base, 128)
|
||||
if not entries:
|
||||
return {"table_base": table_base, "table_base_hex": h16(table_base), "interesting_count": 0, "interesting_entries": []}
|
||||
|
||||
target_counts = Counter(int(entry["target"]) for entry in entries if isinstance(entry.get("target"), int))
|
||||
default_target, _ = target_counts.most_common(1)[0]
|
||||
interesting: list[JsonObject] = []
|
||||
for entry in entries:
|
||||
index = int(entry["index"])
|
||||
selector = _selector_from_dispatch_index(index)
|
||||
if selector is None:
|
||||
continue
|
||||
target = int(entry["target"])
|
||||
label = entry.get("target_label") or h16(target)
|
||||
decoded = bool(entry.get("decoded_code", True))
|
||||
if target == default_target and decoded:
|
||||
continue
|
||||
interesting.append(
|
||||
{
|
||||
"selector": selector,
|
||||
"selector_hex": f"0x{selector:03X}",
|
||||
"dispatch_index": index,
|
||||
"dispatch_index_hex": f"0x{index:03X}",
|
||||
"entry_address_hex": h16(int(entry.get("entry_address", table_base + index * 2))),
|
||||
"target": target,
|
||||
"target_hex": h16(target),
|
||||
"target_label_or_hex": str(label),
|
||||
"decoded_code": decoded,
|
||||
}
|
||||
)
|
||||
return {
|
||||
"table_base": table_base,
|
||||
"table_base_hex": h16(table_base),
|
||||
"entry_count": len(entries),
|
||||
"default_target_hex": h16(default_target),
|
||||
"interesting_count": len(interesting),
|
||||
"interesting_entries": interesting[:80],
|
||||
}
|
||||
|
||||
|
||||
def _indirect_entries(payload: Mapping[str, Any], table_base: int) -> list[JsonObject]:
|
||||
indirect = payload.get("indirect_flow")
|
||||
if not isinstance(indirect, Mapping):
|
||||
return []
|
||||
for site in indirect.get("sites", []):
|
||||
if not isinstance(site, Mapping):
|
||||
continue
|
||||
table = site.get("table")
|
||||
if isinstance(table, Mapping) and int(table.get("base", -1)) == table_base:
|
||||
entries = table.get("entries", [])
|
||||
if isinstance(entries, list):
|
||||
return [dict(entry) for entry in entries if isinstance(entry, Mapping)]
|
||||
return []
|
||||
|
||||
|
||||
def _raw_dispatch_entries(rom_path: Path, table_base: int, count: int) -> list[JsonObject]:
|
||||
rom = Rom(rom_path.read_bytes())
|
||||
entries = []
|
||||
for index in range(count):
|
||||
address = table_base + index * 2
|
||||
if not rom.contains(address, 2):
|
||||
break
|
||||
target = rom.u16(address)
|
||||
entries.append({"index": index, "entry_address": address, "target": target, "target_label": None, "decoded_code": True})
|
||||
return entries
|
||||
|
||||
|
||||
def _selector_from_dispatch_index(index: int) -> int | None:
|
||||
if 0 <= index <= 0x007F:
|
||||
return index
|
||||
if 0x0100 <= index <= 0x01FF:
|
||||
return index - 0x0080
|
||||
if 0x0200 <= index <= 0x027F:
|
||||
return index - 0x0080
|
||||
return None
|
||||
|
||||
|
||||
def _selector_from_offset(offset: int, element: str) -> int | None:
|
||||
if element == "word_value":
|
||||
if offset % 2:
|
||||
return None
|
||||
return (offset // 2) & 0x01FF
|
||||
return offset & 0x01FF
|
||||
|
||||
|
||||
def _new_selector_hint(selector: int) -> JsonObject:
|
||||
return {
|
||||
"selector": selector,
|
||||
"selector_hex": f"0x{selector:03X}",
|
||||
"name": "state_selector_candidate",
|
||||
"score": 0,
|
||||
"tables": [],
|
||||
"reasons": [],
|
||||
"accesses": [],
|
||||
"seed_frames": [],
|
||||
"cmd1_read_frame": frame_hex(encode_host_frame(0x01, selector, 0)),
|
||||
}
|
||||
|
||||
|
||||
def _add_seed_value(hint: JsonObject, value: int) -> None:
|
||||
frames = hint.setdefault("seed_frames", [])
|
||||
value_hex = f"0x{value & 0xFFFF:04X}"
|
||||
if any(frame.get("value_hex") == value_hex for frame in frames):
|
||||
return
|
||||
frames.append(
|
||||
{
|
||||
"value": value & 0xFFFF,
|
||||
"value_hex": value_hex,
|
||||
"cmd0_frame": frame_hex(encode_host_frame(0x00, int(hint["selector"]), value)),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def _access_score(access: Mapping[str, Any], table_name: str) -> int:
|
||||
score = 1
|
||||
if access.get("access") == "read":
|
||||
score += 1
|
||||
if access.get("access") == "write":
|
||||
score += 1
|
||||
if "primary" in table_name or "current" in table_name:
|
||||
score += 1
|
||||
return score
|
||||
|
||||
|
||||
def _access_reason(access: Mapping[str, Any], table_name: str) -> str:
|
||||
function = access.get("function_label") or "<no function>"
|
||||
instruction = access.get("instruction") or ""
|
||||
return f"{table_name} {access.get('access')} in {function}: {instruction}"
|
||||
|
||||
|
||||
__all__ = [
|
||||
"analyze_ccu_seed_hints",
|
||||
"checksum",
|
||||
"encode_host_frame",
|
||||
"format_text_report",
|
||||
"frame_hex",
|
||||
"load_seed_hints_input",
|
||||
"main",
|
||||
"selector_bytes",
|
||||
"write_ccu_seed_hints",
|
||||
]
|
||||
730
h8536/eeprom_layout.py
Normal file
730
h8536/eeprom_layout.py
Normal file
@@ -0,0 +1,730 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
from collections import Counter
|
||||
from collections.abc import Iterable, Mapping
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
from .emulator.peripherals.x24164 import (
|
||||
X24164_FACTORY_DEFAULT_BASE,
|
||||
X24164_FACTORY_DEFAULT_BYTES,
|
||||
X24164_LOGICAL_PAGE_COUNT,
|
||||
X24164_LOGICAL_PAGE_SIZE,
|
||||
factory_default_words_from_rom,
|
||||
)
|
||||
from .formatting import h16, label_for
|
||||
from .rom import Rom
|
||||
|
||||
|
||||
JsonObject = dict[str, Any]
|
||||
|
||||
DEFAULT_INPUT = Path("build/rom_decompiled.json")
|
||||
DEFAULT_ROM = Path("ROM/M27C512@DIP28_1.BIN")
|
||||
|
||||
SHADOW_BASE = 0xF400
|
||||
SHADOW_SIZE = 0x0100
|
||||
SELECTOR_MAP_BASE = 0xC564
|
||||
SELECTOR_MAP_COUNT = 0x0200
|
||||
RECORD_RAM_BASE = 0xF7B0
|
||||
RECORD_BYTES = 8
|
||||
|
||||
STATE_BYTES: tuple[tuple[int, str], ...] = (
|
||||
(0xF402, "factory_signature_word"),
|
||||
(0xF404, "feature_or_option_flags_candidate"),
|
||||
(0xF730, "connect_display_state_candidate"),
|
||||
(0xF731, "session_latch_candidate"),
|
||||
(0xF732, "display_dispatch_selector_candidate"),
|
||||
(0xF76E, "eeprom_page_and_persist_flags"),
|
||||
(0xF790, "connection_latch_shadow_candidate"),
|
||||
(0xF791, "feature_flag_gate_candidate"),
|
||||
(0xFB03, "report_bridge_suppress_candidate"),
|
||||
)
|
||||
|
||||
|
||||
def load_eeprom_layout_input(path: Path) -> JsonObject:
|
||||
with path.open("r", encoding="utf-8") as handle:
|
||||
payload = json.load(handle)
|
||||
if not isinstance(payload, dict) or "instructions" not in payload:
|
||||
raise ValueError(f"{path} does not look like h8536_decompiler JSON output")
|
||||
return payload
|
||||
|
||||
|
||||
def analyze_eeprom_layout(payload: Mapping[str, Any], *, rom_path: Path | None = DEFAULT_ROM) -> JsonObject:
|
||||
rom = _load_rom(rom_path)
|
||||
factory_entries = _factory_entries(rom)
|
||||
selector_map = _selector_map_entries(rom, factory_entries)
|
||||
xrefs = _xref_summary(payload)
|
||||
|
||||
return {
|
||||
"kind": "eeprom_layout",
|
||||
"summary": {
|
||||
"confidence": "medium-high",
|
||||
"model": (
|
||||
"The ROM treats the traced P9 bus as two X24164-style EEPROM banks, "
|
||||
"mirrors a 0x100-byte factory/default block into F400-F4FF, and loads "
|
||||
"sixteen 8-byte persistent records into F7B0-F82F at boot."
|
||||
),
|
||||
"factory_default_count": len(factory_entries),
|
||||
"selector_persistence_mapping_count": len(selector_map),
|
||||
"persistent_record_count": X24164_LOGICAL_PAGE_COUNT,
|
||||
},
|
||||
"physical_model": _physical_model(),
|
||||
"boot_flow": _boot_flow(),
|
||||
"factory_defaults": {
|
||||
"rom_base": X24164_FACTORY_DEFAULT_BASE,
|
||||
"rom_base_hex": h16(X24164_FACTORY_DEFAULT_BASE),
|
||||
"byte_count": X24164_FACTORY_DEFAULT_BYTES,
|
||||
"shadow_base": SHADOW_BASE,
|
||||
"shadow_base_hex": h16(SHADOW_BASE),
|
||||
"shadow_range_hex": f"{h16(SHADOW_BASE)}-{h16(SHADOW_BASE + SHADOW_SIZE - 1)}",
|
||||
"entries": factory_entries,
|
||||
"notable_entries": _notable_factory_entries(factory_entries, xrefs, selector_map),
|
||||
},
|
||||
"persistent_records": _persistent_records(factory_entries),
|
||||
"serial_persistence_mapping": {
|
||||
"table_base": SELECTOR_MAP_BASE,
|
||||
"table_base_hex": h16(SELECTOR_MAP_BASE),
|
||||
"entry_count": SELECTOR_MAP_COUNT,
|
||||
"mapped_entry_count": len(selector_map),
|
||||
"entries": selector_map,
|
||||
"offset_histogram": _offset_histogram(selector_map),
|
||||
"high_byte_histogram": _high_byte_histogram(selector_map),
|
||||
"address_formula": (
|
||||
"command 4 persists to EEPROM address "
|
||||
"(((F76E & 0x0F) << 8) | (mapping_low_byte & 0xFE)) when F76E.7 is set"
|
||||
),
|
||||
},
|
||||
"xrefs": xrefs,
|
||||
"state_byte_hints": _state_byte_hints(factory_entries, xrefs),
|
||||
"bench_implications": [
|
||||
"A live EEPROM dump should first compare F400-F4FF shadow-equivalent offsets against the ROM factory table, especially F402/F404 and mapped offsets.",
|
||||
"Pages 0x0-0xF offset 0x00-0x07 are loaded into F7B0-F82F; page 0 carries the signature/options header and pages 1-F default to spaces, so non-space bytes there are high-value identity/config data.",
|
||||
"Serial command 0/4 can mirror values into F400 offsets selected by the ROM mapping table, but command 4 only persists when the continuation path reaches BD2B and F76E.7 is set.",
|
||||
"F76E is not just a page byte: bit7 gates EEPROM persistence, bit6 suppresses the 48FA dispatch path, and only its low nibble survives into the EEPROM page address.",
|
||||
"CONNECT OK is still likely gated by volatile table/session state as well as EEPROM-backed defaults; EEPROM differences may explain why bench and emulator diverge, but are unlikely to be the whole protocol by themselves.",
|
||||
],
|
||||
"caveats": [
|
||||
"The selector map proves where firmware mirrors/persists serial values, not what every field means to the panel UI.",
|
||||
"High bytes in the C564 selector map look structured, but the observed command-0/command-4 paths only use the low byte for F400/EEPROM offsets.",
|
||||
"Indexed F7B0-F82F record consumers can be missed by a static xref pass; dynamic emulator traces should be used once interesting record bytes are found.",
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
def format_text_report(analysis: Mapping[str, Any]) -> str:
|
||||
summary = analysis["summary"]
|
||||
lines = [
|
||||
"H8/536 EEPROM Layout Report",
|
||||
"",
|
||||
f"Summary: {summary['model']}",
|
||||
f"Confidence: {summary['confidence']}",
|
||||
"",
|
||||
"Physical / Logical Model:",
|
||||
]
|
||||
physical = analysis.get("physical_model", {})
|
||||
for row in physical.get("banks", []):
|
||||
lines.append(
|
||||
f"- {row['name']}: logical {row['logical_range_hex']} control {row['control_write_hex']}/{row['control_read_hex']}"
|
||||
)
|
||||
lines.append(f"- bus: {physical.get('bus', 'unknown')}")
|
||||
lines.append(f"- page model: {physical.get('page_model', 'unknown')}")
|
||||
|
||||
lines.extend(["", "Boot Flow:"])
|
||||
for step in analysis.get("boot_flow", []):
|
||||
lines.append(f"- {step['address_hex']} {step['name']}: {step['summary']}")
|
||||
|
||||
defaults = analysis.get("factory_defaults", {})
|
||||
lines.extend(["", "Factory Shadow Block:"])
|
||||
lines.append(
|
||||
f"- ROM {defaults.get('rom_base_hex')} length 0x{int(defaults.get('byte_count', 0)):02X} "
|
||||
f"mirrors to {defaults.get('shadow_range_hex')}"
|
||||
)
|
||||
for entry in defaults.get("notable_entries", [])[:32]:
|
||||
details = []
|
||||
if entry.get("ascii"):
|
||||
details.append(f"ascii={entry['ascii']!r}")
|
||||
if entry.get("mapped_selectors_hex"):
|
||||
details.append(f"selectors={', '.join(entry['mapped_selectors_hex'][:8])}")
|
||||
if entry.get("xref_count"):
|
||||
details.append(f"xrefs={entry['xref_count']}")
|
||||
suffix = f" ({'; '.join(details)})" if details else ""
|
||||
lines.append(
|
||||
f"- {entry['shadow_address_hex']} offset {entry['offset_hex']} "
|
||||
f"default {entry['factory_word_hex']}{suffix}"
|
||||
)
|
||||
|
||||
lines.extend(["", "Persistent 8-Byte Records:"])
|
||||
records = analysis.get("persistent_records", [])
|
||||
if records:
|
||||
first = records[0]
|
||||
last = records[-1]
|
||||
lines.append(
|
||||
f"- {len(records)} records: EEPROM {first['eeprom_range_hex']} .. {last['eeprom_range_hex']} "
|
||||
f"load into RAM {first['ram_range_hex']} .. {last['ram_range_hex']}"
|
||||
)
|
||||
for record in records[:16]:
|
||||
lines.append(
|
||||
f" - record {record['record_index_hex']}: EEPROM {record['eeprom_range_hex']} "
|
||||
f"-> RAM {record['ram_range_hex']} default {record['default_text']!r}"
|
||||
)
|
||||
|
||||
mapping = analysis.get("serial_persistence_mapping", {})
|
||||
lines.extend(["", "Serial Selector -> Shadow/EEPROM Mapping:"])
|
||||
lines.append(
|
||||
f"- table {mapping.get('table_base_hex')} has {mapping.get('mapped_entry_count', 0)} nonzero low-byte mappings"
|
||||
)
|
||||
lines.append(f"- formula: {mapping.get('address_formula')}")
|
||||
for entry in mapping.get("entries", [])[:80]:
|
||||
lines.append(
|
||||
f" - selector {entry['selector_hex']} map_word={entry['mapping_word_hex']} "
|
||||
f"-> {entry['shadow_address_hex']} page+{entry['eeprom_word_offset_hex']} "
|
||||
f"default={entry.get('factory_word_hex', 'unknown')}"
|
||||
)
|
||||
omitted = int(mapping.get("mapped_entry_count", 0)) - min(80, len(mapping.get("entries", [])))
|
||||
if omitted > 0:
|
||||
lines.append(f" - ... {omitted} more mapped selectors omitted")
|
||||
|
||||
lines.extend(["", "State Byte Hints:"])
|
||||
for hint in analysis.get("state_byte_hints", []):
|
||||
lines.append(
|
||||
f"- {hint['address_hex']} {hint['name']}: {hint['summary']} "
|
||||
f"(xrefs={hint['xref_count']})"
|
||||
)
|
||||
|
||||
lines.extend(["", "Bench Implications:"])
|
||||
for item in analysis.get("bench_implications", []):
|
||||
lines.append(f"- {item}")
|
||||
|
||||
lines.extend(["", "Caveats:"])
|
||||
for item in analysis.get("caveats", []):
|
||||
lines.append(f"- {item}")
|
||||
return "\n".join(lines).rstrip() + "\n"
|
||||
|
||||
|
||||
def write_eeprom_layout(
|
||||
input_path: Path,
|
||||
output_path: Path,
|
||||
*,
|
||||
rom_path: Path | None = DEFAULT_ROM,
|
||||
as_json: bool = False,
|
||||
) -> JsonObject:
|
||||
analysis = analyze_eeprom_layout(load_eeprom_layout_input(input_path), rom_path=rom_path)
|
||||
output_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
if as_json:
|
||||
output_path.write_text(json.dumps(analysis, indent=2, sort_keys=True) + "\n", encoding="utf-8")
|
||||
else:
|
||||
output_path.write_text(format_text_report(analysis), encoding="utf-8")
|
||||
return analysis
|
||||
|
||||
|
||||
def main(argv: list[str] | None = None, stdout: Any | None = None) -> int:
|
||||
parser = argparse.ArgumentParser(description="Mine ROM-backed X24164 EEPROM layout and persistence hints.")
|
||||
parser.add_argument("input", nargs="?", type=Path, default=DEFAULT_INPUT)
|
||||
parser.add_argument("--rom", type=Path, default=DEFAULT_ROM, help="ROM binary to mine")
|
||||
parser.add_argument("--json", action="store_true", help="emit structured JSON instead of readable text")
|
||||
parser.add_argument("--out", type=Path, default=None, help="write report to this path")
|
||||
args = parser.parse_args(argv)
|
||||
|
||||
stream = stdout
|
||||
if stream is None:
|
||||
import sys
|
||||
|
||||
stream = sys.stdout
|
||||
|
||||
rom_path = args.rom if args.rom and args.rom.exists() else None
|
||||
analysis = analyze_eeprom_layout(load_eeprom_layout_input(args.input), rom_path=rom_path)
|
||||
rendered = json.dumps(analysis, indent=2, sort_keys=True) + "\n" if args.json else format_text_report(analysis)
|
||||
if args.out:
|
||||
args.out.parent.mkdir(parents=True, exist_ok=True)
|
||||
args.out.write_text(rendered, encoding="utf-8")
|
||||
print(f"wrote {args.out}", file=stream)
|
||||
else:
|
||||
print(rendered, end="", file=stream)
|
||||
return 0
|
||||
|
||||
|
||||
def _load_rom(path: Path | None) -> Rom | None:
|
||||
if path is None or not path.exists():
|
||||
return None
|
||||
return Rom(path.read_bytes())
|
||||
|
||||
|
||||
def _physical_model() -> JsonObject:
|
||||
return {
|
||||
"bus": "P91/SCL and P97/SDA bit-banged two-wire bus through ROM routines C121/C08B/C0DB/C10C/C142",
|
||||
"page_size": X24164_LOGICAL_PAGE_SIZE,
|
||||
"page_count": X24164_LOGICAL_PAGE_COUNT,
|
||||
"page_model": "16 logical pages of 0x100 bytes; low 8 address bits are sent as the EEPROM word address",
|
||||
"banks": [
|
||||
{
|
||||
"name": "lower_x24164_candidate",
|
||||
"logical_range": [0x0000, 0x07FF],
|
||||
"logical_range_hex": "0x000-0x7FF",
|
||||
"control_write": 0xA0,
|
||||
"control_write_hex": "A0",
|
||||
"control_read": 0xA1,
|
||||
"control_read_hex": "A1",
|
||||
},
|
||||
{
|
||||
"name": "upper_x24164_candidate",
|
||||
"logical_range": [0x0800, 0x0FFF],
|
||||
"logical_range_hex": "0x800-0xFFF",
|
||||
"control_write": 0xE0,
|
||||
"control_write_hex": "E0",
|
||||
"control_read": 0xE1,
|
||||
"control_read_hex": "E1",
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
def _boot_flow() -> list[JsonObject]:
|
||||
return [
|
||||
{
|
||||
"address": 0x40BB,
|
||||
"address_hex": h16(0x40BB),
|
||||
"name": "eeprom_boot_gate",
|
||||
"summary": "initializes queue/table scratch, checks P7DR.7, then checks F402 == H'6B6F before trusting persisted state",
|
||||
},
|
||||
{
|
||||
"address": 0x4103,
|
||||
"address_hex": h16(0x4103),
|
||||
"name": "factory_default_fill",
|
||||
"summary": "copies ROM C964-CA63 into F400-F4FF and writes the same 0x100-byte defaults to each EEPROM page",
|
||||
},
|
||||
{
|
||||
"address": 0x4187,
|
||||
"address_hex": h16(0x4187),
|
||||
"name": "record_header_blank",
|
||||
"summary": "overwrites page offsets 0x00-0x07 on pages 0x1-0xF with four H'2020 words after factory replication; page 0 keeps the signature/options header",
|
||||
},
|
||||
{
|
||||
"address": 0x41D2,
|
||||
"address_hex": h16(0x41D2),
|
||||
"name": "persistent_record_load",
|
||||
"summary": "reads page offsets 0x00-0x07 from pages 0x0-0xF into F7B0-F82F; record 0 is a signature/options header, records 1-F are label/identity-like slots",
|
||||
},
|
||||
{
|
||||
"address": 0xBD2B,
|
||||
"address_hex": h16(0xBD2B),
|
||||
"name": "serial_persist_path",
|
||||
"summary": "command-4 continuation writes the live value into F400+map[selector] and persists it through BFE0 when F76E.7 is set",
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
def _factory_entries(rom: Rom | None) -> list[JsonObject]:
|
||||
if rom is None:
|
||||
return []
|
||||
entries = []
|
||||
for offset, word in factory_default_words_from_rom(rom.data):
|
||||
shadow_address = SHADOW_BASE + offset
|
||||
bytes_pair = [(word >> 8) & 0xFF, word & 0xFF]
|
||||
entries.append(
|
||||
{
|
||||
"offset": offset,
|
||||
"offset_hex": f"0x{offset:02X}",
|
||||
"rom_address": X24164_FACTORY_DEFAULT_BASE + offset,
|
||||
"rom_address_hex": h16(X24164_FACTORY_DEFAULT_BASE + offset),
|
||||
"shadow_address": shadow_address,
|
||||
"shadow_address_hex": h16(shadow_address),
|
||||
"factory_word": word,
|
||||
"factory_word_hex": f"0x{word:04X}",
|
||||
"bytes_hex": " ".join(f"{byte:02X}" for byte in bytes_pair),
|
||||
"ascii": _word_ascii(word),
|
||||
"default_eeprom_word_after_record_blank": 0x2020 if offset < RECORD_BYTES else word,
|
||||
"default_eeprom_word_after_record_blank_hex": "0x2020" if offset < RECORD_BYTES else f"0x{word:04X}",
|
||||
}
|
||||
)
|
||||
return entries
|
||||
|
||||
|
||||
def _selector_map_entries(rom: Rom | None, factory_entries: list[JsonObject]) -> list[JsonObject]:
|
||||
if rom is None:
|
||||
return []
|
||||
defaults_by_offset = {int(entry["offset"]): entry for entry in factory_entries}
|
||||
entries: list[JsonObject] = []
|
||||
for selector in range(SELECTOR_MAP_COUNT):
|
||||
address = SELECTOR_MAP_BASE + selector * 2
|
||||
if not rom.contains(address, 2):
|
||||
break
|
||||
word = rom.u16(address)
|
||||
low = word & 0xFF
|
||||
if low == 0:
|
||||
continue
|
||||
aligned = low & 0xFE
|
||||
shadow_address = SHADOW_BASE + low
|
||||
aligned_shadow_address = SHADOW_BASE + aligned
|
||||
default = defaults_by_offset.get(aligned)
|
||||
entry: JsonObject = {
|
||||
"selector": selector,
|
||||
"selector_hex": f"0x{selector:03X}",
|
||||
"entry_address": address,
|
||||
"entry_address_hex": h16(address),
|
||||
"mapping_word": word,
|
||||
"mapping_word_hex": f"0x{word:04X}",
|
||||
"mapping_high_byte": (word >> 8) & 0xFF,
|
||||
"mapping_high_byte_hex": f"0x{(word >> 8) & 0xFF:02X}",
|
||||
"mapping_low_byte": low,
|
||||
"mapping_low_byte_hex": f"0x{low:02X}",
|
||||
"shadow_offset": low,
|
||||
"shadow_offset_hex": f"0x{low:02X}",
|
||||
"shadow_address": shadow_address,
|
||||
"shadow_address_hex": h16(shadow_address),
|
||||
"aligned_shadow_address": aligned_shadow_address,
|
||||
"aligned_shadow_address_hex": h16(aligned_shadow_address),
|
||||
"eeprom_word_offset": aligned,
|
||||
"eeprom_word_offset_hex": f"0x{aligned:02X}",
|
||||
"command0_shadow_mirror": True,
|
||||
"command4_shadow_mirror": True,
|
||||
"command4_persist_when_f76e_bit7": True,
|
||||
}
|
||||
if default is not None:
|
||||
entry["factory_word"] = default["factory_word"]
|
||||
entry["factory_word_hex"] = default["factory_word_hex"]
|
||||
entry["factory_ascii"] = default["ascii"]
|
||||
entries.append(entry)
|
||||
return entries
|
||||
|
||||
|
||||
def _notable_factory_entries(
|
||||
factory_entries: list[JsonObject],
|
||||
xrefs: Mapping[str, Any],
|
||||
selector_map: list[JsonObject],
|
||||
) -> list[JsonObject]:
|
||||
direct_counts: Counter[int] = Counter()
|
||||
for item in xrefs.get("direct_xrefs", []):
|
||||
if not isinstance(item, Mapping) or not isinstance(item.get("address"), int):
|
||||
continue
|
||||
address = int(item["address"])
|
||||
if SHADOW_BASE <= address < SHADOW_BASE + SHADOW_SIZE:
|
||||
direct_counts[address & 0xFF] += 1
|
||||
|
||||
selectors_by_offset: dict[int, list[str]] = {}
|
||||
for entry in selector_map:
|
||||
selectors_by_offset.setdefault(int(entry["eeprom_word_offset"]), []).append(str(entry["selector_hex"]))
|
||||
|
||||
notable = []
|
||||
for entry in factory_entries:
|
||||
offset = int(entry["offset"])
|
||||
word = int(entry["factory_word"])
|
||||
mapped = selectors_by_offset.get(offset, [])
|
||||
xref_count = direct_counts.get(offset, 0)
|
||||
if word in (0x0000, 0xFFFF) and not mapped and not xref_count and offset not in (0x02, 0x04):
|
||||
continue
|
||||
item = dict(entry)
|
||||
item["xref_count"] = xref_count
|
||||
item["mapped_selectors_hex"] = mapped[:24]
|
||||
notable.append(item)
|
||||
return notable
|
||||
|
||||
|
||||
def _persistent_records(factory_entries: list[JsonObject]) -> list[JsonObject]:
|
||||
factory_by_offset = {int(entry["offset"]): int(entry["factory_word"]) for entry in factory_entries}
|
||||
records = []
|
||||
for index in range(X24164_LOGICAL_PAGE_COUNT):
|
||||
eeprom_base = index * X24164_LOGICAL_PAGE_SIZE
|
||||
ram_base = RECORD_RAM_BASE + index * RECORD_BYTES
|
||||
default_words = [
|
||||
factory_by_offset.get(offset, 0xFFFF) if index == 0 else 0x2020
|
||||
for offset in range(0, RECORD_BYTES, 2)
|
||||
]
|
||||
default_bytes = bytearray()
|
||||
for word in default_words:
|
||||
default_bytes.extend([(word >> 8) & 0xFF, word & 0xFF])
|
||||
records.append(
|
||||
{
|
||||
"record_index": index,
|
||||
"record_index_hex": f"0x{index:X}",
|
||||
"eeprom_base": eeprom_base,
|
||||
"eeprom_base_hex": f"0x{eeprom_base:03X}",
|
||||
"eeprom_range_hex": f"0x{eeprom_base:03X}-0x{eeprom_base + RECORD_BYTES - 1:03X}",
|
||||
"ram_base": ram_base,
|
||||
"ram_base_hex": h16(ram_base),
|
||||
"ram_range_hex": f"{h16(ram_base)}-{h16(ram_base + RECORD_BYTES - 1)}",
|
||||
"word_offsets": [0, 2, 4, 6],
|
||||
"default_words": default_words,
|
||||
"default_words_hex": [f"0x{word:04X}" for word in default_words],
|
||||
"default_bytes_hex": default_bytes.hex(" ").upper(),
|
||||
"default_text": _ascii(default_bytes),
|
||||
"role_candidate": "page-0 signature/options header" if index == 0 else "8-byte persistent label/identity slot",
|
||||
}
|
||||
)
|
||||
return records
|
||||
|
||||
|
||||
def _offset_histogram(entries: Iterable[Mapping[str, Any]]) -> list[JsonObject]:
|
||||
counts = Counter(int(entry["eeprom_word_offset"]) for entry in entries)
|
||||
return [
|
||||
{"offset": offset, "offset_hex": f"0x{offset:02X}", "selector_count": count}
|
||||
for offset, count in sorted(counts.items())
|
||||
]
|
||||
|
||||
|
||||
def _high_byte_histogram(entries: Iterable[Mapping[str, Any]]) -> list[JsonObject]:
|
||||
counts = Counter(int(entry["mapping_high_byte"]) for entry in entries)
|
||||
return [
|
||||
{"high_byte": value, "high_byte_hex": f"0x{value:02X}", "selector_count": count}
|
||||
for value, count in sorted(counts.items())
|
||||
]
|
||||
|
||||
|
||||
def _state_byte_hints(factory_entries: list[JsonObject], xrefs: Mapping[str, Any]) -> list[JsonObject]:
|
||||
defaults = {int(entry["shadow_address"]): entry for entry in factory_entries}
|
||||
refs_by_address: Counter[int] = Counter()
|
||||
examples_by_address: dict[int, list[JsonObject]] = {}
|
||||
for item in xrefs.get("direct_xrefs", []):
|
||||
if not isinstance(item, Mapping) or not isinstance(item.get("address"), int):
|
||||
continue
|
||||
address = int(item["address"])
|
||||
refs_by_address[address] += 1
|
||||
examples_by_address.setdefault(address, []).append(dict(item))
|
||||
|
||||
hints = []
|
||||
for address, name in STATE_BYTES:
|
||||
default = defaults.get(address)
|
||||
summary = _state_summary(address, default)
|
||||
hints.append(
|
||||
{
|
||||
"address": address,
|
||||
"address_hex": h16(address),
|
||||
"name": name,
|
||||
"summary": summary,
|
||||
"factory_word_hex": default.get("factory_word_hex") if default else None,
|
||||
"xref_count": refs_by_address.get(address, 0),
|
||||
"xrefs": examples_by_address.get(address, [])[:12],
|
||||
}
|
||||
)
|
||||
return hints
|
||||
|
||||
|
||||
def _state_summary(address: int, default: Mapping[str, Any] | None) -> str:
|
||||
factory = f"factory {default['factory_word_hex']}" if default else "volatile/no factory word"
|
||||
if address == 0xF402:
|
||||
return f"{factory}; boot accepts persisted state only when this word is H'6B6F"
|
||||
if address == 0xF404:
|
||||
return f"{factory}; bits 1-4 are tested with F791 gates in display/status routines"
|
||||
if address == 0xF76E:
|
||||
return "bit7 enables command-4 EEPROM persistence, bit6 suppresses 48FA dispatch, low nibble selects EEPROM page"
|
||||
if address == 0xF791:
|
||||
return "volatile gate tested alongside F404 option bits before setting report/display flags"
|
||||
if address == 0xF732:
|
||||
return "volatile display dispatch selector feeding the 493E pointer table and 48FA report bridge"
|
||||
return factory
|
||||
|
||||
|
||||
def _xref_summary(payload: Mapping[str, Any]) -> JsonObject:
|
||||
instructions = _instruction_sequence(payload.get("instructions"))
|
||||
functions = _function_ranges(payload)
|
||||
direct = []
|
||||
dynamic = []
|
||||
for ins in instructions:
|
||||
for ref in _references(ins):
|
||||
if _is_interesting_address(ref):
|
||||
direct.append(_xref_item(ins, ref, functions))
|
||||
operands = str(ins.get("operands", ""))
|
||||
dynamic_kind = _dynamic_kind(operands)
|
||||
if dynamic_kind:
|
||||
item = _base_instruction_item(ins, functions)
|
||||
item["kind"] = dynamic_kind
|
||||
item["operand"] = operands
|
||||
dynamic.append(item)
|
||||
return {
|
||||
"direct_xref_count": len(direct),
|
||||
"dynamic_xref_count": len(dynamic),
|
||||
"direct_xrefs": direct,
|
||||
"dynamic_xrefs": dynamic,
|
||||
}
|
||||
|
||||
|
||||
def _xref_item(ins: Mapping[str, Any], address: int, functions: list[JsonObject]) -> JsonObject:
|
||||
item = _base_instruction_item(ins, functions)
|
||||
item.update(
|
||||
{
|
||||
"address": address,
|
||||
"address_hex": h16(address),
|
||||
"region": _region_for_address(address),
|
||||
"access": _access_direction(ins, address) or "read_write_candidate",
|
||||
}
|
||||
)
|
||||
return item
|
||||
|
||||
|
||||
def _base_instruction_item(ins: Mapping[str, Any], functions: list[JsonObject]) -> JsonObject:
|
||||
address = int(ins["address"])
|
||||
function = _function_for_address(functions, address)
|
||||
item: JsonObject = {
|
||||
"instruction_address": address,
|
||||
"instruction_address_hex": h16(address),
|
||||
"mnemonic": str(ins.get("mnemonic", "")),
|
||||
"operands": str(ins.get("operands", "")),
|
||||
"instruction": str(ins.get("text") or _instruction_text(ins)),
|
||||
}
|
||||
if function:
|
||||
item["function_start"] = function["start"]
|
||||
item["function_start_hex"] = h16(int(function["start"]))
|
||||
item["function_label"] = function["label"]
|
||||
return item
|
||||
|
||||
|
||||
def _is_interesting_address(address: int) -> bool:
|
||||
return (
|
||||
SHADOW_BASE <= address < SHADOW_BASE + SHADOW_SIZE
|
||||
or RECORD_RAM_BASE <= address < RECORD_RAM_BASE + X24164_LOGICAL_PAGE_COUNT * RECORD_BYTES
|
||||
or any(address == item[0] for item in STATE_BYTES)
|
||||
)
|
||||
|
||||
|
||||
def _region_for_address(address: int) -> str:
|
||||
if SHADOW_BASE <= address < SHADOW_BASE + SHADOW_SIZE:
|
||||
return "f400_shadow_defaults"
|
||||
if RECORD_RAM_BASE <= address < RECORD_RAM_BASE + X24164_LOGICAL_PAGE_COUNT * RECORD_BYTES:
|
||||
return "persistent_record_ram"
|
||||
return "state_or_gate_ram"
|
||||
|
||||
|
||||
def _dynamic_kind(operands: str) -> str | None:
|
||||
patterns = (
|
||||
("@(-H'0C00", "indexed_f400_shadow_access"),
|
||||
("@(-H'0850", "indexed_persistent_record_store"),
|
||||
("@(-H'084E", "indexed_persistent_record_store"),
|
||||
("@(-H'084C", "indexed_persistent_record_store"),
|
||||
("@(-H'084A", "indexed_persistent_record_store"),
|
||||
("@(-H'3A9C", "selector_to_shadow_mapping_word"),
|
||||
("@(-H'3A9B", "selector_to_shadow_mapping_low_byte"),
|
||||
)
|
||||
upper = operands.upper()
|
||||
for pattern, kind in patterns:
|
||||
if pattern in upper:
|
||||
return kind
|
||||
return None
|
||||
|
||||
|
||||
def _function_ranges(payload: Mapping[str, Any]) -> list[JsonObject]:
|
||||
call_graph = payload.get("call_graph")
|
||||
if not isinstance(call_graph, Mapping):
|
||||
return []
|
||||
nodes = call_graph.get("nodes")
|
||||
if not isinstance(nodes, list):
|
||||
return []
|
||||
ranges: list[JsonObject] = []
|
||||
for node in nodes:
|
||||
if not isinstance(node, Mapping):
|
||||
continue
|
||||
start = node.get("start")
|
||||
end = node.get("end")
|
||||
if isinstance(start, int) and isinstance(end, int):
|
||||
ranges.append({"start": start, "end": end, "label": str(node.get("label") or label_for(start))})
|
||||
return sorted(ranges, key=lambda item: int(item["start"]))
|
||||
|
||||
|
||||
def _function_for_address(functions: list[JsonObject], address: int) -> JsonObject | None:
|
||||
for function in functions:
|
||||
if int(function["start"]) <= address <= int(function["end"]):
|
||||
return function
|
||||
return None
|
||||
|
||||
|
||||
def _instruction_sequence(value: object) -> list[JsonObject]:
|
||||
if isinstance(value, Mapping):
|
||||
values: Iterable[Any] = value.values()
|
||||
elif isinstance(value, list):
|
||||
values = value
|
||||
else:
|
||||
values = []
|
||||
return sorted(
|
||||
[item for item in values if isinstance(item, dict) and isinstance(item.get("address"), int)],
|
||||
key=lambda item: int(item["address"]),
|
||||
)
|
||||
|
||||
|
||||
def _references(ins: Mapping[str, Any]) -> list[int]:
|
||||
refs = ins.get("references", [])
|
||||
if not isinstance(refs, list):
|
||||
return []
|
||||
output: list[int] = []
|
||||
for ref in refs:
|
||||
if isinstance(ref, Mapping) and isinstance(ref.get("address"), int):
|
||||
output.append(int(ref["address"]))
|
||||
elif isinstance(ref, int):
|
||||
output.append(ref)
|
||||
return output
|
||||
|
||||
|
||||
def _access_direction(ins: Mapping[str, Any], address: int) -> str | None:
|
||||
root = _mnemonic_root(str(ins.get("mnemonic", "")))
|
||||
if root in {"BTST", "CMP", "CMP:E", "CMP:G", "CMP:I", "MOVFPE", "TST"}:
|
||||
return "read"
|
||||
if root in {"BCLR", "BNOT", "BSET", "CLR", "INC", "INC:G", "NEG", "NOT"}:
|
||||
return "write"
|
||||
if root in {"ADD:Q", "ADD:G", "ADDS", "ADDX", "AND", "OR", "SUB", "SUBS", "SUBX", "XOR"}:
|
||||
return "write"
|
||||
if root in {"MOV:G", "MOV:S", "MOVTPE"}:
|
||||
source, destination = _source_destination_operands(str(ins.get("operands", "")))
|
||||
if _operand_mentions_address(destination, address):
|
||||
return "write"
|
||||
if _operand_mentions_address(source, address):
|
||||
return "read"
|
||||
return None
|
||||
|
||||
|
||||
def _source_destination_operands(operands: str) -> tuple[str, str]:
|
||||
depth = 0
|
||||
split_at: int | None = None
|
||||
for index, char in enumerate(operands):
|
||||
if char in "({":
|
||||
depth += 1
|
||||
elif char in ")}" and depth:
|
||||
depth -= 1
|
||||
elif char == "," and depth == 0:
|
||||
split_at = index
|
||||
if split_at is None:
|
||||
operand = operands.strip()
|
||||
return "", operand
|
||||
return operands[:split_at].strip(), operands[split_at + 1 :].strip()
|
||||
|
||||
|
||||
def _operand_mentions_address(operand: str, address: int) -> bool:
|
||||
operand_upper = operand.upper().replace(" ", "")
|
||||
negative = (0x10000 - address) & 0xFFFF
|
||||
return (
|
||||
f"H'{address:04X}" in operand_upper
|
||||
or f"0X{address:04X}" in operand_upper
|
||||
or f"${address:04X}" in operand_upper
|
||||
or f"-H'{negative:04X}" in operand_upper
|
||||
or f"-0X{negative:04X}" in operand_upper
|
||||
or f"-${negative:04X}" in operand_upper
|
||||
)
|
||||
|
||||
|
||||
def _instruction_text(ins: Mapping[str, Any]) -> str:
|
||||
operands = str(ins.get("operands", ""))
|
||||
return f"{ins.get('mnemonic', '')} {operands}".strip()
|
||||
|
||||
|
||||
def _mnemonic_root(mnemonic: str) -> str:
|
||||
return mnemonic.rsplit(".", 1)[0].upper()
|
||||
|
||||
|
||||
def _word_ascii(word: int) -> str:
|
||||
chars = [(word >> 8) & 0xFF, word & 0xFF]
|
||||
if all(0x20 <= byte <= 0x7E for byte in chars):
|
||||
return "".join(chr(byte) for byte in chars)
|
||||
if any(0x20 <= byte <= 0x7E for byte in chars):
|
||||
return "".join(chr(byte) if 0x20 <= byte <= 0x7E else "." for byte in chars)
|
||||
return ""
|
||||
|
||||
|
||||
def _ascii(data: bytes | bytearray) -> str:
|
||||
return "".join(chr(value) if 0x20 <= value <= 0x7E else "." for value in data)
|
||||
|
||||
|
||||
__all__ = [
|
||||
"analyze_eeprom_layout",
|
||||
"format_text_report",
|
||||
"load_eeprom_layout_input",
|
||||
"main",
|
||||
"write_eeprom_layout",
|
||||
]
|
||||
@@ -4,6 +4,7 @@ import argparse
|
||||
from pathlib import Path
|
||||
|
||||
from ..formatting import h16, parse_int
|
||||
from .eeprom_image import write_eeprom_snapshot
|
||||
from .memory import describe_regions
|
||||
from .runner import H8536Emulator
|
||||
|
||||
@@ -47,6 +48,11 @@ def build_arg_parser() -> argparse.ArgumentParser:
|
||||
parser.add_argument("--p9-fast-optimistic-wrapper", action="store_true", help="legacy fallback for older wrapper experiments; known BFE0/BFFE wrappers use the X24164 model")
|
||||
parser.add_argument("--p7-input", type=parse_int, default=0xFF, help="external P7 pin state for input bits; DIP-off board default is 0xFF")
|
||||
parser.add_argument("--eeprom-seed", choices=("blank", "factory"), default="blank", help="initial X24164/shadow state before reset")
|
||||
parser.add_argument("--eeprom-load", type=Path, help="load a 0x1000-byte logical EEPROM image before running")
|
||||
parser.add_argument("--eeprom-save", type=Path, help="save the final 0x1000-byte logical EEPROM image after running")
|
||||
parser.add_argument("--eeprom-report", type=Path, help="write a readable EEPROM snapshot report after running")
|
||||
parser.add_argument("--eeprom-report-json", type=Path, help="write a structured EEPROM snapshot report after running")
|
||||
parser.add_argument("--eeprom-report-include-image", action="store_true", help="include the full EEPROM image as hex in JSON reports")
|
||||
return parser
|
||||
|
||||
|
||||
@@ -70,6 +76,9 @@ def main(argv: list[str] | None = None) -> int:
|
||||
p7_input=args.p7_input,
|
||||
eeprom_seed=args.eeprom_seed,
|
||||
)
|
||||
if args.eeprom_load:
|
||||
emulator.memory.load_eeprom_image(args.eeprom_load.read_bytes())
|
||||
print(f"eeprom_loaded={args.eeprom_load}")
|
||||
print(f"rom={rom_path}")
|
||||
print(f"reset_vector={h16(emulator.reset_vector())}")
|
||||
if args.memory_map:
|
||||
@@ -82,4 +91,20 @@ def main(argv: list[str] | None = None) -> int:
|
||||
print(line)
|
||||
if not report.heartbeat_seen:
|
||||
print("heartbeat_status=not reached; no heartbeat is reported unless bytes are emitted via SCI1_TDR")
|
||||
if args.eeprom_save:
|
||||
args.eeprom_save.parent.mkdir(parents=True, exist_ok=True)
|
||||
args.eeprom_save.write_bytes(emulator.memory.dump_eeprom_image())
|
||||
print(f"eeprom_saved={args.eeprom_save}")
|
||||
if args.eeprom_report:
|
||||
write_eeprom_snapshot(emulator.memory, args.eeprom_report, rom_bytes=rom_bytes)
|
||||
print(f"eeprom_report={args.eeprom_report}")
|
||||
if args.eeprom_report_json:
|
||||
write_eeprom_snapshot(
|
||||
emulator.memory,
|
||||
args.eeprom_report_json,
|
||||
rom_bytes=rom_bytes,
|
||||
as_json=True,
|
||||
include_image_hex=args.eeprom_report_include_image,
|
||||
)
|
||||
print(f"eeprom_report_json={args.eeprom_report_json}")
|
||||
return 0
|
||||
|
||||
351
h8536/emulator/eeprom_image.py
Normal file
351
h8536/emulator/eeprom_image.py
Normal file
@@ -0,0 +1,351 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import json
|
||||
from collections import defaultdict
|
||||
from collections.abc import Mapping
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
from ..formatting import h16
|
||||
from .memory import MemoryMap
|
||||
from .peripherals.x24164 import (
|
||||
X24164_FACTORY_DEFAULT_BYTES,
|
||||
X24164_LOGICAL_PAGE_COUNT,
|
||||
X24164_LOGICAL_PAGE_SIZE,
|
||||
X24164_LOGICAL_SIZE,
|
||||
factory_default_words_from_rom,
|
||||
)
|
||||
|
||||
|
||||
JsonObject = dict[str, Any]
|
||||
|
||||
SELECTOR_MAP_BASE = 0xC564
|
||||
SELECTOR_MAP_COUNT = 0x0200
|
||||
RECORD_BYTES = 8
|
||||
SHADOW_BASE = 0xF400
|
||||
|
||||
|
||||
def build_eeprom_snapshot(
|
||||
memory: MemoryMap,
|
||||
*,
|
||||
rom_bytes: bytes | None = None,
|
||||
include_image_hex: bool = False,
|
||||
) -> JsonObject:
|
||||
image = memory.dump_eeprom_image()
|
||||
selectors_by_offset = _selectors_by_offset(rom_bytes)
|
||||
factory_image = _factory_image(rom_bytes)
|
||||
write_events = _write_events(memory, selectors_by_offset)
|
||||
write_words = _coalesced_write_words(memory, selectors_by_offset)
|
||||
factory_diffs = _factory_diffs(image, factory_image, selectors_by_offset)
|
||||
|
||||
report: JsonObject = {
|
||||
"kind": "emulator_eeprom_snapshot",
|
||||
"summary": {
|
||||
"logical_size": len(image),
|
||||
"logical_size_hex": f"0x{len(image):04X}",
|
||||
"sha256": hashlib.sha256(image).hexdigest(),
|
||||
"write_byte_count": len(write_events),
|
||||
"write_word_count": len(write_words),
|
||||
"factory_diff_word_count": len(factory_diffs),
|
||||
"record_count": X24164_LOGICAL_PAGE_COUNT,
|
||||
},
|
||||
"records": _records(image),
|
||||
"write_events": write_events,
|
||||
"write_word_events": write_words,
|
||||
"factory_diffs": factory_diffs,
|
||||
"shadow_f400": _shadow_summary(memory, rom_bytes, selectors_by_offset),
|
||||
}
|
||||
if include_image_hex:
|
||||
report["image_hex"] = image.hex()
|
||||
return report
|
||||
|
||||
|
||||
def format_eeprom_snapshot(report: Mapping[str, Any], *, limit: int = 80) -> str:
|
||||
summary = report["summary"]
|
||||
lines = [
|
||||
"Emulator EEPROM Snapshot",
|
||||
"",
|
||||
f"size={summary['logical_size_hex']} sha256={summary['sha256']}",
|
||||
(
|
||||
f"writes: bytes={summary['write_byte_count']} words={summary['write_word_count']} "
|
||||
f"factory_diff_words={summary['factory_diff_word_count']}"
|
||||
),
|
||||
"",
|
||||
"Persistent Records:",
|
||||
]
|
||||
for record in report.get("records", []):
|
||||
lines.append(
|
||||
f"- page {record['page_hex']} EEPROM {record['range_hex']} "
|
||||
f"bytes={record['bytes_hex']} text={record['ascii']!r}"
|
||||
)
|
||||
|
||||
lines.extend(["", "EEPROM Word Writes:"])
|
||||
word_events = list(report.get("write_word_events", []))
|
||||
if not word_events:
|
||||
lines.append("- none since EEPROM setup/load")
|
||||
for event in word_events[:limit]:
|
||||
suffix = _event_suffix(event)
|
||||
lines.append(
|
||||
f"- {event['address_hex']} page={event['page_hex']} offset={event['offset_hex']} "
|
||||
f"{event['old_word_hex']}->{event['new_word_hex']} source={event['source']}{suffix}"
|
||||
)
|
||||
if len(word_events) > limit:
|
||||
lines.append(f"- ... {len(word_events) - limit} more word writes omitted")
|
||||
|
||||
lines.extend(["", "Factory Diffs:"])
|
||||
diffs = list(report.get("factory_diffs", []))
|
||||
if not diffs:
|
||||
lines.append("- current EEPROM image matches ROM factory/default image")
|
||||
for diff in diffs[:limit]:
|
||||
suffix = _event_suffix(diff)
|
||||
lines.append(
|
||||
f"- {diff['address_hex']} page={diff['page_hex']} offset={diff['offset_hex']} "
|
||||
f"expected={diff['expected_word_hex']} actual={diff['actual_word_hex']}{suffix}"
|
||||
)
|
||||
if len(diffs) > limit:
|
||||
lines.append(f"- ... {len(diffs) - limit} more factory diffs omitted")
|
||||
|
||||
shadow = report.get("shadow_f400", {})
|
||||
lines.extend(["", "F400 Shadow Diffs:"])
|
||||
shadow_diffs = list(shadow.get("diffs", [])) if isinstance(shadow, Mapping) else []
|
||||
if not shadow_diffs:
|
||||
lines.append("- F400-F4FF shadow matches ROM factory words or no ROM factory baseline was supplied")
|
||||
for diff in shadow_diffs[:limit]:
|
||||
suffix = _event_suffix(diff)
|
||||
lines.append(
|
||||
f"- {diff['address_hex']} offset={diff['offset_hex']} "
|
||||
f"expected={diff['expected_word_hex']} actual={diff['actual_word_hex']}{suffix}"
|
||||
)
|
||||
if len(shadow_diffs) > limit:
|
||||
lines.append(f"- ... {len(shadow_diffs) - limit} more shadow diffs omitted")
|
||||
return "\n".join(lines).rstrip() + "\n"
|
||||
|
||||
|
||||
def write_eeprom_snapshot(
|
||||
memory: MemoryMap,
|
||||
output_path: Path,
|
||||
*,
|
||||
rom_bytes: bytes | None = None,
|
||||
as_json: bool = False,
|
||||
include_image_hex: bool = False,
|
||||
) -> JsonObject:
|
||||
report = build_eeprom_snapshot(memory, rom_bytes=rom_bytes, include_image_hex=include_image_hex)
|
||||
output_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
if as_json:
|
||||
output_path.write_text(json.dumps(report, indent=2, sort_keys=True) + "\n", encoding="utf-8")
|
||||
else:
|
||||
output_path.write_text(format_eeprom_snapshot(report), encoding="utf-8")
|
||||
return report
|
||||
|
||||
|
||||
def _write_events(memory: MemoryMap, selectors_by_offset: Mapping[int, list[int]]) -> list[JsonObject]:
|
||||
events = []
|
||||
for index, event in enumerate(memory.p9_bus.x24164_bus.write_events):
|
||||
item = _address_info(event.logical_address, selectors_by_offset)
|
||||
item.update(
|
||||
{
|
||||
"index": index,
|
||||
"device": event.device,
|
||||
"device_offset": event.device_offset,
|
||||
"device_offset_hex": f"0x{event.device_offset:03X}",
|
||||
"old_value": event.old_value,
|
||||
"old_value_hex": f"0x{event.old_value & 0xFF:02X}",
|
||||
"new_value": event.new_value,
|
||||
"new_value_hex": f"0x{event.new_value & 0xFF:02X}",
|
||||
"source": event.source,
|
||||
}
|
||||
)
|
||||
events.append(item)
|
||||
return events
|
||||
|
||||
|
||||
def _coalesced_write_words(memory: MemoryMap, selectors_by_offset: Mapping[int, list[int]]) -> list[JsonObject]:
|
||||
events = memory.p9_bus.x24164_bus.write_events
|
||||
words: list[JsonObject] = []
|
||||
index = 0
|
||||
while index < len(events):
|
||||
event = events[index]
|
||||
next_event = events[index + 1] if index + 1 < len(events) else None
|
||||
if (
|
||||
next_event is not None
|
||||
and (event.logical_address & 1) == 0
|
||||
and next_event.logical_address == ((event.logical_address + 1) & 0x0FFF)
|
||||
and next_event.device == event.device
|
||||
and next_event.source == event.source
|
||||
):
|
||||
old_word = ((event.old_value & 0xFF) << 8) | (next_event.old_value & 0xFF)
|
||||
new_word = ((event.new_value & 0xFF) << 8) | (next_event.new_value & 0xFF)
|
||||
item = _address_info(event.logical_address, selectors_by_offset)
|
||||
item.update(
|
||||
{
|
||||
"index": index,
|
||||
"device": event.device,
|
||||
"old_word": old_word,
|
||||
"old_word_hex": f"0x{old_word:04X}",
|
||||
"new_word": new_word,
|
||||
"new_word_hex": f"0x{new_word:04X}",
|
||||
"source": event.source,
|
||||
}
|
||||
)
|
||||
words.append(item)
|
||||
index += 2
|
||||
else:
|
||||
index += 1
|
||||
return words
|
||||
|
||||
|
||||
def _factory_diffs(
|
||||
image: bytes,
|
||||
factory_image: bytes | None,
|
||||
selectors_by_offset: Mapping[int, list[int]],
|
||||
) -> list[JsonObject]:
|
||||
if factory_image is None:
|
||||
return []
|
||||
diffs = []
|
||||
for address in range(0, min(len(image), len(factory_image)), 2):
|
||||
expected = (factory_image[address] << 8) | factory_image[address + 1]
|
||||
actual = (image[address] << 8) | image[address + 1]
|
||||
if expected == actual:
|
||||
continue
|
||||
item = _address_info(address, selectors_by_offset)
|
||||
item.update(
|
||||
{
|
||||
"expected_word": expected,
|
||||
"expected_word_hex": f"0x{expected:04X}",
|
||||
"actual_word": actual,
|
||||
"actual_word_hex": f"0x{actual:04X}",
|
||||
}
|
||||
)
|
||||
diffs.append(item)
|
||||
return diffs
|
||||
|
||||
|
||||
def _shadow_summary(
|
||||
memory: MemoryMap,
|
||||
rom_bytes: bytes | None,
|
||||
selectors_by_offset: Mapping[int, list[int]],
|
||||
) -> JsonObject:
|
||||
if rom_bytes is None:
|
||||
return {"diffs": [], "note": "no ROM factory baseline supplied"}
|
||||
factory_words = dict(factory_default_words_from_rom(rom_bytes))
|
||||
diffs = []
|
||||
for offset in range(0, X24164_FACTORY_DEFAULT_BYTES, 2):
|
||||
expected = factory_words[offset]
|
||||
address = SHADOW_BASE + offset
|
||||
high = memory.external.get(address & 0xFFFF, 0xFF)
|
||||
low = memory.external.get((address + 1) & 0xFFFF, 0xFF)
|
||||
actual = ((high & 0xFF) << 8) | (low & 0xFF)
|
||||
if expected == actual:
|
||||
continue
|
||||
item = _address_info(offset, selectors_by_offset)
|
||||
item.update(
|
||||
{
|
||||
"address": address,
|
||||
"address_hex": h16(address),
|
||||
"expected_word": expected,
|
||||
"expected_word_hex": f"0x{expected:04X}",
|
||||
"actual_word": actual,
|
||||
"actual_word_hex": f"0x{actual:04X}",
|
||||
}
|
||||
)
|
||||
diffs.append(item)
|
||||
return {"diffs": diffs, "diff_count": len(diffs)}
|
||||
|
||||
|
||||
def _records(image: bytes) -> list[JsonObject]:
|
||||
records = []
|
||||
for page in range(X24164_LOGICAL_PAGE_COUNT):
|
||||
base = page * X24164_LOGICAL_PAGE_SIZE
|
||||
data = image[base : base + RECORD_BYTES]
|
||||
records.append(
|
||||
{
|
||||
"page": page,
|
||||
"page_hex": f"0x{page:X}",
|
||||
"address": base,
|
||||
"address_hex": f"0x{base:03X}",
|
||||
"range_hex": f"0x{base:03X}-0x{base + RECORD_BYTES - 1:03X}",
|
||||
"bytes_hex": data.hex(" ").upper(),
|
||||
"words_hex": [
|
||||
f"0x{((data[index] << 8) | data[index + 1]):04X}"
|
||||
for index in range(0, len(data), 2)
|
||||
],
|
||||
"ascii": _ascii(data),
|
||||
"is_blank_spaces": data == (b" " * RECORD_BYTES),
|
||||
}
|
||||
)
|
||||
return records
|
||||
|
||||
|
||||
def _factory_image(rom_bytes: bytes | None) -> bytes | None:
|
||||
if rom_bytes is None:
|
||||
return None
|
||||
image = bytearray([0xFF] * X24164_LOGICAL_SIZE)
|
||||
for offset, word in factory_default_words_from_rom(rom_bytes):
|
||||
for page in range(X24164_LOGICAL_PAGE_COUNT):
|
||||
address = (page * X24164_LOGICAL_PAGE_SIZE) + offset
|
||||
image[address] = (word >> 8) & 0xFF
|
||||
image[address + 1] = word & 0xFF
|
||||
for page in range(1, X24164_LOGICAL_PAGE_COUNT):
|
||||
base = page * X24164_LOGICAL_PAGE_SIZE
|
||||
for offset in range(0, RECORD_BYTES, 2):
|
||||
image[base + offset] = 0x20
|
||||
image[base + offset + 1] = 0x20
|
||||
return bytes(image)
|
||||
|
||||
|
||||
def _selectors_by_offset(rom_bytes: bytes | None) -> dict[int, list[int]]:
|
||||
if rom_bytes is None:
|
||||
return {}
|
||||
result: dict[int, list[int]] = defaultdict(list)
|
||||
for selector in range(SELECTOR_MAP_COUNT):
|
||||
address = SELECTOR_MAP_BASE + selector * 2
|
||||
if address + 1 >= len(rom_bytes):
|
||||
break
|
||||
low = rom_bytes[address + 1]
|
||||
if low:
|
||||
result[low & 0xFE].append(selector)
|
||||
return dict(result)
|
||||
|
||||
|
||||
def _address_info(address: int, selectors_by_offset: Mapping[int, list[int]]) -> JsonObject:
|
||||
address &= 0x0FFF
|
||||
page = (address // X24164_LOGICAL_PAGE_SIZE) & 0x0F
|
||||
offset = address & 0xFF
|
||||
aligned_offset = offset & 0xFE
|
||||
selectors = selectors_by_offset.get(aligned_offset, [])
|
||||
return {
|
||||
"address": address,
|
||||
"address_hex": f"0x{address:03X}",
|
||||
"page": page,
|
||||
"page_hex": f"0x{page:X}",
|
||||
"offset": offset,
|
||||
"offset_hex": f"0x{offset:02X}",
|
||||
"aligned_offset": aligned_offset,
|
||||
"aligned_offset_hex": f"0x{aligned_offset:02X}",
|
||||
"record_byte": offset if offset < RECORD_BYTES else None,
|
||||
"role": "record_header_or_label" if offset < RECORD_BYTES else "factory_shadow_offset",
|
||||
"mapped_selectors": selectors[:24],
|
||||
"mapped_selectors_hex": [f"0x{selector:03X}" for selector in selectors[:24]],
|
||||
}
|
||||
|
||||
|
||||
def _event_suffix(event: Mapping[str, Any]) -> str:
|
||||
parts = []
|
||||
if event.get("role"):
|
||||
parts.append(str(event["role"]))
|
||||
selectors = event.get("mapped_selectors_hex")
|
||||
if isinstance(selectors, list) and selectors:
|
||||
parts.append("selectors=" + ",".join(str(item) for item in selectors[:6]))
|
||||
return f" ({'; '.join(parts)})" if parts else ""
|
||||
|
||||
|
||||
def _ascii(data: bytes) -> str:
|
||||
return "".join(chr(value) if 0x20 <= value <= 0x7E else "." for value in data)
|
||||
|
||||
|
||||
__all__ = [
|
||||
"build_eeprom_snapshot",
|
||||
"format_eeprom_snapshot",
|
||||
"write_eeprom_snapshot",
|
||||
]
|
||||
@@ -161,6 +161,20 @@ class MemoryMap:
|
||||
self.p9_bus.x24164_bus.seed_factory_defaults_from_rom(self.rom.data)
|
||||
self.p9_bus.clear_x24164_trace()
|
||||
|
||||
def load_eeprom_image(self, data: bytes | bytearray, *, mirror_shadow: bool = True) -> None:
|
||||
self.p9_bus.x24164_bus.load_linear(data)
|
||||
if mirror_shadow:
|
||||
image = self.p9_bus.x24164_bus.dump_linear()
|
||||
for offset in range(min(0x0100, len(image))):
|
||||
self.external[(0xF400 + offset) & 0xFFFF] = image[offset]
|
||||
self.p9_bus.clear_x24164_trace()
|
||||
|
||||
def dump_eeprom_image(self) -> bytes:
|
||||
return self.p9_bus.x24164_bus.dump_linear()
|
||||
|
||||
def clear_eeprom_write_log(self) -> None:
|
||||
self.p9_bus.x24164_bus.clear_write_log()
|
||||
|
||||
def _set_register(self, address: int, value: int) -> None:
|
||||
self.registers[address - REGISTER_FIELD_START] = value & 0xFF
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ from __future__ import annotations
|
||||
|
||||
from .lcd import LCD, LCD_E_CLOCK_DATA, LCD_E_CLOCK_STATUS, LCD_LINE_WIDTH
|
||||
from .p9_bus import P9_ACK_BIT, P9_STROBE_BIT, P9Bus, P9StrobeEvent, P9TraceEvent
|
||||
from .x24164 import X24164Bus, X24164Device, X24164TraceEvent, factory_default_words_from_rom
|
||||
from .x24164 import X24164Bus, X24164Device, X24164TraceEvent, X24164WriteEvent, factory_default_words_from_rom
|
||||
|
||||
__all__ = [
|
||||
"LCD_E_CLOCK_DATA",
|
||||
@@ -17,5 +17,6 @@ __all__ = [
|
||||
"X24164Bus",
|
||||
"X24164Device",
|
||||
"X24164TraceEvent",
|
||||
"X24164WriteEvent",
|
||||
"factory_default_words_from_rom",
|
||||
]
|
||||
|
||||
@@ -4,6 +4,7 @@ from dataclasses import dataclass, field
|
||||
|
||||
|
||||
X24164_SIZE = 2048
|
||||
X24164_LOGICAL_SIZE = 4096
|
||||
X24164_FACTORY_DEFAULT_BASE = 0xC964
|
||||
X24164_FACTORY_DEFAULT_BYTES = 0x0100
|
||||
X24164_LOGICAL_PAGE_SIZE = 0x0100
|
||||
@@ -72,12 +73,30 @@ class X24164TraceEvent:
|
||||
return " ".join(parts)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class X24164WriteEvent:
|
||||
logical_address: int
|
||||
device: str
|
||||
device_offset: int
|
||||
old_value: int
|
||||
new_value: int
|
||||
source: str
|
||||
|
||||
def line(self) -> str:
|
||||
return (
|
||||
f"addr={self.logical_address & 0x0FFF:03X} device={self.device} "
|
||||
f"offset={self.device_offset & (X24164_SIZE - 1):03X} "
|
||||
f"{self.old_value & 0xFF:02X}->{self.new_value & 0xFF:02X} source={self.source}"
|
||||
)
|
||||
|
||||
|
||||
class X24164Bus:
|
||||
"""Bit-level two-wire bus model for X24164 EEPROMs."""
|
||||
|
||||
def __init__(self, devices: list[X24164Device] | None = None) -> None:
|
||||
self.devices = devices if devices is not None else default_x24164_devices()
|
||||
self.trace_events: list[X24164TraceEvent] = []
|
||||
self.write_events: list[X24164WriteEvent] = []
|
||||
self.active = False
|
||||
self.phase = "idle"
|
||||
self.selected: X24164Device | None = None
|
||||
@@ -197,6 +216,18 @@ class X24164Bus:
|
||||
)
|
||||
return True, value
|
||||
|
||||
def read_linear_byte(self, address: int) -> tuple[bool, int]:
|
||||
device = self._device_for_linear_address(address)
|
||||
if device is None:
|
||||
self.trace_events.append(
|
||||
X24164TraceEvent("x24164_linear_read_miss", address=address & 0x0FFF, message="no_mapped_device")
|
||||
)
|
||||
return False, 0xFF
|
||||
offset = address & (X24164_SIZE - 1)
|
||||
value = device.read(offset)
|
||||
self.trace_events.append(X24164TraceEvent("x24164_linear_read_byte", device.name, value=value, address=offset))
|
||||
return True, value
|
||||
|
||||
def write_linear_word(self, address: int, value: int) -> bool:
|
||||
device = self._device_for_linear_address(address)
|
||||
if device is None:
|
||||
@@ -205,8 +236,8 @@ class X24164Bus:
|
||||
)
|
||||
return False
|
||||
offset = address & (X24164_SIZE - 1)
|
||||
device.write(offset, (value >> 8) & 0xFF)
|
||||
device.write((offset + 1) & (X24164_SIZE - 1), value & 0xFF)
|
||||
self._write_device_byte(device, offset, (value >> 8) & 0xFF, source="linear_word")
|
||||
self._write_device_byte(device, (offset + 1) & (X24164_SIZE - 1), value & 0xFF, source="linear_word")
|
||||
self.trace_events.append(
|
||||
X24164TraceEvent(
|
||||
"x24164_linear_write_word",
|
||||
@@ -218,20 +249,61 @@ class X24164Bus:
|
||||
)
|
||||
return True
|
||||
|
||||
def write_linear_byte(self, address: int, value: int, *, source: str = "linear_byte") -> bool:
|
||||
device = self._device_for_linear_address(address)
|
||||
if device is None:
|
||||
self.trace_events.append(
|
||||
X24164TraceEvent("x24164_linear_write_miss", address=address & 0x0FFF, message="no_mapped_device")
|
||||
)
|
||||
return False
|
||||
offset = address & (X24164_SIZE - 1)
|
||||
self._write_device_byte(device, offset, value, source=source)
|
||||
self.trace_events.append(X24164TraceEvent("x24164_linear_write_byte", device.name, value=value & 0xFF, address=offset))
|
||||
return True
|
||||
|
||||
def dump_linear(self) -> bytes:
|
||||
data = bytearray()
|
||||
for address in range(X24164_LOGICAL_SIZE):
|
||||
device = self._device_for_linear_address(address)
|
||||
if device is None:
|
||||
data.append(0xFF)
|
||||
else:
|
||||
data.append(device.read(address & (X24164_SIZE - 1)))
|
||||
return bytes(data)
|
||||
|
||||
def load_linear(self, data: bytes | bytearray, *, fill: int = 0xFF) -> None:
|
||||
if len(data) > X24164_LOGICAL_SIZE:
|
||||
raise ValueError(f"EEPROM image is too large: {len(data)} > {X24164_LOGICAL_SIZE}")
|
||||
padded = bytearray([fill & 0xFF] * X24164_LOGICAL_SIZE)
|
||||
padded[: len(data)] = data
|
||||
for address, value in enumerate(padded):
|
||||
device = self._device_for_linear_address(address)
|
||||
if device is not None:
|
||||
device.write(address & (X24164_SIZE - 1), value)
|
||||
self.clear_write_log()
|
||||
|
||||
def clear_write_log(self) -> None:
|
||||
self.write_events.clear()
|
||||
|
||||
def seed_factory_defaults_from_rom(self, rom_bytes: bytes) -> None:
|
||||
for offset, word in factory_default_words_from_rom(rom_bytes):
|
||||
for page in range(X24164_LOGICAL_PAGE_COUNT):
|
||||
self.write_linear_word((page * X24164_LOGICAL_PAGE_SIZE) + offset, word)
|
||||
|
||||
for page in range(X24164_LOGICAL_PAGE_COUNT):
|
||||
for page in range(1, X24164_LOGICAL_PAGE_COUNT):
|
||||
base = page * X24164_LOGICAL_PAGE_SIZE
|
||||
for offset in range(0, 8, 2):
|
||||
self.write_linear_word(base + offset, 0x2020)
|
||||
self.clear_write_log()
|
||||
|
||||
def trace_lines(self, limit: int | None = None) -> list[str]:
|
||||
events = self.trace_events if limit is None else self.trace_events[-limit:]
|
||||
return [event.line() for event in events]
|
||||
|
||||
def write_log_lines(self, limit: int | None = None) -> list[str]:
|
||||
events = self.write_events if limit is None else self.write_events[-limit:]
|
||||
return [event.line() for event in events]
|
||||
|
||||
def _scl_rising(self, master_sda: bool, master_sda_output: bool) -> None:
|
||||
if not self.active:
|
||||
return
|
||||
@@ -327,7 +399,7 @@ class X24164Bus:
|
||||
self._ack_armed_on_current_clock = True
|
||||
self.phase = "ignore"
|
||||
return
|
||||
self.selected.write(self.address, value)
|
||||
self._write_device_byte(self.selected, self.address, value, source="bit_banged")
|
||||
self.trace_events.append(
|
||||
X24164TraceEvent("x24164_write_data", self.selected.name, value=value, address=self.address, ack=True)
|
||||
)
|
||||
@@ -365,6 +437,24 @@ class X24164Bus:
|
||||
return device
|
||||
return None
|
||||
|
||||
def _linear_base_for_device(self, device: X24164Device) -> int:
|
||||
return 0x0800 if device.control_base == 0xE0 else 0x0000
|
||||
|
||||
def _write_device_byte(self, device: X24164Device, offset: int, value: int, *, source: str) -> None:
|
||||
offset &= X24164_SIZE - 1
|
||||
old_value = device.read(offset)
|
||||
device.write(offset, value)
|
||||
self.write_events.append(
|
||||
X24164WriteEvent(
|
||||
logical_address=(self._linear_base_for_device(device) + offset) & 0x0FFF,
|
||||
device=device.name,
|
||||
device_offset=offset,
|
||||
old_value=old_value,
|
||||
new_value=value & 0xFF,
|
||||
source=source,
|
||||
)
|
||||
)
|
||||
|
||||
def _selected_name(self) -> str | None:
|
||||
return self.selected.name if self.selected is not None else None
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ from .constants import (
|
||||
SCI_SSR_RDRF,
|
||||
VECTOR_SCI1_RXI,
|
||||
)
|
||||
from .eeprom_image import write_eeprom_snapshot
|
||||
from .errors import UnsupportedInstruction
|
||||
from .memory import MemoryAccess
|
||||
from .runner import H8536Emulator
|
||||
@@ -216,6 +217,7 @@ def run_rx_probe(
|
||||
p9_fast_optimistic_wrapper: bool = False,
|
||||
p7_input: int = 0xFF,
|
||||
eeprom_seed: str = "blank",
|
||||
eeprom_image: bytes | None = None,
|
||||
stop_after_tx_frame: bool = True,
|
||||
) -> tuple[Path, H8536Emulator, str, list[FrameResult]]:
|
||||
rom_bytes, discovered_rom_path = load_rom(rom_path)
|
||||
@@ -231,6 +233,8 @@ def run_rx_probe(
|
||||
p7_input=p7_input,
|
||||
eeprom_seed=eeprom_seed,
|
||||
)
|
||||
if eeprom_image is not None:
|
||||
emulator.memory.load_eeprom_image(eeprom_image)
|
||||
|
||||
boot_context = RunContext()
|
||||
boot_steps_used, boot_reason = _run_until(emulator, boot_steps, _rx_ready, boot_context)
|
||||
@@ -277,6 +281,11 @@ def build_arg_parser() -> argparse.ArgumentParser:
|
||||
parser.add_argument("--p9-fast-optimistic-wrapper", action="store_true", help="legacy fallback for older wrapper experiments; known BFE0/BFFE wrappers use the X24164 model")
|
||||
parser.add_argument("--p7-input", type=parse_int, default=0xFF, help="external P7 pin state for input bits; DIP-off board default is 0xFF")
|
||||
parser.add_argument("--eeprom-seed", choices=("blank", "factory"), default="blank", help="initial X24164/shadow state before reset")
|
||||
parser.add_argument("--eeprom-load", type=Path, help="load a 0x1000-byte logical EEPROM image before booting the ROM")
|
||||
parser.add_argument("--eeprom-save", type=Path, help="save the final 0x1000-byte logical EEPROM image after probing")
|
||||
parser.add_argument("--eeprom-report", type=Path, help="write a readable EEPROM snapshot report after probing")
|
||||
parser.add_argument("--eeprom-report-json", type=Path, help="write a structured EEPROM snapshot report after probing")
|
||||
parser.add_argument("--eeprom-report-include-image", action="store_true", help="include the full EEPROM image as hex in JSON reports")
|
||||
return parser
|
||||
|
||||
|
||||
@@ -305,16 +314,35 @@ def main(argv: list[str] | None = None) -> int:
|
||||
p9_fast_optimistic_wrapper=args.p9_fast_optimistic_wrapper,
|
||||
p7_input=args.p7_input,
|
||||
eeprom_seed=args.eeprom_seed,
|
||||
eeprom_image=args.eeprom_load.read_bytes() if args.eeprom_load else None,
|
||||
stop_after_tx_frame=not args.keep_listening,
|
||||
)
|
||||
|
||||
print(f"rom={rom_path}")
|
||||
if args.eeprom_load:
|
||||
print(f"eeprom_loaded={args.eeprom_load}")
|
||||
print(f"reset_vector={h16(emulator.reset_vector())}")
|
||||
print(boot_summary)
|
||||
for index, result in enumerate(results):
|
||||
for line in result.lines(index):
|
||||
print(line)
|
||||
print("total_tx_frames=" + " | ".join(format_frame(frame) for frame in emulator.sci1.tx_frames))
|
||||
if args.eeprom_save:
|
||||
args.eeprom_save.parent.mkdir(parents=True, exist_ok=True)
|
||||
args.eeprom_save.write_bytes(emulator.memory.dump_eeprom_image())
|
||||
print(f"eeprom_saved={args.eeprom_save}")
|
||||
if args.eeprom_report:
|
||||
write_eeprom_snapshot(emulator.memory, args.eeprom_report, rom_bytes=emulator.memory.rom.data)
|
||||
print(f"eeprom_report={args.eeprom_report}")
|
||||
if args.eeprom_report_json:
|
||||
write_eeprom_snapshot(
|
||||
emulator.memory,
|
||||
args.eeprom_report_json,
|
||||
rom_bytes=emulator.memory.rom.data,
|
||||
as_json=True,
|
||||
include_image_hex=args.eeprom_report_include_image,
|
||||
)
|
||||
print(f"eeprom_report_json={args.eeprom_report_json}")
|
||||
return 0
|
||||
|
||||
|
||||
|
||||
393
h8536/emulator/state_search.py
Normal file
393
h8536/emulator/state_search.py
Normal file
@@ -0,0 +1,393 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import itertools
|
||||
import json
|
||||
import sys
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
from typing import Iterable, TextIO
|
||||
|
||||
from ..formatting import h16, parse_int
|
||||
from .cli import load_rom
|
||||
from .errors import UnsupportedInstruction
|
||||
from .runner import H8536Emulator
|
||||
from .rx_probe import RunContext, _run_until, _rx_ready
|
||||
|
||||
|
||||
CONNECT_WORDS = (0x0000, 0x0080, 0x4080, 0x8080, 0xC080)
|
||||
F730_LATCH_VALUES = (0x00, 0x01, 0x41, 0x81, 0xC1)
|
||||
|
||||
PRESET_DESCRIPTIONS = {
|
||||
"connect-branch": "Start at loc_2CB9 with E000[0]/F730 matrix patches.",
|
||||
"connect-queue": "Queue selector zero in F970, start at loc_2806, then enter loc_2CB9 through the ROM dispatch.",
|
||||
"custom": "Use only --byte/--word/--matrix-* patches and --pc.",
|
||||
}
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class StatePatch:
|
||||
size: int
|
||||
address: int
|
||||
value: int
|
||||
source: str = "user"
|
||||
|
||||
def label(self) -> str:
|
||||
kind = "byte" if self.size == 1 else "word"
|
||||
width = 2 if self.size == 1 else 4
|
||||
return f"{kind}:{h16(self.address)}=0x{self.value:0{width}X}"
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class SearchCase:
|
||||
patches: tuple[StatePatch, ...]
|
||||
pc: int
|
||||
|
||||
def name(self) -> str:
|
||||
return ", ".join(patch.label() for patch in self.patches)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class SearchResult:
|
||||
case_index: int
|
||||
patches: tuple[StatePatch, ...]
|
||||
pc: int
|
||||
steps: int
|
||||
stopped_reason: str
|
||||
final_pc: int
|
||||
display: str
|
||||
line0: str
|
||||
outcome: str
|
||||
f730: int
|
||||
e000: int
|
||||
f9b4: int
|
||||
f9b9: int
|
||||
unsupported: str | None = None
|
||||
|
||||
def payload(self) -> dict[str, object]:
|
||||
return {
|
||||
"case_index": self.case_index,
|
||||
"patches": [patch.label() for patch in self.patches],
|
||||
"pc": h16(self.pc),
|
||||
"steps": self.steps,
|
||||
"stopped_reason": self.stopped_reason,
|
||||
"final_pc": h16(self.final_pc),
|
||||
"display": self.display,
|
||||
"line0": self.line0,
|
||||
"outcome": self.outcome,
|
||||
"f730": f"0x{self.f730:02X}",
|
||||
"e000": f"0x{self.e000:04X}",
|
||||
"f9b4": f"0x{self.f9b4:02X}",
|
||||
"f9b9": f"0x{self.f9b9:02X}",
|
||||
"unsupported": self.unsupported,
|
||||
}
|
||||
|
||||
|
||||
def build_arg_parser() -> argparse.ArgumentParser:
|
||||
parser = argparse.ArgumentParser(
|
||||
description=(
|
||||
"Patch internal emulator state and search for CONNECT LCD outcomes. "
|
||||
"This is bounded ROM execution, not an unbounded serial fuzzer."
|
||||
)
|
||||
)
|
||||
parser.add_argument("--preset", choices=sorted(PRESET_DESCRIPTIONS), default="connect-queue")
|
||||
parser.add_argument("--rom", type=Path, help="ROM image path; defaults to ROM/M27C512@DIP28_1.BIN when present")
|
||||
parser.add_argument("--pc", type=parse_int, help="entry PC for custom/direct tests")
|
||||
parser.add_argument("--stop-pc", type=parse_int, default=0xFFFF, help="sentinel PC used to stop after RTS")
|
||||
parser.add_argument("--stack", type=parse_int, default=0xFF00, help="temporary stack pointer for direct function entry")
|
||||
parser.add_argument("--boot-steps", type=int, default=250_000, help="steps to boot before patching state")
|
||||
parser.add_argument("--max-steps", type=int, default=120_000, help="steps to run each patched case")
|
||||
parser.add_argument("--clock-hz", type=parse_int, default=10_000_000)
|
||||
parser.add_argument("--interval-steps", type=int, default=512)
|
||||
parser.add_argument("--frt1-ocia-steps", type=int, default=None)
|
||||
parser.add_argument("--frt2-ocia-steps", type=int, default=None)
|
||||
parser.add_argument("--no-p9-fast-path", action="store_true")
|
||||
parser.add_argument("--p9-fast-input", type=parse_int, default=0xFF)
|
||||
parser.add_argument("--p9-fast-optimistic-wrapper", action="store_true")
|
||||
parser.add_argument("--p7-input", type=parse_int, default=0xFF)
|
||||
parser.add_argument("--eeprom-seed", choices=("blank", "factory"), default="blank")
|
||||
parser.add_argument("--byte", action="append", default=[], help="fixed byte patch, e.g. F730=0")
|
||||
parser.add_argument("--word", action="append", default=[], help="fixed word patch, e.g. E000=0x8080")
|
||||
parser.add_argument("--matrix-byte", action="append", default=[], help="byte matrix patch, e.g. F730=0,1,0x41")
|
||||
parser.add_argument("--matrix-word", action="append", default=[], help="word matrix patch, e.g. E000=0x4080,0x8080")
|
||||
parser.add_argument("--target", choices=("ok", "dxc", "not-act", "any-connect", "changed"), default="ok")
|
||||
parser.add_argument("--first-hit", action="store_true", help="stop after the first target hit")
|
||||
parser.add_argument("--show-all", action="store_true", help="print every case, not only hits/non-baseline outcomes")
|
||||
parser.add_argument("--limit", type=int, help="maximum number of cases to run")
|
||||
parser.add_argument("--json-out", type=Path, help="write machine-readable results")
|
||||
parser.add_argument("--dry-run", action="store_true", help="print planned cases without running the emulator")
|
||||
return parser
|
||||
|
||||
|
||||
def main(argv: list[str] | None = None, *, stdout: TextIO = sys.stdout) -> int:
|
||||
args = build_arg_parser().parse_args(argv)
|
||||
cases = build_cases(args)
|
||||
if args.limit is not None:
|
||||
cases = cases[: max(0, args.limit)]
|
||||
|
||||
if args.dry_run:
|
||||
print(f"preset={args.preset} cases={len(cases)}", file=stdout)
|
||||
print(PRESET_DESCRIPTIONS[args.preset], file=stdout)
|
||||
for index, case in enumerate(cases[:32]):
|
||||
print(f"case[{index}] pc={h16(case.pc)} {case.name()}", file=stdout)
|
||||
if len(cases) > 32:
|
||||
print(f"... {len(cases) - 32} more cases", file=stdout)
|
||||
return 0
|
||||
|
||||
results = run_search(args, cases)
|
||||
print(format_results(results, target=args.target, show_all=args.show_all), file=stdout)
|
||||
if args.json_out:
|
||||
write_json(args.json_out, args, cases, results)
|
||||
return 0
|
||||
|
||||
|
||||
def build_cases(args: argparse.Namespace) -> list[SearchCase]:
|
||||
fixed = []
|
||||
fixed.extend(parse_patch_list(args.byte, size=1, source="user"))
|
||||
fixed.extend(parse_patch_list(args.word, size=2, source="user"))
|
||||
|
||||
matrix_groups: list[list[StatePatch]] = []
|
||||
if args.preset == "connect-branch":
|
||||
matrix_groups.extend(
|
||||
[
|
||||
[StatePatch(2, 0xE000, value, "preset") for value in CONNECT_WORDS],
|
||||
[StatePatch(1, 0xF730, value, "preset") for value in F730_LATCH_VALUES],
|
||||
]
|
||||
)
|
||||
default_pc = 0x2CB9
|
||||
elif args.preset == "connect-queue":
|
||||
fixed.extend(
|
||||
[
|
||||
StatePatch(1, 0xF9B9, 0x00, "preset"),
|
||||
StatePatch(1, 0xF9B4, 0x01, "preset"),
|
||||
StatePatch(2, 0xF970, 0x0000, "preset"),
|
||||
]
|
||||
)
|
||||
matrix_groups.extend(
|
||||
[
|
||||
[StatePatch(2, 0xE000, value, "preset") for value in CONNECT_WORDS],
|
||||
[StatePatch(1, 0xF730, value, "preset") for value in F730_LATCH_VALUES],
|
||||
]
|
||||
)
|
||||
default_pc = 0x2806
|
||||
else:
|
||||
default_pc = 0x2CB9
|
||||
|
||||
matrix_groups.extend(parse_matrix_list(args.matrix_byte, size=1, source="user"))
|
||||
matrix_groups.extend(parse_matrix_list(args.matrix_word, size=2, source="user"))
|
||||
pc = args.pc if args.pc is not None else default_pc
|
||||
if not matrix_groups:
|
||||
return [SearchCase(tuple(fixed), pc)]
|
||||
return [
|
||||
SearchCase(tuple(fixed + list(combo)), pc)
|
||||
for combo in itertools.product(*matrix_groups)
|
||||
]
|
||||
|
||||
|
||||
def run_search(args: argparse.Namespace, cases: Iterable[SearchCase]) -> list[SearchResult]:
|
||||
rom_bytes, _rom_path = load_rom(args.rom)
|
||||
results: list[SearchResult] = []
|
||||
for index, case in enumerate(cases):
|
||||
emulator = H8536Emulator(
|
||||
rom_bytes,
|
||||
interval_steps=args.interval_steps,
|
||||
frt1_ocia_steps=args.frt1_ocia_steps,
|
||||
frt2_ocia_steps=args.frt2_ocia_steps,
|
||||
clock_hz=args.clock_hz,
|
||||
p9_fast_path_enabled=not args.no_p9_fast_path,
|
||||
p9_fast_default_input_byte=args.p9_fast_input,
|
||||
p9_fast_default_wrapper_success=args.p9_fast_optimistic_wrapper,
|
||||
p7_input=args.p7_input,
|
||||
eeprom_seed=args.eeprom_seed,
|
||||
)
|
||||
_run_until(emulator, args.boot_steps, _rx_ready, RunContext())
|
||||
result = run_case(emulator, case, max_steps=args.max_steps, stop_pc=args.stop_pc, stack=args.stack)
|
||||
results.append(SearchResult(index, case.patches, case.pc, *result))
|
||||
if args.first_hit and target_matches(results[-1].outcome, args.target):
|
||||
break
|
||||
return results
|
||||
|
||||
|
||||
def run_case(
|
||||
emulator: H8536Emulator,
|
||||
case: SearchCase,
|
||||
*,
|
||||
max_steps: int,
|
||||
stop_pc: int,
|
||||
stack: int,
|
||||
) -> tuple[int, str, int, str, str, str, int, int, int, int, str | None]:
|
||||
for patch in case.patches:
|
||||
apply_patch(emulator, patch)
|
||||
emulator.cpu.sr |= 0x0700
|
||||
emulator.cpu.regs[7] = stack & 0xFFFF
|
||||
emulator.memory.write16(stack, stop_pc)
|
||||
emulator.cpu.pc = case.pc & 0xFFFF
|
||||
|
||||
stopped_reason = "max_steps"
|
||||
unsupported: str | None = None
|
||||
steps = 0
|
||||
for index in range(max(0, max_steps)):
|
||||
if emulator.cpu.pc == (stop_pc & 0xFFFF):
|
||||
stopped_reason = "stop_pc"
|
||||
break
|
||||
try:
|
||||
emulator.step()
|
||||
except UnsupportedInstruction as exc:
|
||||
stopped_reason = "unsupported_instruction"
|
||||
unsupported = str(exc)
|
||||
break
|
||||
steps = index + 1
|
||||
display = emulator.memory.lcd.display_text(lines=4, width=16)
|
||||
line0 = emulator.memory.lcd.line_text(0)
|
||||
outcome = classify_display(display)
|
||||
return (
|
||||
steps,
|
||||
stopped_reason,
|
||||
emulator.cpu.pc,
|
||||
display,
|
||||
line0,
|
||||
outcome,
|
||||
emulator.memory.read8(0xF730),
|
||||
emulator.memory.read16(0xE000),
|
||||
emulator.memory.read8(0xF9B4),
|
||||
emulator.memory.read8(0xF9B9),
|
||||
unsupported,
|
||||
)
|
||||
|
||||
|
||||
def apply_patch(emulator: H8536Emulator, patch: StatePatch) -> None:
|
||||
if patch.size == 1:
|
||||
emulator.memory.write8(patch.address, patch.value)
|
||||
elif patch.size == 2:
|
||||
emulator.memory.write16(patch.address, patch.value)
|
||||
else:
|
||||
raise ValueError(f"unsupported patch size {patch.size}")
|
||||
|
||||
|
||||
def classify_display(display: str) -> str:
|
||||
if "CONNECT: OK" in display:
|
||||
return "ok"
|
||||
if "CONNECT:DXC-637" in display:
|
||||
return "dxc"
|
||||
if "CONNECT:NOT ACT" in display or "CONNECT NOT ACT" in display:
|
||||
return "not-act"
|
||||
if "CONNECT" in display:
|
||||
return "other-connect"
|
||||
return "other"
|
||||
|
||||
|
||||
def target_matches(outcome: str, target: str) -> bool:
|
||||
if target == "any-connect":
|
||||
return outcome in {"ok", "dxc", "not-act", "other-connect"}
|
||||
if target == "changed":
|
||||
return outcome != "not-act"
|
||||
return outcome == target
|
||||
|
||||
|
||||
def format_results(results: list[SearchResult], *, target: str, show_all: bool = False) -> str:
|
||||
lines = [
|
||||
"Emulator CONNECT state search",
|
||||
f"cases={len(results)} target={target}",
|
||||
]
|
||||
hits = [result for result in results if target_matches(result.outcome, target)]
|
||||
lines.append(f"hits={len(hits)}")
|
||||
selected = results if show_all else [result for result in results if result.outcome != "not-act"]
|
||||
if not selected:
|
||||
selected = hits[:]
|
||||
for result in selected:
|
||||
hit = "hit" if target_matches(result.outcome, target) else "miss"
|
||||
lines.append(
|
||||
f"[{result.case_index:03d}] {hit} outcome={result.outcome} stopped={result.stopped_reason} "
|
||||
f"steps={result.steps} pc={h16(result.final_pc)} E000=0x{result.e000:04X} "
|
||||
f"F730=0x{result.f730:02X} line0={result.line0!r}"
|
||||
)
|
||||
lines.append(" " + ", ".join(patch.label() for patch in result.patches))
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def parse_patch_list(values: Iterable[str], *, size: int, source: str) -> list[StatePatch]:
|
||||
return [parse_single_patch(value, size=size, source=source) for value in values]
|
||||
|
||||
|
||||
def parse_matrix_list(values: Iterable[str], *, size: int, source: str) -> list[list[StatePatch]]:
|
||||
return [parse_matrix_patch(value, size=size, source=source) for value in values]
|
||||
|
||||
|
||||
def parse_single_patch(text: str, *, size: int, source: str = "user") -> StatePatch:
|
||||
address, value_text = split_assignment(text)
|
||||
value = parse_int(value_text)
|
||||
return StatePatch(size, parse_address(address), value & mask_for_size(size), source)
|
||||
|
||||
|
||||
def parse_matrix_patch(text: str, *, size: int, source: str = "user") -> list[StatePatch]:
|
||||
address, values_text = split_assignment(text)
|
||||
address_value = parse_address(address)
|
||||
patches = []
|
||||
for value_text in values_text.split(","):
|
||||
if not value_text.strip():
|
||||
continue
|
||||
patches.append(StatePatch(size, address_value, parse_int(value_text) & mask_for_size(size), source))
|
||||
if not patches:
|
||||
raise argparse.ArgumentTypeError(f"matrix patch has no values: {text!r}")
|
||||
return patches
|
||||
|
||||
|
||||
def split_assignment(text: str) -> tuple[str, str]:
|
||||
if "=" not in text:
|
||||
raise argparse.ArgumentTypeError(f"patch must be ADDRESS=VALUE, got {text!r}")
|
||||
left, right = text.split("=", 1)
|
||||
if not left.strip() or not right.strip():
|
||||
raise argparse.ArgumentTypeError(f"patch must be ADDRESS=VALUE, got {text!r}")
|
||||
return left.strip(), right.strip()
|
||||
|
||||
|
||||
def parse_address(text: str) -> int:
|
||||
token = text.strip()
|
||||
if token.upper().startswith("H'"):
|
||||
token = "0x" + token[2:]
|
||||
elif not token.lower().startswith("0x") and any(char in token.upper() for char in "ABCDEF"):
|
||||
token = "0x" + token
|
||||
return parse_int(token) & 0xFFFF
|
||||
|
||||
|
||||
def mask_for_size(size: int) -> int:
|
||||
if size == 1:
|
||||
return 0xFF
|
||||
if size == 2:
|
||||
return 0xFFFF
|
||||
raise ValueError(f"unsupported patch size {size}")
|
||||
|
||||
|
||||
def write_json(path: Path, args: argparse.Namespace, cases: list[SearchCase], results: list[SearchResult]) -> None:
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
payload = {
|
||||
"kind": "h8536_emulator_state_search",
|
||||
"preset": args.preset,
|
||||
"description": PRESET_DESCRIPTIONS[args.preset],
|
||||
"target": args.target,
|
||||
"case_count": len(cases),
|
||||
"result_count": len(results),
|
||||
"hits": [result.payload() for result in results if target_matches(result.outcome, args.target)],
|
||||
"results": [result.payload() for result in results],
|
||||
}
|
||||
path.write_text(json.dumps(payload, indent=2, sort_keys=True) + "\n", encoding="utf-8")
|
||||
|
||||
|
||||
__all__ = [
|
||||
"CONNECT_WORDS",
|
||||
"F730_LATCH_VALUES",
|
||||
"SearchCase",
|
||||
"SearchResult",
|
||||
"StatePatch",
|
||||
"apply_patch",
|
||||
"build_arg_parser",
|
||||
"build_cases",
|
||||
"classify_display",
|
||||
"main",
|
||||
"parse_address",
|
||||
"parse_matrix_patch",
|
||||
"parse_single_patch",
|
||||
"run_case",
|
||||
"run_search",
|
||||
"target_matches",
|
||||
]
|
||||
754
h8536/rx_branch_trace.py
Normal file
754
h8536/rx_branch_trace.py
Normal file
@@ -0,0 +1,754 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
from collections.abc import Mapping
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
from .formatting import h16
|
||||
|
||||
|
||||
JsonObject = dict[str, Any]
|
||||
|
||||
DEFAULT_INPUT = Path("build/rom_decompiled.json")
|
||||
|
||||
RX_CAPTURE_START = 0xF868
|
||||
RX_CAPTURE_END = 0xF86D
|
||||
RX_VALIDATION_START = 0xF860
|
||||
RX_VALIDATION_END = 0xF865
|
||||
TX_STAGING_START = 0xF850
|
||||
TX_STAGING_END = 0xF854
|
||||
CHECKSUM_SEED = 0x5A
|
||||
|
||||
|
||||
def load_rx_branch_input(path: Path) -> JsonObject:
|
||||
with path.open("r", encoding="utf-8") as handle:
|
||||
payload = json.load(handle)
|
||||
if not isinstance(payload, dict) or "instructions" not in payload:
|
||||
raise ValueError(f"{path} does not look like h8536_decompiler JSON output")
|
||||
return payload
|
||||
|
||||
|
||||
def analyze_rx_branch_trace(payload: Mapping[str, Any]) -> JsonObject:
|
||||
instructions = _instruction_sequence(payload.get("instructions"))
|
||||
by_address = {int(ins["address"]): ins for ins in instructions if "address" in ins}
|
||||
|
||||
selector_decode = _selector_decode(by_address)
|
||||
stages = [
|
||||
_rx_interrupt_capture(by_address),
|
||||
_frame_validation(by_address),
|
||||
_dispatcher(by_address),
|
||||
_error_retry_path(by_address),
|
||||
_pending_selector_ring(by_address),
|
||||
_resend_and_timeout_paths(by_address),
|
||||
]
|
||||
commands = _command_branches(by_address)
|
||||
table_surfaces = _table_surfaces(by_address)
|
||||
downstream_traces = _downstream_traces(by_address)
|
||||
feedback_loops = _feedback_loops(by_address)
|
||||
confidence = _confidence(stages, commands)
|
||||
|
||||
return {
|
||||
"kind": "rx_branch_trace",
|
||||
"summary": {
|
||||
"title": "SCI1 RX frame branch map",
|
||||
"confidence": confidence,
|
||||
"core_finding": (
|
||||
"The ROM captures six SCI1 bytes, validates a 0x5A-seeded XOR checksum, "
|
||||
"decodes RX[0] & 0x07, then splits into initial commands while FAA2 == 0 "
|
||||
"and continuation commands while FAA2 != 0."
|
||||
),
|
||||
},
|
||||
"frame_model": {
|
||||
"channel": "SCI1",
|
||||
"capture_buffer": _range_payload(RX_CAPTURE_START, RX_CAPTURE_END),
|
||||
"validation_buffer": _range_payload(RX_VALIDATION_START, RX_VALIDATION_END),
|
||||
"tx_staging_buffer": _range_payload(TX_STAGING_START, TX_STAGING_END),
|
||||
"frame_length": 6,
|
||||
"checksum_seed": CHECKSUM_SEED,
|
||||
"checksum_seed_hex": f"0x{CHECKSUM_SEED:02X}",
|
||||
"command_expression": "RX[0] & 0x07",
|
||||
"index_expression_candidate": "loc_622B(RX[1], RX[2])",
|
||||
"value_expression_candidate": "RX[3:4]",
|
||||
},
|
||||
"selector_decode": selector_decode,
|
||||
"stages": stages,
|
||||
"commands": commands,
|
||||
"table_surfaces": table_surfaces,
|
||||
"downstream_traces": downstream_traces,
|
||||
"feedback_loops": feedback_loops,
|
||||
"state_bits": _state_bits(),
|
||||
"bench_implications": _bench_implications(),
|
||||
"caveats": [
|
||||
"This is an address-driven static branch map, not proof of every runtime predicate value.",
|
||||
"Semantic names are candidates; the branch destinations and RAM side effects are ROM evidence.",
|
||||
"loc_BE70/F970 and loc_3E54/F870 are separate queues; this report names them separately because mixing them up changes the command-4/5 interpretation.",
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
def format_text_report(analysis: Mapping[str, Any]) -> str:
|
||||
summary = analysis["summary"]
|
||||
frame = analysis["frame_model"]
|
||||
lines = [
|
||||
"H8/536 SCI1 RX Branch Trace",
|
||||
"",
|
||||
f"Summary: {summary['core_finding']}",
|
||||
f"Confidence: {summary['confidence']}",
|
||||
"",
|
||||
"Frame Model:",
|
||||
f"- capture buffer: {frame['capture_buffer']['range_hex']}",
|
||||
f"- validation buffer: {frame['validation_buffer']['range_hex']}",
|
||||
f"- command: {frame['command_expression']}",
|
||||
f"- logical index candidate: {frame['index_expression_candidate']}",
|
||||
f"- value candidate: {frame['value_expression_candidate']}",
|
||||
f"- checksum: 0x5A XOR RX[0..4] == RX[5]",
|
||||
"",
|
||||
"Selector Decode:",
|
||||
]
|
||||
selector_decode = analysis.get("selector_decode", {})
|
||||
if selector_decode:
|
||||
status = "present" if selector_decode.get("present") else "missing"
|
||||
lines.append(f"- {selector_decode['title']}: {status}")
|
||||
lines.append(f" {selector_decode['summary']}")
|
||||
for rule in selector_decode.get("rules", []):
|
||||
evidence = _join_hex(rule.get("evidence_addresses_hex", []))
|
||||
suffix = f" [{evidence}]" if evidence else ""
|
||||
lines.append(f" - {rule['condition']}: {rule['outcome']}{suffix}")
|
||||
for implication in selector_decode.get("implications", []):
|
||||
lines.append(f" - implication: {implication}")
|
||||
|
||||
lines.extend([
|
||||
"",
|
||||
"Stages:",
|
||||
])
|
||||
for stage in analysis.get("stages", []):
|
||||
status = "present" if stage.get("present") else "missing"
|
||||
lines.append(f"- {stage['title']}: {status}")
|
||||
lines.append(f" {stage['summary']}")
|
||||
for branch in stage.get("branches", []):
|
||||
condition = branch.get("condition")
|
||||
outcome = branch.get("outcome")
|
||||
evidence = _join_hex(branch.get("evidence_addresses_hex", []))
|
||||
suffix = f" [{evidence}]" if evidence else ""
|
||||
lines.append(f" - {condition}: {outcome}{suffix}")
|
||||
|
||||
lines.extend(["", "Command Branches:"])
|
||||
for command in analysis.get("commands", []):
|
||||
response = command.get("response_candidate") or "no immediate serial response"
|
||||
lines.append(
|
||||
f"- cmd {command['command_hex']} {command['name']}: {command['availability']}; "
|
||||
f"handler {command['handler_address_hex']}; {command['summary']}"
|
||||
)
|
||||
lines.append(f" response: {response}")
|
||||
for effect in command.get("side_effects", []):
|
||||
lines.append(f" - {effect}")
|
||||
|
||||
lines.extend(["", "Table Surfaces:"])
|
||||
for surface in analysis.get("table_surfaces", []):
|
||||
status = "present" if surface.get("present") else "missing"
|
||||
evidence = _join_hex(surface.get("evidence_addresses_hex", []))
|
||||
suffix = f" [{evidence}]" if evidence else ""
|
||||
lines.append(f"- {surface['name']} {surface['range_hex']}: {status}; {surface['summary']}{suffix}")
|
||||
for detail in surface.get("details", []):
|
||||
lines.append(f" - {detail}")
|
||||
|
||||
lines.extend(["", "Downstream Flow Traces:"])
|
||||
for trace in analysis.get("downstream_traces", []):
|
||||
status = "present" if trace.get("present") else "missing"
|
||||
lines.append(f"- {trace['title']}: {status}")
|
||||
lines.append(f" {trace['summary']}")
|
||||
for step in trace.get("steps", []):
|
||||
evidence = _join_hex(step.get("evidence_addresses_hex", []))
|
||||
suffix = f" [{evidence}]" if evidence else ""
|
||||
lines.append(f" - {step['name']}: {step['effect']}{suffix}")
|
||||
|
||||
lines.extend(["", "RX-to-TX Feedback Loops:"])
|
||||
for loop in analysis.get("feedback_loops", []):
|
||||
status = "present" if loop.get("present") else "missing"
|
||||
lines.append(f"- {loop['name']}: {status}")
|
||||
lines.append(f" trigger: {loop['trigger']}")
|
||||
lines.append(f" path: {loop['path']}")
|
||||
lines.append(f" TX outcome: {loop['tx_outcome']}")
|
||||
if loop.get("timing_gate"):
|
||||
lines.append(f" timing/gate: {loop['timing_gate']}")
|
||||
if loop.get("bench_read"):
|
||||
lines.append(f" bench read: {loop['bench_read']}")
|
||||
evidence = _join_hex(loop.get("evidence_addresses_hex", []))
|
||||
if evidence:
|
||||
lines.append(f" evidence: {evidence}")
|
||||
|
||||
lines.extend(["", "State Bits:"])
|
||||
for bit in analysis.get("state_bits", []):
|
||||
lines.append(f"- {bit['address_hex']}.{bit['bit']}: {bit['name']} - {bit['meaning']}")
|
||||
|
||||
lines.extend(["", "Bench Implications:"])
|
||||
for implication in analysis.get("bench_implications", []):
|
||||
lines.append(f"- {implication}")
|
||||
|
||||
lines.extend(["", "Caveats:"])
|
||||
for caveat in analysis.get("caveats", []):
|
||||
lines.append(f"- {caveat}")
|
||||
return "\n".join(lines).rstrip() + "\n"
|
||||
|
||||
|
||||
def write_rx_branch_trace(input_path: Path, output_path: Path, *, as_json: bool = False) -> JsonObject:
|
||||
analysis = analyze_rx_branch_trace(load_rx_branch_input(input_path))
|
||||
output_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
if as_json:
|
||||
output_path.write_text(json.dumps(analysis, indent=2, sort_keys=True) + "\n", encoding="utf-8")
|
||||
else:
|
||||
output_path.write_text(format_text_report(analysis), encoding="utf-8")
|
||||
return analysis
|
||||
|
||||
|
||||
def main(argv: list[str] | None = None, stdout: Any | None = None) -> int:
|
||||
parser = argparse.ArgumentParser(description="Trace the H8/536 SCI1 RX command branch tree.")
|
||||
parser.add_argument("input", nargs="?", type=Path, default=DEFAULT_INPUT)
|
||||
parser.add_argument("--json", action="store_true", help="emit structured JSON instead of readable text")
|
||||
parser.add_argument("--out", type=Path, default=None, help="write report to this path")
|
||||
args = parser.parse_args(argv)
|
||||
|
||||
stream = stdout
|
||||
if stream is None:
|
||||
import sys
|
||||
|
||||
stream = sys.stdout
|
||||
|
||||
analysis = analyze_rx_branch_trace(load_rx_branch_input(args.input))
|
||||
rendered = json.dumps(analysis, indent=2, sort_keys=True) + "\n" if args.json else format_text_report(analysis)
|
||||
if args.out:
|
||||
args.out.parent.mkdir(parents=True, exist_ok=True)
|
||||
args.out.write_text(rendered, encoding="utf-8")
|
||||
print(f"wrote {args.out}", file=stream)
|
||||
else:
|
||||
print(rendered, end="", file=stream)
|
||||
return 0
|
||||
|
||||
|
||||
def _selector_decode(by_address: Mapping[int, JsonObject]) -> JsonObject:
|
||||
return {
|
||||
"title": "loc_622B logical selector decode",
|
||||
"present": _has_all(by_address, (0x622B, 0x622D, 0x6231, 0x6244, 0x624D, 0x6256, 0x625F, 0x6264)),
|
||||
"summary": (
|
||||
"The RX handler builds a raw word from RX[1:2], uses RX[1] bit0-bit2 as a page, "
|
||||
"keeps RX[2] as the low selector byte, and maps the result into a 0x000-0x1FF logical selector."
|
||||
),
|
||||
"input_expression_candidate": "raw = (RX[1] << 8) | RX[2]; page = RX[1] & 0x07; low = RX[2]",
|
||||
"rules": [
|
||||
_branch("page 0, or pages 4-7, and low <= 0x7F", "selector = 0x000 + low", [0x6234, 0x6236, 0x6244, 0x6248, 0x6264]),
|
||||
_branch("page 1 and low <= 0xFF", "selector = 0x080 + low", [0x6238, 0x623A, 0x624D, 0x6251, 0x6264]),
|
||||
_branch("page 2 and low <= 0x7F", "selector = 0x180 + low", [0x623C, 0x623E, 0x6256, 0x625A, 0x6264]),
|
||||
_branch("page 3, page/range failure, or page 0/4-7 with low > 0x7F", "selector forced to 0x01FF", [0x6240, 0x6242, 0x625F, 0x6261]),
|
||||
],
|
||||
"implications": [
|
||||
"RX[1].7 is not part of the selector here because the dispatcher rejects byte1 bit7 before command handling.",
|
||||
"Commands shaped as 01 80 xx ... are rejected before loc_622B-derived command handling even if the low byte looks useful.",
|
||||
"Page 4-7 encodings appear to alias the page-0 decode path unless the low byte is out of range.",
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
def _rx_interrupt_capture(by_address: Mapping[int, JsonObject]) -> JsonObject:
|
||||
return {
|
||||
"title": "SCI1 RXI/ERI byte capture",
|
||||
"present": _has_all(by_address, (0xBB57, 0xBB67, 0xBB6D, 0xBB90, 0xBB96, 0xBB9E)),
|
||||
"summary": (
|
||||
"ERI latches FAA4.7 and clears physical error flags; RXI clears RDRF, reads SCI1_RDR, "
|
||||
"stores bytes into F868-F86D, reloads F9C1, and sets F9C5 when six bytes are captured."
|
||||
),
|
||||
"branches": [
|
||||
_branch("ERI taken", "set FAA4.7, clear ORER/FER/PER, then fall into RXI byte capture", [0xBB57, 0xBB5B, 0xBB5F, 0xBB63]),
|
||||
_branch("F9C1 == 0 before byte", "clear F9C3 so the byte starts a fresh frame", [0xBB71, 0xBB75, 0xBB77]),
|
||||
_branch("F9C1 != 0 and F9C3 <= 5", "append byte at F868 + F9C3 and increment F9C3", [0xBB7D, 0xBB82, 0xBB8A, 0xBB90, 0xBB96]),
|
||||
_branch("F9C1 != 0 and F9C3 > 5", "clear FAA4 and skip storing this byte", [0xBB7D, 0xBB82, 0xBB84, 0xBB88]),
|
||||
_branch("incremented F9C3 == 6", "load F9C5 with 0x14 as the RX/session timeout window", [0xBB9A, 0xBB9C, 0xBB9E]),
|
||||
_branch("any RXI exit", "reload F9C1 with 0x05 as the inter-byte timeout", [0xBBA3]),
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
def _frame_validation(by_address: Mapping[int, JsonObject]) -> JsonObject:
|
||||
return {
|
||||
"title": "six-byte validation and checksum",
|
||||
"present": _has_all(by_address, (0xBBAB, 0xBBB3, 0xBBCB, 0xBBCF, 0xBBD6, 0xBBEC, 0xBBF0, 0xBC01)),
|
||||
"summary": (
|
||||
"The main-loop processor only runs when F9C3 is six, copies F868-F86D to F860-F865, "
|
||||
"clears F9C3, rejects physical-error frames, checks the 0x5A XOR checksum, and decodes the index."
|
||||
),
|
||||
"branches": [
|
||||
_branch("F9C3 != 6", "return without processing", [0xBBAB, 0xBBB0]),
|
||||
_branch("FAA4.7 set after capture", "enter retry/error path at BE29", [0xBBCF, 0xBBD3]),
|
||||
_branch("checksum mismatch", "enter retry/error path at BE29", [0xBBD6, 0xBBD8, 0xBBDC, 0xBBE0, 0xBBE4, 0xBBE8, 0xBBEC, 0xBBF0]),
|
||||
_branch("checksum valid", "clear FAA6, decode selector from RX[1:2] through loc_622B, and dispatch on RX[0] & 0x07", [0xBBF3, 0xBBF7, 0xBBFD, 0xBC01, 0xBC08, 0xBC0C]),
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
def _dispatcher(by_address: Mapping[int, JsonObject]) -> JsonObject:
|
||||
return {
|
||||
"title": "FAA2 split dispatcher",
|
||||
"present": _has_all(by_address, (0xBC0F, 0xBC15, 0xBC3A, 0xBC5C, 0xBC63)),
|
||||
"summary": (
|
||||
"FAA2 == 0 enters the initial dispatcher for commands 0, 1, 2, and 7. "
|
||||
"FAA2 != 0 enters the continuation dispatcher; commands 4, 5, and 6 only live there."
|
||||
),
|
||||
"branches": [
|
||||
_branch("FAA2 == 0", "set FAA2.7 and test initial commands 0, 1, 2, 7", [0xBC0F, 0xBC13, 0xBC15, 0xBC20, 0xBC24, 0xBC29, 0xBC2E]),
|
||||
_branch("FAA2 != 0 and command bit2 set", "continuation commands 4, 5, 6, 7 are possible", [0xBC0F, 0xBC13, 0xBC3A, 0xBC45, 0xBC4A, 0xBC4F, 0xBC54]),
|
||||
_branch("FAA2 != 0 and command bit2 clear", "BCLR FAA2.3; if that bit was set, clear FAA3 and re-enter initial dispatcher", [0xBC3A, 0xBC3C, 0xBC5C, 0xBC60, 0xBC63, 0xBC67]),
|
||||
_branch("byte1 bit7 set on initial path", "branch to BD0B and return without normal command handling", [0xBC19, 0xBC1D, 0xBD0B]),
|
||||
_branch("byte1 bit7 set on continuation path", "branch to BE27 and return without normal command handling", [0xBC3E, 0xBC42, 0xBE27]),
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
def _error_retry_path(by_address: Mapping[int, JsonObject]) -> JsonObject:
|
||||
return {
|
||||
"title": "checksum/error retry path",
|
||||
"present": _has_all(by_address, (0xBE29, 0xBE2D, 0xBE33, 0xBE37, 0xBE4D, 0xBE6A)),
|
||||
"summary": (
|
||||
"Physical RX errors or checksum failures can either be ignored, clear the session after two retries, "
|
||||
"or stage a command-7 retry/error echo of RX[1:4]."
|
||||
),
|
||||
"branches": [
|
||||
_branch("FAA5.7 == 0", "return after clearing FAA4.7; retry echo is disabled", [0xBE29, 0xBE2D, 0xBE31]),
|
||||
_branch("FAA5.7 == 1 and FAA6 < 2", "stage F850=0x07 and F851-F854=RX[1:4], then call BA26", [0xBE33, 0xBE37, 0xBE3C, 0xBE4D, 0xBE52, 0xBE5A, 0xBE62, 0xBE6A]),
|
||||
_branch("FAA5.7 == 1 and FAA6 >= 2", "load F9C0=0x1F and clear FAA3/FAA2", [0xBE37, 0xBE3C, 0xBE3E, 0xBE43, 0xBE47]),
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
def _pending_selector_ring(by_address: Mapping[int, JsonObject]) -> JsonObject:
|
||||
return {
|
||||
"title": "pending selector ring at loc_BE70",
|
||||
"present": _has_all(by_address, (0xBE70, 0xBE78, 0xBE84, 0xBE91, 0xBE95)),
|
||||
"summary": (
|
||||
"Several write/ACK paths call BE70 with R5 as the logical selector. BE70 deduplicates that selector "
|
||||
"against a small F970-like ring using cursors F9B9/F9B4, then appends it if absent."
|
||||
),
|
||||
"branches": [
|
||||
_branch("selector already present", "exit without appending", [0xBE84, 0xBE88, 0xBE9D]),
|
||||
_branch("ring scan reaches F9B4 cursor", "store R5 into the pending ring and advance F9B4", [0xBE80, 0xBE82, 0xBE91, 0xBE95, 0xBE99]),
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
def _resend_and_timeout_paths(by_address: Mapping[int, JsonObject]) -> JsonObject:
|
||||
return {
|
||||
"title": "session timeout and resend side paths",
|
||||
"present": _has_all(by_address, (0x3FEF, 0x3FF5, 0x3FFD, 0x4003, 0xBE9E, 0xBEB5, 0xBED5, 0xBEE4)),
|
||||
"summary": (
|
||||
"After any complete RX frame, F9C5 keeps FAA5.7/session-gate state alive for a short window. "
|
||||
"When that window expires, 3FEF can clear queue cursors and call 400C, while BE9E handles resend countdowns."
|
||||
),
|
||||
"branches": [
|
||||
_branch("F9C5 == 0 at loc_3FEF", "clear F9B5/F9B0 and clear FAA5.7; if FAA5.7 was set, call 400C reset/NOT-ACT state clear", [0x3FEF, 0x3FF3, 0x3FF5, 0x3FF9, 0x3FFD, 0x4001, 0x4003]),
|
||||
_branch("F9C5 != 0 at loc_3FEF", "set FAA5.7, allowing retry/resend/session-gated paths", [0x3FEF, 0x3FF3, 0x4007]),
|
||||
_branch("BE9E sees no pending FAA5 & FAA3 & 0x80", "clear FAA2 and return", [0xBE9E, 0xBEA5, 0xBEA9, 0xBEAD, 0xBEAF]),
|
||||
_branch("BE9E pending and F9C6==0 and F9C8!=0", "decrement F9C8, reload F9C6, and possibly resend staged TX through BA26", [0xBEB5, 0xBEBB, 0xBEC1, 0xBEC5, 0xBECB, 0xBED1, 0xBED5]),
|
||||
_branch("BE9E pending but F9C8==0", "clear F9C5, which lets 3FEF collapse the session gate later", [0xBEBB, 0xBEBF, 0xBEE4]),
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
def _table_surfaces(by_address: Mapping[int, JsonObject]) -> list[JsonObject]:
|
||||
return [
|
||||
{
|
||||
"name": "primary_value_table",
|
||||
"range_hex": "H'E000-H'E3FF",
|
||||
"present": _has_all(by_address, (0xBC75, 0xBC95, 0xBCEC, 0xBD1A, 0xBD35)),
|
||||
"summary": "logical selector word table; command 0 and continuation command 4 write it, command 1 reads it",
|
||||
"details": [
|
||||
"indexed as E000 + 2*selector after loc_622B",
|
||||
"selector zero writes force the low byte to 0x80 on commands 0 and 4",
|
||||
"this is the table that must contain E000[0]=0x8080 for the emulator-correlated CONNECT OK branch",
|
||||
],
|
||||
"evidence_addresses_hex": _hexes([0xBC75, 0xBC95, 0xBCEC, 0xBD1A, 0xBD35], by_address),
|
||||
},
|
||||
{
|
||||
"name": "current_report_value_table",
|
||||
"range_hex": "H'E800-H'EBFF",
|
||||
"present": _has_all(by_address, (0xBC79, 0xBC99, 0xBD1E, 0xBB35)),
|
||||
"summary": "current/report value table used when queued serial reports are converted into TX frames",
|
||||
"details": [
|
||||
"command 0 writes both primary E000 and current E800 for zero and nonzero selectors",
|
||||
"command 4 writes E800 only on the selector-zero special path; the nonzero command-4 path does not show a matching E800 write here",
|
||||
"loc_BAF2 reads E800 + 2*queued_selector when building autonomous report frames",
|
||||
],
|
||||
"evidence_addresses_hex": _hexes([0xBC79, 0xBC99, 0xBD1E, 0xBB35, 0xBB39, 0xBB3F], by_address),
|
||||
},
|
||||
{
|
||||
"name": "secondary_value_table",
|
||||
"range_hex": "H'E400-H'E7FF",
|
||||
"present": _has_all(by_address, (0xBDE5, 0xBDE9)),
|
||||
"summary": "secondary logical selector word table written by continuation command 6",
|
||||
"details": [
|
||||
"command 6 writes RX[3:4] to E400 + 2*selector",
|
||||
"the matching EC00 flag bit is bit6 rather than bit7",
|
||||
],
|
||||
"evidence_addresses_hex": _hexes([0xBDE5, 0xBDE9], by_address),
|
||||
},
|
||||
{
|
||||
"name": "dirty_flag_table",
|
||||
"range_hex": "H'EC00-H'EDFF",
|
||||
"present": _has_all(by_address, (0xBC82, 0xBC9D, 0xBD22, 0xBD39, 0xBDE9)),
|
||||
"summary": "per-selector flag bytes; command 0/4 set bit7 and command 6 sets bit6",
|
||||
"details": [
|
||||
"the same logical selector indexes this byte table directly, not as a word offset",
|
||||
"loc_48FA and other consumers test these table bits before raising follow-on reports",
|
||||
],
|
||||
"evidence_addresses_hex": _hexes([0xBC82, 0xBC9D, 0xBD22, 0xBD39, 0xBDE9, 0x4911], by_address),
|
||||
},
|
||||
{
|
||||
"name": "mapped_shadow_or_eeprom_surface",
|
||||
"range_hex": "H'F400-H'F4FF",
|
||||
"present": _has_all(by_address, (0xBCA1, 0xBCA9, 0xBD3D, 0xBD45, 0xBD49, 0xBD5F)),
|
||||
"summary": "optional mapped mirror/persistence surface selected through ROM tables around C564/C565",
|
||||
"details": [
|
||||
"nonzero command-0 and command-4 writes consult mapping bytes/words before mirroring into F400",
|
||||
"command 4 can call BFE0 to persist the mapped word when F76E.7 is set",
|
||||
"selector zero bypasses this mapped mirror path",
|
||||
],
|
||||
"evidence_addresses_hex": _hexes([0xBCA1, 0xBCA9, 0xBD3D, 0xBD45, 0xBD49, 0xBD5F], by_address),
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
def _downstream_traces(by_address: Mapping[int, JsonObject]) -> list[JsonObject]:
|
||||
return [
|
||||
{
|
||||
"title": "immediate response staging through loc_BA26",
|
||||
"present": _has_all(by_address, (0xBCB0, 0xBCCD, 0xBCD7, 0xBCFA, 0xBA36, 0xBA64, 0xBA72, 0xBA7F)),
|
||||
"summary": (
|
||||
"Command 0, command 1, command 7, and the retry/error path stage F850-F854, call BA26, "
|
||||
"copy that staging area to F858-F85C, compute F85D, send the first byte, then let TXI send bytes 1-5."
|
||||
),
|
||||
"steps": [
|
||||
_step("stage command-0 echo", "F850=0x04 and F851-F854 mirror the accepted host fields before BA26", [0xBCB0, 0xBCB5, 0xBCC1, 0xBCC9, 0xBCCD]),
|
||||
_step("stage command-1 readback", "F850=0x04, F853/F854 receive the E000 table word, and BA26 sends it", [0xBCD7, 0xBCEC, 0xBCF0, 0xBCF6, 0xBCFA]),
|
||||
_step("finalize TX frame", "BA26 copies F850-F854 to F858-F85C and computes F85D as 0x5A XOR bytes 0-4", [0xBA36, 0xBA3A, 0xBA42, 0xBA4A, 0xBA4E, 0xBA64]),
|
||||
_step("start SCI1 transmission", "BA26 waits for TDRE, writes F858 to SCI1_TDR, sets F9C2=1, clears TDRE, and enables TIE", [0xBA68, 0xBA72, 0xBA76, 0xBA7B, 0xBA7F]),
|
||||
_step("finish SCI1 transmission", "TXI indexes F858+F9C2 until six bytes are sent, then disables TIE", [0xBAAB, 0xBAB1, 0xBAB5, 0xBABF, 0xBAC3, 0xBACA]),
|
||||
],
|
||||
},
|
||||
{
|
||||
"title": "selector-processing queue BE70/F970 into loc_2806",
|
||||
"present": _has_all(by_address, (0xBE70, 0xBE91, 0xBE95, 0x2806, 0x2819, 0x2822, 0x289F)),
|
||||
"summary": (
|
||||
"BE70 appends unique logical selectors into the F970 ring. The main loop later consumes that ring at "
|
||||
"loc_2806 and dispatches selector-specific behavior through the 28A6 jump table."
|
||||
),
|
||||
"steps": [
|
||||
_step("append unique selector", "BE70 scans from F9B9 to F9B4, skips duplicates, writes R5 to F970+2*cursor, and advances F9B4", [0xBE70, 0xBE78, 0xBE84, 0xBE91, 0xBE95, 0xBE99]),
|
||||
_step("consume selector", "loc_2806 reads F970+2*F9B9, advances F9B9, masks the selector to 0x01FF, and keeps it in R5", [0x2806, 0x280C, 0x2819, 0x281D, 0x2822, 0x2826, 0x282A]),
|
||||
_step("active-selector side path", "selectors matching F736/F738/F73A/F73C/F73E/F740/F742/F754 call loc_48FA before the jump table", [0x2837, 0x285E, 0x2878, 0x2892, 0x2CAB, 0x2CAD]),
|
||||
_step("selector jump table", "the consumer jumps through table 28A6; selector zero is emulator-correlated with the CONNECT handler window", [0x289F, 0x28A3]),
|
||||
],
|
||||
},
|
||||
{
|
||||
"title": "serial-report queue loc_3E54/F870 into loc_BAF2",
|
||||
"present": _has_all(by_address, (0x3E54, 0x3E76, 0x3E7A, 0x3E9A, 0x3EBF, 0x3EC3, 0xBAF2, 0xBB00)),
|
||||
"summary": (
|
||||
"loc_3E54 is a shared enqueue helper. R2.bit7 queues serial-visible report indexes in F870, "
|
||||
"while R2.bit6 queues selector-processing work in F970. loc_BAF2 is the path that turns F870 entries into outbound 6-byte frames."
|
||||
),
|
||||
"steps": [
|
||||
_step("enqueue serial report", "when R2.7 is set, loc_3E54 deduplicates R3 in F870 and advances F9B0", [0x3E54, 0x3E58, 0x3E6C, 0x3E76, 0x3E7A, 0x3E7E]),
|
||||
_step("queue backpressure drain", "if the serial queue is nearly full, loc_3E54 calls loc_3FD3 until there is space", [0x3E82, 0x3E8B, 0x3E91, 0x3E93]),
|
||||
_step("enqueue selector processing", "when R2.6 is set, loc_3E54 deduplicates R3 in F970 and advances F9B4", [0x3E9A, 0x3E9E, 0x3EB2, 0x3EBF, 0x3EC3, 0x3EC7]),
|
||||
_step("dequeue serial report", "loc_BAF2 compares F9B5/F9B0, reads F870+2*F9B5, builds a TX report from E800, and calls BA26", [0xBAF2, 0xBAF8, 0xBB00, 0xBB08, 0xBB1C, 0xBB35, 0xBB43]),
|
||||
_step("open continuation latch", "after a report send, loc_BAF2 sets FAA2.3, FAA3.7, F9C6, and F9C8; command 4/5/6 can then consume or ACK that report", [0xBB00, 0xBB46, 0xBB4C, 0xBB51]),
|
||||
],
|
||||
},
|
||||
{
|
||||
"title": "TXI/RXI race and continuation collapse",
|
||||
"present": _has_all(by_address, (0xBA84, 0xBA8A, 0xBA90, 0xBA96, 0xBA9A, 0xBA9E, 0xBAA2)),
|
||||
"summary": (
|
||||
"The TX interrupt handler has an interlock: if a queued report is awaiting continuation and RX bytes are already arriving, "
|
||||
"it clears the report/continuation state and stops TXI before the normal frame-finish path."
|
||||
),
|
||||
"steps": [
|
||||
_step("detect overlap", "TXI tests FAA2.3, FAA5.7, and nonzero F9C3 before continuing the TX frame", [0xBA84, 0xBA8A, 0xBA90]),
|
||||
_step("collapse continuation", "on overlap it clears FAA2.3 and FAA3, disables TIE, and loads F9C0=0x1F", [0xBA96, 0xBA9A, 0xBA9E, 0xBAA2]),
|
||||
_step("normal completion path", "without the overlap, TXI sends bytes until F9C2 reaches six and then starts the post-TX delay path", [0xBAA9, 0xBAB5, 0xBAC3, 0xBACA, 0xBADA, 0xBAED]),
|
||||
],
|
||||
},
|
||||
{
|
||||
"title": "session expiry into reset/not-active state",
|
||||
"present": _has_all(by_address, (0x3FEF, 0x3FFD, 0x4003, 0x400C, 0x4028, 0x4042)),
|
||||
"summary": (
|
||||
"When F9C5 reaches zero, loc_3FEF clears queue cursors and FAA5.7; if FAA5.7 had been set, "
|
||||
"loc_400C clears connection/session RAM and refreshes the inactive display state."
|
||||
),
|
||||
"steps": [
|
||||
_step("expire RX session", "F9C5==0 clears F9B5/F9B0 and clears FAA5.7", [0x3FEF, 0x3FF5, 0x3FF9, 0x3FFD]),
|
||||
_step("call reset side path", "if clearing FAA5.7 changed the bit, loc_3FEF calls loc_400C", [0x4001, 0x4003]),
|
||||
_step("clear connection state", "loc_400C clears F730/F756-F759/F732/F75C/FB03/F791/F795/F76E and calls follow-on display/session refresh routines", [0x400C, 0x4010, 0x4020, 0x4028, 0x4034, 0x403C, 0x4040, 0x4042]),
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
def _feedback_loops(by_address: Mapping[int, JsonObject]) -> list[JsonObject]:
|
||||
return [
|
||||
{
|
||||
"name": "command-0 write echo and selector-processing loop",
|
||||
"present": _has_all(by_address, (0xBC69, 0xBC86, 0xBCB0, 0xBCCD, 0xBE70, 0x2806, 0x28A3)),
|
||||
"trigger": "valid command 0 while FAA2 == 0",
|
||||
"path": "RX validation -> BC69 table write -> BE70 appends selector to F970 -> BCB0/BA26 sends immediate 0x04 echo -> later loc_2806 consumes F970",
|
||||
"tx_outcome": "immediate command-4-style echo frame plus possible later selector-driven reports/display work",
|
||||
"timing_gate": "BA26 sets F9C0=0x64 and F9C4=0x07, temporarily delaying queued TX and heartbeat enqueue",
|
||||
"bench_read": "a command-0 probe is stateful: it both writes table state and spends time in the post-TX delay, so it can disturb the continuation window being tested",
|
||||
"evidence_addresses_hex": _hexes([0xBC69, 0xBC75, 0xBC86, 0xBCB0, 0xBCCD, 0xBA2C, 0xBA31, 0xBE70, 0x2819, 0x28A3], by_address),
|
||||
},
|
||||
{
|
||||
"name": "command-1 readback and previous-frame loop",
|
||||
"present": _has_all(by_address, (0xBCD7, 0xBCEC, 0xBCFA, 0xBA36, 0xBE05, 0xBE22)),
|
||||
"trigger": "valid command 1 while FAA2 == 0, followed by optional command 7",
|
||||
"path": "RX validation -> BCD7 reads E000[selector] -> BA26 finalizes TX in F858-F85D -> BE05 can copy F858-F85C back to F850-F854",
|
||||
"tx_outcome": "direct 0x04 readback, then command 7 can retransmit the exact last finalized TX frame",
|
||||
"timing_gate": "command 1 clears FAA2.7 and never enters continuation handling; it is a readback path, not an ACK path",
|
||||
"bench_read": "command 7 after command 1 proves what was last finalized, but does not prove a hidden continuation token by itself",
|
||||
"evidence_addresses_hex": _hexes([0xBCD7, 0xBCEC, 0xBCFA, 0xBA36, 0xBA64, 0xBE05, 0xBE09, 0xBE22], by_address),
|
||||
},
|
||||
{
|
||||
"name": "retry/error 07 echo loop",
|
||||
"present": _has_all(by_address, (0xBE29, 0xBE2D, 0xBE4D, 0xBE6A, 0xBA26)),
|
||||
"trigger": "physical RX error or checksum mismatch while FAA5.7 is set and retry count FAA6 is below two",
|
||||
"path": "RX validation error -> BE29 retry gate -> BE4D stages F850=0x07 and copies RX[1:4] into F851-F854 -> BA26 sends",
|
||||
"tx_outcome": "a 0x07 frame that can echo the host payload bytes, independent of E000/E800 table contents",
|
||||
"timing_gate": "after two retries, the ROM loads F9C0=0x1F and clears FAA3/FAA2 instead of sending another echo",
|
||||
"bench_read": "visible 07 frames in a noisy/tight timing run can be retry echoes, not necessarily device status or a continuation token",
|
||||
"evidence_addresses_hex": _hexes([0xBE29, 0xBE2D, 0xBE33, 0xBE37, 0xBE3E, 0xBE43, 0xBE47, 0xBE4D, 0xBE6A], by_address),
|
||||
},
|
||||
{
|
||||
"name": "autonomous report to host continuation loop",
|
||||
"present": _has_all(by_address, (0x3E54, 0x3E76, 0x3FD3, 0xBAF2, 0xBB00, 0xBB43, 0xBD67)),
|
||||
"trigger": "firmware enqueues a serial-visible report via loc_3E54 with R2.7 set",
|
||||
"path": "loc_3E54 appends report selector to F870 -> loc_3FD3 allows BAF2 when FAA2/F9C0 gates are clear -> BAF2 sends report -> command 4/5/6 continuation can advance F9B5",
|
||||
"tx_outcome": "autonomous 6-byte report frame built from E800[selector], with FAA2.3/FAA3.7 left set to await host continuation or ACK",
|
||||
"timing_gate": "F9C0 must count down before BAF2 can send, and F9C6/F9C8/BE9E control repeated sends while FAA3.7 remains live",
|
||||
"bench_read": "the actual ACK/write target is not just the selector; it is the report that is live under FAA2.3 before TXI/RXI or BE9E clears it",
|
||||
"evidence_addresses_hex": _hexes([0x3E54, 0x3E76, 0x3E7A, 0x3FD3, 0x3FE5, 0x3FEB, 0xBAF2, 0xBB00, 0xBB35, 0xBB43, 0xBB46, 0xBB51, 0xBD67, 0xBDC2, 0xBDED], by_address),
|
||||
},
|
||||
{
|
||||
"name": "selector-processing to report loop",
|
||||
"present": _has_all(by_address, (0xBE70, 0x2806, 0x2CAD, 0x48FA, 0x4926, 0x3E54)),
|
||||
"trigger": "command 0/4/selected command 5 calls BE70, or loc_3E54 is called with R2.6 set",
|
||||
"path": "BE70/F970 selector queue -> loc_2806 selector dispatch -> active-selector side path loc_48FA -> loc_3E54 can enqueue report 0x00F6 when E1EC.13 is set",
|
||||
"tx_outcome": "possible later serial report produced through the F870/BAF2 loop rather than an immediate response to the original RX frame",
|
||||
"timing_gate": "loc_48FA is gated by FB03.7, F732 values, E1EC.13, and F76E.6 before reaching its indirect table",
|
||||
"bench_read": "selector-zero CONNECT work and visible serial reports can be separated in time; lack of immediate TX does not mean the selector queue did nothing",
|
||||
"evidence_addresses_hex": _hexes([0xBE70, 0xBE91, 0x2806, 0x2819, 0x2CAD, 0x48FA, 0x490F, 0x4921, 0x4923, 0x4926], by_address),
|
||||
},
|
||||
{
|
||||
"name": "TXI/RXI overlap cancellation loop",
|
||||
"present": _has_all(by_address, (0xBA84, 0xBA8A, 0xBA90, 0xBA96, 0xBA9A, 0xBAA2)),
|
||||
"trigger": "host RX bytes begin while a report TX is active, FAA2.3 is set, and FAA5.7 is set",
|
||||
"path": "TXI observes FAA2.3 + FAA5.7 + F9C3 != 0 -> clears FAA2.3/FAA3 -> disables TIE -> loads F9C0=0x1F",
|
||||
"tx_outcome": "the pending report/continuation state can be canceled before the host command reaches the continuation dispatcher",
|
||||
"timing_gate": "this depends on byte timing relative to TXI and F9C3, so polite emulator injection can miss it",
|
||||
"bench_read": "this is a concrete ROM reason why bench timing might see latches/retries while a too-polite emulator reaches cleaner continuation paths",
|
||||
"evidence_addresses_hex": _hexes([0xBA84, 0xBA8A, 0xBA90, 0xBA96, 0xBA9A, 0xBA9E, 0xBAA2], by_address),
|
||||
},
|
||||
{
|
||||
"name": "session-expiry to heartbeat/not-active loop",
|
||||
"present": _has_all(by_address, (0xBB9E, 0xBF31, 0xBF37, 0x3FEF, 0x400C, 0x4046, 0x4067)),
|
||||
"trigger": "any complete six-byte RX frame loads F9C5, then FRT2 decrements it to zero",
|
||||
"path": "RX complete -> F9C5=0x14 -> FRT2 decrements F9C5 -> loc_3FEF clears session/queues and calls 400C -> loc_4046 can later enqueue heartbeat selector 0",
|
||||
"tx_outcome": "eventual return to idle heartbeat/report behavior and inactive-session display state",
|
||||
"timing_gate": "F9C4 gates heartbeat enqueue; BA26 reloads it to 0x07 after each send, matching the roughly 700 ms heartbeat cadence",
|
||||
"bench_read": "the common CONNECT NOT ACT after arbitrary six-byte traffic is consistent with this expiry/reset loop",
|
||||
"evidence_addresses_hex": _hexes([0xBB9E, 0xBF31, 0xBF37, 0x3FEF, 0x3FFD, 0x4003, 0x400C, 0x4046, 0x4067, 0xBA31], by_address),
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
def _command_branches(by_address: Mapping[int, JsonObject]) -> list[JsonObject]:
|
||||
return [
|
||||
{
|
||||
"command": 0x00,
|
||||
"command_hex": "0x00",
|
||||
"name": "set_value_acked_candidate",
|
||||
"handler_address_hex": h16(0xBC69),
|
||||
"availability": "initial path only: checksum valid, FAA2 == 0, RX[1].7 == 0",
|
||||
"summary": "writes RX[3:4] into primary/current tables, flags the selector, calls BE70, and sends an echo-style 0x04 response",
|
||||
"response_candidate": "F850=0x04; F851-F854 mostly echo RX[1:4]; BA26 sends it",
|
||||
"side_effects": [
|
||||
"selector zero is special: the low byte is forced to 0x80 after the high byte is taken from RX[3]",
|
||||
"nonzero selectors can mirror into an auxiliary table via a mapping table",
|
||||
"clears FAA2.7 before exit",
|
||||
],
|
||||
"evidence_addresses_hex": _hexes([0xBC69, 0xBC75, 0xBC79, 0xBC82, 0xBC86, 0xBCB0, 0xBCCD, 0xBCD0], by_address),
|
||||
},
|
||||
{
|
||||
"command": 0x01,
|
||||
"command_hex": "0x01",
|
||||
"name": "read_value_candidate",
|
||||
"handler_address_hex": h16(0xBCD7),
|
||||
"availability": "initial path only: checksum valid, FAA2 == 0, RX[1].7 == 0",
|
||||
"summary": "reads primary table E000 + 2*selector and stages a 0x04 response",
|
||||
"response_candidate": "F850=0x04; F851 is overwritten with RX[2]; F853/F854 receive table high/low; F852 is not freshly written here",
|
||||
"side_effects": [
|
||||
"does not enter continuation command handling",
|
||||
"clears FAA2.7 before exit",
|
||||
],
|
||||
"evidence_addresses_hex": _hexes([0xBCD7, 0xBCE0, 0xBCE8, 0xBCEC, 0xBCF0, 0xBCF6, 0xBCFA, 0xBCFD], by_address),
|
||||
},
|
||||
{
|
||||
"command": 0x02,
|
||||
"command_hex": "0x02",
|
||||
"name": "initial_clear_or_noop_candidate",
|
||||
"handler_address_hex": h16(0xBD04),
|
||||
"availability": "initial path only: checksum valid, FAA2 == 0, RX[1].7 == 0",
|
||||
"summary": "clears FAA2.7 and returns without staging a response",
|
||||
"response_candidate": None,
|
||||
"side_effects": ["likely a quiet/session-clear style command on the initial path"],
|
||||
"evidence_addresses_hex": _hexes([0xBD04, 0xBD08], by_address),
|
||||
},
|
||||
{
|
||||
"command": 0x04,
|
||||
"command_hex": "0x04",
|
||||
"name": "continuation_set_value_candidate",
|
||||
"handler_address_hex": h16(0xBD0E),
|
||||
"availability": "continuation path only: checksum valid, FAA2 != 0, command bit2 set, RX[1].7 == 0",
|
||||
"summary": "writes a value into the primary table without an immediate serial response; selector zero also updates the current/report table",
|
||||
"response_candidate": None,
|
||||
"side_effects": [
|
||||
"selector zero is special: RX[3] becomes the high byte and low byte is forced to 0x80",
|
||||
"nonzero selectors write E000 and flag EC00.7; the matching E800 current/report write is not present in this handler",
|
||||
"nonzero selectors can mirror/persist through F400/BFE0 when mapping and F76E.7 allow it",
|
||||
"if FAA2.3 was set from a queued report, advances F9B5 to consume that report",
|
||||
"clears FAA3 and FAA2 before exit",
|
||||
],
|
||||
"evidence_addresses_hex": _hexes([0xBD0E, 0xBD1A, 0xBD1E, 0xBD22, 0xBD26, 0xBD35, 0xBD64, 0xBD67, 0xBD6D, 0xBD75, 0xBD79], by_address),
|
||||
},
|
||||
{
|
||||
"command": 0x05,
|
||||
"command_hex": "0x05",
|
||||
"name": "continuation_ack_or_clear_pending_candidate",
|
||||
"handler_address_hex": h16(0xBD80),
|
||||
"availability": "continuation path only: checksum valid, FAA2 != 0, command bit2 set, RX[1].7 == 0",
|
||||
"summary": "ACK/session-clear path; usually no response, but selected logical indexes feed BE70 or clear connection latches",
|
||||
"response_candidate": None,
|
||||
"side_effects": [
|
||||
"selectors 0x006C, 0x006D, and 0x006E call BE70",
|
||||
"with F731.7 set, selectors 0x006B, 0x0096, 0x0097, 0x00C6, and 0x00F8 clear F731.7/F790.7",
|
||||
"if FAA2.3 was set from a queued report, advances F9B5",
|
||||
"clears FAA3 and FAA2 before exit",
|
||||
],
|
||||
"evidence_addresses_hex": _hexes([0xBD80, 0xBD85, 0xBD94, 0xBD9A, 0xBDB5, 0xBDBF, 0xBDC2, 0xBDC8, 0xBDD0, 0xBDD4], by_address),
|
||||
},
|
||||
{
|
||||
"command": 0x06,
|
||||
"command_hex": "0x06",
|
||||
"name": "continuation_set_secondary_candidate",
|
||||
"handler_address_hex": h16(0xBDDB),
|
||||
"availability": "continuation path only: checksum valid, FAA2 != 0, command bit2 set, RX[1].7 == 0",
|
||||
"summary": "writes RX[3:4] into the secondary table and sets flag-table bit 6",
|
||||
"response_candidate": None,
|
||||
"side_effects": [
|
||||
"if FAA2.3 was set from a queued report, advances F9B5",
|
||||
"clears FAA3 and FAA2 before exit",
|
||||
],
|
||||
"evidence_addresses_hex": _hexes([0xBDDB, 0xBDE5, 0xBDE9, 0xBDED, 0xBDF3, 0xBDFB, 0xBDFF], by_address),
|
||||
},
|
||||
{
|
||||
"command": 0x07,
|
||||
"command_hex": "0x07",
|
||||
"name": "retransmit_previous_tx_candidate",
|
||||
"handler_address_hex": h16(0xBE05),
|
||||
"availability": "initial or continuation path",
|
||||
"summary": "copies the previous finalized TX frame bytes back into staging and sends them again",
|
||||
"response_candidate": "previous TX frame retransmitted through BA26",
|
||||
"side_effects": ["loads F9C0 with 0x1F before sending"],
|
||||
"evidence_addresses_hex": _hexes([0xBE05, 0xBE09, 0xBE0D, 0xBE11, 0xBE15, 0xBE19, 0xBE1D, 0xBE22], by_address),
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
def _state_bits() -> list[JsonObject]:
|
||||
return [
|
||||
{"address": 0xFAA2, "address_hex": h16(0xFAA2), "bit": 7, "name": "rx_command_in_progress_candidate", "meaning": "set on initial-path parse; cleared by command 0/1/2 exits or by continuation cleanup"},
|
||||
{"address": 0xFAA2, "address_hex": h16(0xFAA2), "bit": 3, "name": "queued_report_ack_needed_candidate", "meaning": "set by the autonomous queue send path at BB00; continuation command 4/5/6 can advance F9B5 only when this bit was set"},
|
||||
{"address": 0xFAA3, "address_hex": h16(0xFAA3), "bit": 7, "name": "pending_resend_mask_candidate", "meaning": "set after queued report send; BE9E masks it with FAA5.7 before resend/clear decisions"},
|
||||
{"address": 0xFAA4, "address_hex": h16(0xFAA4), "bit": 7, "name": "rx_physical_error_latch_candidate", "meaning": "set by SCI1 ERI and tested before checksum dispatch"},
|
||||
{"address": 0xFAA5, "address_hex": h16(0xFAA5), "bit": 7, "name": "rx_session_gate_candidate", "meaning": "set while F9C5 is alive after a complete RX frame; gates retry/resend and heartbeat/report enqueue behavior"},
|
||||
]
|
||||
|
||||
|
||||
def _bench_implications() -> list[str]:
|
||||
return [
|
||||
"A standalone command 4 frame from idle should not hit BD0E; command 4 is continuation-only and initial dispatch does not accept it.",
|
||||
"Command 5 is not a generic always-live ACK. It only performs ACK/session-clear work on the continuation path while FAA2 != 0.",
|
||||
"A valid six-byte RX frame loads F9C5 with 0x14, which temporarily sets FAA5.7; when that window expires, loc_3FEF can clear queue/session state and call loc_400C.",
|
||||
"The observed 07 retry/error family can be produced by BE4D from RX[1:4] after error/checksum retry conditions; it is not automatically proof of a table value or a command-4 continuation token.",
|
||||
"To prove CONNECT OK through serial, the bench has to create or preserve FAA2 != 0 at the moment command 4 arrives, and ideally FAA2.3 if it expects queued-report advancement.",
|
||||
]
|
||||
|
||||
|
||||
def _branch(condition: str, outcome: str, evidence: list[int]) -> JsonObject:
|
||||
return {
|
||||
"condition": condition,
|
||||
"outcome": outcome,
|
||||
"evidence_addresses_hex": [h16(address) for address in evidence],
|
||||
}
|
||||
|
||||
|
||||
def _step(name: str, effect: str, evidence: list[int]) -> JsonObject:
|
||||
return {
|
||||
"name": name,
|
||||
"effect": effect,
|
||||
"evidence_addresses_hex": [h16(address) for address in evidence],
|
||||
}
|
||||
|
||||
|
||||
def _range_payload(start: int, end: int) -> JsonObject:
|
||||
return {
|
||||
"start": start,
|
||||
"end": end,
|
||||
"start_hex": h16(start),
|
||||
"end_hex": h16(end),
|
||||
"range_hex": f"{h16(start)}-{h16(end)}",
|
||||
}
|
||||
|
||||
|
||||
def _instruction_sequence(value: Any) -> list[JsonObject]:
|
||||
if isinstance(value, list):
|
||||
return [dict(item) for item in value if isinstance(item, Mapping) and "address" in item]
|
||||
return []
|
||||
|
||||
|
||||
def _has_all(by_address: Mapping[int, JsonObject], addresses: tuple[int, ...]) -> bool:
|
||||
return all(address in by_address for address in addresses)
|
||||
|
||||
|
||||
def _hexes(addresses: list[int], by_address: Mapping[int, JsonObject]) -> list[str]:
|
||||
return [h16(address) for address in addresses if address in by_address]
|
||||
|
||||
|
||||
def _join_hex(values: Any) -> str:
|
||||
if not isinstance(values, list):
|
||||
return ""
|
||||
return ", ".join(str(value) for value in values)
|
||||
|
||||
|
||||
def _confidence(stages: list[JsonObject], commands: list[JsonObject]) -> str:
|
||||
present = sum(1 for stage in stages if stage.get("present"))
|
||||
command_evidence = sum(1 for command in commands if command.get("evidence_addresses_hex"))
|
||||
if present >= 5 and command_evidence >= 6:
|
||||
return "high"
|
||||
if present >= 3 and command_evidence >= 4:
|
||||
return "medium"
|
||||
return "low"
|
||||
|
||||
|
||||
__all__ = [
|
||||
"analyze_rx_branch_trace",
|
||||
"format_text_report",
|
||||
"load_rx_branch_input",
|
||||
"main",
|
||||
"write_rx_branch_trace",
|
||||
]
|
||||
732
h8536/state_map_runner.py
Normal file
732
h8536/state_map_runner.py
Normal file
@@ -0,0 +1,732 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import re
|
||||
import sys
|
||||
import time
|
||||
from dataclasses import dataclass, field
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from typing import Any, Iterable, TextIO
|
||||
|
||||
from .bench_connect_lcd import (
|
||||
BenchLogger,
|
||||
FrameDetector,
|
||||
_import_serial,
|
||||
_relay_command,
|
||||
_relay_settle,
|
||||
_wait_for_ready,
|
||||
format_frame,
|
||||
frame_checksum,
|
||||
frame_checksum_ok,
|
||||
label_frame,
|
||||
parse_frame,
|
||||
)
|
||||
|
||||
|
||||
READBACK_E000_FRAME = bytes.fromhex("01000000005B")
|
||||
COMMAND7_REPEAT_FRAME = bytes.fromhex("07000000005D")
|
||||
HEARTBEAT_FRAME = bytes.fromhex("0000000080DA")
|
||||
|
||||
CONNECT_FORCE_PRESETS: dict[str, tuple[bytes, int, str]] = {
|
||||
"clear": (bytes.fromhex("04000000005E"), 0x0080, "selector-zero no-bit clear/inactive primer"),
|
||||
"dxc": (bytes.fromhex("04000040001E"), 0x4080, "selector-zero CONNECT:DXC-637 candidate"),
|
||||
"ok": (bytes.fromhex("0400008000DE"), 0x8080, "selector-zero CONNECT: OK candidate"),
|
||||
"both": (bytes.fromhex("040000C0009E"), 0xC080, "selector-zero bit14+bit15 priority test"),
|
||||
}
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class StateMapEvent:
|
||||
direction: str
|
||||
frame: bytes
|
||||
timestamp_ms: int | None = None
|
||||
label: str = ""
|
||||
source: str = ""
|
||||
|
||||
@property
|
||||
def frame_text(self) -> str:
|
||||
return format_frame(self.frame)
|
||||
|
||||
|
||||
@dataclass
|
||||
class StateMapRunContext:
|
||||
args: argparse.Namespace
|
||||
logger: BenchLogger
|
||||
detector: FrameDetector
|
||||
device: Any
|
||||
relay: Any | None = None
|
||||
events: list[StateMapEvent] = field(default_factory=list)
|
||||
|
||||
|
||||
def default_log_path() -> Path:
|
||||
return Path("captures") / f"state-map-runner-{datetime.now().strftime('%Y%m%d-%H%M%S')}.txt"
|
||||
|
||||
|
||||
def build_arg_parser() -> argparse.ArgumentParser:
|
||||
parser = argparse.ArgumentParser(
|
||||
description=(
|
||||
"Run or analyze the PT2 state-map proof sequence: visible 07 drain candidate, "
|
||||
"selector-zero force, E000[0] readback, and command-7 hidden-response probe."
|
||||
)
|
||||
)
|
||||
parser.add_argument("--analyze-log", type=Path, help="analyze an existing bench log instead of opening serial ports")
|
||||
parser.add_argument("--json-out", type=Path, help="write machine-readable state-map analysis")
|
||||
parser.add_argument("--preset", choices=sorted(CONNECT_FORCE_PRESETS), default="ok", help="selector-zero force preset")
|
||||
parser.add_argument("--force-frame", type=parse_frame, help="override preset with a custom selector-zero command-4 frame")
|
||||
parser.add_argument("--expected-word", type=_int_arg, help="expected E000[0] readback word; default follows --preset")
|
||||
parser.add_argument("--port", default="COM5", help="RS232 serial port connected to the RCP")
|
||||
parser.add_argument("--baud", type=int, default=38400, help="RCP serial baud rate")
|
||||
parser.add_argument("--relay-port", default="COM6", help="Pico relay serial port")
|
||||
parser.add_argument("--relay-baud", type=int, default=115200, help="Pico relay serial baud rate")
|
||||
parser.add_argument("--no-power-cycle", action="store_true", help="do not send relay off/on before the test")
|
||||
parser.add_argument("--power-off-command", default="off", help="relay command used to remove DUT power")
|
||||
parser.add_argument("--power-on-command", default="on", help="relay command used to apply DUT power")
|
||||
parser.add_argument("--off-seconds", type=float, default=1.5, help="seconds to hold the DUT powered off")
|
||||
parser.add_argument("--relay-settle", type=float, default=2.0, help="seconds to wait after opening the relay port")
|
||||
parser.add_argument("--ready-timeout", type=float, default=10.0, help="seconds to wait for heartbeat before sending")
|
||||
parser.add_argument("--ready-heartbeats", type=int, default=2, help="heartbeat frames to observe before sending")
|
||||
parser.add_argument("--require-ready", action="store_true", help="abort if ready heartbeat count is not observed")
|
||||
parser.add_argument("--sync", choices=("checksum", "fixed"), default="checksum", help="RX frame sync strategy")
|
||||
parser.add_argument("--pre-drain", type=float, default=0.250, help="seconds to drain/log RX before priming")
|
||||
parser.add_argument("--prime-frame", action="append", type=parse_frame, help="optional trigger/tickle frame; repeatable")
|
||||
parser.add_argument("--prime-repeat", type=int, default=0, help="times to send each prime frame while hunting a trigger")
|
||||
parser.add_argument("--prime-gap", type=float, default=0.120, help="seconds to listen after each prime frame")
|
||||
parser.add_argument("--trigger-timeout", type=float, default=3.0, help="seconds to wait for a visible 07 drain candidate")
|
||||
parser.add_argument("--trigger-prefix", default="07", help="hex prefix for the trigger frame; default any device 07...")
|
||||
parser.add_argument(
|
||||
"--trigger-poll-interval",
|
||||
type=float,
|
||||
default=0.002,
|
||||
help="seconds between non-blocking serial polls while hunting a trigger",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--read-poll-interval",
|
||||
type=float,
|
||||
default=0.002,
|
||||
help="seconds between non-blocking serial polls during timed listen/guard windows",
|
||||
)
|
||||
parser.add_argument("--force-guard", type=float, default=0.005, help="seconds after the detected trigger before force TX")
|
||||
parser.add_argument("--post-force-listen", type=float, default=0.050, help="seconds to listen before readback")
|
||||
parser.add_argument("--readback-frame", type=parse_frame, default=READBACK_E000_FRAME, help="E000[0] readback frame")
|
||||
parser.add_argument("--readback-window", type=float, default=0.300, help="seconds to listen after readback")
|
||||
parser.add_argument("--no-command7-probe", action="store_true", help="skip command-7 previous-frame probe")
|
||||
parser.add_argument("--command7-window", type=float, default=0.300, help="seconds to listen after command-7 probe")
|
||||
parser.add_argument("--final-read", type=float, default=2.0, help="seconds to listen after the proof sequence")
|
||||
parser.add_argument("--prompt-screen", action="store_true", help="prompt for observed LCD text after the sequence")
|
||||
parser.add_argument("--log", type=Path, help="capture log path")
|
||||
parser.add_argument("--dry-run", action="store_true", help="print the planned state-map sequence without opening ports")
|
||||
return parser
|
||||
|
||||
|
||||
def main(argv: list[str] | None = None, *, stdout: TextIO = sys.stdout) -> int:
|
||||
args = build_arg_parser().parse_args(argv)
|
||||
force_frame, expected_word, preset_note = resolve_force(args)
|
||||
|
||||
if args.analyze_log:
|
||||
text = args.analyze_log.read_text(encoding="utf-8")
|
||||
events = parse_bench_log(text)
|
||||
analysis = analyze_events(events, expected_word=expected_word)
|
||||
print(format_analysis_report(analysis), file=stdout)
|
||||
if args.json_out:
|
||||
_write_json(args.json_out, analysis)
|
||||
return 0
|
||||
|
||||
log_path = args.log or default_log_path()
|
||||
if args.dry_run:
|
||||
_print_dry_run(args, log_path, force_frame, expected_word, preset_note, stdout)
|
||||
return 0
|
||||
|
||||
serial = _import_serial()
|
||||
logger = BenchLogger(log_path, stdout=stdout)
|
||||
detector = FrameDetector(sync_mode=args.sync)
|
||||
try:
|
||||
logger.emit("PT2 state-map proof runner")
|
||||
logger.emit(f"device={args.port} {args.baud} 8N1 relay={args.relay_port} {args.relay_baud} sync={args.sync}")
|
||||
logger.emit(f"log={log_path}")
|
||||
_emit_plan(logger, args, force_frame, expected_word, preset_note)
|
||||
|
||||
with serial.Serial(args.port, args.baud, bytesize=8, parity="N", stopbits=1, timeout=0.05) as device:
|
||||
ctx = StateMapRunContext(args=args, logger=logger, detector=detector, device=device)
|
||||
try:
|
||||
_prepare_device(ctx)
|
||||
trigger = _hunt_trigger(ctx, _parse_prefix(args.trigger_prefix))
|
||||
if trigger is None:
|
||||
logger.event("STATE no visible-drain token candidate; force/readback skipped")
|
||||
_read_for_collect(ctx, args.final_read)
|
||||
return _finish(ctx, logger, expected_word, args.json_out)
|
||||
|
||||
logger.event(f"STATE visible-drain token candidate {trigger.frame_text}")
|
||||
if args.force_guard > 0:
|
||||
logger.event(f"STATE force guard {args.force_guard:.3f}s")
|
||||
_read_for_collect(ctx, args.force_guard)
|
||||
_send_and_record(ctx, force_frame, "selector_zero_force")
|
||||
if args.post_force_listen > 0:
|
||||
_read_for_collect(ctx, args.post_force_listen)
|
||||
_send_and_record(ctx, args.readback_frame, "e000_readback")
|
||||
_read_for_collect(ctx, args.readback_window)
|
||||
if not args.no_command7_probe:
|
||||
_send_and_record(ctx, COMMAND7_REPEAT_FRAME, "command7_previous_frame_probe")
|
||||
_read_for_collect(ctx, args.command7_window)
|
||||
_read_for_collect(ctx, args.final_read)
|
||||
if args.prompt_screen:
|
||||
_prompt_screen("LCD after state-map proof sequence", logger)
|
||||
finally:
|
||||
if ctx.relay is not None:
|
||||
ctx.relay.close()
|
||||
return _finish(ctx, logger, expected_word, args.json_out)
|
||||
finally:
|
||||
logger.close()
|
||||
|
||||
|
||||
def resolve_force(args: argparse.Namespace) -> tuple[bytes, int, str]:
|
||||
preset_frame, preset_word, preset_note = CONNECT_FORCE_PRESETS[args.preset]
|
||||
frame = args.force_frame or preset_frame
|
||||
expected_word = args.expected_word if args.expected_word is not None else _expected_word_for_force(frame, preset_word)
|
||||
return frame, expected_word, preset_note
|
||||
|
||||
|
||||
def analyze_events(events: Iterable[StateMapEvent], *, expected_word: int | None = None) -> dict[str, Any]:
|
||||
event_list = list(events)
|
||||
trigger_indexes = [index for index, event in enumerate(event_list) if is_visible_drain_candidate(event)]
|
||||
first_trigger = trigger_indexes[0] if trigger_indexes else None
|
||||
force_indexes = [index for index, event in enumerate(event_list) if is_selector_zero_force(event)]
|
||||
first_force_after_trigger = _first_after(force_indexes, first_trigger)
|
||||
readback_tx_indexes = [index for index, event in enumerate(event_list) if is_tx_frame(event, READBACK_E000_FRAME)]
|
||||
command7_tx_indexes = [index for index, event in enumerate(event_list) if is_tx_frame(event, COMMAND7_REPEAT_FRAME)]
|
||||
readback_rx_indexes = [index for index, event in enumerate(event_list) if is_selector_zero_readback(event)]
|
||||
|
||||
direct_readbacks = [_readback_info(event_list[index], expected_word) for index in readback_rx_indexes]
|
||||
first_direct_after_force = _first_after(readback_rx_indexes, first_force_after_trigger)
|
||||
command7_after_force = _first_after(command7_tx_indexes, first_force_after_trigger)
|
||||
command7_replay = _first_after(readback_rx_indexes, command7_after_force) if command7_after_force is not None else None
|
||||
warnings = _state_warnings(event_list, first_trigger, first_force_after_trigger)
|
||||
outcome = _outcome(
|
||||
first_trigger=first_trigger,
|
||||
first_force=first_force_after_trigger,
|
||||
first_direct=first_direct_after_force,
|
||||
command7_replay=command7_replay,
|
||||
events=event_list,
|
||||
expected_word=expected_word,
|
||||
)
|
||||
facts = {
|
||||
"kind": "pt2_state_map_analysis",
|
||||
"event_count": len(event_list),
|
||||
"expected_word": _word_payload(expected_word),
|
||||
"outcome": outcome,
|
||||
"warnings": warnings,
|
||||
"trigger_candidates": [_event_payload(event_list[index]) for index in trigger_indexes],
|
||||
"selector_zero_forces": [_event_payload(event_list[index]) for index in force_indexes],
|
||||
"readback_tx": [_event_payload(event_list[index]) for index in readback_tx_indexes],
|
||||
"command7_tx": [_event_payload(event_list[index]) for index in command7_tx_indexes],
|
||||
"direct_readbacks": direct_readbacks,
|
||||
"first_trigger_index": first_trigger,
|
||||
"first_force_after_trigger_index": first_force_after_trigger,
|
||||
"first_direct_readback_after_force_index": first_direct_after_force,
|
||||
"command7_replay_readback_index": command7_replay,
|
||||
"post_force_rx_labels": _post_force_rx_labels(event_list, first_force_after_trigger),
|
||||
"state_map_notes": [
|
||||
"A device 07... frame is treated as a visible F870 drain/token candidate, not proof by itself.",
|
||||
"The proof target is a retained selector-zero readback: RX 04 00 QQ HH LL after the force/readback turn.",
|
||||
"Command 0/1 or overlapping RX before the force can spend or clear the FAA2/FAA3 opportunity.",
|
||||
],
|
||||
}
|
||||
return facts
|
||||
|
||||
|
||||
def format_analysis_report(analysis: dict[str, Any]) -> str:
|
||||
lines = [
|
||||
"PT2 state-map analysis",
|
||||
f"outcome={analysis['outcome']['name']} confidence={analysis['outcome']['confidence']}",
|
||||
f"reason={analysis['outcome']['reason']}",
|
||||
f"events={analysis['event_count']} triggers={len(analysis['trigger_candidates'])} "
|
||||
f"forces={len(analysis['selector_zero_forces'])} readbacks={len(analysis['direct_readbacks'])}",
|
||||
]
|
||||
expected = analysis.get("expected_word") or {}
|
||||
if expected:
|
||||
lines.append(f"expected_e0000={expected['hex']}")
|
||||
if analysis["trigger_candidates"]:
|
||||
first = analysis["trigger_candidates"][0]
|
||||
lines.append(f"first_trigger={first['frame']} label={first['label'] or '(unlabeled)'}")
|
||||
if analysis["selector_zero_forces"]:
|
||||
first = analysis["selector_zero_forces"][0]
|
||||
lines.append(f"first_force={first['frame']}")
|
||||
for readback in analysis["direct_readbacks"]:
|
||||
match = " expected" if readback.get("matches_expected") else ""
|
||||
lines.append(
|
||||
f"readback frame={readback['frame']} qq=0x{readback['qq']:02X} "
|
||||
f"value={readback['value_hex']}{match}"
|
||||
)
|
||||
for warning in analysis["warnings"]:
|
||||
lines.append(f"warning={warning}")
|
||||
labels = analysis.get("post_force_rx_labels", {})
|
||||
if labels:
|
||||
joined = ", ".join(f"{name}={count}" for name, count in sorted(labels.items()))
|
||||
lines.append(f"post_force_rx={joined}")
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def parse_bench_log(text: str) -> list[StateMapEvent]:
|
||||
lines = text.splitlines()
|
||||
events: list[StateMapEvent] = []
|
||||
rx_detect_seen = False
|
||||
for line in lines:
|
||||
detect = _DETECT_RE.match(line.strip())
|
||||
if detect:
|
||||
rx_detect_seen = True
|
||||
frame = _parse_hex_bytes(detect.group("hex"))
|
||||
if len(frame) == 6:
|
||||
events.append(
|
||||
StateMapEvent(
|
||||
direction="rx",
|
||||
frame=frame,
|
||||
timestamp_ms=_timestamp_to_ms(detect.group("ts")),
|
||||
label=detect.group("label"),
|
||||
source="detect",
|
||||
)
|
||||
)
|
||||
continue
|
||||
chunk = _CHUNK_RE.match(line.strip())
|
||||
if not chunk or chunk.group("direction") != "TX":
|
||||
continue
|
||||
frame = _parse_hex_bytes(chunk.group("hex"))
|
||||
if len(frame) == 6:
|
||||
events.append(
|
||||
StateMapEvent(
|
||||
direction="tx",
|
||||
frame=frame,
|
||||
timestamp_ms=_timestamp_to_ms(chunk.group("ts")),
|
||||
label="",
|
||||
source="tx_chunk",
|
||||
)
|
||||
)
|
||||
|
||||
if rx_detect_seen:
|
||||
return events
|
||||
|
||||
detector = FrameDetector()
|
||||
for line in lines:
|
||||
chunk = _CHUNK_RE.match(line.strip())
|
||||
if not chunk or chunk.group("direction") != "RX":
|
||||
continue
|
||||
data = _parse_hex_bytes(chunk.group("hex"))
|
||||
for frame, label in detector.feed(data):
|
||||
events.append(
|
||||
StateMapEvent(
|
||||
direction="rx",
|
||||
frame=frame,
|
||||
timestamp_ms=_timestamp_to_ms(chunk.group("ts")),
|
||||
label=label,
|
||||
source="rx_chunk_resync",
|
||||
)
|
||||
)
|
||||
return events
|
||||
|
||||
|
||||
def is_visible_drain_candidate(event: StateMapEvent) -> bool:
|
||||
return event.direction == "rx" and len(event.frame) == 6 and frame_checksum_ok(event.frame) and event.frame[0] == 0x07
|
||||
|
||||
|
||||
def is_selector_zero_force(event: StateMapEvent) -> bool:
|
||||
return (
|
||||
event.direction == "tx"
|
||||
and len(event.frame) == 6
|
||||
and frame_checksum_ok(event.frame)
|
||||
and event.frame[0] == 0x04
|
||||
and event.frame[1] == 0x00
|
||||
and event.frame[2] == 0x00
|
||||
)
|
||||
|
||||
|
||||
def is_selector_zero_readback(event: StateMapEvent) -> bool:
|
||||
return (
|
||||
event.direction == "rx"
|
||||
and len(event.frame) == 6
|
||||
and frame_checksum_ok(event.frame)
|
||||
and event.frame[0] == 0x04
|
||||
and event.frame[1] == 0x00
|
||||
)
|
||||
|
||||
|
||||
def is_tx_frame(event: StateMapEvent, frame: bytes) -> bool:
|
||||
return event.direction == "tx" and event.frame == frame
|
||||
|
||||
|
||||
def _prepare_device(ctx: StateMapRunContext) -> None:
|
||||
args = ctx.args
|
||||
if not args.no_power_cycle:
|
||||
serial = _import_serial()
|
||||
ctx.relay = serial.Serial(args.relay_port, args.relay_baud, timeout=0.25)
|
||||
_relay_settle(ctx.relay, args.relay_settle, ctx.logger)
|
||||
_relay_command(ctx.relay, args.power_off_command, ctx.logger)
|
||||
time.sleep(args.off_seconds)
|
||||
ctx.device.reset_input_buffer()
|
||||
ctx.detector = FrameDetector(sync_mode=args.sync)
|
||||
_relay_command(ctx.relay, args.power_on_command, ctx.logger)
|
||||
else:
|
||||
ctx.device.reset_input_buffer()
|
||||
ready = _wait_for_ready(ctx.device, ctx.detector, ctx.logger, args.ready_timeout, args.ready_heartbeats)
|
||||
if args.require_ready and not ready:
|
||||
raise SystemExit(2)
|
||||
if args.pre_drain > 0:
|
||||
ctx.logger.event(f"STATE pre-drain {args.pre_drain:.3f}s")
|
||||
_read_for_collect(ctx, args.pre_drain)
|
||||
|
||||
|
||||
def _hunt_trigger(ctx: StateMapRunContext, prefix: bytes) -> StateMapEvent | None:
|
||||
args = ctx.args
|
||||
primes = list(args.prime_frame or [])
|
||||
for repeat_index in range(max(0, args.prime_repeat)):
|
||||
for prime_index, prime in enumerate(primes, start=1):
|
||||
ctx.logger.event(f"STATE prime {repeat_index + 1}/{args.prime_repeat}.{prime_index}")
|
||||
_send_and_record(ctx, prime, "prime")
|
||||
trigger = _read_until_trigger(ctx, args.prime_gap, prefix)
|
||||
if trigger is not None:
|
||||
return trigger
|
||||
return _read_until_trigger(ctx, args.trigger_timeout, prefix)
|
||||
|
||||
|
||||
def _read_until_trigger(ctx: StateMapRunContext, seconds: float, prefix: bytes) -> StateMapEvent | None:
|
||||
ctx.logger.event(f"STATE wait visible-drain prefix={format_frame(prefix)} timeout={seconds:.3f}s")
|
||||
deadline = time.monotonic() + max(0.0, seconds)
|
||||
poll_interval = max(0.001, float(getattr(ctx.args, "trigger_poll_interval", 0.002)))
|
||||
while time.monotonic() < deadline:
|
||||
events = _read_available_for_collect(ctx)
|
||||
for event in events:
|
||||
if is_visible_drain_candidate(event) and event.frame.startswith(prefix):
|
||||
return event
|
||||
remaining = deadline - time.monotonic()
|
||||
if remaining > 0:
|
||||
time.sleep(min(poll_interval, remaining))
|
||||
return None
|
||||
|
||||
|
||||
def _read_for_collect(ctx: StateMapRunContext, seconds: float) -> list[StateMapEvent]:
|
||||
observed: list[StateMapEvent] = []
|
||||
deadline = time.monotonic() + max(0.0, seconds)
|
||||
poll_interval = max(0.001, float(getattr(ctx.args, "read_poll_interval", 0.002)))
|
||||
while time.monotonic() < deadline:
|
||||
events = _read_available_for_collect(ctx)
|
||||
if events:
|
||||
observed.extend(events)
|
||||
continue
|
||||
remaining = deadline - time.monotonic()
|
||||
if remaining > 0:
|
||||
time.sleep(min(poll_interval, remaining))
|
||||
return observed
|
||||
|
||||
|
||||
def _read_available_for_collect(ctx: StateMapRunContext) -> list[StateMapEvent]:
|
||||
waiting = getattr(ctx.device, "in_waiting", 0)
|
||||
if waiting <= 0:
|
||||
return []
|
||||
return _record_rx_data(ctx, ctx.device.read(waiting))
|
||||
|
||||
|
||||
def _record_rx_data(ctx: StateMapRunContext, data: bytes) -> list[StateMapEvent]:
|
||||
observed: list[StateMapEvent] = []
|
||||
dropped_before = ctx.detector.dropped_bytes
|
||||
ctx.logger.chunk("RX", data)
|
||||
for frame, label in ctx.detector.feed(data):
|
||||
event = StateMapEvent(
|
||||
direction="rx",
|
||||
frame=frame,
|
||||
timestamp_ms=_now_ms(),
|
||||
label=label,
|
||||
source="live",
|
||||
)
|
||||
ctx.events.append(event)
|
||||
observed.append(event)
|
||||
state_label = _state_frame_label(event)
|
||||
ctx.logger.event(f"DETECT {label} {format_frame(frame)}")
|
||||
if state_label:
|
||||
ctx.logger.event(f"STATE_FRAME {state_label} {format_frame(frame)}")
|
||||
dropped_now = ctx.detector.dropped_bytes - dropped_before
|
||||
if dropped_now:
|
||||
ctx.logger.event(
|
||||
f"RESYNC dropped_bytes={dropped_now} total_dropped={ctx.detector.dropped_bytes} "
|
||||
f"buffered={len(ctx.detector.buffer)}"
|
||||
)
|
||||
return observed
|
||||
|
||||
|
||||
def _send_and_record(ctx: StateMapRunContext, frame: bytes, label: str) -> None:
|
||||
ctx.device.write(frame)
|
||||
ctx.device.flush()
|
||||
ctx.logger.chunk("TX", frame)
|
||||
ctx.logger.event(f"SENT {label} checksum_ok={int(frame_checksum_ok(frame))}")
|
||||
ctx.events.append(
|
||||
StateMapEvent(direction="tx", frame=frame, timestamp_ms=_now_ms(), label=label, source="live")
|
||||
)
|
||||
|
||||
|
||||
def _finish(ctx: StateMapRunContext, logger: BenchLogger, expected_word: int | None, json_path: Path | None) -> int:
|
||||
analysis = analyze_events(ctx.events, expected_word=expected_word)
|
||||
logger.emit()
|
||||
logger.emit(format_analysis_report(analysis))
|
||||
logger.emit()
|
||||
logger.emit("Summary")
|
||||
logger.emit(f"rx_frames={len(ctx.detector.frames)} trailing_unframed_bytes={len(ctx.detector.buffer)}")
|
||||
logger.emit(f"resync_events={ctx.detector.resync_events} dropped_bytes={ctx.detector.dropped_bytes}")
|
||||
for label, count in sorted(ctx.detector.labels.items()):
|
||||
logger.emit(f"{label}={count}")
|
||||
if json_path:
|
||||
_write_json(json_path, analysis)
|
||||
return 0
|
||||
|
||||
|
||||
def _outcome(
|
||||
*,
|
||||
first_trigger: int | None,
|
||||
first_force: int | None,
|
||||
first_direct: int | None,
|
||||
command7_replay: int | None,
|
||||
events: list[StateMapEvent],
|
||||
expected_word: int | None,
|
||||
) -> dict[str, str]:
|
||||
if first_trigger is None:
|
||||
return {
|
||||
"name": "no_visible_drain_token",
|
||||
"confidence": "high",
|
||||
"reason": "No device 07... frame was observed, so the alternate RX opportunity was not demonstrated.",
|
||||
}
|
||||
if first_force is None:
|
||||
return {
|
||||
"name": "token_observed_but_not_forced",
|
||||
"confidence": "high",
|
||||
"reason": "A device 07... frame was observed, but no selector-zero command-4 force followed it.",
|
||||
}
|
||||
readback_index = first_direct if first_direct is not None else command7_replay
|
||||
if readback_index is not None:
|
||||
value = _readback_value(events[readback_index].frame)
|
||||
if expected_word is not None and value == expected_word:
|
||||
return {
|
||||
"name": "selector_zero_retained",
|
||||
"confidence": "high",
|
||||
"reason": f"E000[0] readback matched expected 0x{expected_word:04X}.",
|
||||
}
|
||||
return {
|
||||
"name": "selector_zero_readback_unexpected",
|
||||
"confidence": "medium",
|
||||
"reason": f"A selector-zero readback appeared, but value 0x{value:04X} did not match the expected word.",
|
||||
}
|
||||
if _only_heartbeat_after_force(events, first_force):
|
||||
return {
|
||||
"name": "force_not_proven_heartbeat_only",
|
||||
"confidence": "medium",
|
||||
"reason": "After the force/readback turn, only heartbeat frames were observed.",
|
||||
}
|
||||
return {
|
||||
"name": "force_not_proven",
|
||||
"confidence": "medium",
|
||||
"reason": "No direct or command-7-recovered selector-zero readback was observed after the force.",
|
||||
}
|
||||
|
||||
|
||||
def _state_warnings(events: list[StateMapEvent], trigger_index: int | None, force_index: int | None) -> list[str]:
|
||||
warnings: list[str] = []
|
||||
if trigger_index is None or force_index is None:
|
||||
return warnings
|
||||
trigger = events[trigger_index]
|
||||
force = events[force_index]
|
||||
if trigger.timestamp_ms is not None and force.timestamp_ms is not None:
|
||||
guard_ms = force.timestamp_ms - trigger.timestamp_ms
|
||||
if guard_ms < 2:
|
||||
warnings.append(f"force_guard_short_{guard_ms}ms_may_overlap_TXI")
|
||||
between = events[trigger_index + 1 : force_index]
|
||||
for event in between:
|
||||
if event.direction != "tx" or not event.frame:
|
||||
continue
|
||||
command = event.frame[0]
|
||||
if command == 0x00:
|
||||
warnings.append("command0_between_trigger_and_force_can_destroy_token")
|
||||
elif command == 0x01:
|
||||
warnings.append("command1_readback_between_trigger_and_force_can_spend_token")
|
||||
elif command in {0x04, 0x05, 0x06}:
|
||||
warnings.append(f"command{command}_between_trigger_and_force_can_spend_alternate_tail")
|
||||
return sorted(set(warnings))
|
||||
|
||||
|
||||
def _post_force_rx_labels(events: list[StateMapEvent], force_index: int | None) -> dict[str, int]:
|
||||
if force_index is None:
|
||||
return {}
|
||||
counts: dict[str, int] = {}
|
||||
for event in events[force_index + 1 :]:
|
||||
if event.direction != "rx":
|
||||
continue
|
||||
label = _state_frame_label(event) or event.label or label_frame(event.frame) or "rx_unlabeled"
|
||||
counts[label] = counts.get(label, 0) + 1
|
||||
return counts
|
||||
|
||||
|
||||
def _state_frame_label(event: StateMapEvent) -> str:
|
||||
if event.direction == "rx" and is_visible_drain_candidate(event):
|
||||
return "visible_drain_token_candidate"
|
||||
if event.direction == "rx" and is_selector_zero_readback(event):
|
||||
return "selector_zero_readback_proof_candidate"
|
||||
if event.direction == "tx" and is_selector_zero_force(event):
|
||||
return "selector_zero_force"
|
||||
if event.direction == "tx" and event.frame == READBACK_E000_FRAME:
|
||||
return "e000_readback_probe"
|
||||
if event.direction == "tx" and event.frame == COMMAND7_REPEAT_FRAME:
|
||||
return "command7_previous_frame_probe"
|
||||
return ""
|
||||
|
||||
|
||||
def _readback_info(event: StateMapEvent, expected_word: int | None = None) -> dict[str, Any]:
|
||||
value = _readback_value(event.frame)
|
||||
return {
|
||||
"frame": event.frame_text,
|
||||
"timestamp_ms": event.timestamp_ms,
|
||||
"qq": event.frame[2],
|
||||
"value": value,
|
||||
"value_hex": f"0x{value:04X}",
|
||||
"matches_expected": expected_word is not None and value == expected_word,
|
||||
}
|
||||
|
||||
|
||||
def _readback_value(frame: bytes) -> int:
|
||||
return ((frame[3] << 8) | frame[4]) & 0xFFFF
|
||||
|
||||
|
||||
def _first_after(indexes: list[int], anchor: int | None) -> int | None:
|
||||
if anchor is None:
|
||||
return indexes[0] if indexes else None
|
||||
for index in indexes:
|
||||
if index > anchor:
|
||||
return index
|
||||
return None
|
||||
|
||||
|
||||
def _only_heartbeat_after_force(events: list[StateMapEvent], force_index: int) -> bool:
|
||||
rx_after = [event for event in events[force_index + 1 :] if event.direction == "rx"]
|
||||
return bool(rx_after) and all(event.frame == HEARTBEAT_FRAME for event in rx_after)
|
||||
|
||||
|
||||
def _event_payload(event: StateMapEvent) -> dict[str, Any]:
|
||||
return {
|
||||
"direction": event.direction,
|
||||
"frame": event.frame_text,
|
||||
"timestamp_ms": event.timestamp_ms,
|
||||
"label": event.label,
|
||||
"state_label": _state_frame_label(event),
|
||||
"source": event.source,
|
||||
}
|
||||
|
||||
|
||||
def _word_payload(word: int | None) -> dict[str, Any] | None:
|
||||
if word is None:
|
||||
return None
|
||||
return {"value": word & 0xFFFF, "hex": f"0x{word & 0xFFFF:04X}"}
|
||||
|
||||
|
||||
def _emit_plan(logger: BenchLogger, args: argparse.Namespace, force_frame: bytes, expected_word: int, preset_note: str) -> None:
|
||||
logger.emit(f"preset={args.preset} note={preset_note}")
|
||||
logger.emit(f"force={format_frame(force_frame)} checksum_ok={int(frame_checksum_ok(force_frame))}")
|
||||
logger.emit(f"expected_e0000=0x{expected_word:04X}")
|
||||
logger.emit(f"readback={format_frame(args.readback_frame)} checksum_ok={int(frame_checksum_ok(args.readback_frame))}")
|
||||
logger.emit(f"command7_probe={int(not args.no_command7_probe)} frame={format_frame(COMMAND7_REPEAT_FRAME)}")
|
||||
logger.emit("guardrails=no command-0/command-1 is sent between trigger and force by this runner")
|
||||
|
||||
|
||||
def _print_dry_run(
|
||||
args: argparse.Namespace,
|
||||
log_path: Path,
|
||||
force_frame: bytes,
|
||||
expected_word: int,
|
||||
preset_note: str,
|
||||
stdout: TextIO,
|
||||
) -> None:
|
||||
print("PT2 state-map proof runner", file=stdout)
|
||||
print(f"device={args.port} {args.baud} 8N1", file=stdout)
|
||||
print(f"relay={args.relay_port} {args.relay_baud}", file=stdout)
|
||||
print(f"power_cycle={int(not args.no_power_cycle)}", file=stdout)
|
||||
print(f"preset={args.preset} note={preset_note}", file=stdout)
|
||||
print(f"force={format_frame(force_frame)} checksum_ok={int(frame_checksum_ok(force_frame))}", file=stdout)
|
||||
print(f"expected_e0000=0x{expected_word:04X}", file=stdout)
|
||||
print(f"readback={format_frame(args.readback_frame)} checksum_ok={int(frame_checksum_ok(args.readback_frame))}", file=stdout)
|
||||
for prime in args.prime_frame or []:
|
||||
print(f"prime={format_frame(prime)} checksum_ok={int(frame_checksum_ok(prime))}", file=stdout)
|
||||
print(f"prime_repeat={args.prime_repeat} prime_gap={args.prime_gap:.3f}", file=stdout)
|
||||
print(f"trigger_prefix={format_frame(_parse_prefix(args.trigger_prefix))} timeout={args.trigger_timeout:.3f}", file=stdout)
|
||||
print(f"force_guard={args.force_guard:.3f} post_force_listen={args.post_force_listen:.3f}", file=stdout)
|
||||
print(f"command7_probe={int(not args.no_command7_probe)} frame={format_frame(COMMAND7_REPEAT_FRAME)}", file=stdout)
|
||||
print(f"log={log_path}", file=stdout)
|
||||
|
||||
|
||||
def _expected_word_for_force(frame: bytes, default: int) -> int:
|
||||
if len(frame) == 6 and frame_checksum_ok(frame) and frame[0] == 0x04 and frame[1] == 0 and frame[2] == 0:
|
||||
return ((frame[3] << 8) | 0x0080) & 0xFFFF
|
||||
return default
|
||||
|
||||
|
||||
def _parse_prefix(text: str) -> bytes:
|
||||
normalized = text.strip().replace(",", " ").replace(":", " ").replace("-", " ").replace("_", " ")
|
||||
if not normalized:
|
||||
return b""
|
||||
parts = normalized.split()
|
||||
if len(parts) == 1:
|
||||
compact = parts[0]
|
||||
if compact.lower().startswith("0x"):
|
||||
compact = compact[2:]
|
||||
if compact.upper().startswith("H'"):
|
||||
compact = compact[2:]
|
||||
if len(compact) % 2:
|
||||
compact = "0" + compact
|
||||
return bytes(int(compact[index : index + 2], 16) for index in range(0, len(compact), 2))
|
||||
return bytes(int(part, 16) for part in parts)
|
||||
|
||||
|
||||
def _parse_hex_bytes(text: str) -> bytes:
|
||||
normalized = text.strip().replace(",", " ").replace(":", " ").replace("-", " ").replace("_", " ")
|
||||
if not normalized:
|
||||
return b""
|
||||
return bytes(int(part, 16) for part in normalized.split())
|
||||
|
||||
|
||||
def _timestamp_to_ms(text: str) -> int:
|
||||
hour, minute, rest = text.split(":")
|
||||
second, milli = rest.split(".")
|
||||
return ((int(hour) * 60 + int(minute)) * 60 + int(second)) * 1000 + int(milli)
|
||||
|
||||
|
||||
def _now_ms() -> int:
|
||||
now = datetime.now()
|
||||
return ((now.hour * 60 + now.minute) * 60 + now.second) * 1000 + now.microsecond // 1000
|
||||
|
||||
|
||||
def _int_arg(text: str) -> int:
|
||||
return int(text, 0)
|
||||
|
||||
|
||||
def _prompt_screen(label: str, logger: BenchLogger) -> None:
|
||||
note = input(f"{label}: type observed LCD text, or press Enter to skip: ").strip()
|
||||
logger.event(f"SCREEN {label}: {note or '(no note)'}")
|
||||
|
||||
|
||||
def _write_json(path: Path, analysis: dict[str, Any]) -> None:
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
path.write_text(json.dumps(analysis, indent=2, sort_keys=True) + "\n", encoding="utf-8")
|
||||
|
||||
|
||||
_CHUNK_RE = re.compile(
|
||||
r"^(?P<ts>\d\d:\d\d:\d\d\.\d{3})\s+(?P<direction>TX|RX)\s+\d+\s+bytes\s+(?P<hex>[0-9A-Fa-f ]+)$"
|
||||
)
|
||||
_DETECT_RE = re.compile(
|
||||
r"^(?P<ts>\d\d:\d\d:\d\d\.\d{3})\s+DETECT\s+(?P<label>\S+)\s+(?P<hex>[0-9A-Fa-f ]+)$"
|
||||
)
|
||||
|
||||
|
||||
__all__ = [
|
||||
"COMMAND7_REPEAT_FRAME",
|
||||
"CONNECT_FORCE_PRESETS",
|
||||
"READBACK_E000_FRAME",
|
||||
"StateMapEvent",
|
||||
"analyze_events",
|
||||
"build_arg_parser",
|
||||
"format_analysis_report",
|
||||
"main",
|
||||
"parse_bench_log",
|
||||
"resolve_force",
|
||||
]
|
||||
8
h8536_ccu_seed_hints.py
Normal file
8
h8536_ccu_seed_hints.py
Normal file
@@ -0,0 +1,8 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Compatibility wrapper for the H8/536 CCU seed-hint miner."""
|
||||
|
||||
from h8536.ccu_seed_hints import main
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
8
h8536_eeprom_layout.py
Normal file
8
h8536_eeprom_layout.py
Normal file
@@ -0,0 +1,8 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Compatibility wrapper for the H8/536 EEPROM layout miner."""
|
||||
|
||||
from h8536.eeprom_layout import main
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
5
h8536_emulator_state_search.py
Normal file
5
h8536_emulator_state_search.py
Normal file
@@ -0,0 +1,5 @@
|
||||
from h8536.emulator.state_search import main
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
8
h8536_rx_branch_trace.py
Normal file
8
h8536_rx_branch_trace.py
Normal file
@@ -0,0 +1,8 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Compatibility wrapper for the H8/536 SCI1 RX branch trace CLI."""
|
||||
|
||||
from h8536.rx_branch_trace import main
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
16
scripts/state_map_runner.py
Normal file
16
scripts/state_map_runner.py
Normal file
@@ -0,0 +1,16 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Run or analyze the PT2 state-map selector-zero proof sequence."""
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
if str(ROOT) not in sys.path:
|
||||
sys.path.insert(0, str(ROOT))
|
||||
|
||||
from h8536.state_map_runner import main
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
118
tests/test_ccu_seed_hints.py
Normal file
118
tests/test_ccu_seed_hints.py
Normal file
@@ -0,0 +1,118 @@
|
||||
import io
|
||||
import json
|
||||
import tempfile
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
from h8536.ccu_seed_hints import (
|
||||
analyze_ccu_seed_hints,
|
||||
checksum,
|
||||
encode_host_frame,
|
||||
frame_hex,
|
||||
main,
|
||||
selector_bytes,
|
||||
write_ccu_seed_hints,
|
||||
)
|
||||
|
||||
|
||||
def reference(address: int) -> dict:
|
||||
return {"address": address}
|
||||
|
||||
|
||||
def instruction(address: int, mnemonic: str, operands: str = "", refs: list[int] | None = None) -> dict:
|
||||
return {
|
||||
"address": address,
|
||||
"mnemonic": mnemonic,
|
||||
"operands": operands,
|
||||
"text": f"{mnemonic} {operands}".strip(),
|
||||
"references": [reference(item) for item in (refs or [])],
|
||||
"targets": [],
|
||||
}
|
||||
|
||||
|
||||
def payload() -> dict:
|
||||
return {
|
||||
"instructions": [
|
||||
instruction(0xC000, "MOV:G.W", "#H'0006, R3"),
|
||||
instruction(0xC004, "MOV:G.W", "@(-H'2000,R3), R0"),
|
||||
instruction(0xC008, "MOV:G.W", "R1, @H'E1EC", [0xE1EC]),
|
||||
],
|
||||
"call_graph": {
|
||||
"nodes": [
|
||||
{"start": 0xC000, "end": 0xC0FF, "label": "loc_C000"},
|
||||
],
|
||||
},
|
||||
"indirect_flow": {
|
||||
"sites": [
|
||||
{
|
||||
"table": {
|
||||
"base": 0x28A6,
|
||||
"entries": [
|
||||
{"index": 0, "entry_address": 0x28A6, "target": 0x2CB9, "target_label": "loc_2CB9"},
|
||||
{"index": 1, "entry_address": 0x28A8, "target": 0x1234, "target_label": "loc_1234"},
|
||||
{"index": 2, "entry_address": 0x28AA, "target": 0x1234, "target_label": "loc_1234"},
|
||||
],
|
||||
},
|
||||
}
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
class CcuSeedHintsTest(unittest.TestCase):
|
||||
def test_selector_encoding_matches_loc_622b_ranges(self):
|
||||
self.assertEqual(selector_bytes(0x000), (0x00, 0x00))
|
||||
self.assertEqual(selector_bytes(0x07F), (0x00, 0x7F))
|
||||
self.assertEqual(selector_bytes(0x080), (0x01, 0x00))
|
||||
self.assertEqual(selector_bytes(0x17F), (0x01, 0xFF))
|
||||
self.assertEqual(selector_bytes(0x180), (0x02, 0x00))
|
||||
self.assertEqual(selector_bytes(0x1FF), (0x02, 0x7F))
|
||||
|
||||
def test_frame_encoding_uses_xor_seed(self):
|
||||
frame = encode_host_frame(0x00, 0x000, 0x8080)
|
||||
|
||||
self.assertEqual(frame, [0x00, 0x00, 0x00, 0x80, 0x80, 0x5A])
|
||||
self.assertEqual(checksum(frame[:5]), frame[5])
|
||||
self.assertEqual(frame_hex(frame), "00 00 00 80 80 5A")
|
||||
|
||||
def test_analysis_emits_seed_plan_and_selector_reasons(self):
|
||||
analysis = analyze_ccu_seed_hints(payload(), rom_path=None)
|
||||
by_selector = {
|
||||
int(item["selector"]): item
|
||||
for item in analysis["selector_candidates"]
|
||||
}
|
||||
|
||||
self.assertEqual(analysis["kind"], "ccu_seed_hints")
|
||||
self.assertIn(0x000, by_selector)
|
||||
self.assertIn(0x0F6, by_selector)
|
||||
self.assertIn("00 00 00 80 80 5A", [step["frame"] for step in analysis["seed_plan"]["steps"]])
|
||||
self.assertIn("01 01 76 00 00 2C", [step["readback_frame"] for step in analysis["seed_plan"]["steps"]])
|
||||
|
||||
def test_write_json_output(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
input_path = Path(tmp) / "rom.json"
|
||||
output_path = Path(tmp) / "hints.json"
|
||||
input_path.write_text(json.dumps(payload()), encoding="utf-8")
|
||||
|
||||
write_ccu_seed_hints(input_path, output_path, rom_path=None, as_json=True)
|
||||
|
||||
written = json.loads(output_path.read_text(encoding="utf-8"))
|
||||
self.assertEqual(written["kind"], "ccu_seed_hints")
|
||||
self.assertGreaterEqual(written["summary"]["candidate_count"], 1)
|
||||
|
||||
def test_cli_writes_text_report(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
input_path = Path(tmp) / "rom.json"
|
||||
output_path = Path(tmp) / "hints.txt"
|
||||
input_path.write_text(json.dumps(payload()), encoding="utf-8")
|
||||
|
||||
stdout = io.StringIO()
|
||||
rc = main([str(input_path), "--rom", str(Path(tmp) / "missing.bin"), "--out", str(output_path)], stdout=stdout)
|
||||
|
||||
self.assertEqual(rc, 0)
|
||||
self.assertIn("wrote", stdout.getvalue())
|
||||
self.assertIn("Candidate Fake-CCU Seed Plan", output_path.read_text(encoding="utf-8"))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
120
tests/test_eeprom_layout.py
Normal file
120
tests/test_eeprom_layout.py
Normal file
@@ -0,0 +1,120 @@
|
||||
import io
|
||||
import json
|
||||
import tempfile
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
from h8536.eeprom_layout import (
|
||||
analyze_eeprom_layout,
|
||||
format_text_report,
|
||||
main,
|
||||
write_eeprom_layout,
|
||||
)
|
||||
|
||||
|
||||
def reference(address: int) -> dict:
|
||||
return {"address": address}
|
||||
|
||||
|
||||
def instruction(address: int, mnemonic: str, operands: str = "", refs: list[int] | None = None) -> dict:
|
||||
return {
|
||||
"address": address,
|
||||
"mnemonic": mnemonic,
|
||||
"operands": operands,
|
||||
"text": f"{mnemonic} {operands}".strip(),
|
||||
"references": [reference(item) for item in (refs or [])],
|
||||
"targets": [],
|
||||
}
|
||||
|
||||
|
||||
def payload() -> dict:
|
||||
return {
|
||||
"instructions": [
|
||||
instruction(0x410C, "MOV:G.W", "R5, @(-H'0C00,R0)"),
|
||||
instruction(0x41E8, "MOV:G.W", "R5, @(-H'0850,R1)"),
|
||||
instruction(0x40FA, "CMP:G.W", "#H'6B6F, @H'F402", [0xF402]),
|
||||
instruction(0xBD3D, "MOV:G.B", "@(-H'3A9B,R4), R1"),
|
||||
instruction(0xBD45, "MOV:G.W", "R0, @(-H'0C00,R1)"),
|
||||
instruction(0xBD49, "BTST.B", "#7, @H'F76E", [0xF76E]),
|
||||
],
|
||||
"call_graph": {
|
||||
"nodes": [
|
||||
{"start": 0x4000, "end": 0x4216, "label": "loc_4000"},
|
||||
{"start": 0xBD0E, "end": 0xBD67, "label": "loc_BD0E"},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def rom_bytes() -> bytes:
|
||||
rom = bytearray([0xFF] * 0xCB00)
|
||||
rom[0xC564 : 0xC564 + 0x400] = b"\x00" * 0x400
|
||||
rom[0xC564 + 0x015 * 2 : 0xC564 + 0x015 * 2 + 2] = b"\x40\xAA"
|
||||
rom[0xC966:0xC968] = b"\x6B\x6F"
|
||||
rom[0xC968:0xC96A] = b"\xFE\x00"
|
||||
rom[0xCA0E:0xCA10] = b"\x80\x00"
|
||||
return bytes(rom)
|
||||
|
||||
|
||||
class EepromLayoutTest(unittest.TestCase):
|
||||
def test_analysis_extracts_factory_defaults_records_and_selector_mapping(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
rom_path = Path(tmp) / "rom.bin"
|
||||
rom_path.write_bytes(rom_bytes())
|
||||
|
||||
analysis = analyze_eeprom_layout(payload(), rom_path=rom_path)
|
||||
|
||||
self.assertEqual(analysis["kind"], "eeprom_layout")
|
||||
self.assertEqual(analysis["factory_defaults"]["entries"][1]["factory_word_hex"], "0x6B6F")
|
||||
self.assertEqual(analysis["persistent_records"][3]["ram_base_hex"], "H'F7C8")
|
||||
self.assertEqual(analysis["persistent_records"][3]["eeprom_base_hex"], "0x300")
|
||||
mapped = analysis["serial_persistence_mapping"]["entries"][0]
|
||||
self.assertEqual(mapped["selector_hex"], "0x015")
|
||||
self.assertEqual(mapped["shadow_address_hex"], "H'F4AA")
|
||||
self.assertEqual(mapped["eeprom_word_offset_hex"], "0xAA")
|
||||
self.assertEqual(mapped["factory_word_hex"], "0x8000")
|
||||
|
||||
def test_text_report_mentions_core_layout(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
rom_path = Path(tmp) / "rom.bin"
|
||||
rom_path.write_bytes(rom_bytes())
|
||||
analysis = analyze_eeprom_layout(payload(), rom_path=rom_path)
|
||||
|
||||
text = format_text_report(analysis)
|
||||
|
||||
self.assertIn("Persistent 8-Byte Records", text)
|
||||
self.assertIn("selector 0x015", text)
|
||||
self.assertIn("F76E", text)
|
||||
|
||||
def test_write_json_output(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
input_path = Path(tmp) / "rom.json"
|
||||
output_path = Path(tmp) / "eeprom.json"
|
||||
rom_path = Path(tmp) / "rom.bin"
|
||||
input_path.write_text(json.dumps(payload()), encoding="utf-8")
|
||||
rom_path.write_bytes(rom_bytes())
|
||||
|
||||
write_eeprom_layout(input_path, output_path, rom_path=rom_path, as_json=True)
|
||||
|
||||
written = json.loads(output_path.read_text(encoding="utf-8"))
|
||||
self.assertEqual(written["kind"], "eeprom_layout")
|
||||
self.assertEqual(written["summary"]["persistent_record_count"], 16)
|
||||
|
||||
def test_cli_writes_text_report(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
input_path = Path(tmp) / "rom.json"
|
||||
output_path = Path(tmp) / "eeprom.txt"
|
||||
rom_path = Path(tmp) / "rom.bin"
|
||||
input_path.write_text(json.dumps(payload()), encoding="utf-8")
|
||||
rom_path.write_bytes(rom_bytes())
|
||||
|
||||
stdout = io.StringIO()
|
||||
rc = main([str(input_path), "--rom", str(rom_path), "--out", str(output_path)], stdout=stdout)
|
||||
|
||||
self.assertEqual(rc, 0)
|
||||
self.assertIn("wrote", stdout.getvalue())
|
||||
self.assertIn("EEPROM Layout Report", output_path.read_text(encoding="utf-8"))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -37,7 +37,8 @@ class EmulatorAddressingTest(unittest.TestCase):
|
||||
self.assertEqual(memory.read16(0xF402), 0x6B6F)
|
||||
self.assertEqual(memory.p9_bus.fast_read_word(0x0010), (True, 0x8000))
|
||||
self.assertEqual(memory.p9_bus.fast_read_word(0x0110), (True, 0x8000))
|
||||
self.assertEqual(memory.p9_bus.fast_read_word(0x0002), (True, 0x2020))
|
||||
self.assertEqual(memory.p9_bus.fast_read_word(0x0002), (True, 0x6B6F))
|
||||
self.assertEqual(memory.p9_bus.fast_read_word(0x0102), (True, 0x2020))
|
||||
|
||||
def test_txi_indexed_byte_load_uses_signed_word_displacement_and_full_index_register(self):
|
||||
rom = rom_with_reset()
|
||||
|
||||
66
tests/test_emulator_eeprom_image.py
Normal file
66
tests/test_emulator_eeprom_image.py
Normal file
@@ -0,0 +1,66 @@
|
||||
import json
|
||||
import tempfile
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
from h8536.emulator.eeprom_image import build_eeprom_snapshot, format_eeprom_snapshot, write_eeprom_snapshot
|
||||
from h8536.emulator.memory import MemoryMap
|
||||
from h8536.emulator.peripherals.x24164 import X24164_LOGICAL_SIZE
|
||||
|
||||
|
||||
def rom_bytes() -> bytes:
|
||||
rom = bytearray([0xFF] * 0xCB00)
|
||||
rom[0xC564 : 0xC564 + 0x400] = b"\x00" * 0x400
|
||||
rom[0xC564 + 0x18 * 2 : 0xC564 + 0x18 * 2 + 2] = b"\x60\x10"
|
||||
rom[0xC964 : 0xC964 + 0x100] = b"\x00" * 0x100
|
||||
rom[0xC966:0xC968] = b"\x6B\x6F"
|
||||
rom[0xC974:0xC976] = b"\x80\x00"
|
||||
return bytes(rom)
|
||||
|
||||
|
||||
class EmulatorEepromImageTest(unittest.TestCase):
|
||||
def test_load_dump_and_write_log_cover_full_logical_image(self):
|
||||
memory = MemoryMap(rom_bytes())
|
||||
image = bytes([index & 0xFF for index in range(X24164_LOGICAL_SIZE)])
|
||||
|
||||
memory.load_eeprom_image(image)
|
||||
self.assertEqual(memory.dump_eeprom_image(), image)
|
||||
self.assertEqual(memory.external[0xF400], 0x00)
|
||||
self.assertEqual(memory.external[0xF402], 0x02)
|
||||
|
||||
self.assertTrue(memory.p9_bus.fast_write_word(0x0810, 0x1234))
|
||||
|
||||
self.assertEqual(memory.p9_bus.fast_read_word(0x0810), (True, 0x1234))
|
||||
self.assertEqual(len(memory.p9_bus.x24164_bus.write_events), 2)
|
||||
self.assertIn("addr=810", memory.p9_bus.x24164_bus.write_log_lines()[-2])
|
||||
|
||||
def test_snapshot_reports_records_factory_diffs_and_mapped_writes(self):
|
||||
memory = MemoryMap(rom_bytes())
|
||||
memory.seed_factory_eeprom_and_shadow()
|
||||
memory.p9_bus.fast_write_word(0x0110, 0x1234)
|
||||
|
||||
report = build_eeprom_snapshot(memory, rom_bytes=rom_bytes())
|
||||
text = format_eeprom_snapshot(report)
|
||||
|
||||
self.assertEqual(report["summary"]["write_word_count"], 1)
|
||||
self.assertEqual(report["write_word_events"][0]["new_word_hex"], "0x1234")
|
||||
self.assertEqual(report["write_word_events"][0]["mapped_selectors_hex"], ["0x018"])
|
||||
self.assertEqual(report["factory_diffs"][0]["address_hex"], "0x110")
|
||||
self.assertIn("EEPROM Word Writes", text)
|
||||
self.assertIn("selector", text)
|
||||
|
||||
def test_write_snapshot_json(self):
|
||||
memory = MemoryMap(rom_bytes())
|
||||
memory.seed_factory_eeprom_and_shadow()
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
output = Path(tmp) / "eeprom.json"
|
||||
write_eeprom_snapshot(memory, output, rom_bytes=rom_bytes(), as_json=True, include_image_hex=True)
|
||||
|
||||
payload = json.loads(output.read_text(encoding="utf-8"))
|
||||
self.assertEqual(payload["kind"], "emulator_eeprom_snapshot")
|
||||
self.assertEqual(len(payload["image_hex"]), X24164_LOGICAL_SIZE * 2)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
120
tests/test_emulator_state_search.py
Normal file
120
tests/test_emulator_state_search.py
Normal file
@@ -0,0 +1,120 @@
|
||||
import io
|
||||
import json
|
||||
import tempfile
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
from unittest.mock import patch
|
||||
|
||||
from h8536.emulator.state_search import (
|
||||
SearchCase,
|
||||
SearchResult,
|
||||
StatePatch,
|
||||
build_cases,
|
||||
classify_display,
|
||||
main,
|
||||
parse_address,
|
||||
parse_matrix_patch,
|
||||
parse_single_patch,
|
||||
target_matches,
|
||||
)
|
||||
|
||||
|
||||
class EmulatorStateSearchTest(unittest.TestCase):
|
||||
def test_parse_patch_accepts_hex_address_and_value(self):
|
||||
patch = parse_single_patch("F730=0x41", size=1)
|
||||
|
||||
self.assertEqual(patch, StatePatch(1, 0xF730, 0x41, "user"))
|
||||
self.assertEqual(patch.label(), "byte:H'F730=0x41")
|
||||
|
||||
def test_parse_matrix_patch_expands_values(self):
|
||||
patches = parse_matrix_patch("E000=0x4080,0x8080", size=2)
|
||||
|
||||
self.assertEqual(patches, [
|
||||
StatePatch(2, 0xE000, 0x4080, "user"),
|
||||
StatePatch(2, 0xE000, 0x8080, "user"),
|
||||
])
|
||||
|
||||
def test_parse_address_accepts_h_quote(self):
|
||||
self.assertEqual(parse_address("H'F970"), 0xF970)
|
||||
|
||||
def test_connect_queue_preset_builds_small_rom_driven_matrix(self):
|
||||
parser = __import__("h8536.emulator.state_search", fromlist=["build_arg_parser"]).build_arg_parser()
|
||||
args = parser.parse_args(["--preset", "connect-queue"])
|
||||
|
||||
cases = build_cases(args)
|
||||
|
||||
self.assertEqual(len(cases), 25)
|
||||
first = cases[0]
|
||||
self.assertEqual(first.pc, 0x2806)
|
||||
self.assertIn(StatePatch(2, 0xF970, 0x0000, "preset"), first.patches)
|
||||
self.assertIn(StatePatch(2, 0xE000, 0x0000, "preset"), first.patches)
|
||||
|
||||
def test_custom_matrix_combines_fixed_and_matrix_patches(self):
|
||||
parser = __import__("h8536.emulator.state_search", fromlist=["build_arg_parser"]).build_arg_parser()
|
||||
args = parser.parse_args([
|
||||
"--preset",
|
||||
"custom",
|
||||
"--pc",
|
||||
"0x2CB9",
|
||||
"--byte",
|
||||
"F730=0",
|
||||
"--matrix-word",
|
||||
"E000=0x4080,0x8080",
|
||||
])
|
||||
|
||||
cases = build_cases(args)
|
||||
|
||||
self.assertEqual(len(cases), 2)
|
||||
self.assertEqual(cases[0], SearchCase((StatePatch(1, 0xF730, 0, "user"), StatePatch(2, 0xE000, 0x4080, "user")), 0x2CB9))
|
||||
|
||||
def test_classify_display(self):
|
||||
self.assertEqual(classify_display(" CONNECT: OK | "), "ok")
|
||||
self.assertEqual(classify_display(" CONNECT:DXC-637 | "), "dxc")
|
||||
self.assertEqual(classify_display(" CONNECT:NOT ACT | "), "not-act")
|
||||
|
||||
def test_target_matching(self):
|
||||
self.assertTrue(target_matches("ok", "ok"))
|
||||
self.assertTrue(target_matches("dxc", "any-connect"))
|
||||
self.assertTrue(target_matches("ok", "changed"))
|
||||
self.assertFalse(target_matches("not-act", "changed"))
|
||||
|
||||
def test_cli_dry_run_lists_cases(self):
|
||||
stdout = io.StringIO()
|
||||
|
||||
rc = main(["--dry-run", "--preset", "connect-branch", "--limit", "2"], stdout=stdout)
|
||||
|
||||
self.assertEqual(rc, 0)
|
||||
output = stdout.getvalue()
|
||||
self.assertIn("preset=connect-branch cases=2", output)
|
||||
self.assertIn("case[0] pc=H'2CB9", output)
|
||||
|
||||
def test_cli_json_output_uses_results_from_run_search(self):
|
||||
fake_result = SearchResult(
|
||||
case_index=0,
|
||||
patches=(StatePatch(2, 0xE000, 0x8080, "preset"),),
|
||||
pc=0x2CB9,
|
||||
steps=10,
|
||||
stopped_reason="stop_pc",
|
||||
final_pc=0xFFFF,
|
||||
display=" CONNECT: OK | ",
|
||||
line0=" CONNECT: OK ",
|
||||
outcome="ok",
|
||||
f730=0x81,
|
||||
e000=0x8080,
|
||||
f9b4=0,
|
||||
f9b9=0,
|
||||
)
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
path = Path(tmpdir) / "out.json"
|
||||
stdout = io.StringIO()
|
||||
with patch("h8536.emulator.state_search.run_search", return_value=[fake_result]):
|
||||
rc = main(["--preset", "custom", "--word", "E000=0x8080", "--json-out", str(path)], stdout=stdout)
|
||||
payload = json.loads(path.read_text(encoding="utf-8"))
|
||||
|
||||
self.assertEqual(rc, 0)
|
||||
self.assertIn("hits=1", stdout.getvalue())
|
||||
self.assertEqual(payload["hits"][0]["outcome"], "ok")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
93
tests/test_rx_branch_trace.py
Normal file
93
tests/test_rx_branch_trace.py
Normal file
@@ -0,0 +1,93 @@
|
||||
import io
|
||||
import json
|
||||
import tempfile
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
from h8536.rx_branch_trace import analyze_rx_branch_trace, format_text_report, main
|
||||
|
||||
|
||||
def ins(address: int, text: str | None = None) -> dict[str, object]:
|
||||
return {
|
||||
"address": address,
|
||||
"text": text or f"INS_{address:04X}",
|
||||
"mnemonic": (text or "NOP").split()[0],
|
||||
"operands": "",
|
||||
"kind": "normal",
|
||||
"targets": [],
|
||||
"references": [],
|
||||
}
|
||||
|
||||
|
||||
def fixture_payload() -> dict[str, object]:
|
||||
addresses = {
|
||||
0x3FEF, 0x3FF3, 0x3FF5, 0x3FF9, 0x3FFD, 0x4001, 0x4003, 0x4007,
|
||||
0xBB57, 0xBB5B, 0xBB5F, 0xBB63, 0xBB67, 0xBB6D, 0xBB71, 0xBB75,
|
||||
0xBB77, 0xBB7D, 0xBB82, 0xBB84, 0xBB88, 0xBB8A, 0xBB90, 0xBB96,
|
||||
0xBB9A, 0xBB9C, 0xBB9E, 0xBBA3, 0xBBAB, 0xBBB0, 0xBBB3, 0xBBCB,
|
||||
0xBBCF, 0xBBD3, 0xBBD6, 0xBBD8, 0xBBDC, 0xBBE0, 0xBBE4, 0xBBE8,
|
||||
0xBBEC, 0xBBF0, 0xBBF3, 0xBBF7, 0xBBFD, 0xBC01, 0xBC08, 0xBC0C,
|
||||
0xBC0F, 0xBC13, 0xBC15, 0xBC19, 0xBC1D, 0xBC20, 0xBC24, 0xBC29,
|
||||
0xBC2E, 0xBC33, 0xBC37, 0xBC3A, 0xBC3C, 0xBC3E, 0xBC42, 0xBC45,
|
||||
0xBC4A, 0xBC4F, 0xBC54, 0xBC5C, 0xBC60, 0xBC63, 0xBC67, 0xBC69,
|
||||
0xBC75, 0xBC79, 0xBC82, 0xBC86, 0xBCB0, 0xBCCD, 0xBCD0, 0xBCD7,
|
||||
0xBCE0, 0xBCE8, 0xBCEC, 0xBCF0, 0xBCF6, 0xBCFA, 0xBCFD, 0xBD04,
|
||||
0xBD08, 0xBD0B, 0xBD0E, 0xBD1A, 0xBD1E, 0xBD22, 0xBD26, 0xBD35,
|
||||
0xBD64, 0xBD67, 0xBD6D, 0xBD75, 0xBD79, 0xBD80, 0xBD85, 0xBD94,
|
||||
0xBD9A, 0xBDB5, 0xBDBF, 0xBDC2, 0xBDC8, 0xBDD0, 0xBDD4, 0xBDDB,
|
||||
0xBDE5, 0xBDE9, 0xBDED, 0xBDF3, 0xBDFB, 0xBDFF, 0xBE05, 0xBE09,
|
||||
0xBE0D, 0xBE11, 0xBE15, 0xBE19, 0xBE1D, 0xBE22, 0xBE27, 0xBE29,
|
||||
0xBE2D, 0xBE31, 0xBE33, 0xBE37, 0xBE3C, 0xBE3E, 0xBE43, 0xBE47,
|
||||
0xBE4D, 0xBE52, 0xBE5A, 0xBE62, 0xBE6A, 0xBE70, 0xBE78, 0xBE80,
|
||||
0xBE82, 0xBE84, 0xBE88, 0xBE91, 0xBE95, 0xBE99, 0xBE9D, 0xBE9E,
|
||||
0xBEA5, 0xBEA9, 0xBEAD, 0xBEAF, 0xBEB5, 0xBEBB, 0xBEBF, 0xBEC1,
|
||||
0xBEC5, 0xBECB, 0xBED1, 0xBED5, 0xBEE4,
|
||||
}
|
||||
return {"instructions": [ins(address) for address in sorted(addresses)]}
|
||||
|
||||
|
||||
class RxBranchTraceTest(unittest.TestCase):
|
||||
def test_analyzes_dispatch_split_and_commands(self):
|
||||
analysis = analyze_rx_branch_trace(fixture_payload())
|
||||
|
||||
self.assertEqual(analysis["summary"]["confidence"], "high")
|
||||
self.assertEqual(analysis["frame_model"]["checksum_seed"], 0x5A)
|
||||
self.assertTrue(analysis["stages"][2]["present"])
|
||||
self.assertIn("FAA2 != 0", analysis["stages"][2]["summary"])
|
||||
|
||||
commands = {command["command"]: command for command in analysis["commands"]}
|
||||
self.assertIn("continuation path only", commands[0x04]["availability"])
|
||||
self.assertIn("selector zero is special", "\n".join(commands[0x04]["side_effects"]))
|
||||
self.assertIn("selectors 0x006C", "\n".join(commands[0x05]["side_effects"]))
|
||||
self.assertIn("previous finalized TX frame", commands[0x07]["summary"])
|
||||
|
||||
def test_text_report_mentions_bench_implications(self):
|
||||
text = format_text_report(analyze_rx_branch_trace(fixture_payload()))
|
||||
|
||||
self.assertIn("H8/536 SCI1 RX Branch Trace", text)
|
||||
self.assertIn("cmd 0x04 continuation_set_value_candidate", text)
|
||||
self.assertIn("standalone command 4 frame from idle should not hit BD0E", text)
|
||||
self.assertIn("Command 5 is not a generic always-live ACK", text)
|
||||
self.assertIn("Selector Decode", text)
|
||||
self.assertIn("TXI/RXI race and continuation collapse", text)
|
||||
self.assertIn("RX-to-TX Feedback Loops", text)
|
||||
|
||||
def test_cli_writes_json_output(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
input_path = Path(tmp) / "rom.json"
|
||||
output_path = Path(tmp) / "rx.json"
|
||||
input_path.write_text(json.dumps(fixture_payload()), encoding="utf-8")
|
||||
stdout = io.StringIO()
|
||||
|
||||
rc = main(["--json", "--out", str(output_path), str(input_path)], stdout=stdout)
|
||||
|
||||
self.assertEqual(rc, 0)
|
||||
self.assertIn("wrote", stdout.getvalue())
|
||||
payload = json.loads(output_path.read_text(encoding="utf-8"))
|
||||
self.assertEqual(payload["kind"], "rx_branch_trace")
|
||||
self.assertIn("downstream_traces", payload)
|
||||
self.assertIn("feedback_loops", payload)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
114
tests/test_state_map_runner.py
Normal file
114
tests/test_state_map_runner.py
Normal file
@@ -0,0 +1,114 @@
|
||||
import io
|
||||
import json
|
||||
import tempfile
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
from h8536.state_map_runner import (
|
||||
CONNECT_FORCE_PRESETS,
|
||||
StateMapEvent,
|
||||
analyze_events,
|
||||
format_analysis_report,
|
||||
main,
|
||||
parse_bench_log,
|
||||
)
|
||||
|
||||
|
||||
class StateMapRunnerTest(unittest.TestCase):
|
||||
def test_force_presets_match_expected_selector_zero_words(self):
|
||||
self.assertEqual(CONNECT_FORCE_PRESETS["dxc"][0], bytes.fromhex("04000040001E"))
|
||||
self.assertEqual(CONNECT_FORCE_PRESETS["dxc"][1], 0x4080)
|
||||
self.assertEqual(CONNECT_FORCE_PRESETS["ok"][0], bytes.fromhex("0400008000DE"))
|
||||
self.assertEqual(CONNECT_FORCE_PRESETS["ok"][1], 0x8080)
|
||||
self.assertEqual(CONNECT_FORCE_PRESETS["both"][0], bytes.fromhex("040000C0009E"))
|
||||
self.assertEqual(CONNECT_FORCE_PRESETS["both"][1], 0xC080)
|
||||
|
||||
def test_analyzes_successful_selector_zero_readback(self):
|
||||
events = [
|
||||
StateMapEvent("rx", bytes.fromhex("0780C060205D"), timestamp_ms=1000),
|
||||
StateMapEvent("tx", bytes.fromhex("0400008000DE"), timestamp_ms=1006),
|
||||
StateMapEvent("tx", bytes.fromhex("01000000005B"), timestamp_ms=1060),
|
||||
StateMapEvent("rx", bytes.fromhex("04001280804C"), timestamp_ms=1070),
|
||||
]
|
||||
|
||||
analysis = analyze_events(events, expected_word=0x8080)
|
||||
|
||||
self.assertEqual(analysis["outcome"]["name"], "selector_zero_retained")
|
||||
self.assertEqual(analysis["direct_readbacks"][0]["value"], 0x8080)
|
||||
self.assertTrue(analysis["direct_readbacks"][0]["matches_expected"])
|
||||
|
||||
def test_warns_when_command1_readback_is_between_trigger_and_force(self):
|
||||
events = [
|
||||
StateMapEvent("rx", bytes.fromhex("07804040A07D"), timestamp_ms=1000),
|
||||
StateMapEvent("tx", bytes.fromhex("01000000005B"), timestamp_ms=1005),
|
||||
StateMapEvent("tx", bytes.fromhex("04000040001E"), timestamp_ms=1010),
|
||||
]
|
||||
|
||||
analysis = analyze_events(events, expected_word=0x4080)
|
||||
|
||||
self.assertIn("command1_readback_between_trigger_and_force_can_spend_token", analysis["warnings"])
|
||||
self.assertEqual(analysis["outcome"]["name"], "force_not_proven")
|
||||
|
||||
def test_parse_bench_log_uses_detect_lines_for_rx_and_tx_chunks_for_host_frames(self):
|
||||
log = (
|
||||
"00:00:01.000 RX 006 bytes 07 80 C0 60 20 5D\n"
|
||||
"00:00:01.000 DETECT visible_C0_6020_family_candidate 07 80 C0 60 20 5D\n"
|
||||
"00:00:01.006 TX 006 bytes 04 00 00 80 00 DE\n"
|
||||
)
|
||||
|
||||
events = parse_bench_log(log)
|
||||
|
||||
self.assertEqual([(event.direction, event.frame.hex()) for event in events], [
|
||||
("rx", "0780c060205d"),
|
||||
("tx", "0400008000de"),
|
||||
])
|
||||
|
||||
def test_format_report_mentions_outcome_and_readback_value(self):
|
||||
analysis = analyze_events(
|
||||
[
|
||||
StateMapEvent("rx", bytes.fromhex("0780C060205D"), timestamp_ms=1000),
|
||||
StateMapEvent("tx", bytes.fromhex("0400008000DE"), timestamp_ms=1006),
|
||||
StateMapEvent("rx", bytes.fromhex("04001280804C"), timestamp_ms=1070),
|
||||
],
|
||||
expected_word=0x8080,
|
||||
)
|
||||
|
||||
report = format_analysis_report(analysis)
|
||||
|
||||
self.assertIn("outcome=selector_zero_retained", report)
|
||||
self.assertIn("value=0x8080 expected", report)
|
||||
|
||||
def test_cli_dry_run_prints_state_map_sequence(self):
|
||||
stdout = io.StringIO()
|
||||
|
||||
rc = main(["--dry-run", "--preset", "dxc", "--prime-frame", "01 80 40 40 30 EB"], stdout=stdout)
|
||||
|
||||
output = stdout.getvalue()
|
||||
self.assertEqual(rc, 0)
|
||||
self.assertIn("PT2 state-map proof runner", output)
|
||||
self.assertIn("force=04 00 00 40 00 1E", output)
|
||||
self.assertIn("expected_e0000=0x4080", output)
|
||||
self.assertIn("prime=01 80 40 40 30 EB", output)
|
||||
|
||||
def test_cli_analyze_log_writes_json(self):
|
||||
log = (
|
||||
"00:00:01.000 DETECT visible_C0_6020_family_candidate 07 80 C0 60 20 5D\n"
|
||||
"00:00:01.006 TX 006 bytes 04 00 00 80 00 DE\n"
|
||||
"00:00:01.070 DETECT table_readback_candidate 04 00 12 80 80 4C\n"
|
||||
)
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
log_path = Path(tmpdir) / "capture.txt"
|
||||
json_path = Path(tmpdir) / "analysis.json"
|
||||
log_path.write_text(log, encoding="utf-8")
|
||||
stdout = io.StringIO()
|
||||
|
||||
rc = main(["--analyze-log", str(log_path), "--preset", "ok", "--json-out", str(json_path)], stdout=stdout)
|
||||
|
||||
payload = json.loads(json_path.read_text(encoding="utf-8"))
|
||||
self.assertEqual(rc, 0)
|
||||
self.assertIn("outcome=selector_zero_retained", stdout.getvalue())
|
||||
self.assertEqual(payload["outcome"]["name"], "selector_zero_retained")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user