From fc0b7cfe38aa2a448f265c9dc0f71413a311a3e6 Mon Sep 17 00:00:00 2001 From: Forest Belton Date: Sun, 1 Aug 2021 14:32:45 -0400 Subject: [PATCH] Implement test cases and program equality check --- ex.py | 7 ++++--- gbso/cpu/__init__.py | 0 gbso/{ => cpu}/cpu.py | 22 ++++++---------------- gbso/{ => cpu}/insn.py | 4 ++-- gbso/{ => cpu}/regs.py | 0 gbso/cpu/state.py | 14 ++++++++++++++ gbso/optimize.py | 2 +- gbso/program.py | 19 ------------------- gbso/program/__init__.py | 0 gbso/program/program.py | 23 +++++++++++++++++++++++ gbso/program/test_case.py | 36 ++++++++++++++++++++++++++++++++++++ tests/insn/helpers.py | 4 ++-- 12 files changed, 88 insertions(+), 43 deletions(-) create mode 100644 gbso/cpu/__init__.py rename gbso/{ => cpu}/cpu.py (72%) rename gbso/{ => cpu}/insn.py (99%) rename gbso/{ => cpu}/regs.py (100%) create mode 100644 gbso/cpu/state.py delete mode 100644 gbso/program.py create mode 100644 gbso/program/__init__.py create mode 100644 gbso/program/program.py create mode 100644 gbso/program/test_case.py diff --git a/ex.py b/ex.py index a409f9b..3b4d937 100644 --- a/ex.py +++ b/ex.py @@ -1,7 +1,8 @@ -from gbso.insn import * +from gbso.cpu.insn import * +from gbso.cpu.regs import R8, R16 + from gbso.optimize import optimize -from gbso.program import Program -from gbso.regs import R8, R16 +from gbso.program.program import Program rLCDC = 0xFF40 rIE = 0xFFFF diff --git a/gbso/cpu/__init__.py b/gbso/cpu/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/gbso/cpu.py b/gbso/cpu/cpu.py similarity index 72% rename from gbso/cpu.py rename to gbso/cpu/cpu.py index b63068a..eca2154 100644 --- a/gbso/cpu.py +++ b/gbso/cpu/cpu.py @@ -1,17 +1,7 @@ -from collections import defaultdict -from dataclasses import dataclass, field -from typing import Dict, Optional +from typing import Optional -from gbso.regs import R16_HI, R16_LO, R8, R16 - - -@dataclass -class CPUState: - carry: int = 0 - cycles: int = 0 - sp: int = 0 - reg8: Dict[R8, int] = field(default_factory=lambda: defaultdict(lambda: 0)) - memory: Dict[int, int] = field(default_factory=lambda: defaultdict(lambda: 0)) +from gbso.cpu.regs import R16_HI, R16_LO, R8, R16 +from gbso.cpu.state import CPUState class CPU: @@ -46,9 +36,9 @@ class CPU: return self.state.memory[nn & 0xFFFF] def get_mem16(self, nn: int) -> int: - return (self.state.memory[nn & 0xFFFF] << 8) | self.state.memory[ - (nn + 1) & 0xFFFF - ] + hi = self.state.memory[nn & 0xFFFF] + lo = self.state.memory[(nn + 1) & 0xFFFF] + return (hi << 8) | lo def set_mem8(self, nn: int, n: int) -> None: self.state.memory[nn & 0xFFFF] = n & 0xFF diff --git a/gbso/insn.py b/gbso/cpu/insn.py similarity index 99% rename from gbso/insn.py rename to gbso/cpu/insn.py index 1f4cdad..0b7092d 100644 --- a/gbso/insn.py +++ b/gbso/cpu/insn.py @@ -1,8 +1,8 @@ from abc import ABC, abstractmethod from dataclasses import dataclass -from gbso.cpu import CPU -from gbso.regs import R16, R8 +from gbso.cpu.cpu import CPU +from gbso.cpu.regs import R16, R8 class Insn(ABC): diff --git a/gbso/regs.py b/gbso/cpu/regs.py similarity index 100% rename from gbso/regs.py rename to gbso/cpu/regs.py diff --git a/gbso/cpu/state.py b/gbso/cpu/state.py new file mode 100644 index 0000000..bd8f69c --- /dev/null +++ b/gbso/cpu/state.py @@ -0,0 +1,14 @@ +from collections import defaultdict +from dataclasses import dataclass, field +from typing import Dict + +from gbso.cpu.regs import R8 + + +@dataclass +class CPUState: + carry: int = 0 + cycles: int = 0 + sp: int = 0 + reg8: Dict[R8, int] = field(default_factory=lambda: defaultdict(lambda: 0)) + memory: Dict[int, int] = field(default_factory=lambda: defaultdict(lambda: 0)) diff --git a/gbso/optimize.py b/gbso/optimize.py index c1e4528..c077160 100644 --- a/gbso/optimize.py +++ b/gbso/optimize.py @@ -1,4 +1,4 @@ -from gbso.program import Program +from gbso.program.program import Program def optimize(prgm: Program) -> Program: diff --git a/gbso/program.py b/gbso/program.py deleted file mode 100644 index 56c4da0..0000000 --- a/gbso/program.py +++ /dev/null @@ -1,19 +0,0 @@ -from dataclasses import dataclass -from typing import List, Optional, Set, Union - -from gbso.cpu import CPU, CPUState -from gbso.insn import Insn -from gbso.regs import R8 - - -@dataclass -class Program: - outputs: Set[Union[R8, int]] - insns: List[Insn] - init_state: Optional[CPUState] = None - - def execute(self) -> CPU: - cpu = CPU(state=self.init_state) - for insn in self.insns: - insn.exec(cpu) - return cpu diff --git a/gbso/program/__init__.py b/gbso/program/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/gbso/program/program.py b/gbso/program/program.py new file mode 100644 index 0000000..5b07d0c --- /dev/null +++ b/gbso/program/program.py @@ -0,0 +1,23 @@ +from dataclasses import dataclass + +from typing import List, Optional, Union + +from gbso.cpu.cpu import CPU +from gbso.cpu.regs import R8 +from gbso.cpu.state import CPUState +from gbso.cpu.insn import Insn + +# TODO: Support 16-bit memory/register outputs +Output = Union[R8, int] + + +@dataclass +class Program: + insns: List[Insn] + outputs: List[Output] + + def execute(self, init_state: Optional[CPUState] = None) -> CPU: + cpu = CPU(state=init_state) + for insn in self.insns: + insn.exec(cpu) + return cpu diff --git a/gbso/program/test_case.py b/gbso/program/test_case.py new file mode 100644 index 0000000..01ee6de --- /dev/null +++ b/gbso/program/test_case.py @@ -0,0 +1,36 @@ +from dataclasses import dataclass +from gbso.cpu.regs import R8 +from gbso.program.program import Program + +from gbso.cpu.state import CPUState + + +@dataclass +class TestCase: + __test__ = False + state: CPUState + + +def eq_on_testcase(p: Program, q: Program, case: TestCase) -> int: + p_cpu = p.execute(case.state) + q_cpu = q.execute(case.state) + delta = 0 + for o in p.outputs: + if type(o) == R8: + delta += eq_8bit(p_cpu.state.reg8[o], q_cpu.state.reg8[o]) + elif type(o) == int: + delta += eq_8bit(p_cpu.state.memory[o], q_cpu.state.memory[o]) + else: + raise TypeError(f"unknown output type {type(o)}") + # TODO: Add penalty for undefined behavior (uninitialized register/memory reads) + return delta + + +# Counts differing bits between two 8-bit values +def eq_8bit(x: int, y: int) -> int: + delta = 0 + for i in range(8): + mask = 1 << i + if x & mask != y & mask: + delta += 1 + return 0 diff --git a/tests/insn/helpers.py b/tests/insn/helpers.py index a83c56f..91ab8de 100644 --- a/tests/insn/helpers.py +++ b/tests/insn/helpers.py @@ -1,5 +1,5 @@ -from gbso.insn import * -from gbso.regs import * +from gbso.cpu.insn import * +from gbso.cpu.regs import * def n8():