diff --git a/build/rom_decompiled.json b/build/rom_decompiled.json index fb9c4c1..57e308b 100644 --- a/build/rom_decompiled.json +++ b/build/rom_decompiled.json @@ -186551,6 +186551,12 @@ "H'BC20", "H'BC22" ], + "availability": "initial_idle_dispatch", + "availability_conditions": [ + "valid checksum/no RX physical error", + "FAA2 == 0" + ], + "availability_summary": "valid checksum/no RX physical error && FAA2 == 0", "handler_start_index": 2147 }, { @@ -186570,6 +186576,13 @@ "H'BC24", "H'BC26" ], + "availability": "initial_idle_dispatch", + "availability_conditions": [ + "valid checksum/no RX physical error", + "FAA2 == 0", + "F861.bit7 == 0" + ], + "availability_summary": "valid checksum/no RX physical error && FAA2 == 0 && F861.bit7 == 0", "handler_start_index": 2179 }, { @@ -186589,6 +186602,12 @@ "H'BC29", "H'BC2B" ], + "availability": "initial_idle_dispatch", + "availability_conditions": [ + "valid checksum/no RX physical error", + "FAA2 == 0" + ], + "availability_summary": "valid checksum/no RX physical error && FAA2 == 0", "handler_start_index": 2191 }, { @@ -186608,6 +186627,12 @@ "H'BC2E", "H'BC30" ], + "availability": "initial_idle_dispatch", + "availability_conditions": [ + "valid checksum/no RX physical error", + "FAA2 == 0" + ], + "availability_summary": "valid checksum/no RX physical error && FAA2 == 0", "handler_start_index": 2275 }, { @@ -186627,6 +186652,12 @@ "H'BC45", "H'BC47" ], + "availability": "continuation_dispatch", + "availability_conditions": [ + "valid checksum/no RX physical error", + "FAA2 != 0" + ], + "availability_summary": "valid checksum/no RX physical error && FAA2 != 0", "handler_start_index": 2194 }, { @@ -186646,6 +186677,12 @@ "H'BC4A", "H'BC4C" ], + "availability": "continuation_dispatch", + "availability_conditions": [ + "valid checksum/no RX physical error", + "FAA2 != 0" + ], + "availability_summary": "valid checksum/no RX physical error && FAA2 != 0", "handler_start_index": 2231 }, { @@ -186665,6 +186702,12 @@ "H'BC4F", "H'BC51" ], + "availability": "continuation_dispatch", + "availability_conditions": [ + "valid checksum/no RX physical error", + "FAA2 != 0" + ], + "availability_summary": "valid checksum/no RX physical error && FAA2 != 0", "handler_start_index": 2263 }, { @@ -186684,9 +186727,105 @@ "H'BC54", "H'BC56" ], + "availability": "continuation_dispatch", + "availability_conditions": [ + "valid checksum/no RX physical error", + "FAA2 != 0" + ], + "availability_summary": "valid checksum/no RX physical error && FAA2 != 0", "handler_start_index": 2275 } ], + "state_split": { + "kind": "serial_command_dispatch_state_split", + "state_address": 64162, + "state_address_hex": "H'FAA2", + "test_address": 48143, + "test_address_hex": "H'BC0F", + "branch_address": 48147, + "branch_address_hex": "H'BC13", + "continuation_target": 48186, + "continuation_target_hex": "H'BC3A", + "initial_idle_commands": [ + 0, + 1, + 2, + 7 + ], + "initial_idle_commands_hex": [ + "H'00", + "H'01", + "H'02", + "H'07" + ], + "continuation_commands": [ + 4, + 5, + 6, + 7 + ], + "continuation_commands_hex": [ + "H'04", + "H'05", + "H'06", + "H'07" + ], + "summary": "FAA2 == 0 takes the initial/idle dispatcher path; FAA2 != 0 takes the continuation dispatcher path.", + "caveat": "Initial dispatch follows checksum validation and RX error handling. Command 1 is only on the initial/idle path and is also gated by F861.bit7 == 0.", + "evidence_addresses": [ + 48143, + 48147 + ], + "evidence_addresses_hex": [ + "H'BC0F", + "H'BC13" + ] + }, + "dispatcher_split": { + "kind": "serial_command_dispatch_state_split", + "state_address": 64162, + "state_address_hex": "H'FAA2", + "test_address": 48143, + "test_address_hex": "H'BC0F", + "branch_address": 48147, + "branch_address_hex": "H'BC13", + "continuation_target": 48186, + "continuation_target_hex": "H'BC3A", + "initial_idle_commands": [ + 0, + 1, + 2, + 7 + ], + "initial_idle_commands_hex": [ + "H'00", + "H'01", + "H'02", + "H'07" + ], + "continuation_commands": [ + 4, + 5, + 6, + 7 + ], + "continuation_commands_hex": [ + "H'04", + "H'05", + "H'06", + "H'07" + ], + "summary": "FAA2 == 0 takes the initial/idle dispatcher path; FAA2 != 0 takes the continuation dispatcher path.", + "caveat": "Initial dispatch follows checksum validation and RX error handling. Command 1 is only on the initial/idle path and is also gated by F861.bit7 == 0.", + "evidence_addresses": [ + 48143, + 48147 + ], + "evidence_addresses_hex": [ + "H'BC0F", + "H'BC13" + ] + }, "cases": [ { "value": 0, @@ -186694,7 +186833,12 @@ "target": 48233, "target_hex": "H'BC69", "compare_address": 48160, - "branch_address": 48162 + "branch_address": 48162, + "availability": "initial_idle_dispatch", + "availability_conditions": [ + "valid checksum/no RX physical error", + "FAA2 == 0" + ] }, { "value": 1, @@ -186702,7 +186846,13 @@ "target": 48343, "target_hex": "H'BCD7", "compare_address": 48164, - "branch_address": 48166 + "branch_address": 48166, + "availability": "initial_idle_dispatch", + "availability_conditions": [ + "valid checksum/no RX physical error", + "FAA2 == 0", + "F861.bit7 == 0" + ] }, { "value": 2, @@ -186710,7 +186860,12 @@ "target": 48388, "target_hex": "H'BD04", "compare_address": 48169, - "branch_address": 48171 + "branch_address": 48171, + "availability": "initial_idle_dispatch", + "availability_conditions": [ + "valid checksum/no RX physical error", + "FAA2 == 0" + ] }, { "value": 7, @@ -186718,7 +186873,12 @@ "target": 48645, "target_hex": "H'BE05", "compare_address": 48174, - "branch_address": 48176 + "branch_address": 48176, + "availability": "initial_idle_dispatch", + "availability_conditions": [ + "valid checksum/no RX physical error", + "FAA2 == 0" + ] }, { "value": 4, @@ -186726,7 +186886,12 @@ "target": 48398, "target_hex": "H'BD0E", "compare_address": 48197, - "branch_address": 48199 + "branch_address": 48199, + "availability": "continuation_dispatch", + "availability_conditions": [ + "valid checksum/no RX physical error", + "FAA2 != 0" + ] }, { "value": 5, @@ -186734,7 +186899,12 @@ "target": 48512, "target_hex": "H'BD80", "compare_address": 48202, - "branch_address": 48204 + "branch_address": 48204, + "availability": "continuation_dispatch", + "availability_conditions": [ + "valid checksum/no RX physical error", + "FAA2 != 0" + ] }, { "value": 6, @@ -186742,7 +186912,12 @@ "target": 48603, "target_hex": "H'BDDB", "compare_address": 48207, - "branch_address": 48209 + "branch_address": 48209, + "availability": "continuation_dispatch", + "availability_conditions": [ + "valid checksum/no RX physical error", + "FAA2 != 0" + ] }, { "value": 7, @@ -186750,7 +186925,12 @@ "target": 48645, "target_hex": "H'BE05", "compare_address": 48212, - "branch_address": 48214 + "branch_address": 48214, + "availability": "continuation_dispatch", + "availability_conditions": [ + "valid checksum/no RX physical error", + "FAA2 != 0" + ] } ], "evidence_addresses": [ @@ -186812,7 +186992,12 @@ "dispatch_compare_address": 48160, "dispatch_compare_address_hex": "H'BC20", "dispatch_branch_address": 48162, - "dispatch_branch_address_hex": "H'BC22" + "dispatch_branch_address_hex": "H'BC22", + "availability": "initial_idle_dispatch", + "availability_conditions": [ + "valid checksum/no RX physical error", + "FAA2 == 0" + ] } ], "evidence_addresses": [ @@ -186888,6 +187073,13 @@ "handler_start_hex": "H'BC69", "handler_end": 48340, "handler_end_hex": "H'BCD4", + "availability": "initial_idle_dispatch", + "availability_conditions": [ + "valid checksum/no RX physical error", + "FAA2 == 0" + ], + "availability_summary": "valid checksum/no RX physical error && FAA2 == 0", + "semantic_notes": [], "effects": [ { "kind": "table_write_candidate", @@ -186977,7 +187169,7 @@ "command_value": 1, "command_value_hex": "H'01", "name_candidate": "read_value", - "summary": "candidate read from the primary table, followed by a response carrying the value", + "summary": "initial/idle-path primary table read only, followed by an odd response staging sequence", "handler_alternatives": [ { "handler_start": 48343, @@ -186987,7 +187179,13 @@ "dispatch_compare_address": 48164, "dispatch_compare_address_hex": "H'BC24", "dispatch_branch_address": 48166, - "dispatch_branch_address_hex": "H'BC26" + "dispatch_branch_address_hex": "H'BC26", + "availability": "initial_idle_dispatch", + "availability_conditions": [ + "valid checksum/no RX physical error", + "FAA2 == 0", + "F861.bit7 == 0" + ] } ], "evidence_addresses": [ @@ -186995,18 +187193,11 @@ 48140, 48164, 48166, - 48304, - 48313, - 48321, - 48329, 48343, 48352, 48360, 48368, 48374, - 48309, - 48317, - 48325, 48348, 48356, 48378 @@ -187038,13 +187229,27 @@ "handler_start_hex": "H'BCD7", "handler_end": 48385, "handler_end_hex": "H'BD01", + "availability": "initial_idle_dispatch", + "availability_conditions": [ + "valid checksum/no RX physical error", + "FAA2 == 0", + "F861.bit7 == 0" + ], + "availability_summary": "valid checksum/no RX physical error && FAA2 == 0 && F861.bit7 == 0", + "semantic_notes": [ + "Only accepted on the initial/idle dispatcher path: valid checksum/no RX error, FAA2 == 0, and F861.bit7 == 0.", + "BCD7 stages F850=0x04, writes F851 from F861 and then overwrites F851 from F862.", + "BCD7 reads the primary table word at E000 + 2*selector; F854 receives the low byte and F853 receives the high byte.", + "F852 is not freshly written in the BCD7 handler, so do not describe the response as a fixed 04 00 QQ hi lo frame." + ], "effects": [ { "kind": "table_read_candidate", "target_candidate": "primary_value_table_candidate", - "destination_candidate": "response value bytes", + "destination_candidate": "response value bytes F854/F853, with F852 not freshly written by BCD7", "table_base": 57344, "table_base_hex": "H'E000", + "address_expression_candidate": "E000 + 2*selector", "evidence_addresses": [ 48364 ], @@ -187070,24 +187275,17 @@ ] } ], - "effect_summary": "Candidate read: reads the primary table and stages a value response.", + "effect_summary": "Initial/idle candidate read: reads the primary table and stages an odd value response with F852 possibly stale.", "evidence_addresses_hex": [ "H'BC08", "H'BC0C", "H'BC24", "H'BC26", - "H'BCB0", - "H'BCB9", - "H'BCC1", - "H'BCC9", "H'BCD7", "H'BCE0", "H'BCE8", "H'BCF0", "H'BCF6", - "H'BCB5", - "H'BCBD", - "H'BCC5", "H'BCDC", "H'BCE4", "H'BCFA" @@ -187108,7 +187306,12 @@ "dispatch_compare_address": 48169, "dispatch_compare_address_hex": "H'BC29", "dispatch_branch_address": 48171, - "dispatch_branch_address_hex": "H'BC2B" + "dispatch_branch_address_hex": "H'BC2B", + "availability": "initial_idle_dispatch", + "availability_conditions": [ + "valid checksum/no RX physical error", + "FAA2 == 0" + ] } ], "evidence_addresses": [ @@ -187125,6 +187328,13 @@ "handler_start_hex": "H'BD04", "handler_end": 48395, "handler_end_hex": "H'BD0B", + "availability": "initial_idle_dispatch", + "availability_conditions": [ + "valid checksum/no RX physical error", + "FAA2 == 0" + ], + "availability_summary": "valid checksum/no RX physical error && FAA2 == 0", + "semantic_notes": [], "effects": [ { "kind": "state_clear_candidate", @@ -187165,7 +187375,12 @@ "dispatch_compare_address": 48197, "dispatch_compare_address_hex": "H'BC45", "dispatch_branch_address": 48199, - "dispatch_branch_address_hex": "H'BC47" + "dispatch_branch_address_hex": "H'BC47", + "availability": "continuation_dispatch", + "availability_conditions": [ + "valid checksum/no RX physical error", + "FAA2 != 0" + ] } ], "evidence_addresses": [ @@ -187207,6 +187422,13 @@ "handler_start_hex": "H'BD0E", "handler_end": 48509, "handler_end_hex": "H'BD7D", + "availability": "continuation_dispatch", + "availability_conditions": [ + "valid checksum/no RX physical error", + "FAA2 != 0" + ], + "availability_summary": "valid checksum/no RX physical error && FAA2 != 0", + "semantic_notes": [], "effects": [ { "kind": "table_write_candidate", @@ -187256,7 +187478,7 @@ "command_value": 5, "command_value_hex": "H'05", "name_candidate": "ack_or_clear_pending", - "summary": "candidate pending/event acknowledgement path", + "summary": "continuation-only conditional acknowledgement/session clear path", "handler_alternatives": [ { "handler_start": 48512, @@ -187266,7 +187488,12 @@ "dispatch_compare_address": 48202, "dispatch_compare_address_hex": "H'BC4A", "dispatch_branch_address": 48204, - "dispatch_branch_address_hex": "H'BC4C" + "dispatch_branch_address_hex": "H'BC4C", + "availability": "continuation_dispatch", + "availability_conditions": [ + "valid checksum/no RX physical error", + "FAA2 != 0" + ] } ], "evidence_addresses": [ @@ -187283,11 +187510,28 @@ "handler_start_hex": "H'BD80", "handler_end": 48600, "handler_end_hex": "H'BDD8", + "availability": "continuation_dispatch", + "availability_conditions": [ + "valid checksum/no RX physical error", + "FAA2 != 0" + ], + "availability_summary": "valid checksum/no RX physical error && FAA2 != 0", + "semantic_notes": [ + "Only accepted on the continuation dispatcher path when FAA2 != 0.", + "For selector 0x0040, frame 05 00 40 00 00 1F performs no response staging.", + "The handler clears FAA3/FAA2; F9B5 advances only when FAA2.bit3 was set from a queued report.", + "If FAA2 == 0, command 5 falls through the initial dispatcher instead of doing acknowledgement work." + ], "effects": [ { - "kind": "pending_acknowledgement_candidate", + "kind": "conditional_ack_session_clear_candidate", "target_candidate": "selected event/pending state", - "operation_candidate": "clear selected pending flags and then clear serial session state", + "operation_candidate": "when FAA2 != 0, clear FAA3/FAA2; advance F9B5 only if FAA2.bit3 was set from queued-report state; selector 0x0040 stages no response", + "selector_without_response_hex": "H'0040", + "requires": [ + "FAA2 != 0" + ], + "fallthrough_when": "FAA2 == 0", "evidence_addresses": [ 48578, 48596, @@ -187306,7 +187550,7 @@ ] } ], - "effect_summary": "Candidate acknowledgement/clear: updates pending/event state without an observed immediate response.", + "effect_summary": "Continuation-only ACK/session clear: clears FAA3/FAA2 and only advances F9B5 when queued-report FAA2.bit3 was set; selector 0x0040 has no response.", "evidence_addresses_hex": [ "H'BC08", "H'BC0C", @@ -187329,7 +187573,12 @@ "dispatch_compare_address": 48207, "dispatch_compare_address_hex": "H'BC4F", "dispatch_branch_address": 48209, - "dispatch_branch_address_hex": "H'BC51" + "dispatch_branch_address_hex": "H'BC51", + "availability": "continuation_dispatch", + "availability_conditions": [ + "valid checksum/no RX physical error", + "FAA2 != 0" + ] } ], "evidence_addresses": [ @@ -187363,6 +187612,13 @@ "handler_start_hex": "H'BDDB", "handler_end": 48643, "handler_end_hex": "H'BE03", + "availability": "continuation_dispatch", + "availability_conditions": [ + "valid checksum/no RX physical error", + "FAA2 != 0" + ], + "availability_summary": "valid checksum/no RX physical error && FAA2 != 0", + "semantic_notes": [], "effects": [ { "kind": "table_write_candidate", @@ -187408,7 +187664,7 @@ "command_value": 7, "command_value_hex": "H'07", "name_candidate": "retransmit_or_error_reply", - "summary": "candidate retransmit/NAK-style path; error handling also builds command 0x07 responses", + "summary": "candidate retransmit path; retry/error handling also builds a command 0x07 RX-payload echo", "handler_alternatives": [ { "handler_start": 48645, @@ -187418,7 +187674,12 @@ "dispatch_compare_address": 48174, "dispatch_compare_address_hex": "H'BC2E", "dispatch_branch_address": 48176, - "dispatch_branch_address_hex": "H'BC30" + "dispatch_branch_address_hex": "H'BC30", + "availability": "initial_idle_dispatch", + "availability_conditions": [ + "valid checksum/no RX physical error", + "FAA2 == 0" + ] }, { "handler_start": 48645, @@ -187428,7 +187689,12 @@ "dispatch_compare_address": 48212, "dispatch_compare_address_hex": "H'BC54", "dispatch_branch_address": 48214, - "dispatch_branch_address_hex": "H'BC56" + "dispatch_branch_address_hex": "H'BC56", + "availability": "continuation_dispatch", + "availability_conditions": [ + "valid checksum/no RX physical error", + "FAA2 != 0" + ] } ], "evidence_addresses": [ @@ -187453,6 +187719,20 @@ "handler_start_hex": "H'BE05", "handler_end": 48677, "handler_end_hex": "H'BE25", + "availability": [ + "initial_idle_dispatch", + "continuation_dispatch" + ], + "availability_conditions": [ + "valid checksum/no RX physical error", + "FAA2 == 0", + "FAA2 != 0" + ], + "availability_summary": "valid checksum/no RX physical error && FAA2 == 0 OR valid checksum/no RX physical error && FAA2 != 0", + "semantic_notes": [ + "loc_BE4D is a retry/error echo path: F850=0x07 and F861-F864 are copied into F851-F854 before loc_BA26.", + "Observed frame 07 80 40 20 90 2D means RX bytes F861-F864 were 80 40 20 90; it is not a table value." + ], "effects": [ { "kind": "retransmit_candidate", @@ -187484,6 +187764,17 @@ "H'BE22" ] }, + { + "kind": "retry_error_echo_candidate", + "source_candidate": "RX payload bytes F861-F864", + "destination_candidate": "F850=0x07, F851-F854=F861-F864 before loc_BA26", + "observed_frame_caveat": "07 80 40 20 90 2D echoes RX payload 80 40 20 90; it is not a table value", + "response_candidates": [], + "evidence_addresses": [], + "confidence": "candidate-medium", + "caveat": "Effect is inferred from local data movement and remains a protocol candidate.", + "evidence_addresses_hex": [] + }, { "kind": "response_staging_candidate", "response_candidates": [ @@ -187500,7 +187791,7 @@ ] } ], - "effect_summary": "Candidate retransmit/error reply: reuses prior TX bytes or builds an explicit 0x07 retry/error response.", + "effect_summary": "Candidate retransmit/error reply: reuses prior TX bytes or copies RX payload bytes behind an explicit 0x07 retry/error echo.", "evidence_addresses_hex": [ "H'BC08", "H'BC0C", @@ -187522,6 +187813,13 @@ "command_value_hex": "H'00", "name_candidate": "set_value_acked", "summary": "Candidate acknowledged set: writes value bytes to primary/current tables, flags the index, and stages an echo-style response.", + "availability": "initial_idle_dispatch", + "availability_conditions": [ + "valid checksum/no RX physical error", + "FAA2 == 0" + ], + "availability_summary": "valid checksum/no RX physical error && FAA2 == 0", + "semantic_notes": [], "effects": [ { "kind": "table_write_candidate", @@ -187629,14 +187927,28 @@ "command_value": 1, "command_value_hex": "H'01", "name_candidate": "read_value", - "summary": "Candidate read: reads the primary table and stages a value response.", + "summary": "Initial/idle candidate read: reads the primary table and stages an odd value response with F852 possibly stale.", + "availability": "initial_idle_dispatch", + "availability_conditions": [ + "valid checksum/no RX physical error", + "FAA2 == 0", + "F861.bit7 == 0" + ], + "availability_summary": "valid checksum/no RX physical error && FAA2 == 0 && F861.bit7 == 0", + "semantic_notes": [ + "Only accepted on the initial/idle dispatcher path: valid checksum/no RX error, FAA2 == 0, and F861.bit7 == 0.", + "BCD7 stages F850=0x04, writes F851 from F861 and then overwrites F851 from F862.", + "BCD7 reads the primary table word at E000 + 2*selector; F854 receives the low byte and F853 receives the high byte.", + "F852 is not freshly written in the BCD7 handler, so do not describe the response as a fixed 04 00 QQ hi lo frame." + ], "effects": [ { "kind": "table_read_candidate", "target_candidate": "primary_value_table_candidate", - "destination_candidate": "response value bytes", + "destination_candidate": "response value bytes F854/F853, with F852 not freshly written by BCD7", "table_base": 57344, "table_base_hex": "H'E000", + "address_expression_candidate": "E000 + 2*selector", "evidence_addresses": [ 48364 ], @@ -187670,18 +187982,11 @@ 48140, 48164, 48166, - 48304, - 48313, - 48321, - 48329, 48343, 48352, 48360, 48368, 48374, - 48309, - 48317, - 48325, 48348, 48356, 48378 @@ -187691,18 +187996,11 @@ "H'BC0C", "H'BC24", "H'BC26", - "H'BCB0", - "H'BCB9", - "H'BCC1", - "H'BCC9", "H'BCD7", "H'BCE0", "H'BCE8", "H'BCF0", "H'BCF6", - "H'BCB5", - "H'BCBD", - "H'BCC5", "H'BCDC", "H'BCE4", "H'BCFA" @@ -187716,6 +188014,13 @@ "command_value_hex": "H'02", "name_candidate": "clear_or_abort", "summary": "Candidate clear/abort: clears serial session state without an observed immediate response.", + "availability": "initial_idle_dispatch", + "availability_conditions": [ + "valid checksum/no RX physical error", + "FAA2 == 0" + ], + "availability_summary": "valid checksum/no RX physical error && FAA2 == 0", + "semantic_notes": [], "effects": [ { "kind": "state_clear_candidate", @@ -187755,6 +188060,13 @@ "command_value_hex": "H'04", "name_candidate": "set_value_no_immediate_reply", "summary": "Candidate deferred set: writes value bytes and flags the index without an observed immediate response.", + "availability": "continuation_dispatch", + "availability_conditions": [ + "valid checksum/no RX physical error", + "FAA2 != 0" + ], + "availability_summary": "valid checksum/no RX physical error && FAA2 != 0", + "semantic_notes": [], "effects": [ { "kind": "table_write_candidate", @@ -187812,12 +188124,29 @@ "command_value": 5, "command_value_hex": "H'05", "name_candidate": "ack_or_clear_pending", - "summary": "Candidate acknowledgement/clear: updates pending/event state without an observed immediate response.", + "summary": "Continuation-only ACK/session clear: clears FAA3/FAA2 and only advances F9B5 when queued-report FAA2.bit3 was set; selector 0x0040 has no response.", + "availability": "continuation_dispatch", + "availability_conditions": [ + "valid checksum/no RX physical error", + "FAA2 != 0" + ], + "availability_summary": "valid checksum/no RX physical error && FAA2 != 0", + "semantic_notes": [ + "Only accepted on the continuation dispatcher path when FAA2 != 0.", + "For selector 0x0040, frame 05 00 40 00 00 1F performs no response staging.", + "The handler clears FAA3/FAA2; F9B5 advances only when FAA2.bit3 was set from a queued report.", + "If FAA2 == 0, command 5 falls through the initial dispatcher instead of doing acknowledgement work." + ], "effects": [ { - "kind": "pending_acknowledgement_candidate", + "kind": "conditional_ack_session_clear_candidate", "target_candidate": "selected event/pending state", - "operation_candidate": "clear selected pending flags and then clear serial session state", + "operation_candidate": "when FAA2 != 0, clear FAA3/FAA2; advance F9B5 only if FAA2.bit3 was set from queued-report state; selector 0x0040 stages no response", + "selector_without_response_hex": "H'0040", + "requires": [ + "FAA2 != 0" + ], + "fallthrough_when": "FAA2 == 0", "evidence_addresses": [ 48578, 48596, @@ -187858,6 +188187,13 @@ "command_value_hex": "H'06", "name_candidate": "set_secondary_value", "summary": "Candidate secondary set: writes value bytes to the secondary table and flags the index.", + "availability": "continuation_dispatch", + "availability_conditions": [ + "valid checksum/no RX physical error", + "FAA2 != 0" + ], + "availability_summary": "valid checksum/no RX physical error && FAA2 != 0", + "semantic_notes": [], "effects": [ { "kind": "table_write_candidate", @@ -187911,7 +188247,21 @@ "command_value": 7, "command_value_hex": "H'07", "name_candidate": "retransmit_or_error_reply", - "summary": "Candidate retransmit/error reply: reuses prior TX bytes or builds an explicit 0x07 retry/error response.", + "summary": "Candidate retransmit/error reply: reuses prior TX bytes or copies RX payload bytes behind an explicit 0x07 retry/error echo.", + "availability": [ + "initial_idle_dispatch", + "continuation_dispatch" + ], + "availability_conditions": [ + "valid checksum/no RX physical error", + "FAA2 == 0", + "FAA2 != 0" + ], + "availability_summary": "valid checksum/no RX physical error && FAA2 == 0 OR valid checksum/no RX physical error && FAA2 != 0", + "semantic_notes": [ + "loc_BE4D is a retry/error echo path: F850=0x07 and F861-F864 are copied into F851-F854 before loc_BA26.", + "Observed frame 07 80 40 20 90 2D means RX bytes F861-F864 were 80 40 20 90; it is not a table value." + ], "effects": [ { "kind": "retransmit_candidate", @@ -187943,6 +188293,17 @@ "H'BE22" ] }, + { + "kind": "retry_error_echo_candidate", + "source_candidate": "RX payload bytes F861-F864", + "destination_candidate": "F850=0x07, F851-F854=F861-F864 before loc_BA26", + "observed_frame_caveat": "07 80 40 20 90 2D echoes RX payload 80 40 20 90; it is not a table value", + "response_candidates": [], + "evidence_addresses": [], + "confidence": "candidate-medium", + "caveat": "Effect is inferred from local data movement and remains a protocol candidate.", + "evidence_addresses_hex": [] + }, { "kind": "response_staging_candidate", "response_candidates": [ @@ -190372,8 +190733,8 @@ "send_builder": "loc_BA26", "send_builder_address": 47654, "send_builder_address_hex": "H'BA26", - "window_start": 47870, - "window_start_hex": "H'BAFE", + "window_start": 47872, + "window_start_hex": "H'BB00", "writes": [ { "instruction_address": 47900, @@ -190694,6 +191055,7 @@ "caveat": "Per-byte source is inferred from the final observed write to this staging byte before loc_BA26." } ], + "semantic_notes": [], "evidence_addresses": [ 47900, 47915, @@ -190881,7 +191243,8 @@ "confidence": "candidate-medium", "caveat": "Per-byte source is inferred from the final observed write to this staging byte before loc_BA26." } - ] + ], + "semantic_notes": [] }, { "id": "response_at_BCCD", @@ -191231,6 +191594,7 @@ "caveat": "Per-byte source is inferred from the final observed write to this staging byte before loc_BA26." } ], + "semantic_notes": [], "evidence_addresses": [ 48304, 48313, @@ -191432,7 +191796,8 @@ "confidence": "candidate-medium", "caveat": "Per-byte source is inferred from the final observed write to this staging byte before loc_BA26." } - ] + ], + "semantic_notes": [] }, { "id": "response_at_BCFA", @@ -191442,101 +191807,9 @@ "send_builder": "loc_BA26", "send_builder_address": 47654, "send_builder_address_hex": "H'BA26", - "window_start": 48297, - "window_start_hex": "H'BCA9", + "window_start": 48343, + "window_start_hex": "H'BCD7", "writes": [ - { - "instruction_address": 48304, - "instruction_address_hex": "H'BCB0", - "addresses": [ - 63568 - ], - "addresses_hex": [ - "H'F850" - ], - "source_operand": "#H'04", - "source": { - "kind": "immediate", - "value": 4, - "value_hex": "H'04" - }, - "instruction": "MOV:G.B #H'04, @H'F850" - }, - { - "instruction_address": 48313, - "instruction_address_hex": "H'BCB9", - "addresses": [ - 63569 - ], - "addresses_hex": [ - "H'F851" - ], - "source_operand": "R0", - "source": { - "kind": "rx_frame_byte", - "rx_offset": 1, - "rx_offsets": [ - 1 - ], - "rx_address": 63585, - "rx_address_hex": "H'F861", - "evidence_address": 48309, - "evidence_address_hex": "H'BCB5", - "instruction": "MOV:G.B @H'F861, R0" - }, - "instruction": "MOV:G.B R0, @H'F851" - }, - { - "instruction_address": 48321, - "instruction_address_hex": "H'BCC1", - "addresses": [ - 63570, - 63571 - ], - "addresses_hex": [ - "H'F852", - "H'F853" - ], - "source_operand": "R0", - "source": { - "kind": "rx_frame_word", - "rx_offset": 2, - "rx_offsets": [ - 2, - 3 - ], - "rx_address": 63586, - "rx_address_hex": "H'F862", - "evidence_address": 48317, - "evidence_address_hex": "H'BCBD", - "instruction": "MOV:G.W @H'F862, R0" - }, - "instruction": "MOV:G.W R0, @H'F852" - }, - { - "instruction_address": 48329, - "instruction_address_hex": "H'BCC9", - "addresses": [ - 63572 - ], - "addresses_hex": [ - "H'F854" - ], - "source_operand": "R0", - "source": { - "kind": "rx_frame_byte", - "rx_offset": 4, - "rx_offsets": [ - 4 - ], - "rx_address": 63588, - "rx_address_hex": "H'F864", - "evidence_address": 48325, - "evidence_address_hex": "H'BCC5", - "instruction": "MOV:G.B @H'F864, R0" - }, - "instruction": "MOV:G.B R0, @H'F854" - }, { "instruction_address": 48343, "instruction_address_hex": "H'BCD7", @@ -191661,30 +191934,6 @@ } ], "rx_reads": [ - { - "instruction_address": 48309, - "instruction_address_hex": "H'BCB5", - "rx_offset": 1, - "rx_address": 63585, - "rx_address_hex": "H'F861", - "instruction": "MOV:G.B @H'F861, R0" - }, - { - "instruction_address": 48317, - "instruction_address_hex": "H'BCBD", - "rx_offset": 2, - "rx_address": 63586, - "rx_address_hex": "H'F862", - "instruction": "MOV:G.W @H'F862, R0" - }, - { - "instruction_address": 48325, - "instruction_address_hex": "H'BCC5", - "rx_offset": 4, - "rx_address": 63588, - "rx_address_hex": "H'F864", - "instruction": "MOV:G.B @H'F864, R0" - }, { "instruction_address": 48348, "instruction_address_hex": "H'BCDC", @@ -191703,35 +191952,21 @@ } ], "evidence_addresses": [ - 48304, - 48313, - 48321, - 48329, 48343, 48352, 48360, 48368, 48374, - 48309, - 48317, - 48325, 48348, 48356, 48378 ], "evidence_addresses_hex": [ - "H'BCB0", - "H'BCB9", - "H'BCC1", - "H'BCC9", "H'BCD7", "H'BCE0", "H'BCE8", "H'BCF0", "H'BCF6", - "H'BCB5", - "H'BCBD", - "H'BCC5", "H'BCDC", "H'BCE4", "H'BCFA" @@ -191821,38 +192056,15 @@ "tx_staging_byte": "TX[2]", "address": 63570, "address_hex": "H'F852", - "source_kind": "rx_frame_byte", - "source_expression": "rx[2]", + "source_kind": "stale_or_unchanged", + "source_expression": "stale/unchanged", "source": { - "kind": "rx_frame_byte", - "rx_offset": 2, - "rx_address": 63586, - "rx_address_hex": "H'F862", - "derived_from": { - "kind": "rx_frame_word", - "rx_offset": 2, - "rx_offsets": [ - 2, - 3 - ], - "rx_address": 63586, - "rx_address_hex": "H'F862", - "evidence_address": 48317, - "evidence_address_hex": "H'BCBD", - "instruction": "MOV:G.W @H'F862, R0" - } + "kind": "unknown" }, - "write_instruction_address": 48321, - "write_instruction_address_hex": "H'BCC1", - "instruction": "MOV:G.W R0, @H'F852", - "evidence_addresses": [ - 48321 - ], - "evidence_addresses_hex": [ - "H'BCC1" - ], - "confidence": "candidate-medium", - "caveat": "Per-byte source is inferred from the final observed write to this staging byte before loc_BA26." + "evidence_addresses": [], + "evidence_addresses_hex": [], + "confidence": "candidate-low", + "caveat": "BCD7 does not freshly write F852 before loc_BA26." }, { "offset": 3, @@ -191932,17 +192144,20 @@ "caveat": "Per-byte source is inferred from the final observed write to this staging byte before loc_BA26." } ], + "semantic_notes": [ + "Command 1 BCD7 staging is odd: F850=0x04; F851 is written from F861 then overwritten by F862.", + "The primary table word is read from E000 + 2*selector; F854/F853 receive low/high value bytes.", + "F852 may be stale or unchanged in this handler; avoid a fixed 04 00 QQ hi lo response shape." + ], "evidence_addresses": [ 48343, 48360, - 48321, 48374, 48368 ], "evidence_addresses_hex": [ "H'BCD7", "H'BCE8", - "H'BCC1", "H'BCF6", "H'BCF0" ], @@ -192023,38 +192238,15 @@ "tx_staging_byte": "TX[2]", "address": 63570, "address_hex": "H'F852", - "source_kind": "rx_frame_byte", - "source_expression": "rx[2]", + "source_kind": "stale_or_unchanged", + "source_expression": "stale/unchanged", "source": { - "kind": "rx_frame_byte", - "rx_offset": 2, - "rx_address": 63586, - "rx_address_hex": "H'F862", - "derived_from": { - "kind": "rx_frame_word", - "rx_offset": 2, - "rx_offsets": [ - 2, - 3 - ], - "rx_address": 63586, - "rx_address_hex": "H'F862", - "evidence_address": 48317, - "evidence_address_hex": "H'BCBD", - "instruction": "MOV:G.W @H'F862, R0" - } + "kind": "unknown" }, - "write_instruction_address": 48321, - "write_instruction_address_hex": "H'BCC1", - "instruction": "MOV:G.W R0, @H'F852", - "evidence_addresses": [ - 48321 - ], - "evidence_addresses_hex": [ - "H'BCC1" - ], - "confidence": "candidate-medium", - "caveat": "Per-byte source is inferred from the final observed write to this staging byte before loc_BA26." + "evidence_addresses": [], + "evidence_addresses_hex": [], + "confidence": "candidate-low", + "caveat": "BCD7 does not freshly write F852 before loc_BA26." }, { "offset": 3, @@ -192133,6 +192325,11 @@ "confidence": "candidate-medium", "caveat": "Per-byte source is inferred from the final observed write to this staging byte before loc_BA26." } + ], + "semantic_notes": [ + "Command 1 BCD7 staging is odd: F850=0x04; F851 is written from F861 then overwritten by F862.", + "The primary table word is read from E000 + 2*selector; F854/F853 receive low/high value bytes.", + "F852 may be stale or unchanged in this handler; avoid a fixed 04 00 QQ hi lo response shape." ] }, { @@ -192143,8 +192340,8 @@ "send_builder": "loc_BA26", "send_builder_address": 47654, "send_builder_address_hex": "H'BA26", - "window_start": 48627, - "window_start_hex": "H'BDF3", + "window_start": 48645, + "window_start_hex": "H'BE05", "writes": [ { "instruction_address": 48649, @@ -192452,6 +192649,7 @@ "caveat": "Per-byte source is inferred from the final observed write to this staging byte before loc_BA26." } ], + "semantic_notes": [], "evidence_addresses": [ 48649, 48657, @@ -192666,7 +192864,8 @@ "confidence": "candidate-medium", "caveat": "Per-byte source is inferred from the final observed write to this staging byte before loc_BA26." } - ] + ], + "semantic_notes": [] }, { "id": "response_at_BE6A", @@ -192676,8 +192875,8 @@ "send_builder": "loc_BA26", "send_builder_address": 47654, "send_builder_address_hex": "H'BA26", - "window_start": 48702, - "window_start_hex": "H'BE3E", + "window_start": 48717, + "window_start_hex": "H'BE4D", "writes": [ { "instruction_address": 48717, @@ -193016,6 +193215,10 @@ "caveat": "Per-byte source is inferred from the final observed write to this staging byte before loc_BA26." } ], + "semantic_notes": [ + "loc_BE4D retry/error echo stages F850=0x07 and copies F861-F864 into F851-F854 before loc_BA26.", + "Observed 07 80 40 20 90 2D echoes RX payload bytes 80 40 20 90; it is not a table-derived value." + ], "evidence_addresses": [ 48717, 48726, @@ -193217,6 +193420,10 @@ "confidence": "candidate-medium", "caveat": "Per-byte source is inferred from the final observed write to this staging byte before loc_BA26." } + ], + "semantic_notes": [ + "loc_BE4D retry/error echo stages F850=0x07 and copies F861-F864 into F851-F854 before loc_BA26.", + "Observed 07 80 40 20 90 2D echoes RX payload bytes 80 40 20 90; it is not a table-derived value." ] } ], @@ -193401,6 +193608,7 @@ "caveat": "Per-byte source is inferred from the final observed write to this staging byte before loc_BA26." } ], + "semantic_notes": [], "evidence_addresses": [ 47900, 47915, @@ -193614,6 +193822,7 @@ "caveat": "Per-byte source is inferred from the final observed write to this staging byte before loc_BA26." } ], + "semantic_notes": [], "evidence_addresses": [ 48304, 48313, @@ -193712,38 +193921,15 @@ "tx_staging_byte": "TX[2]", "address": 63570, "address_hex": "H'F852", - "source_kind": "rx_frame_byte", - "source_expression": "rx[2]", + "source_kind": "stale_or_unchanged", + "source_expression": "stale/unchanged", "source": { - "kind": "rx_frame_byte", - "rx_offset": 2, - "rx_address": 63586, - "rx_address_hex": "H'F862", - "derived_from": { - "kind": "rx_frame_word", - "rx_offset": 2, - "rx_offsets": [ - 2, - 3 - ], - "rx_address": 63586, - "rx_address_hex": "H'F862", - "evidence_address": 48317, - "evidence_address_hex": "H'BCBD", - "instruction": "MOV:G.W @H'F862, R0" - } + "kind": "unknown" }, - "write_instruction_address": 48321, - "write_instruction_address_hex": "H'BCC1", - "instruction": "MOV:G.W R0, @H'F852", - "evidence_addresses": [ - 48321 - ], - "evidence_addresses_hex": [ - "H'BCC1" - ], - "confidence": "candidate-medium", - "caveat": "Per-byte source is inferred from the final observed write to this staging byte before loc_BA26." + "evidence_addresses": [], + "evidence_addresses_hex": [], + "confidence": "candidate-low", + "caveat": "BCD7 does not freshly write F852 before loc_BA26." }, { "offset": 3, @@ -193823,17 +194009,20 @@ "caveat": "Per-byte source is inferred from the final observed write to this staging byte before loc_BA26." } ], + "semantic_notes": [ + "Command 1 BCD7 staging is odd: F850=0x04; F851 is written from F861 then overwritten by F862.", + "The primary table word is read from E000 + 2*selector; F854/F853 receive low/high value bytes.", + "F852 may be stale or unchanged in this handler; avoid a fixed 04 00 QQ hi lo response shape." + ], "evidence_addresses": [ 48343, 48360, - 48321, 48374, 48368 ], "evidence_addresses_hex": [ "H'BCD7", "H'BCE8", - "H'BCC1", "H'BCF6", "H'BCF0" ], @@ -194051,6 +194240,7 @@ "caveat": "Per-byte source is inferred from the final observed write to this staging byte before loc_BA26." } ], + "semantic_notes": [], "evidence_addresses": [ 48649, 48657, @@ -194260,6 +194450,10 @@ "caveat": "Per-byte source is inferred from the final observed write to this staging byte before loc_BA26." } ], + "semantic_notes": [ + "loc_BE4D retry/error echo stages F850=0x07 and copies F861-F864 into F851-F854 before loc_BA26.", + "Observed 07 80 40 20 90 2D echoes RX payload bytes 80 40 20 90; it is not a table-derived value." + ], "evidence_addresses": [ 48717, 48726, @@ -194457,6 +194651,7 @@ "caveat": "Per-byte source is inferred from the final observed write to this staging byte before loc_BA26." } ], + "semantic_notes": [], "evidence_addresses": [ 47900, 47915, @@ -194670,6 +194865,7 @@ "caveat": "Per-byte source is inferred from the final observed write to this staging byte before loc_BA26." } ], + "semantic_notes": [], "evidence_addresses": [ 48304, 48313, @@ -194768,38 +194964,15 @@ "tx_staging_byte": "TX[2]", "address": 63570, "address_hex": "H'F852", - "source_kind": "rx_frame_byte", - "source_expression": "rx[2]", + "source_kind": "stale_or_unchanged", + "source_expression": "stale/unchanged", "source": { - "kind": "rx_frame_byte", - "rx_offset": 2, - "rx_address": 63586, - "rx_address_hex": "H'F862", - "derived_from": { - "kind": "rx_frame_word", - "rx_offset": 2, - "rx_offsets": [ - 2, - 3 - ], - "rx_address": 63586, - "rx_address_hex": "H'F862", - "evidence_address": 48317, - "evidence_address_hex": "H'BCBD", - "instruction": "MOV:G.W @H'F862, R0" - } + "kind": "unknown" }, - "write_instruction_address": 48321, - "write_instruction_address_hex": "H'BCC1", - "instruction": "MOV:G.W R0, @H'F852", - "evidence_addresses": [ - 48321 - ], - "evidence_addresses_hex": [ - "H'BCC1" - ], - "confidence": "candidate-medium", - "caveat": "Per-byte source is inferred from the final observed write to this staging byte before loc_BA26." + "evidence_addresses": [], + "evidence_addresses_hex": [], + "confidence": "candidate-low", + "caveat": "BCD7 does not freshly write F852 before loc_BA26." }, { "offset": 3, @@ -194879,17 +195052,20 @@ "caveat": "Per-byte source is inferred from the final observed write to this staging byte before loc_BA26." } ], + "semantic_notes": [ + "Command 1 BCD7 staging is odd: F850=0x04; F851 is written from F861 then overwritten by F862.", + "The primary table word is read from E000 + 2*selector; F854/F853 receive low/high value bytes.", + "F852 may be stale or unchanged in this handler; avoid a fixed 04 00 QQ hi lo response shape." + ], "evidence_addresses": [ 48343, 48360, - 48321, 48374, 48368 ], "evidence_addresses_hex": [ "H'BCD7", "H'BCE8", - "H'BCC1", "H'BCF6", "H'BCF0" ], @@ -195107,6 +195283,7 @@ "caveat": "Per-byte source is inferred from the final observed write to this staging byte before loc_BA26." } ], + "semantic_notes": [], "evidence_addresses": [ 48649, 48657, @@ -195316,6 +195493,10 @@ "caveat": "Per-byte source is inferred from the final observed write to this staging byte before loc_BA26." } ], + "semantic_notes": [ + "loc_BE4D retry/error echo stages F850=0x07 and copies F861-F864 into F851-F854 before loc_BA26.", + "Observed 07 80 40 20 90 2D echoes RX payload bytes 80 40 20 90; it is not a table-derived value." + ], "evidence_addresses": [ 48717, 48726, @@ -195609,6 +195790,7 @@ "instruction": "MOV:G.B R4, @H'F853" } ], + "semantic_notes": [], "evidence_addresses": [ 47900, 47904, @@ -195730,6 +195912,7 @@ "instruction": "MOV:G.B R0, @H'F854" } ], + "semantic_notes": [], "evidence_addresses": [ 48304, 48313, @@ -195764,96 +195947,6 @@ "call_address": 48378, "call_address_hex": "H'BCFA", "writes": [ - { - "address": 63568, - "address_hex": "H'F850", - "instruction_address": 48304, - "instruction_address_hex": "H'BCB0", - "source": { - "kind": "immediate", - "value": 4, - "value_hex": "H'04" - }, - "instruction": "MOV:G.B #H'04, @H'F850" - }, - { - "address": 63569, - "address_hex": "H'F851", - "instruction_address": 48313, - "instruction_address_hex": "H'BCB9", - "source": { - "kind": "rx_frame_byte", - "rx_offset": 1, - "rx_offsets": [ - 1 - ], - "rx_address": 63585, - "rx_address_hex": "H'F861", - "evidence_address": 48309, - "evidence_address_hex": "H'BCB5", - "instruction": "MOV:G.B @H'F861, R0" - }, - "instruction": "MOV:G.B R0, @H'F851" - }, - { - "address": 63570, - "address_hex": "H'F852", - "instruction_address": 48321, - "instruction_address_hex": "H'BCC1", - "source": { - "kind": "rx_frame_word", - "rx_offset": 2, - "rx_offsets": [ - 2, - 3 - ], - "rx_address": 63586, - "rx_address_hex": "H'F862", - "evidence_address": 48317, - "evidence_address_hex": "H'BCBD", - "instruction": "MOV:G.W @H'F862, R0" - }, - "instruction": "MOV:G.W R0, @H'F852" - }, - { - "address": 63571, - "address_hex": "H'F853", - "instruction_address": 48321, - "instruction_address_hex": "H'BCC1", - "source": { - "kind": "rx_frame_word", - "rx_offset": 2, - "rx_offsets": [ - 2, - 3 - ], - "rx_address": 63586, - "rx_address_hex": "H'F862", - "evidence_address": 48317, - "evidence_address_hex": "H'BCBD", - "instruction": "MOV:G.W @H'F862, R0" - }, - "instruction": "MOV:G.W R0, @H'F852" - }, - { - "address": 63572, - "address_hex": "H'F854", - "instruction_address": 48329, - "instruction_address_hex": "H'BCC9", - "source": { - "kind": "rx_frame_byte", - "rx_offset": 4, - "rx_offsets": [ - 4 - ], - "rx_address": 63588, - "rx_address_hex": "H'F864", - "evidence_address": 48325, - "evidence_address_hex": "H'BCC5", - "instruction": "MOV:G.B @H'F864, R0" - }, - "instruction": "MOV:G.B R0, @H'F854" - }, { "address": 63568, "address_hex": "H'F850", @@ -195952,36 +196045,27 @@ "instruction": "MOV:G.B R0, @H'F853" } ], + "semantic_notes": [ + "Command 1 BCD7 staging is odd: F850=0x04; F851 is written from F861 then overwritten by F862.", + "The primary table word is read from E000 + 2*selector; F854/F853 receive low/high value bytes.", + "F852 may be stale or unchanged in this handler; avoid a fixed 04 00 QQ hi lo response shape." + ], "evidence_addresses": [ - 48304, - 48313, - 48321, - 48329, 48343, 48352, 48360, 48368, 48374, - 48309, - 48317, - 48325, 48348, 48356, 48378 ], "evidence_addresses_hex": [ - "H'BCB0", - "H'BCB9", - "H'BCC1", - "H'BCC9", "H'BCD7", "H'BCE0", "H'BCE8", "H'BCF0", "H'BCF6", - "H'BCB5", - "H'BCBD", - "H'BCC5", "H'BCDC", "H'BCE4", "H'BCFA" @@ -196101,6 +196185,7 @@ "instruction": "MOV:G.W R0, @H'F854" } ], + "semantic_notes": [], "evidence_addresses": [ 48649, 48657, @@ -196218,6 +196303,10 @@ "instruction": "MOV:G.B R0, @H'F854" } ], + "semantic_notes": [ + "loc_BE4D retry/error echo stages F850=0x07 and copies F861-F864 into F851-F854 before loc_BA26.", + "Observed 07 80 40 20 90 2D echoes RX payload bytes 80 40 20 90; it is not a table-derived value." + ], "evidence_addresses": [ 48717, 48726, @@ -196304,7 +196393,14 @@ "response_candidates": [ "response_at_BE6A" ], - "summary": "Candidate retry path clears/consults serial flags, increments FAA6, compares it with 2, and when still below the apparent limit stages a command 0x07 response.", + "summary": "Candidate retry path clears/consults serial flags, increments FAA6, compares it with 2, and when still below the apparent limit enters loc_BE4D to stage a command 0x07 retry/error echo of RX payload bytes F861-F864.", + "echo_response_candidate": { + "entry_label": "loc_BE4D", + "entry_address": 48717, + "entry_address_hex": "H'BE4D", + "staging_candidate": "F850=0x07; F851-F854=F861-F864", + "observed_frame_caveat": "07 80 40 20 90 2D echoes RX payload 80 40 20 90 and is not a table value." + }, "evidence_addresses": [ 48681, 48685, @@ -196316,10 +196412,10 @@ 48726, 48734, 48742, - 48746, 48722, 48730, - 48738 + 48738, + 48746 ], "evidence_addresses_hex": [ "H'BE29", @@ -196332,10 +196428,10 @@ "H'BE56", "H'BE5E", "H'BE66", - "H'BE6A", "H'BE52", "H'BE5A", - "H'BE62" + "H'BE62", + "H'BE6A" ], "confidence": "candidate-medium" }, @@ -196645,21 +196741,20 @@ }, { "name": "host_ack_can_advance_queue", - "summary": "Commands 0x05/0x06 are modeled as acknowledgement paths that can clear pending state or advance F9B5.", + "summary": "Command 0x05 is a continuation-only ACK/session clear path: it clears FAA3/FAA2 and advances F9B5 only when queued-report FAA2.bit3 was set. Selector 0x0040 has no response; if FAA2 == 0 the command falls through instead of doing ACK work.", "command_values_hex": [ - "H'05", - "H'06" + "H'05" ], "state_addresses_hex": [ + "H'FAA2", + "H'FAA3", "H'F9B5" ], "evidence_addresses": [ 48136, 48140, 48202, - 48204, - 48207, - 48209 + 48204 ] } ], @@ -197798,6 +197893,12 @@ "H'BC20", "H'BC22" ], + "availability": "initial_idle_dispatch", + "availability_conditions": [ + "valid checksum/no RX physical error", + "FAA2 == 0" + ], + "availability_summary": "valid checksum/no RX physical error && FAA2 == 0", "handler_start_index": 2147 }, { @@ -197817,6 +197918,13 @@ "H'BC24", "H'BC26" ], + "availability": "initial_idle_dispatch", + "availability_conditions": [ + "valid checksum/no RX physical error", + "FAA2 == 0", + "F861.bit7 == 0" + ], + "availability_summary": "valid checksum/no RX physical error && FAA2 == 0 && F861.bit7 == 0", "handler_start_index": 2179 }, { @@ -197836,6 +197944,12 @@ "H'BC29", "H'BC2B" ], + "availability": "initial_idle_dispatch", + "availability_conditions": [ + "valid checksum/no RX physical error", + "FAA2 == 0" + ], + "availability_summary": "valid checksum/no RX physical error && FAA2 == 0", "handler_start_index": 2191 }, { @@ -197855,6 +197969,12 @@ "H'BC2E", "H'BC30" ], + "availability": "initial_idle_dispatch", + "availability_conditions": [ + "valid checksum/no RX physical error", + "FAA2 == 0" + ], + "availability_summary": "valid checksum/no RX physical error && FAA2 == 0", "handler_start_index": 2275 }, { @@ -197874,6 +197994,12 @@ "H'BC45", "H'BC47" ], + "availability": "continuation_dispatch", + "availability_conditions": [ + "valid checksum/no RX physical error", + "FAA2 != 0" + ], + "availability_summary": "valid checksum/no RX physical error && FAA2 != 0", "handler_start_index": 2194 }, { @@ -197893,6 +198019,12 @@ "H'BC4A", "H'BC4C" ], + "availability": "continuation_dispatch", + "availability_conditions": [ + "valid checksum/no RX physical error", + "FAA2 != 0" + ], + "availability_summary": "valid checksum/no RX physical error && FAA2 != 0", "handler_start_index": 2231 }, { @@ -197912,6 +198044,12 @@ "H'BC4F", "H'BC51" ], + "availability": "continuation_dispatch", + "availability_conditions": [ + "valid checksum/no RX physical error", + "FAA2 != 0" + ], + "availability_summary": "valid checksum/no RX physical error && FAA2 != 0", "handler_start_index": 2263 }, { @@ -197931,9 +198069,105 @@ "H'BC54", "H'BC56" ], + "availability": "continuation_dispatch", + "availability_conditions": [ + "valid checksum/no RX physical error", + "FAA2 != 0" + ], + "availability_summary": "valid checksum/no RX physical error && FAA2 != 0", "handler_start_index": 2275 } ], + "state_split": { + "kind": "serial_command_dispatch_state_split", + "state_address": 64162, + "state_address_hex": "H'FAA2", + "test_address": 48143, + "test_address_hex": "H'BC0F", + "branch_address": 48147, + "branch_address_hex": "H'BC13", + "continuation_target": 48186, + "continuation_target_hex": "H'BC3A", + "initial_idle_commands": [ + 0, + 1, + 2, + 7 + ], + "initial_idle_commands_hex": [ + "H'00", + "H'01", + "H'02", + "H'07" + ], + "continuation_commands": [ + 4, + 5, + 6, + 7 + ], + "continuation_commands_hex": [ + "H'04", + "H'05", + "H'06", + "H'07" + ], + "summary": "FAA2 == 0 takes the initial/idle dispatcher path; FAA2 != 0 takes the continuation dispatcher path.", + "caveat": "Initial dispatch follows checksum validation and RX error handling. Command 1 is only on the initial/idle path and is also gated by F861.bit7 == 0.", + "evidence_addresses": [ + 48143, + 48147 + ], + "evidence_addresses_hex": [ + "H'BC0F", + "H'BC13" + ] + }, + "dispatcher_split": { + "kind": "serial_command_dispatch_state_split", + "state_address": 64162, + "state_address_hex": "H'FAA2", + "test_address": 48143, + "test_address_hex": "H'BC0F", + "branch_address": 48147, + "branch_address_hex": "H'BC13", + "continuation_target": 48186, + "continuation_target_hex": "H'BC3A", + "initial_idle_commands": [ + 0, + 1, + 2, + 7 + ], + "initial_idle_commands_hex": [ + "H'00", + "H'01", + "H'02", + "H'07" + ], + "continuation_commands": [ + 4, + 5, + 6, + 7 + ], + "continuation_commands_hex": [ + "H'04", + "H'05", + "H'06", + "H'07" + ], + "summary": "FAA2 == 0 takes the initial/idle dispatcher path; FAA2 != 0 takes the continuation dispatcher path.", + "caveat": "Initial dispatch follows checksum validation and RX error handling. Command 1 is only on the initial/idle path and is also gated by F861.bit7 == 0.", + "evidence_addresses": [ + 48143, + 48147 + ], + "evidence_addresses_hex": [ + "H'BC0F", + "H'BC13" + ] + }, "cases": [ { "value": 0, @@ -197941,7 +198175,12 @@ "target": 48233, "target_hex": "H'BC69", "compare_address": 48160, - "branch_address": 48162 + "branch_address": 48162, + "availability": "initial_idle_dispatch", + "availability_conditions": [ + "valid checksum/no RX physical error", + "FAA2 == 0" + ] }, { "value": 1, @@ -197949,7 +198188,13 @@ "target": 48343, "target_hex": "H'BCD7", "compare_address": 48164, - "branch_address": 48166 + "branch_address": 48166, + "availability": "initial_idle_dispatch", + "availability_conditions": [ + "valid checksum/no RX physical error", + "FAA2 == 0", + "F861.bit7 == 0" + ] }, { "value": 2, @@ -197957,7 +198202,12 @@ "target": 48388, "target_hex": "H'BD04", "compare_address": 48169, - "branch_address": 48171 + "branch_address": 48171, + "availability": "initial_idle_dispatch", + "availability_conditions": [ + "valid checksum/no RX physical error", + "FAA2 == 0" + ] }, { "value": 7, @@ -197965,7 +198215,12 @@ "target": 48645, "target_hex": "H'BE05", "compare_address": 48174, - "branch_address": 48176 + "branch_address": 48176, + "availability": "initial_idle_dispatch", + "availability_conditions": [ + "valid checksum/no RX physical error", + "FAA2 == 0" + ] }, { "value": 4, @@ -197973,7 +198228,12 @@ "target": 48398, "target_hex": "H'BD0E", "compare_address": 48197, - "branch_address": 48199 + "branch_address": 48199, + "availability": "continuation_dispatch", + "availability_conditions": [ + "valid checksum/no RX physical error", + "FAA2 != 0" + ] }, { "value": 5, @@ -197981,7 +198241,12 @@ "target": 48512, "target_hex": "H'BD80", "compare_address": 48202, - "branch_address": 48204 + "branch_address": 48204, + "availability": "continuation_dispatch", + "availability_conditions": [ + "valid checksum/no RX physical error", + "FAA2 != 0" + ] }, { "value": 6, @@ -197989,7 +198254,12 @@ "target": 48603, "target_hex": "H'BDDB", "compare_address": 48207, - "branch_address": 48209 + "branch_address": 48209, + "availability": "continuation_dispatch", + "availability_conditions": [ + "valid checksum/no RX physical error", + "FAA2 != 0" + ] }, { "value": 7, @@ -197997,7 +198267,12 @@ "target": 48645, "target_hex": "H'BE05", "compare_address": 48212, - "branch_address": 48214 + "branch_address": 48214, + "availability": "continuation_dispatch", + "availability_conditions": [ + "valid checksum/no RX physical error", + "FAA2 != 0" + ] } ], "evidence_addresses": [ @@ -198059,7 +198334,12 @@ "dispatch_compare_address": 48160, "dispatch_compare_address_hex": "H'BC20", "dispatch_branch_address": 48162, - "dispatch_branch_address_hex": "H'BC22" + "dispatch_branch_address_hex": "H'BC22", + "availability": "initial_idle_dispatch", + "availability_conditions": [ + "valid checksum/no RX physical error", + "FAA2 == 0" + ] } ], "evidence_addresses": [ @@ -198135,6 +198415,13 @@ "handler_start_hex": "H'BC69", "handler_end": 48340, "handler_end_hex": "H'BCD4", + "availability": "initial_idle_dispatch", + "availability_conditions": [ + "valid checksum/no RX physical error", + "FAA2 == 0" + ], + "availability_summary": "valid checksum/no RX physical error && FAA2 == 0", + "semantic_notes": [], "effects": [ { "kind": "table_write_candidate", @@ -198224,7 +198511,7 @@ "command_value": 1, "command_value_hex": "H'01", "name_candidate": "read_value", - "summary": "candidate read from the primary table, followed by a response carrying the value", + "summary": "initial/idle-path primary table read only, followed by an odd response staging sequence", "handler_alternatives": [ { "handler_start": 48343, @@ -198234,7 +198521,13 @@ "dispatch_compare_address": 48164, "dispatch_compare_address_hex": "H'BC24", "dispatch_branch_address": 48166, - "dispatch_branch_address_hex": "H'BC26" + "dispatch_branch_address_hex": "H'BC26", + "availability": "initial_idle_dispatch", + "availability_conditions": [ + "valid checksum/no RX physical error", + "FAA2 == 0", + "F861.bit7 == 0" + ] } ], "evidence_addresses": [ @@ -198242,18 +198535,11 @@ 48140, 48164, 48166, - 48304, - 48313, - 48321, - 48329, 48343, 48352, 48360, 48368, 48374, - 48309, - 48317, - 48325, 48348, 48356, 48378 @@ -198285,13 +198571,27 @@ "handler_start_hex": "H'BCD7", "handler_end": 48385, "handler_end_hex": "H'BD01", + "availability": "initial_idle_dispatch", + "availability_conditions": [ + "valid checksum/no RX physical error", + "FAA2 == 0", + "F861.bit7 == 0" + ], + "availability_summary": "valid checksum/no RX physical error && FAA2 == 0 && F861.bit7 == 0", + "semantic_notes": [ + "Only accepted on the initial/idle dispatcher path: valid checksum/no RX error, FAA2 == 0, and F861.bit7 == 0.", + "BCD7 stages F850=0x04, writes F851 from F861 and then overwrites F851 from F862.", + "BCD7 reads the primary table word at E000 + 2*selector; F854 receives the low byte and F853 receives the high byte.", + "F852 is not freshly written in the BCD7 handler, so do not describe the response as a fixed 04 00 QQ hi lo frame." + ], "effects": [ { "kind": "table_read_candidate", "target_candidate": "primary_value_table_candidate", - "destination_candidate": "response value bytes", + "destination_candidate": "response value bytes F854/F853, with F852 not freshly written by BCD7", "table_base": 57344, "table_base_hex": "H'E000", + "address_expression_candidate": "E000 + 2*selector", "evidence_addresses": [ 48364 ], @@ -198317,24 +198617,17 @@ ] } ], - "effect_summary": "Candidate read: reads the primary table and stages a value response.", + "effect_summary": "Initial/idle candidate read: reads the primary table and stages an odd value response with F852 possibly stale.", "evidence_addresses_hex": [ "H'BC08", "H'BC0C", "H'BC24", "H'BC26", - "H'BCB0", - "H'BCB9", - "H'BCC1", - "H'BCC9", "H'BCD7", "H'BCE0", "H'BCE8", "H'BCF0", "H'BCF6", - "H'BCB5", - "H'BCBD", - "H'BCC5", "H'BCDC", "H'BCE4", "H'BCFA" @@ -198355,7 +198648,12 @@ "dispatch_compare_address": 48169, "dispatch_compare_address_hex": "H'BC29", "dispatch_branch_address": 48171, - "dispatch_branch_address_hex": "H'BC2B" + "dispatch_branch_address_hex": "H'BC2B", + "availability": "initial_idle_dispatch", + "availability_conditions": [ + "valid checksum/no RX physical error", + "FAA2 == 0" + ] } ], "evidence_addresses": [ @@ -198372,6 +198670,13 @@ "handler_start_hex": "H'BD04", "handler_end": 48395, "handler_end_hex": "H'BD0B", + "availability": "initial_idle_dispatch", + "availability_conditions": [ + "valid checksum/no RX physical error", + "FAA2 == 0" + ], + "availability_summary": "valid checksum/no RX physical error && FAA2 == 0", + "semantic_notes": [], "effects": [ { "kind": "state_clear_candidate", @@ -198412,7 +198717,12 @@ "dispatch_compare_address": 48197, "dispatch_compare_address_hex": "H'BC45", "dispatch_branch_address": 48199, - "dispatch_branch_address_hex": "H'BC47" + "dispatch_branch_address_hex": "H'BC47", + "availability": "continuation_dispatch", + "availability_conditions": [ + "valid checksum/no RX physical error", + "FAA2 != 0" + ] } ], "evidence_addresses": [ @@ -198454,6 +198764,13 @@ "handler_start_hex": "H'BD0E", "handler_end": 48509, "handler_end_hex": "H'BD7D", + "availability": "continuation_dispatch", + "availability_conditions": [ + "valid checksum/no RX physical error", + "FAA2 != 0" + ], + "availability_summary": "valid checksum/no RX physical error && FAA2 != 0", + "semantic_notes": [], "effects": [ { "kind": "table_write_candidate", @@ -198503,7 +198820,7 @@ "command_value": 5, "command_value_hex": "H'05", "name_candidate": "ack_or_clear_pending", - "summary": "candidate pending/event acknowledgement path", + "summary": "continuation-only conditional acknowledgement/session clear path", "handler_alternatives": [ { "handler_start": 48512, @@ -198513,7 +198830,12 @@ "dispatch_compare_address": 48202, "dispatch_compare_address_hex": "H'BC4A", "dispatch_branch_address": 48204, - "dispatch_branch_address_hex": "H'BC4C" + "dispatch_branch_address_hex": "H'BC4C", + "availability": "continuation_dispatch", + "availability_conditions": [ + "valid checksum/no RX physical error", + "FAA2 != 0" + ] } ], "evidence_addresses": [ @@ -198530,11 +198852,28 @@ "handler_start_hex": "H'BD80", "handler_end": 48600, "handler_end_hex": "H'BDD8", + "availability": "continuation_dispatch", + "availability_conditions": [ + "valid checksum/no RX physical error", + "FAA2 != 0" + ], + "availability_summary": "valid checksum/no RX physical error && FAA2 != 0", + "semantic_notes": [ + "Only accepted on the continuation dispatcher path when FAA2 != 0.", + "For selector 0x0040, frame 05 00 40 00 00 1F performs no response staging.", + "The handler clears FAA3/FAA2; F9B5 advances only when FAA2.bit3 was set from a queued report.", + "If FAA2 == 0, command 5 falls through the initial dispatcher instead of doing acknowledgement work." + ], "effects": [ { - "kind": "pending_acknowledgement_candidate", + "kind": "conditional_ack_session_clear_candidate", "target_candidate": "selected event/pending state", - "operation_candidate": "clear selected pending flags and then clear serial session state", + "operation_candidate": "when FAA2 != 0, clear FAA3/FAA2; advance F9B5 only if FAA2.bit3 was set from queued-report state; selector 0x0040 stages no response", + "selector_without_response_hex": "H'0040", + "requires": [ + "FAA2 != 0" + ], + "fallthrough_when": "FAA2 == 0", "evidence_addresses": [ 48578, 48596, @@ -198553,7 +198892,7 @@ ] } ], - "effect_summary": "Candidate acknowledgement/clear: updates pending/event state without an observed immediate response.", + "effect_summary": "Continuation-only ACK/session clear: clears FAA3/FAA2 and only advances F9B5 when queued-report FAA2.bit3 was set; selector 0x0040 has no response.", "evidence_addresses_hex": [ "H'BC08", "H'BC0C", @@ -198576,7 +198915,12 @@ "dispatch_compare_address": 48207, "dispatch_compare_address_hex": "H'BC4F", "dispatch_branch_address": 48209, - "dispatch_branch_address_hex": "H'BC51" + "dispatch_branch_address_hex": "H'BC51", + "availability": "continuation_dispatch", + "availability_conditions": [ + "valid checksum/no RX physical error", + "FAA2 != 0" + ] } ], "evidence_addresses": [ @@ -198610,6 +198954,13 @@ "handler_start_hex": "H'BDDB", "handler_end": 48643, "handler_end_hex": "H'BE03", + "availability": "continuation_dispatch", + "availability_conditions": [ + "valid checksum/no RX physical error", + "FAA2 != 0" + ], + "availability_summary": "valid checksum/no RX physical error && FAA2 != 0", + "semantic_notes": [], "effects": [ { "kind": "table_write_candidate", @@ -198655,7 +199006,7 @@ "command_value": 7, "command_value_hex": "H'07", "name_candidate": "retransmit_or_error_reply", - "summary": "candidate retransmit/NAK-style path; error handling also builds command 0x07 responses", + "summary": "candidate retransmit path; retry/error handling also builds a command 0x07 RX-payload echo", "handler_alternatives": [ { "handler_start": 48645, @@ -198665,7 +199016,12 @@ "dispatch_compare_address": 48174, "dispatch_compare_address_hex": "H'BC2E", "dispatch_branch_address": 48176, - "dispatch_branch_address_hex": "H'BC30" + "dispatch_branch_address_hex": "H'BC30", + "availability": "initial_idle_dispatch", + "availability_conditions": [ + "valid checksum/no RX physical error", + "FAA2 == 0" + ] }, { "handler_start": 48645, @@ -198675,7 +199031,12 @@ "dispatch_compare_address": 48212, "dispatch_compare_address_hex": "H'BC54", "dispatch_branch_address": 48214, - "dispatch_branch_address_hex": "H'BC56" + "dispatch_branch_address_hex": "H'BC56", + "availability": "continuation_dispatch", + "availability_conditions": [ + "valid checksum/no RX physical error", + "FAA2 != 0" + ] } ], "evidence_addresses": [ @@ -198700,6 +199061,20 @@ "handler_start_hex": "H'BE05", "handler_end": 48677, "handler_end_hex": "H'BE25", + "availability": [ + "initial_idle_dispatch", + "continuation_dispatch" + ], + "availability_conditions": [ + "valid checksum/no RX physical error", + "FAA2 == 0", + "FAA2 != 0" + ], + "availability_summary": "valid checksum/no RX physical error && FAA2 == 0 OR valid checksum/no RX physical error && FAA2 != 0", + "semantic_notes": [ + "loc_BE4D is a retry/error echo path: F850=0x07 and F861-F864 are copied into F851-F854 before loc_BA26.", + "Observed frame 07 80 40 20 90 2D means RX bytes F861-F864 were 80 40 20 90; it is not a table value." + ], "effects": [ { "kind": "retransmit_candidate", @@ -198731,6 +199106,17 @@ "H'BE22" ] }, + { + "kind": "retry_error_echo_candidate", + "source_candidate": "RX payload bytes F861-F864", + "destination_candidate": "F850=0x07, F851-F854=F861-F864 before loc_BA26", + "observed_frame_caveat": "07 80 40 20 90 2D echoes RX payload 80 40 20 90; it is not a table value", + "response_candidates": [], + "evidence_addresses": [], + "confidence": "candidate-medium", + "caveat": "Effect is inferred from local data movement and remains a protocol candidate.", + "evidence_addresses_hex": [] + }, { "kind": "response_staging_candidate", "response_candidates": [ @@ -198747,7 +199133,7 @@ ] } ], - "effect_summary": "Candidate retransmit/error reply: reuses prior TX bytes or builds an explicit 0x07 retry/error response.", + "effect_summary": "Candidate retransmit/error reply: reuses prior TX bytes or copies RX payload bytes behind an explicit 0x07 retry/error echo.", "evidence_addresses_hex": [ "H'BC08", "H'BC0C", @@ -198769,6 +199155,13 @@ "command_value_hex": "H'00", "name_candidate": "set_value_acked", "summary": "Candidate acknowledged set: writes value bytes to primary/current tables, flags the index, and stages an echo-style response.", + "availability": "initial_idle_dispatch", + "availability_conditions": [ + "valid checksum/no RX physical error", + "FAA2 == 0" + ], + "availability_summary": "valid checksum/no RX physical error && FAA2 == 0", + "semantic_notes": [], "effects": [ { "kind": "table_write_candidate", @@ -198876,14 +199269,28 @@ "command_value": 1, "command_value_hex": "H'01", "name_candidate": "read_value", - "summary": "Candidate read: reads the primary table and stages a value response.", + "summary": "Initial/idle candidate read: reads the primary table and stages an odd value response with F852 possibly stale.", + "availability": "initial_idle_dispatch", + "availability_conditions": [ + "valid checksum/no RX physical error", + "FAA2 == 0", + "F861.bit7 == 0" + ], + "availability_summary": "valid checksum/no RX physical error && FAA2 == 0 && F861.bit7 == 0", + "semantic_notes": [ + "Only accepted on the initial/idle dispatcher path: valid checksum/no RX error, FAA2 == 0, and F861.bit7 == 0.", + "BCD7 stages F850=0x04, writes F851 from F861 and then overwrites F851 from F862.", + "BCD7 reads the primary table word at E000 + 2*selector; F854 receives the low byte and F853 receives the high byte.", + "F852 is not freshly written in the BCD7 handler, so do not describe the response as a fixed 04 00 QQ hi lo frame." + ], "effects": [ { "kind": "table_read_candidate", "target_candidate": "primary_value_table_candidate", - "destination_candidate": "response value bytes", + "destination_candidate": "response value bytes F854/F853, with F852 not freshly written by BCD7", "table_base": 57344, "table_base_hex": "H'E000", + "address_expression_candidate": "E000 + 2*selector", "evidence_addresses": [ 48364 ], @@ -198917,18 +199324,11 @@ 48140, 48164, 48166, - 48304, - 48313, - 48321, - 48329, 48343, 48352, 48360, 48368, 48374, - 48309, - 48317, - 48325, 48348, 48356, 48378 @@ -198938,18 +199338,11 @@ "H'BC0C", "H'BC24", "H'BC26", - "H'BCB0", - "H'BCB9", - "H'BCC1", - "H'BCC9", "H'BCD7", "H'BCE0", "H'BCE8", "H'BCF0", "H'BCF6", - "H'BCB5", - "H'BCBD", - "H'BCC5", "H'BCDC", "H'BCE4", "H'BCFA" @@ -198963,6 +199356,13 @@ "command_value_hex": "H'02", "name_candidate": "clear_or_abort", "summary": "Candidate clear/abort: clears serial session state without an observed immediate response.", + "availability": "initial_idle_dispatch", + "availability_conditions": [ + "valid checksum/no RX physical error", + "FAA2 == 0" + ], + "availability_summary": "valid checksum/no RX physical error && FAA2 == 0", + "semantic_notes": [], "effects": [ { "kind": "state_clear_candidate", @@ -199002,6 +199402,13 @@ "command_value_hex": "H'04", "name_candidate": "set_value_no_immediate_reply", "summary": "Candidate deferred set: writes value bytes and flags the index without an observed immediate response.", + "availability": "continuation_dispatch", + "availability_conditions": [ + "valid checksum/no RX physical error", + "FAA2 != 0" + ], + "availability_summary": "valid checksum/no RX physical error && FAA2 != 0", + "semantic_notes": [], "effects": [ { "kind": "table_write_candidate", @@ -199059,12 +199466,29 @@ "command_value": 5, "command_value_hex": "H'05", "name_candidate": "ack_or_clear_pending", - "summary": "Candidate acknowledgement/clear: updates pending/event state without an observed immediate response.", + "summary": "Continuation-only ACK/session clear: clears FAA3/FAA2 and only advances F9B5 when queued-report FAA2.bit3 was set; selector 0x0040 has no response.", + "availability": "continuation_dispatch", + "availability_conditions": [ + "valid checksum/no RX physical error", + "FAA2 != 0" + ], + "availability_summary": "valid checksum/no RX physical error && FAA2 != 0", + "semantic_notes": [ + "Only accepted on the continuation dispatcher path when FAA2 != 0.", + "For selector 0x0040, frame 05 00 40 00 00 1F performs no response staging.", + "The handler clears FAA3/FAA2; F9B5 advances only when FAA2.bit3 was set from a queued report.", + "If FAA2 == 0, command 5 falls through the initial dispatcher instead of doing acknowledgement work." + ], "effects": [ { - "kind": "pending_acknowledgement_candidate", + "kind": "conditional_ack_session_clear_candidate", "target_candidate": "selected event/pending state", - "operation_candidate": "clear selected pending flags and then clear serial session state", + "operation_candidate": "when FAA2 != 0, clear FAA3/FAA2; advance F9B5 only if FAA2.bit3 was set from queued-report state; selector 0x0040 stages no response", + "selector_without_response_hex": "H'0040", + "requires": [ + "FAA2 != 0" + ], + "fallthrough_when": "FAA2 == 0", "evidence_addresses": [ 48578, 48596, @@ -199105,6 +199529,13 @@ "command_value_hex": "H'06", "name_candidate": "set_secondary_value", "summary": "Candidate secondary set: writes value bytes to the secondary table and flags the index.", + "availability": "continuation_dispatch", + "availability_conditions": [ + "valid checksum/no RX physical error", + "FAA2 != 0" + ], + "availability_summary": "valid checksum/no RX physical error && FAA2 != 0", + "semantic_notes": [], "effects": [ { "kind": "table_write_candidate", @@ -199158,7 +199589,21 @@ "command_value": 7, "command_value_hex": "H'07", "name_candidate": "retransmit_or_error_reply", - "summary": "Candidate retransmit/error reply: reuses prior TX bytes or builds an explicit 0x07 retry/error response.", + "summary": "Candidate retransmit/error reply: reuses prior TX bytes or copies RX payload bytes behind an explicit 0x07 retry/error echo.", + "availability": [ + "initial_idle_dispatch", + "continuation_dispatch" + ], + "availability_conditions": [ + "valid checksum/no RX physical error", + "FAA2 == 0", + "FAA2 != 0" + ], + "availability_summary": "valid checksum/no RX physical error && FAA2 == 0 OR valid checksum/no RX physical error && FAA2 != 0", + "semantic_notes": [ + "loc_BE4D is a retry/error echo path: F850=0x07 and F861-F864 are copied into F851-F854 before loc_BA26.", + "Observed frame 07 80 40 20 90 2D means RX bytes F861-F864 were 80 40 20 90; it is not a table value." + ], "effects": [ { "kind": "retransmit_candidate", @@ -199190,6 +199635,17 @@ "H'BE22" ] }, + { + "kind": "retry_error_echo_candidate", + "source_candidate": "RX payload bytes F861-F864", + "destination_candidate": "F850=0x07, F851-F854=F861-F864 before loc_BA26", + "observed_frame_caveat": "07 80 40 20 90 2D echoes RX payload 80 40 20 90; it is not a table value", + "response_candidates": [], + "evidence_addresses": [], + "confidence": "candidate-medium", + "caveat": "Effect is inferred from local data movement and remains a protocol candidate.", + "evidence_addresses_hex": [] + }, { "kind": "response_staging_candidate", "response_candidates": [ @@ -199246,8 +199702,8 @@ "send_builder": "loc_BA26", "send_builder_address": 47654, "send_builder_address_hex": "H'BA26", - "window_start": 47870, - "window_start_hex": "H'BAFE", + "window_start": 47872, + "window_start_hex": "H'BB00", "writes": [ { "instruction_address": 47900, @@ -199568,6 +200024,7 @@ "caveat": "Per-byte source is inferred from the final observed write to this staging byte before loc_BA26." } ], + "semantic_notes": [], "evidence_addresses": [ 47900, 47915, @@ -199755,7 +200212,8 @@ "confidence": "candidate-medium", "caveat": "Per-byte source is inferred from the final observed write to this staging byte before loc_BA26." } - ] + ], + "semantic_notes": [] }, { "id": "response_at_BCCD", @@ -200105,6 +200563,7 @@ "caveat": "Per-byte source is inferred from the final observed write to this staging byte before loc_BA26." } ], + "semantic_notes": [], "evidence_addresses": [ 48304, 48313, @@ -200306,7 +200765,8 @@ "confidence": "candidate-medium", "caveat": "Per-byte source is inferred from the final observed write to this staging byte before loc_BA26." } - ] + ], + "semantic_notes": [] }, { "id": "response_at_BCFA", @@ -200316,101 +200776,9 @@ "send_builder": "loc_BA26", "send_builder_address": 47654, "send_builder_address_hex": "H'BA26", - "window_start": 48297, - "window_start_hex": "H'BCA9", + "window_start": 48343, + "window_start_hex": "H'BCD7", "writes": [ - { - "instruction_address": 48304, - "instruction_address_hex": "H'BCB0", - "addresses": [ - 63568 - ], - "addresses_hex": [ - "H'F850" - ], - "source_operand": "#H'04", - "source": { - "kind": "immediate", - "value": 4, - "value_hex": "H'04" - }, - "instruction": "MOV:G.B #H'04, @H'F850" - }, - { - "instruction_address": 48313, - "instruction_address_hex": "H'BCB9", - "addresses": [ - 63569 - ], - "addresses_hex": [ - "H'F851" - ], - "source_operand": "R0", - "source": { - "kind": "rx_frame_byte", - "rx_offset": 1, - "rx_offsets": [ - 1 - ], - "rx_address": 63585, - "rx_address_hex": "H'F861", - "evidence_address": 48309, - "evidence_address_hex": "H'BCB5", - "instruction": "MOV:G.B @H'F861, R0" - }, - "instruction": "MOV:G.B R0, @H'F851" - }, - { - "instruction_address": 48321, - "instruction_address_hex": "H'BCC1", - "addresses": [ - 63570, - 63571 - ], - "addresses_hex": [ - "H'F852", - "H'F853" - ], - "source_operand": "R0", - "source": { - "kind": "rx_frame_word", - "rx_offset": 2, - "rx_offsets": [ - 2, - 3 - ], - "rx_address": 63586, - "rx_address_hex": "H'F862", - "evidence_address": 48317, - "evidence_address_hex": "H'BCBD", - "instruction": "MOV:G.W @H'F862, R0" - }, - "instruction": "MOV:G.W R0, @H'F852" - }, - { - "instruction_address": 48329, - "instruction_address_hex": "H'BCC9", - "addresses": [ - 63572 - ], - "addresses_hex": [ - "H'F854" - ], - "source_operand": "R0", - "source": { - "kind": "rx_frame_byte", - "rx_offset": 4, - "rx_offsets": [ - 4 - ], - "rx_address": 63588, - "rx_address_hex": "H'F864", - "evidence_address": 48325, - "evidence_address_hex": "H'BCC5", - "instruction": "MOV:G.B @H'F864, R0" - }, - "instruction": "MOV:G.B R0, @H'F854" - }, { "instruction_address": 48343, "instruction_address_hex": "H'BCD7", @@ -200535,30 +200903,6 @@ } ], "rx_reads": [ - { - "instruction_address": 48309, - "instruction_address_hex": "H'BCB5", - "rx_offset": 1, - "rx_address": 63585, - "rx_address_hex": "H'F861", - "instruction": "MOV:G.B @H'F861, R0" - }, - { - "instruction_address": 48317, - "instruction_address_hex": "H'BCBD", - "rx_offset": 2, - "rx_address": 63586, - "rx_address_hex": "H'F862", - "instruction": "MOV:G.W @H'F862, R0" - }, - { - "instruction_address": 48325, - "instruction_address_hex": "H'BCC5", - "rx_offset": 4, - "rx_address": 63588, - "rx_address_hex": "H'F864", - "instruction": "MOV:G.B @H'F864, R0" - }, { "instruction_address": 48348, "instruction_address_hex": "H'BCDC", @@ -200577,35 +200921,21 @@ } ], "evidence_addresses": [ - 48304, - 48313, - 48321, - 48329, 48343, 48352, 48360, 48368, 48374, - 48309, - 48317, - 48325, 48348, 48356, 48378 ], "evidence_addresses_hex": [ - "H'BCB0", - "H'BCB9", - "H'BCC1", - "H'BCC9", "H'BCD7", "H'BCE0", "H'BCE8", "H'BCF0", "H'BCF6", - "H'BCB5", - "H'BCBD", - "H'BCC5", "H'BCDC", "H'BCE4", "H'BCFA" @@ -200695,38 +201025,15 @@ "tx_staging_byte": "TX[2]", "address": 63570, "address_hex": "H'F852", - "source_kind": "rx_frame_byte", - "source_expression": "rx[2]", + "source_kind": "stale_or_unchanged", + "source_expression": "stale/unchanged", "source": { - "kind": "rx_frame_byte", - "rx_offset": 2, - "rx_address": 63586, - "rx_address_hex": "H'F862", - "derived_from": { - "kind": "rx_frame_word", - "rx_offset": 2, - "rx_offsets": [ - 2, - 3 - ], - "rx_address": 63586, - "rx_address_hex": "H'F862", - "evidence_address": 48317, - "evidence_address_hex": "H'BCBD", - "instruction": "MOV:G.W @H'F862, R0" - } + "kind": "unknown" }, - "write_instruction_address": 48321, - "write_instruction_address_hex": "H'BCC1", - "instruction": "MOV:G.W R0, @H'F852", - "evidence_addresses": [ - 48321 - ], - "evidence_addresses_hex": [ - "H'BCC1" - ], - "confidence": "candidate-medium", - "caveat": "Per-byte source is inferred from the final observed write to this staging byte before loc_BA26." + "evidence_addresses": [], + "evidence_addresses_hex": [], + "confidence": "candidate-low", + "caveat": "BCD7 does not freshly write F852 before loc_BA26." }, { "offset": 3, @@ -200806,17 +201113,20 @@ "caveat": "Per-byte source is inferred from the final observed write to this staging byte before loc_BA26." } ], + "semantic_notes": [ + "Command 1 BCD7 staging is odd: F850=0x04; F851 is written from F861 then overwritten by F862.", + "The primary table word is read from E000 + 2*selector; F854/F853 receive low/high value bytes.", + "F852 may be stale or unchanged in this handler; avoid a fixed 04 00 QQ hi lo response shape." + ], "evidence_addresses": [ 48343, 48360, - 48321, 48374, 48368 ], "evidence_addresses_hex": [ "H'BCD7", "H'BCE8", - "H'BCC1", "H'BCF6", "H'BCF0" ], @@ -200897,38 +201207,15 @@ "tx_staging_byte": "TX[2]", "address": 63570, "address_hex": "H'F852", - "source_kind": "rx_frame_byte", - "source_expression": "rx[2]", + "source_kind": "stale_or_unchanged", + "source_expression": "stale/unchanged", "source": { - "kind": "rx_frame_byte", - "rx_offset": 2, - "rx_address": 63586, - "rx_address_hex": "H'F862", - "derived_from": { - "kind": "rx_frame_word", - "rx_offset": 2, - "rx_offsets": [ - 2, - 3 - ], - "rx_address": 63586, - "rx_address_hex": "H'F862", - "evidence_address": 48317, - "evidence_address_hex": "H'BCBD", - "instruction": "MOV:G.W @H'F862, R0" - } + "kind": "unknown" }, - "write_instruction_address": 48321, - "write_instruction_address_hex": "H'BCC1", - "instruction": "MOV:G.W R0, @H'F852", - "evidence_addresses": [ - 48321 - ], - "evidence_addresses_hex": [ - "H'BCC1" - ], - "confidence": "candidate-medium", - "caveat": "Per-byte source is inferred from the final observed write to this staging byte before loc_BA26." + "evidence_addresses": [], + "evidence_addresses_hex": [], + "confidence": "candidate-low", + "caveat": "BCD7 does not freshly write F852 before loc_BA26." }, { "offset": 3, @@ -201007,6 +201294,11 @@ "confidence": "candidate-medium", "caveat": "Per-byte source is inferred from the final observed write to this staging byte before loc_BA26." } + ], + "semantic_notes": [ + "Command 1 BCD7 staging is odd: F850=0x04; F851 is written from F861 then overwritten by F862.", + "The primary table word is read from E000 + 2*selector; F854/F853 receive low/high value bytes.", + "F852 may be stale or unchanged in this handler; avoid a fixed 04 00 QQ hi lo response shape." ] }, { @@ -201017,8 +201309,8 @@ "send_builder": "loc_BA26", "send_builder_address": 47654, "send_builder_address_hex": "H'BA26", - "window_start": 48627, - "window_start_hex": "H'BDF3", + "window_start": 48645, + "window_start_hex": "H'BE05", "writes": [ { "instruction_address": 48649, @@ -201326,6 +201618,7 @@ "caveat": "Per-byte source is inferred from the final observed write to this staging byte before loc_BA26." } ], + "semantic_notes": [], "evidence_addresses": [ 48649, 48657, @@ -201540,7 +201833,8 @@ "confidence": "candidate-medium", "caveat": "Per-byte source is inferred from the final observed write to this staging byte before loc_BA26." } - ] + ], + "semantic_notes": [] }, { "id": "response_at_BE6A", @@ -201550,8 +201844,8 @@ "send_builder": "loc_BA26", "send_builder_address": 47654, "send_builder_address_hex": "H'BA26", - "window_start": 48702, - "window_start_hex": "H'BE3E", + "window_start": 48717, + "window_start_hex": "H'BE4D", "writes": [ { "instruction_address": 48717, @@ -201890,6 +202184,10 @@ "caveat": "Per-byte source is inferred from the final observed write to this staging byte before loc_BA26." } ], + "semantic_notes": [ + "loc_BE4D retry/error echo stages F850=0x07 and copies F861-F864 into F851-F854 before loc_BA26.", + "Observed 07 80 40 20 90 2D echoes RX payload bytes 80 40 20 90; it is not a table-derived value." + ], "evidence_addresses": [ 48717, 48726, @@ -202091,6 +202389,10 @@ "confidence": "candidate-medium", "caveat": "Per-byte source is inferred from the final observed write to this staging byte before loc_BA26." } + ], + "semantic_notes": [ + "loc_BE4D retry/error echo stages F850=0x07 and copies F861-F864 into F851-F854 before loc_BA26.", + "Observed 07 80 40 20 90 2D echoes RX payload bytes 80 40 20 90; it is not a table-derived value." ] } ], @@ -202275,6 +202577,7 @@ "caveat": "Per-byte source is inferred from the final observed write to this staging byte before loc_BA26." } ], + "semantic_notes": [], "evidence_addresses": [ 47900, 47915, @@ -202488,6 +202791,7 @@ "caveat": "Per-byte source is inferred from the final observed write to this staging byte before loc_BA26." } ], + "semantic_notes": [], "evidence_addresses": [ 48304, 48313, @@ -202586,38 +202890,15 @@ "tx_staging_byte": "TX[2]", "address": 63570, "address_hex": "H'F852", - "source_kind": "rx_frame_byte", - "source_expression": "rx[2]", + "source_kind": "stale_or_unchanged", + "source_expression": "stale/unchanged", "source": { - "kind": "rx_frame_byte", - "rx_offset": 2, - "rx_address": 63586, - "rx_address_hex": "H'F862", - "derived_from": { - "kind": "rx_frame_word", - "rx_offset": 2, - "rx_offsets": [ - 2, - 3 - ], - "rx_address": 63586, - "rx_address_hex": "H'F862", - "evidence_address": 48317, - "evidence_address_hex": "H'BCBD", - "instruction": "MOV:G.W @H'F862, R0" - } + "kind": "unknown" }, - "write_instruction_address": 48321, - "write_instruction_address_hex": "H'BCC1", - "instruction": "MOV:G.W R0, @H'F852", - "evidence_addresses": [ - 48321 - ], - "evidence_addresses_hex": [ - "H'BCC1" - ], - "confidence": "candidate-medium", - "caveat": "Per-byte source is inferred from the final observed write to this staging byte before loc_BA26." + "evidence_addresses": [], + "evidence_addresses_hex": [], + "confidence": "candidate-low", + "caveat": "BCD7 does not freshly write F852 before loc_BA26." }, { "offset": 3, @@ -202697,17 +202978,20 @@ "caveat": "Per-byte source is inferred from the final observed write to this staging byte before loc_BA26." } ], + "semantic_notes": [ + "Command 1 BCD7 staging is odd: F850=0x04; F851 is written from F861 then overwritten by F862.", + "The primary table word is read from E000 + 2*selector; F854/F853 receive low/high value bytes.", + "F852 may be stale or unchanged in this handler; avoid a fixed 04 00 QQ hi lo response shape." + ], "evidence_addresses": [ 48343, 48360, - 48321, 48374, 48368 ], "evidence_addresses_hex": [ "H'BCD7", "H'BCE8", - "H'BCC1", "H'BCF6", "H'BCF0" ], @@ -202925,6 +203209,7 @@ "caveat": "Per-byte source is inferred from the final observed write to this staging byte before loc_BA26." } ], + "semantic_notes": [], "evidence_addresses": [ 48649, 48657, @@ -203134,6 +203419,10 @@ "caveat": "Per-byte source is inferred from the final observed write to this staging byte before loc_BA26." } ], + "semantic_notes": [ + "loc_BE4D retry/error echo stages F850=0x07 and copies F861-F864 into F851-F854 before loc_BA26.", + "Observed 07 80 40 20 90 2D echoes RX payload bytes 80 40 20 90; it is not a table-derived value." + ], "evidence_addresses": [ 48717, 48726, @@ -203331,6 +203620,7 @@ "caveat": "Per-byte source is inferred from the final observed write to this staging byte before loc_BA26." } ], + "semantic_notes": [], "evidence_addresses": [ 47900, 47915, @@ -203544,6 +203834,7 @@ "caveat": "Per-byte source is inferred from the final observed write to this staging byte before loc_BA26." } ], + "semantic_notes": [], "evidence_addresses": [ 48304, 48313, @@ -203642,38 +203933,15 @@ "tx_staging_byte": "TX[2]", "address": 63570, "address_hex": "H'F852", - "source_kind": "rx_frame_byte", - "source_expression": "rx[2]", + "source_kind": "stale_or_unchanged", + "source_expression": "stale/unchanged", "source": { - "kind": "rx_frame_byte", - "rx_offset": 2, - "rx_address": 63586, - "rx_address_hex": "H'F862", - "derived_from": { - "kind": "rx_frame_word", - "rx_offset": 2, - "rx_offsets": [ - 2, - 3 - ], - "rx_address": 63586, - "rx_address_hex": "H'F862", - "evidence_address": 48317, - "evidence_address_hex": "H'BCBD", - "instruction": "MOV:G.W @H'F862, R0" - } + "kind": "unknown" }, - "write_instruction_address": 48321, - "write_instruction_address_hex": "H'BCC1", - "instruction": "MOV:G.W R0, @H'F852", - "evidence_addresses": [ - 48321 - ], - "evidence_addresses_hex": [ - "H'BCC1" - ], - "confidence": "candidate-medium", - "caveat": "Per-byte source is inferred from the final observed write to this staging byte before loc_BA26." + "evidence_addresses": [], + "evidence_addresses_hex": [], + "confidence": "candidate-low", + "caveat": "BCD7 does not freshly write F852 before loc_BA26." }, { "offset": 3, @@ -203753,17 +204021,20 @@ "caveat": "Per-byte source is inferred from the final observed write to this staging byte before loc_BA26." } ], + "semantic_notes": [ + "Command 1 BCD7 staging is odd: F850=0x04; F851 is written from F861 then overwritten by F862.", + "The primary table word is read from E000 + 2*selector; F854/F853 receive low/high value bytes.", + "F852 may be stale or unchanged in this handler; avoid a fixed 04 00 QQ hi lo response shape." + ], "evidence_addresses": [ 48343, 48360, - 48321, 48374, 48368 ], "evidence_addresses_hex": [ "H'BCD7", "H'BCE8", - "H'BCC1", "H'BCF6", "H'BCF0" ], @@ -203981,6 +204252,7 @@ "caveat": "Per-byte source is inferred from the final observed write to this staging byte before loc_BA26." } ], + "semantic_notes": [], "evidence_addresses": [ 48649, 48657, @@ -204190,6 +204462,10 @@ "caveat": "Per-byte source is inferred from the final observed write to this staging byte before loc_BA26." } ], + "semantic_notes": [ + "loc_BE4D retry/error echo stages F850=0x07 and copies F861-F864 into F851-F854 before loc_BA26.", + "Observed 07 80 40 20 90 2D echoes RX payload bytes 80 40 20 90; it is not a table-derived value." + ], "evidence_addresses": [ 48717, 48726, @@ -206599,7 +206875,14 @@ "response_candidates": [ "response_at_BE6A" ], - "summary": "Candidate retry path clears/consults serial flags, increments FAA6, compares it with 2, and when still below the apparent limit stages a command 0x07 response.", + "summary": "Candidate retry path clears/consults serial flags, increments FAA6, compares it with 2, and when still below the apparent limit enters loc_BE4D to stage a command 0x07 retry/error echo of RX payload bytes F861-F864.", + "echo_response_candidate": { + "entry_label": "loc_BE4D", + "entry_address": 48717, + "entry_address_hex": "H'BE4D", + "staging_candidate": "F850=0x07; F851-F854=F861-F864", + "observed_frame_caveat": "07 80 40 20 90 2D echoes RX payload 80 40 20 90 and is not a table value." + }, "evidence_addresses": [ 48681, 48685, @@ -206611,10 +206894,10 @@ 48726, 48734, 48742, - 48746, 48722, 48730, - 48738 + 48738, + 48746 ], "evidence_addresses_hex": [ "H'BE29", @@ -206627,10 +206910,10 @@ "H'BE56", "H'BE5E", "H'BE66", - "H'BE6A", "H'BE52", "H'BE5A", - "H'BE62" + "H'BE62", + "H'BE6A" ], "confidence": "candidate-medium" }, @@ -206940,21 +207223,20 @@ }, { "name": "host_ack_can_advance_queue", - "summary": "Commands 0x05/0x06 are modeled as acknowledgement paths that can clear pending state or advance F9B5.", + "summary": "Command 0x05 is a continuation-only ACK/session clear path: it clears FAA3/FAA2 and advances F9B5 only when queued-report FAA2.bit3 was set. Selector 0x0040 has no response; if FAA2 == 0 the command falls through instead of doing ACK work.", "command_values_hex": [ - "H'05", - "H'06" + "H'05" ], "state_addresses_hex": [ + "H'FAA2", + "H'FAA3", "H'F9B5" ], "evidence_addresses": [ 48136, 48140, 48202, - 48204, - 48207, - 48209 + 48204 ] } ], diff --git a/build/rom_serial_pseudocode.c b/build/rom_serial_pseudocode.c index fe07477..e010109 100644 --- a/build/rom_serial_pseudocode.c +++ b/build/rom_serial_pseudocode.c @@ -113,26 +113,45 @@ extern volatile u8 MEM8[0x10000]; * - byte4: value_lo (medium) - candidate low byte of a word value * - byte5: checksum (high) - 0x5A-seeded XOR of bytes 0..4 * dispatch: command_low3 = RX_FRAME(0) & 0x07; observed H'00, H'01, H'02, H'04, H'05, H'06, H'07 + * dispatcher split: FAA2 == 0 accepts initial/idle commands H'00, H'01, H'02, H'07; FAA2 != 0 accepts continuation commands H'04, H'05, H'06, H'07 + * dispatcher caveat: Initial dispatch follows checksum validation and RX error handling. Command 1 is only on the initial/idle path and is also gated by F861.bit7 == 0. * dispatch evidence: H'BC08, H'BC0C, H'BC20, H'BC22, H'BC24, H'BC26, H'BC29, H'BC2B, H'BC2E, H'BC30, H'BC45, H'BC47, H'BC4A, H'BC4C, H'BC4F, H'BC51, H'BC54, H'BC56 * index decoder: RX[1:2] -> logical index via loc_622B (medium) * command candidates: * - H'00 set_value_acked: candidate write of RX[3:4] into primary/current tables, followed by a response; handler H'BC69; responses response_at_BCCD - * - H'01 read_value: candidate read from the primary table, followed by a response carrying the value; handler H'BCD7; responses response_at_BCFA + * availability: valid checksum/no RX physical error && FAA2 == 0 + * - H'01 read_value: initial/idle-path primary table read only, followed by an odd response staging sequence; handler H'BCD7; responses response_at_BCFA + * availability: valid checksum/no RX physical error && FAA2 == 0 && F861.bit7 == 0 + * note: Only accepted on the initial/idle dispatcher path: valid checksum/no RX error, FAA2 == 0, and F861.bit7 == 0. + * note: BCD7 stages F850=0x04, writes F851 from F861 and then overwrites F851 from F862. + * note: BCD7 reads the primary table word at E000 + 2*selector; F854 receives the low byte and F853 receives the high byte. + * note: F852 is not freshly written in the BCD7 handler, so do not describe the response as a fixed 04 00 QQ hi lo frame. * - H'02 clear_or_abort: candidate clear/abort path with no immediate response builder; handler H'BD04; responses none + * availability: valid checksum/no RX physical error && FAA2 == 0 * - H'04 set_value_no_immediate_reply: candidate write/update path that stores a value without an immediate serial response; handler H'BD0E; responses none - * - H'05 ack_or_clear_pending: candidate pending/event acknowledgement path; handler H'BD80; responses none + * availability: valid checksum/no RX physical error && FAA2 != 0 + * - H'05 ack_or_clear_pending: continuation-only conditional acknowledgement/session clear path; handler H'BD80; responses none + * availability: valid checksum/no RX physical error && FAA2 != 0 + * note: Only accepted on the continuation dispatcher path when FAA2 != 0. + * note: For selector 0x0040, frame 05 00 40 00 00 1F performs no response staging. + * note: The handler clears FAA3/FAA2; F9B5 advances only when FAA2.bit3 was set from a queued report. + * note: If FAA2 == 0, command 5 falls through the initial dispatcher instead of doing acknowledgement work. * - H'06 set_secondary_value: candidate secondary-table value write path; handler H'BDDB; responses none - * - H'07 retransmit_or_error_reply: candidate retransmit/NAK-style path; error handling also builds command 0x07 responses; handler H'BE05; responses response_at_BE22 + * availability: valid checksum/no RX physical error && FAA2 != 0 + * - H'07 retransmit_or_error_reply: candidate retransmit path; retry/error handling also builds a command 0x07 RX-payload echo; handler H'BE05; responses response_at_BE22 + * availability: valid checksum/no RX physical error && FAA2 == 0 OR valid checksum/no RX physical error && FAA2 != 0 + * note: loc_BE4D is a retry/error echo path: F850=0x07 and F861-F864 are copied into F851-F854 before loc_BA26. + * note: Observed frame 07 80 40 20 90 2D means RX bytes F861-F864 were 80 40 20 90; it is not a table value. * command effects: * - H'00 set_value_acked: Candidate acknowledged set: writes value bytes to primary/current tables, flags the index, and stages an echo-style response. * effect: table_write_candidate; target primary_value_table_candidate; source RX[3:4] value bytes, with an observed 0x80 fallback when decoded index is zero; table H'E000 * effect: table_write_candidate; target current_value_table_candidate; source same candidate value written to the primary table; table H'E800 * effect: flag_update_candidate; target per_index_flag_table_candidate; set bit 7; table H'EC00 * evidence: H'BC08, H'BC0C, H'BC20, H'BC22, H'BCB0, H'BCB9, H'BCC1, H'BCC9, H'BCB5, H'BCBD, H'BCC5, H'BCCD - * - H'01 read_value: Candidate read: reads the primary table and stages a value response. + * - H'01 read_value: Initial/idle candidate read: reads the primary table and stages an odd value response with F852 possibly stale. * effect: table_read_candidate; target primary_value_table_candidate; table H'E000 * effect: response_staging_candidate; stage F850-F854 and call loc_BA26 - * evidence: H'BC08, H'BC0C, H'BC24, H'BC26, H'BCB0, H'BCB9, H'BCC1, H'BCC9, H'BCD7, H'BCE0, H'BCE8, H'BCF0, H'BCF6, H'BCB5, H'BCBD, H'BCC5, H'BCDC, H'BCE4, H'BCFA + * evidence: H'BC08, H'BC0C, H'BC24, H'BC26, H'BCD7, H'BCE0, H'BCE8, H'BCF0, H'BCF6, H'BCDC, H'BCE4, H'BCFA * - H'02 clear_or_abort: Candidate clear/abort: clears serial session state without an observed immediate response. * effect: state_clear_candidate; target serial_session_flags_candidate; clear bit 7; state H'FAA2 * evidence: H'BC08, H'BC0C, H'BC29, H'BC2B @@ -140,15 +159,16 @@ extern volatile u8 MEM8[0x10000]; * effect: table_write_candidate; target primary_value_table_candidate; source RX[3:4] value bytes, with an observed 0x80 fallback when decoded index is zero; table H'E000 * effect: flag_update_candidate; target per_index_flag_table_candidate; set bit 7; table H'EC00 * evidence: H'BC08, H'BC0C, H'BC45, H'BC47 - * - H'05 ack_or_clear_pending: Candidate acknowledgement/clear: updates pending/event state without an observed immediate response. - * effect: pending_acknowledgement_candidate; target selected event/pending state; clear selected pending flags and then clear serial session state + * - H'05 ack_or_clear_pending: Continuation-only ACK/session clear: clears FAA3/FAA2 and only advances F9B5 when queued-report FAA2.bit3 was set; selector 0x0040 has no response. + * effect: conditional_ack_session_clear_candidate; target selected event/pending state; when FAA2 != 0, clear FAA3/FAA2; advance F9B5 only if FAA2.bit3 was set from queued-report state; selector 0x0040 stages no response; selector H'0040 has no response * evidence: H'BC08, H'BC0C, H'BC4A, H'BC4C * - H'06 set_secondary_value: Candidate secondary set: writes value bytes to the secondary table and flags the index. * effect: table_write_candidate; target secondary_value_table_candidate; source RX[3:4] value bytes; table H'E400 * effect: flag_update_candidate; target per_index_flag_table_candidate; set bit 6; table H'EC00 * evidence: H'BC08, H'BC0C, H'BC4F, H'BC51 - * - H'07 retransmit_or_error_reply: Candidate retransmit/error reply: reuses prior TX bytes or builds an explicit 0x07 retry/error response. + * - H'07 retransmit_or_error_reply: Candidate retransmit/error reply: reuses prior TX bytes or copies RX payload bytes behind an explicit 0x07 retry/error echo. * effect: retransmit_candidate; target TX staging bytes H'F850-H'F854 before loc_BA26; source previous TX frame bytes H'F858-H'F85C + * effect: retry_error_echo_candidate; target F850=0x07, F851-F854=F861-F864 before loc_BA26; source RX payload bytes F861-F864; 07 80 40 20 90 2D echoes RX payload 80 40 20 90; it is not a table value * effect: response_staging_candidate; stage F850-F854 and call loc_BA26 * evidence: H'BC08, H'BC0C, H'BC2E, H'BC30, H'BC54, H'BC56, H'BE09, H'BE11, H'BE19, H'BE22 * response schemas: @@ -156,8 +176,11 @@ extern volatile u8 MEM8[0x10000]; * evidence: H'BB1C, H'BB2B, H'BB20, H'BB3F, H'BB39 * - response_at_BCCD: byte0=0x04; byte1=rx[1]; byte2=rx[2]; byte3=rx[3]; byte4=rx[4] * evidence: H'BCB0, H'BCB9, H'BCC1, H'BCC9 - * - response_at_BCFA: byte0=0x04; byte1=rx[2]; byte2=rx[2]; byte3=primary_value_table_candidate; byte4=primary_value_table_candidate - * evidence: H'BCD7, H'BCE8, H'BCC1, H'BCF6, H'BCF0 + * - response_at_BCFA: byte0=0x04; byte1=rx[2]; byte2=stale/unchanged; byte3=primary_value_table_candidate; byte4=primary_value_table_candidate + * note: Command 1 BCD7 staging is odd: F850=0x04; F851 is written from F861 then overwritten by F862. + * note: The primary table word is read from E000 + 2*selector; F854/F853 receive low/high value bytes. + * note: F852 may be stale or unchanged in this handler; avoid a fixed 04 00 QQ hi lo response shape. + * evidence: H'BCD7, H'BCE8, H'BCF6, H'BCF0 * - response_at_BE22: byte0=tx[0]; byte1=tx[1]; byte2=tx[2]; byte3=tx[3]; byte4=tx[4] * evidence: H'BE09, H'BE11, H'BE19 * - ... 1 more candidate response schemas @@ -186,7 +209,9 @@ extern volatile u8 MEM8[0x10000]; * - ... 7 more state-variable candidates * retry/error model candidate: * - checksum path: 0x5A-seeded XOR over RX[0..4] differs from RX[5] -> loc_BE29 - * - retry path: counter H'FAA6, threshold 2; Candidate retry path clears/consults serial flags, increments FAA6, compares it with 2, and when still below the apparent limit stages a command 0x07 response. + * - retry path: counter H'FAA6, threshold 2; Candidate retry path clears/consults serial flags, increments FAA6, compares it with 2, and when still below the apparent limit enters loc_BE4D to stage a command 0x07 retry/error echo of RX payload bytes F861-F864. + * echo path: loc_BE4D: F850=0x07; F851-F854=F861-F864 + * echo caveat: 07 80 40 20 90 2D echoes RX payload 80 40 20 90 and is not a table value. * - command 0x07 path: Candidate retransmit/explicit command 0x07 path either copies previous TX frame bytes back to F850-F854 or stages an observed 0x07 response before loc_BA26. * - evidence: H'BBD8, H'BBDC, H'BBE0, H'BBE4, H'BBE8, H'BBEC, H'BBF0, H'BE4D, H'BE56, H'BE5E, H'BE66, H'BE52, H'BE5A, H'BE62, H'BE6A, H'BE29, H'BE2D, H'BE33, H'BE37, H'BE43, H'BE47, H'BE05, H'BE0D, H'BE15, H'BE09, H'BE11, H'BE19, H'BE22 * gate/queue state machine candidate: @@ -201,7 +226,7 @@ extern volatile u8 MEM8[0x10000]; * - session_timeout_clears_gate_and_queue: When F9C5 is clear, loc_3FEF clears F9B5/F9B0 and clears FAA5.bit7; when nonzero, it sets FAA5.bit7. * - idle_heartbeat_gate_initial_delay_loaded: Startup/init loads F9C4 with H'14 before the first idle/default report can be queued. * - idle_heartbeat_gate_post_send_delay_loaded: loc_BA26 reloads F9C4 with H'07 after each send, matching the observed heartbeat spacing. - * - host_ack_can_advance_queue: Commands 0x05/0x06 are modeled as acknowledgement paths that can clear pending state or advance F9B5.; commands H'05, H'06 + * - host_ack_can_advance_queue: Command 0x05 is a continuation-only ACK/session clear path: it clears FAA3/FAA2 and advances F9B5 only when queued-report FAA2.bit3 was set. Selector 0x0040 has no response; if FAA2 == 0 the command falls through instead of doing ACK work.; commands H'05 * - caveat: Many panel controls may require host/session traffic before reporting. Observed autonomous call/camera-power indexes are runtime/capture overlays, not ROM constants. * - evidence: H'3FD3, H'3FD7, H'3FD9, H'3FDD, H'3FDF, H'3FE3, H'3FE5, H'3FE9, H'3FEB, H'3FEF, H'3FF3, H'3FF5, H'3FF9, H'3FFD, H'4001, H'4003, H'4005, H'4007, H'4046, H'404A, H'404C, H'4050, H'4052, H'4056, H'4058, H'4059, H'405D, H'405F, H'4063, H'4065, H'4067, H'406C, H'4070, H'BAF2, H'BAF6, H'BAF8, H'BAFC, H'BAFE, H'BB00, H'BB04, H'BB06, H'BB08, H'BB0C, H'BB0E, H'BB11, H'BB13, H'BB15, H'BB17, H'BB19, H'BB1C, H'BB20, H'BB24, H'BB26, H'BB29, H'BB2B, H'BB2F, H'BB33, H'BB35, H'BB39, H'BB3D, H'BB3F, H'BB43, H'BE9E, H'BEA2, H'BEA5, H'BEA9, H'BEAD, H'BEAF, H'BEB3, H'BEB5, H'BEB9, H'BEBB, H'BEBF, H'BEC1, H'BEC5, H'BECB, H'BECF, H'BED1, H'BED5 * TX/autonomous report model candidate: @@ -338,33 +363,70 @@ void sci1_process_candidate_protocol_command(void) u16 logical_index = sci1_rx_candidate_logical_index(); u16 value = sci1_rx_candidate_value(); + bool session_active = MEM8[0xFAA2u] != 0u; + + if (!session_active) { + /* Initial/idle dispatcher: valid checksum/no RX error, FAA2 == 0. */ + switch (command) { + case 0x00u: + /* set_value_acked: candidate write of RX[3:4] into primary/current tables, followed by a response + * availability: valid checksum/no RX physical error && FAA2 == 0 + * candidate effect: table_write_candidate; target primary_value_table_candidate; source RX[3:4] value bytes, with an observed 0x80 fallback when decoded index is zero; table H'E000 + * candidate effect: table_write_candidate; target current_value_table_candidate; source same candidate value written to the primary table; table H'E800 + * candidate effect: flag_update_candidate; target per_index_flag_table_candidate; set bit 7; table H'EC00 + * evidence: H'BC08, H'BC0C, H'BC20, H'BC22, H'BCB0, H'BCB9, H'BCC1, H'BCC9, H'BCB5, H'BCBD, H'BCC5, H'BCCD + */ + candidate_set_value_acked(logical_index, value); + break; + case 0x01u: + /* read_value: initial/idle-path primary table read only, followed by an odd response staging sequence + * availability: valid checksum/no RX physical error && FAA2 == 0 && F861.bit7 == 0 + * note: Only accepted on the initial/idle dispatcher path: valid checksum/no RX error, FAA2 == 0, and F861.bit7 == 0. + * note: BCD7 stages F850=0x04, writes F851 from F861 and then overwrites F851 from F862. + * note: BCD7 reads the primary table word at E000 + 2*selector; F854 receives the low byte and F853 receives the high byte. + * note: F852 is not freshly written in the BCD7 handler, so do not describe the response as a fixed 04 00 QQ hi lo frame. + * candidate effect: table_read_candidate; target primary_value_table_candidate; table H'E000 + * candidate effect: response_staging_candidate; stage F850-F854 and call loc_BA26 + * evidence: H'BC08, H'BC0C, H'BC24, H'BC26, H'BCD7, H'BCE0, H'BCE8, H'BCF0, H'BCF6, H'BCDC, H'BCE4, H'BCFA + */ + if ((RX_FRAME(1) & 0x80u) != 0u) { + candidate_unknown_command(command, logical_index, value); + break; + } + candidate_read_value(logical_index, value); + break; + case 0x02u: + /* clear_or_abort: candidate clear/abort path with no immediate response builder + * availability: valid checksum/no RX physical error && FAA2 == 0 + * candidate effect: state_clear_candidate; target serial_session_flags_candidate; clear bit 7; state H'FAA2 + * evidence: H'BC08, H'BC0C, H'BC29, H'BC2B + */ + candidate_clear_or_abort(logical_index, value); + break; + case 0x07u: + /* retransmit_or_error_reply: candidate retransmit path; retry/error handling also builds a command 0x07 RX-payload echo + * availability: valid checksum/no RX physical error && FAA2 == 0 OR valid checksum/no RX physical error && FAA2 != 0 + * note: loc_BE4D is a retry/error echo path: F850=0x07 and F861-F864 are copied into F851-F854 before loc_BA26. + * note: Observed frame 07 80 40 20 90 2D means RX bytes F861-F864 were 80 40 20 90; it is not a table value. + * candidate effect: retransmit_candidate; target TX staging bytes H'F850-H'F854 before loc_BA26; source previous TX frame bytes H'F858-H'F85C + * candidate effect: retry_error_echo_candidate; target F850=0x07, F851-F854=F861-F864 before loc_BA26; source RX payload bytes F861-F864; 07 80 40 20 90 2D echoes RX payload 80 40 20 90; it is not a table value + * candidate effect: response_staging_candidate; stage F850-F854 and call loc_BA26 + * evidence: H'BC08, H'BC0C, H'BC2E, H'BC30, H'BC54, H'BC56, H'BE09, H'BE11, H'BE19, H'BE22 + */ + candidate_retransmit_or_error_reply(logical_index, value); + break; + default: + candidate_unknown_command(command, logical_index, value); + break; + } + return; + } + + /* Continuation dispatcher: FAA2 != 0. */ switch (command) { - case 0x00u: - /* set_value_acked: candidate write of RX[3:4] into primary/current tables, followed by a response - * candidate effect: table_write_candidate; target primary_value_table_candidate; source RX[3:4] value bytes, with an observed 0x80 fallback when decoded index is zero; table H'E000 - * candidate effect: table_write_candidate; target current_value_table_candidate; source same candidate value written to the primary table; table H'E800 - * candidate effect: flag_update_candidate; target per_index_flag_table_candidate; set bit 7; table H'EC00 - * evidence: H'BC08, H'BC0C, H'BC20, H'BC22, H'BCB0, H'BCB9, H'BCC1, H'BCC9, H'BCB5, H'BCBD, H'BCC5, H'BCCD - */ - candidate_set_value_acked(logical_index, value); - break; - case 0x01u: - /* read_value: candidate read from the primary table, followed by a response carrying the value - * candidate effect: table_read_candidate; target primary_value_table_candidate; table H'E000 - * candidate effect: response_staging_candidate; stage F850-F854 and call loc_BA26 - * evidence: H'BC08, H'BC0C, H'BC24, H'BC26, H'BCB0, H'BCB9, H'BCC1, H'BCC9, H'BCD7, H'BCE0, H'BCE8, H'BCF0, H'BCF6, H'BCB5, H'BCBD, H'BCC5, H'BCDC, H'BCE4, H'BCFA - */ - candidate_read_value(logical_index, value); - break; - case 0x02u: - /* clear_or_abort: candidate clear/abort path with no immediate response builder - * candidate effect: state_clear_candidate; target serial_session_flags_candidate; clear bit 7; state H'FAA2 - * evidence: H'BC08, H'BC0C, H'BC29, H'BC2B - */ - candidate_clear_or_abort(logical_index, value); - break; case 0x04u: /* set_value_no_immediate_reply: candidate write/update path that stores a value without an immediate serial response + * availability: valid checksum/no RX physical error && FAA2 != 0 * candidate effect: table_write_candidate; target primary_value_table_candidate; source RX[3:4] value bytes, with an observed 0x80 fallback when decoded index is zero; table H'E000 * candidate effect: flag_update_candidate; target per_index_flag_table_candidate; set bit 7; table H'EC00 * evidence: H'BC08, H'BC0C, H'BC45, H'BC47 @@ -372,14 +434,20 @@ void sci1_process_candidate_protocol_command(void) candidate_set_value_no_immediate_reply(logical_index, value); break; case 0x05u: - /* ack_or_clear_pending: candidate pending/event acknowledgement path - * candidate effect: pending_acknowledgement_candidate; target selected event/pending state; clear selected pending flags and then clear serial session state + /* ack_or_clear_pending: continuation-only conditional acknowledgement/session clear path + * availability: valid checksum/no RX physical error && FAA2 != 0 + * note: Only accepted on the continuation dispatcher path when FAA2 != 0. + * note: For selector 0x0040, frame 05 00 40 00 00 1F performs no response staging. + * note: The handler clears FAA3/FAA2; F9B5 advances only when FAA2.bit3 was set from a queued report. + * note: If FAA2 == 0, command 5 falls through the initial dispatcher instead of doing acknowledgement work. + * candidate effect: conditional_ack_session_clear_candidate; target selected event/pending state; when FAA2 != 0, clear FAA3/FAA2; advance F9B5 only if FAA2.bit3 was set from queued-report state; selector 0x0040 stages no response; selector H'0040 has no response * evidence: H'BC08, H'BC0C, H'BC4A, H'BC4C */ candidate_ack_or_clear_pending(logical_index, value); break; case 0x06u: /* set_secondary_value: candidate secondary-table value write path + * availability: valid checksum/no RX physical error && FAA2 != 0 * candidate effect: table_write_candidate; target secondary_value_table_candidate; source RX[3:4] value bytes; table H'E400 * candidate effect: flag_update_candidate; target per_index_flag_table_candidate; set bit 6; table H'EC00 * evidence: H'BC08, H'BC0C, H'BC4F, H'BC51 @@ -387,8 +455,12 @@ void sci1_process_candidate_protocol_command(void) candidate_set_secondary_value(logical_index, value); break; case 0x07u: - /* retransmit_or_error_reply: candidate retransmit/NAK-style path; error handling also builds command 0x07 responses + /* retransmit_or_error_reply: candidate retransmit path; retry/error handling also builds a command 0x07 RX-payload echo + * availability: valid checksum/no RX physical error && FAA2 == 0 OR valid checksum/no RX physical error && FAA2 != 0 + * note: loc_BE4D is a retry/error echo path: F850=0x07 and F861-F864 are copied into F851-F854 before loc_BA26. + * note: Observed frame 07 80 40 20 90 2D means RX bytes F861-F864 were 80 40 20 90; it is not a table value. * candidate effect: retransmit_candidate; target TX staging bytes H'F850-H'F854 before loc_BA26; source previous TX frame bytes H'F858-H'F85C + * candidate effect: retry_error_echo_candidate; target F850=0x07, F851-F854=F861-F864 before loc_BA26; source RX payload bytes F861-F864; 07 80 40 20 90 2D echoes RX payload 80 40 20 90; it is not a table value * candidate effect: response_staging_candidate; stage F850-F854 and call loc_BA26 * evidence: H'BC08, H'BC0C, H'BC2E, H'BC30, H'BC54, H'BC56, H'BE09, H'BE11, H'BE19, H'BE22 */ diff --git a/h8536/serial_pseudocode.py b/h8536/serial_pseudocode.py index 1d5bf2e..4f52b1d 100644 --- a/h8536/serial_pseudocode.py +++ b/h8536/serial_pseudocode.py @@ -385,6 +385,17 @@ def _semantics_lines( " * dispatch: command_low3 = RX_FRAME(0) & 0x07" + (f"; observed {values}" if values else ""), ) + state_split = dispatch.get("state_split") or dispatch.get("dispatcher_split") + if isinstance(state_split, dict): + initial = ", ".join(str(item) for item in state_split.get("initial_idle_commands_hex", []) if item) + continuation = ", ".join(str(item) for item in state_split.get("continuation_commands_hex", []) if item) + lines.append( + " * dispatcher split: FAA2 == 0 accepts initial/idle commands " + f"{initial or 'none'}; FAA2 != 0 accepts continuation commands {continuation or 'none'}", + ) + caveat = str(state_split.get("caveat") or "").strip() + if caveat: + lines.append(f" * dispatcher caveat: {_comment_text(caveat)}") if opts.include_evidence: lines.append(f" * dispatch evidence: {_hex_join(dispatch.get('evidence_addresses_hex'))}") @@ -405,6 +416,11 @@ def _semantics_lines( handler = command.get("handler_start_hex") or "multiple" responses = ", ".join(str(item) for item in command.get("response_candidates", [])) or "none" lines.append(f" * - {value} {name}: {summary}; handler {handler}; responses {responses}") + availability = _comment_text(str(command.get("availability_summary") or "")) + if availability: + lines.append(f" * availability: {availability}") + for note in command.get("semantic_notes", []): + lines.append(f" * note: {_comment_text(str(note))}") 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=" * ")) @@ -458,9 +474,51 @@ def _semantics_lines( " u16 logical_index = sci1_rx_candidate_logical_index();", " u16 value = sci1_rx_candidate_value();", "", - " switch (command) {", ], ) + lines.extend(_command_dispatch_switch_lines(commands, opts)) + return lines + + +def _command_dispatch_switch_lines(commands: list[JsonObject], opts: SerialPseudocodeOptions) -> list[str]: + initial = [ + command for command in commands + if _command_has_availability(command, "initial_idle_dispatch") + ] + continuation = [ + command for command in commands + if _command_has_availability(command, "continuation_dispatch") + ] + if not initial and not continuation: + return _flat_command_switch_lines(commands, opts, indent=" ") + ["}", ""] + + lines = [ + " bool session_active = MEM8[0xFAA2u] != 0u;", + "", + " if (!session_active) {", + " /* Initial/idle dispatcher: valid checksum/no RX error, FAA2 == 0. */", + ] + lines.extend(_flat_command_switch_lines(initial, opts, indent=" ")) + lines.extend( + [ + " return;", + " }", + "", + " /* Continuation dispatcher: FAA2 != 0. */", + ], + ) + lines.extend(_flat_command_switch_lines(continuation, opts, indent=" ")) + lines.extend(["}", ""]) + return lines + + +def _flat_command_switch_lines( + commands: list[JsonObject], + opts: SerialPseudocodeOptions, + *, + indent: str, +) -> list[str]: + lines = [f"{indent}switch (command) {{"] for command in commands: value = command.get("command_value") if not isinstance(value, int): @@ -468,28 +526,50 @@ def _semantics_lines( name = _safe_identifier(str(command.get("name_candidate") or f"command_{value:02X}")) summary = _comment_text(str(command.get("summary") or "candidate command semantics unknown")) evidence = _hex_join(command.get("evidence_addresses_hex")) - lines.append(f" case 0x{value:02X}u:") - lines.append(f" /* {name}: {summary}") + lines.append(f"{indent}case 0x{value:02X}u:") + lines.append(f"{indent} /* {name}: {summary}") + availability = _comment_text(str(command.get("availability_summary") or "")) + if availability: + lines.append(f"{indent} * availability: {availability}") + for note in command.get("semantic_notes", []): + lines.append(f"{indent} * note: {_comment_text(str(note))}") for effect_line in _command_effect_switch_lines(command): - lines.append(f" * {effect_line}") + lines.append(f"{indent} * {effect_line}") if opts.include_evidence and evidence: - lines.append(f" * evidence: {evidence}") - lines.append(" */") - lines.append(f" candidate_{name}(logical_index, value);") - lines.append(" break;") + lines.append(f"{indent} * evidence: {evidence}") + lines.append(f"{indent} */") + if value == 0x01: + lines.append(f"{indent} if ((RX_FRAME(1) & 0x80u) != 0u) {{") + lines.append(f"{indent} candidate_unknown_command(command, logical_index, value);") + lines.append(f"{indent} break;") + lines.append(f"{indent} }}") + lines.append(f"{indent} candidate_{name}(logical_index, value);") + lines.append(f"{indent} break;") lines.extend( [ - " default:", - " candidate_unknown_command(command, logical_index, value);", - " break;", - " }", - "}", - "", + f"{indent}default:", + f"{indent} candidate_unknown_command(command, logical_index, value);", + f"{indent} break;", + f"{indent}}}", ], ) return lines +def _command_has_availability(command: JsonObject, availability: str) -> bool: + value = command.get("availability") + if value == availability: + return True + if isinstance(value, list) and availability in value: + return True + conditions = " ".join(str(item) for item in command.get("availability_conditions", [])) + if availability == "initial_idle_dispatch": + return "FAA2 == 0" in conditions + if availability == "continuation_dispatch": + return "FAA2 != 0" in conditions + return False + + def _command_effect_comment_lines( value: object, opts: SerialPseudocodeOptions, @@ -528,6 +608,8 @@ def _response_schema_comment_lines( response_id = schema.get("response_id") or schema.get("call_address_hex") or "candidate_response" byte_text = _response_schema_summary(schema) lines.append(f"{prefix}- {response_id}: {byte_text}") + for note in schema.get("semantic_notes", []): + lines.append(f"{prefix} note: {_comment_text(str(note))}") evidence = _hex_join(schema.get("evidence_addresses_hex")) if opts.include_evidence and evidence: lines.append(f"{prefix} evidence: {evidence}") @@ -612,6 +694,13 @@ def _retry_error_comment_lines( threshold = retry.get("threshold_candidate", "?") summary = _comment_text(str(retry.get("summary") or "candidate retry path")) lines.append(f"{prefix}- retry path: counter {counter}, threshold {threshold}; {summary}") + echo = retry.get("echo_response_candidate") + if isinstance(echo, dict): + staging = _comment_text(str(echo.get("staging_candidate") or "retry/error echo staging")) + caveat = _comment_text(str(echo.get("observed_frame_caveat") or "")) + lines.append(f"{prefix} echo path: {echo.get('entry_label', 'loc_BE4D')}: {staging}") + if caveat: + lines.append(f"{prefix} echo caveat: {caveat}") if isinstance(command_07, dict): summary = _comment_text(str(command_07.get("summary") or "candidate command 0x07 path")) lines.append(f"{prefix}- command 0x07 path: {summary}") @@ -987,6 +1076,8 @@ def _effect_summary(effect: JsonObject) -> str: operation = effect.get("operation_candidate") table = effect.get("table_base_hex") state = effect.get("state_address_hex") + selector = effect.get("selector_without_response_hex") + observed = effect.get("observed_frame_caveat") if target: parts.append(f"target {target}") if source: @@ -997,6 +1088,10 @@ def _effect_summary(effect: JsonObject) -> str: parts.append(f"table {table}") if state: parts.append(f"state {state}") + if selector: + parts.append(f"selector {selector} has no response") + if observed: + parts.append(str(observed)) return _comment_text("; ".join(parts)) diff --git a/h8536/serial_semantics.py b/h8536/serial_semantics.py index 83148e0..92167eb 100644 --- a/h8536/serial_semantics.py +++ b/h8536/serial_semantics.py @@ -392,6 +392,9 @@ def _find_command_dispatch(ordered: list[JsonObject]) -> JsonObject | None: continue comparisons = _dispatch_comparisons(ordered, index + 1, selector_reg) + state_split = _dispatcher_state_split(ordered, index + 1, comparisons) + if state_split: + _annotate_dispatch_comparison_availability(comparisons, state_split) command_values = sorted({int(item["command_value"]) for item in comparisons}) candidate = { "kind": "command_dispatch_candidate", @@ -413,6 +416,8 @@ def _find_command_dispatch(ordered: list[JsonObject]) -> JsonObject | None: "command_values": command_values, "command_values_hex": [_h16(value, width=2) for value in command_values], "comparisons": comparisons, + "state_split": state_split, + "dispatcher_split": state_split, "cases": [ { "value": int(item["command_value"]), @@ -421,6 +426,8 @@ def _find_command_dispatch(ordered: list[JsonObject]) -> JsonObject | None: "target_hex": item["handler_start_hex"], "compare_address": item["compare_address"], "branch_address": item["branch_address"], + "availability": item.get("availability"), + "availability_conditions": item.get("availability_conditions", []), } for item in comparisons ], @@ -446,6 +453,101 @@ def _find_command_dispatch(ordered: list[JsonObject]) -> JsonObject | None: return best +def _dispatcher_state_split( + ordered: list[JsonObject], + start_index: int, + comparisons: list[JsonObject], +) -> JsonObject | None: + if not comparisons: + return None + for index in range(start_index, min(len(ordered) - 1, start_index + 24)): + ins = ordered[index] + if not _has_ref_in_range(ins, 0xFAA2, 0xFAA2): + continue + if _mnemonic_root(str(ins.get("mnemonic", ""))) != "TST": + continue + branch = ordered[index + 1] + if _mnemonic_root(str(branch.get("mnemonic", ""))) != "BNE": + continue + targets = _targets(branch) + if not targets: + continue + continuation_start = int(targets[0]) + initial_values = sorted( + { + int(item["command_value"]) + for item in comparisons + if int(item.get("compare_address", 0)) < continuation_start + } + ) + continuation_values = sorted( + { + int(item["command_value"]) + for item in comparisons + if int(item.get("compare_address", 0)) >= continuation_start + } + ) + if not initial_values and not continuation_values: + continue + return { + "kind": "serial_command_dispatch_state_split", + "state_address": 0xFAA2, + "state_address_hex": _h16(0xFAA2), + "test_address": int(ins["address"]), + "test_address_hex": _h16(int(ins["address"])), + "branch_address": int(branch["address"]), + "branch_address_hex": _h16(int(branch["address"])), + "continuation_target": continuation_start, + "continuation_target_hex": _h16(continuation_start), + "initial_idle_commands": initial_values, + "initial_idle_commands_hex": [_h16(value, width=2) for value in initial_values], + "continuation_commands": continuation_values, + "continuation_commands_hex": [_h16(value, width=2) for value in continuation_values], + "summary": ( + "FAA2 == 0 takes the initial/idle dispatcher path; FAA2 != 0 takes " + "the continuation dispatcher path." + ), + "caveat": ( + "Initial dispatch follows checksum validation and RX error handling. Command 1 " + "is only on the initial/idle path and is also gated by F861.bit7 == 0." + ), + "evidence_addresses": [int(ins["address"]), int(branch["address"])], + "evidence_addresses_hex": _hlist([int(ins["address"]), int(branch["address"])]), + } + return None + + +def _annotate_dispatch_comparison_availability( + comparisons: list[JsonObject], + state_split: Mapping[str, Any], +) -> None: + continuation_start = state_split.get("continuation_target") + if not isinstance(continuation_start, int): + return + for item in comparisons: + compare_address = item.get("compare_address") + if not isinstance(compare_address, int): + continue + command_value = int(item.get("command_value", -1)) + if compare_address < continuation_start: + availability = "initial_idle_dispatch" + conditions = [ + "valid checksum/no RX physical error", + "FAA2 == 0", + ] + if command_value == 0x01: + conditions.append("F861.bit7 == 0") + else: + availability = "continuation_dispatch" + conditions = [ + "valid checksum/no RX physical error", + "FAA2 != 0", + ] + item["availability"] = availability + item["availability_conditions"] = conditions + item["availability_summary"] = " && ".join(conditions) + + def _dispatch_comparisons( ordered: list[JsonObject], start_index: int, @@ -537,6 +639,8 @@ def _command_candidates( "dispatch_compare_address_hex": comparison["compare_address_hex"], "dispatch_branch_address": comparison["branch_address"], "dispatch_branch_address_hex": comparison["branch_address_hex"], + "availability": comparison.get("availability"), + "availability_conditions": comparison.get("availability_conditions", []), } if alternative not in command["handler_alternatives"]: command["handler_alternatives"].append(alternative) @@ -555,6 +659,30 @@ def _command_candidates( command["handler_start_hex"] = _h16(starts_for_command[0]) if len(starts_for_command) == 1 else None command["handler_end"] = ends_for_command[0] if len(ends_for_command) == 1 else None command["handler_end_hex"] = _h16(ends_for_command[0]) if len(ends_for_command) == 1 else None + availability_conditions = _dedupe_strings( + str(condition) + for alt in alternatives + for condition in alt.get("availability_conditions", []) + if condition + ) + availability = _dedupe_strings( + str(alt["availability"]) + for alt in alternatives + if alt.get("availability") + ) + availability_summaries = _dedupe_strings( + " && ".join(str(condition) for condition in alt.get("availability_conditions", []) if condition) + for alt in alternatives + if alt.get("availability_conditions") + ) + command["availability"] = availability[0] if len(availability) == 1 else availability + command["availability_conditions"] = availability_conditions + command["availability_summary"] = ( + availability_summaries[0] + if len(availability_summaries) == 1 + else " OR ".join(availability_summaries) + ) + command["semantic_notes"] = _command_semantic_notes(int(command["command_value"])) ranges_for_command = [ (alt["handler_start"], alt["handler_end"]) @@ -657,26 +785,47 @@ def _command_name_candidate(value: int) -> str: def _command_summary(value: int) -> str: return { 0x00: "candidate write of RX[3:4] into primary/current tables, followed by a response", - 0x01: "candidate read from the primary table, followed by a response carrying the value", + 0x01: "initial/idle-path primary table read only, followed by an odd response staging sequence", 0x02: "candidate clear/abort path with no immediate response builder", 0x04: "candidate write/update path that stores a value without an immediate serial response", - 0x05: "candidate pending/event acknowledgement path", + 0x05: "continuation-only conditional acknowledgement/session clear path", 0x06: "candidate secondary-table value write path", - 0x07: "candidate retransmit/NAK-style path; error handling also builds command 0x07 responses", + 0x07: "candidate retransmit path; retry/error handling also builds a command 0x07 RX-payload echo", }.get(value, "candidate command semantics are unknown") +def _command_semantic_notes(value: int) -> list[str]: + return { + 0x01: [ + "Only accepted on the initial/idle dispatcher path: valid checksum/no RX error, FAA2 == 0, and F861.bit7 == 0.", + "BCD7 stages F850=0x04, writes F851 from F861 and then overwrites F851 from F862.", + "BCD7 reads the primary table word at E000 + 2*selector; F854 receives the low byte and F853 receives the high byte.", + "F852 is not freshly written in the BCD7 handler, so do not describe the response as a fixed 04 00 QQ hi lo frame.", + ], + 0x05: [ + "Only accepted on the continuation dispatcher path when FAA2 != 0.", + "For selector 0x0040, frame 05 00 40 00 00 1F performs no response staging.", + "The handler clears FAA3/FAA2; F9B5 advances only when FAA2.bit3 was set from a queued report.", + "If FAA2 == 0, command 5 falls through the initial dispatcher instead of doing acknowledgement work.", + ], + 0x07: [ + "loc_BE4D is a retry/error echo path: F850=0x07 and F861-F864 are copied into F851-F854 before loc_BA26.", + "Observed frame 07 80 40 20 90 2D means RX bytes F861-F864 were 80 40 20 90; it is not a table value.", + ], + }.get(value, []) + + def _command_effect_summary(value: int, effects: list[JsonObject]) -> str: if not effects: return "No structured command effects were inferred." return { 0x00: "Candidate acknowledged set: writes value bytes to primary/current tables, flags the index, and stages an echo-style response.", - 0x01: "Candidate read: reads the primary table and stages a value response.", + 0x01: "Initial/idle candidate read: reads the primary table and stages an odd value response with F852 possibly stale.", 0x02: "Candidate clear/abort: clears serial session state without an observed immediate response.", 0x04: "Candidate deferred set: writes value bytes and flags the index without an observed immediate response.", - 0x05: "Candidate acknowledgement/clear: updates pending/event state without an observed immediate response.", + 0x05: "Continuation-only ACK/session clear: clears FAA3/FAA2 and only advances F9B5 when queued-report FAA2.bit3 was set; selector 0x0040 has no response.", 0x06: "Candidate secondary set: writes value bytes to the secondary table and flags the index.", - 0x07: "Candidate retransmit/error reply: reuses prior TX bytes or builds an explicit 0x07 retry/error response.", + 0x07: "Candidate retransmit/error reply: reuses prior TX bytes or copies RX payload bytes behind an explicit 0x07 retry/error echo.", }.get(value, "Candidate effects are inferred from handler-local writes, reads, calls, and response staging.") @@ -740,9 +889,10 @@ def _command_effects( { "kind": "table_read_candidate", "target_candidate": "primary_value_table_candidate", - "destination_candidate": "response value bytes", + "destination_candidate": "response value bytes F854/F853, with F852 not freshly written by BCD7", "table_base": 0xE000, "table_base_hex": _h16(0xE000), + "address_expression_candidate": "E000 + 2*selector", "evidence_addresses": _table_access_addresses(table_accesses, 0x2000, "read"), } ) @@ -781,9 +931,15 @@ def _command_effects( elif value == 0x05: add( { - "kind": "pending_acknowledgement_candidate", + "kind": "conditional_ack_session_clear_candidate", "target_candidate": "selected event/pending state", - "operation_candidate": "clear selected pending flags and then clear serial session state", + "operation_candidate": ( + "when FAA2 != 0, clear FAA3/FAA2; advance F9B5 only if FAA2.bit3 was " + "set from queued-report state; selector 0x0040 stages no response" + ), + "selector_without_response_hex": _h16(0x0040), + "requires": ["FAA2 != 0"], + "fallthrough_when": "FAA2 == 0", "evidence_addresses": _dedupe_ints( _state_access_addresses(state_accesses, 0xFAA2) + _state_access_addresses(state_accesses, 0xFAA3) @@ -822,6 +978,16 @@ def _command_effects( "evidence_addresses": _addresses_in_ranges(ordered, ranges, 0xBE05, 0xBE22), } ) + add( + { + "kind": "retry_error_echo_candidate", + "source_candidate": "RX payload bytes F861-F864", + "destination_candidate": "F850=0x07, F851-F854=F861-F864 before loc_BA26", + "observed_frame_caveat": "07 80 40 20 90 2D echoes RX payload 80 40 20 90; it is not a table value", + "response_candidates": [item for item in response_ids if item == "response_at_BE6A"], + "evidence_addresses": _addresses_in_ranges(ordered, ranges, 0xBE4D, 0xBE6A), + } + ) if response_ids: add( @@ -852,6 +1018,10 @@ def _command_effect_aliases(commands: list[JsonObject]) -> list[JsonObject]: "command_value_hex": command.get("command_value_hex"), "name_candidate": command.get("name_candidate"), "summary": command.get("effect_summary", command.get("summary")), + "availability": command.get("availability"), + "availability_conditions": command.get("availability_conditions", []), + "availability_summary": command.get("availability_summary"), + "semantic_notes": command.get("semantic_notes", []), "effects": command.get("effects", []), "response_candidates": command.get("response_candidates", []), "evidence_addresses": command.get("evidence_addresses", []), @@ -938,6 +1108,7 @@ def _response_candidates(ordered: list[JsonObject]) -> list[JsonObject]: } response["schema"] = _response_schema(response) response["byte_schema"] = response["schema"]["bytes"] + response["semantic_notes"] = response["schema"].get("semantic_notes", []) responses.append(response) return responses @@ -1004,6 +1175,7 @@ def _response_schema(response: Mapping[str, Any]) -> JsonObject: for addr in item.get("evidence_addresses", []) if isinstance(addr, int) ) + semantic_notes = _response_schema_semantic_notes(response, bytes_out) return { "kind": "response_schema_candidate", "response_id": response.get("id"), @@ -1014,6 +1186,7 @@ def _response_schema(response: Mapping[str, Any]) -> JsonObject: "buffer_end": TX_STAGING_END, "buffer_end_hex": _h16(TX_STAGING_END), "bytes": bytes_out, + "semantic_notes": semantic_notes, "evidence_addresses": evidence_addresses, "evidence_addresses_hex": _hlist(evidence_addresses), "confidence": "candidate-medium" if evidence_addresses else "candidate-low", @@ -1024,6 +1197,30 @@ def _response_schema(response: Mapping[str, Any]) -> JsonObject: } +def _response_schema_semantic_notes( + response: Mapping[str, Any], + bytes_out: list[JsonObject], +) -> list[str]: + call_address = response.get("call_address") + if call_address == 0xBCFA: + for item in bytes_out: + if item.get("offset") == 2 and item.get("source_kind") == "unknown": + item["source_kind"] = "stale_or_unchanged" + item["source_expression"] = "stale/unchanged" + item["caveat"] = "BCD7 does not freshly write F852 before loc_BA26." + return [ + "Command 1 BCD7 staging is odd: F850=0x04; F851 is written from F861 then overwritten by F862.", + "The primary table word is read from E000 + 2*selector; F854/F853 receive low/high value bytes.", + "F852 may be stale or unchanged in this handler; avoid a fixed 04 00 QQ hi lo response shape.", + ] + if call_address == 0xBE6A: + return [ + "loc_BE4D retry/error echo stages F850=0x07 and copies F861-F864 into F851-F854 before loc_BA26.", + "Observed 07 80 40 20 90 2D echoes RX payload bytes 80 40 20 90; it is not a table-derived value.", + ] + return [] + + def _response_schemas(responses: list[JsonObject]) -> list[JsonObject]: return [ response["schema"] @@ -1203,6 +1400,7 @@ def _response_builder_aliases(responses: list[JsonObject]) -> list[JsonObject]: "call_address": response.get("call_address"), "call_address_hex": response.get("call_address_hex"), "writes": writes, + "semantic_notes": response.get("semantic_notes", []), "evidence_addresses": response.get("evidence_addresses", []), "evidence_addresses_hex": response.get("evidence_addresses_hex", []), "confidence": response.get("confidence", "medium"), @@ -1571,6 +1769,7 @@ def _retry_error_model(ordered: list[JsonObject], responses: list[JsonObject]) - retry_evidence = _dedupe_ints( [int(ins["address"]) for ins in retry_path if _has_ref_in_range(ins, 0xFAA2, 0xFAA6)] + [int(ins["address"]) for ins in retry_path if _has_ref_in_range(ins, TX_STAGING_START, TX_STAGING_END)] + + [int(ins["address"]) for ins in retry_path if _has_ref_in_range(ins, RX_FRAME_START + 1, RX_FRAME_START + 4)] + [int(ins["address"]) for ins in retry_path if _is_send_builder_call(ins)] + _response_evidence_addresses(responses, checksum_error_response) ) @@ -1606,8 +1805,16 @@ def _retry_error_model(ordered: list[JsonObject], responses: list[JsonObject]) - "response_candidates": checksum_error_response, "summary": ( "Candidate retry path clears/consults serial flags, increments FAA6, compares it " - "with 2, and when still below the apparent limit stages a command 0x07 response." + "with 2, and when still below the apparent limit enters loc_BE4D to stage a " + "command 0x07 retry/error echo of RX payload bytes F861-F864." ), + "echo_response_candidate": { + "entry_label": "loc_BE4D", + "entry_address": 0xBE4D, + "entry_address_hex": _h16(0xBE4D), + "staging_candidate": "F850=0x07; F851-F854=F861-F864", + "observed_frame_caveat": "07 80 40 20 90 2D echoes RX payload 80 40 20 90 and is not a table value.", + }, "evidence_addresses": retry_evidence, "evidence_addresses_hex": _hlist(retry_evidence), "confidence": "candidate-medium" if retry_counter_evidence else "candidate-low", @@ -1646,7 +1853,7 @@ def _gate_queue_model(ordered: list[JsonObject], commands: list[JsonObject]) -> command_ack_values = [ int(command["command_value"]) for command in commands - if command.get("command_value") in {0x05, 0x06} + if command.get("command_value") == 0x05 ] if not evidence and not command_ack_values: return None @@ -1774,13 +1981,18 @@ def _gate_queue_model(ordered: list[JsonObject], commands: list[JsonObject]) -> }, { "name": "host_ack_can_advance_queue", - "summary": "Commands 0x05/0x06 are modeled as acknowledgement paths that can clear pending state or advance F9B5.", + "summary": ( + "Command 0x05 is a continuation-only ACK/session clear path: it clears " + "FAA3/FAA2 and advances F9B5 only when queued-report FAA2.bit3 was set. " + "Selector 0x0040 has no response; if FAA2 == 0 the command falls through " + "instead of doing ACK work." + ), "command_values_hex": [_h16(value, width=2) for value in command_ack_values], - "state_addresses_hex": [_h16(0xF9B5)], + "state_addresses_hex": [_h16(0xFAA2), _h16(0xFAA3), _h16(0xF9B5)], "evidence_addresses": _dedupe_ints( addr for command in commands - if command.get("command_value") in {0x05, 0x06} + if command.get("command_value") == 0x05 for addr in command.get("evidence_addresses", []) if isinstance(addr, int) ), @@ -2283,9 +2495,26 @@ def _response_window(ordered: list[JsonObject], call_index: int) -> list[JsonObj for index in range(call_index - 1, max(-1, call_index - 48), -1): candidate = ordered[index] mnemonic = str(candidate.get("mnemonic", "")).upper() - if mnemonic in {"RTS", "RTE"}: + root = _mnemonic_root(mnemonic) + if root in {"RTS", "RTE"}: break - if candidate.get("kind") == "branch" and mnemonic != "BSR": + if root in { + "BRA", + "BEQ", + "BNE", + "BCS", + "BCC", + "BHI", + "BLS", + "BGE", + "BLT", + "BGT", + "BLE", + "BMI", + "BPL", + "BVS", + "BVC", + }: break start = index return ordered[start:call_index] diff --git a/tests/test_serial_pseudocode.py b/tests/test_serial_pseudocode.py index 93e9212..bd334d7 100644 --- a/tests/test_serial_pseudocode.py +++ b/tests/test_serial_pseudocode.py @@ -196,6 +196,60 @@ class SerialPseudocodeTest(unittest.TestCase): self.assertIn("case 0x01u:", text) self.assertIn("candidate_read_value(logical_index, value);", text) + def test_protocol_pseudocode_surfaces_dispatcher_split(self): + payload = candidate_payload() + payload["instructions"] = [ + {"address": 0xBC08, "mnemonic": "MOV:G.B", "operands": "@H'F860, R0", "references": [{"address": 0xF860}], "targets": []}, + {"address": 0xBC0C, "mnemonic": "AND.B", "operands": "#H'07, R0", "references": [], "targets": []}, + {"address": 0xBC0F, "mnemonic": "TST.B", "operands": "@H'FAA2", "references": [{"address": 0xFAA2}], "targets": []}, + {"address": 0xBC13, "mnemonic": "BNE", "operands": "loc_BC3A", "references": [], "targets": [0xBC3A]}, + {"address": 0xBC19, "mnemonic": "BTST.B", "operands": "#7, @H'F861", "references": [{"address": 0xF861}], "targets": []}, + {"address": 0xBC20, "mnemonic": "CMP:E", "operands": "#H'00, R0", "references": [], "targets": []}, + {"address": 0xBC22, "mnemonic": "BEQ", "operands": "loc_BC69", "references": [], "targets": [0xBC69]}, + {"address": 0xBC24, "mnemonic": "CMP:E", "operands": "#H'01, R0", "references": [], "targets": []}, + {"address": 0xBC26, "mnemonic": "BEQ", "operands": "loc_BCD7", "references": [], "targets": [0xBCD7]}, + {"address": 0xBC29, "mnemonic": "CMP:E", "operands": "#H'02, R0", "references": [], "targets": []}, + {"address": 0xBC2B, "mnemonic": "BEQ", "operands": "loc_BD04", "references": [], "targets": [0xBD04]}, + {"address": 0xBC2E, "mnemonic": "CMP:E", "operands": "#H'07, R0", "references": [], "targets": []}, + {"address": 0xBC30, "mnemonic": "BEQ", "operands": "loc_BE05", "references": [], "targets": [0xBE05]}, + {"address": 0xBC45, "mnemonic": "CMP:E", "operands": "#H'04, R0", "references": [], "targets": []}, + {"address": 0xBC47, "mnemonic": "BEQ", "operands": "loc_BD0E", "references": [], "targets": [0xBD0E]}, + {"address": 0xBC4A, "mnemonic": "CMP:E", "operands": "#H'05, R0", "references": [], "targets": []}, + {"address": 0xBC4C, "mnemonic": "BEQ", "operands": "loc_BD80", "references": [], "targets": [0xBD80]}, + {"address": 0xBC4F, "mnemonic": "CMP:E", "operands": "#H'06, R0", "references": [], "targets": []}, + {"address": 0xBC51, "mnemonic": "BEQ", "operands": "loc_BDDB", "references": [], "targets": [0xBDDB]}, + {"address": 0xBC54, "mnemonic": "CMP:E", "operands": "#H'07, R0", "references": [], "targets": []}, + {"address": 0xBC56, "mnemonic": "BEQ", "operands": "loc_BE05", "references": [], "targets": [0xBE05]}, + ] + + text = generate_serial_pseudocode(payload) + + self.assertIn("dispatcher split: FAA2 == 0 accepts initial/idle commands H'00, H'01, H'02, H'07; FAA2 != 0 accepts continuation commands H'04, H'05, H'06, H'07", text) + self.assertIn("bool session_active = MEM8[0xFAA2u] != 0u;", text) + self.assertIn("Initial/idle dispatcher", text) + self.assertIn("Continuation dispatcher", text) + self.assertIn("availability: valid checksum/no RX physical error && FAA2 == 0 && F861.bit7 == 0", text) + + def test_protocol_pseudocode_surfaces_retry_echo_schema(self): + payload = candidate_payload() + payload["instructions"] = [ + {"address": 0xBE4B, "mnemonic": "BRA", "operands": "loc_BE6D", "references": [], "targets": [0xBE6D]}, + {"address": 0xBE4D, "mnemonic": "MOV:G.B", "operands": "#H'07, @H'F850", "references": [{"address": 0xF850}], "targets": []}, + {"address": 0xBE52, "mnemonic": "MOV:G.B", "operands": "@H'F861, R0", "references": [{"address": 0xF861}], "targets": []}, + {"address": 0xBE56, "mnemonic": "MOV:G.B", "operands": "R0, @H'F851", "references": [{"address": 0xF851}], "targets": []}, + {"address": 0xBE5A, "mnemonic": "MOV:G.W", "operands": "@H'F862, R0", "references": [{"address": 0xF862}], "targets": []}, + {"address": 0xBE5E, "mnemonic": "MOV:G.W", "operands": "R0, @H'F852", "references": [{"address": 0xF852}], "targets": []}, + {"address": 0xBE62, "mnemonic": "MOV:G.B", "operands": "@H'F864, R0", "references": [{"address": 0xF864}], "targets": []}, + {"address": 0xBE66, "mnemonic": "MOV:G.B", "operands": "R0, @H'F854", "references": [{"address": 0xF854}], "targets": []}, + {"address": 0xBE6A, "mnemonic": "BSR", "operands": "loc_BA26", "references": [], "targets": [0xBA26]}, + ] + + text = generate_serial_pseudocode(payload) + + self.assertIn("response_at_BE6A: byte0=0x07; byte1=rx[1]; byte2=rx[2]; byte3=rx[3]; byte4=rx[4]", text) + self.assertIn("Observed 07 80 40 20 90 2D echoes RX payload bytes 80 40 20 90", text) + self.assertIn("not a table-derived value", text) + def test_surfaces_refined_semantic_candidates(self): analysis = { "protocol_semantics": [ diff --git a/tests/test_serial_semantics.py b/tests/test_serial_semantics.py index 0c5b8f0..67b4d18 100644 --- a/tests/test_serial_semantics.py +++ b/tests/test_serial_semantics.py @@ -288,6 +288,8 @@ class SerialSemanticsTest(unittest.TestCase): command_1_text = semantic_text(command_1) self.assertIn("read", command_1_text) self.assertIn("response", command_1_text) + self.assertIn("initial/idle", command_1_text) + self.assertIn("f852", command_1_text) command_6 = command_item(effects, 0x06) self.assertIsNotNone(command_6) @@ -295,6 +297,13 @@ class SerialSemanticsTest(unittest.TestCase): self.assertIn("write", command_6_text) self.assertIn("secondary_value_table", command_6_text) + command_5 = command_item(effects, 0x05) + self.assertIsNotNone(command_5) + command_5_text = semantic_text(command_5) + self.assertIn("faa2 != 0", command_5_text) + self.assertIn("0040", command_5_text) + self.assertIn("no response", command_5_text) + def test_planned_response_schema_tracks_immediates_and_rx_copies(self): semantics = only_semantics(self, planned_semantics_payload()) @@ -391,10 +400,105 @@ class SerialSemanticsTest(unittest.TestCase): self.assertIn("be9e", gate_text) self.assertIn("bed5", gate_text) self.assertIn("f9c5", gate_text) - self.assertIn("commands 0x05", gate_text) - self.assertIn("0x06", gate_text) + self.assertIn("command 0x05", gate_text) + self.assertIn("selector 0x0040 has no response", gate_text) + self.assertIn("faa2 == 0", gate_text) self.assertIn("not rom constants", gate_text) + def test_actual_dispatch_split_marks_initial_and_continuation_commands(self): + semantics = only_semantics( + self, + base_payload( + [ + instruction(0xBC08, "MOV:G.B", "@H'F860, R0", [0xF860]), + instruction(0xBC0C, "AND.B", "#H'07, R0"), + instruction(0xBC0F, "TST.B", "@H'FAA2", [0xFAA2]), + instruction(0xBC13, "BNE", "loc_BC3A", targets=[0xBC3A]), + instruction(0xBC19, "BTST.B", "#7, @H'F861", [0xF861]), + instruction(0xBC20, "CMP:E", "#H'00, R0"), + instruction(0xBC22, "BEQ", "loc_BC69", targets=[0xBC69]), + instruction(0xBC24, "CMP:E", "#H'01, R0"), + instruction(0xBC26, "BEQ", "loc_BCD7", targets=[0xBCD7]), + instruction(0xBC29, "CMP:E", "#H'02, R0"), + instruction(0xBC2B, "BEQ", "loc_BD04", targets=[0xBD04]), + instruction(0xBC2E, "CMP:E", "#H'07, R0"), + instruction(0xBC30, "BEQ", "loc_BE05", targets=[0xBE05]), + instruction(0xBC45, "CMP:E", "#H'04, R0"), + instruction(0xBC47, "BEQ", "loc_BD0E", targets=[0xBD0E]), + instruction(0xBC4A, "CMP:E", "#H'05, R0"), + instruction(0xBC4C, "BEQ", "loc_BD80", targets=[0xBD80]), + instruction(0xBC4F, "CMP:E", "#H'06, R0"), + instruction(0xBC51, "BEQ", "loc_BDDB", targets=[0xBDDB]), + instruction(0xBC54, "CMP:E", "#H'07, R0"), + instruction(0xBC56, "BEQ", "loc_BE05", targets=[0xBE05]), + ] + ), + ) + + split = semantics["command_dispatch"]["state_split"] + self.assertEqual(split["initial_idle_commands"], [0, 1, 2, 7]) + self.assertEqual(split["continuation_commands"], [4, 5, 6, 7]) + + command_1 = command_item(semantics["commands"], 0x01) + self.assertIn("FAA2 == 0", command_1["availability_conditions"]) + self.assertIn("F861.bit7 == 0", command_1["availability_conditions"]) + + command_5 = command_item(semantics["commands"], 0x05) + self.assertIn("FAA2 != 0", command_5["availability_conditions"]) + + def test_actual_bcd7_command1_response_marks_f852_stale(self): + semantics = only_semantics( + self, + base_payload( + [ + instruction(0xBCD4, "BRA", "loc_BE6F", targets=[0xBE6F]), + instruction(0xBCD7, "MOV:G.B", "#H'04, @H'F850", [0xF850]), + instruction(0xBCDC, "MOV:G.B", "@H'F861, R0", [0xF861]), + instruction(0xBCE0, "MOV:G.B", "R0, @H'F851", [0xF851]), + instruction(0xBCE4, "MOV:G.B", "@H'F862, R0", [0xF862]), + instruction(0xBCE8, "MOV:G.B", "R0, @H'F851", [0xF851]), + instruction(0xBCEC, "MOV:G.W", "@(-H'2000,R4), R0"), + instruction(0xBCF0, "MOV:G.B", "R0, @H'F854", [0xF854]), + instruction(0xBCF4, "SWAP.B", "R0"), + instruction(0xBCF6, "MOV:G.B", "R0, @H'F853", [0xF853]), + instruction(0xBCFA, "BSR", "loc_BA26", targets=[0xBA26]), + ] + ), + ) + + schema = semantics["response_schema"][0] + byte_sources = {item["offset"]: item["source_expression"] for item in schema["bytes"]} + self.assertEqual(byte_sources[0], "0x04") + self.assertEqual(byte_sources[1], "rx[2]") + self.assertEqual(byte_sources[2], "stale/unchanged") + self.assertEqual(byte_sources[3], "primary_value_table_candidate") + self.assertEqual(byte_sources[4], "primary_value_table_candidate") + self.assertIn("avoid a fixed 04 00 qq hi lo", semantic_text(schema)) + + def test_actual_be4d_retry_error_echo_copies_rx_payload(self): + semantics = only_semantics( + self, + base_payload( + [ + instruction(0xBE4B, "BRA", "loc_BE6D", targets=[0xBE6D]), + instruction(0xBE4D, "MOV:G.B", "#H'07, @H'F850", [0xF850]), + instruction(0xBE52, "MOV:G.B", "@H'F861, R0", [0xF861]), + instruction(0xBE56, "MOV:G.B", "R0, @H'F851", [0xF851]), + instruction(0xBE5A, "MOV:G.W", "@H'F862, R0", [0xF862]), + instruction(0xBE5E, "MOV:G.W", "R0, @H'F852", [0xF852]), + instruction(0xBE62, "MOV:G.B", "@H'F864, R0", [0xF864]), + instruction(0xBE66, "MOV:G.B", "R0, @H'F854", [0xF854]), + instruction(0xBE6A, "BSR", "loc_BA26", targets=[0xBA26]), + ] + ), + ) + + schema = semantics["response_schema"][0] + byte_sources = {item["offset"]: item["source_expression"] for item in schema["bytes"]} + self.assertEqual(byte_sources, {0: "0x07", 1: "rx[1]", 2: "rx[2]", 3: "rx[3]", 4: "rx[4]"}) + self.assertIn("80 40 20 90", semantic_text(schema)) + self.assertIn("not a table", semantic_text(schema)) + def test_timer_interrupt_model_surfaces_frt2_idle_heartbeat_counter(self): semantics = only_semantics( self,