import unittest

from h8536.decoder import H8536Decoder
from h8536.analysis import trace
from h8536.rom import Rom


def decode(data: list[int]):
    return H8536Decoder(Rom(bytes(data), base=0)).decode(0)


class ManualDecoderExamplesTest(unittest.TestCase):
    def test_manual_add_general_register_indirect_byte_example(self):
        instruction = decode([0xD0, 0x21])

        self.assertEqual(instruction.text, "ADD:G.B @R0, R1")
        self.assertEqual(instruction.size, 2)
        self.assertEqual(instruction.targets, [])

    def test_manual_add_general_absolute_8_word_example(self):
        instruction = decode([0x0D, 0x11, 0x21])

        self.assertEqual(instruction.text, "ADD:G.W @BR:H'11, R1")
        self.assertEqual(instruction.size, 3)
        self.assertEqual(instruction.targets, [])

    def test_reset_sequence_style_ldc_word_immediate_to_sr(self):
        instruction = decode([0x0C, 0x07, 0x00, 0x88])

        self.assertEqual(instruction.text, "LDC.W #H'0700, SR")
        self.assertEqual(instruction.size, 4)
        self.assertEqual(instruction.targets, [])

    def test_branch_targets_and_flow_metadata_for_bne_and_bra(self):
        bne = decode([0x26, 0x02])
        bra = decode([0x20, 0xFE])

        self.assertEqual(bne.text, "BNE loc_0004")
        self.assertEqual(bne.size, 2)
        self.assertEqual(bne.kind, "branch")
        self.assertEqual(bne.targets, [0x0004])
        self.assertTrue(bne.fallthrough)

        self.assertEqual(bra.text, "BRA loc_0000")
        self.assertEqual(bra.size, 2)
        self.assertEqual(bra.kind, "jump")
        self.assertEqual(bra.targets, [0x0000])
        self.assertFalse(bra.fallthrough)

    def test_jsr_rts_and_bsr_basics(self):
        jsr_abs = decode([0x18, 0x12, 0x34])
        jsr_reg = decode([0x11, 0xD8])
        rts = decode([0x19])
        bsr = decode([0x0E, 0x02])

        self.assertEqual(jsr_abs.text, "JSR @loc_1234")
        self.assertEqual(jsr_abs.size, 3)
        self.assertEqual(jsr_abs.kind, "call")
        self.assertEqual(jsr_abs.targets, [0x1234])

        self.assertEqual(jsr_reg.text, "JSR @R0")
        self.assertEqual(jsr_reg.size, 2)
        self.assertEqual(jsr_reg.kind, "call")
        self.assertEqual(jsr_reg.targets, [])

        self.assertEqual(rts.text, "RTS")
        self.assertEqual(rts.size, 1)
        self.assertEqual(rts.kind, "return")
        self.assertEqual(rts.targets, [])
        self.assertFalse(rts.fallthrough)

        self.assertEqual(bsr.text, "BSR loc_0004")
        self.assertEqual(bsr.size, 2)
        self.assertEqual(bsr.kind, "call")
        self.assertEqual(bsr.targets, [0x0004])

    def test_movfpe_movtpe_extended_general_form(self):
        movfpe = decode([0x05, 0x80, 0x00, 0x82])
        movtpe = decode([0x05, 0x80, 0x00, 0x92])

        self.assertEqual(movfpe.text, "MOVFPE.B @BR:H'80, R2")
        self.assertEqual(movfpe.size, 4)
        self.assertEqual(movfpe.targets, [])

        self.assertEqual(movtpe.text, "MOVTPE.B R2, @BR:H'80")
        self.assertEqual(movtpe.size, 4)
        self.assertEqual(movtpe.targets, [])

    def test_trace_tracks_ldc_immediate_to_br_for_short_absolute_operands(self):
        rom = Rom(bytes([0x04, 0xFE, 0x89, 0x60, 0xFC]), base=0)
        decoder = H8536Decoder(rom)

        instructions = trace(decoder, [0], 0, len(rom.data))

        self.assertEqual(instructions[0].text, "LDC.B #H'FE, BR")
        self.assertTrue(instructions[0].writes_br)
        self.assertEqual(instructions[0].br_value, 0xFE)
        self.assertEqual(instructions[3].text, "MOV:L.B @SYSCR1, R0")
        self.assertEqual(instructions[3].references, [0xFEFC])


if __name__ == "__main__":
    unittest.main()
