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

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

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

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:

+ 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():

Loading…
Cancel
Save