import unittest from h8536.model import Instruction from h8536.sci_protocol import ( analyze_sci_protocol, sci_protocol_comment_for_instruction, sci_protocol_json_payload, sci_protocol_metadata_for_instruction, ) def ins( address: int, mnemonic: str, operands: str = "", references: list[int] | None = None, kind: str = "normal", targets: list[int] | None = None, ) -> Instruction: return Instruction( address, b"", mnemonic, operands, kind=kind, targets=targets or [], references=references or [], ) class SciProtocolTest(unittest.TestCase): def test_tdre_polling_loop_is_wait_for_transmit_data_register_empty(self): instructions = { 0x2000: ins(0x2000, "BTST.B", "#7, @SCI1_SSR", references=[0xFEDC]), 0x2004: ins(0x2004, "BEQ", "loc_2000", kind="branch", targets=[0x2000]), } analysis = analyze_sci_protocol(instructions) self.assertIn( "wait for SCI1 transmit data register empty (TDRE=1)", sci_protocol_comment_for_instruction(analysis, 0x2000), ) self.assertIn( "repeat SCI1 transmit-empty wait while TDRE=0", sci_protocol_comment_for_instruction(analysis, 0x2004), ) metadata = sci_protocol_metadata_for_instruction(analysis, 0x2000)[0] self.assertEqual(metadata["action"], "wait_for_tdre") self.assertEqual(metadata["branch_address"], 0x2004) def test_write_to_tdr_is_rs232_sci_transmit_byte(self): instructions = { 0x2100: ins(0x2100, "MOV:G.B", "R0, @SCI1_TDR", references=[0xFEDB]), } analysis = analyze_sci_protocol(instructions) self.assertEqual( sci_protocol_comment_for_instruction(analysis, 0x2100), "write RS232/SCI byte to SCI1 TDR for transmission", ) self.assertEqual(analysis["channels"]["SCI1"]["events"][0]["register"], "TDR") def test_scr_tie_bit_set_and_clear_enable_disable_tx_interrupt(self): instructions = { 0x2200: ins(0x2200, "BSET.B", "#7, @SCI1_SCR", references=[0xFEDA]), 0x2204: ins(0x2204, "BCLR.B", "#7, @SCI1_SCR", references=[0xFEDA]), } analysis = analyze_sci_protocol(instructions) self.assertEqual( sci_protocol_comment_for_instruction(analysis, 0x2200), "enable SCI1 TX interrupt (TIE); gates TXI when hardware sets TDRE", ) self.assertEqual( sci_protocol_comment_for_instruction(analysis, 0x2204), "disable SCI1 TX interrupt (TIE); gates TXI when hardware sets TDRE", ) def test_sci1_transmit_path_comments_tdr_tdre_and_tie_timing(self): instructions = { 0xBA72: ins(0xBA72, "MOV:G.B", "R0, @SCI1_TDR", references=[0xFEDB]), 0xBA7B: ins(0xBA7B, "BCLR.B", "#7, @SCI1_SSR", references=[0xFEDC]), 0xBA7F: ins(0xBA7F, "BSET.B", "#7, @SCI1_SCR", references=[0xFEDA]), 0xBAB5: ins(0xBAB5, "MOV:G.B", "R1, @SCI1_TDR", references=[0xFEDB]), 0xBABB: ins(0xBABB, "BCLR.B", "#7, @SCI1_SSR", references=[0xFEDC]), } analysis = analyze_sci_protocol(instructions) self.assertEqual( sci_protocol_comment_for_instruction(analysis, 0xBA72), "write RS232/SCI byte to SCI1 TDR for transmission", ) self.assertEqual( sci_protocol_comment_for_instruction(analysis, 0xBA7B), "clear SCI1 TDRE after TDR write; TXI can fire again when hardware reasserts TDRE", ) self.assertEqual( sci_protocol_comment_for_instruction(analysis, 0xBA7F), "enable SCI1 TX interrupt (TIE); gates TXI when hardware sets TDRE", ) self.assertEqual( sci_protocol_comment_for_instruction(analysis, 0xBAB5), "write RS232/SCI byte to SCI1 TDR for transmission", ) self.assertEqual( sci_protocol_comment_for_instruction(analysis, 0xBABB), "clear SCI1 TDRE after TDR write; TXI can fire again when hardware reasserts TDRE", ) def test_receive_path_clears_rdrf_then_reads_received_byte(self): instructions = { 0x2300: ins(0x2300, "BCLR.B", "#6, @SCI1_SSR", references=[0xFEDC]), 0x2304: ins(0x2304, "MOV:G.B", "@SCI1_RDR, R0", references=[0xFEDD]), } analysis = analyze_sci_protocol(instructions) self.assertEqual( sci_protocol_comment_for_instruction(analysis, 0x2300), "clear SCI1 RDRF with SSR R/(W)* semantics: write 0 clears latched hardware flag, write 1 preserves hardware-owned state", ) self.assertEqual( sci_protocol_comment_for_instruction(analysis, 0x2304), "read SCI1 received byte from RDR", ) def test_receive_error_handler_clears_overrun_framing_and_parity_errors(self): instructions = { 0x2400: ins(0x2400, "BCLR.B", "#5, @SCI1_SSR", references=[0xFEDC]), 0x2404: ins(0x2404, "BCLR.B", "#4, @SCI1_SSR", references=[0xFEDC]), 0x2408: ins(0x2408, "BCLR.B", "#3, @SCI1_SSR", references=[0xFEDC]), } analysis = analyze_sci_protocol(instructions) self.assertEqual( sci_protocol_comment_for_instruction(analysis, 0x2400), "clear SCI1 ORER with SSR R/(W)* semantics: write 0 clears latched hardware flag, write 1 preserves hardware-owned state", ) self.assertEqual( sci_protocol_comment_for_instruction(analysis, 0x2404), "clear SCI1 FER with SSR R/(W)* semantics: write 0 clears latched hardware flag, write 1 preserves hardware-owned state", ) self.assertEqual( sci_protocol_comment_for_instruction(analysis, 0x2408), "clear SCI1 PER with SSR R/(W)* semantics: write 0 clears latched hardware flag, write 1 preserves hardware-owned state", ) def test_immediate_scr_write_reports_protocol_control_bits(self): instructions = { 0x2500: ins(0x2500, "MOV:G.B", "#H'B0, @SCI2_SCR", references=[0xFEF2]), } analysis = analyze_sci_protocol(instructions) comment = sci_protocol_comment_for_instruction(analysis, 0x2500) self.assertIn( "enable SCI2 TX interrupt (TIE); gates TXI when hardware sets TDRE", comment, ) self.assertIn("disable SCI2 receive and receive-error interrupts (RIE)", comment) self.assertIn("enable SCI2 transmitter (TE)", comment) self.assertIn("enable SCI2 receiver (RE)", comment) def test_json_payload_keeps_events_without_instruction_index(self): instructions = { 0x2600: ins(0x2600, "MOV:G.B", "R0, @SCI2_TDR", references=[0xFEF3]), } analysis = analyze_sci_protocol(instructions) payload = sci_protocol_json_payload(analysis) self.assertIn("manual_references", payload) self.assertEqual(payload["events"][0]["action"], "write_tdr") self.assertEqual(payload["channels"]["SCI2"]["events"][0]["register"], "TDR") if __name__ == "__main__": unittest.main()