Browse Source

Add equivalence classes for instructions

master
Forest Belton 2 years ago
parent
commit
8e4e3b956f
15 changed files with 555 additions and 20 deletions
  1. +377
    -2
      gbso/cpu/insn.py
  2. +2
    -1
      gbso/cpu/regs.py
  3. +66
    -2
      gbso/program/mutate.py
  4. +0
    -0
      tests/cpu/__init__.py
  5. +0
    -0
      tests/cpu/insn/__init__.py
  6. +0
    -0
      tests/cpu/insn/helpers.py
  7. +4
    -4
      tests/cpu/insn/test_alu16.py
  8. +3
    -3
      tests/cpu/insn/test_alu8.py
  9. +1
    -1
      tests/cpu/insn/test_bit.py
  10. +1
    -1
      tests/cpu/insn/test_cpu.py
  11. +4
    -4
      tests/cpu/insn/test_loads16.py
  12. +1
    -1
      tests/cpu/insn/test_loads8.py
  13. +1
    -1
      tests/cpu/insn/test_shift.py
  14. +0
    -0
      tests/program/__init__.py
  15. +95
    -0
      tests/program/test_mutate.py

+ 377
- 2
gbso/cpu/insn.py View File

@ -1,11 +1,29 @@
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from collections import defaultdict
from dataclasses import dataclass from dataclasses import dataclass
from enum import Enum
from typing import Dict, List, Type
from gbso.cpu.cpu import CPU from gbso.cpu.cpu import CPU
from gbso.cpu.regs import R16, R8 from gbso.cpu.regs import R16, R8
class Operand(Enum):
R8 = "R8"
R16 = "R16" # BC, DE, HL, SP
R16_NO_SP = "R16_NO_SP" # BC, DE, HL
IMM3 = "IMM3"
IMM8 = "IMM8"
IMM16 = "IMM16"
SIMM8 = "SIMM8"
class Insn(ABC): class Insn(ABC):
@staticmethod
@abstractmethod
def signature() -> List[Operand]:
pass
@staticmethod @staticmethod
@abstractmethod @abstractmethod
def cycles() -> int: def cycles() -> int:
@ -25,6 +43,10 @@ class LD_R_R(Insn):
dst: R8 dst: R8
src: R8 src: R8
@staticmethod
def signature() -> List[Operand]:
return [Operand.R8, Operand.R8]
@staticmethod @staticmethod
def cycles() -> int: def cycles() -> int:
return 4 return 4
@ -41,6 +63,10 @@ class LD_R_N8(Insn):
dst: R8 dst: R8
imm: int imm: int
@staticmethod
def signature() -> List[Operand]:
return [Operand.R8, Operand.IMM8]
@staticmethod @staticmethod
def cycles() -> int: def cycles() -> int:
return 8 return 8
@ -56,6 +82,10 @@ class LD_R_N8(Insn):
class LD_R_HL(Insn): class LD_R_HL(Insn):
dst: R8 dst: R8
@staticmethod
def signature() -> List[Operand]:
return [Operand.R8]
@staticmethod @staticmethod
def cycles() -> int: def cycles() -> int:
return 8 return 8
@ -71,6 +101,10 @@ class LD_R_HL(Insn):
class LD_HL_R(Insn): class LD_HL_R(Insn):
src: R8 src: R8
@staticmethod
def signature() -> List[Operand]:
return [Operand.R8]
@staticmethod @staticmethod
def cycles() -> int: def cycles() -> int:
return 8 return 8
@ -86,6 +120,10 @@ class LD_HL_R(Insn):
class LD_HL_N(Insn): class LD_HL_N(Insn):
imm: int imm: int
@staticmethod
def signature() -> List[Operand]:
return [Operand.IMM8]
@staticmethod @staticmethod
def cycles() -> int: def cycles() -> int:
return 12 return 12
@ -99,6 +137,10 @@ class LD_HL_N(Insn):
@dataclass @dataclass
class LD_A_BC(Insn): class LD_A_BC(Insn):
@staticmethod
def signature() -> List[Operand]:
return []
@staticmethod @staticmethod
def cycles() -> int: def cycles() -> int:
return 8 return 8
@ -112,6 +154,10 @@ class LD_A_BC(Insn):
@dataclass @dataclass
class LD_A_DE(Insn): class LD_A_DE(Insn):
@staticmethod
def signature() -> List[Operand]:
return []
@staticmethod @staticmethod
def cycles() -> int: def cycles() -> int:
return 8 return 8
@ -127,6 +173,10 @@ class LD_A_DE(Insn):
class LD_A_NN(Insn): class LD_A_NN(Insn):
nn: int nn: int
@staticmethod
def signature() -> List[Operand]:
return [Operand.IMM16]
@staticmethod @staticmethod
def cycles() -> int: def cycles() -> int:
return 16 return 16
@ -140,6 +190,10 @@ class LD_A_NN(Insn):
@dataclass @dataclass
class LD_BC_A(Insn): class LD_BC_A(Insn):
@staticmethod
def signature() -> List[Operand]:
return []
@staticmethod @staticmethod
def cycles() -> int: def cycles() -> int:
return 8 return 8
@ -153,6 +207,10 @@ class LD_BC_A(Insn):
@dataclass @dataclass
class LD_DE_A(Insn): class LD_DE_A(Insn):
@staticmethod
def signature() -> List[Operand]:
return []
@staticmethod @staticmethod
def cycles() -> int: def cycles() -> int:
return 8 return 8
@ -168,6 +226,10 @@ class LD_DE_A(Insn):
class LD_NN_A(Insn): class LD_NN_A(Insn):
nn: int nn: int
@staticmethod
def signature() -> List[Operand]:
return [Operand.IMM16]
@staticmethod @staticmethod
def cycles() -> int: def cycles() -> int:
return 16 return 16
@ -183,6 +245,10 @@ class LD_NN_A(Insn):
class LD_A_FF_N(Insn): class LD_A_FF_N(Insn):
n: int n: int
@staticmethod
def signature() -> List[Operand]:
return [Operand.IMM8]
@staticmethod @staticmethod
def cycles() -> int: def cycles() -> int:
return 12 return 12
@ -198,6 +264,10 @@ class LD_A_FF_N(Insn):
class LD_FF_N_A(Insn): class LD_FF_N_A(Insn):
n: int n: int
@staticmethod
def signature() -> List[Operand]:
return [Operand.IMM8]
@staticmethod @staticmethod
def cycles() -> int: def cycles() -> int:
return 12 return 12
@ -211,6 +281,10 @@ class LD_FF_N_A(Insn):
@dataclass @dataclass
class LD_A_FF_C(Insn): class LD_A_FF_C(Insn):
@staticmethod
def signature() -> List[Operand]:
return []
@staticmethod @staticmethod
def cycles() -> int: def cycles() -> int:
return 8 return 8
@ -224,6 +298,10 @@ class LD_A_FF_C(Insn):
@dataclass @dataclass
class LD_FF_C_A(Insn): class LD_FF_C_A(Insn):
@staticmethod
def signature() -> List[Operand]:
return []
@staticmethod @staticmethod
def cycles() -> int: def cycles() -> int:
return 8 return 8
@ -237,6 +315,10 @@ class LD_FF_C_A(Insn):
@dataclass @dataclass
class LDI_HL_A(Insn): class LDI_HL_A(Insn):
@staticmethod
def signature() -> List[Operand]:
return []
@staticmethod @staticmethod
def cycles() -> int: def cycles() -> int:
return 8 return 8
@ -252,6 +334,10 @@ class LDI_HL_A(Insn):
@dataclass @dataclass
class LDI_A_HL(Insn): class LDI_A_HL(Insn):
@staticmethod
def signature() -> List[Operand]:
return []
@staticmethod @staticmethod
def cycles() -> int: def cycles() -> int:
return 8 return 8
@ -267,6 +353,10 @@ class LDI_A_HL(Insn):
@dataclass @dataclass
class LDD_HL_A(Insn): class LDD_HL_A(Insn):
@staticmethod
def signature() -> List[Operand]:
return []
@staticmethod @staticmethod
def cycles() -> int: def cycles() -> int:
return 8 return 8
@ -282,6 +372,10 @@ class LDD_HL_A(Insn):
@dataclass @dataclass
class LDD_A_HL(Insn): class LDD_A_HL(Insn):
@staticmethod
def signature() -> List[Operand]:
return []
@staticmethod @staticmethod
def cycles() -> int: def cycles() -> int:
return 8 return 8
@ -300,6 +394,10 @@ class LD_RR_NN(Insn):
rr: R16 rr: R16
nn: int nn: int
@staticmethod
def signature() -> List[Operand]:
return [Operand.R16, Operand.IMM16]
@staticmethod @staticmethod
def cycles() -> int: def cycles() -> int:
return 12 return 12
@ -315,6 +413,10 @@ class LD_RR_NN(Insn):
class LD_NN_SP(Insn): class LD_NN_SP(Insn):
nn: int nn: int
@staticmethod
def signature() -> List[Operand]:
return [Operand.IMM16]
@staticmethod @staticmethod
def cycles() -> int: def cycles() -> int:
return 20 return 20
@ -328,6 +430,10 @@ class LD_NN_SP(Insn):
@dataclass @dataclass
class LD_SP_HL(Insn): class LD_SP_HL(Insn):
@staticmethod
def signature() -> List[Operand]:
return []
@staticmethod @staticmethod
def cycles() -> int: def cycles() -> int:
return 8 return 8
@ -345,6 +451,10 @@ class LD_SP_HL(Insn):
class PUSH_RR(Insn): class PUSH_RR(Insn):
rr: R16 rr: R16
@staticmethod
def signature() -> List[Operand]:
return [Operand.R16_NO_SP]
@staticmethod @staticmethod
def cycles() -> int: def cycles() -> int:
return 16 return 16
@ -362,6 +472,10 @@ class PUSH_RR(Insn):
class POP_RR(Insn): class POP_RR(Insn):
rr: R16 rr: R16
@staticmethod
def signature() -> List[Operand]:
return [Operand.R16_NO_SP]
@staticmethod @staticmethod
def cycles() -> int: def cycles() -> int:
return 12 return 12
@ -379,6 +493,10 @@ class POP_RR(Insn):
class ADD_A_R(Insn): class ADD_A_R(Insn):
r: R8 r: R8
@staticmethod
def signature() -> List[Operand]:
return [Operand.R8]
@staticmethod @staticmethod
def cycles() -> int: def cycles() -> int:
return 4 return 4
@ -396,6 +514,10 @@ class ADD_A_R(Insn):
class ADD_A_N(Insn): class ADD_A_N(Insn):
n: int n: int
@staticmethod
def signature() -> List[Operand]:
return [Operand.IMM8]
@staticmethod @staticmethod
def cycles() -> int: def cycles() -> int:
return 8 return 8
@ -411,6 +533,10 @@ class ADD_A_N(Insn):
@dataclass @dataclass
class ADD_A_HL(Insn): class ADD_A_HL(Insn):
@staticmethod
def signature() -> List[Operand]:
return []
@staticmethod @staticmethod
def cycles() -> int: def cycles() -> int:
return 8 return 8
@ -428,6 +554,10 @@ class ADD_A_HL(Insn):
class ADC_A_R(Insn): class ADC_A_R(Insn):
r: R8 r: R8
@staticmethod
def signature() -> List[Operand]:
return [Operand.R8]
@staticmethod @staticmethod
def cycles() -> int: def cycles() -> int:
return 4 return 4
@ -445,6 +575,10 @@ class ADC_A_R(Insn):
class ADC_A_N(Insn): class ADC_A_N(Insn):
n: int n: int
@staticmethod
def signature() -> List[Operand]:
return [Operand.IMM8]
@staticmethod @staticmethod
def cycles() -> int: def cycles() -> int:
return 8 return 8
@ -460,6 +594,10 @@ class ADC_A_N(Insn):
@dataclass @dataclass
class ADC_A_HL(Insn): class ADC_A_HL(Insn):
@staticmethod
def signature() -> List[Operand]:
return []
@staticmethod @staticmethod
def cycles() -> int: def cycles() -> int:
return 8 return 8
@ -477,6 +615,10 @@ class ADC_A_HL(Insn):
class SUB_A_R(Insn): class SUB_A_R(Insn):
r: R8 r: R8
@staticmethod
def signature() -> List[Operand]:
return [Operand.R8]
@staticmethod @staticmethod
def cycles() -> int: def cycles() -> int:
return 4 return 4
@ -494,6 +636,10 @@ class SUB_A_R(Insn):
class SUB_A_N(Insn): class SUB_A_N(Insn):
n: int n: int
@staticmethod
def signature() -> List[Operand]:
return [Operand.IMM8]
@staticmethod @staticmethod
def cycles() -> int: def cycles() -> int:
return 8 return 8
@ -509,6 +655,10 @@ class SUB_A_N(Insn):
@dataclass @dataclass
class SUB_A_HL(Insn): class SUB_A_HL(Insn):
@staticmethod
def signature() -> List[Operand]:
return []
@staticmethod @staticmethod
def cycles() -> int: def cycles() -> int:
return 8 return 8
@ -526,6 +676,10 @@ class SUB_A_HL(Insn):
class SBC_A_R(Insn): class SBC_A_R(Insn):
r: R8 r: R8
@staticmethod
def signature() -> List[Operand]:
return [Operand.R8]
@staticmethod @staticmethod
def cycles() -> int: def cycles() -> int:
return 4 return 4
@ -543,6 +697,10 @@ class SBC_A_R(Insn):
class SBC_A_N(Insn): class SBC_A_N(Insn):
n: int n: int
@staticmethod
def signature() -> List[Operand]:
return [Operand.IMM8]
@staticmethod @staticmethod
def cycles() -> int: def cycles() -> int:
return 8 return 8
@ -558,6 +716,10 @@ class SBC_A_N(Insn):
@dataclass @dataclass
class SBC_A_HL(Insn): class SBC_A_HL(Insn):
@staticmethod
def signature() -> List[Operand]:
return []
@staticmethod @staticmethod
def cycles() -> int: def cycles() -> int:
return 8 return 8
@ -575,6 +737,10 @@ class SBC_A_HL(Insn):
class AND_A_R(Insn): class AND_A_R(Insn):
r: R8 r: R8
@staticmethod
def signature() -> List[Operand]:
return [Operand.R8]
@staticmethod @staticmethod
def cycles() -> int: def cycles() -> int:
return 4 return 4
@ -592,6 +758,10 @@ class AND_A_R(Insn):
class AND_A_N(Insn): class AND_A_N(Insn):
n: int n: int
@staticmethod
def signature() -> List[Operand]:
return [Operand.IMM8]
@staticmethod @staticmethod
def cycles() -> int: def cycles() -> int:
return 8 return 8
@ -607,6 +777,10 @@ class AND_A_N(Insn):
@dataclass @dataclass
class AND_A_HL(Insn): class AND_A_HL(Insn):
@staticmethod
def signature() -> List[Operand]:
return []
@staticmethod @staticmethod
def cycles() -> int: def cycles() -> int:
return 8 return 8
@ -624,6 +798,10 @@ class AND_A_HL(Insn):
class XOR_A_R(Insn): class XOR_A_R(Insn):
r: R8 r: R8
@staticmethod
def signature() -> List[Operand]:
return [Operand.R8]
@staticmethod @staticmethod
def cycles() -> int: def cycles() -> int:
return 4 return 4
@ -641,6 +819,10 @@ class XOR_A_R(Insn):
class XOR_A_N(Insn): class XOR_A_N(Insn):
n: int n: int
@staticmethod
def signature() -> List[Operand]:
return [Operand.IMM8]
@staticmethod @staticmethod
def cycles() -> int: def cycles() -> int:
return 8 return 8
@ -656,6 +838,10 @@ class XOR_A_N(Insn):
@dataclass @dataclass
class XOR_A_HL(Insn): class XOR_A_HL(Insn):
@staticmethod
def signature() -> List[Operand]:
return []
@staticmethod @staticmethod
def cycles() -> int: def cycles() -> int:
return 8 return 8
@ -673,6 +859,10 @@ class XOR_A_HL(Insn):
class OR_A_R(Insn): class OR_A_R(Insn):
r: R8 r: R8
@staticmethod
def signature() -> List[Operand]:
return [Operand.R8]
@staticmethod @staticmethod
def cycles() -> int: def cycles() -> int:
return 4 return 4
@ -690,6 +880,10 @@ class OR_A_R(Insn):
class OR_A_N(Insn): class OR_A_N(Insn):
n: int n: int
@staticmethod
def signature() -> List[Operand]:
return [Operand.IMM8]
@staticmethod @staticmethod
def cycles() -> int: def cycles() -> int:
return 8 return 8
@ -705,6 +899,10 @@ class OR_A_N(Insn):
@dataclass @dataclass
class OR_A_HL(Insn): class OR_A_HL(Insn):
@staticmethod
def signature() -> List[Operand]:
return []
@staticmethod @staticmethod
def cycles() -> int: def cycles() -> int:
return 8 return 8
@ -722,6 +920,10 @@ class OR_A_HL(Insn):
class CP_A_R(Insn): class CP_A_R(Insn):
r: R8 r: R8
@staticmethod
def signature() -> List[Operand]:
return [Operand.R8]
@staticmethod @staticmethod
def cycles() -> int: def cycles() -> int:
return 4 return 4
@ -737,6 +939,10 @@ class CP_A_R(Insn):
class CP_A_N(Insn): class CP_A_N(Insn):
n: int n: int
@staticmethod
def signature() -> List[Operand]:
return [Operand.IMM8]
@staticmethod @staticmethod
def cycles() -> int: def cycles() -> int:
return 8 return 8
@ -750,6 +956,10 @@ class CP_A_N(Insn):
@dataclass @dataclass
class CP_A_HL(Insn): class CP_A_HL(Insn):
@staticmethod
def signature() -> List[Operand]:
return []
@staticmethod @staticmethod
def cycles() -> int: def cycles() -> int:
return 8 return 8
@ -767,6 +977,10 @@ class CP_A_HL(Insn):
class INC_R(Insn): class INC_R(Insn):
r: R8 r: R8
@staticmethod
def signature() -> List[Operand]:
return [Operand.R8]
@staticmethod @staticmethod
def cycles() -> int: def cycles() -> int:
return 4 return 4
@ -780,7 +994,9 @@ class INC_R(Insn):
@dataclass @dataclass
class INC_HL(Insn): class INC_HL(Insn):
r: R8
@staticmethod
def signature() -> List[Operand]:
return []
@staticmethod @staticmethod
def cycles() -> int: def cycles() -> int:
@ -798,6 +1014,10 @@ class INC_HL(Insn):
class DEC_R(Insn): class DEC_R(Insn):
r: R8 r: R8
@staticmethod
def signature() -> List[Operand]:
return [Operand.R8]
@staticmethod @staticmethod
def cycles() -> int: def cycles() -> int:
return 4 return 4
@ -811,7 +1031,9 @@ class DEC_R(Insn):
@dataclass @dataclass
class DEC_HL(Insn): class DEC_HL(Insn):
r: R8
@staticmethod
def signature() -> List[Operand]:
return []
@staticmethod @staticmethod
def cycles() -> int: def cycles() -> int:
@ -827,6 +1049,10 @@ class DEC_HL(Insn):
@dataclass @dataclass
class CPL(Insn): class CPL(Insn):
@staticmethod
def signature() -> List[Operand]:
return []
@staticmethod @staticmethod
def cycles() -> int: def cycles() -> int:
return 4 return 4
@ -842,6 +1068,10 @@ class CPL(Insn):
class ADD_HL_RR(Insn): class ADD_HL_RR(Insn):
rr: R16 rr: R16
@staticmethod
def signature() -> List[Operand]:
return [Operand.R16]
@staticmethod @staticmethod
def cycles() -> int: def cycles() -> int:
return 8 return 8
@ -859,6 +1089,10 @@ class ADD_HL_RR(Insn):
class INC_RR(Insn): class INC_RR(Insn):
rr: R16 rr: R16
@staticmethod
def signature() -> List[Operand]:
return [Operand.R16]
@staticmethod @staticmethod
def cycles() -> int: def cycles() -> int:
return 8 return 8
@ -874,6 +1108,10 @@ class INC_RR(Insn):
class DEC_RR(Insn): class DEC_RR(Insn):
rr: R16 rr: R16
@staticmethod
def signature() -> List[Operand]:
return [Operand.R16]
@staticmethod @staticmethod
def cycles() -> int: def cycles() -> int:
return 8 return 8
@ -889,6 +1127,10 @@ class DEC_RR(Insn):
class ADD_SP_DD(Insn): class ADD_SP_DD(Insn):
dd: int dd: int
@staticmethod
def signature() -> List[Operand]:
return [Operand.SIMM8]
@staticmethod @staticmethod
def cycles() -> int: def cycles() -> int:
return 16 return 16
@ -906,6 +1148,10 @@ class ADD_SP_DD(Insn):
class LD_HL_SP_DD(Insn): class LD_HL_SP_DD(Insn):
dd: int dd: int
@staticmethod
def signature() -> List[Operand]:
return [Operand.SIMM8]
@staticmethod @staticmethod
def cycles() -> int: def cycles() -> int:
return 12 return 12
@ -921,6 +1167,10 @@ class LD_HL_SP_DD(Insn):
@dataclass @dataclass
class RLCA(Insn): class RLCA(Insn):
@staticmethod
def signature() -> List[Operand]:
return []
@staticmethod @staticmethod
def cycles() -> int: def cycles() -> int:
return 4 return 4
@ -936,6 +1186,10 @@ class RLCA(Insn):
@dataclass @dataclass
class RLA(Insn): class RLA(Insn):
@staticmethod
def signature() -> List[Operand]:
return []
@staticmethod @staticmethod
def cycles() -> int: def cycles() -> int:
return 4 return 4
@ -952,6 +1206,10 @@ class RLA(Insn):
@dataclass @dataclass
class RRCA(Insn): class RRCA(Insn):
@staticmethod
def signature() -> List[Operand]:
return []
@staticmethod @staticmethod
def cycles() -> int: def cycles() -> int:
return 4 return 4
@ -967,6 +1225,10 @@ class RRCA(Insn):
@dataclass @dataclass
class RRA(Insn): class RRA(Insn):
@staticmethod
def signature() -> List[Operand]:
return []
@staticmethod @staticmethod
def cycles() -> int: def cycles() -> int:
return 4 return 4
@ -985,6 +1247,10 @@ class RRA(Insn):
class RLC_R(Insn): class RLC_R(Insn):
r: R8 r: R8
@staticmethod
def signature() -> List[Operand]:
return [Operand.R8]
@staticmethod @staticmethod
def cycles() -> int: def cycles() -> int:
return 8 return 8
@ -1000,6 +1266,10 @@ class RLC_R(Insn):
@dataclass @dataclass
class RLC_HL(Insn): class RLC_HL(Insn):
@staticmethod
def signature() -> List[Operand]:
return []
@staticmethod @staticmethod
def cycles() -> int: def cycles() -> int:
return 16 return 16
@ -1016,6 +1286,10 @@ class RLC_HL(Insn):
class RL_R(Insn): class RL_R(Insn):
r: R8 r: R8
@staticmethod
def signature() -> List[Operand]:
return [Operand.R8]
@staticmethod @staticmethod
def cycles() -> int: def cycles() -> int:
return 8 return 8
@ -1032,6 +1306,10 @@ class RL_R(Insn):
@dataclass @dataclass
class RL_HL(Insn): class RL_HL(Insn):
@staticmethod
def signature() -> List[Operand]:
return []
@staticmethod @staticmethod
def cycles() -> int: def cycles() -> int:
return 16 return 16
@ -1049,6 +1327,10 @@ class RL_HL(Insn):
class RRC_R(Insn): class RRC_R(Insn):
r: R8 r: R8
@staticmethod
def signature() -> List[Operand]:
return [Operand.R8]
@staticmethod @staticmethod
def cycles() -> int: def cycles() -> int:
return 8 return 8
@ -1064,6 +1346,10 @@ class RRC_R(Insn):
@dataclass @dataclass
class RRC_HL(Insn): class RRC_HL(Insn):
@staticmethod
def signature() -> List[Operand]:
return []
@staticmethod @staticmethod
def cycles() -> int: def cycles() -> int:
return 16 return 16
@ -1080,6 +1366,10 @@ class RRC_HL(Insn):
class RR_R(Insn): class RR_R(Insn):
r: R8 r: R8
@staticmethod
def signature() -> List[Operand]:
return [Operand.R8]
@staticmethod @staticmethod
def cycles() -> int: def cycles() -> int:
return 8 return 8
@ -1096,6 +1386,10 @@ class RR_R(Insn):
@dataclass @dataclass
class RR_HL(Insn): class RR_HL(Insn):
@staticmethod
def signature() -> List[Operand]:
return []
@staticmethod @staticmethod
def cycles() -> int: def cycles() -> int:
return 16 return 16
@ -1113,6 +1407,10 @@ class RR_HL(Insn):
class SLA_R(Insn): class SLA_R(Insn):
r: R8 r: R8
@staticmethod
def signature() -> List[Operand]:
return [Operand.R8]
@staticmethod @staticmethod
def cycles() -> int: def cycles() -> int:
return 8 return 8
@ -1128,6 +1426,10 @@ class SLA_R(Insn):
@dataclass @dataclass
class SLA_HL(Insn): class SLA_HL(Insn):
@staticmethod
def signature() -> List[Operand]:
return []
@staticmethod @staticmethod
def cycles() -> int: def cycles() -> int:
return 16 return 16
@ -1144,6 +1446,10 @@ class SLA_HL(Insn):
class SWAP_R(Insn): class SWAP_R(Insn):
r: R8 r: R8
@staticmethod
def signature() -> List[Operand]:
return [Operand.R8]
@staticmethod @staticmethod
def cycles() -> int: def cycles() -> int:
return 8 return 8
@ -1159,6 +1465,10 @@ class SWAP_R(Insn):
@dataclass @dataclass
class SWAP_HL(Insn): class SWAP_HL(Insn):
@staticmethod
def signature() -> List[Operand]:
return []
@staticmethod @staticmethod
def cycles() -> int: def cycles() -> int:
return 16 return 16
@ -1175,6 +1485,10 @@ class SWAP_HL(Insn):
class SRA_R(Insn): class SRA_R(Insn):
r: R8 r: R8
@staticmethod
def signature() -> List[Operand]:
return [Operand.R8]
@staticmethod @staticmethod
def cycles() -> int: def cycles() -> int:
return 8 return 8
@ -1190,6 +1504,10 @@ class SRA_R(Insn):
@dataclass @dataclass
class SRA_HL(Insn): class SRA_HL(Insn):
@staticmethod
def signature() -> List[Operand]:
return []
@staticmethod @staticmethod
def cycles() -> int: def cycles() -> int:
return 16 return 16
@ -1206,6 +1524,10 @@ class SRA_HL(Insn):
class SRL_R(Insn): class SRL_R(Insn):
r: R8 r: R8
@staticmethod
def signature() -> List[Operand]:
return [Operand.R8]
@staticmethod @staticmethod
def cycles() -> int: def cycles() -> int:
return 8 return 8
@ -1221,6 +1543,10 @@ class SRL_R(Insn):
@dataclass @dataclass
class SRL_HL(Insn): class SRL_HL(Insn):
@staticmethod
def signature() -> List[Operand]:
return []
@staticmethod @staticmethod
def cycles() -> int: def cycles() -> int:
return 16 return 16
@ -1238,6 +1564,10 @@ class SET_N_R(Insn):
n: int n: int
r: R8 r: R8
@staticmethod
def signature() -> List[Operand]:
return [Operand.IMM3, Operand.R8]
@staticmethod @staticmethod
def cycles() -> int: def cycles() -> int:
return 8 return 8
@ -1253,6 +1583,10 @@ class SET_N_R(Insn):
class SET_N_HL(Insn): class SET_N_HL(Insn):
n: int n: int
@staticmethod
def signature() -> List[Operand]:
return [Operand.IMM3]
@staticmethod @staticmethod
def cycles() -> int: def cycles() -> int:
return 16 return 16
@ -1270,6 +1604,10 @@ class RES_N_R(Insn):
n: int n: int
r: R8 r: R8
@staticmethod
def signature() -> List[Operand]:
return [Operand.IMM3, Operand.R8]
@staticmethod @staticmethod
def cycles() -> int: def cycles() -> int:
return 8 return 8
@ -1285,6 +1623,10 @@ class RES_N_R(Insn):
class RES_N_HL(Insn): class RES_N_HL(Insn):
n: int n: int
@staticmethod
def signature() -> List[Operand]:
return [Operand.IMM3]
@staticmethod @staticmethod
def cycles() -> int: def cycles() -> int:
return 16 return 16
@ -1299,6 +1641,10 @@ class RES_N_HL(Insn):
@dataclass @dataclass
class CCF(Insn): class CCF(Insn):
@staticmethod
def signature() -> List[Operand]:
return []
@staticmethod @staticmethod
def cycles() -> int: def cycles() -> int:
return 4 return 4
@ -1312,6 +1658,10 @@ class CCF(Insn):
@dataclass @dataclass
class SCF(Insn): class SCF(Insn):
@staticmethod
def signature() -> List[Operand]:
return []
@staticmethod @staticmethod
def cycles() -> int: def cycles() -> int:
return 4 return 4
@ -1325,6 +1675,10 @@ class SCF(Insn):
@dataclass @dataclass
class UNUSED(Insn): class UNUSED(Insn):
@staticmethod
def signature() -> List[Operand]:
return []
@staticmethod @staticmethod
def cycles() -> int: def cycles() -> int:
return 0 return 0
@ -1334,3 +1688,24 @@ class UNUSED(Insn):
def pretty(self) -> str: def pretty(self) -> str:
return "UNUSED" return "UNUSED"
ALL_INSN_CLASSES: List[Type[Insn]] = [
cls for cls in Insn.__subclasses__() if cls != Insn # type: ignore
]
INSNS_BY_SIGNATURE: Dict[str, List[Type[Insn]]] = defaultdict(list)
def get_signature_class(insn: Insn) -> List[Type[Insn]]:
signature_key = get_signature_key(insn.signature())
return INSNS_BY_SIGNATURE[signature_key]
def get_signature_key(signature: List[Operand]) -> str:
return "-".join([ty.value for ty in signature])
for insn_cls in ALL_INSN_CLASSES:
signature_key = get_signature_key(insn_cls.signature())
INSNS_BY_SIGNATURE[signature_key].append(insn_cls)

