import unittest

from h8536.emulator import H8536Emulator, MemoryMap, P7DDR, P7DR


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 EmulatorAddressingTest(unittest.TestCase):
    def test_p7dr_reads_external_pin_state_for_input_bits(self):
        memory = MemoryMap(b"\x00" * 4, p7_input=0xFF)

        memory.write8(P7DDR, 0x00)
        memory.write8(P7DR, 0x00)

        self.assertEqual(memory.read8(P7DR), 0xFF)

    def test_p7dr_output_bits_read_latch_while_input_bits_read_pins(self):
        memory = MemoryMap(b"\x00" * 4, p7_input=0x7F)

        memory.write8(P7DDR, 0x80)
        memory.write8(P7DR, 0x00)

        self.assertEqual(memory.read8(P7DR), 0x7F)

    def test_factory_eeprom_seed_populates_shadow_and_page_labels(self):
        rom = bytearray([0xFF] * 0xCB00)
        rom[0xC966:0xC968] = b"\x6B\x6F"
        rom[0xC974:0xC976] = b"\x80\x00"

        memory = MemoryMap(bytes(rom))
        memory.seed_factory_eeprom_and_shadow()

        self.assertEqual(memory.read16(0xF402), 0x6B6F)
        self.assertEqual(memory.p9_bus.fast_read_word(0x0010), (True, 0x8000))
        self.assertEqual(memory.p9_bus.fast_read_word(0x0110), (True, 0x8000))
        self.assertEqual(memory.p9_bus.fast_read_word(0x0002), (True, 0x6B6F))
        self.assertEqual(memory.p9_bus.fast_read_word(0x0102), (True, 0x2020))

    def test_txi_indexed_byte_load_uses_signed_word_displacement_and_full_index_register(self):
        rom = rom_with_reset()
        rom[0x1000:0x1004] = b"\xF0\xF8\x58\x80"  # MOV:G.B @(-H'07A8,R0), R0

        emulator = H8536Emulator(bytes(rom))
        emulator.cpu.regs[0] = 5
        emulator.memory.write8(0xF85D, 0xA6)
        emulator.step()

        self.assertEqual(emulator.cpu.regs[0], 0x00A6)
        self.assertEqual(emulator.cpu.pc, 0x1004)

    def test_txi_indexed_byte_load_preserves_destination_high_byte(self):
        rom = rom_with_reset()
        rom[0x1000:0x1004] = b"\xF0\xF8\x58\x80"  # MOV:G.B @(-H'07A8,R0), R0

        emulator = H8536Emulator(bytes(rom))
        emulator.cpu.regs[0] = 0x0105
        emulator.memory.write8(0xF95D, 0x4B)
        emulator.step()

        self.assertEqual(emulator.cpu.regs[0], 0x014B)

    def test_extu_byte_clears_high_byte_before_txi_indexed_load(self):
        rom = rom_with_reset()
        rom[0x1000:0x1002] = b"\xA0\x12"  # EXTU.B R0
        rom[0x1002:0x1006] = b"\xF0\xF8\x58\x80"  # MOV:G.B @(-H'07A8,R0), R0

        emulator = H8536Emulator(bytes(rom))
        emulator.cpu.regs[0] = 0x0105
        emulator.memory.write8(0xF85D, 0xC7)
        emulator.memory.write8(0xF95D, 0x99)
        emulator.run(max_steps=2)

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

    def test_byte_immediate_to_word_destination_writes_zero_extended_word(self):
        rom = rom_with_reset()
        rom[0x1000:0x1005] = b"\x1D\xF8\x70\x06\xAB"  # MOV:G.W #H'AB, @H'F870

        emulator = H8536Emulator(bytes(rom))
        emulator.memory.write16(0xF870, 0xFFFF)
        emulator.step()

        self.assertEqual(emulator.memory.read16(0xF870), 0x00AB)
        self.assertEqual(emulator.cpu.pc, 0x1005)

    def test_signed_byte_displacement_addresses_below_base(self):
        rom = rom_with_reset()
        rom[0x1000:0x1003] = b"\xE1\xFE\x81"  # MOV:G.B @(H'-02,R1), R1

        emulator = H8536Emulator(bytes(rom), p7_input=0x37)
        emulator.cpu.regs[1] = 0xFE90
        emulator.memory.write8(0xFE8E, 0x37)
        emulator.step()

        self.assertEqual(emulator.cpu.regs[1], 0xFE37)


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