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

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

@ -1,6 +1,6 @@
import pytest
from tests.insn.helpers import *
from tests.cpu.insn.helpers import *
@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_mem8(0x1234, n)
INC_HL(n).exec(cpu)
INC_HL().exec(cpu)
assert cpu.hl == (n + 1) & 0xFF
@ -302,7 +302,7 @@ def test_dec_hl(n):
cpu.set_reg16(R16.HL, 0x1234)
cpu.set_mem8(0x1234, n)
DEC_HL(n).exec(cpu)
DEC_HL().exec(cpu)
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
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])

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

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

@ -1,9 +1,9 @@
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):
cpu = CPU()
@ -30,7 +30,7 @@ def test_ld_sp_hl():
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):
cpu = CPU()
cpu.set_reg16(R16.SP, 0xFFFF)
@ -42,7 +42,7 @@ def test_push_rr(rr):
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):
cpu = CPU()
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
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])

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

@ -1,6 +1,6 @@
import pytest
from tests.insn.helpers import *
from tests.cpu.insn.helpers import *
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