import unittest from h8536.dataflow import analyze_dataflow, state_for_instruction from h8536.model import Instruction def reg_after(analysis, address, register): return analysis["instructions"][address]["after"]["registers"][register] def reg_before(analysis, address, register): return analysis["instructions"][address]["before"]["registers"][register] def control_after(analysis, address, register): return analysis["instructions"][address]["after"]["control"][register] class DataflowTest(unittest.TestCase): def test_tracks_immediate_load_copy_and_simple_arithmetic(self): instructions = { 0x0100: Instruction(0x0100, b"\x58\x02\x00", "MOV:I.W", "#H'0200, R0"), 0x0103: Instruction(0x0103, b"\xA0\x81", "MOV:G.W", "R0, R1"), 0x0105: Instruction(0x0105, b"\xA1\x08", "ADD:Q.W", "#1, R1"), 0x0107: Instruction(0x0107, b"\x0C\x00\x02\x31", "SUB.W", "#H'0002, R1"), } analysis = analyze_dataflow(instructions) self.assertEqual(reg_after(analysis, 0x0100, "R0")["value"], 0x0200) self.assertEqual(reg_after(analysis, 0x0100, "R0")["width"], 16) self.assertEqual(reg_before(analysis, 0x0103, "R0")["value"], 0x0200) self.assertEqual(reg_after(analysis, 0x0103, "R1")["value"], 0x0200) self.assertEqual(reg_after(analysis, 0x0105, "R1")["value"], 0x0201) self.assertEqual(reg_after(analysis, 0x0107, "R1")["value"], 0x01FF) def test_tracks_byte_immediates_without_promising_word_width(self): instructions = { 0x0200: Instruction(0x0200, b"\x52\x7F", "MOV:E.B", "#H'7F, R2"), 0x0202: Instruction(0x0202, b"\xA2\x83", "MOV:G.B", "R2, R3"), 0x0204: Instruction(0x0204, b"\x58\x20\x00", "MOV:I.W", "#H'2000, R0"), 0x0207: Instruction(0x0207, b"\xD0\x84", "MOV:G.W", "@R0, R4"), } analysis = analyze_dataflow(instructions) self.assertEqual(reg_after(analysis, 0x0200, "R2")["value"], 0x7F) self.assertEqual(reg_after(analysis, 0x0200, "R2")["width"], 8) self.assertEqual(reg_after(analysis, 0x0202, "R3")["value"], 0x7F) self.assertEqual(reg_after(analysis, 0x0202, "R3")["width"], 8) self.assertEqual(reg_after(analysis, 0x0207, "R0")["value"], 0x2000) self.assertFalse(reg_after(analysis, 0x0207, "R4")["known"]) self.assertEqual(reg_after(analysis, 0x0207, "R4")["reason"], "memory_load") def test_calls_and_ambiguous_branches_do_not_leak_known_state(self): instructions = { 0x0300: Instruction(0x0300, b"\x58\x12\x34", "MOV:I.W", "#H'1234, R0"), 0x0303: Instruction(0x0303, b"\x26\x03", "BNE", "loc_0308", kind="branch", targets=[0x0308]), 0x0305: Instruction(0x0305, b"\xA0\x08", "ADD:Q.W", "#1, R0"), 0x0308: Instruction(0x0308, b"\xA0\x08", "ADD:Q.W", "#1, R0"), 0x030A: Instruction(0x030A, b"\x18\x04\x00", "JSR", "@loc_0400", kind="call", targets=[0x0400]), 0x030D: Instruction(0x030D, b"\xA0\x08", "ADD:Q.W", "#1, R0"), } analysis = analyze_dataflow(instructions) self.assertFalse(reg_before(analysis, 0x0305, "R0")["known"]) self.assertEqual(reg_before(analysis, 0x0305, "R0")["reason"], "block_entry") self.assertFalse(reg_before(analysis, 0x0308, "R0")["known"]) self.assertEqual(reg_before(analysis, 0x0308, "R0")["reason"], "block_entry") self.assertFalse(reg_after(analysis, 0x030A, "R0")["known"]) self.assertEqual(reg_after(analysis, 0x030A, "R0")["reason"], "call") self.assertFalse(reg_before(analysis, 0x030D, "R0")["known"]) def test_tracks_control_register_loads_and_stc_copies(self): instructions = { 0x0400: Instruction( 0x0400, b"\x04\xFE\x89", "LDC.B", "#H'FE, BR", writes_br=True, br_value=0xFE, ), 0x0403: Instruction(0x0403, b"\xA0\x99", "STC.B", "BR, R1"), 0x0405: Instruction(0x0405, b"\x04\x01\x48", "ORC.B", "#H'01, CCR"), } analysis = analyze_dataflow(instructions) self.assertEqual(control_after(analysis, 0x0400, "BR")["value"], 0xFE) self.assertEqual(control_after(analysis, 0x0400, "BR")["width"], 8) self.assertEqual(reg_after(analysis, 0x0403, "R1")["value"], 0xFE) self.assertFalse(control_after(analysis, 0x0405, "CCR")["known"]) def test_state_lookup_helper_returns_instruction_record(self): instructions = { 0x0500: Instruction(0x0500, b"\x58\x00\x01", "MOV:I.W", "#H'0001, R0"), } analysis = analyze_dataflow(instructions) self.assertEqual(state_for_instruction(analysis, 0x0500)["after"]["registers"]["R0"]["value"], 1) self.assertEqual(state_for_instruction(analysis, 0x9999), {}) if __name__ == "__main__": unittest.main()