Browse Source

Implement test cases and program equality check

master
Forest Belton 2 years ago
parent
commit
fc0b7cfe38
12 changed files with 88 additions and 43 deletions
  1. +4
    -3
      ex.py
  2. +0
    -0
      gbso/cpu/__init__.py
  3. +6
    -16
      gbso/cpu/cpu.py
  4. +2
    -2
      gbso/cpu/insn.py
  5. +0
    -0
      gbso/cpu/regs.py
  6. +14
    -0
      gbso/cpu/state.py
  7. +1
    -1
      gbso/optimize.py
  8. +0
    -19
      gbso/program.py
  9. +0
    -0
      gbso/program/__init__.py
  10. +23
    -0
      gbso/program/program.py
  11. +36
    -0
      gbso/program/test_case.py
  12. +2
    -2
      tests/insn/helpers.py

+ 4
- 3
ex.py View File

@ -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.optimize import optimize
from gbso.program import Program
from gbso.regs import R8, R16
from gbso.program.program import Program
rLCDC = 0xFF40 rLCDC = 0xFF40
rIE = 0xFFFF rIE = 0xFFFF

+ 0
- 0
gbso/cpu/__init__.py View File


gbso/cpu.py → gbso/cpu/cpu.py View File

@ -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: class CPU:
@ -46,9 +36,9 @@ class CPU:
return self.state.memory[nn & 0xFFFF] return self.state.memory[nn & 0xFFFF]
def get_mem16(self, nn: int) -> int: 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: def set_mem8(self, nn: int, n: int) -> None:
self.state.memory[nn & 0xFFFF] = n & 0xFF self.state.memory[nn & 0xFFFF] = n & 0xFF

gbso/insn.py → gbso/cpu/insn.py View File

@ -1,8 +1,8 @@
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from dataclasses import dataclass 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): class Insn(ABC):

gbso/regs.py → gbso/cpu/regs.py View File


+ 14
- 0
gbso/cpu/state.py View File

@ -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))

+ 1
- 1
gbso/optimize.py View File

@ -1,4 +1,4 @@
from gbso.program import Program
from gbso.program.program import Program
def optimize(prgm: Program) -> Program: def optimize(prgm: Program) -> Program:

+ 0
- 19
gbso/program.py View File

@ -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

+ 0
- 0
gbso/program/__init__.py View File


+ 23
- 0
gbso/program/program.py View File

@ -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

+ 36
- 0
gbso/program/test_case.py View File

@ -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

+ 2
- 2
tests/insn/helpers.py View File

@ -1,5 +1,5 @@
from gbso.insn import *
from gbso.regs import *
from gbso.cpu.insn import *
from gbso.cpu.regs import *
def n8(): def n8():

Loading…
Cancel
Save