diff --git a/conftest.py b/conftest.py new file mode 100644 index 0000000..db21658 --- /dev/null +++ b/conftest.py @@ -0,0 +1,3 @@ +def pytest_report_teststatus(report, config): + if report.passed and report.when == "call": + return report.outcome, "", report.outcome.upper() diff --git a/gbso/insn.py b/gbso/insn.py index 8b25aa9..cbc624d 100644 --- a/gbso/insn.py +++ b/gbso/insn.py @@ -22,6 +22,7 @@ class LD_R_R(Insn): def exec(self, cpu: CPU) -> None: cpu.set_reg8(self.dst, cpu.get_reg8(self.src)) + cpu.cycles += 4 def pretty(self) -> str: return f"LD {self.dst}, {self.src}" @@ -34,6 +35,7 @@ class LD_R_N8(Insn): def exec(self, cpu: CPU) -> None: cpu.set_reg8(self.dst, self.imm) + cpu.cycles += 8 def pretty(self) -> str: return f"LD {self.dst}, {hex(self.imm)}" @@ -45,6 +47,7 @@ class LD_R_HL(Insn): def exec(self, cpu: CPU) -> None: cpu.set_reg8(self.dst, cpu.get_mem8(cpu.get_reg16(R16.HL))) + cpu.cycles += 8 def pretty(self) -> str: return f"LD {self.dst}, (HL)" @@ -55,7 +58,8 @@ class LD_HL_R(Insn): src: R8 def exec(self, cpu: CPU) -> None: - cpu.set_mem8(cpu.get_reg16(R16.HL), cpu.get_reg8(self.src)) + cpu.deref_hl_set(cpu.get_reg8(self.src)) + cpu.cycles += 8 def pretty(self) -> str: return f"LD (HL), {self.src}" @@ -67,6 +71,7 @@ class LD_HL_N(Insn): def exec(self, cpu: CPU) -> None: cpu.set_mem8(cpu.get_reg16(R16.HL), self.imm) + cpu.cycles += 12 def pretty(self) -> str: return f"LD (HL), {hex(self.imm & 0xff)}" @@ -76,6 +81,7 @@ class LD_HL_N(Insn): class LD_A_BC(Insn): def exec(self, cpu: CPU) -> None: cpu.set_reg8(R8.A, cpu.get_mem8(cpu.get_reg16(R16.BC))) + cpu.cycles += 8 def pretty(self) -> str: return "LD A, (BC)" @@ -85,6 +91,7 @@ class LD_A_BC(Insn): class LD_A_DE(Insn): def exec(self, cpu: CPU) -> None: cpu.set_reg8(R8.A, cpu.get_mem8(cpu.get_reg16(R16.DE))) + cpu.cycles += 8 def pretty(self) -> str: return "LD A, (DE)" @@ -96,6 +103,7 @@ class LD_A_NN(Insn): def exec(self, cpu: CPU) -> None: cpu.set_reg8(R8.A, cpu.get_mem8(self.nn)) + cpu.cycles += 16 def pretty(self) -> str: return f"LD A, ({hex(self.nn)})" @@ -105,12 +113,20 @@ class LD_A_NN(Insn): class LD_BC_A(Insn): def exec(self, cpu: CPU) -> None: cpu.set_mem8(cpu.get_reg16(R16.BC), cpu.get_reg8(R8.A)) + cpu.cycles += 8 + + def pretty(self) -> str: + return "LD (BC), A" @dataclass class LD_DE_A(Insn): def exec(self, cpu: CPU) -> None: cpu.set_mem8(cpu.get_reg16(R16.DE), cpu.get_reg8(R8.A)) + cpu.cycles += 8 + + def pretty(self) -> str: + return "LD (DE), A" @dataclass @@ -119,6 +135,10 @@ class LD_NN_A(Insn): def exec(self, cpu: CPU) -> None: cpu.set_mem8(self.nn, cpu.get_reg8(R8.A)) + cpu.cycles += 16 + + def pretty(self) -> str: + return f"LD ({hex(self.nn)}), A" @dataclass @@ -127,6 +147,10 @@ class LD_A_FF_N(Insn): def exec(self, cpu: CPU) -> None: cpu.set_reg8(R8.A, cpu.get_mem8(0xFF00 + self.n)) + cpu.cycles += 12 + + def pretty(self) -> str: + return f"LD A, (0xFF00 + {hex(self.n)})" @dataclass @@ -135,18 +159,30 @@ class LD_FF_N_A(Insn): def exec(self, cpu: CPU) -> None: cpu.set_mem8(0xFF00 + self.n, cpu.get_reg8(R8.A)) + cpu.cycles += 12 + + def pretty(self) -> str: + return f"LD (0xFF00 + {hex(self.n)}), A" @dataclass class LD_A_FF_C(Insn): def exec(self, cpu: CPU) -> None: cpu.set_reg8(R8.A, cpu.get_mem8(0xFF00 + cpu.get_reg8(R8.C))) + cpu.cycles += 8 + + def pretty(self) -> str: + return "LD A, (0xFF00 + C)" @dataclass class LD_FF_C_A(Insn): def exec(self, cpu: CPU) -> None: cpu.set_mem8(0xFF00 + cpu.get_reg8(R8.C), cpu.get_reg8(R8.A)) + cpu.cycles += 8 + + def pretty(self) -> str: + return "LD (0xFF00 + C), A" @dataclass @@ -155,6 +191,10 @@ class LDI_HL_A(Insn): hl = cpu.get_reg16(R16.HL) cpu.set_mem8(hl, cpu.get_reg8(R8.A)) cpu.set_reg16(R16.HL, hl + 1) + cpu.cycles += 8 + + def pretty(self) -> str: + return "LDI (HL), A" @dataclass @@ -163,6 +203,10 @@ class LDI_A_HL(Insn): hl = cpu.get_reg16(R16.HL) cpu.set_reg8(R8.A, cpu.get_mem8(hl)) cpu.set_reg16(R16.HL, hl + 1) + cpu.cycles += 8 + + def pretty(self) -> str: + return "LDI A, (HL)" @dataclass @@ -171,6 +215,10 @@ class LDD_HL_A(Insn): hl = cpu.get_reg16(R16.HL) cpu.set_mem8(hl, cpu.get_reg8(R8.A)) cpu.set_reg16(R16.HL, hl - 1) + cpu.cycles += 8 + + def pretty(self) -> str: + return "LDD (HL), A" @dataclass @@ -179,6 +227,10 @@ class LDD_A_HL(Insn): hl = cpu.get_reg16(R16.HL) cpu.set_reg8(R8.A, cpu.get_mem8(hl)) cpu.set_reg16(R16.HL, hl - 1) + cpu.cycles += 8 + + def pretty(self) -> str: + return "LDD A, (HL)" @dataclass diff --git a/tests/test_insn.py b/tests/test_insn.py new file mode 100644 index 0000000..244016e --- /dev/null +++ b/tests/test_insn.py @@ -0,0 +1,232 @@ +import pytest + +from gbso.insn import * +from gbso.regs import * + + +def n8(): + for i in range(0xFF + 1): + yield i + + +def n16(): + for i in range(0xFFFF + 1): + yield i + + +@pytest.mark.parametrize("dst,src", [(x, y) for x in R8 for y in R8]) +def test_ld_r_r(dst, src): + cpu = CPU() + cpu.set_reg8(src, 0x7F) + + LD_R_R(dst, src).exec(cpu) + assert cpu.get_reg8(src) == 0x7F + assert cpu.cycles == 4 + + +@pytest.mark.parametrize("r,imm", [(r, imm) for r in R8 for imm in n8()]) +def test_ld_r_n8(r, imm): + cpu = CPU() + + LD_R_N8(r, 0x7F).exec(cpu) + assert cpu.get_reg8(r) == 0x7F + assert cpu.cycles == 8 + + +@pytest.mark.parametrize("r", R8) +def test_ld_r_hl(r): + cpu = CPU() + cpu.set_reg16(R16.HL, 0x1234) + cpu.set_mem8(0x1234, 0x7F) + + LD_R_HL(r).exec(cpu) + assert cpu.get_reg8(r) == 0x7F + assert cpu.cycles == 8 + + +@pytest.mark.parametrize("r", R8) +def test_ld_hl_r(r): + cpu = CPU() + cpu.set_reg8(r, 0x7F) + cpu.set_reg16(R16.HL, 0x1234) + + LD_HL_R(r).exec(cpu) + hl = cpu.deref_hl() + + if r == R8.H: + assert hl == 0x12 + elif r == R8.L: + assert hl == 0x34 + else: + assert hl == 0x7F + + assert cpu.cycles == 8 + + +@pytest.mark.parametrize("imm", n8()) +def test_ld_hl_n8(imm): + cpu = CPU() + cpu.set_reg16(R16.HL, 0x1234) + + LD_HL_N(imm).exec(cpu) + + assert cpu.deref_hl() == imm + assert cpu.cycles == 12 + + +def test_ld_a_bc(): + cpu = CPU() + cpu.set_reg16(R16.BC, 0x1234) + cpu.set_mem8(0x1234, 0x7F) + + LD_A_BC().exec(cpu) + + assert cpu.get_reg8(R8.A) == 0x7F + assert cpu.cycles == 8 + + +def test_ld_a_de(): + cpu = CPU() + cpu.set_reg16(R16.DE, 0x1234) + cpu.set_mem8(0x1234, 0x7F) + + LD_A_DE().exec(cpu) + + assert cpu.get_reg8(R8.A) == 0x7F + assert cpu.cycles == 8 + + +# @pytest.mark.parametrize("nn", n16()) +def test_ld_a_nn(nn=0x1234): + cpu = CPU() + cpu.set_mem8(nn, 0x7F) + + LD_A_NN(nn).exec(cpu) + + assert cpu.get_reg8(R8.A) == 0x7F + assert cpu.cycles == 16 + + +def test_ld_bc_a(): + cpu = CPU() + cpu.set_reg16(R16.BC, 0x1234) + cpu.set_reg8(R8.A, 0x7F) + + LD_BC_A().exec(cpu) + + assert cpu.get_mem8(0x1234) == 0x7F + assert cpu.cycles == 8 + + +def test_ld_de_a(): + cpu = CPU() + cpu.set_reg16(R16.DE, 0x1234) + cpu.set_reg8(R8.A, 0x7F) + + LD_DE_A().exec(cpu) + + assert cpu.get_mem8(0x1234) == 0x7F + assert cpu.cycles == 8 + + +# @pytest.mark.parametrize("nn", n16()) +def test_ld_nn_a(nn=0x1234): + cpu = CPU() + cpu.set_reg8(R8.A, 0x7F) + + LD_NN_A(nn).exec(cpu) + + assert cpu.get_mem8(nn) == 0x7F + assert cpu.cycles == 16 + + +@pytest.mark.parametrize("n", n8()) +def test_ld_a_ff_n(n): + cpu = CPU() + cpu.set_mem8(0xFF00 + n, 0x7F) + + LD_A_FF_N(n).exec(cpu) + + assert cpu.get_reg8(R8.A) == 0x7F + assert cpu.cycles == 12 + + +@pytest.mark.parametrize("n", n8()) +def test_ld_ff_n_a(n): + cpu = CPU() + cpu.set_reg8(R8.A, 0x7F) + + LD_FF_N_A(n).exec(cpu) + + assert cpu.get_mem8(0xFF00 + n) == 0x7F + assert cpu.cycles == 12 + + +def test_ld_a_ff_c(): + cpu = CPU() + cpu.set_reg8(R8.C, 0x12) + cpu.set_mem8(0xFF12, 0x7F) + + LD_A_FF_C().exec(cpu) + + assert cpu.get_reg8(R8.A) == 0x7F + assert cpu.cycles == 8 + + +def test_ld_ff_c_a(): + cpu = CPU() + cpu.set_reg8(R8.C, 0x12) + cpu.set_reg8(R8.A, 0x7F) + + LD_FF_C_A().exec(cpu) + + assert cpu.get_mem8(0xFF12) == 0x7F + assert cpu.cycles == 8 + + +def test_ldi_hl_a(): + cpu = CPU() + cpu.set_reg8(R8.A, 0x7F) + cpu.set_reg16(R16.HL, 0x1234) + + LDI_HL_A().exec(cpu) + + assert cpu.get_reg16(R16.HL) == 0x1235 + assert cpu.get_mem8(0x1234) == 0x7F + assert cpu.cycles == 8 + + +def test_ldi_a_hl(): + cpu = CPU() + cpu.set_mem8(0x1234, 0x7F) + cpu.set_reg16(R16.HL, 0x1234) + + LDI_A_HL().exec(cpu) + + assert cpu.get_reg16(R16.HL) == 0x1235 + assert cpu.get_reg8(R8.A) == 0x7F + assert cpu.cycles == 8 + + +def test_ldd_hl_a(): + cpu = CPU() + cpu.set_reg8(R8.A, 0x7F) + cpu.set_reg16(R16.HL, 0x1234) + + LDD_HL_A().exec(cpu) + + assert cpu.get_reg16(R16.HL) == 0x1233 + assert cpu.get_mem8(0x1234) == 0x7F + assert cpu.cycles == 8 + + +def test_ldd_a_hl(): + cpu = CPU() + cpu.set_mem8(0x1234, 0x7F) + cpu.set_reg16(R16.HL, 0x1234) + + LDD_A_HL().exec(cpu) + + assert cpu.get_reg16(R16.HL) == 0x1233 + assert cpu.get_reg8(R8.A) == 0x7F + assert cpu.cycles == 8