# PT2 Protocol Working Notes This document is the current working model for the serial protocol spoken by the Sony RCP-TX7 panel ROM. A later RCP manual mentions a "PT2 compatibility mode" for controlling the same CCU family this panel was made for. We are using "PT2" here as a practical label for this six-byte SCI1 protocol model. It is not yet a claim that every field name matches Sony's official PT2 terminology. Focused companion notes: - [PT2 Copy State Machine](pt2-copy-state-machine.md) - [PT2 Menu State Machine](pt2-menu-state-machine.md) - [PT2 Session Rhythm ROM Trace](pt2-session-rhythm-trace.md) - [PT2 Continuation Command Trace](pt2-continuation-command-trace.md) - [PT2 Report Aftermath ROM Trace](pt2-report-aftermath-trace.md) - [PT2 Shutter Display Trace](pt2-shutter-display-trace.md) - [PT2 Button Report Bench Plan](pt2-button-report-bench-plan.md) ## Current High-Confidence Facts - The real bench link is `38400 8E1`, not `38400 8N1`. - The ROM uses H8/536 SCI1 through the MAX202 RS232 transceiver. - Frames are six bytes long. - The checksum is `0x5A XOR byte0 XOR byte1 XOR byte2 XOR byte3 XOR byte4`. - The ROM validates the checksum before normal command dispatch. - The first byte is decoded as `command = byte0 & 0x07`. - Bytes 1 and 2 encode a logical selector. - Bytes 3 and 4 are a 16-bit value for write-style commands. - The protocol is stateful. Some commands only work while a continuation/session latch is live. - `CONNECT:NOT ACT` is recoverable without a power cycle. - `CONNECT: OK` can be reached on real hardware after using the correct `8E1` serial format. ## Hardware Path SCI1 is the external serial path: - H8/536 pin 66, `P95/TXD`, goes to MAX202 pin 11. - MAX202 pin 12 goes to H8/536 pin 67, `P96/RXD`. - The ROM initializes SCI1 as async 8-bit, even parity, 1 stop. ROM evidence: - `build/rom_decompiled.asm:437`: `SCI1_SMR = H'24`. - `build/rom_decompiled.asm:438`: `SCI1_SCR = H'3C`. - `build/rom_decompiled.asm:439`: `SCI1_BRR = H'07`. Bench implication: - Use `38400 8E1` for all real-device captures and probes. - Old `38400 8N1` captures mostly exercised parity/error handling and retry echoes. Do not assign normal protocol meaning to old `8N1` `07...` frames until they are reproduced under `8E1`. ## Frame Format Working host/RCP frame layout: ```text byte0 command byte; ROM uses byte0 & 0x07 byte1 selector page/high bits; byte1.7 is rejected by normal handlers byte2 selector low byte byte3 value high byte byte4 value low byte byte5 checksum = 0x5A XOR byte0..byte4 ``` Examples: ```text 00 00 00 80 80 5A ; command 0, selector 0x000, value 0x8080 01 00 00 00 00 5B ; command 1, read selector 0x000 04 00 00 80 00 DE ; command 4 shape, selector 0x000, value high 0x80 07 00 00 00 00 5D ; command 7, repeat previous finalized TX frame ``` ## Selector Decode The ROM builds a raw selector from bytes 1 and 2, then maps it through `loc_622B`. Known decode: | byte1 page | byte2 range | selector | | --- | --- | --- | | page 0, or pages 4-7 | `00-7F` | `0x000 + byte2` | | page 1 | `00-FF` | `0x080 + byte2` | | page 2 | `00-7F` | `0x180 + byte2` | | page 3 | any | `0x1FF` fallback | | invalid range | any | `0x1FF` fallback | Important caveats: - `byte1.7` is rejected before normal command handling. - Frames like `01 80 40 ...` may look like a selector encoding, but the normal command path rejects `byte1.7`. - Pages 4-7 appear to alias the page-0 path when the low byte is in range. ## Checksum Checksum formula: ```python checksum = 0x5A for b in frame[:5]: checksum ^= b ``` The ROM path: - RXI captures bytes into `F868-F86D`. - Main-loop processing copies them to `F860-F865`. - Physical error latch `FAA4.7` is checked before checksum dispatch. - Checksum mismatch enters the retry/error path. - Valid checksum clears retry counter `FAA6`, decodes selector, and dispatches on `byte0 & 0x07`. Key ROM areas: - RXI/ERI capture: `BB57`, `BB67`. - Validation and checksum: `BBAB-BBF0`. - Selector decode call: `BC01 -> 622B`. - Command dispatch: `BC08-BC67`. ## Command Model The biggest protocol lesson is that the command set has two modes: - Initial dispatcher: active while `FAA2 == 0`. - Continuation dispatcher: active while `FAA2 != 0`. That means command numbers are not globally meaningful. Command `4`, `5`, and `6` are continuation-path commands, not normal idle commands. | Command | Path | Current meaning | Response | | --- | --- | --- | --- | | `0x00` | initial | Set primary/current value, queue selector processing | Immediate `0x04` echo-style response | | `0x01` | initial | Read primary value table | Immediate `0x04` readback response | | `0x02` | initial | Quiet clear/no-op style command | No immediate response seen in ROM | | `0x04` | continuation | Set/update value without immediate response | Usually no immediate response | | `0x05` | continuation | ACK/session-clear/pending handling | Usually no immediate response | | `0x06` | continuation | Set secondary value table | Usually no immediate response | | `0x07` | both | Retransmit previous finalized TX frame | Repeats last TX frame | ## Command Details ### Command 0: Initial Set Value Path: `BC69`. Conditions: - Valid checksum. - `FAA2 == 0`. - `byte1.7 == 0`. Effects: - Writes value into `E000 + 2*selector`. - Writes value into `E800 + 2*selector`. - Sets dirty flag bit 7 in `EC00 + selector`. - Calls `BE70`, which appends the selector into the `F970` selector-processing queue. - Sends an immediate `0x04` response through `BA26`. Selector-zero special case: - For selector `0x000`, the low byte is forced to `0x80`. - This makes selector-zero writes look like `xx80`, not arbitrary `xxLL`. Important candidate: ```text 00 00 00 80 80 5A ; selector 0 = 0x8080, strongest CONNECT OK seed ``` ### Command 1: Read Value Path: `BCD7`. Effects: - Reads `E000 + 2*selector`. - Stages a `0x04` response. - Clears `FAA2.7`. Useful readback examples: ```text 01 00 00 00 00 5B ; read selector 0x000 01 00 40 00 00 1B ; read selector 0x040 01 01 76 00 00 2C ; read selector 0x0F6 ``` Bench implication: - Command 1 verifies table state. - It is not an ACK and does not enter continuation handling. - Command 7 after command 1 can repeat the last finalized readback. ### Command 2: Initial Clear/No-Op Candidate Path: `BD04`. Effects: - Clears `FAA2.7`. - Returns without staging an obvious response. Meaning is still unclear. Treat it as a quiet/session-control candidate, not as a data write. ### Command 4: Continuation Set Value Path: `BD0E`. Conditions: - Valid checksum. - `FAA2 != 0`. - Command bit 2 set. - `byte1.7 == 0`. Effects: - Writes value into `E000 + 2*selector`. - Selector zero also updates `E800`. - Nonzero selectors set dirty flag bit 7 in `EC00 + selector`. - Can mirror/persist mapped nonzero selectors through the `F400`/EEPROM path when `F76E.7` allows it. - If `FAA2.3` was set by a queued report, command 4 can advance `F9B5` to consume that report. - Clears `FAA3` and `FAA2` before exit. Known CONNECT test frames: ```text 04 00 00 40 00 1E 04 00 00 80 00 DE 04 00 00 C0 00 9E ``` ROM caveat: - A standalone command 4 from a truly idle `FAA2 == 0` state should not reach `BD0E`. - Bench evidence now proves the panel can still recover from `CONNECT:NOT ACT` without power cycling, so visible `NOT ACT` is not equivalent to "all serial continuation state is impossible". ### Command 5: Continuation ACK/Clear Candidate Path: `BD80`. Conditions: - Continuation path only. Effects: - Usually no immediate response. - Selectors `0x006C`, `0x006D`, and `0x006E` call `BE70`. - If `F731.7` is set, selectors `0x006B`, `0x0096`, `0x0097`, `0x00C6`, and `0x00F8` clear `F731.7/F790.7`. - If `FAA2.3` was set by a queued report, command 5 can advance `F9B5`. - Clears `FAA3` and `FAA2` before exit. Bench implication: - Command 5 is not a generic always-live ACK. - It only has ACK-like meaning when the continuation latch is live. ### Command 6: Continuation Secondary Set Path: `BDDB`. Effects: - Writes value into `E400 + 2*selector`. - Sets dirty flag bit 6 in `EC00 + selector`. - Can advance queued-report state when `FAA2.3` is live. - Clears `FAA3` and `FAA2`. ### Command 7: Repeat Previous TX Path: `BE05`. Effects: - Copies the previous finalized TX frame back into staging. - Sends it again through `BA26`. - Works from initial and continuation paths. Bench implication: - Command 7 is useful as a "what did you last finalize?" probe. - It does not prove a hidden continuation token by itself. ## RCP Transmit Frames The TX side uses the same six-byte checksum model. TX staging: - `F850-F854`: staging bytes. - `F858-F85C`: finalized bytes. - `F85D`: computed checksum. - `BA26`: finalizes and starts SCI1 TX. - TXI sends bytes 1-5 after the first TDR write. Known RCP-origin frames: | Frame | Confidence | Current meaning | | --- | --- | --- | | `00 00 00 00 80 DA` | high | idle heartbeat / selector-zero report | | `00 00 07 80 00 DD` | medium-high | observed CAM POWER button/report candidate | | `00 00 15 80 00 CF` | medium-high | observed CALL on/report candidate | | `00 00 15 00 00 4F` | medium-high | observed CALL off/report candidate | | `02 00 02 00 00 5A` | medium | emulator CONNECT OK path response candidate | Heartbeat: - Idle frame: `00 00 00 00 80 DA`. - Observed cadence: about 700 ms. - ROM path: `loc_4067` enqueues selector 0, `loc_BAF2/BB08` dequeues it, `BB1C/BB20/BB2B` stages TX bytes, `BA26` emits the frame. - FRT2 timing model: `TCR=H'02`, `OCRA=H'7A12`, modeled as a 100 ms tick at 10 MHz; `F9C4=0x07` gives about 700 ms post-send heartbeat delay. ## Retry/Error 07 Frames `07...` frames are easy to misread. The ROM can generate a `0x07` retry/error echo when: - A physical RX error occurs, or - A checksum mismatch occurs, and - `FAA5.7` is set, and - Retry count `FAA6` is below two. Path: - `BE29` retry gate. - `BE4D` stages `F850=0x07`. - `F851-F854` copy host `RX[1:4]`. - `BA26` sends it. Bench implication: - A visible `07...` frame is not automatically a normal status report or ACK. - Old `8N1` captures produced many misleading `07...` frames because parity errors exercised this path. ## Table Model The ROM behaves like a selector-indexed state machine. The CCU likely seeds values, and the RCP updates LCD/lamp/control behavior from those values. | Table | Range | Role | | --- | --- | --- | | Primary value table | `E000-E3FF` | Command 0/4 writes, command 1 reads | | Secondary value table | `E400-E7FF` | Command 6 writes | | Current/report table | `E800-EBFF` | Used when RCP builds outbound report frames | | Dirty/flag table | `EC00-EFFF` | Per-selector flags, bit7 for primary writes, bit6 for secondary writes | | EEPROM/shadow | `F400-F4FF` | Optional mapped persistence/config surface | Important details: - Selector zero is special in command 0 and command 4. - Command 0 writes both `E000` and `E800`. - Command 4 writes `E800` only for selector zero in the current ROM evidence. - `BAF2` reads `E800 + 2*selector` when building autonomous RCP reports. - `BE70/F970` is a selector-processing queue. - `3E54/F870` is a separate serial-visible report queue. Do not mix up: - `F970`: "process this selector internally". - `F870`: "send this selector/report over serial". ## State And Queues Important RAM/state bytes: | Address | Working name | Meaning | | --- | --- | --- | | `FAA2.7` | RX command in progress | Set on initial parse, cleared on exits | | `FAA2.3` | queued report continuation needed | Set after autonomous report send | | `FAA3.7` | pending resend mask | Set after queued report send | | `FAA4.7` | RX physical error latch | Set by SCI1 ERI | | `FAA5.7` | RX session gate | Set while `F9C5` is alive after complete RX | | `FAA6` | retry counter | Limits retry/error echoes | | `F9C1` | inter-byte timeout | Reloaded on RXI | | `F9C3` | RX byte count | Counts up to six | | `F9C4` | heartbeat/report cadence gate | Controls idle heartbeat enqueue | | `F9C5` | RX/session timeout | Loaded with `0x14` after full RX frame | | `F9B0/F9B5` | serial report queue cursors | Drive `F870`/`BAF2` | | `F9B4/F9B9` | selector-processing queue cursors | Drive `F970`/`2806` | Session expiry: - A complete six-byte RX frame loads `F9C5=0x14`. - FRT2 decrements `F9C5`. - When `F9C5` reaches zero, `loc_3FEF` can clear queues/session state. - If `FAA5.7` was set, expiry calls `loc_400C`. - `loc_400C` clears connection/session RAM and refreshes inactive display state. This explains why random traffic tends to settle back to `CONNECT:NOT ACT`. ## CONNECT State `CONNECT` strings are built through the LCD driver, not received as literal serial text. Known LCD path: ```text FAF0-FAFF line buffer -> 3ECC -> 3F28 -> 3F40 -> F200/F201 LCD ports ``` ROM/emulator findings: - Boot/no-active-session display can show `CONNECT:NOT ACT`. - Direct emulator entry at `loc_2CB9` with `E000[0]=0x8080` and `F730=0` reaches `CONNECT: OK`. - Queued selector-zero path reaches OK when: - `F970[0]=0` - `F9B9=0` - `F9B4=1` - `E000[0]=0x8080` - `F730=0` - Selector zero dispatches through the `28A6` jump table into the CONNECT handler window. Bench findings: - Correct `8E1` serial format made the CONNECT path work on real hardware. - Real hardware can recover from `CONNECT:NOT ACT` to `CONNECT: OK` without a power cycle. - Successful active-looking state included: - `CONNECT: OK` - CAM POWER lamp illuminated - numeric readouts illuminated as `----` - Matrix tests show that cadence matters: - `40 -> 80 -> C0` with 10 ms, 50 ms, or 150 ms inter-frame gaps stayed at `CONNECT:NOT ACT` after a fresh power-cycle test. - The same order with 700 ms and 1.5 s inter-frame gaps produced `CONNECT: OK` before falling back to `CONNECT:NOT ACT`. - At 700 ms gaps, no single frame worked by itself. - At 700 ms gaps, every tested two-frame pair worked: `40 -> 80`, `80 -> C0`, and `40 -> C0`. - A repeated identical pair also worked: `80 -> 80` at about 700 ms produced eight `02 00 02 00 00 5A` OK-path responses, then heartbeat traffic resumed. - The no-power-cycle recovery test from an already visible `CONNECT:NOT ACT` state produced repeated `02 00 02 00 00 5A` OK-path responses, then returned to heartbeat traffic. Current interpretation: - `CONNECT:NOT ACT` is a normal no-active-session/cleared-state display, not a terminal latch. - `CONNECT: OK` is table/state driven, probably selector-zero active/connect state. - `0x8080` at selector zero is the strongest known active/connect value. - The panel likely expects the CCU to consume/ACK report-queue frames and keep seeding or refreshing state after entering OK. - The working fake-CCU sequence is probably not "three frames as fast as possible"; it appears to need CCU-like cadence or a live session window, roughly on the heartbeat/report timescale. - A single selector-zero continuation-shaped frame is insufficient in the current tests; two selector-zero writes at the working cadence are enough. They do not need to carry different values, because `80 -> 80` also worked. ROM report-source update: - The active `02/01 ...` frames seen during CONNECT OK attempts are best modeled as `F870 -> BAF2 -> BA26` report-queue transmissions. - `BAF2` dequeues a report word, encodes the first three TX bytes, reads the payload from `E800 + 2*selector`, and `BA26` appends the `0x5A` XOR checksum. - After sending a queued report, the ROM sets `FAA2.3` and `FAA3.7`; command `4`, `5`, or `6` can then consume/advance the report only while that continuation latch is live. - The emitted report does not appear to encode a required ACK selector. The ROM consume test is `FAA2.3`, so a generic command-5 continuation ACK such as `05 00 40 00 00 1F` is the cleanest report-cursor consume candidate. - Low commands `0/1/2` during the report wait can clear the continuation latch and re-enter initial dispatch, but they do not advance `F9B5`. Command `7` retransmits and also does not advance `F9B5`. - Emulator testing now supports the reactive ACK model: waiting for a finished RCP report frame, then sending `05 00 40 00 00 1F`, repeatedly advanced `F9B5`, cleared `FAA2/FAA3`, and kept the emulated LCD at `CONNECT: OK`. - This makes a reactive fake-CCU test more valuable than another blind fixed-delay matrix: recover to OK, wait for the first active report, then send one candidate continuation/ACK frame. ## Candidate CCU Seed Values These are syntactically valid host frames produced from ROM table mining. Use them as candidate fake-CCU state seeds, not as final protocol truth. | Selector | Candidate value | Frame | Why it matters | | --- | --- | --- | --- | | `0x000` | `0x8080` | `00 00 00 80 80 5A` | strongest CONNECT OK seed | | `0x003` | `0x8000` | `00 00 03 80 00 D9` | ROM default enabled bit candidate | | `0x040` | `0xFFFF` | `00 00 40 FF FF 1A` | ROM default all-ones/status block candidate | | `0x040` | `0x4030` | `00 00 40 40 30 6A` | bench-touched 0x40 family value | | `0x0F6` | `0x2000` | `00 01 76 20 00 0D` | `loc_48FA` tests `E1EC.13` and can enqueue report `0x00F6` | Readbacks: ```text 01 00 00 00 00 5B ; selector 0x000 01 00 03 00 00 58 ; selector 0x003 01 00 40 00 00 1B ; selector 0x040 01 01 76 00 00 2C ; selector 0x0F6 ``` ## Observed Button/Panel Reports Before the protocol format was corrected, the RCP appeared to emit only a few report families by itself: ```text 00 00 00 00 80 DA ; heartbeat 00 00 07 80 00 DD ; CAM POWER candidate 00 00 15 80 00 CF ; CALL on candidate 00 00 15 00 00 4F ; CALL off candidate ``` Current interpretation: - The RCP can report some panel events. - Many other controls probably need CCU-provided state before they become reportable or meaningful. - The CCU likely streams display/lamp/readout state to the RCP, while the RCP reports operator changes back. ## EEPROM And Board Config The P9 bus is not the external PT2 serial link. It is a bit-banged EEPROM/config path: - H8 pin 62, `P91`, reaches X24164 pin 6 `SCL`. - H8 pin 68, `P97`, reaches shared X24164 pin 5 `SDA`. ROM findings: - `loc_40BB` checks `P7DR.7` and `F402 == H'6B6F` before deciding whether to default EEPROM/shadow tables. - `loc_4103` writes ROM default words through `BFE0`. - `loc_41D2` reads sixteen 8-byte records into `F7B0-F82F`. - Command 4 can persist mapped serial table writes when `F76E.7` is set. Current interpretation: - EEPROM stores panel/config/default state. - It can affect startup and option behavior. - After the `8E1` discovery, EEPROM is less likely to be the fundamental reason CONNECT failed, but it can still influence which selectors/features are active. ## Known Useful Bench Commands Minimal CONNECT sequence runner: ```powershell .\.venv\Scripts\python.exe scripts\bench_connect_lcd_sequence.py --port COM5 --relay-port COM6 --parity E --prompt-screen ``` Recover from `CONNECT:NOT ACT` without power cycling: ```powershell .\.venv\Scripts\python.exe scripts\bench_connect_lcd_sequence.py --port COM5 --relay-port COM6 --no-power-cycle --parity E --prompt-before-send --prompt-screen --post-sequence-read 10 --log captures\connect-notact-to-ok.txt ``` Run the reproducibility/minimization matrix: ```powershell .\.venv\Scripts\python.exe scripts\connect_ok_matrix.py --suite minimal --parity E --prompt-observation --result-json captures\connect-ok-minimal-result.json ``` Test timing/cadence: ```powershell .\.venv\Scripts\python.exe scripts\connect_ok_matrix.py --suite gap --parity E --prompt-observation --result-json captures\connect-ok-gap-result.json ``` Test whether OK is held: ```powershell .\.venv\Scripts\python.exe scripts\connect_ok_matrix.py --suite hold --parity E --prompt-observation --result-json captures\connect-ok-hold-result.json ``` Sweep strong continuation/ACK candidates after recovering to `CONNECT: OK`: ```powershell .\.venv\Scripts\python.exe scripts\connect_ok_advance_sweep.py --suite core --parity E --prompt-observation --result-json captures\connect-ok-advance-core-result.json ``` Candidate suites: - `core`: `05 00 40 00 00 1F` pure command-5 report-consume candidate, then `04 00 00 80 00 DE` selector-zero refresh/consume candidate. - `special`: command-5 `0x006C/0x006D/0x006E`, which call the ROM's `BE70` queue helper. - `latch`: command-5 `0x006B/0x0096/0x0097/0x00C6/0x00F8`, which can clear the `F731/F790` latch bits when that path is live. - `all`: all of the above. Current matrix result summary: ```text minimal suite at 150 ms gaps: all cases stayed CONNECT NOT ACT gap suite at 10/50/150 ms: stayed CONNECT NOT ACT gap suite at 700 ms and 1.5 s: CONNECT OK, then CONNECT NOT ACT minimal suite at 700 ms gaps: singles stayed CONNECT NOT ACT; all pairs reached CONNECT OK then CONNECT NOT ACT repeated 80 -> 80 at about 700 ms: CONNECT OK responses, then CONNECT NOT ACT hold suite at 150 ms gaps: stayed CONNECT NOT ACT no-power-cycle NOT ACT recovery: CONNECT OK responses observed, then heartbeat resumes ``` Advance sweep result summary: ```text core suite: no new LCD behavior all suite, ack-006C (05 00 6C 00 00 33): CONNECT OK, then LCD blank while CAM POWER lamp stayed on all suite, ack-006D (05 00 6D 00 00 32): CONNECT OK, then COPY IN PROGRESS, then CONNECT NOT ACT all suite, ack-006E/latch candidates: mostly OK then NOT ACT fresh isolated ack-006D run: COPY path reproduced after a relay power-cycle ``` The `0x006D` copy path is now confirmed outside the earlier all-suite ordering caveat: `captures/connect-ok-advance-special-20260526-153339.txt` power-cycled the panel, waited for heartbeats, sent the two-frame `0x8080` CONNECT baseline, then sent only `05 00 6D 00 00 32`; the LCD again entered the copy path. The ROM strongly supports that these are real selector side effects: - Command 5 compares `R5` against `0x006C`, `0x006D`, and `0x006E`; those selectors call `BE70` to append the selector to the `F970` processing queue. - Selector `0x006C` dispatches to `H'2FAF`. Forced decoding shows it manipulates `F76E`, `F795`, `F797`, `F799`, can set display selector `F732=H'1904`, sets `FB02=H'14`, and calls the `48FA` display/report bridge. - Selector `0x006D` dispatches to `H'3015`. Forced decoding shows it sets `F731.7`, loads `F798=H'C8`, sets `F795.6/F795.7`, sets display selector `F732=H'1903`, sets `FB02=H'64`, calls `48FA`, then sets `F76E.6`. - The LCD dispatch for these states is now traced: `loc_48FA` reads the high byte at `F732`, so `F732=H'1903/H'1904` selects display page `0x19`, not direct page `0x03/0x04`. `493E[0x19] -> H'930A`; that page's local table at `H'931C` includes `H'9F6A` for `COPY` / `IN PROGRESS` and `H'9FDA` for `COPY` / `COMPLETED`. The low byte at `F733` is the substate selector: `0x03` is in-progress and `0x04` is completed. - This makes the likely copy handshake: `0x006D` starts copy and sets the `F795.6/F795.7` in-progress flags; `0x006C` is the completion/exit sibling only when those flags are live. Sending `0x006C` alone can therefore blank or clear state instead of displaying `COPY COMPLETED`. - Bench step-through confirmed the sequence model: `006C` alone produced `CONNECT OK -> blank`; `006D` alone produced `CONNECT OK -> COPY IN PROGRESS`; `006D` followed by `006C` after 250 ms produced a brief `COPY IN PROGRESS` then `COPY COMPLETED`; the same after 1.0 s and 1.5 s produced a longer `COPY IN PROGRESS` then `COPY COMPLETED`; after 2.0 s or 2.5 s it fell to `CONNECT:NOT ACT` instead of completing. Repeating `006D` before `006C` also completed successfully in the 2x and 3x repeat tests. A longer `006D` hold test kept `COPY IN PROGRESS` active for several seconds and then completed when `006C` arrived, while the same hold without `006C` timed out from `COPY IN PROGRESS` to `CONNECT:NOT ACT`. This points to `006D` as an in-progress/progress-window refresh selector and `006C` as the explicit completion/exit selector, not a stateless command pair. - The FRT1 timer path decrements `F797` and `F798`; when either reaches zero, it clears `F731.7`. This matches the observed transient display modes falling back to `CONNECT:NOT ACT`. - The string `COPY IN PROGRESS` is present in the ROM LCD resources, so the `006D` result is not a generic serial artifact. - Manual interpretation: the RCP-TX7 operating manual describes `COPY IN PROGRESS` as the LCD state shown during the multi-camera `COPY TO SLAVES` data-transfer operation over the RS232C command-link system. During that state, all linked RCP units display the message and their buttons/knobs are locked until `COPY COMPLETED`. Therefore selector `0x006D` is best treated as entering a command-link copy/data-transfer state, not as a normal CCU connection ACK. RCP-side OTHERS/COPY menu trace: - The OTHERS menu is page `0x01`: `493E[0x01] -> H'631C`, local table `H'632E`. - Local table entry 1 points to `H'6FF0`, the page that renders `OTHERS` / `COPY TO SLAVES`. - The entry descriptor immediately before `H'6FF0` requires selector `0x0015` in the secondary table: `E400[0x0015] != 0`. Because command 6 writes `E400`, this is probably a CCU-provided feature/visibility bit, and command 6 must be sent in a live continuation/report window to have an effect. - The root OTHERS/SHUTTER page `H'6EE4` consumes `F770.0/F770.1`; the COPY page `H'6FF0` consumes `F770.2`. Both handlers read `F770`, clear it, and then act on those low bits. - The only decoded direct writers to `F770` found so far write high bits `0x80`, `0x40`, and `0x20` from value/dial redraw paths. No direct decoded write to `F770=0x01/0x02/0x04` has been found yet, so the physical OTHERS/COPY action probably arrives through an indirect panel-key latch or page-local mechanism. - When the page sees `F770.2` set, it only follows the local copy-start branch if `F791.7` is already set. That branch sets `F76E.6`, `F795.7`, `F731.7`, `F798=H'C8`, `F711.7`, `F726=H'64`, calls `loc_5500`, then displays `COPY TO SLAVES`. This is the RCP-side equivalent of the serial `0x006D` copy-start effect. - If `F770.2` is set while `F791.7` is clear, the ROM diverts through `H'704C` to a `SET RCP` / `MASTER` display path instead of starting copy. That makes `F791.7` a second, likely master/link/session gate for the physical COPY operation. - The OTHERS root handler at `H'6EE4` also tests primary selector `E000[0x008F]` (`H'E11E`) bits 11 and 12, and uses them to set `F711.6` and `F711.4`. Bench isolation shows this selector is broader than an OTHERS gate: bit 11 makes the SHUTTER seven-segment display show observed `EUS`, probably the manual's `EVS` display, and bit 12 makes it show the literal letters `OFF`; either bit also illuminates the iris AUTO lamp. - Candidate gate probes, not final protocol truth: ```text 00 01 0F 18 00 4C ; command 0, set E000[0x008F] bits 11+12 06 00 15 00 01 48 ; command 6, set E400[0x0015] nonzero; requires live continuation 06 00 15 80 00 C9 ; command 6, alternate nonzero visibility value; requires live continuation ``` Read table state: ```powershell .\.venv\Scripts\python.exe scripts\serial_table_dump.py --port COM5 --relay-port COM6 --start 0x000 --count 0x200 --parity E --log captures\table-read-8e1.txt ``` ## Current Best Model Of Normal CCU/RCP Communication The protocol looks like a shared selector table with stateful reporting: 1. CCU sends initial state seeds into selector tables, especially selector zero and status/display selectors. 2. RCP updates LCD, lamps, and numeric readouts from selector dispatch handlers. 3. RCP emits heartbeat/report frames from `E800` via the `F870 -> BAF2` report queue. 4. Host/CCU uses continuation commands to consume/ACK/update live reports while `FAA2/FAA3` gates are active. 5. If the CCU stops talking or session state expires, RCP clears volatile session state and returns to `CONNECT:NOT ACT`. This fits the real panel behavior: - Idle panel emits heartbeat. - Correct fake-CCU traffic can wake it to `CONNECT: OK`. - Without richer CCU state, readouts illuminate but show placeholders like `----`. ## Active-State Local-Control Watch Bench evidence now suggests `CONNECT: OK` is maintained by an ongoing CCU refresh stream, not by one magic wake frame. This creates a useful local-control test: keep the RCP in the active state and press/turn physical controls while logging device TX. Two JSON scenarios are set up for that: ```powershell .\.venv\Scripts\python.exe scripts\serial_scenario.py scenarios\active-control-report-watch-quiet.json --parity E --log captures\active-control-report-watch-quiet.txt --result-json captures\active-control-report-watch-quiet-result.json .\.venv\Scripts\python.exe scripts\serial_scenario.py scenarios\active-control-report-watch-broad.json --parity E --log captures\active-control-report-watch-broad.txt --result-json captures\active-control-report-watch-broad-result.json .\.venv\Scripts\python.exe scripts\serial_scenario.py scenarios\active-control-report-watch-gated.json --parity E --log captures\active-control-report-watch-gated.txt --result-json captures\active-control-report-watch-gated-result.json ``` The quiet scenario sends the selector-zero OK seed, then refreshes `E000[0x0093]=0x9020` every 0.60 s. This is the lowest-noise active hold that has already kept `CONNECT: OK` alive. The broad scenario streams `E000[0x008F]=0x1800` and `E000[0x0093]=0xFFFF` every cycle. This is noisier, but may open more local-control/status gates. The gated scenario first seeds candidate secondary-table feature bits with command 6: ```text 06 00 15 80 00 C9 ; E400[0x0015] OTHERS/COPY visibility candidate 06 01 0F 18 00 4A ; E400[0x008F] shutter/OTHERS bits 11+12 06 01 13 FF FF 4E ; E400[0x0093] broad local-report/status gate candidate ``` Then it streams `E000[0x008F]=0x1800` and `E000[0x0093]=0x90FF`. This is the better next watch if quiet/broad E000-only refreshes do not produce local-control TX. After a run, summarize unexpected device frames with: ```powershell .\.venv\Scripts\python.exe scripts\serial_scenario_unexpected.py captures\active-control-report-watch-quiet.txt --show-all .\.venv\Scripts\python.exe scripts\serial_scenario_unexpected.py captures\active-control-report-watch-broad.txt --show-all .\.venv\Scripts\python.exe scripts\serial_scenario_unexpected.py captures\active-control-report-watch-gated.txt --show-all ``` Expected refresh responses are ignored by default: heartbeat, table readbacks, and `02 00 02 00 00 5A`. Any remaining checksum-valid frame is a candidate local-control report. This is where physical button/dial reports should appear if the RCP only talks while the CCU keeps the selector tables active. The unexpected-frame summarizer also labels known autonomous button frames so they are not mistaken for newly unlocked controls: ```text 00 00 15 80 00 CF ; known CALL active report 00 00 15 00 00 4F ; known CALL inactive report 00 00 07 80 00 DD ; known CAM POWER report ``` First quiet/broad watch captures on 2026-05-26 showed no unexpected checksum-valid frames in the observed window: ```text active-control-report-watch-quiet: 114 detected frames, all expected refresh/heartbeat/readback active-control-report-watch-broad: 133 detected frames, all expected refresh/heartbeat/readback ``` Those runs stopped mid-scenario, so this is not a full negative proof. The practical interpretation is narrower: E000 active-state refresh alone did not make the pressed controls emit visible TX in the watched window. The gated command-6 version is the next ROM-supported test. The first gated watch capture produced a new periodic active-state response: ```text 02 00 04 00 00 5C ; gated 0x0004 transition candidate, seen twice 01 00 04 00 00 5F ; gated 0x0004 active response candidate, seen 328 times ``` After treating those as expected gated refresh traffic, the capture had zero novel frames. A direct search found no known CALL/CAM POWER frames in that log. So the gated setup changed the session/status response shape, but it did not yet prove that physical controls beyond the already-known autonomous buttons are reporting. ## Lamp Selector Mapping Bench lamp sweeps now prove that several panel outputs are directly driven by command-0 selector writes while `CONNECT: OK` is alive. The current detailed map lives in `docs/pt2-lamp-selector-map.md`. Newest confirmed behavior: - `lamp-known-button-selector-probe` made CAM, CALL, BARS, MASTER, and camera tally outputs flash individually. - `0x0007 = 0x8000/0x0000` blinked CAM POWER in isolation. - `0x0015 = 0x8000/0x0000` blinked CALL and red tally in isolation. - Fresh-boot isolation maps `0x0013` to SLAVE, `0x0016` to green tally, and `0x0017` to BARS for the `0x8000/0x0000` value pair. - Fresh-boot isolation maps `0x0092` to iris AUTO/OFF behavior, and maps both `0x00B9` and `0x0110` to KNEE-related behavior. - A ROM trace now explains why the KNEE tests looked non-level-held. `loc_1795` is reached from the panel input lane (`F104 -> F692 -> F6F0.1`) and reads `E000[0x00B9]` plus `E000[0x0110]`. - The live KNEE value/report gate is `0x00B9.13`, not the earlier bench-only `0x00B9.15` guess. - `0x0110.15` forces a timed KNEE page/display override (`F732=0x1C03`, `FB02=0x14`), which matches the observed "lights, then clears" behavior. - On that KNEE LCD page, `0x0110.15` selects `DL`, `0x00B9.15` selects `PRESET`, and both clear selects `AUTO`. - When `0x00B9.13` is set and `0x0110.15` is clear, the ROM reports/updates selector `0x00BC` from the `F692 - F6B2` panel-input delta. See `docs/pt2-knee-rom-trace.md`. - The first KNEE ROM probe produced a new bench LCD state with `DTL` on the left and `KNEE` on the right, matching the ROM page-0x1C DETAIL/KNEE neighborhood. - A follow-up isolation lit the KNEE AUTO lamp in later KNEE windows but did not change the LCD. Current model: KNEE AUTO lamp/status is mostly selector driven, while the DETAIL/KNEE LCD page needs an additional local menu/display condition. ## What Is Still Unknown - The official PT2 names for commands and selectors. - Which selectors drive every lamp and numeric display. - Whether the CCU sends a periodic refresh stream after CONNECT OK. - Exact hold time before OK falls back to NOT ACT, if no refresh traffic follows. - Whether command 4 CONNECT success depends on an existing continuation latch, timing, or a side effect created by earlier frames. - Whether the remaining emulator/bench mismatch is an SCI timing bug or a missing ROM/peripheral behavior. The emulator now has an optional 8E1 TX wire-timing mode; with it enabled, repeated `80` no longer immediately reaches `CONNECT: OK`, which is closer to bench behavior but still needs calibration. - How EEPROM option bits change selector behavior. - Whether all old visible `07...` families can be reproduced under `8E1`. ## Next Best Refinements 1. Finish the CONNECT matrix runs: - rerun `hold` with 700 ms gaps to measure how long OK remains without refresh traffic. 2. Run the reactive advance sweep from OK and compare: - ACK-only `05 00 40 00 00 1F` - refresh/consume `04 00 00 80 00 DE` - command-5 special selectors `0x006C/0x006D/0x006E` 3. Test whether periodic report ACKs plus periodic `80` refreshes hold CONNECT OK. 4. Sweep emulator RX phase/gap with `--tx-wire-timing` and compare where `BD0E` is reached or missed. 5. Dump selector table state before and after CONNECT OK. 6. Seed selectors `0x003`, `0x040`, and `0x0F6` after selector-zero OK and watch lamps/readouts. 7. Mine selector dispatch handlers for known UI text terms: `IRIS`, `GAIN`, `SHUTTER`, `BARS`, `BLACK`, `CALL`, `AUTO`, `DIAG`. 8. Run the active-control watch scenarios and map any unexpected frames back to physical controls. ## Source Files And Reports Generated evidence: - `build/rom_decompiled.asm` - `build/rom_rx_branch_trace.txt` - `build/rom_ccu_seed_hints.txt` - `build/rom_eeprom_layout.txt` - `build/rom_table_xrefs.txt` - `build/connect-state-search-ok.json` Useful tools: - `h8536_protocol_trace.py` - `h8536_protocol_capture.py` - `h8536_rx_branch_trace.py` - `h8536_ccu_seed_hints.py` - `h8536_emulator_rx_probe.py` - `h8536_emulator_state_search.py` - `scripts/bench_connect_lcd_sequence.py` - `scripts/connect_ok_matrix.py` - `scripts/connect_ok_advance_sweep.py` - `scripts/serial_table_dump.py` - `scripts/serial_scenario.py`