From 1ad03d56921e69096e156c464cf7874acbb27660 Mon Sep 17 00:00:00 2001 From: Aiden <68633820+awils27@users.noreply.github.com> Date: Tue, 26 May 2026 11:07:36 +1000 Subject: [PATCH] More ccu based mining --- README.md | 9 +- build/rom_ccu_seed_hints.json | 1981 +++++++++++++++++++++++++++++++++ build/rom_ccu_seed_hints.txt | 168 +++ h8536/ccu_seed_hints.py | 597 ++++++++++ h8536_ccu_seed_hints.py | 8 + tests/test_ccu_seed_hints.py | 118 ++ 6 files changed, 2880 insertions(+), 1 deletion(-) create mode 100644 build/rom_ccu_seed_hints.json create mode 100644 build/rom_ccu_seed_hints.txt create mode 100644 h8536/ccu_seed_hints.py create mode 100644 h8536_ccu_seed_hints.py create mode 100644 tests/test_ccu_seed_hints.py diff --git a/README.md b/README.md index f037d65..8811467 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,7 @@ To run the newer sidecar protocol and gate/queue analysis tools: .\.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_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 ``` @@ -81,6 +82,7 @@ The real-device bench helper uses `pyserial`; install repo dependencies with `.\ - 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. - 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. @@ -121,6 +123,7 @@ Current serial observations: - 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: @@ -143,6 +146,7 @@ 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_consistency.txt build/callgraph.dot ``` @@ -207,6 +211,7 @@ 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_consistency.py --help ``` @@ -214,6 +219,7 @@ python h8536_consistency.py --help - `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_consistency.py`: flags JSON-to-pseudocode semantic hazards such as byte immediates written to word destinations. For the emulator harness: @@ -282,6 +288,7 @@ python h8536_emulator_rx_probe.py --help - `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/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/rx_probe.py`: host-frame injection and response/listener probe for SCI1 RX experiments. @@ -294,7 +301,7 @@ 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_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. diff --git a/build/rom_ccu_seed_hints.json b/build/rom_ccu_seed_hints.json new file mode 100644 index 0000000..8956384 --- /dev/null +++ b/build/rom_ccu_seed_hints.json @@ -0,0 +1,1981 @@ +{ + "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." + ], + "dispatch_table": { + "default_target_hex": "H'2CA6", + "entry_count": 128, + "interesting_count": 25, + "interesting_entries": [ + { + "decoded_code": false, + "dispatch_index": 0, + "dispatch_index_hex": "0x000", + "entry_address_hex": "H'28A6", + "selector": 0, + "selector_hex": "0x000", + "target": 11449, + "target_hex": "H'2CB9", + "target_label_or_hex": "H'2CB9" + }, + { + "decoded_code": false, + "dispatch_index": 7, + "dispatch_index_hex": "0x007", + "entry_address_hex": "H'28B4", + "selector": 7, + "selector_hex": "0x007", + "target": 11715, + "target_hex": "H'2DC3", + "target_label_or_hex": "H'2DC3" + }, + { + "decoded_code": false, + "dispatch_index": 18, + "dispatch_index_hex": "0x012", + "entry_address_hex": "H'28CA", + "selector": 18, + "selector_hex": "0x012", + "target": 11779, + "target_hex": "H'2E03", + "target_label_or_hex": "H'2E03" + }, + { + "decoded_code": false, + "dispatch_index": 19, + "dispatch_index_hex": "0x013", + "entry_address_hex": "H'28CC", + "selector": 19, + "selector_hex": "0x013", + "target": 11782, + "target_hex": "H'2E06", + "target_label_or_hex": "H'2E06" + }, + { + "decoded_code": false, + "dispatch_index": 21, + "dispatch_index_hex": "0x015", + "entry_address_hex": "H'28D0", + "selector": 21, + "selector_hex": "0x015", + "target": 11833, + "target_hex": "H'2E39", + "target_label_or_hex": "H'2E39" + }, + { + "decoded_code": false, + "dispatch_index": 22, + "dispatch_index_hex": "0x016", + "entry_address_hex": "H'28D2", + "selector": 22, + "selector_hex": "0x016", + "target": 11866, + "target_hex": "H'2E5A", + "target_label_or_hex": "H'2E5A" + }, + { + "decoded_code": false, + "dispatch_index": 23, + "dispatch_index_hex": "0x017", + "entry_address_hex": "H'28D4", + "selector": 23, + "selector_hex": "0x017", + "target": 11909, + "target_hex": "H'2E85", + "target_label_or_hex": "H'2E85" + }, + { + "decoded_code": false, + "dispatch_index": 24, + "dispatch_index_hex": "0x018", + "entry_address_hex": "H'28D6", + "selector": 24, + "selector_hex": "0x018", + "target": 11887, + "target_hex": "H'2E6F", + "target_label_or_hex": "H'2E6F" + }, + { + "decoded_code": false, + "dispatch_index": 26, + "dispatch_index_hex": "0x01A", + "entry_address_hex": "H'28DA", + "selector": 26, + "selector_hex": "0x01A", + "target": 11972, + "target_hex": "H'2EC4", + "target_label_or_hex": "H'2EC4" + }, + { + "decoded_code": false, + "dispatch_index": 35, + "dispatch_index_hex": "0x023", + "entry_address_hex": "H'28EC", + "selector": 35, + "selector_hex": "0x023", + "target": 12006, + "target_hex": "H'2EE6", + "target_label_or_hex": "H'2EE6" + }, + { + "decoded_code": false, + "dispatch_index": 36, + "dispatch_index_hex": "0x024", + "entry_address_hex": "H'28EE", + "selector": 36, + "selector_hex": "0x024", + "target": 12044, + "target_hex": "H'2F0C", + "target_label_or_hex": "H'2F0C" + }, + { + "decoded_code": false, + "dispatch_index": 37, + "dispatch_index_hex": "0x025", + "entry_address_hex": "H'28F0", + "selector": 37, + "selector_hex": "0x025", + "target": 12060, + "target_hex": "H'2F1C", + "target_label_or_hex": "H'2F1C" + }, + { + "decoded_code": false, + "dispatch_index": 67, + "dispatch_index_hex": "0x043", + "entry_address_hex": "H'292C", + "selector": 67, + "selector_hex": "0x043", + "target": 12106, + "target_hex": "H'2F4A", + "target_label_or_hex": "H'2F4A" + }, + { + "decoded_code": false, + "dispatch_index": 74, + "dispatch_index_hex": "0x04A", + "entry_address_hex": "H'293A", + "selector": 74, + "selector_hex": "0x04A", + "target": 12124, + "target_hex": "H'2F5C", + "target_label_or_hex": "H'2F5C" + }, + { + "decoded_code": false, + "dispatch_index": 78, + "dispatch_index_hex": "0x04E", + "entry_address_hex": "H'2942", + "selector": 78, + "selector_hex": "0x04E", + "target": 12124, + "target_hex": "H'2F5C", + "target_label_or_hex": "H'2F5C" + }, + { + "decoded_code": false, + "dispatch_index": 82, + "dispatch_index_hex": "0x052", + "entry_address_hex": "H'294A", + "selector": 82, + "selector_hex": "0x052", + "target": 12124, + "target_hex": "H'2F5C", + "target_label_or_hex": "H'2F5C" + }, + { + "decoded_code": false, + "dispatch_index": 86, + "dispatch_index_hex": "0x056", + "entry_address_hex": "H'2952", + "selector": 86, + "selector_hex": "0x056", + "target": 12124, + "target_hex": "H'2F5C", + "target_label_or_hex": "H'2F5C" + }, + { + "decoded_code": false, + "dispatch_index": 90, + "dispatch_index_hex": "0x05A", + "entry_address_hex": "H'295A", + "selector": 90, + "selector_hex": "0x05A", + "target": 12124, + "target_hex": "H'2F5C", + "target_label_or_hex": "H'2F5C" + }, + { + "decoded_code": false, + "dispatch_index": 94, + "dispatch_index_hex": "0x05E", + "entry_address_hex": "H'2962", + "selector": 94, + "selector_hex": "0x05E", + "target": 12124, + "target_hex": "H'2F5C", + "target_label_or_hex": "H'2F5C" + }, + { + "decoded_code": false, + "dispatch_index": 98, + "dispatch_index_hex": "0x062", + "entry_address_hex": "H'296A", + "selector": 98, + "selector_hex": "0x062", + "target": 12124, + "target_hex": "H'2F5C", + "target_label_or_hex": "H'2F5C" + }, + { + "decoded_code": false, + "dispatch_index": 102, + "dispatch_index_hex": "0x066", + "entry_address_hex": "H'2972", + "selector": 102, + "selector_hex": "0x066", + "target": 12124, + "target_hex": "H'2F5C", + "target_label_or_hex": "H'2F5C" + }, + { + "decoded_code": false, + "dispatch_index": 106, + "dispatch_index_hex": "0x06A", + "entry_address_hex": "H'297A", + "selector": 106, + "selector_hex": "0x06A", + "target": 12124, + "target_hex": "H'2F5C", + "target_label_or_hex": "H'2F5C" + }, + { + "decoded_code": false, + "dispatch_index": 107, + "dispatch_index_hex": "0x06B", + "entry_address_hex": "H'297C", + "selector": 107, + "selector_hex": "0x06B", + "target": 12146, + "target_hex": "H'2F72", + "target_label_or_hex": "H'2F72" + }, + { + "decoded_code": false, + "dispatch_index": 108, + "dispatch_index_hex": "0x06C", + "entry_address_hex": "H'297E", + "selector": 108, + "selector_hex": "0x06C", + "target": 12207, + "target_hex": "H'2FAF", + "target_label_or_hex": "H'2FAF" + }, + { + "decoded_code": false, + "dispatch_index": 109, + "dispatch_index_hex": "0x06D", + "entry_address_hex": "H'2980", + "selector": 109, + "selector_hex": "0x06D", + "target": 12309, + "target_hex": "H'3015", + "target_label_or_hex": "H'3015" + } + ], + "table_base": 10406, + "table_base_hex": "H'28A6" + }, + "display_text_hints": { + "note": "Text hits are ROM display resources, not literal serial payloads.", + "regions": [ + { + "count": 2, + "end": 10075, + "samples": [ + "FhG~H", + "%SbS" + ], + "start": 10006 + }, + { + "count": 2, + "end": 10713, + "samples": [ + "/\\/r/", + "4N0M," + ], + "start": 10618 + }, + { + "count": 2, + "end": 22576, + "samples": [ + "Z [", + "Z [" + ], + "start": 22436 + }, + { + "count": 2, + "end": 23432, + "samples": [ + "0123456789", + "0 1 2 3 4 5 6 7 8 910111213141516171819" + ], + "start": 23381 + }, + { + "count": 4, + "end": 25412, + "samples": [ + "lZltl", + "[c.T", + "rXs(s", + "vpwhx6" + ], + "start": 25340 + }, + { + "count": 17, + "end": 26456, + "samples": [ + "OPERATION", + "PAINT", + "ADV~Xd", + "OPERATION", + "IRIS/M.BLK", + "OPERATION", + "LOCK", + "DYNA LATITUDE Xe/" + ], + "start": 25559 + }, + { + "count": 7, + "end": 27029, + "samples": [ + "TLCS Xg", + "ON OFF~Xh", + "AGC GAIN AE Xh", + "CL F16 F11 F8 F5.6F4 F2.8F2 F1.8F1.4 OP DPR HYP HIGHMID LOW 36dB30dB24dB18dB12dB 9dB 6dB 3dB 0dB-3dB", + "AUTO FUNC Xi;", + "ATW Xi\\", + "ON OFF~Xi" + ], + "start": 26592 + }, + { + "count": 10, + "end": 27719, + "samples": [ + "AUTO FUNC XjO", + "STD SPOT.L~Xjp", + "A.IRIS MODE Xj", + "AI BACK.L~Xj", + "AUTO FUNC Xk=", + "AUTO FOCUS Xk^", + "ON OFF~Xk", + "DIAG Xk" + ], + "start": 27215 + } + ], + "term_hits": [ + { + "hit_count": 0, + "samples": [], + "term": "CONNECT" + }, + { + "hit_count": 4, + "samples": [ + { + "address_hex": "H'77F4", + "text": "literal COMM LINK" + }, + { + "address_hex": "H'78F4", + "text": "literal COMM LINK" + }, + { + "address_hex": "H'77F4", + "text": "COMM LINK ITEM-1Xw" + }, + { + "address_hex": "H'78F4", + "text": "COMM LINK ITEM-2Xx" + } + ], + "term": "COMM LINK" + }, + { + "hit_count": 2, + "samples": [ + { + "address_hex": "H'A027", + "text": "literal COMPLETED" + }, + { + "address_hex": "H'A025", + "text": "COMPLETED" + } + ], + "term": "COMPLETED" + }, + { + "hit_count": 6, + "samples": [ + { + "address_hex": "H'7149", + "text": "literal CAM" + }, + { + "address_hex": "H'71FC", + "text": "literal CAM" + }, + { + "address_hex": "H'72C7", + "text": "literal CAM" + }, + { + "address_hex": "H'7144", + "text": "CAM ID SET~XqD" + }, + { + "address_hex": "H'71F9", + "text": "CAM ID IND Xq" + }, + { + "address_hex": "H'72C7", + "text": "CAM BARS~Xr" + } + ], + "term": "CAM" + }, + { + "hit_count": 12, + "samples": [ + { + "address_hex": "H'72D1", + "text": "literal BARS" + }, + { + "address_hex": "H'757D", + "text": "literal BARS" + }, + { + "address_hex": "H'9C61", + "text": "literal BARS" + }, + { + "address_hex": "H'9E7D", + "text": "literal BARS" + }, + { + "address_hex": "H'72C7", + "text": "CAM BARS~Xr" + }, + { + "address_hex": "H'757A", + "text": "BARS TYPE Xuz" + }, + { + "address_hex": "H'9C3A", + "text": "OPERATION OPERATION NG:BARS NG:HIGH LIGHT NG:LOW LIGHT OK" + }, + { + "address_hex": "H'9E56", + "text": "OPERATION OPERATION NG:BARS NG OK" + } + ], + "term": "BARS" + }, + { + "hit_count": 22, + "samples": [ + { + "address_hex": "H'65CC", + "text": "literal BLACK" + }, + { + "address_hex": "H'6647", + "text": "literal BLACK" + }, + { + "address_hex": "H'6709", + "text": "literal BLACK" + }, + { + "address_hex": "H'78E0", + "text": "literal BLACK" + }, + { + "address_hex": "H'65C9", + "text": "BLACK STR Xe" + }, + { + "address_hex": "H'6644", + "text": "BLACK STR XfD" + }, + { + "address_hex": "H'6706", + "text": "BLACK STR Xg" + }, + { + "address_hex": "H'78D7", + "text": "WHITE BLACK~Xx" + } + ], + "term": "BLACK" + }, + { + "hit_count": 6, + "samples": [ + { + "address_hex": "H'6461", + "text": "literal IRIS" + }, + { + "address_hex": "H'6A92", + "text": "literal IRIS" + }, + { + "address_hex": "H'A5CA", + "text": "literal IRIS" + }, + { + "address_hex": "H'6461", + "text": "IRIS/M.BLK" + }, + { + "address_hex": "H'6A8E", + "text": "A.IRIS MODE Xj" + }, + { + "address_hex": "H'A5A6", + "text": "NG NG:?? NG:IRIS NOT CL NG:BARS" + } + ], + "term": "IRIS" + }, + { + "hit_count": 10, + "samples": [ + { + "address_hex": "H'6825", + "text": "literal GAIN" + }, + { + "address_hex": "H'7813", + "text": "literal GAIN" + }, + { + "address_hex": "H'98A1", + "text": "literal GAIN" + }, + { + "address_hex": "H'99B8", + "text": "literal GAIN" + }, + { + "address_hex": "H'681F", + "text": "AGC GAIN AE Xh" + }, + { + "address_hex": "H'7813", + "text": "GAIN SHUTTER~Xx" + }, + { + "address_hex": "H'9895", + "text": "START:PUSH AGAINX" + }, + { + "address_hex": "H'99AC", + "text": "START:PUSH AGAINX" + } + ], + "term": "GAIN" + }, + { + "hit_count": 4, + "samples": [ + { + "address_hex": "H'6FB2", + "text": "literal SHUTTER" + }, + { + "address_hex": "H'781A", + "text": "literal SHUTTER" + }, + { + "address_hex": "H'6FAE", + "text": "SHUTTER Xo" + }, + { + "address_hex": "H'7813", + "text": "GAIN SHUTTER~Xx" + } + ], + "term": "SHUTTER" + }, + { + "hit_count": 8, + "samples": [ + { + "address_hex": "H'B53E", + "text": "literal CALL" + }, + { + "address_hex": "H'B563", + "text": "literal CALL" + }, + { + "address_hex": "H'B62F", + "text": "literal CALL" + }, + { + "address_hex": "H'B654", + "text": "literal CALL" + }, + { + "address_hex": "H'B537", + "text": "RECALL X" + }, + { + "address_hex": "H'B558", + "text": "SCENE F. RECALL~X" + }, + { + "address_hex": "H'B628", + "text": "RECALL X" + }, + { + "address_hex": "H'B649", + "text": "SETUP F. RECALL~X" + } + ], + "term": "CALL" + }, + { + "hit_count": 0, + "samples": [], + "term": "POWER" + }, + { + "hit_count": 34, + "samples": [ + { + "address_hex": "H'693E", + "text": "literal AUTO" + }, + { + "address_hex": "H'6A52", + "text": "literal AUTO" + }, + { + "address_hex": "H'6B40", + "text": "literal AUTO" + }, + { + "address_hex": "H'6B61", + "text": "literal AUTO" + }, + { + "address_hex": "H'693B", + "text": "AUTO FUNC Xi;" + }, + { + "address_hex": "H'6A4F", + "text": "AUTO FUNC XjO" + }, + { + "address_hex": "H'6B3D", + "text": "AUTO FUNC Xk=" + }, + { + "address_hex": "H'6B5E", + "text": "AUTO FOCUS Xk^" + } + ], + "term": "AUTO" + }, + { + "hit_count": 6, + "samples": [ + { + "address_hex": "H'6BF5", + "text": "literal DIAG" + }, + { + "address_hex": "H'6C19", + "text": "literal DIAG" + }, + { + "address_hex": "H'6E46", + "text": "literal DIAG" + }, + { + "address_hex": "H'6BEF", + "text": "DIAG Xk" + }, + { + "address_hex": "H'6C16", + "text": "DIAG DATA Xl" + }, + { + "address_hex": "H'6E40", + "text": "DIAG Xn@" + } + ], + "term": "DIAG" + }, + { + "hit_count": 0, + "samples": [], + "term": "DXC" + } + ] + }, + "kind": "ccu_seed_hints", + "seed_plan": { + "model": "candidate initial CCU state push using command 0 writes, verified with command 1 reads", + "steps": [ + { + "frame": "00 00 00 80 80 5A", + "name": "cmd0 seed selector 0x000 = 0x8080", + "readback_frame": "01 00 00 00 00 5B", + "selector": 0, + "selector_hex": "0x000", + "value": 32896, + "value_hex": "0x8080", + "why": "selector zero active/connect candidate from emulator state search" + }, + { + "frame": "00 00 03 80 00 D9", + "name": "cmd0 seed selector 0x003 = 0x8000", + "readback_frame": "01 00 03 00 00 58", + "selector": 3, + "selector_hex": "0x003", + "value": 32768, + "value_hex": "0x8000", + "why": "ROM default state also sets selector 0x003 high bit" + }, + { + "frame": "00 00 40 FF FF 1A", + "name": "cmd0 seed selector 0x040 = 0xFFFF", + "readback_frame": "01 00 40 00 00 1B", + "selector": 64, + "selector_hex": "0x040", + "value": 65535, + "value_hex": "0xFFFF", + "why": "ROM default all-ones/status candidate touched by bench 0x40 family" + }, + { + "frame": "00 01 76 20 00 0D", + "name": "cmd0 seed selector 0x0F6 = 0x2000", + "readback_frame": "01 01 76 00 00 2C", + "selector": 246, + "selector_hex": "0x0F6", + "value": 8192, + "value_hex": "0x2000", + "why": "sets E1EC bit13 candidate used by loc_48FA report bridge" + } + ] + }, + "selector_candidates": [ + { + "accesses": [ + { + "access": "write", + "address_hex": "H'4096", + "function": "loc_4096", + "instruction": "MOV:G.W #H'0080, @H'E000", + "table": "primary_value_table_candidate" + }, + { + "access": "write", + "address_hex": "H'40A8", + "function": "loc_4096", + "instruction": "MOV:G.W #H'0080, @H'E800", + "table": "current_value_table_candidate" + }, + { + "access": "write", + "address_hex": "H'4088", + "function": "loc_4075", + "instruction": "CLR.W @(-H'1400,R0)", + "table": "flag_table_candidate" + } + ], + "cmd1_read_frame": "01 00 00 00 00 5B", + "dispatch_target": { + "decoded_code": false, + "dispatch_index": 0, + "dispatch_index_hex": "0x000", + "entry_address_hex": "H'28A6", + "selector": 0, + "selector_hex": "0x000", + "target": 11449, + "target_hex": "H'2CB9", + "target_label_or_hex": "H'2CB9" + }, + "name": "heartbeat_or_idle_report_candidate", + "reasons": [ + "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", + "observed RCP autonomous report frame(s): 00 00 00 00 80 DA", + "selector dispatches to H'2CB9" + ], + "score": 18, + "seed_frames": [ + { + "cmd0_frame": "00 00 00 00 80 DA", + "value": 128, + "value_hex": "0x0080" + }, + { + "cmd0_frame": "00 00 00 80 80 5A", + "value": 32896, + "value_hex": "0x8080" + } + ], + "selector": 0, + "selector_hex": "0x000", + "tables": [ + "primary_value_table_candidate", + "current_value_table_candidate", + "flag_table_candidate" + ] + }, + { + "accesses": [ + { + "access": "read", + "address_hex": "H'17D0", + "function": "loc_17C9", + "instruction": "BTST.W #12, @H'E126", + "table": "primary_value_table_candidate" + }, + { + "access": "read", + "address_hex": "H'1802", + "function": "loc_17FB", + "instruction": "BTST.W #12, @H'E126", + "table": "primary_value_table_candidate" + }, + { + "access": "read", + "address_hex": "H'183A", + "function": "loc_182D", + "instruction": "BTST.W #5, @H'E126", + "table": "primary_value_table_candidate" + }, + { + "access": "read", + "address_hex": "H'189E", + "function": "loc_1891", + "instruction": "BTST.W #5, @H'E126", + "table": "primary_value_table_candidate" + }, + { + "access": "read", + "address_hex": "H'18F4", + "function": "loc_18E7", + "instruction": "BTST.W #5, @H'E126", + "table": "primary_value_table_candidate" + } + ], + "cmd1_read_frame": "01 01 13 00 00 49", + "name": "state_selector_candidate", + "reasons": [ + "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", + "primary_value_table_candidate read in loc_18E7: BTST.W #5, @H'E126" + ], + "score": 15, + "seed_frames": [], + "selector": 147, + "selector_hex": "0x093", + "tables": [ + "primary_value_table_candidate" + ] + }, + { + "accesses": [ + { + "access": "read", + "address_hex": "H'490F", + "function": "loc_48FA", + "instruction": "BTST.W #13, @H'E1EC", + "table": "primary_value_table_candidate" + }, + { + "access": "read", + "address_hex": "H'4915", + "function": "loc_48FA", + "instruction": "MOV:G.W @H'E1EC, R0", + "table": "primary_value_table_candidate" + }, + { + "access": "write", + "address_hex": "H'491D", + "function": "loc_48FA", + "instruction": "MOV:G.W R0, @H'E9EC", + "table": "current_value_table_candidate" + } + ], + "cmd1_read_frame": "01 01 76 00 00 2C", + "name": "active_status_bridge_candidate", + "reasons": [ + "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" + ], + "score": 14, + "seed_frames": [ + { + "cmd0_frame": "00 01 76 20 00 0D", + "value": 8192, + "value_hex": "0x2000" + } + ], + "selector": 246, + "selector_hex": "0x0F6", + "tables": [ + "primary_value_table_candidate", + "current_value_table_candidate" + ] + }, + { + "accesses": [ + { + "access": "write", + "address_hex": "H'409C", + "function": "loc_4096", + "instruction": "MOV:G.W #H'8000, @H'E006", + "table": "primary_value_table_candidate" + }, + { + "access": "write", + "address_hex": "H'40AE", + "function": "loc_4096", + "instruction": "MOV:G.W #H'8000, @H'E806", + "table": "current_value_table_candidate" + } + ], + "cmd1_read_frame": "01 00 03 00 00 58", + "name": "default_enabled_bit_candidate", + "reasons": [ + "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" + ], + "score": 11, + "seed_frames": [ + { + "cmd0_frame": "00 00 03 80 00 D9", + "value": 32768, + "value_hex": "0x8000" + } + ], + "selector": 3, + "selector_hex": "0x003", + "tables": [ + "primary_value_table_candidate", + "current_value_table_candidate" + ] + }, + { + "accesses": [ + { + "access": "write", + "address_hex": "H'40A2", + "function": "loc_4096", + "instruction": "MOV:G.W #H'FFFF, @H'E080", + "table": "primary_value_table_candidate" + }, + { + "access": "write", + "address_hex": "H'40B4", + "function": "loc_4096", + "instruction": "MOV:G.W #H'FFFF, @H'E880", + "table": "current_value_table_candidate" + } + ], + "cmd1_read_frame": "01 00 40 00 00 1B", + "name": "default_all_ones_or_status_block_candidate", + "reasons": [ + "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" + ], + "score": 11, + "seed_frames": [ + { + "cmd0_frame": "00 00 40 FF FF 1A", + "value": 65535, + "value_hex": "0xFFFF" + }, + { + "cmd0_frame": "00 00 40 40 30 6A", + "value": 16432, + "value_hex": "0x4030" + } + ], + "selector": 64, + "selector_hex": "0x040", + "tables": [ + "primary_value_table_candidate", + "current_value_table_candidate" + ] + }, + { + "accesses": [ + { + "access": "read", + "address_hex": "H'3DDA", + "function": "vec_ad_adi_3D99", + "instruction": "MOV:G.W @H'E102, R0", + "table": "primary_value_table_candidate" + }, + { + "access": "read", + "address_hex": "H'3DFA", + "function": "vec_ad_adi_3D99", + "instruction": "CMP:G.W @H'E102, R1", + "table": "primary_value_table_candidate" + }, + { + "access": "write", + "address_hex": "H'15ED", + "function": "loc_15E0", + "instruction": "MOV:G.W R1, @H'E902", + "table": "current_value_table_candidate" + } + ], + "cmd1_read_frame": "01 01 01 00 00 5B", + "name": "state_selector_candidate", + "reasons": [ + "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" + ], + "score": 9, + "seed_frames": [], + "selector": 129, + "selector_hex": "0x081", + "tables": [ + "primary_value_table_candidate", + "current_value_table_candidate" + ] + }, + { + "accesses": [ + { + "access": "read", + "address_hex": "H'2657", + "function": "loc_2650", + "instruction": "MOV:G.W @H'E124, R0", + "table": "primary_value_table_candidate" + }, + { + "access": "read", + "address_hex": "H'268B", + "function": "loc_2650", + "instruction": "CMP:G.W @H'E124, R0", + "table": "primary_value_table_candidate" + }, + { + "access": "write", + "address_hex": "H'2691", + "function": "loc_2650", + "instruction": "MOV:G.W R0, @H'E924", + "table": "current_value_table_candidate" + } + ], + "cmd1_read_frame": "01 01 12 00 00 48", + "name": "state_selector_candidate", + "reasons": [ + "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" + ], + "score": 9, + "seed_frames": [], + "selector": 146, + "selector_hex": "0x092", + "tables": [ + "primary_value_table_candidate", + "current_value_table_candidate" + ] + }, + { + "accesses": [], + "cmd1_read_frame": "01 00 6B 00 00 30", + "dispatch_target": { + "decoded_code": false, + "dispatch_index": 107, + "dispatch_index_hex": "0x06B", + "entry_address_hex": "H'297C", + "selector": 107, + "selector_hex": "0x06B", + "target": 12146, + "target_hex": "H'2F72", + "target_label_or_hex": "H'2F72" + }, + "name": "connection_latch_clear_candidate", + "reasons": [ + "when F731.7 is set, command 5 on this selector clears F731.7/F790.7", + "selector dispatches to H'2F72" + ], + "score": 7, + "seed_frames": [], + "selector": 107, + "selector_hex": "0x06B", + "tables": [] + }, + { + "accesses": [], + "cmd1_read_frame": "01 00 6C 00 00 37", + "dispatch_target": { + "decoded_code": false, + "dispatch_index": 108, + "dispatch_index_hex": "0x06C", + "entry_address_hex": "H'297E", + "selector": 108, + "selector_hex": "0x06C", + "target": 12207, + "target_hex": "H'2FAF", + "target_label_or_hex": "H'2FAF" + }, + "name": "command5_be70_candidate", + "reasons": [ + "continuation command 5 calls BE70 for selector 0x006C", + "selector dispatches to H'2FAF" + ], + "score": 7, + "seed_frames": [], + "selector": 108, + "selector_hex": "0x06C", + "tables": [] + }, + { + "accesses": [], + "cmd1_read_frame": "01 00 6D 00 00 36", + "dispatch_target": { + "decoded_code": false, + "dispatch_index": 109, + "dispatch_index_hex": "0x06D", + "entry_address_hex": "H'2980", + "selector": 109, + "selector_hex": "0x06D", + "target": 12309, + "target_hex": "H'3015", + "target_label_or_hex": "H'3015" + }, + "name": "command5_be70_candidate", + "reasons": [ + "continuation command 5 calls BE70 for selector 0x006D", + "selector dispatches to H'3015" + ], + "score": 7, + "seed_frames": [], + "selector": 109, + "selector_hex": "0x06D", + "tables": [] + }, + { + "accesses": [], + "cmd1_read_frame": "01 00 07 00 00 5C", + "dispatch_target": { + "decoded_code": false, + "dispatch_index": 7, + "dispatch_index_hex": "0x007", + "entry_address_hex": "H'28B4", + "selector": 7, + "selector_hex": "0x007", + "target": 11715, + "target_hex": "H'2DC3", + "target_label_or_hex": "H'2DC3" + }, + "name": "camera_power_report_candidate", + "reasons": [ + "observed RCP autonomous report frame(s): 00 00 07 80 00 DD", + "selector dispatches to H'2DC3" + ], + "score": 5, + "seed_frames": [], + "selector": 7, + "selector_hex": "0x007", + "tables": [] + }, + { + "accesses": [], + "cmd1_read_frame": "01 00 15 00 00 4E", + "dispatch_target": { + "decoded_code": false, + "dispatch_index": 21, + "dispatch_index_hex": "0x015", + "entry_address_hex": "H'28D0", + "selector": 21, + "selector_hex": "0x015", + "target": 11833, + "target_hex": "H'2E39", + "target_label_or_hex": "H'2E39" + }, + "name": "call_button_report_candidate", + "reasons": [ + "observed RCP autonomous report frame(s): 00 00 15 80 00 CF, 00 00 15 00 00 4F", + "selector dispatches to H'2E39" + ], + "score": 5, + "seed_frames": [], + "selector": 21, + "selector_hex": "0x015", + "tables": [] + }, + { + "accesses": [ + { + "access": "write", + "address_hex": "H'402C", + "function": "loc_400C", + "instruction": "CLR.W @H'E046", + "table": "primary_value_table_candidate" + } + ], + "cmd1_read_frame": "01 00 23 00 00 78", + "dispatch_target": { + "decoded_code": false, + "dispatch_index": 35, + "dispatch_index_hex": "0x023", + "entry_address_hex": "H'28EC", + "selector": 35, + "selector_hex": "0x023", + "target": 12006, + "target_hex": "H'2EE6", + "target_label_or_hex": "H'2EE6" + }, + "name": "state_selector_candidate", + "reasons": [ + "primary_value_table_candidate write in loc_400C: CLR.W @H'E046", + "selector dispatches to H'2EE6" + ], + "score": 5, + "seed_frames": [], + "selector": 35, + "selector_hex": "0x023", + "tables": [ + "primary_value_table_candidate" + ] + }, + { + "accesses": [], + "cmd1_read_frame": "01 00 6E 00 00 35", + "name": "command5_be70_candidate", + "reasons": [ + "continuation command 5 calls BE70 for selector 0x006E" + ], + "score": 5, + "seed_frames": [], + "selector": 110, + "selector_hex": "0x06E", + "tables": [] + }, + { + "accesses": [], + "cmd1_read_frame": "01 01 16 00 00 4C", + "name": "connection_latch_clear_candidate", + "reasons": [ + "when F731.7 is set, command 5 on this selector clears F731.7/F790.7" + ], + "score": 5, + "seed_frames": [], + "selector": 150, + "selector_hex": "0x096", + "tables": [] + }, + { + "accesses": [], + "cmd1_read_frame": "01 01 17 00 00 4D", + "name": "connection_latch_clear_candidate", + "reasons": [ + "when F731.7 is set, command 5 on this selector clears F731.7/F790.7" + ], + "score": 5, + "seed_frames": [], + "selector": 151, + "selector_hex": "0x097", + "tables": [] + }, + { + "accesses": [], + "cmd1_read_frame": "01 01 46 00 00 1C", + "name": "connection_latch_clear_candidate", + "reasons": [ + "when F731.7 is set, command 5 on this selector clears F731.7/F790.7" + ], + "score": 5, + "seed_frames": [], + "selector": 198, + "selector_hex": "0x0C6", + "tables": [] + }, + { + "accesses": [], + "cmd1_read_frame": "01 01 78 00 00 22", + "name": "connection_latch_clear_candidate", + "reasons": [ + "when F731.7 is set, command 5 on this selector clears F731.7/F790.7" + ], + "score": 5, + "seed_frames": [], + "selector": 248, + "selector_hex": "0x0F8", + "tables": [] + }, + { + "accesses": [ + { + "access": "read", + "address_hex": "H'266F", + "function": "loc_2650", + "instruction": "BTST.W #13, @H'E004", + "table": "primary_value_table_candidate" + } + ], + "cmd1_read_frame": "01 00 02 00 00 59", + "name": "state_selector_candidate", + "reasons": [ + "primary_value_table_candidate read in loc_2650: BTST.W #13, @H'E004" + ], + "score": 3, + "seed_frames": [], + "selector": 2, + "selector_hex": "0x002", + "tables": [ + "primary_value_table_candidate" + ] + }, + { + "accesses": [ + { + "access": "read", + "address_hex": "H'170C", + "function": "loc_1705", + "instruction": "BTST.W #15, @H'E14E", + "table": "primary_value_table_candidate" + } + ], + "cmd1_read_frame": "01 01 27 00 00 7D", + "name": "state_selector_candidate", + "reasons": [ + "primary_value_table_candidate read in loc_1705: BTST.W #15, @H'E14E" + ], + "score": 3, + "seed_frames": [], + "selector": 167, + "selector_hex": "0x0A7", + "tables": [ + "primary_value_table_candidate" + ] + }, + { + "accesses": [ + { + "access": "read", + "address_hex": "H'175A", + "function": "loc_174D", + "instruction": "BTST.W #13, @H'E16E", + "table": "primary_value_table_candidate" + } + ], + "cmd1_read_frame": "01 01 37 00 00 6D", + "name": "state_selector_candidate", + "reasons": [ + "primary_value_table_candidate read in loc_174D: BTST.W #13, @H'E16E" + ], + "score": 3, + "seed_frames": [], + "selector": 183, + "selector_hex": "0x0B7", + "tables": [ + "primary_value_table_candidate" + ] + }, + { + "accesses": [ + { + "access": "read", + "address_hex": "H'179C", + "function": "loc_1795", + "instruction": "BTST.W #13, @H'E172", + "table": "primary_value_table_candidate" + } + ], + "cmd1_read_frame": "01 01 39 00 00 63", + "name": "state_selector_candidate", + "reasons": [ + "primary_value_table_candidate read in loc_1795: BTST.W #13, @H'E172" + ], + "score": 3, + "seed_frames": [], + "selector": 185, + "selector_hex": "0x0B9", + "tables": [ + "primary_value_table_candidate" + ] + }, + { + "accesses": [ + { + "access": "read", + "address_hex": "H'17A7", + "function": "loc_1795", + "instruction": "BTST.W #15, @H'E220", + "table": "primary_value_table_candidate" + } + ], + "cmd1_read_frame": "01 01 90 00 00 CA", + "name": "state_selector_candidate", + "reasons": [ + "primary_value_table_candidate read in loc_1795: BTST.W #15, @H'E220" + ], + "score": 3, + "seed_frames": [], + "selector": 272, + "selector_hex": "0x110", + "tables": [ + "primary_value_table_candidate" + ] + }, + { + "accesses": [], + "cmd1_read_frame": "01 00 12 00 00 49", + "dispatch_target": { + "decoded_code": false, + "dispatch_index": 18, + "dispatch_index_hex": "0x012", + "entry_address_hex": "H'28CA", + "selector": 18, + "selector_hex": "0x012", + "target": 11779, + "target_hex": "H'2E03", + "target_label_or_hex": "H'2E03" + }, + "name": "state_selector_candidate", + "reasons": [ + "selector dispatches to H'2E03" + ], + "score": 2, + "seed_frames": [], + "selector": 18, + "selector_hex": "0x012", + "tables": [] + }, + { + "accesses": [], + "cmd1_read_frame": "01 00 13 00 00 48", + "dispatch_target": { + "decoded_code": false, + "dispatch_index": 19, + "dispatch_index_hex": "0x013", + "entry_address_hex": "H'28CC", + "selector": 19, + "selector_hex": "0x013", + "target": 11782, + "target_hex": "H'2E06", + "target_label_or_hex": "H'2E06" + }, + "name": "state_selector_candidate", + "reasons": [ + "selector dispatches to H'2E06" + ], + "score": 2, + "seed_frames": [], + "selector": 19, + "selector_hex": "0x013", + "tables": [] + }, + { + "accesses": [], + "cmd1_read_frame": "01 00 16 00 00 4D", + "dispatch_target": { + "decoded_code": false, + "dispatch_index": 22, + "dispatch_index_hex": "0x016", + "entry_address_hex": "H'28D2", + "selector": 22, + "selector_hex": "0x016", + "target": 11866, + "target_hex": "H'2E5A", + "target_label_or_hex": "H'2E5A" + }, + "name": "state_selector_candidate", + "reasons": [ + "selector dispatches to H'2E5A" + ], + "score": 2, + "seed_frames": [], + "selector": 22, + "selector_hex": "0x016", + "tables": [] + }, + { + "accesses": [], + "cmd1_read_frame": "01 00 17 00 00 4C", + "dispatch_target": { + "decoded_code": false, + "dispatch_index": 23, + "dispatch_index_hex": "0x017", + "entry_address_hex": "H'28D4", + "selector": 23, + "selector_hex": "0x017", + "target": 11909, + "target_hex": "H'2E85", + "target_label_or_hex": "H'2E85" + }, + "name": "state_selector_candidate", + "reasons": [ + "selector dispatches to H'2E85" + ], + "score": 2, + "seed_frames": [], + "selector": 23, + "selector_hex": "0x017", + "tables": [] + }, + { + "accesses": [], + "cmd1_read_frame": "01 00 18 00 00 43", + "dispatch_target": { + "decoded_code": false, + "dispatch_index": 24, + "dispatch_index_hex": "0x018", + "entry_address_hex": "H'28D6", + "selector": 24, + "selector_hex": "0x018", + "target": 11887, + "target_hex": "H'2E6F", + "target_label_or_hex": "H'2E6F" + }, + "name": "state_selector_candidate", + "reasons": [ + "selector dispatches to H'2E6F" + ], + "score": 2, + "seed_frames": [], + "selector": 24, + "selector_hex": "0x018", + "tables": [] + }, + { + "accesses": [], + "cmd1_read_frame": "01 00 1A 00 00 41", + "dispatch_target": { + "decoded_code": false, + "dispatch_index": 26, + "dispatch_index_hex": "0x01A", + "entry_address_hex": "H'28DA", + "selector": 26, + "selector_hex": "0x01A", + "target": 11972, + "target_hex": "H'2EC4", + "target_label_or_hex": "H'2EC4" + }, + "name": "state_selector_candidate", + "reasons": [ + "selector dispatches to H'2EC4" + ], + "score": 2, + "seed_frames": [], + "selector": 26, + "selector_hex": "0x01A", + "tables": [] + }, + { + "accesses": [], + "cmd1_read_frame": "01 00 24 00 00 7F", + "dispatch_target": { + "decoded_code": false, + "dispatch_index": 36, + "dispatch_index_hex": "0x024", + "entry_address_hex": "H'28EE", + "selector": 36, + "selector_hex": "0x024", + "target": 12044, + "target_hex": "H'2F0C", + "target_label_or_hex": "H'2F0C" + }, + "name": "state_selector_candidate", + "reasons": [ + "selector dispatches to H'2F0C" + ], + "score": 2, + "seed_frames": [], + "selector": 36, + "selector_hex": "0x024", + "tables": [] + }, + { + "accesses": [], + "cmd1_read_frame": "01 00 25 00 00 7E", + "dispatch_target": { + "decoded_code": false, + "dispatch_index": 37, + "dispatch_index_hex": "0x025", + "entry_address_hex": "H'28F0", + "selector": 37, + "selector_hex": "0x025", + "target": 12060, + "target_hex": "H'2F1C", + "target_label_or_hex": "H'2F1C" + }, + "name": "state_selector_candidate", + "reasons": [ + "selector dispatches to H'2F1C" + ], + "score": 2, + "seed_frames": [], + "selector": 37, + "selector_hex": "0x025", + "tables": [] + }, + { + "accesses": [], + "cmd1_read_frame": "01 00 43 00 00 18", + "dispatch_target": { + "decoded_code": false, + "dispatch_index": 67, + "dispatch_index_hex": "0x043", + "entry_address_hex": "H'292C", + "selector": 67, + "selector_hex": "0x043", + "target": 12106, + "target_hex": "H'2F4A", + "target_label_or_hex": "H'2F4A" + }, + "name": "state_selector_candidate", + "reasons": [ + "selector dispatches to H'2F4A" + ], + "score": 2, + "seed_frames": [], + "selector": 67, + "selector_hex": "0x043", + "tables": [] + }, + { + "accesses": [], + "cmd1_read_frame": "01 00 4A 00 00 11", + "dispatch_target": { + "decoded_code": false, + "dispatch_index": 74, + "dispatch_index_hex": "0x04A", + "entry_address_hex": "H'293A", + "selector": 74, + "selector_hex": "0x04A", + "target": 12124, + "target_hex": "H'2F5C", + "target_label_or_hex": "H'2F5C" + }, + "name": "state_selector_candidate", + "reasons": [ + "selector dispatches to H'2F5C" + ], + "score": 2, + "seed_frames": [], + "selector": 74, + "selector_hex": "0x04A", + "tables": [] + }, + { + "accesses": [], + "cmd1_read_frame": "01 00 4E 00 00 15", + "dispatch_target": { + "decoded_code": false, + "dispatch_index": 78, + "dispatch_index_hex": "0x04E", + "entry_address_hex": "H'2942", + "selector": 78, + "selector_hex": "0x04E", + "target": 12124, + "target_hex": "H'2F5C", + "target_label_or_hex": "H'2F5C" + }, + "name": "state_selector_candidate", + "reasons": [ + "selector dispatches to H'2F5C" + ], + "score": 2, + "seed_frames": [], + "selector": 78, + "selector_hex": "0x04E", + "tables": [] + }, + { + "accesses": [], + "cmd1_read_frame": "01 00 52 00 00 09", + "dispatch_target": { + "decoded_code": false, + "dispatch_index": 82, + "dispatch_index_hex": "0x052", + "entry_address_hex": "H'294A", + "selector": 82, + "selector_hex": "0x052", + "target": 12124, + "target_hex": "H'2F5C", + "target_label_or_hex": "H'2F5C" + }, + "name": "state_selector_candidate", + "reasons": [ + "selector dispatches to H'2F5C" + ], + "score": 2, + "seed_frames": [], + "selector": 82, + "selector_hex": "0x052", + "tables": [] + }, + { + "accesses": [], + "cmd1_read_frame": "01 00 56 00 00 0D", + "dispatch_target": { + "decoded_code": false, + "dispatch_index": 86, + "dispatch_index_hex": "0x056", + "entry_address_hex": "H'2952", + "selector": 86, + "selector_hex": "0x056", + "target": 12124, + "target_hex": "H'2F5C", + "target_label_or_hex": "H'2F5C" + }, + "name": "state_selector_candidate", + "reasons": [ + "selector dispatches to H'2F5C" + ], + "score": 2, + "seed_frames": [], + "selector": 86, + "selector_hex": "0x056", + "tables": [] + }, + { + "accesses": [], + "cmd1_read_frame": "01 00 5A 00 00 01", + "dispatch_target": { + "decoded_code": false, + "dispatch_index": 90, + "dispatch_index_hex": "0x05A", + "entry_address_hex": "H'295A", + "selector": 90, + "selector_hex": "0x05A", + "target": 12124, + "target_hex": "H'2F5C", + "target_label_or_hex": "H'2F5C" + }, + "name": "state_selector_candidate", + "reasons": [ + "selector dispatches to H'2F5C" + ], + "score": 2, + "seed_frames": [], + "selector": 90, + "selector_hex": "0x05A", + "tables": [] + }, + { + "accesses": [], + "cmd1_read_frame": "01 00 5E 00 00 05", + "dispatch_target": { + "decoded_code": false, + "dispatch_index": 94, + "dispatch_index_hex": "0x05E", + "entry_address_hex": "H'2962", + "selector": 94, + "selector_hex": "0x05E", + "target": 12124, + "target_hex": "H'2F5C", + "target_label_or_hex": "H'2F5C" + }, + "name": "state_selector_candidate", + "reasons": [ + "selector dispatches to H'2F5C" + ], + "score": 2, + "seed_frames": [], + "selector": 94, + "selector_hex": "0x05E", + "tables": [] + }, + { + "accesses": [], + "cmd1_read_frame": "01 00 62 00 00 39", + "dispatch_target": { + "decoded_code": false, + "dispatch_index": 98, + "dispatch_index_hex": "0x062", + "entry_address_hex": "H'296A", + "selector": 98, + "selector_hex": "0x062", + "target": 12124, + "target_hex": "H'2F5C", + "target_label_or_hex": "H'2F5C" + }, + "name": "state_selector_candidate", + "reasons": [ + "selector dispatches to H'2F5C" + ], + "score": 2, + "seed_frames": [], + "selector": 98, + "selector_hex": "0x062", + "tables": [] + }, + { + "accesses": [], + "cmd1_read_frame": "01 00 66 00 00 3D", + "dispatch_target": { + "decoded_code": false, + "dispatch_index": 102, + "dispatch_index_hex": "0x066", + "entry_address_hex": "H'2972", + "selector": 102, + "selector_hex": "0x066", + "target": 12124, + "target_hex": "H'2F5C", + "target_label_or_hex": "H'2F5C" + }, + "name": "state_selector_candidate", + "reasons": [ + "selector dispatches to H'2F5C" + ], + "score": 2, + "seed_frames": [], + "selector": 102, + "selector_hex": "0x066", + "tables": [] + }, + { + "accesses": [], + "cmd1_read_frame": "01 00 6A 00 00 31", + "dispatch_target": { + "decoded_code": false, + "dispatch_index": 106, + "dispatch_index_hex": "0x06A", + "entry_address_hex": "H'297A", + "selector": 106, + "selector_hex": "0x06A", + "target": 12124, + "target_hex": "H'2F5C", + "target_label_or_hex": "H'2F5C" + }, + "name": "state_selector_candidate", + "reasons": [ + "selector dispatches to H'2F5C" + ], + "score": 2, + "seed_frames": [], + "selector": 106, + "selector_hex": "0x06A", + "tables": [] + } + ], + "summary": { + "candidate_count": 41, + "confidence": "medium", + "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." + }, + "table_model": [ + { + "access_count": 31, + "logical_range_hex": "H'E000-H'E3FF", + "name": "primary_value_table_candidate", + "static_selectors": [ + 0, + 2, + 3, + 35, + 64, + 129, + 146, + 147, + 167, + 183, + 185, + 246, + 272 + ], + "static_selectors_hex": [ + "0x000", + "0x002", + "0x003", + "0x023", + "0x040", + "0x081", + "0x092", + "0x093", + "0x0A7", + "0x0B7", + "0x0B9", + "0x0F6", + "0x110" + ] + }, + { + "access_count": 8, + "logical_range_hex": "H'E400-H'E7FF", + "name": "secondary_value_table_candidate", + "static_selectors": [], + "static_selectors_hex": [] + }, + { + "access_count": 14, + "logical_range_hex": "H'E800-H'EBFF", + "name": "current_value_table_candidate", + "static_selectors": [ + 0, + 3, + 64, + 129, + 146, + 246 + ], + "static_selectors_hex": [ + "0x000", + "0x003", + "0x040", + "0x081", + "0x092", + "0x0F6" + ] + }, + { + "access_count": 6, + "logical_range_hex": "H'EC00-H'EFFF", + "name": "flag_table_candidate", + "static_selectors": [ + 0 + ], + "static_selectors_hex": [ + "0x000" + ] + } + ] +} diff --git a/build/rom_ccu_seed_hints.txt b/build/rom_ccu_seed_hints.txt new file mode 100644 index 0000000..c9ba4ee --- /dev/null +++ b/build/rom_ccu_seed_hints.txt @@ -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. diff --git a/h8536/ccu_seed_hints.py b/h8536/ccu_seed_hints.py new file mode 100644 index 0000000..d4f0ddd --- /dev/null +++ b/h8536/ccu_seed_hints.py @@ -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 "" + 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", +] diff --git a/h8536_ccu_seed_hints.py b/h8536_ccu_seed_hints.py new file mode 100644 index 0000000..f392c53 --- /dev/null +++ b/h8536_ccu_seed_hints.py @@ -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()) diff --git a/tests/test_ccu_seed_hints.py b/tests/test_ccu_seed_hints.py new file mode 100644 index 0000000..04670cf --- /dev/null +++ b/tests/test_ccu_seed_hints.py @@ -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()