import unittest

from h8536.emulator import H8536Emulator


def rom_with_reset(*, reset: int = 0x1000, size: int = 0x1100) -> bytearray:
    rom = bytearray([0xFF] * size)
    rom[0:2] = reset.to_bytes(2, "big")
    return rom


class EmulatorMovFlagTest(unittest.TestCase):
    def test_mov_e_byte_zero_sets_z_for_beq_and_preserves_c(self):
        rom = rom_with_reset()
        rom[0x1000:0x1002] = b"\x50\x00"  # MOV:E.B #H'00, R0
        rom[0x1002:0x1004] = b"\x27\x02"  # BEQ H'1006
        rom[0x1004:0x1006] = b"\x51\x01"  # MOV:E.B #H'01, R1
        rom[0x1006:0x1008] = b"\x52\x02"  # MOV:E.B #H'02, R2

        emulator = H8536Emulator(bytes(rom))
        emulator.cpu.c = True
        emulator.cpu.v = True
        emulator.step()

        self.assertTrue(emulator.cpu.z)
        self.assertFalse(emulator.cpu.n)
        self.assertFalse(emulator.cpu.v)
        self.assertTrue(emulator.cpu.c)

        emulator.run(max_steps=2)

        self.assertEqual(emulator.cpu.regs[1], 0)
        self.assertEqual(emulator.cpu.regs[2], 2)
        self.assertEqual(emulator.cpu.pc, 0x1008)

    def test_mov_e_byte_one_clears_z_for_beq_and_preserves_c(self):
        rom = rom_with_reset()
        rom[0x1000:0x1002] = b"\x50\x01"  # MOV:E.B #H'01, R0
        rom[0x1002:0x1004] = b"\x27\x02"  # BEQ H'1006
        rom[0x1004:0x1006] = b"\x51\x01"  # MOV:E.B #H'01, R1
        rom[0x1006:0x1008] = b"\x52\x02"  # MOV:E.B #H'02, R2

        emulator = H8536Emulator(bytes(rom))
        emulator.cpu.c = True
        emulator.cpu.v = True
        emulator.step()

        self.assertFalse(emulator.cpu.z)
        self.assertFalse(emulator.cpu.n)
        self.assertFalse(emulator.cpu.v)
        self.assertTrue(emulator.cpu.c)

        emulator.run(max_steps=2)

        self.assertEqual(emulator.cpu.regs[1], 1)
        self.assertEqual(emulator.cpu.regs[2], 0)
        self.assertEqual(emulator.cpu.pc, 0x1006)

    def test_mov_i_word_immediate_sets_word_flags_and_preserves_c(self):
        rom = rom_with_reset()
        rom[0x1000:0x1003] = b"\x58\x80\x00"  # MOV:I.W #H'8000, R0
        rom[0x1003:0x1006] = b"\x59\x00\x00"  # MOV:I.W #H'0000, R1

        emulator = H8536Emulator(bytes(rom))
        emulator.cpu.c = True
        emulator.cpu.v = True
        emulator.step()

        self.assertEqual(emulator.cpu.regs[0], 0x8000)
        self.assertFalse(emulator.cpu.z)
        self.assertTrue(emulator.cpu.n)
        self.assertFalse(emulator.cpu.v)
        self.assertTrue(emulator.cpu.c)

        emulator.step()

        self.assertEqual(emulator.cpu.regs[1], 0)
        self.assertTrue(emulator.cpu.z)
        self.assertFalse(emulator.cpu.n)
        self.assertFalse(emulator.cpu.v)
        self.assertTrue(emulator.cpu.c)

    def test_mulxu_byte_immediate_writes_word_result_and_clears_carry(self):
        rom = rom_with_reset()
        rom[0x1000:0x1002] = b"\x53\x12"  # MOV:E.B #H'12, R3
        rom[0x1002:0x1005] = b"\x04\x10\xAB"  # MULXU.B #H'10, R3

        emulator = H8536Emulator(bytes(rom))
        emulator.cpu.c = True
        emulator.run(max_steps=2)

        self.assertEqual(emulator.cpu.regs[3], 0x0120)
        self.assertFalse(emulator.cpu.z)
        self.assertFalse(emulator.cpu.n)
        self.assertFalse(emulator.cpu.v)
        self.assertFalse(emulator.cpu.c)

    def test_not_byte_memory_updates_logic_flags_and_preserves_carry(self):
        rom = rom_with_reset()
        rom[0x1000:0x1005] = b"\x15\xF6\x80\x15\x00"  # NOT.B @H'F680, then NOP

        emulator = H8536Emulator(bytes(rom))
        emulator.memory.write8(0xF680, 0xFF)
        emulator.cpu.c = True
        emulator.cpu.v = True
        emulator.step()

        self.assertEqual(emulator.memory.read8(0xF680), 0x00)
        self.assertTrue(emulator.cpu.z)
        self.assertFalse(emulator.cpu.n)
        self.assertFalse(emulator.cpu.v)
        self.assertTrue(emulator.cpu.c)


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