Compare commits
2 Commits
21f0e455ee
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4ebbb963bd | ||
|
|
4364d0ed48 |
@@ -1820,7 +1820,7 @@
|
||||
]
|
||||
},
|
||||
"is_noop": false,
|
||||
"known_report": "0x0013",
|
||||
"known_report": "IRIS/M.BLACK LINK",
|
||||
"shadow": "F6DB",
|
||||
"source": "F006",
|
||||
"table_address": 10180,
|
||||
@@ -2881,6 +2881,38 @@
|
||||
"table_address_hex": "H'274C",
|
||||
"table_offset": 70
|
||||
},
|
||||
{
|
||||
"bank": "IRQ3 A8 panel byte path",
|
||||
"bit": 7,
|
||||
"dirty": "F6F3.3",
|
||||
"dispatcher": "1BF8",
|
||||
"handler": 8206,
|
||||
"handler_hex": "H'200E",
|
||||
"handler_summary": {
|
||||
"instruction_count_scanned": 20,
|
||||
"level_tests": [
|
||||
"F6DB.7"
|
||||
],
|
||||
"report_selectors": [
|
||||
19
|
||||
],
|
||||
"report_selectors_hex": [
|
||||
"0x0013"
|
||||
],
|
||||
"state_writes": [
|
||||
"E826"
|
||||
]
|
||||
},
|
||||
"is_noop": false,
|
||||
"known_report": "IRIS/M.BLACK LINK",
|
||||
"name": "IRIS/M.BLACK LINK",
|
||||
"selector": 19,
|
||||
"shadow": "F6DB",
|
||||
"source": "F006",
|
||||
"table_address": 10180,
|
||||
"table_address_hex": "H'27C4",
|
||||
"table_offset": 190
|
||||
},
|
||||
{
|
||||
"bank": "IRQ3 A8 panel byte path",
|
||||
"bit": 5,
|
||||
|
||||
@@ -15,6 +15,16 @@ The key table is the indirect handler table at `H'2706`, used by `loc_1C0E` afte
|
||||
- Current-level tests: F6D4.3
|
||||
- State writes: E80E
|
||||
|
||||
### IRIS/M.BLACK LINK
|
||||
|
||||
- Emitted selector: `0x0013`
|
||||
- Handler: `H'200E`
|
||||
- Edge source: `F006 -> F6DB` via `F6F3.3`
|
||||
- Trigger bit: `F6DB.7`
|
||||
- Table slot: `H'27C4` -> `H'200E`
|
||||
- Current-level tests: F6DB.7
|
||||
- State writes: E826
|
||||
|
||||
### CALL
|
||||
|
||||
- Emitted selector: `0x0015`
|
||||
|
||||
200
build/rom_0013_button_handler_linear.asm
Normal file
200
build/rom_0013_button_handler_linear.asm
Normal file
@@ -0,0 +1,200 @@
|
||||
; H8/536 ROM disassembly
|
||||
; input: ROM\M27C512@DIP28_1.BIN
|
||||
; bytes: 65536
|
||||
; vector mode: min
|
||||
; analysis: linear sweep
|
||||
;
|
||||
; Notes from the manual:
|
||||
; - H8/536 uses the H8/500 CPU instruction set.
|
||||
; - In minimum mode the reset vector at H'0000-H'0001 is a 16-bit PC.
|
||||
; - The register field is H'FE80-H'FFFF; names below come from appendix B.
|
||||
; - @aa:8 short absolute operands use BR as the upper address byte.
|
||||
; - SCI baud inference uses section 14.2.8 BRR formulas when SMR/BRR are known.
|
||||
; - LCD inference treats E-clock H'F200/H'F201 accesses as status/control and data candidates.
|
||||
|
||||
; Memory Map
|
||||
; H'0000-H'009F exception_vectors vectors
|
||||
; H'00A0-H'00FF dtc_vectors dtc_vectors
|
||||
; H'0100-H'F67F program_or_external program
|
||||
; H'F680-H'FE7F on_chip_ram ram
|
||||
; H'FE80-H'FFFF register_field registers
|
||||
|
||||
; Vectors
|
||||
; H'0000 reset -> vec_reset_1000 (H'1000)
|
||||
; H'0004 invalid_instruction -> vec_reset_1000 (H'1000)
|
||||
; H'0006 zero_divide -> vec_reset_1000 (H'1000)
|
||||
; H'0008 trap_vs -> vec_reset_1000 (H'1000)
|
||||
; H'0010 address_error -> vec_reset_1000 (H'1000)
|
||||
; H'0012 trace -> vec_reset_1000 (H'1000)
|
||||
; H'0016 nmi -> vec_nmi_4393 (H'4393)
|
||||
; H'0020 trapa_0 -> vec_reset_1000 (H'1000)
|
||||
; H'0022 trapa_1 -> vec_reset_1000 (H'1000)
|
||||
; H'0024 trapa_2 -> vec_reset_1000 (H'1000)
|
||||
; H'0026 trapa_3 -> vec_reset_1000 (H'1000)
|
||||
; H'0028 trapa_4 -> vec_reset_1000 (H'1000)
|
||||
; H'002A trapa_5 -> vec_reset_1000 (H'1000)
|
||||
; H'002C trapa_6 -> vec_reset_1000 (H'1000)
|
||||
; H'002E trapa_7 -> vec_reset_1000 (H'1000)
|
||||
; H'0030 trapa_8 -> vec_reset_1000 (H'1000)
|
||||
; H'0032 trapa_9 -> vec_reset_1000 (H'1000)
|
||||
; H'0034 trapa_a -> vec_reset_1000 (H'1000)
|
||||
; H'0036 trapa_b -> vec_reset_1000 (H'1000)
|
||||
; H'0038 trapa_c -> vec_reset_1000 (H'1000)
|
||||
; H'003A trapa_d -> vec_reset_1000 (H'1000)
|
||||
; H'003C trapa_e -> vec_reset_1000 (H'1000)
|
||||
; H'003E trapa_f -> vec_reset_1000 (H'1000)
|
||||
; H'0040 irq0 -> vec_reset_1000 (H'1000)
|
||||
; H'0042 interval_timer -> vec_interval_timer_BFC4 (H'BFC4)
|
||||
; H'0048 irq1 -> vec_reset_1000 (H'1000)
|
||||
; H'0050 irq2 -> vec_reset_1000 (H'1000)
|
||||
; H'0052 irq3 -> vec_irq3_3C30 (H'3C30)
|
||||
; H'0058 irq4 -> vec_irq4_3AC7 (H'3AC7)
|
||||
; H'005A irq5 -> vec_reset_1000 (H'1000)
|
||||
; H'0062 frt1_ocia -> vec_frt1_ocia_BEEA (H'BEEA)
|
||||
; H'006A frt2_ocia -> vec_frt2_ocia_BF23 (H'BF23)
|
||||
; H'0080 sci1_eri -> vec_sci1_eri_BB57 (H'BB57)
|
||||
; H'0082 sci1_rxi -> vec_sci1_rxi_BB67 (H'BB67)
|
||||
; H'0084 sci1_txi -> vec_sci1_txi_BA84 (H'BA84)
|
||||
; H'0090 ad_adi -> vec_ad_adi_3D99 (H'3D99)
|
||||
|
||||
; Symbols
|
||||
; mem_E026 H'E026 program_or_external memory r=4 w=0 width=word
|
||||
; mem_E02A H'E02A program_or_external memory r=1 w=0 width=word
|
||||
; mem_E046 H'E046 program_or_external memory r=0 w=1 width=word
|
||||
; mem_E110 H'E110 program_or_external memory r=1 w=0 width=word
|
||||
; mem_E134 H'E134 program_or_external memory r=1 w=0 width=word
|
||||
; mem_E824 H'E824 program_or_external memory r=0 w=1 width=word
|
||||
; mem_E826 H'E826 program_or_external memory r=0 w=4 width=word
|
||||
; mem_E82A H'E82A program_or_external memory r=0 w=1 width=word
|
||||
; mem_E8D6 H'E8D6 program_or_external memory r=0 w=1 width=word
|
||||
; ram_F6D4 H'F6D4 on_chip_ram ram r=1 w=0 width=byte
|
||||
; ram_F6DB H'F6DB on_chip_ram ram r=3 w=0 width=byte
|
||||
; ram_F713 H'F713 on_chip_ram ram r=2 w=2 width=byte
|
||||
; ram_F726 H'F726 on_chip_ram ram r=0 w=1 width=byte
|
||||
; ram_F730 H'F730 on_chip_ram ram r=1 w=0 width=byte
|
||||
; ram_F731 H'F731 on_chip_ram ram r=5 w=2 width=byte
|
||||
; ram_F732 H'F732 on_chip_ram ram r=0 w=1 width=word
|
||||
; ram_F76A H'F76A on_chip_ram ram r=0 w=1 width=word
|
||||
; ram_F76E H'F76E on_chip_ram ram r=1 w=1 width=byte
|
||||
; ram_F791 H'F791 on_chip_ram ram r=2 w=1 width=byte
|
||||
; ram_F798 H'F798 on_chip_ram ram r=0 w=2 width=byte
|
||||
; ram_FB03 H'FB03 on_chip_ram ram r=1 w=1 width=byte
|
||||
|
||||
; Board Profile
|
||||
; Board trace ties the H8/536 SCI1 pins to a MAX202 RS232 transceiver.
|
||||
; H8 pin 66 P95/TXD (TXD) -> MAX202 pin 11
|
||||
; H8 pin 67 P96/RXD (RXD) -> MAX202 pin 12
|
||||
; SCI2 pin routing is disabled by SYSCR2.P9SCI2E=0 in the observed setup.
|
||||
|
||||
; LCD/Text Scan
|
||||
; search 'CONNECT': not literal, hits=0
|
||||
|
||||
1FC0: F7 13 C5 1D ROTR.B @(H'13C5,R7)
|
||||
1FC4: E8 24 07 80 00 MOV:G.W #H'8000, @(H'24,R0)
|
||||
1FC9: 52 80 MOV:E.B #H'80, R2 ; dataflow R2=H'80
|
||||
1FCB: 5B 40 12 MOV:I.W #H'4012, R3 ; dataflow R3=H'4012
|
||||
1FCE: 1E 1E 83 BSR loc_3E54
|
||||
1FD1: 19 RTS
|
||||
1FD2: 15 F7 91 D7 BCLR.B #7, @H'F791 ; refs ram_F791 in on_chip_ram
|
||||
1FD6: 15 F7 13 D5 BCLR.B #5, @H'F713 ; refs ram_F713 in on_chip_ram
|
||||
1FDA: 1D E8 24 06 00 MOV:G.W #H'00, @H'E824 ; refs mem_E824 in program_or_external
|
||||
1FDF: 52 80 MOV:E.B #H'80, R2 ; dataflow R2=H'80
|
||||
1FE1: 5B 40 12 MOV:I.W #H'4012, R3 ; dataflow R3=H'4012
|
||||
1FE4: 1E 1E 6D BSR loc_3E54
|
||||
1FE7: 19 RTS
|
||||
1FE8: 1D E0 26 80 MOV:G.W @H'E026, R0 ; refs mem_E026 in program_or_external
|
||||
1FEC: A8 CF BSET.W #15, R0
|
||||
1FEE: 1D E8 26 90 MOV:G.W R0, @H'E826 ; refs mem_E826 in program_or_external
|
||||
1FF2: 52 80 MOV:E.B #H'80, R2 ; dataflow R2=H'80
|
||||
1FF4: 5B 00 13 MOV:I.W #H'0013, R3 ; dataflow R3=H'0013
|
||||
1FF7: 1E 1E 5A BSR loc_3E54
|
||||
1FFA: 19 RTS
|
||||
1FFB: 1D E0 26 80 MOV:G.W @H'E026, R0 ; refs mem_E026 in program_or_external
|
||||
1FFF: A8 DF BCLR.W #15, R0
|
||||
2001: 1D E8 26 90 MOV:G.W R0, @H'E826 ; refs mem_E826 in program_or_external
|
||||
2005: 52 80 MOV:E.B #H'80, R2 ; dataflow R2=H'80
|
||||
2007: 5B 00 13 MOV:I.W #H'0013, R3 ; dataflow R3=H'0013
|
||||
200A: 1E 1E 47 BSR loc_3E54
|
||||
200D: 19 RTS
|
||||
200E: 15 F6 DB F7 BTST.B #7, @H'F6DB ; refs ram_F6DB in on_chip_ram
|
||||
2012: 27 33 BEQ loc_2047
|
||||
2014: 15 F7 31 04 03 CMP:G.B #H'03, @H'F731 ; refs ram_F731 in on_chip_ram
|
||||
2019: 22 2C BHI loc_2047
|
||||
201B: 15 F7 91 F5 BTST.B #5, @H'F791 ; refs ram_F791 in on_chip_ram
|
||||
201F: 26 14 BNE loc_2035
|
||||
2021: 1D E0 26 80 MOV:G.W @H'E026, R0 ; refs mem_E026 in program_or_external
|
||||
2025: A8 CE BSET.W #14, R0
|
||||
2027: 1D E8 26 90 MOV:G.W R0, @H'E826 ; refs mem_E826 in program_or_external
|
||||
202B: 52 80 MOV:E.B #H'80, R2 ; dataflow R2=H'80
|
||||
202D: 5B 00 13 MOV:I.W #H'0013, R3 ; dataflow R3=H'0013
|
||||
2030: 1E 1E 21 BSR loc_3E54
|
||||
2033: 20 12 BRA loc_2047
|
||||
|
||||
loc_2035:
|
||||
2035: 1D E0 26 80 MOV:G.W @H'E026, R0 ; refs mem_E026 in program_or_external
|
||||
2039: A8 DE BCLR.W #14, R0
|
||||
203B: 1D E8 26 90 MOV:G.W R0, @H'E826 ; refs mem_E826 in program_or_external
|
||||
203F: 52 80 MOV:E.B #H'80, R2 ; dataflow R2=H'80
|
||||
2041: 5B 00 13 MOV:I.W #H'0013, R3 ; dataflow R3=H'0013
|
||||
2044: 1E 1E 0D BSR loc_3E54
|
||||
|
||||
loc_2047:
|
||||
2047: 19 RTS
|
||||
2048: 15 F6 D4 F6 BTST.B #6, @H'F6D4 ; refs ram_F6D4 in on_chip_ram
|
||||
204C: 27 52 BEQ loc_20A0
|
||||
204E: 15 F7 31 04 02 CMP:G.B #H'02, @H'F731 ; refs ram_F731 in on_chip_ram
|
||||
2053: 22 4B BHI loc_20A0
|
||||
2055: 15 F7 30 F7 BTST.B #7, @H'F730 ; refs ram_F730 in on_chip_ram
|
||||
2059: 27 19 BEQ loc_2074
|
||||
205B: 1D E8 D6 07 80 00 MOV:G.W #H'8000, @H'E8D6 ; refs mem_E8D6 in program_or_external
|
||||
2061: 52 80 MOV:E.B #H'80, R2 ; dataflow R2=H'80
|
||||
2063: 5B 00 6B MOV:I.W #H'006B, R3 ; dataflow R3=H'006B
|
||||
2066: 1E 1D EB BSR loc_3E54
|
||||
2069: 15 F7 31 C7 BSET.B #7, @H'F731 ; refs ram_F731 in on_chip_ram
|
||||
206D: 15 F7 98 06 C8 MOV:G.B #H'C8, @H'F798 ; refs ram_F798 in on_chip_ram
|
||||
2072: 20 2C BRA loc_20A0
|
||||
|
||||
loc_2074:
|
||||
2074: 1D F7 32 13 CLR.W @H'F732 ; refs ram_F732 in on_chip_ram
|
||||
2078: 15 FB 03 D7 BCLR.B #7, @H'FB03 ; refs ram_FB03 in on_chip_ram
|
||||
207C: 1D E0 46 13 CLR.W @H'E046 ; refs mem_E046 in program_or_external
|
||||
2080: 1D F7 6A 13 CLR.W @H'F76A ; refs ram_F76A in on_chip_ram
|
||||
2084: 1E 28 73 BSR loc_48FA
|
||||
2087: 15 F7 13 C6 BSET.B #6, @H'F713 ; refs ram_F713 in on_chip_ram
|
||||
208B: 15 F7 26 06 1E MOV:G.B #H'1E, @H'F726 ; refs ram_F726 in on_chip_ram
|
||||
2090: 15 F7 6E C6 BSET.B #6, @H'F76E ; refs ram_F76E in on_chip_ram
|
||||
2094: 15 F7 31 C7 BSET.B #7, @H'F731 ; refs ram_F731 in on_chip_ram
|
||||
2098: 15 F7 98 06 C8 MOV:G.B #H'C8, @H'F798 ; refs ram_F798 in on_chip_ram
|
||||
209D: 1E 36 38 BSR loc_56D8
|
||||
|
||||
loc_20A0:
|
||||
20A0: 19 RTS
|
||||
20A1: 1D E0 2A 80 MOV:G.W @H'E02A, R0 ; refs mem_E02A in program_or_external
|
||||
20A5: 15 F6 DB F5 BTST.B #5, @H'F6DB ; refs ram_F6DB in on_chip_ram
|
||||
20A9: 27 04 BEQ loc_20AF
|
||||
20AB: A8 CF BSET.W #15, R0
|
||||
20AD: 20 02 BRA loc_20B1
|
||||
|
||||
loc_20AF:
|
||||
20AF: A8 DF BCLR.W #15, R0
|
||||
|
||||
loc_20B1:
|
||||
20B1: 1D E8 2A 90 MOV:G.W R0, @H'E82A ; refs mem_E82A in program_or_external
|
||||
20B5: 52 80 MOV:E.B #H'80, R2 ; dataflow R2=H'80
|
||||
20B7: 5B 00 15 MOV:I.W #H'0015, R3 ; dataflow R3=H'0015
|
||||
20BA: 1E 1D 97 BSR loc_3E54
|
||||
20BD: 19 RTS
|
||||
20BE: 15 F6 DB F3 BTST.B #3, @H'F6DB ; refs ram_F6DB in on_chip_ram
|
||||
20C2: 27 2C BEQ loc_20F0
|
||||
20C4: 15 F7 31 04 03 CMP:G.B #H'03, @H'F731 ; refs ram_F731 in on_chip_ram
|
||||
20C9: 22 25 BHI loc_20F0
|
||||
20CB: 1D E1 10 16 TST.W @H'E110 ; refs mem_E110 in program_or_external
|
||||
20CF: 27 05 BEQ loc_20D6
|
||||
20D1: 1E 06 14 BSR loc_26E8
|
||||
20D4: 20 1A BRA loc_20F0
|
||||
|
||||
loc_20D6:
|
||||
20D6: 1D E1 34 80 MOV:G.W @H'E134, R0 ; refs mem_E134 in program_or_external
|
||||
20DA: A8 FB BTST.W #11, R0
|
||||
20DC: 27 04 BEQ loc_20E2
|
||||
20DE: A8 DB BCLR.W #11, R0
|
||||
7108
build/rom_0013_button_handler_linear.json
Normal file
7108
build/rom_0013_button_handler_linear.json
Normal file
File diff suppressed because it is too large
Load Diff
135
build/rom_0013_handler_linear.asm
Normal file
135
build/rom_0013_handler_linear.asm
Normal file
@@ -0,0 +1,135 @@
|
||||
; H8/536 ROM disassembly
|
||||
; input: ROM\M27C512@DIP28_1.BIN
|
||||
; bytes: 65536
|
||||
; vector mode: min
|
||||
; analysis: linear sweep
|
||||
;
|
||||
; Notes from the manual:
|
||||
; - H8/536 uses the H8/500 CPU instruction set.
|
||||
; - In minimum mode the reset vector at H'0000-H'0001 is a 16-bit PC.
|
||||
; - The register field is H'FE80-H'FFFF; names below come from appendix B.
|
||||
; - @aa:8 short absolute operands use BR as the upper address byte.
|
||||
; - SCI baud inference uses section 14.2.8 BRR formulas when SMR/BRR are known.
|
||||
; - LCD inference treats E-clock H'F200/H'F201 accesses as status/control and data candidates.
|
||||
|
||||
; Memory Map
|
||||
; H'0000-H'009F exception_vectors vectors
|
||||
; H'00A0-H'00FF dtc_vectors dtc_vectors
|
||||
; H'0100-H'F67F program_or_external program
|
||||
; H'F680-H'FE7F on_chip_ram ram
|
||||
; H'FE80-H'FFFF register_field registers
|
||||
|
||||
; Vectors
|
||||
; H'0000 reset -> vec_reset_1000 (H'1000)
|
||||
; H'0004 invalid_instruction -> vec_reset_1000 (H'1000)
|
||||
; H'0006 zero_divide -> vec_reset_1000 (H'1000)
|
||||
; H'0008 trap_vs -> vec_reset_1000 (H'1000)
|
||||
; H'0010 address_error -> vec_reset_1000 (H'1000)
|
||||
; H'0012 trace -> vec_reset_1000 (H'1000)
|
||||
; H'0016 nmi -> vec_nmi_4393 (H'4393)
|
||||
; H'0020 trapa_0 -> vec_reset_1000 (H'1000)
|
||||
; H'0022 trapa_1 -> vec_reset_1000 (H'1000)
|
||||
; H'0024 trapa_2 -> vec_reset_1000 (H'1000)
|
||||
; H'0026 trapa_3 -> vec_reset_1000 (H'1000)
|
||||
; H'0028 trapa_4 -> vec_reset_1000 (H'1000)
|
||||
; H'002A trapa_5 -> vec_reset_1000 (H'1000)
|
||||
; H'002C trapa_6 -> vec_reset_1000 (H'1000)
|
||||
; H'002E trapa_7 -> vec_reset_1000 (H'1000)
|
||||
; H'0030 trapa_8 -> vec_reset_1000 (H'1000)
|
||||
; H'0032 trapa_9 -> vec_reset_1000 (H'1000)
|
||||
; H'0034 trapa_a -> vec_reset_1000 (H'1000)
|
||||
; H'0036 trapa_b -> vec_reset_1000 (H'1000)
|
||||
; H'0038 trapa_c -> vec_reset_1000 (H'1000)
|
||||
; H'003A trapa_d -> vec_reset_1000 (H'1000)
|
||||
; H'003C trapa_e -> vec_reset_1000 (H'1000)
|
||||
; H'003E trapa_f -> vec_reset_1000 (H'1000)
|
||||
; H'0040 irq0 -> vec_reset_1000 (H'1000)
|
||||
; H'0042 interval_timer -> vec_interval_timer_BFC4 (H'BFC4)
|
||||
; H'0048 irq1 -> vec_reset_1000 (H'1000)
|
||||
; H'0050 irq2 -> vec_reset_1000 (H'1000)
|
||||
; H'0052 irq3 -> vec_irq3_3C30 (H'3C30)
|
||||
; H'0058 irq4 -> vec_irq4_3AC7 (H'3AC7)
|
||||
; H'005A irq5 -> vec_reset_1000 (H'1000)
|
||||
; H'0062 frt1_ocia -> vec_frt1_ocia_BEEA (H'BEEA)
|
||||
; H'006A frt2_ocia -> vec_frt2_ocia_BF23 (H'BF23)
|
||||
; H'0080 sci1_eri -> vec_sci1_eri_BB57 (H'BB57)
|
||||
; H'0082 sci1_rxi -> vec_sci1_rxi_BB67 (H'BB67)
|
||||
; H'0084 sci1_txi -> vec_sci1_txi_BA84 (H'BA84)
|
||||
; H'0090 ad_adi -> vec_ad_adi_3D99 (H'3D99)
|
||||
|
||||
; Symbols
|
||||
; mem_E02E H'E02E program_or_external memory r=1 w=0 width=word
|
||||
; mem_E030 H'E030 program_or_external memory r=1 w=0 width=word
|
||||
; mem_E826 H'E826 program_or_external memory r=2 w=0 width=word
|
||||
; mem_E82E H'E82E program_or_external memory r=0 w=1 width=word
|
||||
; ram_F711 H'F711 on_chip_ram ram r=4 w=4 width=byte
|
||||
; ram_F713 H'F713 on_chip_ram ram r=2 w=2 width=byte
|
||||
; ram_F716 H'F716 on_chip_ram ram r=4 w=4 width=byte
|
||||
; ram_F791 H'F791 on_chip_ram ram r=4 w=4 width=byte
|
||||
|
||||
; Board Profile
|
||||
; Board trace ties the H8/536 SCI1 pins to a MAX202 RS232 transceiver.
|
||||
; H8 pin 66 P95/TXD (TXD) -> MAX202 pin 11
|
||||
; H8 pin 67 P96/RXD (RXD) -> MAX202 pin 12
|
||||
; SCI2 pin routing is disabled by SYSCR2.P9SCI2E=0 in the observed setup.
|
||||
|
||||
; LCD/Text Scan
|
||||
; search 'CONNECT': not literal, hits=0
|
||||
|
||||
2E00: 30 FE A3 BRA loc_2CA6
|
||||
2E03: 30 FE A0 BRA loc_2CA6
|
||||
2E06: 1D E8 26 FF BTST.W #15, @H'E826 ; refs mem_E826 in program_or_external
|
||||
2E0A: 27 0A BEQ loc_2E16
|
||||
2E0C: 15 F7 91 C6 BSET.B #6, @H'F791 ; refs ram_F791 in on_chip_ram
|
||||
2E10: 15 F7 13 C4 BSET.B #4, @H'F713 ; refs ram_F713 in on_chip_ram
|
||||
2E14: 20 08 BRA loc_2E1E
|
||||
|
||||
loc_2E16:
|
||||
2E16: 15 F7 91 D6 BCLR.B #6, @H'F791 ; refs ram_F791 in on_chip_ram
|
||||
2E1A: 15 F7 13 D4 BCLR.B #4, @H'F713 ; refs ram_F713 in on_chip_ram
|
||||
|
||||
loc_2E1E:
|
||||
2E1E: 1D E8 26 FE BTST.W #14, @H'E826 ; refs mem_E826 in program_or_external
|
||||
2E22: 27 0A BEQ loc_2E2E
|
||||
2E24: 15 F7 91 C5 BSET.B #5, @H'F791 ; refs ram_F791 in on_chip_ram
|
||||
2E28: 15 F7 16 C7 BSET.B #7, @H'F716 ; refs ram_F716 in on_chip_ram
|
||||
2E2C: 20 08 BRA loc_2E36
|
||||
|
||||
loc_2E2E:
|
||||
2E2E: 15 F7 91 D5 BCLR.B #5, @H'F791 ; refs ram_F791 in on_chip_ram
|
||||
2E32: 15 F7 16 D7 BCLR.B #7, @H'F716 ; refs ram_F716 in on_chip_ram
|
||||
|
||||
loc_2E36:
|
||||
2E36: 30 FE 6D BRA loc_2CA6
|
||||
2E39: FC E0 00 81 MOV:G.W @(-H'2000,R4), R1
|
||||
2E3D: A9 FF BTST.W #15, R1
|
||||
2E3F: 26 0E BNE loc_2E4F
|
||||
2E41: A9 FE BTST.W #14, R1
|
||||
2E43: 26 0A BNE loc_2E4F
|
||||
2E45: 15 F7 11 D0 BCLR.B #0, @H'F711 ; refs ram_F711 in on_chip_ram
|
||||
2E49: 15 F7 16 D5 BCLR.B #5, @H'F716 ; refs ram_F716 in on_chip_ram
|
||||
2E4D: 20 08 BRA loc_2E57
|
||||
|
||||
loc_2E4F:
|
||||
2E4F: 15 F7 11 C0 BSET.B #0, @H'F711 ; refs ram_F711 in on_chip_ram
|
||||
2E53: 15 F7 16 C5 BSET.B #5, @H'F716 ; refs ram_F716 in on_chip_ram
|
||||
|
||||
loc_2E57:
|
||||
2E57: 30 FE 4C BRA loc_2CA6
|
||||
2E5A: FC E0 00 81 MOV:G.W @(-H'2000,R4), R1
|
||||
2E5E: A9 FF BTST.W #15, R1
|
||||
2E60: 26 06 BNE loc_2E68
|
||||
2E62: 15 F7 11 D1 BCLR.B #1, @H'F711 ; refs ram_F711 in on_chip_ram
|
||||
2E66: 20 04 BRA loc_2E6C
|
||||
|
||||
loc_2E68:
|
||||
2E68: 15 F7 11 C1 BSET.B #1, @H'F711 ; refs ram_F711 in on_chip_ram
|
||||
|
||||
loc_2E6C:
|
||||
2E6C: 30 FE 37 BRA loc_2CA6
|
||||
2E6F: 1D E0 30 81 MOV:G.W @H'E030, R1 ; refs mem_E030 in program_or_external
|
||||
2E73: 1D E0 2E 71 CMP:G.W @H'E02E, R1 ; refs mem_E02E in program_or_external
|
||||
2E77: 27 0C BEQ loc_2E85
|
||||
2E79: 1D E8 2E 91 MOV:G.W R1, @H'E82E ; refs mem_E82E in program_or_external
|
||||
2E7D: 52 80 MOV:E.B #H'80, R2 ; dataflow R2=H'80
|
||||
2E7F: 5B 00 17 MOV:I.W #H'0017, R3 ; dataflow R3=H'0017
|
||||
2590
build/rom_0013_handler_linear.json
Normal file
2590
build/rom_0013_handler_linear.json
Normal file
File diff suppressed because it is too large
Load Diff
38
build/rom_0013_table_xrefs.txt
Normal file
38
build/rom_0013_table_xrefs.txt
Normal file
@@ -0,0 +1,38 @@
|
||||
Table/Index Cross-Reference Report for build\rom_0013_handler_linear.json
|
||||
=========================================================================
|
||||
|
||||
Static offsets are emitted only when an index register value can be derived from nearby immediate loads in the current JSON. Other indexed accesses are dynamic.
|
||||
|
||||
LCD correlation hints
|
||||
term 'CONNECT': no LCD/text candidate hits in current decompile
|
||||
term 'CONNECT: OK': no LCD/text candidate hits in current decompile
|
||||
term 'CONNECT: NOT ACT': no LCD/text candidate hits in current decompile
|
||||
term 'NOT ACT': no LCD/text candidate hits in current decompile
|
||||
term 'COMM LINK': no LCD/text candidate hits in current decompile
|
||||
term 'COMPLETED': no LCD/text candidate hits in current decompile
|
||||
caveat: LCD strings can be builder/script output; absence of a literal term does not disprove runtime composition.
|
||||
|
||||
primary_value_table_candidate H'E000-H'E3FF (negative H'2000; direct H'F900-H'F91F)
|
||||
accesses=4 reads=4 writes=0 dynamic=2
|
||||
static offsets: H'002E, H'0030
|
||||
functions: <no function>:4
|
||||
- H'2E39 read index dynamic via R4 operand @(-H'2000,R4); <no function>; MOV:G.W @(-H'2000,R4), R1
|
||||
- H'2E5A read index dynamic via R4 operand @(-H'2000,R4); <no function>; MOV:G.W @(-H'2000,R4), R1
|
||||
- H'2E6F read offset H'0030 selector 0x018 -> H'E030; <no function>; MOV:G.W @H'E030, R1
|
||||
- H'2E73 read offset H'002E selector 0x017 -> H'E02E; <no function>; CMP:G.W @H'E02E, R1
|
||||
|
||||
secondary_value_table_candidate H'E400-H'E7FF (negative H'1C00; direct H'F940-H'F95F)
|
||||
accesses=0 reads=0 writes=0 dynamic=0
|
||||
no references found in current JSON
|
||||
|
||||
current_value_table_candidate H'E800-H'EBFF (negative H'1800; direct H'F920-H'F93F)
|
||||
accesses=3 reads=2 writes=1 dynamic=0
|
||||
static offsets: H'0026, H'002E
|
||||
functions: <no function>:3
|
||||
- H'2E06 read offset H'0026 selector 0x013 -> H'E826; <no function>; BTST.W #15, @H'E826
|
||||
- H'2E1E read offset H'0026 selector 0x013 -> H'E826; <no function>; BTST.W #14, @H'E826
|
||||
- H'2E79 write offset H'002E selector 0x017 -> H'E82E; <no function>; MOV:G.W R1, @H'E82E
|
||||
|
||||
flag_table_candidate H'EC00-H'EFFF (negative H'1400; direct H'F980-H'F99F)
|
||||
accesses=0 reads=0 writes=0 dynamic=0
|
||||
no references found in current JSON
|
||||
@@ -793,6 +793,108 @@
|
||||
]
|
||||
},
|
||||
"selector_candidates": [
|
||||
{
|
||||
"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": "white_balance_black_flare_mode_lane",
|
||||
"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",
|
||||
"Bench-visible white-balance and black/flare lamp lane."
|
||||
],
|
||||
"score": 19,
|
||||
"seed_frames": [
|
||||
{
|
||||
"cmd0_frame": "00 01 13 80 00 C8",
|
||||
"value": 32768,
|
||||
"value_hex": "0x8000"
|
||||
},
|
||||
{
|
||||
"cmd0_frame": "00 01 13 40 00 08",
|
||||
"value": 16384,
|
||||
"value_hex": "0x4000"
|
||||
},
|
||||
{
|
||||
"cmd0_frame": "00 01 13 20 00 68",
|
||||
"value": 8192,
|
||||
"value_hex": "0x2000"
|
||||
},
|
||||
{
|
||||
"cmd0_frame": "00 01 13 10 20 78",
|
||||
"value": 4128,
|
||||
"value_hex": "0x1020"
|
||||
},
|
||||
{
|
||||
"cmd0_frame": "00 01 13 40 40 48",
|
||||
"value": 16448,
|
||||
"value_hex": "0x4040"
|
||||
},
|
||||
{
|
||||
"cmd0_frame": "00 01 13 80 40 88",
|
||||
"value": 32832,
|
||||
"value_hex": "0x8040"
|
||||
},
|
||||
{
|
||||
"cmd0_frame": "00 01 13 00 20 68",
|
||||
"value": 32,
|
||||
"value_hex": "0x0020"
|
||||
},
|
||||
{
|
||||
"cmd0_frame": "00 01 13 00 40 08",
|
||||
"value": 64,
|
||||
"value_hex": "0x0040"
|
||||
},
|
||||
{
|
||||
"cmd0_frame": "00 01 13 00 00 48",
|
||||
"value": 0,
|
||||
"value_hex": "0x0000"
|
||||
}
|
||||
],
|
||||
"selector": 147,
|
||||
"selector_hex": "0x093",
|
||||
"tables": [
|
||||
"primary_value_table_candidate"
|
||||
]
|
||||
},
|
||||
{
|
||||
"accesses": [
|
||||
{
|
||||
@@ -859,61 +961,6 @@
|
||||
"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": [
|
||||
{
|
||||
@@ -1044,6 +1091,75 @@
|
||||
"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": "standard_lamp_lane",
|
||||
"reasons": [
|
||||
"when F731.7 is set, command 5 on this selector clears F731.7/F790.7",
|
||||
"Bench-visible STANDARD lamp lane found from ROM-derived F6D4.6 handler candidate.",
|
||||
"selector dispatches to H'2F72"
|
||||
],
|
||||
"score": 11,
|
||||
"seed_frames": [
|
||||
{
|
||||
"cmd0_frame": "00 00 6B 80 00 B1",
|
||||
"value": 32768,
|
||||
"value_hex": "0x8000"
|
||||
}
|
||||
],
|
||||
"selector": 107,
|
||||
"selector_hex": "0x06B",
|
||||
"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_and_red_tally_lamp_lane",
|
||||
"reasons": [
|
||||
"observed RCP autonomous report frame(s): 00 00 15 80 00 CF, 00 00 15 00 00 4F",
|
||||
"Bench-visible CALL lamp and red tally lane; local CALL handler mirrors F6DB.5 into E800[0x0015].15.",
|
||||
"selector dispatches to H'2E39"
|
||||
],
|
||||
"score": 9,
|
||||
"seed_frames": [
|
||||
{
|
||||
"cmd0_frame": "00 00 15 80 00 CF",
|
||||
"value": 32768,
|
||||
"value_hex": "0x8000"
|
||||
},
|
||||
{
|
||||
"cmd0_frame": "00 00 15 00 00 4F",
|
||||
"value": 0,
|
||||
"value_hex": "0x0000"
|
||||
}
|
||||
],
|
||||
"selector": 21,
|
||||
"selector_hex": "0x015",
|
||||
"tables": []
|
||||
},
|
||||
{
|
||||
"accesses": [
|
||||
{
|
||||
@@ -1124,31 +1240,6 @@
|
||||
"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",
|
||||
@@ -1199,6 +1290,202 @@
|
||||
"selector_hex": "0x06D",
|
||||
"tables": []
|
||||
},
|
||||
{
|
||||
"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": "knee_auto_lamp_or_page_status_lane",
|
||||
"reasons": [
|
||||
"primary_value_table_candidate read in loc_1795: BTST.W #15, @H'E220",
|
||||
"Bench-visible KNEE AUTO source; ROM notes indicate timed KNEE/detail page interaction."
|
||||
],
|
||||
"score": 7,
|
||||
"seed_frames": [
|
||||
{
|
||||
"cmd0_frame": "00 01 90 80 00 4B",
|
||||
"value": 32768,
|
||||
"value_hex": "0x8000"
|
||||
}
|
||||
],
|
||||
"selector": 272,
|
||||
"selector_hex": "0x110",
|
||||
"tables": [
|
||||
"primary_value_table_candidate"
|
||||
]
|
||||
},
|
||||
{
|
||||
"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": "slave_and_iris_mblack_link_lamps",
|
||||
"reasons": [
|
||||
"Selector 0x0013 is a two-bit lamp/status word. ROM dispatch H'2E06 reads current table word H'E826 and fans bit 15 and bit 14 into panel latch RAM.",
|
||||
"0x8000 SLAVE lamp: sets F791.6 and F713.4",
|
||||
"0x4000 IRIS/M.BLACK LINK lamp: sets F791.5 and F716.7",
|
||||
"selector dispatches to H'2E06"
|
||||
],
|
||||
"score": 6,
|
||||
"seed_frames": [
|
||||
{
|
||||
"cmd0_frame": "00 00 13 80 00 C9",
|
||||
"value": 32768,
|
||||
"value_hex": "0x8000"
|
||||
},
|
||||
{
|
||||
"cmd0_frame": "00 00 13 40 00 09",
|
||||
"value": 16384,
|
||||
"value_hex": "0x4000"
|
||||
},
|
||||
{
|
||||
"cmd0_frame": "00 00 13 00 00 49",
|
||||
"value": 0,
|
||||
"value_hex": "0x0000"
|
||||
}
|
||||
],
|
||||
"selector": 19,
|
||||
"selector_hex": "0x013",
|
||||
"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": "bars_lamp_lane",
|
||||
"reasons": [
|
||||
"Bench-visible BARS lamp/latch lane; low writes do not reliably clear the visible latch.",
|
||||
"selector dispatches to H'2E85"
|
||||
],
|
||||
"score": 6,
|
||||
"seed_frames": [
|
||||
{
|
||||
"cmd0_frame": "00 00 17 80 00 CD",
|
||||
"value": 32768,
|
||||
"value_hex": "0x8000"
|
||||
},
|
||||
{
|
||||
"cmd0_frame": "00 00 17 40 00 0D",
|
||||
"value": 16384,
|
||||
"value_hex": "0x4000"
|
||||
},
|
||||
{
|
||||
"cmd0_frame": "00 00 17 00 00 4D",
|
||||
"value": 0,
|
||||
"value_hex": "0x0000"
|
||||
}
|
||||
],
|
||||
"selector": 23,
|
||||
"selector_hex": "0x017",
|
||||
"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": "monitor_selector_lamps",
|
||||
"reasons": [
|
||||
"Bench-visible MONITOR selector cluster found from ROM-derived button-output sweep.",
|
||||
"selector dispatches to H'2EC4"
|
||||
],
|
||||
"score": 6,
|
||||
"seed_frames": [
|
||||
{
|
||||
"cmd0_frame": "00 00 1A 08 08 40",
|
||||
"value": 2056,
|
||||
"value_hex": "0x0808"
|
||||
},
|
||||
{
|
||||
"cmd0_frame": "00 00 1A 20 20 40",
|
||||
"value": 8224,
|
||||
"value_hex": "0x2020"
|
||||
},
|
||||
{
|
||||
"cmd0_frame": "00 00 1A 40 40 40",
|
||||
"value": 16448,
|
||||
"value_hex": "0x4040"
|
||||
},
|
||||
{
|
||||
"cmd0_frame": "00 00 1A 80 80 40",
|
||||
"value": 32896,
|
||||
"value_hex": "0x8080"
|
||||
}
|
||||
],
|
||||
"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": "lcd_selector_button_lamp",
|
||||
"reasons": [
|
||||
"Bench-visible LCD selector-button lamp lane.",
|
||||
"selector dispatches to H'2F0C"
|
||||
],
|
||||
"score": 6,
|
||||
"seed_frames": [
|
||||
{
|
||||
"cmd0_frame": "00 00 24 80 00 FE",
|
||||
"value": 32768,
|
||||
"value_hex": "0x8000"
|
||||
},
|
||||
{
|
||||
"cmd0_frame": "00 00 24 00 00 7E",
|
||||
"value": 0,
|
||||
"value_hex": "0x0000"
|
||||
}
|
||||
],
|
||||
"selector": 36,
|
||||
"selector_hex": "0x024",
|
||||
"tables": []
|
||||
},
|
||||
{
|
||||
"accesses": [],
|
||||
"cmd1_read_frame": "01 00 07 00 00 5C",
|
||||
@@ -1224,31 +1511,6 @@
|
||||
"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": [
|
||||
{
|
||||
@@ -1349,6 +1611,108 @@
|
||||
"selector_hex": "0x0F8",
|
||||
"tables": []
|
||||
},
|
||||
{
|
||||
"accesses": [],
|
||||
"cmd1_read_frame": "01 01 02 00 00 58",
|
||||
"name": "iris_readout_lane",
|
||||
"reasons": [
|
||||
"Bench-visible IRIS seven-segment/display lane."
|
||||
],
|
||||
"score": 4,
|
||||
"seed_frames": [
|
||||
{
|
||||
"cmd0_frame": "00 01 02 80 00 D9",
|
||||
"value": 32768,
|
||||
"value_hex": "0x8000"
|
||||
},
|
||||
{
|
||||
"cmd0_frame": "00 01 02 40 00 19",
|
||||
"value": 16384,
|
||||
"value_hex": "0x4000"
|
||||
},
|
||||
{
|
||||
"cmd0_frame": "00 01 02 00 00 59",
|
||||
"value": 0,
|
||||
"value_hex": "0x0000"
|
||||
}
|
||||
],
|
||||
"selector": 130,
|
||||
"selector_hex": "0x082",
|
||||
"tables": []
|
||||
},
|
||||
{
|
||||
"accesses": [],
|
||||
"cmd1_read_frame": "01 01 03 00 00 59",
|
||||
"name": "combined_iris_shutter_master_gain_status_lane",
|
||||
"reasons": [
|
||||
"Bench-visible combined status/readout lane; clear behavior appears latched or copied elsewhere."
|
||||
],
|
||||
"score": 4,
|
||||
"seed_frames": [
|
||||
{
|
||||
"cmd0_frame": "00 01 03 80 00 D8",
|
||||
"value": 32768,
|
||||
"value_hex": "0x8000"
|
||||
},
|
||||
{
|
||||
"cmd0_frame": "00 01 03 40 00 18",
|
||||
"value": 16384,
|
||||
"value_hex": "0x4000"
|
||||
},
|
||||
{
|
||||
"cmd0_frame": "00 01 03 20 00 78",
|
||||
"value": 8192,
|
||||
"value_hex": "0x2000"
|
||||
},
|
||||
{
|
||||
"cmd0_frame": "00 01 03 00 04 5C",
|
||||
"value": 4,
|
||||
"value_hex": "0x0004"
|
||||
},
|
||||
{
|
||||
"cmd0_frame": "00 01 03 00 00 58",
|
||||
"value": 0,
|
||||
"value_hex": "0x0000"
|
||||
}
|
||||
],
|
||||
"selector": 131,
|
||||
"selector_hex": "0x083",
|
||||
"tables": []
|
||||
},
|
||||
{
|
||||
"accesses": [],
|
||||
"cmd1_read_frame": "01 01 0F 00 00 55",
|
||||
"name": "shutter_display_status_lane",
|
||||
"reasons": [
|
||||
"Bench-visible shutter/status display lane; local F6D0.6/F6D0.7 handlers also queue this selector."
|
||||
],
|
||||
"score": 4,
|
||||
"seed_frames": [
|
||||
{
|
||||
"cmd0_frame": "00 01 0F 80 00 D4",
|
||||
"value": 32768,
|
||||
"value_hex": "0x8000"
|
||||
},
|
||||
{
|
||||
"cmd0_frame": "00 01 0F 20 00 74",
|
||||
"value": 8192,
|
||||
"value_hex": "0x2000"
|
||||
},
|
||||
{
|
||||
"cmd0_frame": "00 01 0F 08 00 5C",
|
||||
"value": 2048,
|
||||
"value_hex": "0x0800"
|
||||
},
|
||||
{
|
||||
"cmd0_frame": "00 01 0F 10 00 44",
|
||||
"value": 4096,
|
||||
"value_hex": "0x1000"
|
||||
}
|
||||
],
|
||||
"selector": 143,
|
||||
"selector_hex": "0x08F",
|
||||
"tables": []
|
||||
},
|
||||
{
|
||||
"accesses": [
|
||||
{
|
||||
@@ -1441,29 +1805,6 @@
|
||||
"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",
|
||||
@@ -1488,30 +1829,6 @@
|
||||
"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",
|
||||
@@ -1536,30 +1853,6 @@
|
||||
"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",
|
||||
@@ -1584,54 +1877,6 @@
|
||||
"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",
|
||||
@@ -1898,7 +2143,7 @@
|
||||
}
|
||||
],
|
||||
"summary": {
|
||||
"candidate_count": 41,
|
||||
"candidate_count": 44,
|
||||
"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."
|
||||
},
|
||||
|
||||
@@ -10,6 +10,13 @@ Table Model:
|
||||
- flag_table_candidate: H'EC00-H'EFFF; accesses=6 static selectors=0x000
|
||||
|
||||
Highest-Value Selector Candidates:
|
||||
- 0x093 white_balance_black_flare_mode_lane: score=19 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
|
||||
seed frames: 0x8000 -> 00 01 13 80 00 C8; 0x4000 -> 00 01 13 40 00 08; 0x2000 -> 00 01 13 20 00 68
|
||||
readback frame: 01 01 13 00 00 49
|
||||
- 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
|
||||
@@ -17,12 +24,6 @@ Highest-Value Selector Candidates:
|
||||
- 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
|
||||
@@ -42,6 +43,18 @@ Highest-Value Selector Candidates:
|
||||
- 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
|
||||
- 0x06B standard_lamp_lane: score=11 tables=none
|
||||
- when F731.7 is set, command 5 on this selector clears F731.7/F790.7
|
||||
- Bench-visible STANDARD lamp lane found from ROM-derived F6D4.6 handler candidate.
|
||||
- selector dispatches to H'2F72
|
||||
seed frames: 0x8000 -> 00 00 6B 80 00 B1
|
||||
readback frame: 01 00 6B 00 00 30
|
||||
- 0x015 call_and_red_tally_lamp_lane: score=9 tables=none
|
||||
- observed RCP autonomous report frame(s): 00 00 15 80 00 CF, 00 00 15 00 00 4F
|
||||
- Bench-visible CALL lamp and red tally lane; local CALL handler mirrors F6DB.5 into E800[0x0015].15.
|
||||
- selector dispatches to H'2E39
|
||||
seed frames: 0x8000 -> 00 00 15 80 00 CF; 0x0000 -> 00 00 15 00 00 4F
|
||||
readback frame: 01 00 15 00 00 4E
|
||||
- 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
|
||||
@@ -52,10 +65,6 @@ Highest-Value Selector Candidates:
|
||||
- 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
|
||||
@@ -64,14 +73,37 @@ Highest-Value Selector Candidates:
|
||||
- continuation command 5 calls BE70 for selector 0x006D
|
||||
- selector dispatches to H'3015
|
||||
readback frame: 01 00 6D 00 00 36
|
||||
- 0x110 knee_auto_lamp_or_page_status_lane: score=7 tables=primary_value_table_candidate
|
||||
- primary_value_table_candidate read in loc_1795: BTST.W #15, @H'E220
|
||||
- Bench-visible KNEE AUTO source; ROM notes indicate timed KNEE/detail page interaction.
|
||||
seed frames: 0x8000 -> 00 01 90 80 00 4B
|
||||
readback frame: 01 01 90 00 00 CA
|
||||
- 0x013 slave_and_iris_mblack_link_lamps: score=6 tables=none
|
||||
- Selector 0x0013 is a two-bit lamp/status word. ROM dispatch H'2E06 reads current table word H'E826 and fans bit 15 and bit 14 into panel latch RAM.
|
||||
- 0x8000 SLAVE lamp: sets F791.6 and F713.4
|
||||
- 0x4000 IRIS/M.BLACK LINK lamp: sets F791.5 and F716.7
|
||||
- selector dispatches to H'2E06
|
||||
seed frames: 0x8000 -> 00 00 13 80 00 C9; 0x4000 -> 00 00 13 40 00 09; 0x0000 -> 00 00 13 00 00 49
|
||||
readback frame: 01 00 13 00 00 48
|
||||
- 0x017 bars_lamp_lane: score=6 tables=none
|
||||
- Bench-visible BARS lamp/latch lane; low writes do not reliably clear the visible latch.
|
||||
- selector dispatches to H'2E85
|
||||
seed frames: 0x8000 -> 00 00 17 80 00 CD; 0x4000 -> 00 00 17 40 00 0D; 0x0000 -> 00 00 17 00 00 4D
|
||||
readback frame: 01 00 17 00 00 4C
|
||||
- 0x01A monitor_selector_lamps: score=6 tables=none
|
||||
- Bench-visible MONITOR selector cluster found from ROM-derived button-output sweep.
|
||||
- selector dispatches to H'2EC4
|
||||
seed frames: 0x0808 -> 00 00 1A 08 08 40; 0x2020 -> 00 00 1A 20 20 40; 0x4040 -> 00 00 1A 40 40 40
|
||||
readback frame: 01 00 1A 00 00 41
|
||||
- 0x024 lcd_selector_button_lamp: score=6 tables=none
|
||||
- Bench-visible LCD selector-button lamp lane.
|
||||
- selector dispatches to H'2F0C
|
||||
seed frames: 0x8000 -> 00 00 24 80 00 FE; 0x0000 -> 00 00 24 00 00 7E
|
||||
readback frame: 01 00 24 00 00 7F
|
||||
- 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
|
||||
@@ -91,24 +123,10 @@ Highest-Value Selector Candidates:
|
||||
- 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
|
||||
- 0x082 iris_readout_lane: score=4 tables=none
|
||||
- Bench-visible IRIS seven-segment/display lane.
|
||||
seed frames: 0x8000 -> 00 01 02 80 00 D9; 0x4000 -> 00 01 02 40 00 19; 0x0000 -> 00 01 02 00 00 59
|
||||
readback frame: 01 01 02 00 00 58
|
||||
|
||||
Display Text Hints:
|
||||
- CONNECT: 0 hit(s)
|
||||
|
||||
@@ -67,6 +67,7 @@
|
||||
typedef uint8_t u8;
|
||||
typedef uint16_t u16;
|
||||
|
||||
#define BIT(n) (1u << (n))
|
||||
extern volatile u8 MEM8[0x10000];
|
||||
|
||||
#define SCI1_SCR MEM8[0xFEDAu]
|
||||
@@ -193,6 +194,36 @@ extern volatile u8 MEM8[0x10000];
|
||||
* evidence: H'1A09, H'1A71, H'3F90, H'407F, H'BB35, H'BC79, H'BC99, H'BD1E
|
||||
* - flag_table_candidate at H'EC00 (bit_flags); observed write
|
||||
* evidence: H'4088, H'BC82, H'BC9D, H'BD22, H'BD39, H'BDE9
|
||||
* panel selector semantics:
|
||||
* - 0x0013 slave_and_iris_mblack_link_lamps: Selector 0x0013 is a two-bit lamp/status word. ROM dispatch H'2E06 reads current table word H'E826 and fans bit 15 and bit 14 into panel latch RAM.
|
||||
* current word: H'E826; dispatch: H'2E06
|
||||
* 0x8000 -> SLAVE lamp: sets F791.6 and F713.4; RAM F791.6, F713.4
|
||||
* 0x4000 -> IRIS/M.BLACK LINK lamp: sets F791.5 and F716.7; RAM F791.5, F716.7
|
||||
* observed values: 0x8000 SLAVE lamp on; 0x4000 IRIS/M.BLACK LINK lamp on; 0x0000 SLAVE and IRIS/M.BLACK LINK latch bits clear through H'2E06
|
||||
* state machine: iris_mblack_link_closed_loop_state_candidate: Bench-proven closed loop: the RCP reports local IRIS/M.BLACK LINK intent, the CCU ACKs selector 0x0013, then the CCU mirrors the accepted selector state back with command 0. The mirrored state controls the next toggle direction.
|
||||
* frames: active report 00 00 13 40 00 09; clear report 00 00 13 00 00 49; ACK 05 00 13 00 00 4C; mirror active 00 00 13 40 00 09; mirror clear 00 00 13 00 00 49
|
||||
* local trigger candidates: provisional_iris_mblack_link_button_toggle_report F006.7 / F6DB.7: When F6DB.7 is asserted and F731 <= 3, the ROM toggles current-table bit 14 at H'E826 based on F791.5, then queues selector 0x0013 through loc_3E54.; H'1FE8/H'1FFB: Adjacent local helpers set or clear current-table bit 15 at H'E826 and queue selector 0x0013.
|
||||
* evidence: bench: 00 00 13 80 00 C9 lights far-right SLAVE lamp, bench: 00 00 13 40 00 09 lights IRIS/M.BLACK LINK lamp, ROM: H'2E06-H'2E32 tests H'E826 bits 15/14 and sets/clears F791/F713/F716 latch bits
|
||||
* - 0x0015 call_and_red_tally_lamp_lane: Bench-visible CALL lamp and red tally lane; local CALL handler mirrors F6DB.5 into E800[0x0015].15.
|
||||
* current word: H'E82A; dispatch: handler unknown
|
||||
* observed values: 0x8000 CALL lamp and red tally on; 0x0000 CALL inactive/clear report
|
||||
* evidence: bench: 00 00 15 80 00 CF lights CALL and red tally, ROM: H'20A1-H'20BA reads F6DB.5, writes H'E82A, and queues selector 0x0015
|
||||
* - 0x0017 bars_lamp_lane: Bench-visible BARS lamp/latch lane; low writes do not reliably clear the visible latch.
|
||||
* current word: H'E82E; dispatch: handler unknown
|
||||
* observed values: 0x8000 BARS lamp on; 0x4000 BARS lamp/latch on; 0x0000 BARS low write; visible latch may remain
|
||||
* evidence: bench: 00 00 17 80 00 CD lights BARS, bench: 00 00 17 40 00 0D also lights the BARS latch in neighbor sweep, ROM: H'1EDE can queue selector 0x0017 from F6D4.2
|
||||
* - 0x001A monitor_selector_lamps: Bench-visible MONITOR selector cluster found from ROM-derived button-output sweep.
|
||||
* current word: H'E834; dispatch: handler unknown
|
||||
* observed values: 0x0808 MONITOR ENC lamp; 0x2020 MONITOR B lamp; 0x4040 MONITOR G lamp; 0x8080 MONITOR R lamp
|
||||
* evidence: bench: 00 00 1A 08 08 40 lights MONITOR ENC, bench: 00 00 1A 20 20 40 lights MONITOR B, bench: 00 00 1A 40 40 40 lights MONITOR G, bench: 00 00 1A 80 80 40 lights MONITOR R, ROM: H'1CB2-H'1D56 writes packed values to H'E834 and queues selector 0x001A
|
||||
* - 0x0024 lcd_selector_button_lamp: Bench-visible LCD selector-button lamp lane.
|
||||
* current word: H'E848; dispatch: dispatch unknown
|
||||
* observed values: 0x8000 LCD selector-button lamp visible; 0x0000 lamp remained visible at 0.5 s in isolation run
|
||||
* - 0x006B standard_lamp_lane: Bench-visible STANDARD lamp lane found from ROM-derived F6D4.6 handler candidate.
|
||||
* current word: H'E8D6; dispatch: handler unknown
|
||||
* observed values: 0x8000 STANDARD lamp on
|
||||
* evidence: bench: 00 00 6B 80 00 B1 lights STANDARD, ROM: H'2048 can write H'E8D6=0x8000 and queue selector 0x006B from F6D4.6
|
||||
* - ... 5 more panel selector annotations
|
||||
* state variable candidates:
|
||||
* - event_queue_read_cursor_candidate H'F9B4: reads 1, writes 2; bits 5
|
||||
* evidence: H'BE78, H'BE95, H'BE99
|
||||
@@ -357,12 +388,197 @@ void frt2_ocia_candidate_tick_isr(void)
|
||||
|
||||
}
|
||||
|
||||
static void sci1_candidate_panel_selector_annotation(u16 logical_index, u16 value)
|
||||
{
|
||||
/* Known bench/ROM selector labels. This helper is commentary for the decompile. */
|
||||
switch (logical_index) {
|
||||
case 0x0013u:
|
||||
/* 0x0013 slave_and_iris_mblack_link_lamps; current word H'E826; H'2E06. */
|
||||
if ((value & 0x8000u) != 0u) {
|
||||
/* SLAVE lamp: sets F791.6 and F713.4. */
|
||||
} else {
|
||||
/* SLAVE lamp: clears F791.6 and F713.4. */
|
||||
}
|
||||
if ((value & 0x4000u) != 0u) {
|
||||
/* IRIS/M.BLACK LINK lamp: sets F791.5 and F716.7. */
|
||||
} else {
|
||||
/* IRIS/M.BLACK LINK lamp: clears F791.5 and F716.7. */
|
||||
}
|
||||
if (value == 0x8000u) {
|
||||
/* SLAVE lamp on. */
|
||||
}
|
||||
if (value == 0x4000u) {
|
||||
/* IRIS/M.BLACK LINK lamp on. */
|
||||
}
|
||||
if (value == 0x0000u) {
|
||||
/* SLAVE and IRIS/M.BLACK LINK latch bits clear through H'2E06. */
|
||||
}
|
||||
break;
|
||||
case 0x0015u:
|
||||
/* 0x0015 call_and_red_tally_lamp_lane; current word H'E82A; handler unknown. */
|
||||
if (value == 0x8000u) {
|
||||
/* CALL lamp and red tally on. */
|
||||
}
|
||||
if (value == 0x0000u) {
|
||||
/* CALL inactive/clear report. */
|
||||
}
|
||||
break;
|
||||
case 0x0017u:
|
||||
/* 0x0017 bars_lamp_lane; current word H'E82E; handler unknown. */
|
||||
if (value == 0x8000u) {
|
||||
/* BARS lamp on. */
|
||||
}
|
||||
if (value == 0x4000u) {
|
||||
/* BARS lamp/latch on. */
|
||||
}
|
||||
if (value == 0x0000u) {
|
||||
/* BARS low write; visible latch may remain. */
|
||||
}
|
||||
break;
|
||||
case 0x001Au:
|
||||
/* 0x001A monitor_selector_lamps; current word H'E834; handler unknown. */
|
||||
if (value == 0x0808u) {
|
||||
/* MONITOR ENC lamp. */
|
||||
}
|
||||
if (value == 0x2020u) {
|
||||
/* MONITOR B lamp. */
|
||||
}
|
||||
if (value == 0x4040u) {
|
||||
/* MONITOR G lamp. */
|
||||
}
|
||||
if (value == 0x8080u) {
|
||||
/* MONITOR R lamp. */
|
||||
}
|
||||
break;
|
||||
case 0x0024u:
|
||||
/* 0x0024 lcd_selector_button_lamp; current word H'E848; dispatch unknown. */
|
||||
if (value == 0x8000u) {
|
||||
/* LCD selector-button lamp visible. */
|
||||
}
|
||||
if (value == 0x0000u) {
|
||||
/* lamp remained visible at 0.5 s in isolation run. */
|
||||
}
|
||||
break;
|
||||
case 0x006Bu:
|
||||
/* 0x006B standard_lamp_lane; current word H'E8D6; handler unknown. */
|
||||
if (value == 0x8000u) {
|
||||
/* STANDARD lamp on. */
|
||||
}
|
||||
break;
|
||||
case 0x0082u:
|
||||
/* 0x0082 iris_readout_lane; current word H'E904; dispatch unknown. */
|
||||
if (value == 0x8000u) {
|
||||
/* IRIS display OP. */
|
||||
}
|
||||
if (value == 0x4000u) {
|
||||
/* IRIS display 1.4. */
|
||||
}
|
||||
if (value == 0x0000u) {
|
||||
/* IRIS display blank. */
|
||||
}
|
||||
break;
|
||||
case 0x0083u:
|
||||
/* 0x0083 combined_iris_shutter_master_gain_status_lane; current word H'E906; dispatch unknown. */
|
||||
if (value == 0x8000u) {
|
||||
/* IRIS AUTO, SHUTTER OFF, MASTER GAIN -3. */
|
||||
}
|
||||
if (value == 0x4000u) {
|
||||
/* IRIS AUTO, SHUTTER OFF, MASTER GAIN 0. */
|
||||
}
|
||||
if (value == 0x2000u) {
|
||||
/* IRIS AUTO, SHUTTER OFF, MASTER GAIN 3. */
|
||||
}
|
||||
if (value == 0x0004u) {
|
||||
/* IRIS AUTO, SHUTTER OFF, MASTER GAIN HP. */
|
||||
}
|
||||
if (value == 0x0000u) {
|
||||
/* same visible state remained at 0.5 s. */
|
||||
}
|
||||
break;
|
||||
case 0x008Fu:
|
||||
/* 0x008F shutter_display_status_lane; current word H'E91E; dispatch unknown. */
|
||||
if (value == 0x8000u) {
|
||||
/* IRIS AUTO plus shutter value beginning with 1. */
|
||||
}
|
||||
if (value == 0x2000u) {
|
||||
/* IRIS AUTO plus shutter 00.0. */
|
||||
}
|
||||
if (value == 0x0800u) {
|
||||
/* IRIS AUTO plus shutter EVS. */
|
||||
}
|
||||
if (value == 0x1000u) {
|
||||
/* IRIS AUTO plus shutter OFF. */
|
||||
}
|
||||
break;
|
||||
case 0x0093u:
|
||||
/* 0x0093 white_balance_black_flare_mode_lane; current word H'E926; dispatch unknown. */
|
||||
if (value == 0x8000u) {
|
||||
/* BLACK/FLARE MANUAL plus white-balance PRESET. */
|
||||
}
|
||||
if (value == 0x4000u) {
|
||||
/* BLACK/FLARE MANUAL plus white-balance AUTO. */
|
||||
}
|
||||
if (value == 0x2000u) {
|
||||
/* BLACK/FLARE MANUAL plus white-balance MANUAL. */
|
||||
}
|
||||
if (value == 0x1020u) {
|
||||
/* BLACK/FLARE MANUAL plus white-balance MANUAL. */
|
||||
}
|
||||
if (value == 0x4040u) {
|
||||
/* BLACK/FLARE AUTO plus white-balance AUTO. */
|
||||
}
|
||||
if (value == 0x8040u) {
|
||||
/* BLACK/FLARE AUTO plus white-balance PRESET. */
|
||||
}
|
||||
if (value == 0x0020u) {
|
||||
/* BLACK/FLARE MANUAL plus white-balance MANUAL. */
|
||||
}
|
||||
if (value == 0x0040u) {
|
||||
/* BLACK/FLARE AUTO plus white-balance MANUAL. */
|
||||
}
|
||||
if (value == 0x0000u) {
|
||||
/* BLACK/FLARE MANUAL plus white-balance MANUAL. */
|
||||
}
|
||||
break;
|
||||
case 0x0110u:
|
||||
/* 0x0110 knee_auto_lamp_or_page_status_lane; current word H'EA20; dispatch unknown. */
|
||||
if (value == 0x8000u) {
|
||||
/* KNEE AUTO lamp/status on. */
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void provisional_iris_mblack_link_button_toggle_report(void)
|
||||
{
|
||||
/* Provisional name for ROM H'200E: When F6DB.7 is asserted and F731 <= 3, the ROM toggles current-table bit 14 at H'E826 based on F791.5, then queues selector 0x0013 through loc_3E54. */
|
||||
/* Source F006.7 / F6DB.7; gate F731 <= 3; current state F791.5. */
|
||||
if ((MEM8[0xF6DBu] & BIT(7)) == 0u) {
|
||||
return;
|
||||
}
|
||||
if (MEM8[0xF731u] > 3u) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ((MEM8[0xF791u] & BIT(5)) == 0u) {
|
||||
/* Requests selector 0x0013=0x4000: 00 00 13 40 00 09. */
|
||||
/* CCU should ACK 05 00 13 00 00 4C, then mirror 00 00 13 40 00 09. */
|
||||
} else {
|
||||
/* Requests selector 0x0013=0x0000: 00 00 13 00 00 49. */
|
||||
/* CCU should ACK 05 00 13 00 00 4C, then mirror 00 00 13 00 00 49. */
|
||||
}
|
||||
}
|
||||
|
||||
void sci1_process_candidate_protocol_command(void)
|
||||
{
|
||||
u8 command = sci1_rx_candidate_command();
|
||||
u16 logical_index = sci1_rx_candidate_logical_index();
|
||||
u16 value = sci1_rx_candidate_value();
|
||||
|
||||
sci1_candidate_panel_selector_annotation(logical_index, value);
|
||||
|
||||
bool session_active = MEM8[0xFAA2u] != 0u;
|
||||
|
||||
if (!session_active) {
|
||||
|
||||
@@ -18,32 +18,32 @@ primary_value_table_candidate H'E000-H'E3FF (negative H'2000; direct H'F900-H'F9
|
||||
accesses=31 reads=21 writes=10 dynamic=11
|
||||
static offsets: H'0000, H'0004, H'0006, H'0046, H'0080, H'0102, H'0124, H'0126, H'014E, H'016E, H'0172, H'01EC, H'0220
|
||||
functions: loc_BBAB:5, loc_2650:3, loc_4096:3, loc_1795:2, loc_19DB:2, loc_1A35:2, loc_48FA:2, vec_ad_adi_3D99:2, <no function>:1, loc_1705:1, loc_174D:1, loc_17C9:1
|
||||
- H'170C read offset H'014E -> H'E14E; loc_1705; BTST.W #15, @H'E14E
|
||||
- H'175A read offset H'016E -> H'E16E; loc_174D; BTST.W #13, @H'E16E
|
||||
- H'179C read offset H'0172 -> H'E172; loc_1795; BTST.W #13, @H'E172
|
||||
- H'17A7 read offset H'0220 -> H'E220; loc_1795; BTST.W #15, @H'E220
|
||||
- H'17D0 read offset H'0126 -> H'E126; loc_17C9; BTST.W #12, @H'E126
|
||||
- H'1802 read offset H'0126 -> H'E126; loc_17FB; BTST.W #12, @H'E126
|
||||
- H'183A read offset H'0126 -> H'E126; loc_182D; BTST.W #5, @H'E126
|
||||
- H'189E read offset H'0126 -> H'E126; loc_1891; BTST.W #5, @H'E126
|
||||
- H'18F4 read offset H'0126 -> H'E126; loc_18E7; BTST.W #5, @H'E126
|
||||
- H'170C read offset H'014E selector 0x0A7 -> H'E14E; loc_1705; BTST.W #15, @H'E14E
|
||||
- H'175A read offset H'016E selector 0x0B7 -> H'E16E; loc_174D; BTST.W #13, @H'E16E
|
||||
- H'179C read offset H'0172 selector 0x0B9 -> H'E172; loc_1795; BTST.W #13, @H'E172
|
||||
- H'17A7 read offset H'0220 selector 0x110 -> H'E220; loc_1795; BTST.W #15, @H'E220
|
||||
- H'17D0 read offset H'0126 selector 0x093 -> H'E126; loc_17C9; BTST.W #12, @H'E126
|
||||
- H'1802 read offset H'0126 selector 0x093 -> H'E126; loc_17FB; BTST.W #12, @H'E126
|
||||
- H'183A read offset H'0126 selector 0x093 -> H'E126; loc_182D; BTST.W #5, @H'E126
|
||||
- H'189E read offset H'0126 selector 0x093 -> H'E126; loc_1891; BTST.W #5, @H'E126
|
||||
- H'18F4 read offset H'0126 selector 0x093 -> H'E126; loc_18E7; BTST.W #5, @H'E126
|
||||
- H'19E3 read index dynamic via R3 operand @(-H'2000,R3); loc_19DB; MOV:G.W @(-H'2000,R3), R0
|
||||
- H'1A03 read index dynamic via R3 operand @(-H'2000,R3); loc_19DB; CMP:G.W @(-H'2000,R3), R1
|
||||
- H'1A3D read index dynamic via R3 operand @(-H'2000,R3); loc_1A35; MOV:G.W @(-H'2000,R3), R0
|
||||
- H'1A6B read index dynamic via R3 operand @(-H'2000,R3); loc_1A35; CMP:G.W @(-H'2000,R3), R0
|
||||
- H'2657 read offset H'0124 -> H'E124; loc_2650; MOV:G.W @H'E124, R0
|
||||
- H'266F read offset H'0004 -> H'E004; loc_2650; BTST.W #13, @H'E004
|
||||
- H'268B read offset H'0124 -> H'E124; loc_2650; CMP:G.W @H'E124, R0
|
||||
- H'3DDA read offset H'0102 -> H'E102; vec_ad_adi_3D99; MOV:G.W @H'E102, R0
|
||||
- H'3DFA read offset H'0102 -> H'E102; vec_ad_adi_3D99; CMP:G.W @H'E102, R1
|
||||
- H'2657 read offset H'0124 selector 0x092 -> H'E124; loc_2650; MOV:G.W @H'E124, R0
|
||||
- H'266F read offset H'0004 selector 0x002 -> H'E004; loc_2650; BTST.W #13, @H'E004
|
||||
- H'268B read offset H'0124 selector 0x092 -> H'E124; loc_2650; CMP:G.W @H'E124, R0
|
||||
- H'3DDA read offset H'0102 selector 0x081 -> H'E102; vec_ad_adi_3D99; MOV:G.W @H'E102, R0
|
||||
- H'3DFA read offset H'0102 selector 0x081 -> H'E102; vec_ad_adi_3D99; CMP:G.W @H'E102, R1
|
||||
- H'3F8C write index dynamic via R0 operand @(-H'2000,R0); <no function>; CLR.W @(-H'2000,R0)
|
||||
- H'402C write offset H'0046 -> H'E046; loc_400C; CLR.W @H'E046
|
||||
- H'402C write offset H'0046 selector 0x023 -> H'E046; loc_400C; CLR.W @H'E046
|
||||
- H'4077 write index dynamic via R0 operand @(-H'2000,R0); loc_4075; CLR.W @(-H'2000,R0)
|
||||
- H'4096 write offset H'0000 -> H'E000; loc_4096; MOV:G.W #H'0080, @H'E000
|
||||
- H'409C write offset H'0006 -> H'E006; loc_4096; MOV:G.W #H'8000, @H'E006
|
||||
- H'40A2 write offset H'0080 -> H'E080; loc_4096; MOV:G.W #H'FFFF, @H'E080
|
||||
- H'490F read offset H'01EC -> H'E1EC; loc_48FA; BTST.W #13, @H'E1EC
|
||||
- H'4915 read offset H'01EC -> H'E1EC; loc_48FA; MOV:G.W @H'E1EC, R0
|
||||
- H'4096 write offset H'0000 selector 0x000 -> H'E000; loc_4096; MOV:G.W #H'0080, @H'E000
|
||||
- H'409C write offset H'0006 selector 0x003 -> H'E006; loc_4096; MOV:G.W #H'8000, @H'E006
|
||||
- H'40A2 write offset H'0080 selector 0x040 -> H'E080; loc_4096; MOV:G.W #H'FFFF, @H'E080
|
||||
- H'490F read offset H'01EC selector 0x0F6 -> H'E1EC; loc_48FA; BTST.W #13, @H'E1EC
|
||||
- H'4915 read offset H'01EC selector 0x0F6 -> H'E1EC; loc_48FA; MOV:G.W @H'E1EC, R0
|
||||
- H'BC75 write index dynamic via R4 operand @(-H'2000,R4); loc_BBAB; MOV:G.W R0, @(-H'2000,R4)
|
||||
- H'BC95 write index dynamic via R4 operand @(-H'2000,R4); loc_BBAB; MOV:G.W R0, @(-H'2000,R4)
|
||||
- H'BCEC read index dynamic via R4 operand @(-H'2000,R4); loc_BBAB; MOV:G.W @(-H'2000,R4), R0
|
||||
@@ -66,16 +66,16 @@ current_value_table_candidate H'E800-H'EBFF (negative H'1800; direct H'F920-H'F9
|
||||
accesses=14 reads=1 writes=13 dynamic=8
|
||||
static offsets: H'0000, H'0006, H'0080, H'0102, H'0124, H'01EC
|
||||
functions: loc_4096:3, loc_BBAB:3, <no function>:1, loc_15E0:1, loc_19DB:1, loc_1A35:1, loc_2650:1, loc_4075:1, loc_48FA:1, loc_BAF2:1
|
||||
- H'15ED write offset H'0102 -> H'E902; loc_15E0; MOV:G.W R1, @H'E902
|
||||
- H'15ED write offset H'0102 selector 0x081 -> H'E902; loc_15E0; MOV:G.W R1, @H'E902
|
||||
- H'1A09 write index dynamic via R3 operand @(-H'1800,R3); loc_19DB; MOV:G.W R1, @(-H'1800,R3)
|
||||
- H'1A71 write index dynamic via R3 operand @(-H'1800,R3); loc_1A35; MOV:G.W R0, @(-H'1800,R3)
|
||||
- H'2691 write offset H'0124 -> H'E924; loc_2650; MOV:G.W R0, @H'E924
|
||||
- H'2691 write offset H'0124 selector 0x092 -> H'E924; loc_2650; MOV:G.W R0, @H'E924
|
||||
- H'3F90 write index dynamic via R0 operand @(-H'1800,R0); <no function>; CLR.W @(-H'1800,R0)
|
||||
- H'407F write index dynamic via R0 operand @(-H'1800,R0); loc_4075; CLR.W @(-H'1800,R0)
|
||||
- H'40A8 write offset H'0000 -> H'E800; loc_4096; MOV:G.W #H'0080, @H'E800
|
||||
- H'40AE write offset H'0006 -> H'E806; loc_4096; MOV:G.W #H'8000, @H'E806
|
||||
- H'40B4 write offset H'0080 -> H'E880; loc_4096; MOV:G.W #H'FFFF, @H'E880
|
||||
- H'491D write offset H'01EC -> H'E9EC; loc_48FA; MOV:G.W R0, @H'E9EC
|
||||
- H'40A8 write offset H'0000 selector 0x000 -> H'E800; loc_4096; MOV:G.W #H'0080, @H'E800
|
||||
- H'40AE write offset H'0006 selector 0x003 -> H'E806; loc_4096; MOV:G.W #H'8000, @H'E806
|
||||
- H'40B4 write offset H'0080 selector 0x040 -> H'E880; loc_4096; MOV:G.W #H'FFFF, @H'E880
|
||||
- H'491D write offset H'01EC selector 0x0F6 -> H'E9EC; loc_48FA; MOV:G.W R0, @H'E9EC
|
||||
- H'BB35 read index dynamic via R0 operand @(-H'1800,R0); loc_BAF2; MOV:G.W @(-H'1800,R0), R4
|
||||
- H'BC79 write index dynamic via R4 operand @(-H'1800,R4); loc_BBAB; MOV:G.W R0, @(-H'1800,R4)
|
||||
- H'BC99 write index dynamic via R4 operand @(-H'1800,R4); loc_BBAB; MOV:G.W R0, @(-H'1800,R4)
|
||||
@@ -85,7 +85,7 @@ flag_table_candidate H'EC00-H'EFFF (negative H'1400; direct H'F980-H'F99F)
|
||||
accesses=6 reads=0 writes=6 dynamic=5
|
||||
static offsets: H'0200
|
||||
functions: loc_BBAB:5, loc_4075:1
|
||||
- H'4088 write offset H'0200 -> H'EE00; loc_4075; CLR.W @(-H'1400,R0)
|
||||
- H'4088 write offset H'0200 selector 0x000 -> H'EE00; loc_4075; CLR.W @(-H'1400,R0)
|
||||
- H'BC82 write index dynamic via R5 operand @(-H'1400,R5); loc_BBAB; BSET.B #7, @(-H'1400,R5)
|
||||
- H'BC9D write index dynamic via R5 operand @(-H'1400,R5); loc_BBAB; BSET.B #7, @(-H'1400,R5)
|
||||
- H'BD22 write index dynamic via R5 operand @(-H'1400,R5); loc_BBAB; BSET.B #7, @(-H'1400,R5)
|
||||
|
||||
@@ -48,9 +48,26 @@ Optionally add periodic state refresh traffic:
|
||||
.\.venv\Scripts\python.exe scripts\ccu_emulator.py --refresh-active --refresh-interval 0.600 --duration 30
|
||||
```
|
||||
|
||||
Enable the bench-proven IRIS/M.BLACK LINK closed-loop module:
|
||||
|
||||
```powershell
|
||||
.\.venv\Scripts\python.exe scripts\ccu_emulator.py --iris-mblack-link --duration 60 --log captures\ccu-iris-mblack-link.txt
|
||||
```
|
||||
|
||||
When the RCP reports selector `0x0013` as `0x4000` or `0x0000`, this module sends:
|
||||
|
||||
```text
|
||||
05 00 13 00 00 4C ; ACK selector 0x0013
|
||||
00 00 13 40 00 09 ; mirror active, or 00 00 13 00 00 49 for clear
|
||||
```
|
||||
|
||||
That matches the bench-proven IRIS/M.BLACK LINK state machine: the RCP reports local intent, the CCU acknowledges the selector, then the CCU mirrors the accepted state back so the next physical press toggles the other way.
|
||||
|
||||
## Layout
|
||||
|
||||
- `frames.py`: checksums, built-in frames, and simple host-frame builders.
|
||||
- `iris_mblack_link.py`: selector `0x0013` ACK-and-mirror state-machine module.
|
||||
- `modules.py`: small protocol-module interface for feature-specific CCU behavior.
|
||||
- `policy.py`: decides whether an RCP frame should be ACKed.
|
||||
- `refresh.py`: optional periodic state-refresh scheduling.
|
||||
- `serial_link.py`: serial read/write plus checksum-resync frame detection.
|
||||
|
||||
@@ -11,6 +11,7 @@ from .frames import (
|
||||
frame_checksum_ok,
|
||||
parse_frame,
|
||||
)
|
||||
from .iris_mblack_link import IrisMblackLinkModule
|
||||
from .policy import AckPolicy
|
||||
|
||||
__all__ = [
|
||||
@@ -19,6 +20,7 @@ __all__ = [
|
||||
"CcuEmulator",
|
||||
"CcuStats",
|
||||
"HEARTBEAT_FRAME",
|
||||
"IrisMblackLinkModule",
|
||||
"NEUTRAL_ACK_FRAME",
|
||||
"AckPolicy",
|
||||
"format_frame",
|
||||
|
||||
@@ -20,6 +20,8 @@ from h8536.bench_connect_lcd import (
|
||||
|
||||
from .controller import CcuConfig, CcuEmulator
|
||||
from .frames import ACTIVE_SEED_COMMAND0, CONNECT_CADENCE_SEQUENCE, NEUTRAL_ACK_FRAME, format_frame, parse_frame
|
||||
from .iris_mblack_link import IrisMblackLinkModule
|
||||
from .modules import CcuModule
|
||||
from .policy import AckPolicy
|
||||
from .refresh import PeriodicRefresh
|
||||
from .serial_link import SerialLink
|
||||
@@ -64,6 +66,18 @@ def build_arg_parser() -> argparse.ArgumentParser:
|
||||
parser.add_argument("--refresh-interval", type=float, default=0.0, help="seconds between optional refresh frames")
|
||||
parser.add_argument("--loop-poll", type=float, default=0.001, help="sleep between service loop iterations")
|
||||
|
||||
parser.add_argument(
|
||||
"--iris-mblack-link",
|
||||
action="store_true",
|
||||
help="enable the selector 0x0013 IRIS/M.BLACK LINK ACK-and-mirror module",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--iris-mblack-link-mirror-delay",
|
||||
type=float,
|
||||
default=0.050,
|
||||
help="seconds between the selector 0x0013 ACK and command-0 mirror",
|
||||
)
|
||||
|
||||
parser.add_argument("--power-cycle", action="store_true", help="power-cycle DUT through relay before starting")
|
||||
parser.add_argument("--relay-port", default="COM6", help="Pico relay serial port")
|
||||
parser.add_argument("--relay-baud", type=int, default=115200, help="Pico relay serial baud rate")
|
||||
@@ -80,10 +94,11 @@ def main(argv: list[str] | None = None, *, stdout: TextIO = sys.stdout) -> int:
|
||||
args = build_arg_parser().parse_args(argv)
|
||||
seed_frames = _seed_frames(args)
|
||||
refresh_frames = _refresh_frames(args)
|
||||
modules = _modules(args)
|
||||
log_path = args.log or _default_log_path()
|
||||
|
||||
if args.dry_run:
|
||||
_print_dry_run(args, seed_frames, refresh_frames, log_path, stdout)
|
||||
_print_dry_run(args, seed_frames, refresh_frames, modules, log_path, stdout)
|
||||
return 0
|
||||
|
||||
serial = _import_serial()
|
||||
@@ -100,6 +115,8 @@ def main(argv: list[str] | None = None, *, stdout: TextIO = sys.stdout) -> int:
|
||||
f"refresh_interval={args.refresh_interval:.3f}s frames="
|
||||
+ " | ".join(format_frame(frame) for frame in refresh_frames)
|
||||
)
|
||||
if modules:
|
||||
logger.emit("modules=" + " | ".join(module.name for module in modules))
|
||||
|
||||
with open_device_serial(serial, args) as device:
|
||||
if args.power_cycle:
|
||||
@@ -128,7 +145,9 @@ def main(argv: list[str] | None = None, *, stdout: TextIO = sys.stdout) -> int:
|
||||
ack_unlabeled_checksum_frames=not args.no_ack_unlabeled,
|
||||
)
|
||||
refresh = PeriodicRefresh(frames=refresh_frames, interval=args.refresh_interval)
|
||||
CcuEmulator(link, logger, config=config, ack_policy=policy, refresh=refresh).run(args.duration)
|
||||
CcuEmulator(link, logger, config=config, ack_policy=policy, refresh=refresh, modules=modules).run(
|
||||
args.duration
|
||||
)
|
||||
return 0
|
||||
finally:
|
||||
if relay is not None:
|
||||
@@ -153,10 +172,18 @@ def _refresh_frames(args: argparse.Namespace) -> list[bytes]:
|
||||
return frames
|
||||
|
||||
|
||||
def _modules(args: argparse.Namespace) -> tuple[CcuModule, ...]:
|
||||
modules: list[CcuModule] = []
|
||||
if args.iris_mblack_link:
|
||||
modules.append(IrisMblackLinkModule(mirror_delay=max(0.0, args.iris_mblack_link_mirror_delay)))
|
||||
return tuple(modules)
|
||||
|
||||
|
||||
def _print_dry_run(
|
||||
args: argparse.Namespace,
|
||||
seed_frames: list[bytes],
|
||||
refresh_frames: list[bytes],
|
||||
modules: tuple[CcuModule, ...],
|
||||
log_path: Path,
|
||||
stdout: TextIO,
|
||||
) -> None:
|
||||
@@ -170,6 +197,7 @@ def _print_dry_run(
|
||||
+ (" | ".join(format_frame(frame) for frame in refresh_frames) or "none"),
|
||||
file=stdout,
|
||||
)
|
||||
print("modules=" + (" | ".join(module.name for module in modules) or "none"), file=stdout)
|
||||
|
||||
|
||||
def _default_log_path() -> Path:
|
||||
|
||||
@@ -6,6 +6,7 @@ from dataclasses import dataclass
|
||||
from h8536.bench_connect_lcd import BenchLogger, format_frame
|
||||
|
||||
from .frames import ACTIVE_SEED_COMMAND0
|
||||
from .modules import CcuModule, ModuleDecision
|
||||
from .policy import AckPolicy
|
||||
from .refresh import PeriodicRefresh
|
||||
from .serial_link import RxFrame, SerialLink
|
||||
@@ -16,6 +17,7 @@ class CcuStats:
|
||||
rx_frames: int = 0
|
||||
tx_frames: int = 0
|
||||
ack_frames: int = 0
|
||||
module_frames: int = 0
|
||||
seed_frames: int = 0
|
||||
refresh_frames: int = 0
|
||||
started_at: float = 0.0
|
||||
@@ -48,12 +50,14 @@ class CcuEmulator:
|
||||
config: CcuConfig | None = None,
|
||||
ack_policy: AckPolicy | None = None,
|
||||
refresh: PeriodicRefresh | None = None,
|
||||
modules: tuple[CcuModule, ...] = (),
|
||||
) -> None:
|
||||
self.link = link
|
||||
self.logger = logger
|
||||
self.config = config or CcuConfig()
|
||||
self.ack_policy = ack_policy or AckPolicy()
|
||||
self.refresh = refresh or PeriodicRefresh()
|
||||
self.modules = modules
|
||||
self.stats = CcuStats()
|
||||
|
||||
def run(self, duration: float) -> CcuStats:
|
||||
@@ -61,7 +65,7 @@ class CcuEmulator:
|
||||
self.logger.event(
|
||||
"CCU_START "
|
||||
f"duration={duration:.3f}s seed_frames={len(self.config.seed_frames)} "
|
||||
f"ack={format_frame(self.ack_policy.ack_frame)}"
|
||||
f"ack={format_frame(self.ack_policy.ack_frame)} modules={len(self.modules)}"
|
||||
)
|
||||
self._wait_ready()
|
||||
self._send_seed_frames()
|
||||
@@ -115,6 +119,9 @@ class CcuEmulator:
|
||||
def _service_rx(self) -> None:
|
||||
for item in self.link.read_available():
|
||||
self._record_rx(item)
|
||||
suppress_default_ack = self._service_modules(item)
|
||||
if suppress_default_ack:
|
||||
continue
|
||||
decision = self.ack_policy.decide(item.frame, item.label)
|
||||
if not decision.should_ack:
|
||||
self.logger.event(f"ACK_SKIP reason={decision.reason} frame={format_frame(item.frame)}")
|
||||
@@ -125,6 +132,28 @@ class CcuEmulator:
|
||||
self.stats.ack_frames += 1
|
||||
self.stats.tx_frames += 1
|
||||
|
||||
def _service_modules(self, item: RxFrame) -> bool:
|
||||
suppress_default_ack = False
|
||||
for module in self.modules:
|
||||
decision = module.on_rx(item.frame, item.label)
|
||||
if decision is None:
|
||||
continue
|
||||
if decision.reason:
|
||||
self.logger.event(
|
||||
f"MODULE {module.name} reason={decision.reason} frame={format_frame(item.frame)}"
|
||||
)
|
||||
self._send_module_decision(decision)
|
||||
suppress_default_ack = suppress_default_ack or decision.suppress_default_ack
|
||||
return suppress_default_ack
|
||||
|
||||
def _send_module_decision(self, decision: ModuleDecision) -> None:
|
||||
for tx in decision.tx:
|
||||
if tx.delay > 0:
|
||||
time.sleep(tx.delay)
|
||||
self.link.send(tx.frame, tx.label)
|
||||
self.stats.module_frames += 1
|
||||
self.stats.tx_frames += 1
|
||||
|
||||
def _service_refresh(self) -> None:
|
||||
for frame in self.refresh.due_frames():
|
||||
self.link.send(frame, "refresh")
|
||||
@@ -141,6 +170,7 @@ class CcuEmulator:
|
||||
self.logger.emit(f"rx_frames={self.stats.rx_frames}")
|
||||
self.logger.emit(f"tx_frames={self.stats.tx_frames}")
|
||||
self.logger.emit(f"ack_frames={self.stats.ack_frames}")
|
||||
self.logger.emit(f"module_frames={self.stats.module_frames}")
|
||||
self.logger.emit(f"seed_frames={self.stats.seed_frames}")
|
||||
self.logger.emit(f"refresh_frames={self.stats.refresh_frames}")
|
||||
self.logger.emit(f"resync_events={self.link.detector.resync_events}")
|
||||
|
||||
65
ccu_emulator/iris_mblack_link.py
Normal file
65
ccu_emulator/iris_mblack_link.py
Normal file
@@ -0,0 +1,65 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
|
||||
from .frames import build_frame, frame_checksum_ok
|
||||
from .modules import ModuleDecision, ModuleTx
|
||||
|
||||
|
||||
SELECTOR_IRIS_MBLACK_LINK = 0x0013
|
||||
IRIS_MBLACK_LINK_CLEAR = 0x0000
|
||||
IRIS_MBLACK_LINK_ACTIVE = 0x4000
|
||||
|
||||
|
||||
def selector_from_frame(frame: bytes) -> int | None:
|
||||
if len(frame) != 6:
|
||||
return None
|
||||
if frame[1] == 0x00:
|
||||
return frame[2]
|
||||
if frame[1] == 0x01:
|
||||
return 0x0080 + frame[2]
|
||||
if frame[1] == 0x02:
|
||||
return 0x0180 + frame[2]
|
||||
return None
|
||||
|
||||
|
||||
def value_from_frame(frame: bytes) -> int | None:
|
||||
if len(frame) != 6:
|
||||
return None
|
||||
return ((frame[3] << 8) | frame[4]) & 0xFFFF
|
||||
|
||||
|
||||
@dataclass
|
||||
class IrisMblackLinkModule:
|
||||
"""Closed-loop CCU side for the IRIS/M.BLACK LINK button/report path."""
|
||||
|
||||
mirror_delay: float = 0.050
|
||||
report_commands: frozenset[int] = frozenset({0x00, 0x01, 0x02})
|
||||
handled_values: frozenset[int] = frozenset({IRIS_MBLACK_LINK_CLEAR, IRIS_MBLACK_LINK_ACTIVE})
|
||||
name: str = "iris_mblack_link"
|
||||
|
||||
def on_rx(self, frame: bytes, label: str = "") -> ModuleDecision | None:
|
||||
if not frame_checksum_ok(frame) or frame[0] not in self.report_commands:
|
||||
return None
|
||||
selector = selector_from_frame(frame)
|
||||
if selector != SELECTOR_IRIS_MBLACK_LINK:
|
||||
return None
|
||||
value = value_from_frame(frame)
|
||||
if value not in self.handled_values:
|
||||
return None
|
||||
|
||||
state = "active" if value == IRIS_MBLACK_LINK_ACTIVE else "clear"
|
||||
ack = build_frame(0x05, SELECTOR_IRIS_MBLACK_LINK, 0x0000)
|
||||
mirror = build_frame(0x00, SELECTOR_IRIS_MBLACK_LINK, value)
|
||||
return ModuleDecision(
|
||||
tx=(
|
||||
ModuleTx(ack, f"{self.name} ack selector=0x0013 value=0x{value:04X}"),
|
||||
ModuleTx(
|
||||
mirror,
|
||||
f"{self.name} mirror {state} selector=0x0013 value=0x{value:04X}",
|
||||
delay=self.mirror_delay,
|
||||
),
|
||||
),
|
||||
suppress_default_ack=True,
|
||||
reason=f"{self.name}_{state}_report",
|
||||
)
|
||||
25
ccu_emulator/modules.py
Normal file
25
ccu_emulator/modules.py
Normal file
@@ -0,0 +1,25 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from typing import Protocol
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class ModuleTx:
|
||||
frame: bytes
|
||||
label: str
|
||||
delay: float = 0.0
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class ModuleDecision:
|
||||
tx: tuple[ModuleTx, ...] = ()
|
||||
suppress_default_ack: bool = False
|
||||
reason: str = ""
|
||||
|
||||
|
||||
class CcuModule(Protocol):
|
||||
name: str
|
||||
|
||||
def on_rx(self, frame: bytes, label: str = "") -> ModuleDecision | None:
|
||||
"""Inspect an RCP frame and optionally provide CCU response frames."""
|
||||
78
docs/pt2-iris-mblack-link-rom-trace.md
Normal file
78
docs/pt2-iris-mblack-link-rom-trace.md
Normal file
@@ -0,0 +1,78 @@
|
||||
# PT2 IRIS/M.BLACK LINK ROM Trace
|
||||
|
||||
This note records the ROM evidence for the bench-visible
|
||||
`IRIS/M.BLACK LINK` lamp.
|
||||
|
||||
## Confirmed Host Trigger
|
||||
|
||||
The isolated bench trigger is:
|
||||
|
||||
```text
|
||||
00 00 13 40 00 09 ; command 0, selector 0x0013, value 0x4000
|
||||
```
|
||||
|
||||
Command 0 mirrors nonzero selector writes into both `E000[selector]` and
|
||||
`E800[selector]`, then queues the selector for internal processing. For this
|
||||
frame that means:
|
||||
|
||||
```text
|
||||
E000[0x0013] = 0x4000
|
||||
E800[0x0013] = 0x4000
|
||||
```
|
||||
|
||||
The current-table word is `E800 + 2 * 0x0013 = H'E826`.
|
||||
|
||||
## Selector Handler
|
||||
|
||||
The selector dispatch table maps selector `0x0013` to `H'2E06`.
|
||||
|
||||
Focused linear decode:
|
||||
|
||||
```text
|
||||
H'2E06 BTST.W #15, @H'E826
|
||||
set/clear F791.6 and F713.4
|
||||
|
||||
H'2E1E BTST.W #14, @H'E826
|
||||
set/clear F791.5 and F716.7
|
||||
```
|
||||
|
||||
Bench labels:
|
||||
|
||||
| Selector value | ROM latch bits | Visible result |
|
||||
| --- | --- | --- |
|
||||
| `0x8000` | `F791.6`, `F713.4` | far-right `SLAVE` lamp |
|
||||
| `0x4000` | `F791.5`, `F716.7` | `IRIS/M.BLACK LINK` lamp |
|
||||
| `0x0000` | clears both bit groups through `H'2E06` | both latch groups clear |
|
||||
|
||||
## Other Trigger Path
|
||||
|
||||
There is also a local panel-input path:
|
||||
|
||||
```text
|
||||
F006.7 / F6DB.7 -> H'200E -> H'E826 bit14 -> loc_3E54 queues selector 0x0013
|
||||
```
|
||||
|
||||
At `H'200E`, the ROM checks `F6DB.7` and requires `F731 <= 3`. It then uses
|
||||
`F791.5` as a current-state toggle:
|
||||
|
||||
- if `F791.5` is clear, it sets `H'E826.14`,
|
||||
- if `F791.5` is set, it clears `H'E826.14`,
|
||||
- in both cases it calls `loc_3E54` with selector `0x0013`.
|
||||
|
||||
This is not a separate lamp driver. It feeds the same `E800[0x0013].14`
|
||||
state consumed by the selector handler.
|
||||
|
||||
## Practical Meaning
|
||||
|
||||
The strongest current model is:
|
||||
|
||||
- CCU/host can drive the lamp directly by sending selector `0x0013` value
|
||||
`0x4000` through command 0.
|
||||
- The panel can also toggle/report the same state through a local input lane,
|
||||
but only when the relevant session/page gate is open.
|
||||
- No other decoded selector currently appears to directly set the same
|
||||
`F791.5` plus `F716.7` latch pair.
|
||||
|
||||
Generated semantics now label selector `0x0013`, `E800[0x0013]`, and the
|
||||
`IRIS/M.BLACK LINK` / `SLAVE` bit meanings so pseudo-code output does not leave
|
||||
this as a generic table write.
|
||||
193
docs/pt2-iris-mblack-link-state-machine.md
Normal file
193
docs/pt2-iris-mblack-link-state-machine.md
Normal file
@@ -0,0 +1,193 @@
|
||||
# PT2 IRIS/M.BLACK LINK State Machine
|
||||
|
||||
Date: 2026-05-27
|
||||
|
||||
This note records the bench-proven closed loop for the `IRIS/M.BLACK LINK`
|
||||
button/lamp path.
|
||||
|
||||
## Short Answer
|
||||
|
||||
There is no current evidence that the CCU first sends a separate
|
||||
"this function exists" capability command for `IRIS/M.BLACK LINK`.
|
||||
|
||||
The stronger model is:
|
||||
|
||||
1. The CCU/RCP session must be awake/active.
|
||||
2. The CCU must service the RCP report queue.
|
||||
3. The CCU is the authoritative owner of selector state.
|
||||
4. When the RCP reports a local button intent, the CCU ACKs the report and
|
||||
mirrors the resulting selector value back to the RCP.
|
||||
|
||||
For this control, selector `0x0013` bit `0x4000` is the
|
||||
`IRIS/M.BLACK LINK` state.
|
||||
|
||||
## Proven Frames
|
||||
|
||||
Selector value frames:
|
||||
|
||||
```text
|
||||
00 00 13 40 00 09 ; command 0, selector 0x0013, value 0x4000, active
|
||||
00 00 13 00 00 49 ; command 0, selector 0x0013, value 0x0000, clear
|
||||
```
|
||||
|
||||
Report ACK:
|
||||
|
||||
```text
|
||||
05 00 13 00 00 4C ; command 5 ACK/continuation for selector 0x0013
|
||||
```
|
||||
|
||||
Readback request:
|
||||
|
||||
```text
|
||||
01 00 13 00 00 48 ; command 1 read selector 0x0013
|
||||
```
|
||||
|
||||
Observed readback shapes:
|
||||
|
||||
```text
|
||||
04 00 13 40 00 0D ; command-0 write response, selector 0x0013 active
|
||||
04 00 13 00 00 4D ; command-0 write response, selector 0x0013 clear
|
||||
04 13 00 40 00 0D ; command-1 readback response, selector 0x0013 active
|
||||
04 13 00 00 00 4D ; command-1 readback response, selector 0x0013 clear
|
||||
```
|
||||
|
||||
## Successful Closed Loop
|
||||
|
||||
Capture:
|
||||
|
||||
```text
|
||||
captures/iris-mblack-link-mirror-state-machine.txt
|
||||
captures/iris-mblack-link-mirror-state-machine-result.json
|
||||
```
|
||||
|
||||
Scenario:
|
||||
|
||||
```text
|
||||
scenarios/iris-mblack-link-mirror-state-machine.json
|
||||
```
|
||||
|
||||
The visible panel behavior was:
|
||||
|
||||
```text
|
||||
press 1: lamp on
|
||||
press 2: lamp off
|
||||
press 3: lamp on
|
||||
```
|
||||
|
||||
The serial behavior matched that cycle.
|
||||
|
||||
### Baseline Clear
|
||||
|
||||
```text
|
||||
TX 00 00 13 00 00 49
|
||||
RX 04 00 13 00 00 4D
|
||||
```
|
||||
|
||||
### Press 1: Active
|
||||
|
||||
```text
|
||||
RX 00 00 13 40 00 09
|
||||
TX 05 00 13 00 00 4C ; ACK report
|
||||
TX 00 00 13 40 00 09 ; mirror active back
|
||||
RX 04 00 13 40 00 0D
|
||||
TX 01 00 13 00 00 48 ; readback
|
||||
RX 04 13 00 40 00 0D
|
||||
```
|
||||
|
||||
### Press 2: Clear
|
||||
|
||||
```text
|
||||
RX 00 00 13 00 00 49
|
||||
TX 05 00 13 00 00 4C ; ACK report
|
||||
TX 00 00 13 00 00 49 ; mirror clear back
|
||||
RX 04 00 13 00 00 4D
|
||||
TX 01 00 13 00 00 48 ; readback
|
||||
RX 04 13 00 00 00 4D
|
||||
```
|
||||
|
||||
### Press 3: Active Again
|
||||
|
||||
```text
|
||||
RX 00 00 13 40 00 09
|
||||
TX 05 00 13 00 00 4C
|
||||
TX 00 00 13 40 00 09
|
||||
RX 04 00 13 40 00 0D
|
||||
TX 01 00 13 00 00 48
|
||||
RX 04 13 00 40 00 0D
|
||||
```
|
||||
|
||||
## Interpretation
|
||||
|
||||
The RCP does not appear to treat the local button press as final local state.
|
||||
It reports local intent to the CCU.
|
||||
|
||||
The CCU then:
|
||||
|
||||
1. ACKs the selector report with command `5`.
|
||||
2. Applies the chosen state back to the RCP with command `0`.
|
||||
|
||||
The RCP uses the mirrored/current selector state to decide the next toggle
|
||||
direction. This explains the earlier failed/incomplete behavior:
|
||||
|
||||
- Without mirroring `0x0013=0x4000` back, repeated presses could keep reporting
|
||||
active because the RCP still believed the selector was clear.
|
||||
- Once the CCU mirrored active back, the next press reported clear.
|
||||
- Once the CCU mirrored clear back, the next press reported active again.
|
||||
|
||||
## Wakeup Versus Capability
|
||||
|
||||
The successful test also had active session traffic:
|
||||
|
||||
```text
|
||||
00 00 00 80 80 5A ; active selector-zero keepalive/report
|
||||
05 00 00 00 00 5F ; ACK for selector zero
|
||||
```
|
||||
|
||||
That traffic looks like general session/connected behavior, not a
|
||||
per-function enable for `IRIS/M.BLACK LINK`.
|
||||
|
||||
So the current working model is:
|
||||
|
||||
- `CONNECT: OK` / active rhythm opens the report path and keeps the panel from
|
||||
falling back to `CONNECT: NOT ACT`.
|
||||
- Selector `0x0013` state tells the RCP the current value of this specific
|
||||
control/lamp.
|
||||
- There may still be feature-specific gates for other controls, but this test
|
||||
did not require a distinct `IRIS/M.BLACK LINK exists` command.
|
||||
|
||||
## ROM Correlation
|
||||
|
||||
The ROM trace in `docs/pt2-iris-mblack-link-rom-trace.md` matches the bench
|
||||
behavior:
|
||||
|
||||
```text
|
||||
F006.7 / F6DB.7 -> H'200E -> H'E826 bit14 -> loc_3E54 queues selector 0x0013
|
||||
```
|
||||
|
||||
At `H'200E`, the ROM reads `F791.5` as the current `IRIS/M.BLACK LINK` state:
|
||||
|
||||
- if `F791.5` is clear, the local press queues/set reports `0x0013=0x4000`;
|
||||
- if `F791.5` is set, the local press queues/clear reports `0x0013=0x0000`.
|
||||
|
||||
The selector handler for `0x0013` updates `F791.5` from `E800[0x0013].14`.
|
||||
That is why mirroring command-0 selector state back to the RCP completes the
|
||||
toggle loop.
|
||||
|
||||
## Implications
|
||||
|
||||
For a CCU emulator:
|
||||
|
||||
- Maintain an authoritative selector table.
|
||||
- Treat RCP button reports as requested state changes, not just notifications.
|
||||
- ACK each report with command `5`.
|
||||
- Write the accepted selector value back with command `0`.
|
||||
- Keep the selector-zero/session rhythm alive separately.
|
||||
|
||||
For other latched buttons:
|
||||
|
||||
- The same pattern is likely: RCP emits a selector report, then expects the CCU
|
||||
to mirror the accepted state back.
|
||||
- A button that only reports "active" repeatedly may not be broken; it may be
|
||||
waiting for the CCU to update the selector state that controls its next
|
||||
toggle direction.
|
||||
|
||||
@@ -71,6 +71,81 @@ Follow-up `lamp-isolate-neighbor-single-boot` result:
|
||||
|
||||
This confirms that the host/CCU can directly drive panel lamps through selector-table writes. It also validates using the ROM dispatch-neighbor list around `0x0007` and `0x0015` as a high-value lamp map.
|
||||
|
||||
Follow-up `panel-atlas-standard-master-*` webcam runs:
|
||||
|
||||
- `0x0012`, `0x0013`, and `0x0014` high-nibble/selected-bit sweeps did not
|
||||
produce a clean STANDARD or MASTER lamp trigger. The `0x0013=0x8000` SLAVE
|
||||
positive control still worked.
|
||||
- `0x0010`, `0x0011`, `0x0015`, `0x0016`, `0x0017`, `0x0018`, `0x0019`, and
|
||||
`0x001A` high-nibble sweeps did not produce a clean STANDARD or MASTER lamp
|
||||
trigger.
|
||||
- `0x0008` through `0x000F` high-nibble sweeps did not produce a clean
|
||||
STANDARD or MASTER lamp trigger.
|
||||
- `0x0017=0x4000` lit the same far-right bottom BARS lamp/latch as the known
|
||||
`0x0017=0x8000` family. Later `0x0018` rows in that run were latch-contaminated
|
||||
and need fresh-boot isolation before assigning a separate meaning.
|
||||
|
||||
Current implication: STANDARD and MASTER are probably not simple direct
|
||||
command-0 high-nibble writes in the `0x0008`-`0x001A` pocket, despite the older
|
||||
broad run that made MASTER flash. Treat that older observation as a real
|
||||
bench-visible event but not yet an isolated selector mapping.
|
||||
|
||||
Later refinement: the ROM-derived fresh-boot output sweep found a clean
|
||||
STANDARD trigger at `0x006B = 0x8000` (`00 00 6B 80 00 B1`). It also reclassed
|
||||
the formerly vague `0x001A` pocket as the MONITOR selector cluster:
|
||||
|
||||
| Selector/value | Visible effect |
|
||||
| --- | --- |
|
||||
| `0x001A = 0x0808` | MONITOR ENC |
|
||||
| `0x001A = 0x2020` | MONITOR B |
|
||||
| `0x001A = 0x4040` | MONITOR G |
|
||||
| `0x001A = 0x8080` | MONITOR R |
|
||||
| `0x006B = 0x8000` | STANDARD |
|
||||
|
||||
### `panel-atlas-big-visual-sweep-0001-017f-highbits`
|
||||
|
||||
The broad webcam sweep and fresh-boot isolation pass produced these confirmed
|
||||
or near-confirmed panel-output mappings:
|
||||
|
||||
| Selector/value | Current meaning |
|
||||
| --- | --- |
|
||||
| `0x0013 = 0x4000` | `IRIS/M.BLACK LINK` lamp |
|
||||
| `0x0024 = 0x8000` | LCD selector-button lamp |
|
||||
| `0x0024 = 0x0000` | LCD selector-button lamp remained visible at 0.5 s; not a simple clear in this timing window |
|
||||
| `0x0082 = 0x8000` | IRIS readout `OP` |
|
||||
| `0x0082 = 0x4000` | IRIS readout `1.4` |
|
||||
| `0x0082 = 0x0000` | IRIS readout blank |
|
||||
| `0x0083 = 0x8000` | MASTER GAIN `-3`, SHUTTER `OFF`, and IRIS AUTO |
|
||||
| `0x0083 = 0x0000` | same visible state remained at 0.5 s; likely latched/copied elsewhere |
|
||||
| `0x0093 = 0x8000` | BLACK/FLARE MANUAL plus white-balance PRESET |
|
||||
| `0x0093 = 0x4000` | BLACK/FLARE MANUAL plus white-balance AUTO |
|
||||
| `0x0093 = 0x2000` | BLACK/FLARE MANUAL plus white-balance MANUAL |
|
||||
| `0x0093 = 0x0000` | BLACK/FLARE MANUAL plus white-balance MANUAL |
|
||||
|
||||
Run health was good: no resync events, no dropped bytes, and command-4
|
||||
readbacks appeared for the listed writes.
|
||||
|
||||
ROM trace now confirms the `0x0013` bit split:
|
||||
|
||||
- `E800[0x0013]` is `H'E826`.
|
||||
- Selector `0x0013` dispatches to `H'2E06`.
|
||||
- `H'E826.15` sets/clears `F791.6` and `F713.4`, matching SLAVE.
|
||||
- `H'E826.14` sets/clears `F791.5` and `F716.7`, matching
|
||||
`IRIS/M.BLACK LINK`.
|
||||
- Local handler `H'200E` can also toggle `H'E826.14` from panel input
|
||||
`F006.7/F6DB.7` when its session gate allows it.
|
||||
|
||||
Interpretation:
|
||||
|
||||
- `0x0082` is the cleanest direct readout lane found so far: set values update
|
||||
the IRIS display and `0x0000` blanks it.
|
||||
- `0x0083` appears to be a combined status/display lane rather than only the
|
||||
MASTER GAIN display. Its `0x8000` state also brings up SHUTTER `OFF` and IRIS
|
||||
AUTO, and the clear write did not visually clear it at 0.5 s.
|
||||
- `0x0093` selects white-balance mode. In this run, `0x2000` and `0x0000` both
|
||||
presented white-balance MANUAL, so bit 13 may be redundant in this context or
|
||||
may need another gate to show a distinct state.
|
||||
|
||||
### `lamp-broad-status-selector-sweep`
|
||||
|
||||
Visible result:
|
||||
|
||||
338
docs/pt2-panel-atlas.md
Normal file
338
docs/pt2-panel-atlas.md
Normal file
@@ -0,0 +1,338 @@
|
||||
# PT2 Panel Atlas
|
||||
|
||||
This note tracks bench tests that intentionally drive visible panel outputs from
|
||||
PT2/command-0 table writes.
|
||||
|
||||
## Scenario Files
|
||||
|
||||
Current compact atlas scenarios:
|
||||
|
||||
```text
|
||||
scenarios/panel-atlas-operator-lamps-v1.json
|
||||
scenarios/panel-atlas-readout-status-v1.json
|
||||
scenarios/panel-atlas-right-stack-isolation-v1.json
|
||||
scenarios/panel-atlas-right-stack-fresh-latch-v1.json
|
||||
scenarios/panel-atlas-standard-master-bit-sweep-v1.json
|
||||
scenarios/panel-atlas-standard-master-neighbor-sweep-v2.json
|
||||
scenarios/panel-atlas-standard-master-lower-neighbor-sweep-v3.json
|
||||
```
|
||||
|
||||
Both are designed for webcam capture with the calibrated bench settings:
|
||||
|
||||
```text
|
||||
--parity E --camera-index 4 --snapshot-delays 0.5
|
||||
```
|
||||
|
||||
## Run: 2026-05-27
|
||||
|
||||
Logs:
|
||||
|
||||
```text
|
||||
captures/panel-atlas-operator-lamps-v1-webcam.txt
|
||||
captures/panel-atlas-readout-status-v1-webcam.txt
|
||||
captures/panel-atlas-right-stack-isolation-v1-webcam.txt
|
||||
captures/panel-atlas-right-stack-fresh-latch-v1-webcam.txt
|
||||
captures/panel-atlas-standard-master-bit-sweep-v1-webcam.txt
|
||||
captures/panel-atlas-standard-master-neighbor-sweep-v2-webcam.txt
|
||||
captures/panel-atlas-standard-master-lower-neighbor-sweep-v3-webcam.txt
|
||||
```
|
||||
|
||||
Snapshots:
|
||||
|
||||
```text
|
||||
captures/panel-atlas-operator-lamps-v1-webcam-shots/
|
||||
captures/panel-atlas-readout-status-v1-webcam-shots/
|
||||
captures/panel-atlas-right-stack-isolation-v1-webcam-shots/
|
||||
captures/panel-atlas-right-stack-fresh-latch-v1-webcam-shots/
|
||||
captures/panel-atlas-standard-master-bit-sweep-v1-webcam-shots/
|
||||
captures/panel-atlas-standard-master-neighbor-sweep-v2-webcam-shots/
|
||||
captures/panel-atlas-standard-master-lower-neighbor-sweep-v3-webcam-shots/
|
||||
```
|
||||
|
||||
Serial health:
|
||||
|
||||
| Run | RX frames | TX frames | Resync | Dropped bytes |
|
||||
| --- | ---: | ---: | ---: | ---: |
|
||||
| operator lamps | 82 | 19 | 0 | 0 |
|
||||
| readout/status | 76 | 16 | 0 | 0 |
|
||||
| right-stack isolation | 115 | 26 | 0 | 0 |
|
||||
| right-stack fresh latch | 20 | 12 | 0 | 0 |
|
||||
| standard/master bit sweep v1 | 144 | 34 | 0 | 0 |
|
||||
| standard/master neighbor sweep v2 | 188 | 47 | 0 | 0 |
|
||||
| standard/master lower-neighbor sweep v3 | 188 | 47 | 0 | 0 |
|
||||
|
||||
The compact and isolation runs stayed in the expected table-readback plus
|
||||
`02 00 02 00 00 5A` CONNECT-OK response rhythm.
|
||||
|
||||
The fresh-latch run ended with one trailing unframed byte after the final send.
|
||||
There were no resync events or dropped bytes; this is consistent with the run
|
||||
ending while the next response frame was just beginning.
|
||||
|
||||
## Confirmed Visible Effects
|
||||
|
||||
## Far-Right Stack Reference
|
||||
|
||||
The physical far-right stack, top to bottom, is:
|
||||
|
||||
```text
|
||||
TALLY light, with camera number
|
||||
STANDARD
|
||||
MASTER
|
||||
SLAVE
|
||||
CAM POWER
|
||||
BARS
|
||||
```
|
||||
|
||||
Use these names instead of generic "right-side status" labels when reviewing
|
||||
webcam crops.
|
||||
|
||||
Readout/status run:
|
||||
|
||||
| Frame | Selector/value | Visible effect |
|
||||
| --- | --- | --- |
|
||||
| `00 01 0F 08 00 5C` | `E000[0x008F]=0x0800` | SHUTTER display shows `EUS`/likely `EVS`; iris AUTO lamp is lit |
|
||||
| `00 01 0F 10 00 44` | `E000[0x008F]=0x1000` | SHUTTER display shows `OFF`; iris AUTO lamp remains lit |
|
||||
| `00 01 13 80 00 C8` | `E000[0x0093]=0x8000` | white-balance / black-flare lamp cluster changes, consistent with prior preset/manual observation |
|
||||
| `00 01 13 90 20 F8` | `E000[0x0093]=0x9020` | black-flare manual-context candidate remains visible |
|
||||
| `00 01 13 90 FF 27` | `E000[0x0093]=0x90FF` | black-flare auto-context candidate remains visible |
|
||||
| `00 01 90 80 00 4B` | `E000[0x0110]=0x8000` | KNEE AUTO lamp lights |
|
||||
|
||||
The `0x00B9` gate writes in this compact run did not light KNEE AUTO by
|
||||
themselves at the 0.5 s snapshot point. That matches the ROM model where
|
||||
`0x00B9.13` is more of a report-path gate, while `0x0110.15` is the stronger
|
||||
visible KNEE AUTO source.
|
||||
|
||||
Operator-lamp run:
|
||||
|
||||
| Frame | Selector/value | Visible effect |
|
||||
| --- | --- | --- |
|
||||
| `00 00 13 80 00 C9` | `E000[0x0013]=0x8000` | far-right SLAVE lamp lights, based on stack order and crop position |
|
||||
| `00 00 17 80 00 CD` | `E000[0x0017]=0x8000` | far-right bottom white BARS lamp lights |
|
||||
| `00 00 1A 80 00 C0` | `E000[0x001A]=0x8000` | lower right white lamp appeared lit in the compact run, later refined as likely `0x0017` carryover |
|
||||
|
||||
Right-stack isolation/fresh-latch refinement:
|
||||
|
||||
| Frame | Selector/value | Visible effect |
|
||||
| --- | --- | --- |
|
||||
| `00 00 15 80 00 CF` | `E000[0x0015]=0x8000` | CALL lamp lights |
|
||||
| `00 00 15 00 00 4F` | `E000[0x0015]=0x0000` | CALL lamp clears |
|
||||
| `00 00 13 80 00 C9` | `E000[0x0013]=0x8000` | far-right SLAVE lamp lights |
|
||||
| `00 00 13 00 00 49` | `E000[0x0013]=0x0000` | SLAVE lamp clears |
|
||||
| `00 00 17 80 00 CD` | `E000[0x0017]=0x8000` | far-right bottom white BARS lamp lights |
|
||||
| `00 00 17 00 00 4D` | `E000[0x0017]=0x0000` | lamp remains lit; low write does not clear it |
|
||||
| `00 00 1A 80 00 C0` | `E000[0x001A]=0x8000` | no independent BARS-lamp effect from fresh boot |
|
||||
| `00 00 07 80 00 DD` / `00 00 07 00 00 5D` | `E000[0x0007]` high/low | no clear visible delta from CONNECT-OK baseline |
|
||||
|
||||
So `0x0017` is currently the strongest BARS lamp-on/latch selector.
|
||||
`0x001A` should not be labeled as the same lamp source from the prior compact
|
||||
run; that was likely carryover after `0x0017` had latched the white lamp on.
|
||||
`0x0007` is still protocol-relevant because the real panel emits the matching
|
||||
CAM POWER event frame, but the host-write visible lamp mapping is not separated
|
||||
from the CONNECT-OK baseline yet.
|
||||
|
||||
ROM trace refinement: selector `0x0013` dispatches to `H'2E06`, reads current
|
||||
table word `E800[0x0013]` at `H'E826`, and maps bit 15 to SLAVE while bit 14
|
||||
maps to `IRIS/M.BLACK LINK`. See `docs/pt2-iris-mblack-link-rom-trace.md`.
|
||||
|
||||
STANDARD/MASTER hunt:
|
||||
|
||||
| Run | Tested selector/value pocket | Result |
|
||||
| --- | --- | --- |
|
||||
| `panel-atlas-standard-master-bit-sweep-v1` | `0x0012`, `0x0013`, `0x0014` with high-bit/high-nibble candidates | no clean STANDARD or MASTER lamp trigger; positive SLAVE control worked |
|
||||
| `panel-atlas-standard-master-neighbor-sweep-v2` | `0x0010`, `0x0011`, `0x0015`, `0x0016`, `0x0017`, `0x0018`, `0x0019`, `0x001A` high-nibble candidates | no clean STANDARD or MASTER trigger |
|
||||
| `panel-atlas-standard-master-lower-neighbor-sweep-v3` | `0x0008` through `0x000F` high-nibble candidates | no clean STANDARD or MASTER trigger |
|
||||
|
||||
The `v2` sweep did show the far-right bottom BARS lamp/latch from the
|
||||
`0x0017` family when using non-`0x8000` high-nibble values. The clearest
|
||||
transition is `00 00 17 40 00 0D` (`E000[0x0017]=0x4000`) lighting the same
|
||||
bottom white BARS lamp that `0x0017=0x8000` can light. The lamp persisted after
|
||||
`0x0017=0x0000`, matching the existing BARS latch behavior. Later `0x0018`
|
||||
rows are contaminated by that latch and need a fresh-boot isolation run before
|
||||
labeling `0x0018` as a separate BARS source.
|
||||
|
||||
## Next Atlas Step
|
||||
|
||||
The next useful run is still a clear/ack/state-transition probe for the
|
||||
`0x0017` BARS latch, now including `0x0017=0x4000`:
|
||||
|
||||
- test likely sibling selectors around `0x0016`, `0x0018`, `0x0019`, and `0x001A`
|
||||
from a fresh boot after `0x0017` has latched on,
|
||||
- try command-7 repeat/ack and selector-zero refreshes to see whether the lamp
|
||||
clears through a state-machine transition rather than a simple low write,
|
||||
- test `0x0007` with an alternate baseline, because CONNECT OK already lights
|
||||
CAM POWER and masks any host-write lamp delta.
|
||||
- do not re-run `0x0008` through `0x0014` high-nibble values for
|
||||
STANDARD/MASTER unless the ROM trace points back there; the webcam sweeps did
|
||||
not show those lamps in that pocket.
|
||||
|
||||
## ROM-Derived Button Output Sweep
|
||||
|
||||
To skip the physical RCP button-press side and directly test likely "on" states
|
||||
from ROM handlers, use:
|
||||
|
||||
```powershell
|
||||
.\.venv\Scripts\python.exe scripts\build_rom_button_output_sweep.py
|
||||
.\.venv\Scripts\python.exe scripts\serial_scenario.py scenarios\panel-atlas-rom-button-output-candidates-v1.json --parity E --quiet-console --log captures\panel-atlas-rom-button-output-candidates-v1-webcam.txt --result-json captures\panel-atlas-rom-button-output-candidates-v1-webcam-result.json --snapshot-dir captures\panel-atlas-rom-button-output-candidates-v1-webcam-shots --camera-index 4 --snapshot-delays 0.5
|
||||
```
|
||||
|
||||
This scenario power-cycles before every candidate, seeds `CONNECT: OK`, sends
|
||||
one command-0 selector/value from the ROM-derived candidate list, and captures a
|
||||
single webcam image 0.5 s later. It is intentionally slower than a continuous
|
||||
sweep but should avoid most latch/carryover ambiguity.
|
||||
|
||||
Run result notes from `captures/panel-atlas-rom-button-output-candidates-v1-webcam-shots/`:
|
||||
|
||||
| Case | Frame | Selector/value | Visible result |
|
||||
| --- | --- | --- | --- |
|
||||
| 001 | `00 00 13 40 00 09` | `0x0013 = 0x4000` | IRIS/M.BLACK LINK |
|
||||
| 002 | `00 00 13 80 00 C9` | `0x0013 = 0x8000` | SLAVE |
|
||||
| 003 | `00 00 15 80 00 CF` | `0x0015 = 0x8000` | CALL and red tally |
|
||||
| 004 | `00 00 17 80 00 CD` | `0x0017 = 0x8000` | BARS |
|
||||
| 005 | `00 01 90 80 00 4B` | `0x0110 = 0x8000` | KNEE AUTO |
|
||||
| 006 | `00 00 1A 08 08 40` | `0x001A = 0x0808` | MONITOR ENC |
|
||||
| 007 | `00 00 1A 20 20 40` | `0x001A = 0x2020` | MONITOR B |
|
||||
| 008 | `00 00 1A 40 40 40` | `0x001A = 0x4040` | MONITOR G |
|
||||
| 009 | `00 00 1A 80 80 40` | `0x001A = 0x8080` | MONITOR R |
|
||||
| 010 | `00 00 6B 80 00 B1` | `0x006B = 0x8000` | STANDARD |
|
||||
| 011 | `00 01 03 00 04 5C` | `0x0083 = 0x0004` | IRIS AUTO, SHUTTER OFF, MASTER GAIN HP |
|
||||
| 012 | `00 01 03 40 00 18` | `0x0083 = 0x4000` | IRIS AUTO, SHUTTER OFF, MASTER GAIN 0 |
|
||||
| 013 | `00 01 03 20 00 78` | `0x0083 = 0x2000` | IRIS AUTO, SHUTTER OFF, MASTER GAIN 3 |
|
||||
| 014 | `00 01 0F 80 00 D4` | `0x008F = 0x8000` | IRIS AUTO, SHUTTER begins with `1...` |
|
||||
| 015 | `00 01 0F 20 00 74` | `0x008F = 0x2000` | IRIS AUTO, SHUTTER `00.0` |
|
||||
| 016 | `00 01 0F 08 00 5C` | `0x008F = 0x0800` | IRIS AUTO, SHUTTER EVS |
|
||||
| 017 | `00 01 0F 10 00 44` | `0x008F = 0x1000` | IRIS AUTO, SHUTTER OFF |
|
||||
| 018 | `00 01 13 10 20 78` | `0x0093 = 0x1020` | BLACK/FLARE MANUAL, white balance MANUAL |
|
||||
| 019 | `00 01 13 40 40 48` | `0x0093 = 0x4040` | BLACK/FLARE AUTO, white balance AUTO |
|
||||
| 020 | `00 01 13 80 40 88` | `0x0093 = 0x8040` | BLACK/FLARE AUTO, white balance PRESET |
|
||||
| 021 | `00 01 13 00 20 68` | `0x0093 = 0x0020` | BLACK/FLARE MANUAL, white balance MANUAL |
|
||||
| 022 | `00 01 13 00 40 08` | `0x0093 = 0x0040` | BLACK/FLARE AUTO, white balance MANUAL |
|
||||
| 023 | `00 01 1A 08 00 49` | `0x009A = 0x0800` | no panel change observed |
|
||||
| 024 | `00 01 37 20 00 4C` | `0x00B7 = 0x2000` | no panel change observed |
|
||||
|
||||
The run directory contains 28 candidate photos. The user-supplied ordered notes
|
||||
covered the first 24, so cases 025-028 still need visual review before assigning
|
||||
meanings:
|
||||
|
||||
| Case | Frame | Selector/value | Candidate |
|
||||
| --- | --- | --- | --- |
|
||||
| 025 | `00 01 39 40 00 22` | `0x00B9 = 0x4000` | F6DC.7 handler value candidate |
|
||||
| 026 | `00 01 44 80 00 9F` | `0x00C4 = 0x8000` | F6D4.0 bundle selector candidate |
|
||||
| 027 | `00 01 46 80 00 9D` | `0x00C6 = 0x8000` | F6D4.0 bundle selector candidate |
|
||||
| 028 | `00 01 78 80 00 A3` | `0x00F8 = 0x8000` | F6D4.1 handler candidate |
|
||||
|
||||
Key refinements from this run:
|
||||
|
||||
- `0x001A` is now best labeled as the MONITOR selector cluster: `ENC`, `B`, `G`,
|
||||
and `R` appeared cleanly from the four packed values.
|
||||
- `0x006B = 0x8000` is the first clean STANDARD lamp trigger.
|
||||
- `0x0083` is a MASTER GAIN/status display word, with values observed for `HP`,
|
||||
`0`, and `3`, while also lighting IRIS AUTO and showing SHUTTER OFF.
|
||||
- `0x008F` carries local shutter display/value states beyond the earlier
|
||||
EVS/OFF bits.
|
||||
- `0x0093` now has stronger white-balance plus BLACK/FLARE field mapping:
|
||||
high/mid bit combinations select WB AUTO/PRESET/MANUAL and BLACK/FLARE
|
||||
AUTO/MANUAL together.
|
||||
|
||||
## Broad Visual Sweep Workflow
|
||||
|
||||
For exploratory lamp/readout mining, use the generated big sweep rather than
|
||||
hand-writing thousands of frames:
|
||||
|
||||
```powershell
|
||||
.\.venv\Scripts\python.exe scripts\build_panel_visual_sweep.py scenarios\panel-atlas-big-visual-sweep-0001-017f-highbits.json --start 0x0001 --end 0x017F --values 0x8000,0x4000,0x2000,0x1000,0x0800 --power-cycle-every 32 --ok-every 8 --listen 0.65 --clear-listen 0.15 --ok-listen 0.30
|
||||
```
|
||||
|
||||
Run the generated scenario with webcam snapshots:
|
||||
|
||||
```powershell
|
||||
.\.venv\Scripts\python.exe scripts\serial_scenario.py scenarios\panel-atlas-big-visual-sweep-0001-017f-highbits.json --parity E --quiet-console --log captures\panel-atlas-big-visual-sweep-0001-017f-highbits-webcam.txt --result-json captures\panel-atlas-big-visual-sweep-0001-017f-highbits-webcam-result.json --snapshot-dir captures\panel-atlas-big-visual-sweep-0001-017f-highbits-webcam-shots --camera-index 4 --snapshot-delays 0.5
|
||||
```
|
||||
|
||||
The generated high-bit sweep covers selectors `0x0001` through `0x017F` with
|
||||
five candidate values per selector, for 1,915 candidate snapshots. It power
|
||||
cycles every 32 selectors to reduce latch contamination. Selector zero is
|
||||
omitted because it controls the CONNECT OK baseline.
|
||||
|
||||
After the run, create labeled review sheets:
|
||||
|
||||
```powershell
|
||||
.\.venv\Scripts\python.exe scripts\make_panel_sweep_contact_sheets.py captures\panel-atlas-big-visual-sweep-0001-017f-highbits-webcam-shots --output-dir captures\panel-atlas-big-visual-sweep-0001-017f-highbits-sheets --only-candidates --crop panel --cols 4 --rows 5 --thumb-width 360
|
||||
```
|
||||
|
||||
If a sheet shows a visible change, the image label has the exact trigger form:
|
||||
`candidate_XXXX_YYYY` means command-0 wrote `E000[0xXXXX]=0xYYYY`. Use that
|
||||
selector/value in a smaller fresh-boot isolation scenario.
|
||||
|
||||
## Broad Sweep Findings
|
||||
|
||||
Run:
|
||||
|
||||
```text
|
||||
captures/panel-atlas-big-visual-sweep-0001-017f-highbits-webcam.txt
|
||||
captures/panel-atlas-big-visual-sweep-0001-017f-highbits-webcam-result.json
|
||||
captures/panel-atlas-big-visual-sweep-0001-017f-highbits-webcam-shots/
|
||||
```
|
||||
|
||||
Serial health:
|
||||
|
||||
| RX frames | TX frames | Resync | Dropped bytes | Snapshots |
|
||||
| ---: | ---: | ---: | ---: | ---: |
|
||||
| 706 | 2372 | 0 | 0 | 1916 |
|
||||
|
||||
User-reviewed new visible hits from the broad sweep:
|
||||
|
||||
| Candidate label | Frame | Selector/value | Reported visible effect |
|
||||
| --- | --- | --- | --- |
|
||||
| `candidate_0013_4000` | `00 00 13 40 00 09` | `E000[0x0013]=0x4000` | `IRIS/M.BLACK LINK` area/lamp |
|
||||
| `candidate_0024_8000` | `00 00 24 80 00 FE` | `E000[0x0024]=0x8000` | LCD selector button/lamp |
|
||||
| `candidate_0082_8000` | `00 01 02 80 00 D9` | `E000[0x0082]=0x8000` | IRIS readout shows `OP` |
|
||||
| `candidate_0082_4000` | `00 01 02 40 00 19` | `E000[0x0082]=0x4000` | IRIS readout shows `1.4` |
|
||||
| `candidate_0083_8000` | `00 01 03 80 00 D8` | `E000[0x0083]=0x8000` | MASTER GAIN readout shows `-3` |
|
||||
| `candidate_0093_8000` | `00 01 13 80 00 C8` | `E000[0x0093]=0x8000` | white-balance PRESET lamp |
|
||||
| `candidate_0093_4000` | `00 01 13 40 00 08` | `E000[0x0093]=0x4000` | white-balance AUTO lamp |
|
||||
| `candidate_0093_2000` | `00 01 13 20 00 68` | `E000[0x0093]=0x2000` | white-balance MANUAL lamp |
|
||||
|
||||
The serial log shows immediate command-4 table readback frames for these writes,
|
||||
so the RCP accepted the selector updates.
|
||||
|
||||
Fresh-boot isolation scenario:
|
||||
|
||||
```powershell
|
||||
.\.venv\Scripts\python.exe scripts\serial_scenario.py scenarios\panel-atlas-big-hits-isolation-v1.json --parity E --quiet-console --log captures\panel-atlas-big-hits-isolation-v1-webcam.txt --result-json captures\panel-atlas-big-hits-isolation-v1-webcam-result.json --snapshot-dir captures\panel-atlas-big-hits-isolation-v1-webcam-shots --camera-index 4 --snapshot-delays 0.5
|
||||
```
|
||||
|
||||
Create review sheets for that isolation run:
|
||||
|
||||
```powershell
|
||||
.\.venv\Scripts\python.exe scripts\make_panel_sweep_contact_sheets.py captures\panel-atlas-big-hits-isolation-v1-webcam-shots --output-dir captures\panel-atlas-big-hits-isolation-v1-sheets --crop panel --cols 3 --rows 4 --thumb-width 420
|
||||
```
|
||||
|
||||
Fresh-boot isolation results:
|
||||
|
||||
| Frame | Selector/value | Confirmed visible effect |
|
||||
| --- | --- | --- |
|
||||
| `00 00 13 40 00 09` | `E000[0x0013]=0x4000` | `IRIS/M.BLACK LINK` lamp |
|
||||
| `00 00 24 80 00 FE` | `E000[0x0024]=0x8000` | LCD selector-button lamp |
|
||||
| `00 00 24 00 00 7E` | `E000[0x0024]=0x0000` | same LCD selector-button lamp remained visible at 0.5 s |
|
||||
| `00 01 02 80 00 D9` | `E000[0x0082]=0x8000` | IRIS readout `OP` |
|
||||
| `00 01 02 40 00 19` | `E000[0x0082]=0x4000` | IRIS readout `1.4` |
|
||||
| `00 01 02 00 00 59` | `E000[0x0082]=0x0000` | IRIS readout blank |
|
||||
| `00 01 03 80 00 D8` | `E000[0x0083]=0x8000` | IRIS AUTO lamp, SHUTTER `OFF`, MASTER GAIN `-3` |
|
||||
| `00 01 03 00 00 58` | `E000[0x0083]=0x0000` | same IRIS AUTO / SHUTTER `OFF` / MASTER GAIN `-3` state remained visible at 0.5 s |
|
||||
| `00 01 13 80 00 C8` | `E000[0x0093]=0x8000` | BLACK/FLARE MANUAL plus white-balance PRESET |
|
||||
| `00 01 13 40 00 08` | `E000[0x0093]=0x4000` | BLACK/FLARE MANUAL plus white-balance AUTO |
|
||||
| `00 01 13 20 00 68` | `E000[0x0093]=0x2000` | BLACK/FLARE MANUAL plus white-balance MANUAL |
|
||||
| `00 01 13 00 00 48` | `E000[0x0093]=0x0000` | BLACK/FLARE MANUAL plus white-balance MANUAL |
|
||||
|
||||
Interpretation:
|
||||
|
||||
- `0x0082` is a direct IRIS display/status selector. Clearing it blanks the IRIS
|
||||
readout, so this one behaves like a simple display source.
|
||||
- `0x0083=0x8000` drives a combined state: MASTER GAIN `-3`, SHUTTER `OFF`, and
|
||||
IRIS AUTO. Clearing the selector did not visibly clear that state in the
|
||||
isolation run, so it may be latched or copied into another display bank.
|
||||
- `0x0093` is now a confirmed white-balance mode selector with
|
||||
`0x8000=PRESET`, `0x4000=AUTO`, and `0x0000/0x2000=MANUAL` under this test
|
||||
context. BLACK/FLARE MANUAL was also present for all tested `0x0093` states.
|
||||
- `0x0024=0x8000` lights an LCD selector-button lamp, but `0x0024=0x0000` did
|
||||
not clear it in this timing window.
|
||||
@@ -154,9 +154,21 @@ def label_frame(frame: bytes) -> str:
|
||||
bytes.fromhex("0000158000CF"): "known_call_button_active_report",
|
||||
bytes.fromhex("00001500004F"): "known_call_button_inactive_report",
|
||||
bytes.fromhex("0000078000DD"): "known_cam_power_button_report",
|
||||
bytes.fromhex("000013000049"): "known_iris_mblack_link_clear_report_candidate",
|
||||
bytes.fromhex("000013400009"): "known_iris_mblack_link_active_report_candidate",
|
||||
bytes.fromhex("0000138000C9"): "known_selector_0013_bit15_report_candidate",
|
||||
bytes.fromhex("000013C00089"): "known_selector_0013_bit15_plus_iris_mblack_link_report_candidate",
|
||||
bytes.fromhex("00010F8000D4"): "known_shutter_onoff_bit7_report_candidate",
|
||||
bytes.fromhex("00010F200074"): "known_shutter_onoff_bit6_report_candidate",
|
||||
bytes.fromhex("00010F000054"): "known_shutter_onoff_clear_report_candidate",
|
||||
bytes.fromhex("010013000048"): "queued_iris_mblack_link_clear_report_candidate",
|
||||
bytes.fromhex("02001300004B"): "queued_iris_mblack_link_clear_report_candidate",
|
||||
bytes.fromhex("010013400008"): "queued_iris_mblack_link_active_report_candidate",
|
||||
bytes.fromhex("02001340000B"): "queued_iris_mblack_link_active_report_candidate",
|
||||
bytes.fromhex("0100138000C8"): "queued_selector_0013_bit15_report_candidate",
|
||||
bytes.fromhex("0200138000CB"): "queued_selector_0013_bit15_report_candidate",
|
||||
bytes.fromhex("010013C00088"): "queued_selector_0013_bit15_plus_iris_mblack_link_report_candidate",
|
||||
bytes.fromhex("020013C0008B"): "queued_selector_0013_bit15_plus_iris_mblack_link_report_candidate",
|
||||
bytes.fromhex("01010F8000D5"): "queued_shutter_onoff_bit7_report_candidate",
|
||||
bytes.fromhex("02010F8000D6"): "queued_shutter_onoff_bit7_report_candidate",
|
||||
bytes.fromhex("01010F200075"): "queued_shutter_onoff_bit6_report_candidate",
|
||||
|
||||
@@ -9,6 +9,7 @@ from typing import Any
|
||||
|
||||
from .formatting import h16
|
||||
from .lcd_text import analyze_lcd_text
|
||||
from .panel_selectors import panel_selector_semantics_payload
|
||||
from .rom import Rom
|
||||
from .serial_semantics import OBSERVED_TX_REPORT_OVERLAY
|
||||
from .table_xrefs import analyze_table_xrefs
|
||||
@@ -125,6 +126,7 @@ def analyze_ccu_seed_hints(payload: Mapping[str, Any], *, rom_path: Path | None
|
||||
selector_hints = _selector_hints_from_tables(table_analysis)
|
||||
_merge_special_selectors(selector_hints)
|
||||
_merge_observed_reports(selector_hints)
|
||||
_merge_panel_selector_semantics(selector_hints)
|
||||
|
||||
dispatch = _dispatch_table_summary(payload, rom_path)
|
||||
for entry in dispatch.get("interesting_entries", []):
|
||||
@@ -362,6 +364,30 @@ def _merge_observed_reports(hints: dict[int, JsonObject]) -> None:
|
||||
hint["reasons"].append(f"observed RCP autonomous report frame(s): {frames}")
|
||||
|
||||
|
||||
def _merge_panel_selector_semantics(hints: dict[int, JsonObject]) -> None:
|
||||
for item in panel_selector_semantics_payload():
|
||||
selector = int(item["selector"])
|
||||
hint = hints.setdefault(selector, _new_selector_hint(selector))
|
||||
hint["score"] += 4
|
||||
hint["name"] = str(item.get("name") or hint["name"])
|
||||
summary = str(item.get("summary") or "").strip()
|
||||
if summary:
|
||||
hint["reasons"].append(summary)
|
||||
for effect in item.get("effects", []):
|
||||
if not isinstance(effect, Mapping):
|
||||
continue
|
||||
name = effect.get("name") or "panel effect"
|
||||
mask = effect.get("mask_hex") or "mask?"
|
||||
when_set = effect.get("when_set") or "set"
|
||||
hint["reasons"].append(f"{mask} {name}: {when_set}")
|
||||
for meaning in item.get("value_meanings", []):
|
||||
if not isinstance(meaning, Mapping):
|
||||
continue
|
||||
value = meaning.get("value")
|
||||
if isinstance(value, int):
|
||||
_add_seed_value(hint, value)
|
||||
|
||||
|
||||
def _seed_plan(hints: Mapping[int, JsonObject]) -> JsonObject:
|
||||
planned = [
|
||||
(0x000, 0x8080, "selector zero active/connect candidate from emulator state search"),
|
||||
@@ -595,3 +621,7 @@ __all__ = [
|
||||
"selector_bytes",
|
||||
"write_ccu_seed_hints",
|
||||
]
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
|
||||
@@ -105,6 +105,7 @@ INPUT_BYTES = [
|
||||
|
||||
KNOWN_REPORTS = {
|
||||
0x0007: "CAM POWER",
|
||||
0x0013: "IRIS/M.BLACK LINK",
|
||||
0x0015: "CALL",
|
||||
}
|
||||
|
||||
|
||||
349
h8536/panel_selectors.py
Normal file
349
h8536/panel_selectors.py
Normal file
@@ -0,0 +1,349 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from copy import deepcopy
|
||||
from typing import Any
|
||||
|
||||
|
||||
JsonObject = dict[str, Any]
|
||||
|
||||
CURRENT_TABLE_BASE = 0xE800
|
||||
PRIMARY_TABLE_BASE = 0xE000
|
||||
SECONDARY_TABLE_BASE = 0xE400
|
||||
|
||||
|
||||
PANEL_SELECTOR_SEMANTICS: tuple[JsonObject, ...] = (
|
||||
{
|
||||
"selector": 0x0013,
|
||||
"selector_hex": "0x0013",
|
||||
"name": "slave_and_iris_mblack_link_lamps",
|
||||
"summary": (
|
||||
"Selector 0x0013 is a two-bit lamp/status word. ROM dispatch H'2E06 "
|
||||
"reads current table word H'E826 and fans bit 15 and bit 14 into panel latch RAM."
|
||||
),
|
||||
"state_machine": {
|
||||
"name_candidate": "iris_mblack_link_closed_loop_state_candidate",
|
||||
"summary": (
|
||||
"Bench-proven closed loop: the RCP reports local IRIS/M.BLACK LINK intent, "
|
||||
"the CCU ACKs selector 0x0013, then the CCU mirrors the accepted selector "
|
||||
"state back with command 0. The mirrored state controls the next toggle direction."
|
||||
),
|
||||
"active_report_frame": "00 00 13 40 00 09",
|
||||
"clear_report_frame": "00 00 13 00 00 49",
|
||||
"ack_frame": "05 00 13 00 00 4C",
|
||||
"active_mirror_frame": "00 00 13 40 00 09",
|
||||
"clear_mirror_frame": "00 00 13 00 00 49",
|
||||
"confidence": "bench-high",
|
||||
},
|
||||
"primary_word_address": PRIMARY_TABLE_BASE + 0x0013 * 2,
|
||||
"primary_word_address_hex": "H'E026",
|
||||
"current_word_address": CURRENT_TABLE_BASE + 0x0013 * 2,
|
||||
"current_word_address_hex": "H'E826",
|
||||
"dispatch_handler": "H'2E06",
|
||||
"confidence": "high",
|
||||
"evidence": [
|
||||
"bench: 00 00 13 80 00 C9 lights far-right SLAVE lamp",
|
||||
"bench: 00 00 13 40 00 09 lights IRIS/M.BLACK LINK lamp",
|
||||
"ROM: H'2E06-H'2E32 tests H'E826 bits 15/14 and sets/clears F791/F713/F716 latch bits",
|
||||
],
|
||||
"effects": [
|
||||
{
|
||||
"bit": 15,
|
||||
"mask": 0x8000,
|
||||
"mask_hex": "0x8000",
|
||||
"name": "SLAVE lamp",
|
||||
"when_set": "sets F791.6 and F713.4",
|
||||
"when_clear": "clears F791.6 and F713.4",
|
||||
"ram_bits": ["F791.6", "F713.4"],
|
||||
"handler_range": "H'2E06-H'2E1A",
|
||||
"confidence": "high",
|
||||
},
|
||||
{
|
||||
"bit": 14,
|
||||
"mask": 0x4000,
|
||||
"mask_hex": "0x4000",
|
||||
"name": "IRIS/M.BLACK LINK lamp",
|
||||
"when_set": "sets F791.5 and F716.7",
|
||||
"when_clear": "clears F791.5 and F716.7",
|
||||
"ram_bits": ["F791.5", "F716.7"],
|
||||
"handler_range": "H'2E1E-H'2E32",
|
||||
"confidence": "high",
|
||||
},
|
||||
],
|
||||
"local_triggers": [
|
||||
{
|
||||
"kind": "panel_input_toggle",
|
||||
"source": "F006.7 / F6DB.7",
|
||||
"handler": "H'200E",
|
||||
"name_candidate": "provisional_iris_mblack_link_button_toggle_report",
|
||||
"summary": (
|
||||
"When F6DB.7 is asserted and F731 <= 3, the ROM toggles current-table "
|
||||
"bit 14 at H'E826 based on F791.5, then queues selector 0x0013 through loc_3E54."
|
||||
),
|
||||
"gate": "F731 <= 3",
|
||||
"current_state_bit": "F791.5",
|
||||
"active_value": 0x4000,
|
||||
"active_value_hex": "0x4000",
|
||||
"clear_value": 0x0000,
|
||||
"clear_value_hex": "0x0000",
|
||||
"writes": ["H'E826 bit14"],
|
||||
"queue_call": "loc_3E54",
|
||||
"confidence": "medium-high",
|
||||
},
|
||||
{
|
||||
"kind": "local_helper_set_clear",
|
||||
"source": "H'1FE8/H'1FFB",
|
||||
"handler": "H'1FE8-H'200D",
|
||||
"summary": "Adjacent local helpers set or clear current-table bit 15 at H'E826 and queue selector 0x0013.",
|
||||
"writes": ["H'E826 bit15"],
|
||||
"queue_call": "loc_3E54",
|
||||
"confidence": "medium",
|
||||
},
|
||||
],
|
||||
"value_meanings": [
|
||||
{"value": 0x8000, "value_hex": "0x8000", "meaning": "SLAVE lamp on", "confidence": "high"},
|
||||
{"value": 0x4000, "value_hex": "0x4000", "meaning": "IRIS/M.BLACK LINK lamp on", "confidence": "high"},
|
||||
{"value": 0x0000, "value_hex": "0x0000", "meaning": "SLAVE and IRIS/M.BLACK LINK latch bits clear through H'2E06", "confidence": "high"},
|
||||
],
|
||||
},
|
||||
{
|
||||
"selector": 0x0015,
|
||||
"selector_hex": "0x0015",
|
||||
"name": "call_and_red_tally_lamp_lane",
|
||||
"summary": "Bench-visible CALL lamp and red tally lane; local CALL handler mirrors F6DB.5 into E800[0x0015].15.",
|
||||
"primary_word_address": PRIMARY_TABLE_BASE + 0x0015 * 2,
|
||||
"primary_word_address_hex": "H'E02A",
|
||||
"current_word_address": CURRENT_TABLE_BASE + 0x0015 * 2,
|
||||
"current_word_address_hex": "H'E82A",
|
||||
"dispatch_handler": "handler unknown",
|
||||
"confidence": "bench-high",
|
||||
"evidence": [
|
||||
"bench: 00 00 15 80 00 CF lights CALL and red tally",
|
||||
"ROM: H'20A1-H'20BA reads F6DB.5, writes H'E82A, and queues selector 0x0015",
|
||||
],
|
||||
"value_meanings": [
|
||||
{"value": 0x8000, "value_hex": "0x8000", "meaning": "CALL lamp and red tally on", "confidence": "bench-high"},
|
||||
{"value": 0x0000, "value_hex": "0x0000", "meaning": "CALL inactive/clear report", "confidence": "bench-high"},
|
||||
],
|
||||
},
|
||||
{
|
||||
"selector": 0x0017,
|
||||
"selector_hex": "0x0017",
|
||||
"name": "bars_lamp_lane",
|
||||
"summary": "Bench-visible BARS lamp/latch lane; low writes do not reliably clear the visible latch.",
|
||||
"primary_word_address": PRIMARY_TABLE_BASE + 0x0017 * 2,
|
||||
"primary_word_address_hex": "H'E02E",
|
||||
"current_word_address": CURRENT_TABLE_BASE + 0x0017 * 2,
|
||||
"current_word_address_hex": "H'E82E",
|
||||
"dispatch_handler": "handler unknown",
|
||||
"confidence": "bench-high",
|
||||
"evidence": [
|
||||
"bench: 00 00 17 80 00 CD lights BARS",
|
||||
"bench: 00 00 17 40 00 0D also lights the BARS latch in neighbor sweep",
|
||||
"ROM: H'1EDE can queue selector 0x0017 from F6D4.2",
|
||||
],
|
||||
"value_meanings": [
|
||||
{"value": 0x8000, "value_hex": "0x8000", "meaning": "BARS lamp on", "confidence": "bench-high"},
|
||||
{"value": 0x4000, "value_hex": "0x4000", "meaning": "BARS lamp/latch on", "confidence": "bench-medium-high"},
|
||||
{"value": 0x0000, "value_hex": "0x0000", "meaning": "BARS low write; visible latch may remain", "confidence": "bench-medium"},
|
||||
],
|
||||
},
|
||||
{
|
||||
"selector": 0x001A,
|
||||
"selector_hex": "0x001A",
|
||||
"name": "monitor_selector_lamps",
|
||||
"summary": "Bench-visible MONITOR selector cluster found from ROM-derived button-output sweep.",
|
||||
"primary_word_address": PRIMARY_TABLE_BASE + 0x001A * 2,
|
||||
"primary_word_address_hex": "H'E034",
|
||||
"current_word_address": CURRENT_TABLE_BASE + 0x001A * 2,
|
||||
"current_word_address_hex": "H'E834",
|
||||
"dispatch_handler": "handler unknown",
|
||||
"confidence": "bench-high",
|
||||
"evidence": [
|
||||
"bench: 00 00 1A 08 08 40 lights MONITOR ENC",
|
||||
"bench: 00 00 1A 20 20 40 lights MONITOR B",
|
||||
"bench: 00 00 1A 40 40 40 lights MONITOR G",
|
||||
"bench: 00 00 1A 80 80 40 lights MONITOR R",
|
||||
"ROM: H'1CB2-H'1D56 writes packed values to H'E834 and queues selector 0x001A",
|
||||
],
|
||||
"value_meanings": [
|
||||
{"value": 0x0808, "value_hex": "0x0808", "meaning": "MONITOR ENC lamp", "confidence": "bench-high"},
|
||||
{"value": 0x2020, "value_hex": "0x2020", "meaning": "MONITOR B lamp", "confidence": "bench-high"},
|
||||
{"value": 0x4040, "value_hex": "0x4040", "meaning": "MONITOR G lamp", "confidence": "bench-high"},
|
||||
{"value": 0x8080, "value_hex": "0x8080", "meaning": "MONITOR R lamp", "confidence": "bench-high"},
|
||||
],
|
||||
},
|
||||
{
|
||||
"selector": 0x0024,
|
||||
"selector_hex": "0x0024",
|
||||
"name": "lcd_selector_button_lamp",
|
||||
"summary": "Bench-visible LCD selector-button lamp lane.",
|
||||
"primary_word_address": PRIMARY_TABLE_BASE + 0x0024 * 2,
|
||||
"primary_word_address_hex": "H'E048",
|
||||
"current_word_address": CURRENT_TABLE_BASE + 0x0024 * 2,
|
||||
"current_word_address_hex": "H'E848",
|
||||
"confidence": "bench-medium",
|
||||
"value_meanings": [
|
||||
{"value": 0x8000, "value_hex": "0x8000", "meaning": "LCD selector-button lamp visible", "confidence": "bench-medium"},
|
||||
{"value": 0x0000, "value_hex": "0x0000", "meaning": "lamp remained visible at 0.5 s in isolation run", "confidence": "bench-low"},
|
||||
],
|
||||
},
|
||||
{
|
||||
"selector": 0x006B,
|
||||
"selector_hex": "0x006B",
|
||||
"name": "standard_lamp_lane",
|
||||
"summary": "Bench-visible STANDARD lamp lane found from ROM-derived F6D4.6 handler candidate.",
|
||||
"primary_word_address": PRIMARY_TABLE_BASE + 0x006B * 2,
|
||||
"primary_word_address_hex": "H'E0D6",
|
||||
"current_word_address": CURRENT_TABLE_BASE + 0x006B * 2,
|
||||
"current_word_address_hex": "H'E8D6",
|
||||
"dispatch_handler": "handler unknown",
|
||||
"confidence": "bench-high",
|
||||
"evidence": [
|
||||
"bench: 00 00 6B 80 00 B1 lights STANDARD",
|
||||
"ROM: H'2048 can write H'E8D6=0x8000 and queue selector 0x006B from F6D4.6",
|
||||
],
|
||||
"value_meanings": [
|
||||
{"value": 0x8000, "value_hex": "0x8000", "meaning": "STANDARD lamp on", "confidence": "bench-high"},
|
||||
],
|
||||
},
|
||||
{
|
||||
"selector": 0x0082,
|
||||
"selector_hex": "0x0082",
|
||||
"name": "iris_readout_lane",
|
||||
"summary": "Bench-visible IRIS seven-segment/display lane.",
|
||||
"primary_word_address": PRIMARY_TABLE_BASE + 0x0082 * 2,
|
||||
"primary_word_address_hex": "H'E104",
|
||||
"current_word_address": CURRENT_TABLE_BASE + 0x0082 * 2,
|
||||
"current_word_address_hex": "H'E904",
|
||||
"confidence": "bench-high",
|
||||
"value_meanings": [
|
||||
{"value": 0x8000, "value_hex": "0x8000", "meaning": "IRIS display OP", "confidence": "bench-high"},
|
||||
{"value": 0x4000, "value_hex": "0x4000", "meaning": "IRIS display 1.4", "confidence": "bench-high"},
|
||||
{"value": 0x0000, "value_hex": "0x0000", "meaning": "IRIS display blank", "confidence": "bench-high"},
|
||||
],
|
||||
},
|
||||
{
|
||||
"selector": 0x0083,
|
||||
"selector_hex": "0x0083",
|
||||
"name": "combined_iris_shutter_master_gain_status_lane",
|
||||
"summary": "Bench-visible combined status/readout lane; clear behavior appears latched or copied elsewhere.",
|
||||
"primary_word_address": PRIMARY_TABLE_BASE + 0x0083 * 2,
|
||||
"primary_word_address_hex": "H'E106",
|
||||
"current_word_address": CURRENT_TABLE_BASE + 0x0083 * 2,
|
||||
"current_word_address_hex": "H'E906",
|
||||
"confidence": "bench-medium-high",
|
||||
"value_meanings": [
|
||||
{"value": 0x8000, "value_hex": "0x8000", "meaning": "IRIS AUTO, SHUTTER OFF, MASTER GAIN -3", "confidence": "bench-medium-high"},
|
||||
{"value": 0x4000, "value_hex": "0x4000", "meaning": "IRIS AUTO, SHUTTER OFF, MASTER GAIN 0", "confidence": "bench-high"},
|
||||
{"value": 0x2000, "value_hex": "0x2000", "meaning": "IRIS AUTO, SHUTTER OFF, MASTER GAIN 3", "confidence": "bench-high"},
|
||||
{"value": 0x0004, "value_hex": "0x0004", "meaning": "IRIS AUTO, SHUTTER OFF, MASTER GAIN HP", "confidence": "bench-high"},
|
||||
{"value": 0x0000, "value_hex": "0x0000", "meaning": "same visible state remained at 0.5 s", "confidence": "bench-low"},
|
||||
],
|
||||
},
|
||||
{
|
||||
"selector": 0x008F,
|
||||
"selector_hex": "0x008F",
|
||||
"name": "shutter_display_status_lane",
|
||||
"summary": "Bench-visible shutter/status display lane; local F6D0.6/F6D0.7 handlers also queue this selector.",
|
||||
"primary_word_address": PRIMARY_TABLE_BASE + 0x008F * 2,
|
||||
"primary_word_address_hex": "H'E11E",
|
||||
"current_word_address": CURRENT_TABLE_BASE + 0x008F * 2,
|
||||
"current_word_address_hex": "H'E91E",
|
||||
"confidence": "bench-high",
|
||||
"evidence": [
|
||||
"bench: 00 01 0F 80 00 D4 shows IRIS AUTO and shutter value beginning with 1",
|
||||
"bench: 00 01 0F 20 00 74 shows IRIS AUTO and shutter 00.0",
|
||||
"bench: 00 01 0F 08 00 5C shows IRIS AUTO and shutter EVS",
|
||||
"bench: 00 01 0F 10 00 44 shows IRIS AUTO and shutter OFF",
|
||||
"ROM: H'24E8/H'252E write H'E91E and queue selector 0x008F from F6D0.7/F6D0.6",
|
||||
],
|
||||
"value_meanings": [
|
||||
{"value": 0x8000, "value_hex": "0x8000", "meaning": "IRIS AUTO plus shutter value beginning with 1", "confidence": "bench-medium-high"},
|
||||
{"value": 0x2000, "value_hex": "0x2000", "meaning": "IRIS AUTO plus shutter 00.0", "confidence": "bench-high"},
|
||||
{"value": 0x0800, "value_hex": "0x0800", "meaning": "IRIS AUTO plus shutter EVS", "confidence": "bench-high"},
|
||||
{"value": 0x1000, "value_hex": "0x1000", "meaning": "IRIS AUTO plus shutter OFF", "confidence": "bench-high"},
|
||||
],
|
||||
},
|
||||
{
|
||||
"selector": 0x0093,
|
||||
"selector_hex": "0x0093",
|
||||
"name": "white_balance_black_flare_mode_lane",
|
||||
"summary": "Bench-visible white-balance and black/flare lamp lane.",
|
||||
"primary_word_address": PRIMARY_TABLE_BASE + 0x0093 * 2,
|
||||
"primary_word_address_hex": "H'E126",
|
||||
"current_word_address": CURRENT_TABLE_BASE + 0x0093 * 2,
|
||||
"current_word_address_hex": "H'E926",
|
||||
"confidence": "bench-high",
|
||||
"value_meanings": [
|
||||
{"value": 0x8000, "value_hex": "0x8000", "meaning": "BLACK/FLARE MANUAL plus white-balance PRESET", "confidence": "bench-high"},
|
||||
{"value": 0x4000, "value_hex": "0x4000", "meaning": "BLACK/FLARE MANUAL plus white-balance AUTO", "confidence": "bench-high"},
|
||||
{"value": 0x2000, "value_hex": "0x2000", "meaning": "BLACK/FLARE MANUAL plus white-balance MANUAL", "confidence": "bench-high"},
|
||||
{"value": 0x1020, "value_hex": "0x1020", "meaning": "BLACK/FLARE MANUAL plus white-balance MANUAL", "confidence": "bench-high"},
|
||||
{"value": 0x4040, "value_hex": "0x4040", "meaning": "BLACK/FLARE AUTO plus white-balance AUTO", "confidence": "bench-high"},
|
||||
{"value": 0x8040, "value_hex": "0x8040", "meaning": "BLACK/FLARE AUTO plus white-balance PRESET", "confidence": "bench-high"},
|
||||
{"value": 0x0020, "value_hex": "0x0020", "meaning": "BLACK/FLARE MANUAL plus white-balance MANUAL", "confidence": "bench-high"},
|
||||
{"value": 0x0040, "value_hex": "0x0040", "meaning": "BLACK/FLARE AUTO plus white-balance MANUAL", "confidence": "bench-high"},
|
||||
{"value": 0x0000, "value_hex": "0x0000", "meaning": "BLACK/FLARE MANUAL plus white-balance MANUAL", "confidence": "bench-high"},
|
||||
],
|
||||
},
|
||||
{
|
||||
"selector": 0x0110,
|
||||
"selector_hex": "0x0110",
|
||||
"name": "knee_auto_lamp_or_page_status_lane",
|
||||
"summary": "Bench-visible KNEE AUTO source; ROM notes indicate timed KNEE/detail page interaction.",
|
||||
"primary_word_address": PRIMARY_TABLE_BASE + 0x0110 * 2,
|
||||
"primary_word_address_hex": "H'E220",
|
||||
"current_word_address": CURRENT_TABLE_BASE + 0x0110 * 2,
|
||||
"current_word_address_hex": "H'EA20",
|
||||
"confidence": "bench-high",
|
||||
"evidence": [
|
||||
"bench: 00 01 90 80 00 4B lights KNEE AUTO",
|
||||
"ROM: KNEE notes identify E000[0x0110].15 as a strong KNEE AUTO/timed display source",
|
||||
],
|
||||
"value_meanings": [
|
||||
{"value": 0x8000, "value_hex": "0x8000", "meaning": "KNEE AUTO lamp/status on", "confidence": "bench-high"},
|
||||
],
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
def panel_selector_semantics_payload() -> list[JsonObject]:
|
||||
return deepcopy(list(PANEL_SELECTOR_SEMANTICS))
|
||||
|
||||
|
||||
def known_panel_selector(selector: int) -> JsonObject | None:
|
||||
normalized = selector & 0x01FF
|
||||
for item in PANEL_SELECTOR_SEMANTICS:
|
||||
if int(item["selector"]) == normalized:
|
||||
return deepcopy(item)
|
||||
return None
|
||||
|
||||
|
||||
def selector_word_address(table_base: int, selector: int) -> int:
|
||||
return (table_base + ((selector & 0x01FF) * 2)) & 0xFFFF
|
||||
|
||||
|
||||
def describe_selector_value(selector: int, value: int) -> list[str]:
|
||||
item = known_panel_selector(selector)
|
||||
if item is None:
|
||||
return []
|
||||
|
||||
normalized_value = value & 0xFFFF
|
||||
lines: list[str] = []
|
||||
for meaning in item.get("value_meanings", []):
|
||||
if not isinstance(meaning, dict) or meaning.get("value") != normalized_value:
|
||||
continue
|
||||
lines.append(str(meaning.get("meaning") or "known panel selector value"))
|
||||
|
||||
for effect in item.get("effects", []):
|
||||
if not isinstance(effect, dict) or not isinstance(effect.get("mask"), int):
|
||||
continue
|
||||
mask = int(effect["mask"]) & 0xFFFF
|
||||
state = "set" if normalized_value & mask else "clear"
|
||||
action = effect.get("when_set") if state == "set" else effect.get("when_clear")
|
||||
name = str(effect.get("name") or f"bit {effect.get('bit', '?')}")
|
||||
if action:
|
||||
lines.append(f"{name}: bit {effect.get('bit', '?')} {state}; {action}")
|
||||
else:
|
||||
lines.append(f"{name}: bit {effect.get('bit', '?')} {state}")
|
||||
return lines
|
||||
@@ -278,6 +278,7 @@ def _declarations(tx_candidate: JsonObject | None, rx_candidate: JsonObject | No
|
||||
"typedef uint8_t u8;",
|
||||
"typedef uint16_t u16;",
|
||||
"",
|
||||
"#define BIT(n) (1u << (n))",
|
||||
"extern volatile u8 MEM8[0x10000];",
|
||||
"",
|
||||
f"#define {channel}_SCR MEM8[{_c_hex(scr)}]",
|
||||
@@ -424,6 +425,7 @@ def _semantics_lines(
|
||||
lines.extend(_command_effect_comment_lines(protocol.get("command_effects"), opts, prefix=" * "))
|
||||
lines.extend(_response_schema_comment_lines(_schema_list(protocol), opts, prefix=" * "))
|
||||
lines.extend(_table_map_comment_lines(_table_map_list(protocol), opts, prefix=" * "))
|
||||
lines.extend(_panel_selector_comment_lines(protocol.get("panel_selector_semantics"), opts, prefix=" * "))
|
||||
lines.extend(_state_variable_comment_lines(protocol.get("state_variable_candidates"), opts, prefix=" * "))
|
||||
lines.extend(_retry_error_comment_lines(protocol.get("retry_error_model"), opts, prefix=" * "))
|
||||
lines.extend(_gate_queue_comment_lines(protocol.get("gate_queue_model"), opts, prefix=" * "))
|
||||
@@ -466,6 +468,8 @@ def _semantics_lines(
|
||||
)
|
||||
lines.extend(_gate_queue_predicate_function_lines(protocol.get("gate_queue_model")))
|
||||
lines.extend(_timer_architecture_function_lines(protocol))
|
||||
lines.extend(_panel_selector_function_lines(protocol.get("panel_selector_semantics")))
|
||||
lines.extend(_panel_selector_provisional_function_lines(protocol.get("panel_selector_semantics")))
|
||||
lines.extend(
|
||||
[
|
||||
"void sci1_process_candidate_protocol_command(void)",
|
||||
@@ -474,6 +478,8 @@ def _semantics_lines(
|
||||
" u16 logical_index = sci1_rx_candidate_logical_index();",
|
||||
" u16 value = sci1_rx_candidate_value();",
|
||||
"",
|
||||
" sci1_candidate_panel_selector_annotation(logical_index, value);",
|
||||
"",
|
||||
],
|
||||
)
|
||||
lines.extend(_command_dispatch_switch_lines(commands, opts))
|
||||
@@ -644,6 +650,70 @@ def _table_map_comment_lines(
|
||||
return lines
|
||||
|
||||
|
||||
def _panel_selector_comment_lines(
|
||||
value: object,
|
||||
opts: SerialPseudocodeOptions,
|
||||
*,
|
||||
prefix: str,
|
||||
) -> list[str]:
|
||||
selectors = _object_list(value)
|
||||
if not selectors:
|
||||
return []
|
||||
lines = [f"{prefix}panel selector semantics:"]
|
||||
for selector in selectors[:6]:
|
||||
selector_hex = selector.get("selector_hex") or _selector_hex(selector.get("selector"))
|
||||
name = selector.get("name") or "panel_selector"
|
||||
current = selector.get("current_word_address_hex") or "current table"
|
||||
dispatch = selector.get("dispatch_handler") or "dispatch unknown"
|
||||
summary = _comment_text(str(selector.get("summary") or "bench/ROM selector annotation"))
|
||||
lines.append(f"{prefix}- {selector_hex} {name}: {summary}")
|
||||
lines.append(f"{prefix} current word: {current}; dispatch: {dispatch}")
|
||||
for effect in _object_list(selector.get("effects"))[:4]:
|
||||
mask = effect.get("mask_hex") or _selector_hex(effect.get("mask"))
|
||||
effect_name = effect.get("name") or "effect"
|
||||
when_set = _comment_text(str(effect.get("when_set") or "set"))
|
||||
bits = ", ".join(str(item) for item in effect.get("ram_bits", []))
|
||||
suffix = f"; RAM {bits}" if bits else ""
|
||||
lines.append(f"{prefix} {mask} -> {effect_name}: {when_set}{suffix}")
|
||||
meanings = []
|
||||
for meaning in _object_list(selector.get("value_meanings"))[:4]:
|
||||
value_hex = meaning.get("value_hex") or _selector_hex(meaning.get("value"))
|
||||
label = _comment_text(str(meaning.get("meaning") or "known panel value"))
|
||||
meanings.append(f"{value_hex} {label}")
|
||||
if meanings:
|
||||
lines.append(f"{prefix} observed values: {'; '.join(meanings)}")
|
||||
state_machine = selector.get("state_machine")
|
||||
if isinstance(state_machine, dict):
|
||||
name_candidate = state_machine.get("name_candidate") or "selector_state_machine_candidate"
|
||||
summary = _comment_text(str(state_machine.get("summary") or "bench-proven selector state-machine candidate"))
|
||||
lines.append(f"{prefix} state machine: {name_candidate}: {summary}")
|
||||
active = state_machine.get("active_report_frame")
|
||||
clear = state_machine.get("clear_report_frame")
|
||||
ack = state_machine.get("ack_frame")
|
||||
mirror_active = state_machine.get("active_mirror_frame")
|
||||
mirror_clear = state_machine.get("clear_mirror_frame")
|
||||
if active or clear or ack:
|
||||
lines.append(
|
||||
f"{prefix} frames: active report {active or '?'}; clear report {clear or '?'}; "
|
||||
f"ACK {ack or '?'}; mirror active {mirror_active or '?'}; mirror clear {mirror_clear or '?'}"
|
||||
)
|
||||
triggers = []
|
||||
for trigger in _object_list(selector.get("local_triggers"))[:3]:
|
||||
source = trigger.get("source") or trigger.get("handler") or "local path"
|
||||
summary = _comment_text(str(trigger.get("summary") or "local trigger candidate"))
|
||||
name_candidate = trigger.get("name_candidate")
|
||||
prefix_text = f"{name_candidate} " if name_candidate else ""
|
||||
triggers.append(f"{prefix_text}{source}: {summary}")
|
||||
if triggers:
|
||||
lines.append(f"{prefix} local trigger candidates: {'; '.join(triggers)}")
|
||||
evidence = ", ".join(str(item) for item in selector.get("evidence", []) if item)
|
||||
if opts.include_evidence and evidence:
|
||||
lines.append(f"{prefix} evidence: {_comment_text(evidence)}")
|
||||
if len(selectors) > 6:
|
||||
lines.append(f"{prefix}- ... {len(selectors) - 6} more panel selector annotations")
|
||||
return lines
|
||||
|
||||
|
||||
def _state_variable_comment_lines(
|
||||
value: object,
|
||||
opts: SerialPseudocodeOptions,
|
||||
@@ -955,6 +1025,121 @@ def _timer_architecture_function_lines(protocol: JsonObject) -> list[str]:
|
||||
)
|
||||
|
||||
|
||||
def _panel_selector_function_lines(value: object) -> list[str]:
|
||||
selectors = _object_list(value)
|
||||
if not selectors:
|
||||
return [
|
||||
"static void sci1_candidate_panel_selector_annotation(u16 logical_index, u16 value)",
|
||||
"{",
|
||||
" (void)logical_index;",
|
||||
" (void)value;",
|
||||
"}",
|
||||
"",
|
||||
]
|
||||
|
||||
lines = [
|
||||
"static void sci1_candidate_panel_selector_annotation(u16 logical_index, u16 value)",
|
||||
"{",
|
||||
" /* Known bench/ROM selector labels. This helper is commentary for the decompile. */",
|
||||
" switch (logical_index) {",
|
||||
]
|
||||
for selector in selectors:
|
||||
selector_value = selector.get("selector")
|
||||
if not isinstance(selector_value, int):
|
||||
continue
|
||||
selector_hex = selector.get("selector_hex") or f"0x{selector_value:04X}"
|
||||
name = _comment_text(str(selector.get("name") or "panel selector"))
|
||||
current = selector.get("current_word_address_hex") or "current table"
|
||||
dispatch = selector.get("dispatch_handler") or "dispatch unknown"
|
||||
lines.append(f" case 0x{selector_value & 0x01FF:04X}u:")
|
||||
lines.append(f" /* {selector_hex} {name}; current word {current}; {dispatch}. */")
|
||||
for effect in _object_list(selector.get("effects")):
|
||||
mask = effect.get("mask")
|
||||
if not isinstance(mask, int):
|
||||
continue
|
||||
effect_name = _comment_text(str(effect.get("name") or "panel effect"))
|
||||
when_set = _comment_text(str(effect.get("when_set") or "set"))
|
||||
when_clear = _comment_text(str(effect.get("when_clear") or "clear"))
|
||||
lines.append(f" if ((value & 0x{mask & 0xFFFF:04X}u) != 0u) {{")
|
||||
lines.append(f" /* {effect_name}: {when_set}. */")
|
||||
lines.append(" } else {")
|
||||
lines.append(f" /* {effect_name}: {when_clear}. */")
|
||||
lines.append(" }")
|
||||
for meaning in _object_list(selector.get("value_meanings")):
|
||||
known_value = meaning.get("value")
|
||||
if not isinstance(known_value, int):
|
||||
continue
|
||||
label = _comment_text(str(meaning.get("meaning") or "known panel value"))
|
||||
lines.append(f" if (value == 0x{known_value & 0xFFFF:04X}u) {{")
|
||||
lines.append(f" /* {label}. */")
|
||||
lines.append(" }")
|
||||
lines.append(" break;")
|
||||
lines.extend(
|
||||
[
|
||||
" default:",
|
||||
" break;",
|
||||
" }",
|
||||
"}",
|
||||
"",
|
||||
],
|
||||
)
|
||||
return lines
|
||||
|
||||
|
||||
def _panel_selector_provisional_function_lines(value: object) -> list[str]:
|
||||
selectors = _object_list(value)
|
||||
lines: list[str] = []
|
||||
for selector in selectors:
|
||||
selector_value = selector.get("selector")
|
||||
if not isinstance(selector_value, int):
|
||||
continue
|
||||
state_machine = selector.get("state_machine")
|
||||
if not isinstance(state_machine, dict):
|
||||
continue
|
||||
for trigger in _object_list(selector.get("local_triggers")):
|
||||
name = str(trigger.get("name_candidate") or "").strip()
|
||||
if not name:
|
||||
continue
|
||||
handler = _comment_text(str(trigger.get("handler") or "handler unknown"))
|
||||
source = _comment_text(str(trigger.get("source") or "source unknown"))
|
||||
gate = _comment_text(str(trigger.get("gate") or "gate unknown"))
|
||||
current_bit = _comment_text(str(trigger.get("current_state_bit") or "current state bit unknown"))
|
||||
summary = _comment_text(str(trigger.get("summary") or "local trigger candidate"))
|
||||
active_value = _int_from_object(trigger.get("active_value"), 0x4000)
|
||||
clear_value = _int_from_object(trigger.get("clear_value"), 0x0000)
|
||||
active_report = _comment_text(str(state_machine.get("active_report_frame") or "active report unknown"))
|
||||
clear_report = _comment_text(str(state_machine.get("clear_report_frame") or "clear report unknown"))
|
||||
ack_frame = _comment_text(str(state_machine.get("ack_frame") or "ACK unknown"))
|
||||
active_mirror = _comment_text(str(state_machine.get("active_mirror_frame") or "active mirror unknown"))
|
||||
clear_mirror = _comment_text(str(state_machine.get("clear_mirror_frame") or "clear mirror unknown"))
|
||||
safe_name = _safe_identifier(name)
|
||||
lines.extend(
|
||||
[
|
||||
f"void {safe_name}(void)",
|
||||
"{",
|
||||
f" /* Provisional name for ROM {handler}: {summary} */",
|
||||
f" /* Source {source}; gate {gate}; current state {current_bit}. */",
|
||||
" if ((MEM8[0xF6DBu] & BIT(7)) == 0u) {",
|
||||
" return;",
|
||||
" }",
|
||||
" if (MEM8[0xF731u] > 3u) {",
|
||||
" return;",
|
||||
" }",
|
||||
"",
|
||||
" if ((MEM8[0xF791u] & BIT(5)) == 0u) {",
|
||||
f" /* Requests selector 0x{selector_value & 0x01FF:04X}=0x{active_value & 0xFFFF:04X}: {active_report}. */",
|
||||
f" /* CCU should ACK {ack_frame}, then mirror {active_mirror}. */",
|
||||
" } else {",
|
||||
f" /* Requests selector 0x{selector_value & 0x01FF:04X}=0x{clear_value & 0xFFFF:04X}: {clear_report}. */",
|
||||
f" /* CCU should ACK {ack_frame}, then mirror {clear_mirror}. */",
|
||||
" }",
|
||||
"}",
|
||||
"",
|
||||
],
|
||||
)
|
||||
return lines
|
||||
|
||||
|
||||
def _timer_tick_function_lines(function_name: str, counters: list[JsonObject], summary: str) -> list[str]:
|
||||
lines = [
|
||||
f"void {function_name}(void)",
|
||||
@@ -1130,6 +1315,16 @@ def _command_hex(value: object) -> str:
|
||||
return "?"
|
||||
|
||||
|
||||
def _selector_hex(value: object) -> str:
|
||||
if isinstance(value, int):
|
||||
return f"0x{value & 0xFFFF:04X}"
|
||||
return "?"
|
||||
|
||||
|
||||
def _int_from_object(value: object, default: int) -> int:
|
||||
return value if isinstance(value, int) else default
|
||||
|
||||
|
||||
def _tx_functions(candidate: JsonObject, opts: SerialPseudocodeOptions) -> list[str]:
|
||||
length = _int_field(candidate, "frame_length", 6)
|
||||
seed = _int_field(candidate, "checksum_seed", 0x5A)
|
||||
@@ -1386,3 +1581,7 @@ def _safe_identifier(value: str) -> str:
|
||||
|
||||
def _comment_text(text: str) -> str:
|
||||
return text.replace("*/", "* /").replace("\r", " ").replace("\n", " ")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
|
||||
@@ -270,6 +270,8 @@ def _run_step(ctx: ScenarioContext, action: str, spec: dict[str, Any]) -> None:
|
||||
_listen(ctx, float(spec.get("seconds", spec.get("value", 0.0))))
|
||||
elif action == "listen_ack":
|
||||
_step_listen_ack(ctx, spec)
|
||||
elif action == "listen_ack_until_quiet":
|
||||
_step_listen_ack_until_quiet(ctx, spec)
|
||||
elif action == "send":
|
||||
frame = _parse_required_frame(spec.get("frame"))
|
||||
label = str(spec.get("label", "send"))
|
||||
@@ -378,7 +380,38 @@ def _step_table_sweep(ctx: ScenarioContext, spec: dict[str, Any]) -> None:
|
||||
|
||||
def _step_listen_ack(ctx: ScenarioContext, spec: dict[str, Any]) -> None:
|
||||
seconds = float(spec.get("seconds", spec.get("value", 1.0)))
|
||||
ack = _ack_config(
|
||||
ack = _ack_config_from_step(spec)
|
||||
ack_text = (
|
||||
f"ack_frame={format_frame(ack['frame'])}"
|
||||
if ack["ack_mode"] == "fixed"
|
||||
else f"ack_mode={ack['ack_mode']}"
|
||||
)
|
||||
ctx.logger.event(
|
||||
f"LISTEN_ACK seconds={seconds:.3f} target_mode={ack['target_mode']} targets={len(ack['targets'])} "
|
||||
f"{ack_text} limit_scope={ack['limit_scope']} max_acks={ack['max_acks']}"
|
||||
)
|
||||
_listen_with_ack(ctx, seconds, None, ack)
|
||||
|
||||
|
||||
def _step_listen_ack_until_quiet(ctx: ScenarioContext, spec: dict[str, Any]) -> None:
|
||||
seconds = float(spec.get("seconds", spec.get("value", 10.0)))
|
||||
quiet_seconds = float(spec.get("quiet_seconds", spec.get("quiet", 0.750)))
|
||||
ack = _ack_config_from_step(spec)
|
||||
ack_text = (
|
||||
f"ack_frame={format_frame(ack['frame'])}"
|
||||
if ack["ack_mode"] == "fixed"
|
||||
else f"ack_mode={ack['ack_mode']}"
|
||||
)
|
||||
ctx.logger.event(
|
||||
f"LISTEN_ACK_UNTIL_QUIET seconds={seconds:.3f} quiet={quiet_seconds:.3f} "
|
||||
f"target_mode={ack['target_mode']} targets={len(ack['targets'])} "
|
||||
f"{ack_text} limit_scope={ack['limit_scope']} max_acks={ack['max_acks']}"
|
||||
)
|
||||
_listen_with_ack(ctx, seconds, None, ack, quiet_seconds=quiet_seconds)
|
||||
|
||||
|
||||
def _ack_config_from_step(spec: dict[str, Any]) -> dict[str, Any]:
|
||||
return _ack_config(
|
||||
{
|
||||
"enabled": spec.get("enabled", True),
|
||||
"frames": spec.get("frames", spec.get("frame")),
|
||||
@@ -393,18 +426,9 @@ def _step_listen_ack(ctx: ScenarioContext, spec: dict[str, Any]) -> None:
|
||||
"ack_mode": spec.get("ack_mode", spec.get("mode", "fixed")),
|
||||
"target_mode": spec.get("target_mode", spec.get("match", "explicit")),
|
||||
"limit_scope": spec.get("limit_scope", spec.get("scope", "local")),
|
||||
"respond_on": spec.get("respond_on", spec.get("send_on", [])),
|
||||
}
|
||||
)
|
||||
ack_text = (
|
||||
f"ack_frame={format_frame(ack['frame'])}"
|
||||
if ack["ack_mode"] == "fixed"
|
||||
else f"ack_mode={ack['ack_mode']}"
|
||||
)
|
||||
ctx.logger.event(
|
||||
f"LISTEN_ACK seconds={seconds:.3f} target_mode={ack['target_mode']} targets={len(ack['targets'])} "
|
||||
f"{ack_text} limit_scope={ack['limit_scope']} max_acks={ack['max_acks']}"
|
||||
)
|
||||
_listen_with_ack(ctx, seconds, None, ack)
|
||||
|
||||
|
||||
def _ack_config(raw: Any) -> dict[str, Any]:
|
||||
@@ -438,6 +462,7 @@ def _ack_config(raw: Any) -> dict[str, Any]:
|
||||
"ack_mode": ack_mode,
|
||||
"target_mode": target_mode,
|
||||
"limit_scope": limit_scope,
|
||||
"respond_on": _response_rules(spec.get("respond_on", [])),
|
||||
}
|
||||
|
||||
|
||||
@@ -458,28 +483,71 @@ def _ack_matches(frame: bytes, ack: dict[str, Any]) -> bool:
|
||||
return frame[0] in {0x00, 0x01, 0x02} and not (frame[1] & 0x80)
|
||||
|
||||
|
||||
def _response_rules(raw: Any) -> list[dict[str, Any]]:
|
||||
values = raw if isinstance(raw, list) else ([raw] if raw else [])
|
||||
rules: list[dict[str, Any]] = []
|
||||
for index, value in enumerate(values):
|
||||
if not isinstance(value, dict):
|
||||
raise SystemExit("respond_on entries must be objects")
|
||||
targets = _parse_frame_list(value.get("frames", value.get("frame")))
|
||||
response_frame = _parse_required_frame(value.get("send", value.get("response")))
|
||||
label = str(value.get("label", f"respond_on_{index + 1}"))
|
||||
rules.append(
|
||||
{
|
||||
"targets": targets,
|
||||
"frame": response_frame,
|
||||
"label": label,
|
||||
"delay": float(value.get("delay", 0.0)),
|
||||
"listen": float(value.get("listen", 0.0)),
|
||||
"once": bool(value.get("once", True)),
|
||||
}
|
||||
)
|
||||
return rules
|
||||
|
||||
|
||||
def _listen_with_ack(
|
||||
ctx: ScenarioContext,
|
||||
seconds: float,
|
||||
selector: int,
|
||||
selector: int | None,
|
||||
ack: dict[str, Any],
|
||||
*,
|
||||
quiet_seconds: float | None = None,
|
||||
) -> list[bytes]:
|
||||
deadline = time.monotonic() + max(0.0, seconds)
|
||||
observed: list[bytes] = []
|
||||
pending: list[bytes] = []
|
||||
pending_index = 0
|
||||
last_activity = time.monotonic()
|
||||
acked_targets: set[bytes] = set()
|
||||
fired_responses: set[int] = set()
|
||||
ack_start = ctx.ack_sent
|
||||
target_start = sum(ctx.target_counts.values())
|
||||
|
||||
def enqueue(frames: list[bytes]) -> None:
|
||||
nonlocal last_activity
|
||||
if not frames:
|
||||
return
|
||||
observed.extend(frames)
|
||||
pending.extend(frames)
|
||||
last_activity = time.monotonic()
|
||||
|
||||
while time.monotonic() < deadline:
|
||||
frames = _read_available(ctx, selector=selector)
|
||||
observed.extend(frames)
|
||||
if not frames:
|
||||
enqueue(frames)
|
||||
if not frames and pending_index >= len(pending):
|
||||
if quiet_seconds is not None and time.monotonic() - last_activity >= quiet_seconds:
|
||||
ctx.logger.event(f"LISTEN_ACK_QUIET quiet={quiet_seconds:.3f}s")
|
||||
break
|
||||
sleep_for = min(max(0.001, ack["poll_interval"]), max(0.0, deadline - time.monotonic()))
|
||||
if sleep_for > 0:
|
||||
time.sleep(sleep_for)
|
||||
continue
|
||||
if not ack["enabled"]:
|
||||
pending_index = len(pending)
|
||||
continue
|
||||
for frame in frames:
|
||||
while pending_index < len(pending):
|
||||
frame = pending[pending_index]
|
||||
pending_index += 1
|
||||
if not _ack_matches(frame, ack):
|
||||
continue
|
||||
_count_target(ctx, frame)
|
||||
@@ -493,7 +561,7 @@ def _listen_with_ack(
|
||||
continue
|
||||
acked_targets.add(frame)
|
||||
if ack["guard"] > 0:
|
||||
observed.extend(_listen(ctx, ack["guard"], selector=selector))
|
||||
enqueue(_listen(ctx, ack["guard"], selector=selector))
|
||||
_send_and_record(ctx, _ack_frame_for_target(frame, ack), "ack", capture=ctx.args.snapshot_acks)
|
||||
ctx.ack_sent += 1
|
||||
if _ack_limit_reached(ctx, ack, ack_start=ack_start, target_start=target_start):
|
||||
@@ -501,7 +569,22 @@ def _listen_with_ack(
|
||||
if ack["abort_on_limit"]:
|
||||
ctx.abort_requested = True
|
||||
if ack["post_read"] > 0:
|
||||
observed.extend(_listen(ctx, ack["post_read"], selector=selector))
|
||||
enqueue(_listen(ctx, ack["post_read"], selector=selector))
|
||||
for rule_index, rule in enumerate(ack["respond_on"]):
|
||||
if frame not in rule["targets"]:
|
||||
continue
|
||||
if rule["once"] and rule_index in fired_responses:
|
||||
continue
|
||||
fired_responses.add(rule_index)
|
||||
if rule["delay"] > 0:
|
||||
time.sleep(rule["delay"])
|
||||
ctx.logger.event(
|
||||
f"RESPOND_ON target={format_frame(frame)} "
|
||||
f"send={format_frame(rule['frame'])} label={rule['label']}"
|
||||
)
|
||||
_send_and_record(ctx, rule["frame"], rule["label"], capture=ctx.args.snapshot_acks)
|
||||
if rule["listen"] > 0:
|
||||
enqueue(_listen(ctx, rule["listen"], selector=selector))
|
||||
if ctx.abort_requested:
|
||||
return observed
|
||||
return observed
|
||||
@@ -753,6 +836,7 @@ def _quiet_console_line(line: str) -> bool:
|
||||
"NOTE ",
|
||||
"SNAPSHOT_SCHEDULE ",
|
||||
"SNAPSHOT_ERROR ",
|
||||
"RESPOND_ON ",
|
||||
"Summary",
|
||||
"rx_frames=",
|
||||
"resync_events=",
|
||||
@@ -760,6 +844,8 @@ def _quiet_console_line(line: str) -> bool:
|
||||
"abort_requested=",
|
||||
"known_shutter",
|
||||
"queued_shutter",
|
||||
"iris_mblack",
|
||||
"selector_0013",
|
||||
)
|
||||
return any(fragment in line for fragment in keep_fragments)
|
||||
|
||||
@@ -772,25 +858,14 @@ def _print_step_dry_run(action: str, spec: dict[str, Any], stdout: TextIO, *, in
|
||||
print(f"{indent}listen={float(spec.get('listen', 0.0)):.3f}s", file=stdout)
|
||||
elif action in {"drain", "listen", "wait"}:
|
||||
print(f"{indent}seconds={float(spec.get('seconds', spec.get('value', 0.0))):.3f}", file=stdout)
|
||||
elif action == "listen_ack":
|
||||
ack = _ack_config(
|
||||
{
|
||||
"enabled": spec.get("enabled", True),
|
||||
"frames": spec.get("frames", spec.get("frame")),
|
||||
"ack_frame": spec.get("ack_frame"),
|
||||
"ack_guard": spec.get("ack_guard", 0.020),
|
||||
"poll_interval": spec.get("poll_interval", 0.005),
|
||||
"post_ack_read": spec.get("post_ack_read", 0.250),
|
||||
"once_per_selector": spec.get("once_per_frame", False),
|
||||
"max_acks": spec.get("max_acks"),
|
||||
"max_target_hits": spec.get("max_target_hits"),
|
||||
"abort_on_limit": spec.get("abort_on_limit", False),
|
||||
"ack_mode": spec.get("ack_mode", spec.get("mode", "fixed")),
|
||||
"target_mode": spec.get("target_mode", spec.get("match", "explicit")),
|
||||
"limit_scope": spec.get("limit_scope", spec.get("scope", "local")),
|
||||
}
|
||||
)
|
||||
elif action in {"listen_ack", "listen_ack_until_quiet"}:
|
||||
ack = _ack_config_from_step(spec)
|
||||
print(f"{indent}seconds={float(spec.get('seconds', spec.get('value', 1.0))):.3f}", file=stdout)
|
||||
if action == "listen_ack_until_quiet":
|
||||
print(
|
||||
f"{indent}quiet={float(spec.get('quiet_seconds', spec.get('quiet', 0.750))):.3f}s",
|
||||
file=stdout,
|
||||
)
|
||||
if not ack["enabled"]:
|
||||
print(f"{indent}ack=disabled", file=stdout)
|
||||
else:
|
||||
@@ -805,6 +880,13 @@ def _print_step_dry_run(action: str, spec: dict[str, Any], stdout: TextIO, *, in
|
||||
f"max_target_hits={ack['max_target_hits']}",
|
||||
file=stdout,
|
||||
)
|
||||
for rule in ack["respond_on"]:
|
||||
print(
|
||||
f"{indent}respond_on={len(rule['targets'])} "
|
||||
f"send={format_frame(rule['frame'])} label={rule['label']} "
|
||||
f"once={int(rule['once'])}",
|
||||
file=stdout,
|
||||
)
|
||||
elif action in {"prompt", "note"}:
|
||||
message = str(spec.get("message", spec.get("value", "Press Enter to continue.")))
|
||||
print(f"{indent}message={message}", file=stdout)
|
||||
|
||||
@@ -31,6 +31,18 @@ KNOWN_FRAME_LABELS = {
|
||||
"00 00 15 80 00 CF": "known_call_button_active_report",
|
||||
"00 00 15 00 00 4F": "known_call_button_inactive_report",
|
||||
"00 00 07 80 00 DD": "known_cam_power_button_report",
|
||||
"00 00 13 00 00 49": "known_iris_mblack_link_clear_report_candidate",
|
||||
"00 00 13 40 00 09": "known_iris_mblack_link_active_report_candidate",
|
||||
"00 00 13 80 00 C9": "known_selector_0013_bit15_report_candidate",
|
||||
"00 00 13 C0 00 89": "known_selector_0013_bit15_plus_iris_mblack_link_report_candidate",
|
||||
"01 00 13 00 00 48": "queued_iris_mblack_link_clear_report_candidate",
|
||||
"02 00 13 00 00 4B": "queued_iris_mblack_link_clear_report_candidate",
|
||||
"01 00 13 40 00 08": "queued_iris_mblack_link_active_report_candidate",
|
||||
"02 00 13 40 00 0B": "queued_iris_mblack_link_active_report_candidate",
|
||||
"01 00 13 80 00 C8": "queued_selector_0013_bit15_report_candidate",
|
||||
"02 00 13 80 00 CB": "queued_selector_0013_bit15_report_candidate",
|
||||
"01 00 13 C0 00 88": "queued_selector_0013_bit15_plus_iris_mblack_link_report_candidate",
|
||||
"02 00 13 C0 00 8B": "queued_selector_0013_bit15_plus_iris_mblack_link_report_candidate",
|
||||
"01 00 17 80 00 CC": "queued_bars_button_selector_0017_active_candidate",
|
||||
"02 00 17 80 00 CF": "queued_bars_button_selector_0017_active_candidate",
|
||||
"01 00 18 80 00 C3": "queued_bars_button_selector_0018_active_candidate",
|
||||
|
||||
@@ -4,6 +4,8 @@ import re
|
||||
from collections.abc import Iterable, Mapping
|
||||
from typing import Any
|
||||
|
||||
from .panel_selectors import panel_selector_semantics_payload
|
||||
|
||||
|
||||
JsonObject = dict[str, Any]
|
||||
|
||||
@@ -140,6 +142,7 @@ def analyze_serial_semantics(payload: Mapping[str, Any]) -> JsonObject:
|
||||
"tx_report_model": None,
|
||||
"periodic_resend_model": None,
|
||||
"timer_interrupt_model": None,
|
||||
"panel_selector_semantics": [],
|
||||
"confidence": "low",
|
||||
"confidence_score": 0.0,
|
||||
"caveat": "No protocol semantics are emitted without both RX and TX serial reconstruction candidates.",
|
||||
@@ -212,6 +215,7 @@ def analyze_serial_semantics(payload: Mapping[str, Any]) -> JsonObject:
|
||||
"tx_report_model": tx_report_model,
|
||||
"periodic_resend_model": periodic_resend_model,
|
||||
"timer_interrupt_model": timer_interrupt_model,
|
||||
"panel_selector_semantics": panel_selector_semantics_payload(),
|
||||
"evidence": evidence,
|
||||
}
|
||||
return {
|
||||
@@ -233,6 +237,7 @@ def analyze_serial_semantics(payload: Mapping[str, Any]) -> JsonObject:
|
||||
"tx_report_model": protocol["tx_report_model"],
|
||||
"periodic_resend_model": protocol["periodic_resend_model"],
|
||||
"timer_interrupt_model": protocol["timer_interrupt_model"],
|
||||
"panel_selector_semantics": protocol["panel_selector_semantics"],
|
||||
"confidence": protocol["confidence"],
|
||||
"confidence_score": protocol["confidence_score"],
|
||||
"caveat": protocol["caveat"],
|
||||
|
||||
@@ -287,6 +287,7 @@ def _logical_operand_accesses(
|
||||
logical_address: int | None = None
|
||||
if isinstance(offset, int):
|
||||
logical_address = (int(table["logical_base_address"]) + offset) & 0xFFFF
|
||||
selector = _selector_for_table_offset(table, offset)
|
||||
access = _base_access(ins, functions, semantic_accesses)
|
||||
access.update(
|
||||
{
|
||||
@@ -306,6 +307,9 @@ def _logical_operand_accesses(
|
||||
if logical_address is not None:
|
||||
access["logical_address"] = logical_address
|
||||
access["logical_address_hex"] = h16(logical_address)
|
||||
if selector is not None:
|
||||
access["selector"] = selector
|
||||
access["selector_hex"] = f"0x{selector:03X}"
|
||||
accesses.append(access)
|
||||
return accesses
|
||||
|
||||
@@ -342,6 +346,7 @@ def _direct_logical_address_access(
|
||||
) -> JsonObject:
|
||||
base = int(table["logical_base_address"])
|
||||
offset = address - base
|
||||
selector = _selector_for_table_offset(table, offset)
|
||||
access = _base_access(ins, functions, semantic_accesses)
|
||||
access.update(
|
||||
{
|
||||
@@ -359,6 +364,9 @@ def _direct_logical_address_access(
|
||||
"access": _access_direction(ins, address) or "read_write_candidate",
|
||||
}
|
||||
)
|
||||
if selector is not None:
|
||||
access["selector"] = selector
|
||||
access["selector_hex"] = f"0x{selector:03X}"
|
||||
return access
|
||||
|
||||
|
||||
@@ -373,6 +381,7 @@ def _direct_candidate_address_access(
|
||||
offset = address - base
|
||||
access = _base_access(ins, functions, semantic_accesses)
|
||||
logical_offset = DIRECT_TABLE_TO_LOGICAL_OFFSET.get(base)
|
||||
selector = _selector_for_table_offset(table, offset)
|
||||
access.update(
|
||||
{
|
||||
"table": table["name"],
|
||||
@@ -392,6 +401,9 @@ def _direct_candidate_address_access(
|
||||
if logical_offset is not None:
|
||||
access["semantic_negative_offset"] = logical_offset
|
||||
access["semantic_negative_offset_hex"] = h16(logical_offset)
|
||||
if selector is not None:
|
||||
access["selector"] = selector
|
||||
access["selector_hex"] = f"0x{selector:03X}"
|
||||
return access
|
||||
|
||||
|
||||
@@ -564,6 +576,8 @@ def _format_access_line(access: Mapping[str, Any]) -> str:
|
||||
index_text = f"index dynamic via {access.get('index_register')} operand {operand}"
|
||||
else:
|
||||
index_text = f"offset {h16(int(index or 0))}"
|
||||
if access.get("selector_hex"):
|
||||
index_text += f" selector {access['selector_hex']}"
|
||||
if access.get("logical_address_hex"):
|
||||
index_text += f" -> {access['logical_address_hex']}"
|
||||
elif access.get("direct_address_hex"):
|
||||
@@ -599,6 +613,19 @@ def _summarize_functions(accesses: Iterable[Mapping[str, Any]]) -> list[JsonObje
|
||||
return sorted(summaries.values(), key=lambda item: (-int(item["access_count"]), str(item["label"])))
|
||||
|
||||
|
||||
def _selector_for_table_offset(table: Mapping[str, Any], offset: int | str) -> int | None:
|
||||
if not isinstance(offset, int):
|
||||
return None
|
||||
element = str(table.get("element_candidate") or "")
|
||||
if element == "word_value":
|
||||
if offset % 2:
|
||||
return None
|
||||
return (offset // 2) & 0x01FF
|
||||
if element == "bit_flags":
|
||||
return offset & 0x01FF
|
||||
return None
|
||||
|
||||
|
||||
def _function_ranges(payload: Mapping[str, Any]) -> list[JsonObject]:
|
||||
call_graph = payload.get("call_graph")
|
||||
if not isinstance(call_graph, Mapping):
|
||||
|
||||
227
scenarios/iris-mblack-link-mirror-state-machine.json
Normal file
227
scenarios/iris-mblack-link-mirror-state-machine.json
Normal file
@@ -0,0 +1,227 @@
|
||||
{
|
||||
"name": "iris-mblack-link-mirror-state-machine",
|
||||
"notes": [
|
||||
"IRIS/M.BLACK LINK closed-loop state-machine probe.",
|
||||
"The RCP reports local button intent as selector 0x0013. This scenario ACKs the report and mirrors the reported selector value back as a command-0 CCU table update.",
|
||||
"Expected cycle if the CCU owns the latched state: press 1 emits 00 00 13 40 00 09, mirror active, press 2 emits 00 00 13 00 00 49, mirror clear, press 3 emits active again.",
|
||||
"If every press still emits active, the local handler is not seeing the mirrored E800/F791 state or the physical input path is level-style rather than toggle-style."
|
||||
],
|
||||
"steps": [
|
||||
{
|
||||
"action": "prompt",
|
||||
"message": "Prepare to test the closed-loop IRIS/M.BLACK LINK state machine. Press Enter to power-cycle and start."
|
||||
},
|
||||
{
|
||||
"action": "power_cycle",
|
||||
"off_seconds": 1.5
|
||||
},
|
||||
{
|
||||
"action": "wait_ready",
|
||||
"heartbeats": 2,
|
||||
"timeout": 10.0,
|
||||
"require": true
|
||||
},
|
||||
{
|
||||
"action": "drain",
|
||||
"seconds": 0.20
|
||||
},
|
||||
{
|
||||
"action": "send",
|
||||
"label": "selector_zero_connect_seed",
|
||||
"frame": "00 00 00 80 00 DA",
|
||||
"listen": 0.20
|
||||
},
|
||||
{
|
||||
"action": "send",
|
||||
"label": "cmd5_latch_clear_0096",
|
||||
"frame": "05 01 16 00 00 48",
|
||||
"listen": 0.02
|
||||
},
|
||||
{
|
||||
"action": "listen_ack_until_quiet",
|
||||
"seconds": 28.00,
|
||||
"quiet_seconds": 0.90,
|
||||
"target_mode": "queued_reports",
|
||||
"ack_mode": "cmd5_selector",
|
||||
"ack_guard": 0.005,
|
||||
"post_ack_read": 0.035,
|
||||
"poll_interval": 0.003,
|
||||
"once_per_frame": false,
|
||||
"limit_scope": "local",
|
||||
"max_acks": 180,
|
||||
"abort_on_limit": false
|
||||
},
|
||||
{
|
||||
"action": "send",
|
||||
"label": "force_selector_0013_clear_baseline",
|
||||
"frame": "00 00 13 00 00 49",
|
||||
"listen": 0.40
|
||||
},
|
||||
{
|
||||
"action": "send",
|
||||
"label": "recover_connect_ok_seed_before_press_1",
|
||||
"frame": "00 00 00 80 00 DA",
|
||||
"listen": 0.20
|
||||
},
|
||||
{
|
||||
"action": "note",
|
||||
"message": "PRESS IRIS/M.BLACK LINK ONCE NOW. The script will mirror selector 0x0013 back to the RCP when it sees the report.",
|
||||
"banner": true
|
||||
},
|
||||
{
|
||||
"action": "listen_ack",
|
||||
"seconds": 9.00,
|
||||
"target_mode": "queued_reports",
|
||||
"ack_mode": "cmd5_selector",
|
||||
"ack_guard": 0.005,
|
||||
"post_ack_read": 0.035,
|
||||
"poll_interval": 0.003,
|
||||
"once_per_frame": false,
|
||||
"limit_scope": "local",
|
||||
"max_acks": 80,
|
||||
"abort_on_limit": false,
|
||||
"respond_on": [
|
||||
{
|
||||
"frames": [
|
||||
"00 00 13 40 00 09",
|
||||
"01 00 13 40 00 08",
|
||||
"02 00 13 40 00 0B"
|
||||
],
|
||||
"send": "00 00 13 40 00 09",
|
||||
"label": "mirror_selector_0013_active_from_button",
|
||||
"delay": 0.050,
|
||||
"listen": 0.20,
|
||||
"once": true
|
||||
},
|
||||
{
|
||||
"frames": [
|
||||
"00 00 13 00 00 49",
|
||||
"01 00 13 00 00 48",
|
||||
"02 00 13 00 00 4B"
|
||||
],
|
||||
"send": "00 00 13 00 00 49",
|
||||
"label": "mirror_selector_0013_clear_from_button",
|
||||
"delay": 0.050,
|
||||
"listen": 0.20,
|
||||
"once": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"action": "send",
|
||||
"label": "readback_selector_0013_after_press_1",
|
||||
"frame": "01 00 13 00 00 48",
|
||||
"listen": 0.60
|
||||
},
|
||||
{
|
||||
"action": "note",
|
||||
"message": "PRESS IRIS/M.BLACK LINK A SECOND TIME NOW. If the mirror completed the latch, this should report clear.",
|
||||
"banner": true
|
||||
},
|
||||
{
|
||||
"action": "listen_ack",
|
||||
"seconds": 9.00,
|
||||
"target_mode": "queued_reports",
|
||||
"ack_mode": "cmd5_selector",
|
||||
"ack_guard": 0.005,
|
||||
"post_ack_read": 0.035,
|
||||
"poll_interval": 0.003,
|
||||
"once_per_frame": false,
|
||||
"limit_scope": "local",
|
||||
"max_acks": 80,
|
||||
"abort_on_limit": false,
|
||||
"respond_on": [
|
||||
{
|
||||
"frames": [
|
||||
"00 00 13 40 00 09",
|
||||
"01 00 13 40 00 08",
|
||||
"02 00 13 40 00 0B"
|
||||
],
|
||||
"send": "00 00 13 40 00 09",
|
||||
"label": "mirror_selector_0013_active_from_button",
|
||||
"delay": 0.050,
|
||||
"listen": 0.20,
|
||||
"once": true
|
||||
},
|
||||
{
|
||||
"frames": [
|
||||
"00 00 13 00 00 49",
|
||||
"01 00 13 00 00 48",
|
||||
"02 00 13 00 00 4B"
|
||||
],
|
||||
"send": "00 00 13 00 00 49",
|
||||
"label": "mirror_selector_0013_clear_from_button",
|
||||
"delay": 0.050,
|
||||
"listen": 0.20,
|
||||
"once": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"action": "send",
|
||||
"label": "readback_selector_0013_after_press_2",
|
||||
"frame": "01 00 13 00 00 48",
|
||||
"listen": 0.60
|
||||
},
|
||||
{
|
||||
"action": "note",
|
||||
"message": "PRESS IRIS/M.BLACK LINK A THIRD TIME NOW to check that active returns after a mirrored clear.",
|
||||
"banner": true
|
||||
},
|
||||
{
|
||||
"action": "listen_ack",
|
||||
"seconds": 9.00,
|
||||
"target_mode": "queued_reports",
|
||||
"ack_mode": "cmd5_selector",
|
||||
"ack_guard": 0.005,
|
||||
"post_ack_read": 0.035,
|
||||
"poll_interval": 0.003,
|
||||
"once_per_frame": false,
|
||||
"limit_scope": "local",
|
||||
"max_acks": 80,
|
||||
"abort_on_limit": false,
|
||||
"respond_on": [
|
||||
{
|
||||
"frames": [
|
||||
"00 00 13 40 00 09",
|
||||
"01 00 13 40 00 08",
|
||||
"02 00 13 40 00 0B"
|
||||
],
|
||||
"send": "00 00 13 40 00 09",
|
||||
"label": "mirror_selector_0013_active_from_button",
|
||||
"delay": 0.050,
|
||||
"listen": 0.20,
|
||||
"once": true
|
||||
},
|
||||
{
|
||||
"frames": [
|
||||
"00 00 13 00 00 49",
|
||||
"01 00 13 00 00 48",
|
||||
"02 00 13 00 00 4B"
|
||||
],
|
||||
"send": "00 00 13 00 00 49",
|
||||
"label": "mirror_selector_0013_clear_from_button",
|
||||
"delay": 0.050,
|
||||
"listen": 0.20,
|
||||
"once": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"action": "send",
|
||||
"label": "readback_selector_0013_after_press_3",
|
||||
"frame": "01 00 13 00 00 48",
|
||||
"listen": 0.60
|
||||
},
|
||||
{
|
||||
"action": "send",
|
||||
"label": "repeat_last_for_hidden_report_check",
|
||||
"frame": "07 00 00 00 00 5D",
|
||||
"listen": 0.60
|
||||
},
|
||||
{
|
||||
"action": "listen",
|
||||
"seconds": 1.00
|
||||
}
|
||||
]
|
||||
}
|
||||
114
scenarios/iris-mblack-link-report-after-quiet-press.json
Normal file
114
scenarios/iris-mblack-link-report-after-quiet-press.json
Normal file
@@ -0,0 +1,114 @@
|
||||
{
|
||||
"name": "iris-mblack-link-report-after-quiet-press",
|
||||
"notes": [
|
||||
"Second-pass IRIS/M.BLACK LINK button report test.",
|
||||
"This drains the startup/current-state report queue until it goes quiet before asking for a physical press, so a fresh selector-0013 report is easier to separate from boot backlog.",
|
||||
"Expected fresh report shapes are 00/01/02 00 13 40 00 for active, or 00/01/02 00 13 00 00 for clear. Page-1 selector 01 13 is not this button; it is selector 0x0093."
|
||||
],
|
||||
"steps": [
|
||||
{
|
||||
"action": "prompt",
|
||||
"message": "Prepare to test IRIS/M.BLACK LINK after the report queue drains. Press Enter to power-cycle and start."
|
||||
},
|
||||
{
|
||||
"action": "power_cycle",
|
||||
"off_seconds": 1.5
|
||||
},
|
||||
{
|
||||
"action": "wait_ready",
|
||||
"heartbeats": 2,
|
||||
"timeout": 10.0,
|
||||
"require": true
|
||||
},
|
||||
{
|
||||
"action": "drain",
|
||||
"seconds": 0.20
|
||||
},
|
||||
{
|
||||
"action": "send",
|
||||
"label": "selector_zero_connect_seed",
|
||||
"frame": "00 00 00 80 00 DA",
|
||||
"listen": 0.20
|
||||
},
|
||||
{
|
||||
"action": "send",
|
||||
"label": "cmd5_latch_clear_0096",
|
||||
"frame": "05 01 16 00 00 48",
|
||||
"listen": 0.02
|
||||
},
|
||||
{
|
||||
"action": "listen_ack_until_quiet",
|
||||
"seconds": 28.00,
|
||||
"quiet_seconds": 0.90,
|
||||
"target_mode": "queued_reports",
|
||||
"ack_mode": "cmd5_selector",
|
||||
"ack_guard": 0.005,
|
||||
"post_ack_read": 0.035,
|
||||
"poll_interval": 0.003,
|
||||
"once_per_frame": false,
|
||||
"limit_scope": "local",
|
||||
"max_acks": 180,
|
||||
"abort_on_limit": false
|
||||
},
|
||||
{
|
||||
"action": "send",
|
||||
"label": "recover_connect_ok_seed_before_press",
|
||||
"frame": "00 00 00 80 00 DA",
|
||||
"listen": 0.20
|
||||
},
|
||||
{
|
||||
"action": "note",
|
||||
"message": "QUEUE IS QUIET. PRESS IRIS/M.BLACK LINK ONCE NOW; fresh-report ACK window is running.",
|
||||
"banner": true
|
||||
},
|
||||
{
|
||||
"action": "listen_ack_until_quiet",
|
||||
"seconds": 14.00,
|
||||
"quiet_seconds": 1.20,
|
||||
"target_mode": "queued_reports",
|
||||
"ack_mode": "cmd5_selector",
|
||||
"ack_guard": 0.005,
|
||||
"post_ack_read": 0.035,
|
||||
"poll_interval": 0.003,
|
||||
"once_per_frame": false,
|
||||
"limit_scope": "local",
|
||||
"max_acks": 64,
|
||||
"abort_on_limit": false
|
||||
},
|
||||
{
|
||||
"action": "send",
|
||||
"label": "recover_connect_ok_seed_before_second_press",
|
||||
"frame": "00 00 00 80 00 DA",
|
||||
"listen": 0.20
|
||||
},
|
||||
{
|
||||
"action": "note",
|
||||
"message": "PRESS IRIS/M.BLACK LINK A SECOND TIME NOW to test the opposite toggle state.",
|
||||
"banner": true
|
||||
},
|
||||
{
|
||||
"action": "listen_ack_until_quiet",
|
||||
"seconds": 14.00,
|
||||
"quiet_seconds": 1.20,
|
||||
"target_mode": "queued_reports",
|
||||
"ack_mode": "cmd5_selector",
|
||||
"ack_guard": 0.005,
|
||||
"post_ack_read": 0.035,
|
||||
"poll_interval": 0.003,
|
||||
"once_per_frame": false,
|
||||
"limit_scope": "local",
|
||||
"max_acks": 64,
|
||||
"abort_on_limit": false
|
||||
},
|
||||
{
|
||||
"action": "send",
|
||||
"label": "repeat_last_for_hidden_report_check",
|
||||
"frame": "07 00 00 00 00 5D",
|
||||
"listen": 0.60
|
||||
},
|
||||
{
|
||||
"action": "listen",
|
||||
"seconds": 1.00
|
||||
}
|
||||
]
|
||||
}
|
||||
125
scenarios/iris-mblack-link-report-press.json
Normal file
125
scenarios/iris-mblack-link-report-press.json
Normal file
@@ -0,0 +1,125 @@
|
||||
{
|
||||
"name": "iris-mblack-link-report-press",
|
||||
"notes": [
|
||||
"Focused queued-report test for the IRIS/M.BLACK LINK physical button.",
|
||||
"The ROM path at H'200E toggles E800[0x0013].14 when F006.7/F6DB.7 is active and F731 <= 3, then calls loc_3E54 with R2=0x80 R3=0x0013.",
|
||||
"This scenario services the local report queue by ACKing checksum-valid 00/01/02 report frames with command 5 for the observed selector.",
|
||||
"Expected selector-0013 report shapes include 00 00 13 40 00 09 for active, 00 00 13 00 00 49 for clear, and bit15 variants 00 00 13 80 00 C9 / 00 00 13 C0 00 89. Queued 01/02 variants are also valid evidence."
|
||||
],
|
||||
"steps": [
|
||||
{
|
||||
"action": "prompt",
|
||||
"message": "Prepare to test the IRIS/M.BLACK LINK button. Press Enter to power-cycle and start the live ACK windows."
|
||||
},
|
||||
{
|
||||
"action": "power_cycle",
|
||||
"off_seconds": 1.5
|
||||
},
|
||||
{
|
||||
"action": "wait_ready",
|
||||
"heartbeats": 2,
|
||||
"timeout": 10.0,
|
||||
"require": true
|
||||
},
|
||||
{
|
||||
"action": "drain",
|
||||
"seconds": 0.25
|
||||
},
|
||||
{
|
||||
"action": "send",
|
||||
"label": "selector_zero_connect_seed",
|
||||
"frame": "00 00 00 80 00 DA",
|
||||
"listen": 0.60
|
||||
},
|
||||
{
|
||||
"action": "send",
|
||||
"label": "cmd5_latch_clear_0096",
|
||||
"frame": "05 01 16 00 00 48",
|
||||
"listen": 0.05
|
||||
},
|
||||
{
|
||||
"action": "listen_ack",
|
||||
"seconds": 3.20,
|
||||
"target_mode": "queued_reports",
|
||||
"ack_mode": "cmd5_selector",
|
||||
"ack_guard": 0.010,
|
||||
"post_ack_read": 0.070,
|
||||
"poll_interval": 0.004,
|
||||
"once_per_frame": false,
|
||||
"limit_scope": "local",
|
||||
"max_acks": 48,
|
||||
"abort_on_limit": false
|
||||
},
|
||||
{
|
||||
"action": "send",
|
||||
"label": "recover_connect_ok_seed",
|
||||
"frame": "00 00 00 80 00 DA",
|
||||
"listen": 0.45
|
||||
},
|
||||
{
|
||||
"action": "listen_ack",
|
||||
"seconds": 1.20,
|
||||
"target_mode": "queued_reports",
|
||||
"ack_mode": "cmd5_selector",
|
||||
"ack_guard": 0.010,
|
||||
"post_ack_read": 0.070,
|
||||
"poll_interval": 0.004,
|
||||
"once_per_frame": false,
|
||||
"limit_scope": "local",
|
||||
"max_acks": 32,
|
||||
"abort_on_limit": false
|
||||
},
|
||||
{
|
||||
"action": "note",
|
||||
"message": "PRESS IRIS/M.BLACK LINK ONCE NOW; live queued-report ACK window is running.",
|
||||
"banner": true
|
||||
},
|
||||
{
|
||||
"action": "listen_ack",
|
||||
"seconds": 10.00,
|
||||
"target_mode": "queued_reports",
|
||||
"ack_mode": "cmd5_selector",
|
||||
"ack_guard": 0.010,
|
||||
"post_ack_read": 0.070,
|
||||
"poll_interval": 0.004,
|
||||
"once_per_frame": false,
|
||||
"limit_scope": "local",
|
||||
"max_acks": 96,
|
||||
"abort_on_limit": false
|
||||
},
|
||||
{
|
||||
"action": "send",
|
||||
"label": "refresh_connect_ok_seed_before_second_press",
|
||||
"frame": "00 00 00 80 00 DA",
|
||||
"listen": 0.35
|
||||
},
|
||||
{
|
||||
"action": "note",
|
||||
"message": "PRESS IRIS/M.BLACK LINK A SECOND TIME NOW to test the toggle-clear report.",
|
||||
"banner": true
|
||||
},
|
||||
{
|
||||
"action": "listen_ack",
|
||||
"seconds": 10.00,
|
||||
"target_mode": "queued_reports",
|
||||
"ack_mode": "cmd5_selector",
|
||||
"ack_guard": 0.010,
|
||||
"post_ack_read": 0.070,
|
||||
"poll_interval": 0.004,
|
||||
"once_per_frame": false,
|
||||
"limit_scope": "local",
|
||||
"max_acks": 96,
|
||||
"abort_on_limit": false
|
||||
},
|
||||
{
|
||||
"action": "send",
|
||||
"label": "repeat_last_for_hidden_report_check",
|
||||
"frame": "07 00 00 00 00 5D",
|
||||
"listen": 0.80
|
||||
},
|
||||
{
|
||||
"action": "listen",
|
||||
"seconds": 1.50
|
||||
}
|
||||
]
|
||||
}
|
||||
74
scenarios/panel-atlas-big-hits-isolation-v1.json
Normal file
74
scenarios/panel-atlas-big-hits-isolation-v1.json
Normal file
@@ -0,0 +1,74 @@
|
||||
{
|
||||
"name": "panel-atlas-big-hits-isolation-v1",
|
||||
"notes": [
|
||||
"Fresh-boot isolation pass for user-reviewed hits from panel-atlas-big-visual-sweep-0001-017f-highbits.",
|
||||
"Each case power-cycles, seeds CONNECT OK, captures a baseline image, sends the candidate, then captures a clear/low write.",
|
||||
"Expected observations are from the first broad image review and must be confirmed here."
|
||||
],
|
||||
"steps": [
|
||||
{"action": "note", "message": "Case 1: 0x0013=0x4000, reported Iris/MBlack Link", "banner": true},
|
||||
{"action": "power_cycle", "off_seconds": 1.5},
|
||||
{"action": "wait_ready", "heartbeats": 2, "timeout": 10.0, "require": true},
|
||||
{"action": "drain", "seconds": 0.5},
|
||||
{"action": "send", "label": "case001_baseline_before_0013_4000", "frame": "00 00 00 80 00 DA", "listen": 0.7},
|
||||
{"action": "send", "label": "case001_candidate_0013_4000_iris_mblack_link", "frame": "00 00 13 40 00 09", "listen": 1.0},
|
||||
{"action": "send", "label": "case001_clear_0013", "frame": "00 00 13 00 00 49", "listen": 0.7},
|
||||
|
||||
{"action": "note", "message": "Case 2: 0x0024=0x8000, reported LCD selector button", "banner": true},
|
||||
{"action": "power_cycle", "off_seconds": 1.5},
|
||||
{"action": "wait_ready", "heartbeats": 2, "timeout": 10.0, "require": true},
|
||||
{"action": "drain", "seconds": 0.5},
|
||||
{"action": "send", "label": "case002_baseline_before_0024_8000", "frame": "00 00 00 80 00 DA", "listen": 0.7},
|
||||
{"action": "send", "label": "case002_candidate_0024_8000_lcd_selector_button", "frame": "00 00 24 80 00 FE", "listen": 1.0},
|
||||
{"action": "send", "label": "case002_clear_0024", "frame": "00 00 24 00 00 7E", "listen": 0.7},
|
||||
|
||||
{"action": "note", "message": "Case 3: 0x0082=0x8000, reported IRIS display OP", "banner": true},
|
||||
{"action": "power_cycle", "off_seconds": 1.5},
|
||||
{"action": "wait_ready", "heartbeats": 2, "timeout": 10.0, "require": true},
|
||||
{"action": "drain", "seconds": 0.5},
|
||||
{"action": "send", "label": "case003_baseline_before_0082_8000", "frame": "00 00 00 80 00 DA", "listen": 0.7},
|
||||
{"action": "send", "label": "case003_candidate_0082_8000_iris_op", "frame": "00 01 02 80 00 D9", "listen": 1.0},
|
||||
{"action": "send", "label": "case003_clear_0082", "frame": "00 01 02 00 00 59", "listen": 0.7},
|
||||
|
||||
{"action": "note", "message": "Case 4: 0x0082=0x4000, reported IRIS display 1.4", "banner": true},
|
||||
{"action": "power_cycle", "off_seconds": 1.5},
|
||||
{"action": "wait_ready", "heartbeats": 2, "timeout": 10.0, "require": true},
|
||||
{"action": "drain", "seconds": 0.5},
|
||||
{"action": "send", "label": "case004_baseline_before_0082_4000", "frame": "00 00 00 80 00 DA", "listen": 0.7},
|
||||
{"action": "send", "label": "case004_candidate_0082_4000_iris_1_4", "frame": "00 01 02 40 00 19", "listen": 1.0},
|
||||
{"action": "send", "label": "case004_clear_0082", "frame": "00 01 02 00 00 59", "listen": 0.7},
|
||||
|
||||
{"action": "note", "message": "Case 5: 0x0083=0x8000, reported MASTER GAIN display -3", "banner": true},
|
||||
{"action": "power_cycle", "off_seconds": 1.5},
|
||||
{"action": "wait_ready", "heartbeats": 2, "timeout": 10.0, "require": true},
|
||||
{"action": "drain", "seconds": 0.5},
|
||||
{"action": "send", "label": "case005_baseline_before_0083_8000", "frame": "00 00 00 80 00 DA", "listen": 0.7},
|
||||
{"action": "send", "label": "case005_candidate_0083_8000_master_gain_minus_3", "frame": "00 01 03 80 00 D8", "listen": 1.0},
|
||||
{"action": "send", "label": "case005_clear_0083", "frame": "00 01 03 00 00 58", "listen": 0.7},
|
||||
|
||||
{"action": "note", "message": "Case 6: 0x0093=0x8000, reported white balance PRESET", "banner": true},
|
||||
{"action": "power_cycle", "off_seconds": 1.5},
|
||||
{"action": "wait_ready", "heartbeats": 2, "timeout": 10.0, "require": true},
|
||||
{"action": "drain", "seconds": 0.5},
|
||||
{"action": "send", "label": "case006_baseline_before_0093_8000", "frame": "00 00 00 80 00 DA", "listen": 0.7},
|
||||
{"action": "send", "label": "case006_candidate_0093_8000_white_balance_preset", "frame": "00 01 13 80 00 C8", "listen": 1.0},
|
||||
{"action": "send", "label": "case006_clear_0093", "frame": "00 01 13 00 00 48", "listen": 0.7},
|
||||
|
||||
{"action": "note", "message": "Case 7: 0x0093=0x4000, reported white balance AUTO", "banner": true},
|
||||
{"action": "power_cycle", "off_seconds": 1.5},
|
||||
{"action": "wait_ready", "heartbeats": 2, "timeout": 10.0, "require": true},
|
||||
{"action": "drain", "seconds": 0.5},
|
||||
{"action": "send", "label": "case007_baseline_before_0093_4000", "frame": "00 00 00 80 00 DA", "listen": 0.7},
|
||||
{"action": "send", "label": "case007_candidate_0093_4000_white_balance_auto", "frame": "00 01 13 40 00 08", "listen": 1.0},
|
||||
{"action": "send", "label": "case007_clear_0093", "frame": "00 01 13 00 00 48", "listen": 0.7},
|
||||
|
||||
{"action": "note", "message": "Case 8: 0x0093=0x2000, reported white balance MANUAL", "banner": true},
|
||||
{"action": "power_cycle", "off_seconds": 1.5},
|
||||
{"action": "wait_ready", "heartbeats": 2, "timeout": 10.0, "require": true},
|
||||
{"action": "drain", "seconds": 0.5},
|
||||
{"action": "send", "label": "case008_baseline_before_0093_2000", "frame": "00 00 00 80 00 DA", "listen": 0.7},
|
||||
{"action": "send", "label": "case008_candidate_0093_2000_white_balance_manual", "frame": "00 01 13 20 00 68", "listen": 1.0},
|
||||
{"action": "send", "label": "case008_clear_0093", "frame": "00 01 13 00 00 48", "listen": 0.7},
|
||||
{"action": "listen", "seconds": 1.0}
|
||||
]
|
||||
}
|
||||
14871
scenarios/panel-atlas-big-visual-sweep-0001-017f-highbits.json
Normal file
14871
scenarios/panel-atlas-big-visual-sweep-0001-017f-highbits.json
Normal file
File diff suppressed because it is too large
Load Diff
142
scenarios/panel-atlas-operator-lamps-v1.json
Normal file
142
scenarios/panel-atlas-operator-lamps-v1.json
Normal file
@@ -0,0 +1,142 @@
|
||||
{
|
||||
"name": "panel-atlas-operator-lamps-v1",
|
||||
"notes": [
|
||||
"Compact visible-output atlas for operator lamps and tally-adjacent selectors.",
|
||||
"Use webcam snapshots to tie each selector/value write to the panel state.",
|
||||
"This intentionally stays on command-0 primary-table writes and avoids COPY/menu latch selectors."
|
||||
],
|
||||
"steps": [
|
||||
{
|
||||
"action": "power_cycle",
|
||||
"off_seconds": 1.5
|
||||
},
|
||||
{
|
||||
"action": "wait_ready",
|
||||
"heartbeats": 2,
|
||||
"timeout": 10.0,
|
||||
"require": true
|
||||
},
|
||||
{
|
||||
"action": "drain",
|
||||
"seconds": 0.25
|
||||
},
|
||||
{
|
||||
"action": "send",
|
||||
"label": "selector_zero_ok_seed_1",
|
||||
"frame": "00 00 00 80 00 DA",
|
||||
"listen": 0.60
|
||||
},
|
||||
{
|
||||
"action": "send",
|
||||
"label": "selector_zero_ok_seed_2",
|
||||
"frame": "00 00 00 80 00 DA",
|
||||
"listen": 0.60
|
||||
},
|
||||
{
|
||||
"action": "send",
|
||||
"label": "cam_power_candidate_0007_high",
|
||||
"frame": "00 00 07 80 00 DD",
|
||||
"listen": 0.90
|
||||
},
|
||||
{
|
||||
"action": "send",
|
||||
"label": "cam_power_candidate_0007_low",
|
||||
"frame": "00 00 07 00 00 5D",
|
||||
"listen": 0.50
|
||||
},
|
||||
{
|
||||
"action": "send",
|
||||
"label": "call_candidate_0015_high",
|
||||
"frame": "00 00 15 80 00 CF",
|
||||
"listen": 0.90
|
||||
},
|
||||
{
|
||||
"action": "send",
|
||||
"label": "call_candidate_0015_low",
|
||||
"frame": "00 00 15 00 00 4F",
|
||||
"listen": 0.50
|
||||
},
|
||||
{
|
||||
"action": "send",
|
||||
"label": "selector_zero_ok_refresh_before_neighbors",
|
||||
"frame": "00 00 00 80 00 DA",
|
||||
"listen": 0.40
|
||||
},
|
||||
{
|
||||
"action": "send",
|
||||
"label": "neighbor_0012_high",
|
||||
"frame": "00 00 12 80 00 C8",
|
||||
"listen": 0.90
|
||||
},
|
||||
{
|
||||
"action": "send",
|
||||
"label": "neighbor_0012_low",
|
||||
"frame": "00 00 12 00 00 48",
|
||||
"listen": 0.45
|
||||
},
|
||||
{
|
||||
"action": "send",
|
||||
"label": "neighbor_0013_high",
|
||||
"frame": "00 00 13 80 00 C9",
|
||||
"listen": 0.90
|
||||
},
|
||||
{
|
||||
"action": "send",
|
||||
"label": "neighbor_0013_low",
|
||||
"frame": "00 00 13 00 00 49",
|
||||
"listen": 0.45
|
||||
},
|
||||
{
|
||||
"action": "send",
|
||||
"label": "neighbor_0016_high",
|
||||
"frame": "00 00 16 80 00 CC",
|
||||
"listen": 0.90
|
||||
},
|
||||
{
|
||||
"action": "send",
|
||||
"label": "neighbor_0016_low",
|
||||
"frame": "00 00 16 00 00 4C",
|
||||
"listen": 0.45
|
||||
},
|
||||
{
|
||||
"action": "send",
|
||||
"label": "neighbor_0017_high",
|
||||
"frame": "00 00 17 80 00 CD",
|
||||
"listen": 0.90
|
||||
},
|
||||
{
|
||||
"action": "send",
|
||||
"label": "neighbor_0017_low",
|
||||
"frame": "00 00 17 00 00 4D",
|
||||
"listen": 0.45
|
||||
},
|
||||
{
|
||||
"action": "send",
|
||||
"label": "neighbor_0018_high",
|
||||
"frame": "00 00 18 80 00 C2",
|
||||
"listen": 0.90
|
||||
},
|
||||
{
|
||||
"action": "send",
|
||||
"label": "neighbor_0018_low",
|
||||
"frame": "00 00 18 00 00 42",
|
||||
"listen": 0.45
|
||||
},
|
||||
{
|
||||
"action": "send",
|
||||
"label": "neighbor_001a_high",
|
||||
"frame": "00 00 1A 80 00 C0",
|
||||
"listen": 0.90
|
||||
},
|
||||
{
|
||||
"action": "send",
|
||||
"label": "neighbor_001a_low",
|
||||
"frame": "00 00 1A 00 00 40",
|
||||
"listen": 0.45
|
||||
},
|
||||
{
|
||||
"action": "listen",
|
||||
"seconds": 0.80
|
||||
}
|
||||
]
|
||||
}
|
||||
124
scenarios/panel-atlas-readout-status-v1.json
Normal file
124
scenarios/panel-atlas-readout-status-v1.json
Normal file
@@ -0,0 +1,124 @@
|
||||
{
|
||||
"name": "panel-atlas-readout-status-v1",
|
||||
"notes": [
|
||||
"Compact visible-output atlas for readouts and status clusters.",
|
||||
"Targets known shutter display, white-balance/black-flare, and KNEE candidate selectors.",
|
||||
"Use webcam snapshots to tie each selector/value write to visible LCD, lamp, and seven-segment states."
|
||||
],
|
||||
"steps": [
|
||||
{
|
||||
"action": "power_cycle",
|
||||
"off_seconds": 1.5
|
||||
},
|
||||
{
|
||||
"action": "wait_ready",
|
||||
"heartbeats": 2,
|
||||
"timeout": 10.0,
|
||||
"require": true
|
||||
},
|
||||
{
|
||||
"action": "drain",
|
||||
"seconds": 0.25
|
||||
},
|
||||
{
|
||||
"action": "send",
|
||||
"label": "selector_zero_ok_seed_1",
|
||||
"frame": "00 00 00 80 00 DA",
|
||||
"listen": 0.60
|
||||
},
|
||||
{
|
||||
"action": "send",
|
||||
"label": "selector_zero_ok_seed_2",
|
||||
"frame": "00 00 00 80 00 DA",
|
||||
"listen": 0.60
|
||||
},
|
||||
{
|
||||
"action": "send",
|
||||
"label": "shutter_008f_evs_bit11",
|
||||
"frame": "00 01 0F 08 00 5C",
|
||||
"listen": 0.90
|
||||
},
|
||||
{
|
||||
"action": "send",
|
||||
"label": "shutter_008f_off_bit12",
|
||||
"frame": "00 01 0F 10 00 44",
|
||||
"listen": 0.90
|
||||
},
|
||||
{
|
||||
"action": "send",
|
||||
"label": "shutter_008f_clear",
|
||||
"frame": "00 01 0F 00 00 54",
|
||||
"listen": 0.50
|
||||
},
|
||||
{
|
||||
"action": "send",
|
||||
"label": "selector_zero_ok_refresh_before_0093",
|
||||
"frame": "00 00 00 80 00 DA",
|
||||
"listen": 0.40
|
||||
},
|
||||
{
|
||||
"action": "send",
|
||||
"label": "status_0093_bit15_wb_preset_black_manual",
|
||||
"frame": "00 01 13 80 00 C8",
|
||||
"listen": 0.90
|
||||
},
|
||||
{
|
||||
"action": "send",
|
||||
"label": "status_0093_9020_black_manual_context",
|
||||
"frame": "00 01 13 90 20 F8",
|
||||
"listen": 0.90
|
||||
},
|
||||
{
|
||||
"action": "send",
|
||||
"label": "status_0093_90ff_black_auto_candidate",
|
||||
"frame": "00 01 13 90 FF 27",
|
||||
"listen": 0.90
|
||||
},
|
||||
{
|
||||
"action": "send",
|
||||
"label": "status_0093_clear",
|
||||
"frame": "00 01 13 00 00 48",
|
||||
"listen": 0.50
|
||||
},
|
||||
{
|
||||
"action": "send",
|
||||
"label": "selector_zero_ok_refresh_before_knee",
|
||||
"frame": "00 00 00 80 00 DA",
|
||||
"listen": 0.40
|
||||
},
|
||||
{
|
||||
"action": "send",
|
||||
"label": "knee_00b9_bit13_report_gate",
|
||||
"frame": "00 01 39 20 00 42",
|
||||
"listen": 0.90
|
||||
},
|
||||
{
|
||||
"action": "send",
|
||||
"label": "knee_00b9_bits15_13_label_gate",
|
||||
"frame": "00 01 39 A0 00 C2",
|
||||
"listen": 0.90
|
||||
},
|
||||
{
|
||||
"action": "send",
|
||||
"label": "knee_0110_bit15_auto_or_dl_candidate",
|
||||
"frame": "00 01 90 80 00 4B",
|
||||
"listen": 1.10
|
||||
},
|
||||
{
|
||||
"action": "send",
|
||||
"label": "knee_0110_clear",
|
||||
"frame": "00 01 90 00 00 CB",
|
||||
"listen": 0.70
|
||||
},
|
||||
{
|
||||
"action": "send",
|
||||
"label": "knee_00b9_clear",
|
||||
"frame": "00 01 39 00 00 62",
|
||||
"listen": 0.70
|
||||
},
|
||||
{
|
||||
"action": "listen",
|
||||
"seconds": 0.80
|
||||
}
|
||||
]
|
||||
}
|
||||
124
scenarios/panel-atlas-right-stack-fresh-latch-v1.json
Normal file
124
scenarios/panel-atlas-right-stack-fresh-latch-v1.json
Normal file
@@ -0,0 +1,124 @@
|
||||
{
|
||||
"name": "panel-atlas-right-stack-fresh-latch-v1",
|
||||
"notes": [
|
||||
"Fresh-boot isolation for right-side status selectors that looked latched in the compact run.",
|
||||
"Each selector gets its own power cycle before high/low testing so 0x0017 cannot contaminate 0x001A.",
|
||||
"Use webcam snapshots to compare CONNECT OK baseline, high write, and low write."
|
||||
],
|
||||
"steps": [
|
||||
{
|
||||
"action": "power_cycle",
|
||||
"off_seconds": 1.5
|
||||
},
|
||||
{
|
||||
"action": "wait_ready",
|
||||
"heartbeats": 2,
|
||||
"timeout": 10.0,
|
||||
"require": true
|
||||
},
|
||||
{
|
||||
"action": "drain",
|
||||
"seconds": 0.25
|
||||
},
|
||||
{
|
||||
"action": "send",
|
||||
"label": "case_0017_ok_seed_1",
|
||||
"frame": "00 00 00 80 00 DA",
|
||||
"listen": 0.60
|
||||
},
|
||||
{
|
||||
"action": "send",
|
||||
"label": "case_0017_ok_seed_2_baseline",
|
||||
"frame": "00 00 00 80 00 DA",
|
||||
"listen": 0.60
|
||||
},
|
||||
{
|
||||
"action": "send",
|
||||
"label": "case_0017_high_bars_family",
|
||||
"frame": "00 00 17 80 00 CD",
|
||||
"listen": 0.90
|
||||
},
|
||||
{
|
||||
"action": "send",
|
||||
"label": "case_0017_low_clear_attempt",
|
||||
"frame": "00 00 17 00 00 4D",
|
||||
"listen": 1.00
|
||||
},
|
||||
{
|
||||
"action": "power_cycle",
|
||||
"off_seconds": 1.5
|
||||
},
|
||||
{
|
||||
"action": "wait_ready",
|
||||
"heartbeats": 2,
|
||||
"timeout": 10.0,
|
||||
"require": true
|
||||
},
|
||||
{
|
||||
"action": "drain",
|
||||
"seconds": 0.25
|
||||
},
|
||||
{
|
||||
"action": "send",
|
||||
"label": "case_001a_ok_seed_1",
|
||||
"frame": "00 00 00 80 00 DA",
|
||||
"listen": 0.60
|
||||
},
|
||||
{
|
||||
"action": "send",
|
||||
"label": "case_001a_ok_seed_2_baseline",
|
||||
"frame": "00 00 00 80 00 DA",
|
||||
"listen": 0.60
|
||||
},
|
||||
{
|
||||
"action": "send",
|
||||
"label": "case_001a_high_bars_family",
|
||||
"frame": "00 00 1A 80 00 C0",
|
||||
"listen": 0.90
|
||||
},
|
||||
{
|
||||
"action": "send",
|
||||
"label": "case_001a_low_clear_attempt",
|
||||
"frame": "00 00 1A 00 00 40",
|
||||
"listen": 1.00
|
||||
},
|
||||
{
|
||||
"action": "power_cycle",
|
||||
"off_seconds": 1.5
|
||||
},
|
||||
{
|
||||
"action": "wait_ready",
|
||||
"heartbeats": 2,
|
||||
"timeout": 10.0,
|
||||
"require": true
|
||||
},
|
||||
{
|
||||
"action": "drain",
|
||||
"seconds": 0.25
|
||||
},
|
||||
{
|
||||
"action": "send",
|
||||
"label": "case_0007_ok_seed_1",
|
||||
"frame": "00 00 00 80 00 DA",
|
||||
"listen": 0.60
|
||||
},
|
||||
{
|
||||
"action": "send",
|
||||
"label": "case_0007_ok_seed_2_baseline",
|
||||
"frame": "00 00 00 80 00 DA",
|
||||
"listen": 0.60
|
||||
},
|
||||
{
|
||||
"action": "send",
|
||||
"label": "case_0007_high_cam_power",
|
||||
"frame": "00 00 07 80 00 DD",
|
||||
"listen": 0.90
|
||||
},
|
||||
{
|
||||
"action": "send",
|
||||
"label": "case_0007_low_clear_attempt",
|
||||
"frame": "00 00 07 00 00 5D",
|
||||
"listen": 1.00
|
||||
}
|
||||
]
|
||||
}
|
||||
154
scenarios/panel-atlas-right-stack-isolation-v1.json
Normal file
154
scenarios/panel-atlas-right-stack-isolation-v1.json
Normal file
@@ -0,0 +1,154 @@
|
||||
{
|
||||
"name": "panel-atlas-right-stack-isolation-v1",
|
||||
"notes": [
|
||||
"Tight right-side lamp isolation for CAM POWER/CALL/BARS/status candidates.",
|
||||
"Each candidate is held high then low twice so webcam frames can be compared against the same baseline.",
|
||||
"Watch the right-side CAM POWER/BARS/status stack and the CALL/CAM POWER lamps; CONNECT OK traffic is kept alive throughout."
|
||||
],
|
||||
"steps": [
|
||||
{
|
||||
"action": "power_cycle",
|
||||
"off_seconds": 1.5
|
||||
},
|
||||
{
|
||||
"action": "wait_ready",
|
||||
"heartbeats": 2,
|
||||
"timeout": 10.0,
|
||||
"require": true
|
||||
},
|
||||
{
|
||||
"action": "drain",
|
||||
"seconds": 0.25
|
||||
},
|
||||
{
|
||||
"action": "send",
|
||||
"label": "selector_zero_ok_seed_1",
|
||||
"frame": "00 00 00 80 00 DA",
|
||||
"listen": 0.60
|
||||
},
|
||||
{
|
||||
"action": "send",
|
||||
"label": "selector_zero_ok_seed_2",
|
||||
"frame": "00 00 00 80 00 DA",
|
||||
"listen": 0.60
|
||||
},
|
||||
{
|
||||
"action": "repeat",
|
||||
"count": 2,
|
||||
"steps": [
|
||||
{
|
||||
"action": "send",
|
||||
"label": "candidate_0007_high_cam_power",
|
||||
"frame": "00 00 07 80 00 DD",
|
||||
"listen": 0.85
|
||||
},
|
||||
{
|
||||
"action": "send",
|
||||
"label": "candidate_0007_low_cam_power",
|
||||
"frame": "00 00 07 00 00 5D",
|
||||
"listen": 0.65
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"action": "send",
|
||||
"label": "selector_zero_ok_refresh_before_0015",
|
||||
"frame": "00 00 00 80 00 DA",
|
||||
"listen": 0.40
|
||||
},
|
||||
{
|
||||
"action": "repeat",
|
||||
"count": 2,
|
||||
"steps": [
|
||||
{
|
||||
"action": "send",
|
||||
"label": "candidate_0015_high_call",
|
||||
"frame": "00 00 15 80 00 CF",
|
||||
"listen": 0.85
|
||||
},
|
||||
{
|
||||
"action": "send",
|
||||
"label": "candidate_0015_low_call",
|
||||
"frame": "00 00 15 00 00 4F",
|
||||
"listen": 0.65
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"action": "send",
|
||||
"label": "selector_zero_ok_refresh_before_0013",
|
||||
"frame": "00 00 00 80 00 DA",
|
||||
"listen": 0.40
|
||||
},
|
||||
{
|
||||
"action": "repeat",
|
||||
"count": 2,
|
||||
"steps": [
|
||||
{
|
||||
"action": "send",
|
||||
"label": "candidate_0013_high_orange_status",
|
||||
"frame": "00 00 13 80 00 C9",
|
||||
"listen": 0.85
|
||||
},
|
||||
{
|
||||
"action": "send",
|
||||
"label": "candidate_0013_low_orange_status",
|
||||
"frame": "00 00 13 00 00 49",
|
||||
"listen": 0.65
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"action": "send",
|
||||
"label": "selector_zero_ok_refresh_before_0017",
|
||||
"frame": "00 00 00 80 00 DA",
|
||||
"listen": 0.40
|
||||
},
|
||||
{
|
||||
"action": "repeat",
|
||||
"count": 2,
|
||||
"steps": [
|
||||
{
|
||||
"action": "send",
|
||||
"label": "candidate_0017_high_bars_family",
|
||||
"frame": "00 00 17 80 00 CD",
|
||||
"listen": 0.85
|
||||
},
|
||||
{
|
||||
"action": "send",
|
||||
"label": "candidate_0017_low_bars_family",
|
||||
"frame": "00 00 17 00 00 4D",
|
||||
"listen": 0.65
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"action": "send",
|
||||
"label": "selector_zero_ok_refresh_before_001a",
|
||||
"frame": "00 00 00 80 00 DA",
|
||||
"listen": 0.40
|
||||
},
|
||||
{
|
||||
"action": "repeat",
|
||||
"count": 2,
|
||||
"steps": [
|
||||
{
|
||||
"action": "send",
|
||||
"label": "candidate_001a_high_bars_family",
|
||||
"frame": "00 00 1A 80 00 C0",
|
||||
"listen": 0.85
|
||||
},
|
||||
{
|
||||
"action": "send",
|
||||
"label": "candidate_001a_low_bars_family",
|
||||
"frame": "00 00 1A 00 00 40",
|
||||
"listen": 0.65
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"action": "listen",
|
||||
"seconds": 0.80
|
||||
}
|
||||
]
|
||||
}
|
||||
1276
scenarios/panel-atlas-rom-button-output-candidates-v1.json
Normal file
1276
scenarios/panel-atlas-rom-button-output-candidates-v1.json
Normal file
File diff suppressed because it is too large
Load Diff
110
scenarios/panel-atlas-standard-master-bit-sweep-v1.json
Normal file
110
scenarios/panel-atlas-standard-master-bit-sweep-v1.json
Normal file
@@ -0,0 +1,110 @@
|
||||
{
|
||||
"name": "panel-atlas-standard-master-bit-sweep-v1",
|
||||
"notes": [
|
||||
"Targeted bench sweep for the far-right STANDARD and MASTER lamps.",
|
||||
"Uses CONNECT OK seed frames, a known SLAVE positive control, then bit sweeps around adjacent selector words.",
|
||||
"Watch far-right stack top to bottom: tally, STANDARD, MASTER, SLAVE, CAM POWER, BARS."
|
||||
],
|
||||
"steps": [
|
||||
{
|
||||
"action": "power_cycle",
|
||||
"off_seconds": 1.5
|
||||
},
|
||||
{
|
||||
"action": "wait_ready",
|
||||
"heartbeats": 2,
|
||||
"timeout": 10.0,
|
||||
"require": true
|
||||
},
|
||||
{
|
||||
"action": "drain",
|
||||
"seconds": 0.8
|
||||
},
|
||||
{
|
||||
"action": "send",
|
||||
"label": "ok_seed_1",
|
||||
"frame": "00 00 00 80 00 DA",
|
||||
"listen": 0.6
|
||||
},
|
||||
{
|
||||
"action": "send",
|
||||
"label": "ok_seed_2",
|
||||
"frame": "00 00 00 80 00 DA",
|
||||
"listen": 0.6
|
||||
},
|
||||
{
|
||||
"action": "send",
|
||||
"label": "positive_control_0013_8000_slave_on",
|
||||
"frame": "00 00 13 80 00 C9",
|
||||
"listen": 0.8
|
||||
},
|
||||
{
|
||||
"action": "send",
|
||||
"label": "clear_0013_after_slave",
|
||||
"frame": "00 00 13 00 00 49",
|
||||
"listen": 0.5
|
||||
},
|
||||
{
|
||||
"action": "repeat",
|
||||
"count": 1,
|
||||
"steps": [
|
||||
{"action": "send", "label": "candidate_0013_4000", "frame": "00 00 13 40 00 09", "listen": 0.75},
|
||||
{"action": "send", "label": "clear_0013_after_4000", "frame": "00 00 13 00 00 49", "listen": 0.45},
|
||||
{"action": "send", "label": "candidate_0013_2000", "frame": "00 00 13 20 00 69", "listen": 0.75},
|
||||
{"action": "send", "label": "clear_0013_after_2000", "frame": "00 00 13 00 00 49", "listen": 0.45},
|
||||
{"action": "send", "label": "candidate_0013_1000", "frame": "00 00 13 10 00 59", "listen": 0.75},
|
||||
{"action": "send", "label": "clear_0013_after_1000", "frame": "00 00 13 00 00 49", "listen": 0.45},
|
||||
{"action": "send", "label": "candidate_0013_0800", "frame": "00 00 13 08 00 41", "listen": 0.75},
|
||||
{"action": "send", "label": "clear_0013_after_0800", "frame": "00 00 13 00 00 49", "listen": 0.45}
|
||||
]
|
||||
},
|
||||
{
|
||||
"action": "send",
|
||||
"label": "ok_refresh_before_0012",
|
||||
"frame": "00 00 00 80 00 DA",
|
||||
"listen": 0.6
|
||||
},
|
||||
{
|
||||
"action": "repeat",
|
||||
"count": 1,
|
||||
"steps": [
|
||||
{"action": "send", "label": "candidate_0012_4000", "frame": "00 00 12 40 00 08", "listen": 0.75},
|
||||
{"action": "send", "label": "clear_0012_after_4000", "frame": "00 00 12 00 00 48", "listen": 0.45},
|
||||
{"action": "send", "label": "candidate_0012_2000", "frame": "00 00 12 20 00 68", "listen": 0.75},
|
||||
{"action": "send", "label": "clear_0012_after_2000", "frame": "00 00 12 00 00 48", "listen": 0.45},
|
||||
{"action": "send", "label": "candidate_0012_1000", "frame": "00 00 12 10 00 58", "listen": 0.75},
|
||||
{"action": "send", "label": "clear_0012_after_1000", "frame": "00 00 12 00 00 48", "listen": 0.45},
|
||||
{"action": "send", "label": "candidate_0012_0800", "frame": "00 00 12 08 00 40", "listen": 0.75},
|
||||
{"action": "send", "label": "clear_0012_after_0800", "frame": "00 00 12 00 00 48", "listen": 0.45},
|
||||
{"action": "send", "label": "candidate_0012_8000", "frame": "00 00 12 80 00 C8", "listen": 0.75},
|
||||
{"action": "send", "label": "clear_0012_after_8000", "frame": "00 00 12 00 00 48", "listen": 0.45}
|
||||
]
|
||||
},
|
||||
{
|
||||
"action": "send",
|
||||
"label": "ok_refresh_before_0014",
|
||||
"frame": "00 00 00 80 00 DA",
|
||||
"listen": 0.6
|
||||
},
|
||||
{
|
||||
"action": "repeat",
|
||||
"count": 1,
|
||||
"steps": [
|
||||
{"action": "send", "label": "candidate_0014_4000", "frame": "00 00 14 40 00 0E", "listen": 0.75},
|
||||
{"action": "send", "label": "clear_0014_after_4000", "frame": "00 00 14 00 00 4E", "listen": 0.45},
|
||||
{"action": "send", "label": "candidate_0014_2000", "frame": "00 00 14 20 00 6E", "listen": 0.75},
|
||||
{"action": "send", "label": "clear_0014_after_2000", "frame": "00 00 14 00 00 4E", "listen": 0.45},
|
||||
{"action": "send", "label": "candidate_0014_1000", "frame": "00 00 14 10 00 5E", "listen": 0.75},
|
||||
{"action": "send", "label": "clear_0014_after_1000", "frame": "00 00 14 00 00 4E", "listen": 0.45},
|
||||
{"action": "send", "label": "candidate_0014_0800", "frame": "00 00 14 08 00 46", "listen": 0.75},
|
||||
{"action": "send", "label": "clear_0014_after_0800", "frame": "00 00 14 00 00 4E", "listen": 0.45},
|
||||
{"action": "send", "label": "candidate_0014_8000", "frame": "00 00 14 80 00 CE", "listen": 0.75},
|
||||
{"action": "send", "label": "clear_0014_after_8000", "frame": "00 00 14 00 00 4E", "listen": 0.45}
|
||||
]
|
||||
},
|
||||
{
|
||||
"action": "listen",
|
||||
"seconds": 1.0
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
{
|
||||
"name": "panel-atlas-standard-master-lower-neighbor-sweep-v3",
|
||||
"notes": [
|
||||
"Third STANDARD/MASTER hunt over lower neighbor selectors 0x0008-0x000F.",
|
||||
"This tests whether the far-right STANDARD/MASTER lamps live closer to the CAM POWER/CALL selector pocket than the SLAVE/BARS pocket.",
|
||||
"Watch far-right stack top to bottom: tally, STANDARD, MASTER, SLAVE, CAM POWER, BARS."
|
||||
],
|
||||
"steps": [
|
||||
{"action": "power_cycle", "off_seconds": 1.5},
|
||||
{"action": "wait_ready", "heartbeats": 2, "timeout": 10.0, "require": true},
|
||||
{"action": "drain", "seconds": 0.8},
|
||||
{"action": "send", "label": "ok_seed_1", "frame": "00 00 00 80 00 DA", "listen": 0.6},
|
||||
{"action": "send", "label": "ok_seed_2", "frame": "00 00 00 80 00 DA", "listen": 0.6},
|
||||
{"action": "send", "label": "positive_control_0013_8000_slave_on", "frame": "00 00 13 80 00 C9", "listen": 0.8},
|
||||
{"action": "send", "label": "clear_0013_after_slave", "frame": "00 00 13 00 00 49", "listen": 0.6},
|
||||
|
||||
{"action": "send", "label": "candidate_0008_8000", "frame": "00 00 08 80 00 D2", "listen": 0.6},
|
||||
{"action": "send", "label": "candidate_0008_4000", "frame": "00 00 08 40 00 12", "listen": 0.6},
|
||||
{"action": "send", "label": "candidate_0008_2000", "frame": "00 00 08 20 00 72", "listen": 0.6},
|
||||
{"action": "send", "label": "candidate_0008_1000", "frame": "00 00 08 10 00 42", "listen": 0.6},
|
||||
{"action": "send", "label": "clear_0008", "frame": "00 00 08 00 00 52", "listen": 0.45},
|
||||
|
||||
{"action": "send", "label": "candidate_0009_8000", "frame": "00 00 09 80 00 D3", "listen": 0.6},
|
||||
{"action": "send", "label": "candidate_0009_4000", "frame": "00 00 09 40 00 13", "listen": 0.6},
|
||||
{"action": "send", "label": "candidate_0009_2000", "frame": "00 00 09 20 00 73", "listen": 0.6},
|
||||
{"action": "send", "label": "candidate_0009_1000", "frame": "00 00 09 10 00 43", "listen": 0.6},
|
||||
{"action": "send", "label": "clear_0009", "frame": "00 00 09 00 00 53", "listen": 0.45},
|
||||
|
||||
{"action": "send", "label": "ok_refresh_before_000A", "frame": "00 00 00 80 00 DA", "listen": 0.6},
|
||||
{"action": "send", "label": "candidate_000A_8000", "frame": "00 00 0A 80 00 D0", "listen": 0.6},
|
||||
{"action": "send", "label": "candidate_000A_4000", "frame": "00 00 0A 40 00 10", "listen": 0.6},
|
||||
{"action": "send", "label": "candidate_000A_2000", "frame": "00 00 0A 20 00 70", "listen": 0.6},
|
||||
{"action": "send", "label": "candidate_000A_1000", "frame": "00 00 0A 10 00 40", "listen": 0.6},
|
||||
{"action": "send", "label": "clear_000A", "frame": "00 00 0A 00 00 50", "listen": 0.45},
|
||||
|
||||
{"action": "send", "label": "candidate_000B_8000", "frame": "00 00 0B 80 00 D1", "listen": 0.6},
|
||||
{"action": "send", "label": "candidate_000B_4000", "frame": "00 00 0B 40 00 11", "listen": 0.6},
|
||||
{"action": "send", "label": "candidate_000B_2000", "frame": "00 00 0B 20 00 71", "listen": 0.6},
|
||||
{"action": "send", "label": "candidate_000B_1000", "frame": "00 00 0B 10 00 41", "listen": 0.6},
|
||||
{"action": "send", "label": "clear_000B", "frame": "00 00 0B 00 00 51", "listen": 0.45},
|
||||
|
||||
{"action": "send", "label": "ok_refresh_before_000C", "frame": "00 00 00 80 00 DA", "listen": 0.6},
|
||||
{"action": "send", "label": "candidate_000C_8000", "frame": "00 00 0C 80 00 D6", "listen": 0.6},
|
||||
{"action": "send", "label": "candidate_000C_4000", "frame": "00 00 0C 40 00 16", "listen": 0.6},
|
||||
{"action": "send", "label": "candidate_000C_2000", "frame": "00 00 0C 20 00 76", "listen": 0.6},
|
||||
{"action": "send", "label": "candidate_000C_1000", "frame": "00 00 0C 10 00 46", "listen": 0.6},
|
||||
{"action": "send", "label": "clear_000C", "frame": "00 00 0C 00 00 56", "listen": 0.45},
|
||||
|
||||
{"action": "send", "label": "candidate_000D_8000", "frame": "00 00 0D 80 00 D7", "listen": 0.6},
|
||||
{"action": "send", "label": "candidate_000D_4000", "frame": "00 00 0D 40 00 17", "listen": 0.6},
|
||||
{"action": "send", "label": "candidate_000D_2000", "frame": "00 00 0D 20 00 77", "listen": 0.6},
|
||||
{"action": "send", "label": "candidate_000D_1000", "frame": "00 00 0D 10 00 47", "listen": 0.6},
|
||||
{"action": "send", "label": "clear_000D", "frame": "00 00 0D 00 00 57", "listen": 0.45},
|
||||
|
||||
{"action": "send", "label": "ok_refresh_before_000E", "frame": "00 00 00 80 00 DA", "listen": 0.6},
|
||||
{"action": "send", "label": "candidate_000E_8000", "frame": "00 00 0E 80 00 D4", "listen": 0.6},
|
||||
{"action": "send", "label": "candidate_000E_4000", "frame": "00 00 0E 40 00 14", "listen": 0.6},
|
||||
{"action": "send", "label": "candidate_000E_2000", "frame": "00 00 0E 20 00 74", "listen": 0.6},
|
||||
{"action": "send", "label": "candidate_000E_1000", "frame": "00 00 0E 10 00 44", "listen": 0.6},
|
||||
{"action": "send", "label": "clear_000E", "frame": "00 00 0E 00 00 54", "listen": 0.45},
|
||||
|
||||
{"action": "send", "label": "candidate_000F_8000", "frame": "00 00 0F 80 00 D5", "listen": 0.6},
|
||||
{"action": "send", "label": "candidate_000F_4000", "frame": "00 00 0F 40 00 15", "listen": 0.6},
|
||||
{"action": "send", "label": "candidate_000F_2000", "frame": "00 00 0F 20 00 75", "listen": 0.6},
|
||||
{"action": "send", "label": "candidate_000F_1000", "frame": "00 00 0F 10 00 45", "listen": 0.6},
|
||||
{"action": "send", "label": "clear_000F", "frame": "00 00 0F 00 00 55", "listen": 0.45},
|
||||
|
||||
{"action": "listen", "seconds": 1.0}
|
||||
]
|
||||
}
|
||||
70
scenarios/panel-atlas-standard-master-neighbor-sweep-v2.json
Normal file
70
scenarios/panel-atlas-standard-master-neighbor-sweep-v2.json
Normal file
@@ -0,0 +1,70 @@
|
||||
{
|
||||
"name": "panel-atlas-standard-master-neighbor-sweep-v2",
|
||||
"notes": [
|
||||
"Second targeted STANDARD/MASTER hunt after 0x0012/0x0013/0x0014 high-bit pass did not show them.",
|
||||
"Sweeps neighboring selector words with high-nibble values; these are the values most similar to confirmed lamp controls.",
|
||||
"Watch far-right stack top to bottom: tally, STANDARD, MASTER, SLAVE, CAM POWER, BARS."
|
||||
],
|
||||
"steps": [
|
||||
{"action": "power_cycle", "off_seconds": 1.5},
|
||||
{"action": "wait_ready", "heartbeats": 2, "timeout": 10.0, "require": true},
|
||||
{"action": "drain", "seconds": 0.8},
|
||||
{"action": "send", "label": "ok_seed_1", "frame": "00 00 00 80 00 DA", "listen": 0.6},
|
||||
{"action": "send", "label": "ok_seed_2", "frame": "00 00 00 80 00 DA", "listen": 0.6},
|
||||
{"action": "send", "label": "positive_control_0013_8000_slave_on", "frame": "00 00 13 80 00 C9", "listen": 0.8},
|
||||
{"action": "send", "label": "clear_0013_after_slave", "frame": "00 00 13 00 00 49", "listen": 0.6},
|
||||
|
||||
{"action": "send", "label": "candidate_0010_8000", "frame": "00 00 10 80 00 CA", "listen": 0.6},
|
||||
{"action": "send", "label": "candidate_0010_4000", "frame": "00 00 10 40 00 0A", "listen": 0.6},
|
||||
{"action": "send", "label": "candidate_0010_2000", "frame": "00 00 10 20 00 6A", "listen": 0.6},
|
||||
{"action": "send", "label": "candidate_0010_1000", "frame": "00 00 10 10 00 5A", "listen": 0.6},
|
||||
{"action": "send", "label": "clear_0010", "frame": "00 00 10 00 00 4A", "listen": 0.45},
|
||||
|
||||
{"action": "send", "label": "candidate_0011_8000", "frame": "00 00 11 80 00 CB", "listen": 0.6},
|
||||
{"action": "send", "label": "candidate_0011_4000", "frame": "00 00 11 40 00 0B", "listen": 0.6},
|
||||
{"action": "send", "label": "candidate_0011_2000", "frame": "00 00 11 20 00 6B", "listen": 0.6},
|
||||
{"action": "send", "label": "candidate_0011_1000", "frame": "00 00 11 10 00 5B", "listen": 0.6},
|
||||
{"action": "send", "label": "clear_0011", "frame": "00 00 11 00 00 4B", "listen": 0.45},
|
||||
|
||||
{"action": "send", "label": "ok_refresh_before_0015", "frame": "00 00 00 80 00 DA", "listen": 0.6},
|
||||
{"action": "send", "label": "candidate_0015_4000", "frame": "00 00 15 40 00 0F", "listen": 0.6},
|
||||
{"action": "send", "label": "candidate_0015_2000", "frame": "00 00 15 20 00 6F", "listen": 0.6},
|
||||
{"action": "send", "label": "candidate_0015_1000", "frame": "00 00 15 10 00 5F", "listen": 0.6},
|
||||
{"action": "send", "label": "candidate_0015_0800", "frame": "00 00 15 08 00 47", "listen": 0.6},
|
||||
{"action": "send", "label": "clear_0015", "frame": "00 00 15 00 00 4F", "listen": 0.45},
|
||||
|
||||
{"action": "send", "label": "candidate_0016_8000", "frame": "00 00 16 80 00 CC", "listen": 0.6},
|
||||
{"action": "send", "label": "candidate_0016_4000", "frame": "00 00 16 40 00 0C", "listen": 0.6},
|
||||
{"action": "send", "label": "candidate_0016_2000", "frame": "00 00 16 20 00 6C", "listen": 0.6},
|
||||
{"action": "send", "label": "candidate_0016_1000", "frame": "00 00 16 10 00 5C", "listen": 0.6},
|
||||
{"action": "send", "label": "clear_0016", "frame": "00 00 16 00 00 4C", "listen": 0.45},
|
||||
|
||||
{"action": "send", "label": "ok_refresh_before_0017", "frame": "00 00 00 80 00 DA", "listen": 0.6},
|
||||
{"action": "send", "label": "candidate_0017_4000", "frame": "00 00 17 40 00 0D", "listen": 0.6},
|
||||
{"action": "send", "label": "candidate_0017_2000", "frame": "00 00 17 20 00 6D", "listen": 0.6},
|
||||
{"action": "send", "label": "candidate_0017_1000", "frame": "00 00 17 10 00 5D", "listen": 0.6},
|
||||
{"action": "send", "label": "candidate_0017_0800", "frame": "00 00 17 08 00 45", "listen": 0.6},
|
||||
{"action": "send", "label": "clear_0017", "frame": "00 00 17 00 00 4D", "listen": 0.45},
|
||||
|
||||
{"action": "send", "label": "candidate_0018_8000", "frame": "00 00 18 80 00 C2", "listen": 0.6},
|
||||
{"action": "send", "label": "candidate_0018_4000", "frame": "00 00 18 40 00 02", "listen": 0.6},
|
||||
{"action": "send", "label": "candidate_0018_2000", "frame": "00 00 18 20 00 62", "listen": 0.6},
|
||||
{"action": "send", "label": "candidate_0018_1000", "frame": "00 00 18 10 00 52", "listen": 0.6},
|
||||
{"action": "send", "label": "clear_0018", "frame": "00 00 18 00 00 42", "listen": 0.45},
|
||||
|
||||
{"action": "send", "label": "ok_refresh_before_0019", "frame": "00 00 00 80 00 DA", "listen": 0.6},
|
||||
{"action": "send", "label": "candidate_0019_8000", "frame": "00 00 19 80 00 C3", "listen": 0.6},
|
||||
{"action": "send", "label": "candidate_0019_4000", "frame": "00 00 19 40 00 03", "listen": 0.6},
|
||||
{"action": "send", "label": "candidate_0019_2000", "frame": "00 00 19 20 00 63", "listen": 0.6},
|
||||
{"action": "send", "label": "candidate_0019_1000", "frame": "00 00 19 10 00 53", "listen": 0.6},
|
||||
{"action": "send", "label": "clear_0019", "frame": "00 00 19 00 00 43", "listen": 0.45},
|
||||
|
||||
{"action": "send", "label": "candidate_001A_8000", "frame": "00 00 1A 80 00 C0", "listen": 0.6},
|
||||
{"action": "send", "label": "candidate_001A_4000", "frame": "00 00 1A 40 00 00", "listen": 0.6},
|
||||
{"action": "send", "label": "candidate_001A_2000", "frame": "00 00 1A 20 00 60", "listen": 0.6},
|
||||
{"action": "send", "label": "candidate_001A_1000", "frame": "00 00 1A 10 00 50", "listen": 0.6},
|
||||
{"action": "send", "label": "clear_001A", "frame": "00 00 1A 00 00 40", "listen": 0.45},
|
||||
|
||||
{"action": "listen", "seconds": 1.0}
|
||||
]
|
||||
}
|
||||
226
scripts/build_panel_visual_sweep.py
Normal file
226
scripts/build_panel_visual_sweep.py
Normal file
@@ -0,0 +1,226 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Build a JSON serial_scenario for broad visible panel sweeps."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
from pathlib import Path
|
||||
from typing import Iterable
|
||||
|
||||
|
||||
CHECKSUM_SEED = 0x5A
|
||||
CONNECT_OK_FRAME = "00 00 00 80 00 DA"
|
||||
SLAVE_POSITIVE_CONTROL = "00 00 13 80 00 C9"
|
||||
SLAVE_CLEAR = "00 00 13 00 00 49"
|
||||
|
||||
|
||||
def main() -> int:
|
||||
args = build_arg_parser().parse_args()
|
||||
values = parse_int_list(args.values)
|
||||
skips = set(parse_int_list(args.skip)) if args.skip else set()
|
||||
selectors = [
|
||||
selector
|
||||
for selector in range(parse_int(args.start), parse_int(args.end) + 1)
|
||||
if selector not in skips and (args.include_selector_zero or selector != 0)
|
||||
]
|
||||
if not selectors:
|
||||
raise SystemExit("selector range is empty after skips")
|
||||
|
||||
scenario = build_scenario(args, selectors, values)
|
||||
args.output.parent.mkdir(parents=True, exist_ok=True)
|
||||
args.output.write_text(json.dumps(scenario, indent=2) + "\n", encoding="utf-8")
|
||||
|
||||
candidate_images = len(selectors) * len(values)
|
||||
windows = window_count(len(selectors), args.power_cycle_every)
|
||||
print(f"wrote {args.output}")
|
||||
print(f"selectors={len(selectors)} values={len(values)} candidate_snapshots={candidate_images}")
|
||||
print(f"power_cycle_windows={windows} estimated_candidate_time={candidate_images * args.listen / 60:.1f}min")
|
||||
return 0
|
||||
|
||||
|
||||
def build_arg_parser() -> argparse.ArgumentParser:
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Generate a serial_scenario JSON file for a broad panel-output webcam sweep."
|
||||
)
|
||||
parser.add_argument("output", type=Path, help="scenario JSON path to write")
|
||||
parser.add_argument("--start", default="0x0001", help="first logical selector, inclusive")
|
||||
parser.add_argument("--end", default="0x017F", help="last logical selector, inclusive")
|
||||
parser.add_argument(
|
||||
"--values",
|
||||
default="0x8000,0x4000,0x2000,0x1000,0x0800",
|
||||
help="comma-separated 16-bit values to try for each selector",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--skip",
|
||||
default="",
|
||||
help="comma-separated logical selectors to skip, e.g. 0x006C,0x006D",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--include-selector-zero",
|
||||
action="store_true",
|
||||
help="include selector 0 in the sweep; omitted by default because it controls CONNECT OK state",
|
||||
)
|
||||
parser.add_argument("--listen", type=float, default=0.65, help="seconds to listen after each candidate send")
|
||||
parser.add_argument("--clear-listen", type=float, default=0.15, help="seconds after selector clear writes")
|
||||
parser.add_argument("--ok-listen", type=float, default=0.30, help="seconds after CONNECT OK refresh writes")
|
||||
parser.add_argument(
|
||||
"--ok-every",
|
||||
type=int,
|
||||
default=8,
|
||||
help="refresh CONNECT OK every N selectors; use 0 to disable periodic refresh",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--power-cycle-every",
|
||||
type=int,
|
||||
default=32,
|
||||
help="power-cycle every N selectors to limit latch contamination; use 0 for one long session",
|
||||
)
|
||||
parser.add_argument("--off-seconds", type=float, default=1.5, help="relay power-off time per window")
|
||||
parser.add_argument("--ready-timeout", type=float, default=10.0, help="wait_ready timeout")
|
||||
parser.add_argument("--drain", type=float, default=0.5, help="seconds to drain after ready")
|
||||
parser.add_argument("--final-listen", type=float, default=1.0, help="seconds to listen at the end")
|
||||
return parser
|
||||
|
||||
|
||||
def build_scenario(args: argparse.Namespace, selectors: list[int], values: list[int]) -> dict[str, object]:
|
||||
start = selectors[0]
|
||||
end = selectors[-1]
|
||||
value_text = ",".join(f"0x{value:04X}" for value in values)
|
||||
scenario: dict[str, object] = {
|
||||
"name": f"panel-atlas-big-visual-sweep-{start:03X}-{end:03X}",
|
||||
"notes": [
|
||||
"Broad visible-output sweep generated by scripts/build_panel_visual_sweep.py.",
|
||||
"Candidate selector/value sends have webcam snapshots enabled; CONNECT OK refreshes and clears do not.",
|
||||
"Use image filenames candidate_XXXX_YYYY to refine any visible trigger into a smaller follow-up scenario.",
|
||||
f"Values: {value_text}",
|
||||
],
|
||||
"steps": [],
|
||||
}
|
||||
steps: list[dict[str, object]] = scenario["steps"] # type: ignore[assignment]
|
||||
|
||||
for window_index, window in enumerate(selector_windows(selectors, args.power_cycle_every), start=1):
|
||||
add_session_prelude(args, steps, window_index)
|
||||
if window_index == 1:
|
||||
steps.append(send_step("positive_control_0013_8000_slave_on", SLAVE_POSITIVE_CONTROL, args.listen))
|
||||
steps.append(send_step("clear_0013_after_slave", SLAVE_CLEAR, args.clear_listen, snapshot=False))
|
||||
|
||||
for selector_index, selector in enumerate(window, start=1):
|
||||
if args.ok_every > 0 and (selector_index - 1) % args.ok_every == 0:
|
||||
steps.append(
|
||||
send_step(
|
||||
f"ok_refresh_before_{selector:04X}",
|
||||
CONNECT_OK_FRAME,
|
||||
args.ok_listen,
|
||||
snapshot=False,
|
||||
)
|
||||
)
|
||||
for value in values:
|
||||
steps.append(
|
||||
send_step(
|
||||
f"candidate_{selector:04X}_{value:04X}",
|
||||
frame_hex(0x00, selector, value),
|
||||
args.listen,
|
||||
)
|
||||
)
|
||||
steps.append(
|
||||
send_step(
|
||||
f"clear_{selector:04X}",
|
||||
frame_hex(0x00, selector, 0),
|
||||
args.clear_listen,
|
||||
snapshot=False,
|
||||
)
|
||||
)
|
||||
|
||||
steps.append({"action": "listen", "seconds": args.final_listen})
|
||||
return scenario
|
||||
|
||||
|
||||
def add_session_prelude(args: argparse.Namespace, steps: list[dict[str, object]], window_index: int) -> None:
|
||||
steps.extend(
|
||||
[
|
||||
{"action": "power_cycle", "off_seconds": args.off_seconds},
|
||||
{
|
||||
"action": "wait_ready",
|
||||
"heartbeats": 2,
|
||||
"timeout": args.ready_timeout,
|
||||
"require": True,
|
||||
},
|
||||
{"action": "drain", "seconds": args.drain},
|
||||
send_step(f"window_{window_index:03d}_ok_seed_1", CONNECT_OK_FRAME, args.ok_listen, snapshot=False),
|
||||
send_step(f"window_{window_index:03d}_ok_seed_2", CONNECT_OK_FRAME, args.ok_listen, snapshot=False),
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
def send_step(label: str, frame: str, listen: float, *, snapshot: bool = True) -> dict[str, object]:
|
||||
step: dict[str, object] = {
|
||||
"action": "send",
|
||||
"label": label,
|
||||
"frame": frame,
|
||||
"listen": listen,
|
||||
}
|
||||
if not snapshot:
|
||||
step["snapshot"] = False
|
||||
return step
|
||||
|
||||
|
||||
def selector_windows(selectors: list[int], size: int) -> Iterable[list[int]]:
|
||||
if size <= 0:
|
||||
yield selectors
|
||||
return
|
||||
for index in range(0, len(selectors), size):
|
||||
yield selectors[index : index + size]
|
||||
|
||||
|
||||
def window_count(selector_count: int, size: int) -> int:
|
||||
if size <= 0:
|
||||
return 1
|
||||
return (selector_count + size - 1) // size
|
||||
|
||||
|
||||
def frame_hex(command: int, selector: int, value: int) -> str:
|
||||
selector_hi, selector_lo = selector_bytes(selector)
|
||||
data = bytes(
|
||||
[
|
||||
command & 0xFF,
|
||||
selector_hi,
|
||||
selector_lo,
|
||||
(value >> 8) & 0xFF,
|
||||
value & 0xFF,
|
||||
]
|
||||
)
|
||||
return " ".join(f"{byte:02X}" for byte in data + bytes([frame_checksum(data)]))
|
||||
|
||||
|
||||
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 frame_checksum(data: bytes) -> int:
|
||||
checksum = CHECKSUM_SEED
|
||||
for value in data[:5]:
|
||||
checksum ^= value
|
||||
return checksum & 0xFF
|
||||
|
||||
|
||||
def parse_int_list(text: str) -> list[int]:
|
||||
values = []
|
||||
for part in text.split(","):
|
||||
item = part.strip()
|
||||
if item:
|
||||
values.append(parse_int(item))
|
||||
return values
|
||||
|
||||
|
||||
def parse_int(text: str) -> int:
|
||||
return int(str(text).strip(), 0)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
167
scripts/build_rom_button_output_sweep.py
Normal file
167
scripts/build_rom_button_output_sweep.py
Normal file
@@ -0,0 +1,167 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Build a fresh-boot webcam sweep for ROM-derived panel-output candidates."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
CHECKSUM_SEED = 0x5A
|
||||
CONNECT_OK_FRAME = "00 00 00 80 00 DA"
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Candidate:
|
||||
label: str
|
||||
selector: int
|
||||
value: int
|
||||
note: str
|
||||
|
||||
|
||||
CANDIDATES: tuple[Candidate, ...] = (
|
||||
Candidate("positive_0013_4000_iris_mblack_link", 0x0013, 0x4000, "known IRIS/M.BLACK LINK lamp positive control"),
|
||||
Candidate("positive_0013_8000_slave", 0x0013, 0x8000, "known SLAVE lamp positive control"),
|
||||
Candidate("positive_0015_8000_call", 0x0015, 0x8000, "known CALL lamp positive control"),
|
||||
Candidate("positive_0017_8000_bars", 0x0017, 0x8000, "known BARS lamp positive control"),
|
||||
Candidate("positive_0110_8000_knee_auto", 0x0110, 0x8000, "known KNEE AUTO positive control"),
|
||||
Candidate("rom_001a_0808_multi_button_default", 0x001A, 0x0808, "F6D3 group default/fallback value"),
|
||||
Candidate("rom_001a_2020_f6d3_bit3_family", 0x001A, 0x2020, "F6D3.3-style packed state candidate"),
|
||||
Candidate("rom_001a_4040_f6d3_bit4_family", 0x001A, 0x4040, "F6D3.4-style packed state candidate"),
|
||||
Candidate("rom_001a_8080_f6d3_bit5_family", 0x001A, 0x8080, "F6D3.5-style packed state candidate"),
|
||||
Candidate("rom_006b_8000_f6d4_bit6_candidate", 0x006B, 0x8000, "F6D4.6 handler report value"),
|
||||
Candidate("rom_0083_0004_f6d0_step_candidate", 0x0083, 0x0004, "F6D0.1 lower-step value candidate"),
|
||||
Candidate("rom_0083_4000_high_tag_candidate", 0x0083, 0x4000, "0x0083 high-bit/tag candidate"),
|
||||
Candidate("rom_0083_2000_high_tag_candidate", 0x0083, 0x2000, "0x0083 high-bit/tag candidate"),
|
||||
Candidate("rom_008f_8000_f6d0_bit7_local", 0x008F, 0x8000, "F6D0.7 local SHUTTER/OTHERS report bit"),
|
||||
Candidate("rom_008f_2000_f6d0_bit6_local", 0x008F, 0x2000, "F6D0.6 local SHUTTER/OTHERS report bit"),
|
||||
Candidate("known_008f_0800_evs_display", 0x008F, 0x0800, "known EVS/shutter display positive control"),
|
||||
Candidate("known_008f_1000_off_display", 0x008F, 0x1000, "known OFF/shutter display positive control"),
|
||||
Candidate("rom_0093_1020_f6dc_bit5_context", 0x0093, 0x1020, "F6DC.5 handler context candidate"),
|
||||
Candidate("rom_0093_4040_f6dc_bit4_context", 0x0093, 0x4040, "F6DC.4 handler context candidate"),
|
||||
Candidate("rom_0093_8040_f6dc_bit3_context", 0x0093, 0x8040, "F6DC.3 handler context candidate"),
|
||||
Candidate("rom_0093_0020_f6dc_bit1_context", 0x0093, 0x0020, "F6DC.1 handler low-field candidate"),
|
||||
Candidate("rom_0093_0040_f6dc_bit0_context", 0x0093, 0x0040, "F6DC.0 handler low-field candidate"),
|
||||
Candidate("rom_009a_0800_iris_auto_candidate", 0x009A, 0x0800, "F6DB.3 IRIS AUTO report candidate"),
|
||||
Candidate("rom_00b7_2000_f6d4_bit0_bundle", 0x00B7, 0x2000, "F6D4.0 bundle selector candidate"),
|
||||
Candidate("rom_00b9_4000_f6dc_bit7_candidate", 0x00B9, 0x4000, "F6DC.7 handler value candidate"),
|
||||
Candidate("rom_00c4_8000_f6d4_bit0_bundle", 0x00C4, 0x8000, "F6D4.0 bundle selector candidate"),
|
||||
Candidate("rom_00c6_8000_f6d4_bit0_bundle", 0x00C6, 0x8000, "F6D4.0 bundle selector candidate"),
|
||||
Candidate("rom_00f8_8000_f6d4_bit1_candidate", 0x00F8, 0x8000, "F6D4.1 handler candidate"),
|
||||
)
|
||||
|
||||
|
||||
def main() -> int:
|
||||
args = build_arg_parser().parse_args()
|
||||
scenario = build_scenario(args)
|
||||
args.output.parent.mkdir(parents=True, exist_ok=True)
|
||||
args.output.write_text(json.dumps(scenario, indent=2) + "\n", encoding="utf-8")
|
||||
print(f"wrote {args.output}")
|
||||
print(f"candidates={len(CANDIDATES)} snapshots={len(CANDIDATES)}")
|
||||
print(f"estimated_hold_time={len(CANDIDATES) * args.listen / 60:.1f}min plus power-cycle/ready time")
|
||||
return 0
|
||||
|
||||
|
||||
def build_arg_parser() -> argparse.ArgumentParser:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument(
|
||||
"output",
|
||||
nargs="?",
|
||||
type=Path,
|
||||
default=Path("scenarios/panel-atlas-rom-button-output-candidates-v1.json"),
|
||||
)
|
||||
parser.add_argument("--listen", type=float, default=0.75, help="seconds to listen after each candidate send")
|
||||
parser.add_argument("--ok-listen", type=float, default=0.25, help="seconds to listen after CONNECT OK seeds")
|
||||
parser.add_argument("--drain", type=float, default=0.25, help="seconds to drain after ready")
|
||||
parser.add_argument("--off-seconds", type=float, default=1.5, help="relay power-off time before each candidate")
|
||||
parser.add_argument("--ready-heartbeats", type=int, default=2, help="heartbeats before each candidate")
|
||||
parser.add_argument("--ready-timeout", type=float, default=10.0, help="ready wait timeout")
|
||||
return parser
|
||||
|
||||
|
||||
def build_scenario(args: argparse.Namespace) -> dict[str, object]:
|
||||
steps: list[dict[str, object]] = []
|
||||
for index, candidate in enumerate(CANDIDATES, start=1):
|
||||
steps.extend(candidate_steps(args, index, candidate))
|
||||
steps.append({"action": "listen", "seconds": 0.8})
|
||||
return {
|
||||
"name": "panel-atlas-rom-button-output-candidates-v1",
|
||||
"notes": [
|
||||
"Fresh-boot webcam sweep for ROM-derived button/report output candidates.",
|
||||
"This deliberately skips physical RCP button presses: each candidate sends command 0 directly.",
|
||||
"Each candidate gets its own power-cycle/CONNECT-OK baseline to reduce latch contamination.",
|
||||
"Candidate snapshots only are enabled; setup, CONNECT OK seeds, and clears should not produce webcam images.",
|
||||
"Run with --camera-index 4 --snapshot-delays 0.5 on the current bench.",
|
||||
],
|
||||
"steps": steps,
|
||||
}
|
||||
|
||||
|
||||
def candidate_steps(args: argparse.Namespace, index: int, candidate: Candidate) -> list[dict[str, object]]:
|
||||
label_base = f"case{index:03d}_{candidate.label}"
|
||||
return [
|
||||
{"action": "power_cycle", "off_seconds": args.off_seconds},
|
||||
{
|
||||
"action": "wait_ready",
|
||||
"heartbeats": args.ready_heartbeats,
|
||||
"timeout": args.ready_timeout,
|
||||
"require": True,
|
||||
},
|
||||
{"action": "drain", "seconds": args.drain},
|
||||
send_step(f"{label_base}_ok_seed_1", CONNECT_OK_FRAME, args.ok_listen, snapshot=False),
|
||||
send_step(f"{label_base}_ok_seed_2", CONNECT_OK_FRAME, args.ok_listen, snapshot=False),
|
||||
{
|
||||
"action": "note",
|
||||
"message": (
|
||||
f"{label_base}: selector 0x{candidate.selector:04X}=0x{candidate.value:04X}; "
|
||||
f"{candidate.note}"
|
||||
),
|
||||
},
|
||||
send_step(label_base, frame_hex(0x00, candidate.selector, candidate.value), args.listen, snapshot=True),
|
||||
send_step(
|
||||
f"{label_base}_clear",
|
||||
frame_hex(0x00, candidate.selector, 0x0000),
|
||||
0.12,
|
||||
snapshot=False,
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
def send_step(label: str, frame: str, listen: float, *, snapshot: bool) -> dict[str, object]:
|
||||
step: dict[str, object] = {
|
||||
"action": "send",
|
||||
"label": label,
|
||||
"frame": frame,
|
||||
"listen": listen,
|
||||
}
|
||||
if not snapshot:
|
||||
step["snapshot"] = False
|
||||
return step
|
||||
|
||||
|
||||
def frame_hex(command: int, selector: int, value: int) -> str:
|
||||
selector_hi, selector_lo = selector_bytes(selector)
|
||||
data = bytes([command & 0xFF, selector_hi, selector_lo, (value >> 8) & 0xFF, value & 0xFF])
|
||||
return " ".join(f"{byte:02X}" for byte in data + bytes([frame_checksum(data)]))
|
||||
|
||||
|
||||
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 frame_checksum(data: bytes) -> int:
|
||||
value = CHECKSUM_SEED
|
||||
for byte in data[:5]:
|
||||
value ^= byte & 0xFF
|
||||
return value & 0xFF
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
135
scripts/make_panel_sweep_contact_sheets.py
Normal file
135
scripts/make_panel_sweep_contact_sheets.py
Normal file
@@ -0,0 +1,135 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Build labeled contact sheets from serial_scenario webcam snapshots."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import math
|
||||
from pathlib import Path
|
||||
|
||||
import cv2
|
||||
import numpy as np
|
||||
|
||||
|
||||
CROP_PRESETS = {
|
||||
"full": (0.00, 0.00, 1.00, 1.00),
|
||||
"panel": (0.03, 0.10, 0.98, 0.94),
|
||||
"right-stack": (0.70, 0.23, 0.92, 0.86),
|
||||
"lcd": (0.00, 0.22, 0.38, 0.78),
|
||||
}
|
||||
|
||||
|
||||
def main() -> int:
|
||||
args = build_arg_parser().parse_args()
|
||||
images = sorted(args.snapshot_dir.glob(args.glob))
|
||||
if args.only_candidates:
|
||||
images = [path for path in images if "candidate_" in path.name]
|
||||
if not images:
|
||||
raise SystemExit(f"no images found in {args.snapshot_dir}")
|
||||
|
||||
output_dir = args.output_dir or args.snapshot_dir.with_name(args.snapshot_dir.name + "-sheets")
|
||||
output_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
per_sheet = args.cols * args.rows
|
||||
total_sheets = math.ceil(len(images) / per_sheet)
|
||||
written: list[Path] = []
|
||||
for sheet_index in range(total_sheets):
|
||||
group = images[sheet_index * per_sheet : (sheet_index + 1) * per_sheet]
|
||||
sheet = build_sheet(group, args)
|
||||
out = output_dir / f"sheet-{sheet_index + 1:03d}.jpg"
|
||||
cv2.imwrite(str(out), sheet)
|
||||
written.append(out)
|
||||
|
||||
print(f"images={len(images)} sheets={len(written)} output_dir={output_dir}")
|
||||
if written:
|
||||
print(f"first={written[0]}")
|
||||
print(f"last={written[-1]}")
|
||||
return 0
|
||||
|
||||
|
||||
def build_arg_parser() -> argparse.ArgumentParser:
|
||||
parser = argparse.ArgumentParser(description="Create labeled contact sheets from webcam snapshots.")
|
||||
parser.add_argument("snapshot_dir", type=Path, help="directory containing serial_scenario snapshots")
|
||||
parser.add_argument("--output-dir", type=Path, help="directory for generated contact sheets")
|
||||
parser.add_argument("--glob", default="*.jpg", help="image glob within snapshot_dir")
|
||||
parser.add_argument("--cols", type=int, default=4, help="thumbnail columns per sheet")
|
||||
parser.add_argument("--rows", type=int, default=5, help="thumbnail rows per sheet")
|
||||
parser.add_argument("--thumb-width", type=int, default=360, help="thumbnail image width")
|
||||
parser.add_argument("--label-height", type=int, default=48, help="label area height per thumbnail")
|
||||
parser.add_argument(
|
||||
"--crop",
|
||||
choices=sorted(CROP_PRESETS),
|
||||
default="panel",
|
||||
help="crop preset before thumbnailing",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--only-candidates",
|
||||
action="store_true",
|
||||
help="include only filenames containing candidate_",
|
||||
)
|
||||
return parser
|
||||
|
||||
|
||||
def build_sheet(paths: list[Path], args: argparse.Namespace) -> np.ndarray:
|
||||
cells = [build_cell(path, args) for path in paths]
|
||||
blank = np.full_like(cells[0], 245)
|
||||
while len(cells) < args.cols * args.rows:
|
||||
cells.append(blank.copy())
|
||||
|
||||
rows = []
|
||||
for row_index in range(args.rows):
|
||||
row_cells = cells[row_index * args.cols : (row_index + 1) * args.cols]
|
||||
rows.append(np.hstack(row_cells))
|
||||
return np.vstack(rows)
|
||||
|
||||
|
||||
def build_cell(path: Path, args: argparse.Namespace) -> np.ndarray:
|
||||
image = cv2.imread(str(path))
|
||||
if image is None:
|
||||
image = np.full((240, 320, 3), 220, dtype=np.uint8)
|
||||
image = crop_image(image, CROP_PRESETS[args.crop])
|
||||
thumb_height = max(1, int(image.shape[0] * args.thumb_width / max(1, image.shape[1])))
|
||||
thumb = cv2.resize(image, (args.thumb_width, thumb_height), interpolation=cv2.INTER_AREA)
|
||||
|
||||
label = snapshot_label(path)
|
||||
cell = np.full((args.label_height + thumb_height, args.thumb_width, 3), 245, dtype=np.uint8)
|
||||
cell[args.label_height : args.label_height + thumb_height, 0 : args.thumb_width] = thumb
|
||||
draw_label(cell, label, args.label_height)
|
||||
return cell
|
||||
|
||||
|
||||
def crop_image(image: np.ndarray, crop: tuple[float, float, float, float]) -> np.ndarray:
|
||||
h, w = image.shape[:2]
|
||||
x1, y1, x2, y2 = crop
|
||||
left = min(w - 1, max(0, int(w * x1)))
|
||||
top = min(h - 1, max(0, int(h * y1)))
|
||||
right = min(w, max(left + 1, int(w * x2)))
|
||||
bottom = min(h, max(top + 1, int(h * y2)))
|
||||
return image[top:bottom, left:right]
|
||||
|
||||
|
||||
def draw_label(cell: np.ndarray, text: str, label_height: int) -> None:
|
||||
font = cv2.FONT_HERSHEY_SIMPLEX
|
||||
scale = 0.48
|
||||
thickness = 1
|
||||
max_chars = 45
|
||||
line1 = text[:max_chars]
|
||||
line2 = text[max_chars : max_chars * 2]
|
||||
cv2.putText(cell, line1, (6, 18), font, scale, (0, 0, 0), thickness, cv2.LINE_AA)
|
||||
if line2:
|
||||
cv2.putText(cell, line2, (6, min(label_height - 8, 38)), font, scale, (0, 0, 0), thickness, cv2.LINE_AA)
|
||||
|
||||
|
||||
def snapshot_label(path: Path) -> str:
|
||||
name = path.name
|
||||
if "_tx_0500ms_" in name:
|
||||
label = name.split("_tx_0500ms_", 1)[1]
|
||||
elif "_tx_" in name:
|
||||
label = name.split("_tx_", 1)[1]
|
||||
else:
|
||||
label = name
|
||||
return label.rsplit("_", 1)[0]
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -86,6 +86,14 @@ class BenchConnectLcdTest(unittest.TestCase):
|
||||
def test_label_frame_marks_copy_in_progress_selector_006d_candidate(self):
|
||||
self.assertEqual(label_frame(bytes.fromhex("00006D000037")), "copy_in_progress_selector_006d_candidate")
|
||||
|
||||
def test_label_frame_marks_iris_mblack_link_reports(self):
|
||||
self.assertEqual(label_frame(bytes.fromhex("000013400009")), "known_iris_mblack_link_active_report_candidate")
|
||||
self.assertEqual(label_frame(bytes.fromhex("02001300004B")), "queued_iris_mblack_link_clear_report_candidate")
|
||||
self.assertEqual(
|
||||
label_frame(bytes.fromhex("010013C00088")),
|
||||
"queued_selector_0013_bit15_plus_iris_mblack_link_report_candidate",
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
import unittest
|
||||
from collections import Counter
|
||||
|
||||
from ccu_emulator.controller import CcuConfig, CcuEmulator
|
||||
from ccu_emulator.frames import ACTIVE_SEED_COMMAND0, NEUTRAL_ACK_FRAME, build_frame, frame_checksum_ok
|
||||
from ccu_emulator.iris_mblack_link import IrisMblackLinkModule
|
||||
from ccu_emulator.policy import AckPolicy
|
||||
from ccu_emulator.refresh import PeriodicRefresh
|
||||
from ccu_emulator.serial_link import RxFrame
|
||||
|
||||
|
||||
class CcuEmulatorFrameTests(unittest.TestCase):
|
||||
@@ -29,5 +33,95 @@ class PeriodicRefreshTests(unittest.TestCase):
|
||||
self.assertEqual(refresh.due_frames(now=10.6), [])
|
||||
|
||||
|
||||
class IrisMblackLinkModuleTests(unittest.TestCase):
|
||||
def test_active_report_gets_selector_ack_and_mirror(self):
|
||||
module = IrisMblackLinkModule(mirror_delay=0.012)
|
||||
|
||||
decision = module.on_rx(bytes.fromhex("000013400009"), "known_iris_mblack_link_active_report_candidate")
|
||||
|
||||
self.assertIsNotNone(decision)
|
||||
assert decision is not None
|
||||
self.assertTrue(decision.suppress_default_ack)
|
||||
self.assertEqual(
|
||||
[tx.frame for tx in decision.tx],
|
||||
[bytes.fromhex("05001300004C"), bytes.fromhex("000013400009")],
|
||||
)
|
||||
self.assertEqual(decision.tx[1].delay, 0.012)
|
||||
self.assertIn("active", decision.reason)
|
||||
|
||||
def test_clear_report_gets_selector_ack_and_mirror(self):
|
||||
module = IrisMblackLinkModule()
|
||||
|
||||
decision = module.on_rx(bytes.fromhex("02001300004B"), "queued_iris_mblack_link_clear_report_candidate")
|
||||
|
||||
self.assertIsNotNone(decision)
|
||||
assert decision is not None
|
||||
self.assertEqual(
|
||||
[tx.frame for tx in decision.tx],
|
||||
[bytes.fromhex("05001300004C"), bytes.fromhex("000013000049")],
|
||||
)
|
||||
self.assertIn("clear", decision.reason)
|
||||
|
||||
def test_non_0013_report_is_ignored(self):
|
||||
module = IrisMblackLinkModule()
|
||||
|
||||
self.assertIsNone(module.on_rx(bytes.fromhex("0000158000CF"), "known_call_button_active_report"))
|
||||
|
||||
|
||||
class CcuEmulatorModuleIntegrationTests(unittest.TestCase):
|
||||
def test_module_response_suppresses_generic_neutral_ack(self):
|
||||
link = FakeLink(RxFrame(bytes.fromhex("000013400009"), "known_iris_mblack_link_active_report_candidate"))
|
||||
logger = FakeLogger()
|
||||
emulator = CcuEmulator(
|
||||
link,
|
||||
logger,
|
||||
config=CcuConfig(seed_frames=(), ready_heartbeats=0),
|
||||
modules=(IrisMblackLinkModule(mirror_delay=0.0),),
|
||||
)
|
||||
|
||||
emulator._service_rx()
|
||||
|
||||
self.assertEqual(
|
||||
[frame for frame, _label in link.sent],
|
||||
[bytes.fromhex("05001300004C"), bytes.fromhex("000013400009")],
|
||||
)
|
||||
self.assertEqual(emulator.stats.ack_frames, 0)
|
||||
self.assertEqual(emulator.stats.module_frames, 2)
|
||||
self.assertEqual(emulator.stats.tx_frames, 2)
|
||||
|
||||
|
||||
class FakeDetector:
|
||||
def __init__(self) -> None:
|
||||
self.labels = Counter()
|
||||
self.resync_events = 0
|
||||
self.dropped_bytes = 0
|
||||
|
||||
|
||||
class FakeLink:
|
||||
def __init__(self, *items: RxFrame) -> None:
|
||||
self.items = list(items)
|
||||
self.sent: list[tuple[bytes, str]] = []
|
||||
self.detector = FakeDetector()
|
||||
|
||||
def read_available(self) -> list[RxFrame]:
|
||||
if not self.items:
|
||||
return []
|
||||
return [self.items.pop(0)]
|
||||
|
||||
def send(self, frame: bytes, label: str) -> None:
|
||||
self.sent.append((frame, label))
|
||||
|
||||
|
||||
class FakeLogger:
|
||||
def __init__(self) -> None:
|
||||
self.lines: list[str] = []
|
||||
|
||||
def event(self, text: str) -> None:
|
||||
self.lines.append(text)
|
||||
|
||||
def emit(self, line: str = "") -> None:
|
||||
self.lines.append(line)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
69
tests/test_panel_selectors.py
Normal file
69
tests/test_panel_selectors.py
Normal file
@@ -0,0 +1,69 @@
|
||||
import unittest
|
||||
|
||||
from h8536.panel_selectors import (
|
||||
CURRENT_TABLE_BASE,
|
||||
describe_selector_value,
|
||||
known_panel_selector,
|
||||
panel_selector_semantics_payload,
|
||||
selector_word_address,
|
||||
)
|
||||
|
||||
|
||||
class PanelSelectorSemanticsTest(unittest.TestCase):
|
||||
def test_selector_0013_maps_to_current_table_word_and_lamp_bits(self):
|
||||
item = known_panel_selector(0x0013)
|
||||
|
||||
self.assertIsNotNone(item)
|
||||
assert item is not None
|
||||
self.assertEqual(item["current_word_address_hex"], "H'E826")
|
||||
self.assertEqual(selector_word_address(CURRENT_TABLE_BASE, 0x0013), 0xE826)
|
||||
|
||||
text = " ".join(describe_selector_value(0x0013, 0x4000))
|
||||
self.assertIn("IRIS/M.BLACK LINK", text)
|
||||
self.assertIn("F791.5", text)
|
||||
self.assertIn("F716.7", text)
|
||||
|
||||
def test_selector_payload_includes_local_panel_toggle_source(self):
|
||||
by_selector = {
|
||||
int(item["selector"]): item
|
||||
for item in panel_selector_semantics_payload()
|
||||
}
|
||||
|
||||
selector_0013 = by_selector[0x0013]
|
||||
trigger_text = " ".join(
|
||||
str(trigger.get("summary", ""))
|
||||
for trigger in selector_0013.get("local_triggers", [])
|
||||
)
|
||||
self.assertIn("F6DB.7", trigger_text)
|
||||
self.assertIn("H'E826", trigger_text)
|
||||
trigger_names = " ".join(
|
||||
str(trigger.get("name_candidate", ""))
|
||||
for trigger in selector_0013.get("local_triggers", [])
|
||||
)
|
||||
self.assertIn("provisional_iris_mblack_link_button_toggle_report", trigger_names)
|
||||
|
||||
def test_selector_payload_includes_closed_loop_state_machine(self):
|
||||
item = known_panel_selector(0x0013)
|
||||
|
||||
self.assertIsNotNone(item)
|
||||
assert item is not None
|
||||
state_machine = item["state_machine"]
|
||||
self.assertEqual(state_machine["name_candidate"], "iris_mblack_link_closed_loop_state_candidate")
|
||||
self.assertEqual(state_machine["ack_frame"], "05 00 13 00 00 4C")
|
||||
self.assertEqual(state_machine["active_mirror_frame"], "00 00 13 40 00 09")
|
||||
self.assertEqual(state_machine["clear_mirror_frame"], "00 00 13 00 00 49")
|
||||
|
||||
def test_rom_button_output_sweep_meanings_are_available(self):
|
||||
monitor_text = " ".join(describe_selector_value(0x001A, 0x4040))
|
||||
standard_text = " ".join(describe_selector_value(0x006B, 0x8000))
|
||||
shutter_text = " ".join(describe_selector_value(0x008F, 0x2000))
|
||||
white_balance_text = " ".join(describe_selector_value(0x0093, 0x8040))
|
||||
|
||||
self.assertIn("MONITOR G", monitor_text)
|
||||
self.assertIn("STANDARD", standard_text)
|
||||
self.assertIn("shutter 00.0", shutter_text)
|
||||
self.assertIn("white-balance PRESET", white_balance_text)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -494,6 +494,77 @@ class SerialPseudocodeTest(unittest.TestCase):
|
||||
self.assertIn("MEM8[0xF9C6u] = (u8)(MEM8[0xF9C6u] - 1u);", text)
|
||||
self.assertIn("candidate effect: table_write_candidate; target primary_value_table_candidate", text)
|
||||
|
||||
def test_panel_selector_semantics_are_emitted_as_comments_and_helper(self):
|
||||
analysis = {
|
||||
"protocol_semantics": [
|
||||
{
|
||||
"confidence": "medium",
|
||||
"confidence_score": 0.7,
|
||||
"commands": [],
|
||||
"panel_selector_semantics": [
|
||||
{
|
||||
"selector": 0x0013,
|
||||
"selector_hex": "0x0013",
|
||||
"name": "slave_and_iris_mblack_link_lamps",
|
||||
"summary": "Selector 0x0013 reads H'E826 and controls two lamp bits.",
|
||||
"current_word_address_hex": "H'E826",
|
||||
"dispatch_handler": "H'2E06",
|
||||
"state_machine": {
|
||||
"name_candidate": "iris_mblack_link_closed_loop_state_candidate",
|
||||
"summary": "RCP reports local intent, CCU ACKs selector 0x0013, then mirrors accepted state back.",
|
||||
"active_report_frame": "00 00 13 40 00 09",
|
||||
"clear_report_frame": "00 00 13 00 00 49",
|
||||
"ack_frame": "05 00 13 00 00 4C",
|
||||
"active_mirror_frame": "00 00 13 40 00 09",
|
||||
"clear_mirror_frame": "00 00 13 00 00 49",
|
||||
},
|
||||
"effects": [
|
||||
{
|
||||
"bit": 14,
|
||||
"mask": 0x4000,
|
||||
"mask_hex": "0x4000",
|
||||
"name": "IRIS/M.BLACK LINK lamp",
|
||||
"when_set": "sets F791.5 and F716.7",
|
||||
"when_clear": "clears F791.5 and F716.7",
|
||||
"ram_bits": ["F791.5", "F716.7"],
|
||||
},
|
||||
],
|
||||
"local_triggers": [
|
||||
{
|
||||
"source": "F006.7 / F6DB.7",
|
||||
"handler": "H'200E",
|
||||
"name_candidate": "provisional_iris_mblack_link_button_toggle_report",
|
||||
"summary": "H'200E toggles H'E826 bit14 and queues selector 0x0013.",
|
||||
"gate": "F731 <= 3",
|
||||
"current_state_bit": "F791.5",
|
||||
"active_value": 0x4000,
|
||||
"clear_value": 0x0000,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
with patch("h8536.serial_pseudocode.analyze_serial_semantics", return_value=analysis):
|
||||
text = generate_serial_pseudocode(candidate_payload())
|
||||
|
||||
self.assertIn("panel selector semantics:", text)
|
||||
self.assertIn("0x0013 slave_and_iris_mblack_link_lamps", text)
|
||||
self.assertIn("0x4000 -> IRIS/M.BLACK LINK lamp", text)
|
||||
self.assertIn("F791.5", text)
|
||||
self.assertIn("F716.7", text)
|
||||
self.assertIn("F006.7 / F6DB.7", text)
|
||||
self.assertIn("iris_mblack_link_closed_loop_state_candidate", text)
|
||||
self.assertIn("ACK 05 00 13 00 00 4C", text)
|
||||
self.assertIn("static void sci1_candidate_panel_selector_annotation", text)
|
||||
self.assertIn("case 0x0013u:", text)
|
||||
self.assertIn("void provisional_iris_mblack_link_button_toggle_report(void)", text)
|
||||
self.assertIn("Source F006.7 / F6DB.7; gate F731 <= 3; current state F791.5.", text)
|
||||
self.assertIn("Requests selector 0x0013=0x4000", text)
|
||||
self.assertIn("CCU should ACK 05 00 13 00 00 4C, then mirror 00 00 13 40 00 09.", text)
|
||||
|
||||
def test_timer_source_models_emit_separate_tick_isrs(self):
|
||||
analysis = {
|
||||
"protocol_semantics": [
|
||||
|
||||
@@ -84,6 +84,67 @@ class SerialScenarioTest(unittest.TestCase):
|
||||
self.assertIn("before_send=1", output)
|
||||
self.assertIn("delays=0,0.25,1", output)
|
||||
|
||||
def test_dry_run_summarizes_listen_ack_until_quiet(self):
|
||||
scenario = {
|
||||
"name": "unit-quiet-ack",
|
||||
"steps": [
|
||||
{
|
||||
"action": "listen_ack_until_quiet",
|
||||
"seconds": 12.0,
|
||||
"quiet_seconds": 0.9,
|
||||
"target_mode": "queued_reports",
|
||||
"ack_mode": "cmd5_selector",
|
||||
"max_acks": 16,
|
||||
}
|
||||
],
|
||||
}
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
path = Path(tmpdir) / "scenario.json"
|
||||
path.write_text(json.dumps(scenario), encoding="utf-8")
|
||||
stdout = io.StringIO()
|
||||
|
||||
exit_code = main([str(path), "--dry-run"], stdout=stdout)
|
||||
|
||||
output = stdout.getvalue()
|
||||
self.assertEqual(exit_code, 0)
|
||||
self.assertIn("step[1]=listen_ack_until_quiet", output)
|
||||
self.assertIn("seconds=12.000", output)
|
||||
self.assertIn("quiet=0.900s", output)
|
||||
self.assertIn("target_mode=queued_reports", output)
|
||||
self.assertIn("ack_mode=cmd5_selector", output)
|
||||
|
||||
def test_dry_run_summarizes_respond_on_rules(self):
|
||||
scenario = {
|
||||
"name": "unit-respond-on",
|
||||
"steps": [
|
||||
{
|
||||
"action": "listen_ack",
|
||||
"seconds": 2.0,
|
||||
"target_mode": "queued_reports",
|
||||
"ack_mode": "cmd5_selector",
|
||||
"respond_on": [
|
||||
{
|
||||
"frames": ["00 00 13 40 00 09"],
|
||||
"send": "00 00 13 40 00 09",
|
||||
"label": "mirror_selector_0013_active_from_button",
|
||||
}
|
||||
],
|
||||
}
|
||||
],
|
||||
}
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
path = Path(tmpdir) / "scenario.json"
|
||||
path.write_text(json.dumps(scenario), encoding="utf-8")
|
||||
stdout = io.StringIO()
|
||||
|
||||
exit_code = main([str(path), "--dry-run"], stdout=stdout)
|
||||
|
||||
output = stdout.getvalue()
|
||||
self.assertEqual(exit_code, 0)
|
||||
self.assertIn("respond_on=1", output)
|
||||
self.assertIn("send=00 00 13 40 00 09", output)
|
||||
self.assertIn("label=mirror_selector_0013_active_from_button", output)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
20
tests/test_serial_scenario_unexpected.py
Normal file
20
tests/test_serial_scenario_unexpected.py
Normal file
@@ -0,0 +1,20 @@
|
||||
import unittest
|
||||
|
||||
from h8536.serial_scenario_unexpected import parse_detected_frames
|
||||
|
||||
|
||||
class SerialScenarioUnexpectedTest(unittest.TestCase):
|
||||
def test_parse_detected_frames_relabels_iris_mblack_link_report(self):
|
||||
frames = parse_detected_frames(
|
||||
[
|
||||
"12:00:00.000 DETECT checksum_ok_unlabeled 00 00 13 40 00 09",
|
||||
"12:00:00.100 DETECT checksum_ok_unlabeled 02 00 13 00 00 4B",
|
||||
]
|
||||
)
|
||||
|
||||
self.assertEqual(frames[0].label, "known_iris_mblack_link_active_report_candidate")
|
||||
self.assertEqual(frames[1].label, "queued_iris_mblack_link_clear_report_candidate")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -405,6 +405,19 @@ class SerialSemanticsTest(unittest.TestCase):
|
||||
self.assertIn("faa2 == 0", gate_text)
|
||||
self.assertIn("not rom constants", gate_text)
|
||||
|
||||
def test_panel_selector_semantics_include_iris_mblack_link_trace(self):
|
||||
semantics = only_semantics(self, planned_semantics_payload())
|
||||
|
||||
panel = semantics["panel_selector_semantics"]
|
||||
panel_text = semantic_text(panel)
|
||||
|
||||
self.assertIn("0x0013", panel_text)
|
||||
self.assertIn("iris/m.black link", panel_text)
|
||||
self.assertIn("h'e826", panel_text)
|
||||
self.assertIn("f791.5", panel_text)
|
||||
self.assertIn("f716.7", panel_text)
|
||||
self.assertIn("f6db.7", panel_text)
|
||||
|
||||
def test_actual_dispatch_split_marks_initial_and_continuation_commands(self):
|
||||
semantics = only_semantics(
|
||||
self,
|
||||
|
||||
@@ -130,9 +130,9 @@ class TableXrefsTest(unittest.TestCase):
|
||||
|
||||
self.assertIn("Table/Index Cross-Reference Report for sample.json", text)
|
||||
self.assertIn("primary_value_table_candidate H'E000", text)
|
||||
self.assertIn("offset H'0006 -> H'E006", text)
|
||||
self.assertIn("offset H'0124 -> H'E124", text)
|
||||
self.assertIn("offset H'0002 at H'F922", text)
|
||||
self.assertIn("offset H'0006 selector 0x003 -> H'E006", text)
|
||||
self.assertIn("offset H'0124 selector 0x092 -> H'E124", text)
|
||||
self.assertIn("offset H'0002 selector 0x001 at H'F922", text)
|
||||
self.assertIn("index dynamic via R4", text)
|
||||
self.assertIn("term 'CONNECT': no LCD/text candidate hits", text)
|
||||
self.assertIn("term 'COMM LINK': 1 candidate", text)
|
||||
|
||||
Reference in New Issue
Block a user