+ 2
- 1
gbso/cpu/regs.py View File

@ -12,7 +12,6 @@ class R8(Enum):
class R16(Enum): class R16(Enum):
AF = "AF"
BC = "BC" BC = "BC"
DE = "DE" DE = "DE"
HL = "HL" HL = "HL"
@ -30,3 +29,5 @@ R16_LO = {
R16.DE: R8.E, R16.DE: R8.E,
R16.HL: R8.L, R16.HL: R8.L,
} }
R16_WITHOUT_SP = [R16.BC, R16.DE, R16.HL]

+ 66
- 2
gbso/program/mutate.py View File

@ -1,7 +1,9 @@
from enum import Enum from enum import Enum
from random import random
from typing import List, Tuple, TypeVar
from gbso.cpu.regs import R16, R16_WITHOUT_SP, R8
from random import choice, randint, random, randrange
from typing import List, Tuple, Type, TypeVar, Union
from gbso.cpu.insn import ALL_INSN_CLASSES, Insn, Operand, UNUSED
from gbso.program.program import Program from gbso.program.program import Program
@ -40,6 +42,42 @@ def mutate_program(
return new_prgm return new_prgm
# TODO: Implement
def mutate_opcode(prgm: Program) -> Program:
return prgm
# TODO: Implement
def mutate_operand(prgm: Program) -> Program:
return prgm
def mutate_swap(prgm: Program) -> Program:
i = randrange(len(prgm.insns))
j = randrange(len(prgm.insns))
insns = prgm.insns.copy()
insn = insns[i]
insns[i] = insns[j]
insns[j] = insn
return Program(insns=insns)
def mutate_insn(prgm: Program, prob_unused: float) -> Program:
unused = random()
next_insn: Insn = UNUSED()
if unused > prob_unused:
cls = choice(ALL_INSN_CLASSES)
next_insn = create_random_insn(cls)
insns = prgm.insns.copy()
insn_index = randrange(len(prgm.insns))
insns[insn_index] = next_insn
return Program(insns=insns)
A = TypeVar("A") A = TypeVar("A")
@ -55,3 +93,29 @@ def sample_probs(probs: List[Tuple[A, float]]) -> A:
val, _ = probs[-1] val, _ = probs[-1]
return val return val
def create_random_insn(insn_cls: Type[Insn]) -> Insn:
args = [create_random_operand(op) for op in insn_cls.signature()]
return insn_cls(*args) # type: ignore
def create_random_operand(op: Operand) -> Union[int, R8, R16]:
value: Union[int, R8, R16]
if op == Operand.R8:
value = choice(list(R8))
elif op == Operand.R16:
value = choice(list(R16))
elif op == Operand.R16_NO_SP:
value = choice(R16_WITHOUT_SP)
elif op == Operand.IMM3:
value = randrange(8)
elif op == Operand.IMM8:
value = randrange(256)
elif op == Operand.IMM16:
value = randrange(65536)
elif op == Operand.SIMM8:
value = randint(-128, 127)
return value

tests/insn/__init__.py → tests/cpu/__init__.py View File


+ 0
- 0
tests/cpu/insn/__init__.py View File


tests/insn/helpers.py → tests/cpu/insn/helpers.py View File


tests/insn/test_alu16.py → tests/cpu/insn/test_alu16.py View File

@ -1,9 +1,9 @@
import pytest import pytest
from tests.insn.helpers import *
from tests.cpu.insn.helpers import *
@pytest.mark.parametrize("rr", set(R16) - {R16.AF, R16.HL})
@pytest.mark.parametrize("rr", set(R16) - {R16.HL})
def test_add_hl_rr(rr): def test_add_hl_rr(rr):
cpu = CPU() cpu = CPU()
cpu.set_reg16(R16.HL, 0xABCD) cpu.set_reg16(R16.HL, 0xABCD)
@ -21,7 +21,7 @@ def test_add_hl_rr(rr):
assert cpu.state.carry == 1 assert cpu.state.carry == 1
@pytest.mark.parametrize("rr", set(R16) - {R16.AF})
@pytest.mark.parametrize("rr", set(R16))
def test_inc_rr(rr): def test_inc_rr(rr):
cpu = CPU() cpu = CPU()
cpu.set_reg16(rr, 0xABCD) cpu.set_reg16(rr, 0xABCD)
@ -31,7 +31,7 @@ def test_inc_rr(rr):
assert cpu.get_reg16(rr) == 0xABCE assert cpu.get_reg16(rr) == 0xABCE
@pytest.mark.parametrize("rr", set(R16) - {R16.AF})
@pytest.mark.parametrize("rr", set(R16))
def test_dec_rr(rr): def test_dec_rr(rr):
cpu = CPU() cpu = CPU()
cpu.set_reg16(rr, 0xABCD) cpu.set_reg16(rr, 0xABCD)

tests/insn/test_alu8.py → tests/cpu/insn/test_alu8.py View File

@ -1,6 +1,6 @@
import pytest import pytest
from tests.insn.helpers import *
from tests.cpu.insn.helpers import *
@pytest.mark.parametrize("r", set(R8) - {R8.A}) @pytest.mark.parametrize("r", set(R8) - {R8.A})
@ -281,7 +281,7 @@ def test_inc_hl(n):
cpu.set_reg16(R16.HL, 0x1234) cpu.set_reg16(R16.HL, 0x1234)
cpu.set_mem8(0x1234, n) cpu.set_mem8(0x1234, n)
INC_HL(n).exec(cpu)
INC_HL().exec(cpu)
assert cpu.hl == (n + 1) & 0xFF assert cpu.hl == (n + 1) & 0xFF
@ -302,7 +302,7 @@ def test_dec_hl(n):
cpu.set_reg16(R16.HL, 0x1234) cpu.set_reg16(R16.HL, 0x1234)
cpu.set_mem8(0x1234, n) cpu.set_mem8(0x1234, n)
DEC_HL(n).exec(cpu)
DEC_HL().exec(cpu)
assert cpu.hl == (n - 1) & 0xFF assert cpu.hl == (n - 1) & 0xFF

tests/insn/test_bit.py → tests/cpu/insn/test_bit.py View File

@ -1,6 +1,6 @@
import pytest import pytest
from tests.insn.helpers import *
from tests.cpu.insn.helpers import *
@pytest.mark.parametrize("n,r", [(n, r) for n in range(8) for r in R8]) @pytest.mark.parametrize("n,r", [(n, r) for n in range(8) for r in R8])

tests/insn/test_cpu.py → tests/cpu/insn/test_cpu.py View File

@ -1,4 +1,4 @@
from tests.insn.helpers import *
from tests.cpu.insn.helpers import *
def test_ccf(): def test_ccf():

tests/insn/test_loads16.py → tests/cpu/insn/test_loads16.py View File

@ -1,9 +1,9 @@
import pytest import pytest
from tests.insn.helpers import *
from tests.cpu.insn.helpers import *
@pytest.mark.parametrize("rr", set(R16) - {R16.AF})
@pytest.mark.parametrize("rr", set(R16))
def test_ld_rr_nn(rr): def test_ld_rr_nn(rr):
cpu = CPU() cpu = CPU()
@ -30,7 +30,7 @@ def test_ld_sp_hl():
assert cpu.get_reg16(R16.SP) == 0x1234 assert cpu.get_reg16(R16.SP) == 0x1234
@pytest.mark.parametrize("rr", set(R16) - {R16.AF, R16.SP})
@pytest.mark.parametrize("rr", R16_WITHOUT_SP)
def test_push_rr(rr): def test_push_rr(rr):
cpu = CPU() cpu = CPU()
cpu.set_reg16(R16.SP, 0xFFFF) cpu.set_reg16(R16.SP, 0xFFFF)
@ -42,7 +42,7 @@ def test_push_rr(rr):
assert cpu.get_mem16(0xFFFD) == 0x1234 assert cpu.get_mem16(0xFFFD) == 0x1234
@pytest.mark.parametrize("rr", set(R16) - {R16.AF, R16.SP})
@pytest.mark.parametrize("rr", R16_WITHOUT_SP)
def test_pop_rr(rr): def test_pop_rr(rr):
cpu = CPU() cpu = CPU()
cpu.set_reg16(R16.SP, 0xFFFD) cpu.set_reg16(R16.SP, 0xFFFD)

tests/insn/test_loads8.py → tests/cpu/insn/test_loads8.py View File

@ -1,6 +1,6 @@
import pytest import pytest
from tests.insn.helpers import *
from tests.cpu.insn.helpers import *
@pytest.mark.parametrize("dst,src", [(x, y) for x in R8 for y in R8]) @pytest.mark.parametrize("dst,src", [(x, y) for x in R8 for y in R8])

tests/insn/test_shift.py → tests/cpu/insn/test_shift.py View File

@ -1,6 +1,6 @@
import pytest import pytest
from tests.insn.helpers import *
from tests.cpu.insn.helpers import *
def test_rlca(): def test_rlca():

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


+ 95
- 0
tests/program/test_mutate.py View File

@ -0,0 +1,95 @@
from gbso.cpu.insn import ADD_A_N, ADD_A_R, DEC_HL, LD_RR_NN
from gbso.program.mutate import *
def test_mutate_swap():
trials = 1000
swapped_count = 0
insn_a = ADD_A_R(R8.C)
insn_b = DEC_HL()
prgm = Program(insns=[insn_a, insn_b])
for _ in range(trials):
new_prgm = mutate_swap(prgm)
assert len(new_prgm.insns) == 2
assert isinstance(new_prgm.insns[0], Insn)
assert isinstance(new_prgm.insns[1], Insn)
assert new_prgm.insns == prgm.insns or new_prgm.insns == [insn_b, insn_a]
if new_prgm.insns != prgm.insns:
swapped_count += 1
assert abs(0.5 - (swapped_count / trials)) < 0.05
def test_mutate_insn():
trials = 1000
seen_add_insns = 0
seen_unused = 0
add_insn = ADD_A_N(128)
prgm = Program(insns=[add_insn])
for _ in range(trials):
new_prgm = mutate_insn(prgm, 0.5)
assert len(new_prgm.insns) == 1
assert isinstance(new_prgm.insns[0], Insn)
if new_prgm.insns[0] == add_insn:
seen_add_insns += 1
elif new_prgm.insns[0] == UNUSED():
seen_unused += 1
assert seen_add_insns < 10
assert abs(0.5 - (seen_unused / trials)) < 0.05
def test_create_random_insn():
insn = create_random_insn(ADD_A_R)
assert type(insn) == ADD_A_R
assert type(insn.r) == R8
insn = create_random_insn(ADD_A_N)
assert type(insn) == ADD_A_N
assert type(insn.n) == int
insn = create_random_insn(LD_RR_NN)
assert type(insn) == LD_RR_NN
assert type(insn.rr) == R16
assert type(insn.nn) == int
def test_create_random_operand():
op = create_random_operand(Operand.R8)
assert type(op) == R8
op = create_random_operand(Operand.R16)
assert type(op) == R16
for _ in range(100):
op = create_random_operand(Operand.R16_NO_SP)
assert type(op) == R16
assert op != R16.SP
for _ in range(100):
op = create_random_operand(Operand.IMM3)
assert type(op) == int
assert op >= 0 and op < 8
for _ in range(300):
op = create_random_operand(Operand.IMM8)
assert type(op) == int
assert op >= 0 and op < 256
for _ in range(500):
op = create_random_operand(Operand.IMM16)
assert type(op) == int
assert op >= 0 and op < 65536
for _ in range(300):
op = create_random_operand(Operand.SIMM8)
assert type(op) == int
assert op >= -128 and op < 128

Loading…
Cancel
Save