gameboy superoptimizer
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

1714 lines
34 KiB

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:
pass
@abstractmethod
def exec(self, cpu: CPU) -> None:
pass
@abstractmethod
def pretty(self) -> str:
pass
@dataclass
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
def exec(self, cpu: CPU) -> None:
cpu.set_reg8(self.dst, cpu.get_reg8(self.src))
def pretty(self) -> str:
return f"LD {self.dst.value}, {self.src.value}"
@dataclass
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
def exec(self, cpu: CPU) -> None:
cpu.set_reg8(self.dst, self.imm)
def pretty(self) -> str:
return f"LD {self.dst.value}, {hex(self.imm)}"
@dataclass
class LD_R_HL(Insn):
dst: R8
@staticmethod
def signature() -> List[Operand]:
return [Operand.R8]
@staticmethod
def cycles() -> int:
return 8
def exec(self, cpu: CPU) -> None:
cpu.set_reg8(self.dst, cpu.get_mem8(cpu.get_reg16(R16.HL)))
def pretty(self) -> str:
return f"LD {self.dst.value}, (HL)"
@dataclass
class LD_HL_R(Insn):
src: R8
@staticmethod
def signature() -> List[Operand]:
return [Operand.R8]
@staticmethod
def cycles() -> int:
return 8
def exec(self, cpu: CPU) -> None:
cpu.hl = cpu.get_reg8(self.src)
def pretty(self) -> str:
return f"LD (HL), {self.src.value}"
@dataclass
class LD_HL_N(Insn):
imm: int
@staticmethod
def signature() -> List[Operand]:
return [Operand.IMM8]
@staticmethod
def cycles() -> int:
return 12
def exec(self, cpu: CPU) -> None:
cpu.set_mem8(cpu.get_reg16(R16.HL), self.imm)
def pretty(self) -> str:
return f"LD (HL), {hex(self.imm & 0xff)}"
@dataclass
class LD_A_BC(Insn):
@staticmethod
def signature() -> List[Operand]:
return []
@staticmethod
def cycles() -> int:
return 8
def exec(self, cpu: CPU) -> None:
cpu.set_reg8(R8.A, cpu.get_mem8(cpu.get_reg16(R16.BC)))
def pretty(self) -> str:
return "LD A, (BC)"
@dataclass
class LD_A_DE(Insn):
@staticmethod
def signature() -> List[Operand]:
return []
@staticmethod
def cycles() -> int:
return 8
def exec(self, cpu: CPU) -> None:
cpu.set_reg8(R8.A, cpu.get_mem8(cpu.get_reg16(R16.DE)))
def pretty(self) -> str:
return "LD A, (DE)"
@dataclass
class LD_A_NN(Insn):
nn: int
@staticmethod
def signature() -> List[Operand]:
return [Operand.IMM16]
@staticmethod
def cycles() -> int:
return 16
def exec(self, cpu: CPU) -> None:
cpu.set_reg8(R8.A, cpu.get_mem8(self.nn))
def pretty(self) -> str:
return f"LD A, ({hex(self.nn)})"
@dataclass
class LD_BC_A(Insn):
@staticmethod
def signature() -> List[Operand]:
return []
@staticmethod
def cycles() -> int:
return 8
def exec(self, cpu: CPU) -> None:
cpu.set_mem8(cpu.get_reg16(R16.BC), cpu.get_reg8(R8.A))
def pretty(self) -> str:
return "LD (BC), A"
@dataclass
class LD_DE_A(Insn):
@staticmethod
def signature() -> List[Operand]:
return []
@staticmethod
def cycles() -> int:
return 8
def exec(self, cpu: CPU) -> None:
cpu.set_mem8(cpu.get_reg16(R16.DE), cpu.get_reg8(R8.A))
def pretty(self) -> str:
return "LD (DE), A"
@dataclass
class LD_NN_A(Insn):
nn: int
@staticmethod
def signature() -> List[Operand]:
return [Operand.IMM16]
@staticmethod
def cycles() -> int:
return 16
def exec(self, cpu: CPU) -> None:
cpu.set_mem8(self.nn, cpu.get_reg8(R8.A))
def pretty(self) -> str:
return f"LD ({hex(self.nn)}), A"
@dataclass
class LD_A_FF_N(Insn):
n: int
@staticmethod
def signature() -> List[Operand]:
return [Operand.IMM8]
@staticmethod
def cycles() -> int:
return 12
def exec(self, cpu: CPU) -> None:
cpu.set_reg8(R8.A, cpu.get_mem8(0xFF00 + self.n))
def pretty(self) -> str:
return f"LD A, (0xFF00 + {hex(self.n)})"
@dataclass
class LD_FF_N_A(Insn):
n: int
@staticmethod
def signature() -> List[Operand]:
return [Operand.IMM8]
@staticmethod
def cycles() -> int:
return 12
def exec(self, cpu: CPU) -> None:
cpu.set_mem8(0xFF00 + self.n, cpu.get_reg8(R8.A))
def pretty(self) -> str:
return f"LD (0xFF00 + {hex(self.n)}), A"
@dataclass
class LD_A_FF_C(Insn):
@staticmethod
def signature() -> List[Operand]:
return []
@staticmethod
def cycles() -> int:
return 8
def exec(self, cpu: CPU) -> None:
cpu.set_reg8(R8.A, cpu.get_mem8(0xFF00 + cpu.get_reg8(R8.C)))
def pretty(self) -> str:
return "LD A, (0xFF00 + C)"
@dataclass
class LD_FF_C_A(Insn):
@staticmethod
def signature() -> List[Operand]:
return []
@staticmethod
def cycles() -> int:
return 8
def exec(self, cpu: CPU) -> None:
cpu.set_mem8(0xFF00 + cpu.get_reg8(R8.C), cpu.get_reg8(R8.A))
def pretty(self) -> str:
return "LD (0xFF00 + C), A"
@dataclass
class LDI_HL_A(Insn):
@staticmethod
def signature() -> List[Operand]:
return []
@staticmethod
def cycles() -> int:
return 8
def exec(self, cpu: CPU) -> None:
hl = cpu.get_reg16(R16.HL)
cpu.set_mem8(hl, cpu.get_reg8(R8.A))
cpu.set_reg16(R16.HL, hl + 1)
def pretty(self) -> str:
return "LDI (HL), A"
@dataclass
class LDI_A_HL(Insn):
@staticmethod
def signature() -> List[Operand]:
return []
@staticmethod
def cycles() -> int:
return 8
def exec(self, cpu: CPU) -> None:
hl = cpu.get_reg16(R16.HL)
cpu.set_reg8(R8.A, cpu.get_mem8(hl))
cpu.set_reg16(R16.HL, hl + 1)
def pretty(self) -> str:
return "LDI A, (HL)"
@dataclass
class LDD_HL_A(Insn):
@staticmethod
def signature() -> List[Operand]:
return []
@staticmethod
def cycles() -> int:
return 8
def exec(self, cpu: CPU) -> None:
hl = cpu.get_reg16(R16.HL)
cpu.set_mem8(hl, cpu.get_reg8(R8.A))
cpu.set_reg16(R16.HL, hl - 1)
def pretty(self) -> str:
return "LDD (HL), A"
@dataclass
class LDD_A_HL(Insn):
@staticmethod
def signature() -> List[Operand]:
return []
@staticmethod
def cycles() -> int:
return 8
def exec(self, cpu: CPU) -> None:
hl = cpu.get_reg16(R16.HL)
cpu.set_reg8(R8.A, cpu.get_mem8(hl))
cpu.set_reg16(R16.HL, hl - 1)
def pretty(self) -> str:
return "LDD A, (HL)"
@dataclass
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
def exec(self, cpu: CPU) -> None:
cpu.set_reg16(self.rr, self.nn)
def pretty(self) -> str:
return f"LD {self.rr.value}, {hex(self.nn)}"
@dataclass
class LD_NN_SP(Insn):
nn: int
@staticmethod
def signature() -> List[Operand]:
return [Operand.IMM16]
@staticmethod
def cycles() -> int:
return 20
def exec(self, cpu: CPU) -> None:
cpu.set_mem16(self.nn, cpu.get_reg16(R16.SP))
def pretty(self) -> str:
return f"LD ({hex(self.nn)}), SP"
@dataclass
class LD_SP_HL(Insn):
@staticmethod
def signature() -> List[Operand]:
return []
@staticmethod
def cycles() -> int:
return 8
def exec(self, cpu: CPU) -> None:
cpu.set_reg16(R16.SP, cpu.get_reg16(R16.HL))
def pretty(self) -> str:
return "LD SP, HL"
# NOTE: Normally we would support PUSH AF, but then non-carry flags could impact
# the program (by popping or inspecting where F was stored)
@dataclass
class PUSH_RR(Insn):
rr: R16
@staticmethod
def signature() -> List[Operand]:
return [Operand.R16_NO_SP]
@staticmethod
def cycles() -> int:
return 16
def exec(self, cpu: CPU) -> None:
sp = cpu.get_reg16(R16.SP) - 2
cpu.set_reg16(R16.SP, sp)
cpu.set_mem16(sp, cpu.get_reg16(self.rr))
def pretty(self) -> str:
return f"PUSH {self.rr.value}"
@dataclass
class POP_RR(Insn):
rr: R16
@staticmethod
def signature() -> List[Operand]:
return [Operand.R16_NO_SP]
@staticmethod
def cycles() -> int:
return 12
def exec(self, cpu: CPU) -> None:
sp = cpu.get_reg16(R16.SP)
cpu.set_reg16(self.rr, cpu.get_mem16(sp))
cpu.set_reg16(R16.SP, sp + 2)
def pretty(self) -> str:
return f"POP {self.rr.value}"
@dataclass
class ADD_A_R(Insn):
r: R8
@staticmethod
def signature() -> List[Operand]:
return [Operand.R8]
@staticmethod
def cycles() -> int:
return 4
def exec(self, cpu: CPU) -> None:
a = cpu.get_reg8(R8.A) + cpu.get_reg8(self.r)
cpu.state.carry = 1 if a > 0xFF else 0
cpu.set_reg8(R8.A, a)
def pretty(self) -> str:
return f"ADD A, {self.r.value}"
@dataclass
class ADD_A_N(Insn):
n: int
@staticmethod
def signature() -> List[Operand]:
return [Operand.IMM8]
@staticmethod
def cycles() -> int:
return 8
def exec(self, cpu: CPU) -> None:
a = cpu.get_reg8(R8.A) + self.n
cpu.state.carry = 1 if a > 0xFF else 0
cpu.set_reg8(R8.A, a)
def pretty(self) -> str:
return f"ADD A, {hex(self.n)}"
@dataclass
class ADD_A_HL(Insn):
@staticmethod
def signature() -> List[Operand]:
return []
@staticmethod
def cycles() -> int:
return 8
def exec(self, cpu: CPU) -> None:
a = cpu.get_reg8(R8.A) + cpu.get_mem8(cpu.get_reg16(R16.HL))
cpu.state.carry = 1 if a > 0xFF else 0
cpu.set_reg8(R8.A, a)
def pretty(self) -> str:
return f"ADD A, (HL)"
@dataclass
class ADC_A_R(Insn):
r: R8
@staticmethod
def signature() -> List[Operand]:
return [Operand.R8]
@staticmethod
def cycles() -> int:
return 4
def exec(self, cpu: CPU) -> None:
a = cpu.get_reg8(R8.A) + cpu.get_reg8(self.r) + cpu.state.carry
cpu.state.carry = 1 if a > 0xFF else 0
cpu.set_reg8(R8.A, a)
def pretty(self) -> str:
return f"ADC A, {self.r.value}"
@dataclass
class ADC_A_N(Insn):
n: int
@staticmethod
def signature() -> List[Operand]:
return [Operand.IMM8]
@staticmethod
def cycles() -> int:
return 8
def exec(self, cpu: CPU) -> None:
a = cpu.get_reg8(R8.A) + self.n + cpu.state.carry
cpu.state.carry = 1 if a > 0xFF else 0
cpu.set_reg8(R8.A, a)
def pretty(self) -> str:
return f"ADC A, {hex(self.n)}"
@dataclass
class ADC_A_HL(Insn):
@staticmethod
def signature() -> List[Operand]:
return []
@staticmethod
def cycles() -> int:
return 8
def exec(self, cpu: CPU) -> None:
a = cpu.get_reg8(R8.A) + cpu.get_mem8(cpu.get_reg16(R16.HL)) + cpu.state.carry
cpu.state.carry = 1 if a > 0xFF else 0
cpu.set_reg8(R8.A, a)
def pretty(self) -> str:
return f"ADC A, (HL)"
@dataclass
class SUB_A_R(Insn):
r: R8
@staticmethod
def signature() -> List[Operand]:
return [Operand.R8]
@staticmethod
def cycles() -> int:
return 4
def exec(self, cpu: CPU) -> None:
a = cpu.get_reg8(R8.A) - cpu.get_reg8(self.r)
cpu.state.carry = 1 if a < 0 else 0
cpu.set_reg8(R8.A, a)
def pretty(self) -> str:
return f"SUB A, {self.r.value}"
@dataclass
class SUB_A_N(Insn):
n: int
@staticmethod
def signature() -> List[Operand]:
return [Operand.IMM8]
@staticmethod
def cycles() -> int:
return 8
def exec(self, cpu: CPU) -> None:
a = cpu.get_reg8(R8.A) - self.n
cpu.state.carry = 1 if a < 0 else 0
cpu.set_reg8(R8.A, a)
def pretty(self) -> str:
return f"SUB A, {hex(self.n)}"
@dataclass
class SUB_A_HL(Insn):
@staticmethod
def signature() -> List[Operand]:
return []
@staticmethod
def cycles() -> int:
return 8
def exec(self, cpu: CPU) -> None:
a = cpu.get_reg8(R8.A) - cpu.get_mem8(cpu.get_reg16(R16.HL))
cpu.state.carry = 1 if a < 0 else 0
cpu.set_reg8(R8.A, a)
def pretty(self) -> str:
return "SUB A, (HL)"
@dataclass
class SBC_A_R(Insn):
r: R8
@staticmethod
def signature() -> List[Operand]:
return [Operand.R8]
@staticmethod
def cycles() -> int:
return 4
def exec(self, cpu: CPU) -> None:
a = cpu.get_reg8(R8.A) - cpu.get_reg8(self.r) - cpu.state.carry
cpu.state.carry = 1 if a < 0 else 0
cpu.set_reg8(R8.A, a)
def pretty(self) -> str:
return f"SBC A, {self.r.value}"
@dataclass
class SBC_A_N(Insn):
n: int
@staticmethod
def signature() -> List[Operand]:
return [Operand.IMM8]
@staticmethod
def cycles() -> int:
return 8
def exec(self, cpu: CPU) -> None:
a = cpu.get_reg8(R8.A) - self.n - cpu.state.carry
cpu.state.carry = 1 if a < 0 else 0
cpu.set_reg8(R8.A, a)
def pretty(self) -> str:
return f"SBC A, {hex(self.n)}"
@dataclass
class SBC_A_HL(Insn):
@staticmethod
def signature() -> List[Operand]:
return []
@staticmethod
def cycles() -> int:
return 8
def exec(self, cpu: CPU) -> None:
a = cpu.get_reg8(R8.A) - cpu.get_mem8(cpu.get_reg16(R16.HL)) - cpu.state.carry
cpu.state.carry = 1 if a < 0 else 0
cpu.set_reg8(R8.A, a)
def pretty(self) -> str:
return "SBC A, (HL)"
@dataclass
class AND_A_R(Insn):
r: R8
@staticmethod
def signature() -> List[Operand]:
return [Operand.R8]
@staticmethod
def cycles() -> int:
return 4
def exec(self, cpu: CPU) -> None:
a = cpu.get_reg8(R8.A) & cpu.get_reg8(self.r)
cpu.set_reg8(R8.A, a)
cpu.state.carry = 0
def pretty(self) -> str:
return f"AND A, {self.r.value}"
@dataclass
class AND_A_N(Insn):
n: int
@staticmethod
def signature() -> List[Operand]:
return [Operand.IMM8]
@staticmethod
def cycles() -> int:
return 8
def exec(self, cpu: CPU) -> None:
a = cpu.get_reg8(R8.A) & self.n
cpu.set_reg8(R8.A, a)
cpu.state.carry = 0
def pretty(self) -> str:
return f"AND A, {hex(self.n)}"
@dataclass
class AND_A_HL(Insn):
@staticmethod
def signature() -> List[Operand]:
return []
@staticmethod
def cycles() -> int:
return 8
def exec(self, cpu: CPU) -> None:
a = cpu.get_reg8(R8.A) & cpu.get_mem8(cpu.get_reg16(R16.HL))
cpu.set_reg8(R8.A, a)
cpu.state.carry = 0
def pretty(self) -> str:
return "AND A, (HL)"
@dataclass
class XOR_A_R(Insn):
r: R8
@staticmethod
def signature() -> List[Operand]:
return [Operand.R8]
@staticmethod
def cycles() -> int:
return 4
def exec(self, cpu: CPU) -> None:
a = cpu.get_reg8(R8.A) ^ cpu.get_reg8(self.r)
cpu.set_reg8(R8.A, a)
cpu.state.carry = 0
def pretty(self) -> str:
return f"XOR A, {self.r.value}"
@dataclass
class XOR_A_N(Insn):
n: int
@staticmethod
def signature() -> List[Operand]:
return [Operand.IMM8]
@staticmethod
def cycles() -> int:
return 8
def exec(self, cpu: CPU) -> None:
a = cpu.get_reg8(R8.A) ^ self.n
cpu.set_reg8(R8.A, a)
cpu.state.carry = 0
def pretty(self) -> str:
return f"XOR A, {hex(self.n)}"
@dataclass
class XOR_A_HL(Insn):
@staticmethod
def signature() -> List[Operand]:
return []
@staticmethod
def cycles() -> int:
return 8
def exec(self, cpu: CPU) -> None:
a = cpu.get_reg8(R8.A) ^ cpu.get_mem8(cpu.get_reg16(R16.HL))
cpu.set_reg8(R8.A, a)
cpu.state.carry = 0
def pretty(self) -> str:
return "XOR A, (HL)"
@dataclass
class OR_A_R(Insn):
r: R8
@staticmethod
def signature() -> List[Operand]:
return [Operand.R8]
@staticmethod
def cycles() -> int:
return 4
def exec(self, cpu: CPU) -> None:
a = cpu.get_reg8(R8.A) | cpu.get_reg8(self.r)
cpu.set_reg8(R8.A, a)
cpu.state.carry = 0
def pretty(self) -> str:
return f"OR A, {self.r.value}"
@dataclass
class OR_A_N(Insn):
n: int
@staticmethod
def signature() -> List[Operand]:
return [Operand.IMM8]
@staticmethod
def cycles() -> int:
return 8
def exec(self, cpu: CPU) -> None:
a = cpu.get_reg8(R8.A) | self.n
cpu.set_reg8(R8.A, a)
cpu.state.carry = 0
def pretty(self) -> str:
return f"OR A, {hex(self.n)}"
@dataclass
class OR_A_HL(Insn):
@staticmethod
def signature() -> List[Operand]:
return []
@staticmethod
def cycles() -> int:
return 8
def exec(self, cpu: CPU) -> None:
a = cpu.get_reg8(R8.A) | cpu.get_mem8(cpu.get_reg16(R16.HL))
cpu.set_reg8(R8.A, a)
cpu.state.carry = 0
def pretty(self) -> str:
return "OR A, (HL)"
@dataclass
class CP_A_R(Insn):
r: R8
@staticmethod
def signature() -> List[Operand]:
return [Operand.R8]
@staticmethod
def cycles() -> int:
return 4
def exec(self, cpu: CPU) -> None:
cpu.state.carry = 1 if cpu.get_reg8(R8.A) < cpu.get_reg8(self.r) else 0
def pretty(self) -> str:
return f"CP A, {self.r.value}"
@dataclass
class CP_A_N(Insn):
n: int
@staticmethod
def signature() -> List[Operand]:
return [Operand.IMM8]
@staticmethod
def cycles() -> int:
return 8
def exec(self, cpu: CPU) -> None:
cpu.state.carry = 1 if cpu.get_reg8(R8.A) < self.n else 0
def pretty(self) -> str:
return f"CP A, {hex(self.n)}"
@dataclass
class CP_A_HL(Insn):
@staticmethod
def signature() -> List[Operand]:
return []
@staticmethod
def cycles() -> int:
return 8
def exec(self, cpu: CPU) -> None:
cpu.state.carry = (
1 if cpu.get_reg8(R8.A) < cpu.get_mem8(cpu.get_reg16(R16.HL)) else 0
)
def pretty(self) -> str:
return "CP A, (HL)"
@dataclass
class INC_R(Insn):
r: R8
@staticmethod
def signature() -> List[Operand]:
return [Operand.R8]
@staticmethod
def cycles() -> int:
return 4
def exec(self, cpu: CPU) -> None:
cpu.set_reg8(self.r, cpu.get_reg8(self.r) + 1)
def pretty(self) -> str:
return f"INC {self.r.value}"
@dataclass
class INC_HL(Insn):
@staticmethod
def signature() -> List[Operand]:
return []
@staticmethod
def cycles() -> int:
return 12
def exec(self, cpu: CPU) -> None:
hl = cpu.get_reg16(R16.HL)
cpu.set_mem8(hl, cpu.get_mem8(hl) + 1)
def pretty(self) -> str:
return "INC (HL)"
@dataclass
class DEC_R(Insn):
r: R8
@staticmethod
def signature() -> List[Operand]:
return [Operand.R8]
@staticmethod
def cycles() -> int:
return 4
def exec(self, cpu: CPU) -> None:
cpu.set_reg8(self.r, cpu.get_reg8(self.r) - 1)
def pretty(self) -> str:
return f"DEC {self.r.value}"
@dataclass
class DEC_HL(Insn):
@staticmethod
def signature() -> List[Operand]:
return []
@staticmethod
def cycles() -> int:
return 12
def exec(self, cpu: CPU) -> None:
hl = cpu.get_reg16(R16.HL)
cpu.set_mem8(hl, cpu.get_mem8(hl) - 1)
def pretty(self) -> str:
return "DEC (HL)"
# TODO: Implement DAA
@dataclass
class CPL(Insn):
@staticmethod
def signature() -> List[Operand]:
return []
@staticmethod
def cycles() -> int:
return 4
def exec(self, cpu: CPU) -> None:
cpu.set_reg8(R8.A, cpu.get_reg8(R8.A) ^ 0xFF)
def pretty(self) -> str:
return "CPL"
@dataclass
class ADD_HL_RR(Insn):
rr: R16
@staticmethod
def signature() -> List[Operand]:
return [Operand.R16]
@staticmethod
def cycles() -> int:
return 8
def exec(self, cpu: CPU) -> None:
hl = cpu.get_reg16(R16.HL) + cpu.get_reg16(self.rr)
cpu.state.carry = 1 if hl > 0xFFFF else 0
cpu.set_reg16(R16.HL, hl)
def pretty(self) -> str:
return f"ADD HL, {self.rr.value}"
@dataclass
class INC_RR(Insn):
rr: R16
@staticmethod
def signature() -> List[Operand]:
return [Operand.R16]
@staticmethod
def cycles() -> int:
return 8
def exec(self, cpu: CPU) -> None:
cpu.set_reg16(self.rr, cpu.get_reg16(self.rr) + 1)
def pretty(self) -> str:
return f"INC {self.rr.value}"
@dataclass
class DEC_RR(Insn):
rr: R16
@staticmethod
def signature() -> List[Operand]:
return [Operand.R16]
@staticmethod
def cycles() -> int:
return 8
def exec(self, cpu: CPU) -> None:
cpu.set_reg16(self.rr, cpu.get_reg16(self.rr) - 1)
def pretty(self) -> str:
return f"DEC {self.rr.value}"
@dataclass
class ADD_SP_DD(Insn):
dd: int
@staticmethod
def signature() -> List[Operand]:
return [Operand.SIMM8]
@staticmethod
def cycles() -> int:
return 16
def exec(self, cpu: CPU) -> None:
sp = cpu.get_reg16(R16.SP) + self.dd
cpu.state.carry = 1 if sp > 0xFFFF or sp < 0 else 0
cpu.set_reg16(R16.SP, sp & 0xFFFF)
def pretty(self) -> str:
return f"ADD SP, {self.dd}"
@dataclass
class LD_HL_SP_DD(Insn):
dd: int
@staticmethod
def signature() -> List[Operand]:
return [Operand.SIMM8]
@staticmethod
def cycles() -> int:
return 12
def exec(self, cpu: CPU) -> None:
sp = cpu.get_reg16(R16.SP) + self.dd
cpu.state.carry = 1 if sp > 0xFFFF or sp < 0 else 0
cpu.set_reg16(R16.HL, sp & 0xFFFF)
def pretty(self) -> str:
return f"LD HL, SP + {self.dd}"
@dataclass
class RLCA(Insn):
@staticmethod
def signature() -> List[Operand]:
return []
@staticmethod
def cycles() -> int:
return 4
def exec(self, cpu: CPU) -> None:
a = cpu.get_reg8(R8.A)
cpu.state.carry = (a >> 7) & 1
cpu.set_reg8(R8.A, ((a << 1) & 0xFF) | cpu.state.carry)
def pretty(self) -> str:
return "RLCA"
@dataclass
class RLA(Insn):
@staticmethod
def signature() -> List[Operand]:
return []
@staticmethod
def cycles() -> int:
return 4
def exec(self, cpu: CPU) -> None:
a = cpu.get_reg8(R8.A)
next_carry = (a >> 7) & 1
cpu.set_reg8(R8.A, ((a << 1) & 0xFF) | cpu.state.carry)
cpu.state.carry = next_carry
def pretty(self) -> str:
return "RLA"
@dataclass
class RRCA(Insn):
@staticmethod
def signature() -> List[Operand]:
return []
@staticmethod
def cycles() -> int:
return 4
def exec(self, cpu: CPU) -> None:
a = cpu.get_reg8(R8.A)
cpu.state.carry = a & 1
cpu.set_reg8(R8.A, (a >> 1) | (cpu.state.carry << 7))
def pretty(self) -> str:
return "RRCA"
@dataclass
class RRA(Insn):
@staticmethod
def signature() -> List[Operand]:
return []
@staticmethod
def cycles() -> int:
return 4
def exec(self, cpu: CPU) -> None:
a = cpu.get_reg8(R8.A)
next_carry = a & 1
cpu.set_reg8(R8.A, (a >> 1) | (cpu.state.carry << 7))
cpu.state.carry = next_carry
def pretty(self) -> str:
return "RRA"
@dataclass
class RLC_R(Insn):
r: R8
@staticmethod
def signature() -> List[Operand]:
return [Operand.R8]
@staticmethod
def cycles() -> int:
return 8
def exec(self, cpu: CPU) -> None:
r = cpu.get_reg8(self.r)
cpu.state.carry = (r >> 7) & 1
cpu.set_reg8(self.r, ((r << 1) & 0xFF) | cpu.state.carry)
def pretty(self) -> str:
return f"RLC {self.r.value}"
@dataclass
class RLC_HL(Insn):
@staticmethod
def signature() -> List[Operand]:
return []
@staticmethod
def cycles() -> int:
return 16
def exec(self, cpu: CPU) -> None:
cpu.state.carry = (cpu.hl >> 7) & 1
cpu.hl = ((cpu.hl << 1) & 0xFF) | cpu.state.carry
def pretty(self) -> str:
return f"RLC (HL)"
@dataclass
class RL_R(Insn):
r: R8
@staticmethod
def signature() -> List[Operand]:
return [Operand.R8]
@staticmethod
def cycles() -> int:
return 8
def exec(self, cpu: CPU) -> None:
r = cpu.get_reg8(self.r)
next_carry = (r >> 7) & 1
cpu.set_reg8(self.r, ((r << 1) & 0xFF) | cpu.state.carry)
cpu.state.carry = next_carry
def pretty(self) -> str:
return f"RL {self.r.value}"
@dataclass
class RL_HL(Insn):
@staticmethod
def signature() -> List[Operand]:
return []
@staticmethod
def cycles() -> int:
return 16
def exec(self, cpu: CPU) -> None:
next_carry = (cpu.hl >> 7) & 1
cpu.hl = ((cpu.hl << 1) & 0xFF) | cpu.state.carry
cpu.state.carry = next_carry
def pretty(self) -> str:
return "RL (HL)"
@dataclass
class RRC_R(Insn):
r: R8
@staticmethod
def signature() -> List[Operand]:
return [Operand.R8]
@staticmethod
def cycles() -> int:
return 8
def exec(self, cpu: CPU) -> None:
r = cpu.get_reg8(self.r)
cpu.state.carry = r & 1
cpu.set_reg8(self.r, (r >> 1) | (cpu.state.carry << 7))
def pretty(self) -> str:
return f"RRC {self.r.value}"
@dataclass
class RRC_HL(Insn):
@staticmethod
def signature() -> List[Operand]:
return []
@staticmethod
def cycles() -> int:
return 16
def exec(self, cpu: CPU) -> None:
cpu.state.carry = cpu.hl & 1
cpu.hl = (cpu.hl >> 1) | (cpu.state.carry << 7)
def pretty(self) -> str:
return "RRC (HL)"
@dataclass
class RR_R(Insn):
r: R8
@staticmethod
def signature() -> List[Operand]:
return [Operand.R8]
@staticmethod
def cycles() -> int:
return 8
def exec(self, cpu: CPU) -> None:
r = cpu.get_reg8(self.r)
next_carry = r & 1
cpu.set_reg8(self.r, (r >> 1) | (cpu.state.carry << 7))
cpu.state.carry = next_carry
def pretty(self) -> str:
return f"RR {self.r.value}"
@dataclass
class RR_HL(Insn):
@staticmethod
def signature() -> List[Operand]:
return []
@staticmethod
def cycles() -> int:
return 16
def exec(self, cpu: CPU) -> None:
next_carry = cpu.hl & 1
cpu.hl = (cpu.hl >> 1) | (cpu.state.carry << 7)
cpu.state.carry = next_carry
def pretty(self) -> str:
return "RR (HL)"
@dataclass
class SLA_R(Insn):
r: R8
@staticmethod
def signature() -> List[Operand]:
return [Operand.R8]
@staticmethod
def cycles() -> int:
return 8
def exec(self, cpu: CPU) -> None:
r = cpu.get_reg8(self.r)
cpu.state.carry = 1 if r & (1 << 7) else 0
cpu.set_reg8(self.r, (r << 1) & 0xFF)
def pretty(self) -> str:
return f"SLA {self.r.value}"
@dataclass
class SLA_HL(Insn):
@staticmethod
def signature() -> List[Operand]:
return []
@staticmethod
def cycles() -> int:
return 16
def exec(self, cpu: CPU) -> None:
cpu.state.carry = 1 if cpu.hl & (1 << 7) else 0
cpu.hl = (cpu.hl << 1) & 0xFF
def pretty(self) -> str:
return "SLA (HL)"
@dataclass
class SWAP_R(Insn):
r: R8
@staticmethod
def signature() -> List[Operand]:
return [Operand.R8]
@staticmethod
def cycles() -> int:
return 8
def exec(self, cpu: CPU) -> None:
r = cpu.get_reg8(self.r)
cpu.set_reg8(self.r, ((r << 4) & 0xFF) | (r >> 4))
cpu.state.carry = 0
def pretty(self) -> str:
return f"SWAP {self.r.value}"
@dataclass
class SWAP_HL(Insn):
@staticmethod
def signature() -> List[Operand]:
return []
@staticmethod
def cycles() -> int:
return 16
def exec(self, cpu: CPU) -> None:
cpu.hl = ((cpu.hl << 4) & 0xFF) | (cpu.hl >> 4)
cpu.state.carry = 0
def pretty(self) -> str:
return "SWAP (HL)"
@dataclass
class SRA_R(Insn):
r: R8
@staticmethod
def signature() -> List[Operand]:
return [Operand.R8]
@staticmethod
def cycles() -> int:
return 8
def exec(self, cpu: CPU) -> None:
r = cpu.get_reg8(self.r)
cpu.state.carry = 1 if r & (1 << 0) else 0
cpu.set_reg8(self.r, (r >> 1) | (r & (1 << 7)))
def pretty(self) -> str:
return f"SRA {self.r.value}"
@dataclass
class SRA_HL(Insn):
@staticmethod
def signature() -> List[Operand]:
return []
@staticmethod
def cycles() -> int:
return 16
def exec(self, cpu: CPU) -> None:
cpu.state.carry = 1 if cpu.hl & (1 << 0) else 0
cpu.hl = (cpu.hl >> 1) | (cpu.hl & (1 << 7))
def pretty(self) -> str:
return "SRA (HL)"
@dataclass
class SRL_R(Insn):
r: R8
@staticmethod
def signature() -> List[Operand]:
return [Operand.R8]
@staticmethod
def cycles() -> int:
return 8
def exec(self, cpu: CPU) -> None:
r = cpu.get_reg8(self.r)
cpu.state.carry = 1 if r & (1 << 0) else 0
cpu.set_reg8(self.r, r >> 1)
def pretty(self) -> str:
return f"SRL {self.r.value}"
@dataclass
class SRL_HL(Insn):
@staticmethod
def signature() -> List[Operand]:
return []
@staticmethod
def cycles() -> int:
return 16
def exec(self, cpu: CPU) -> None:
cpu.state.carry = 1 if cpu.hl & (1 << 0) else 0
cpu.hl = cpu.hl >> 1
def pretty(self) -> str:
return "SRL (HL)"
@dataclass
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
def exec(self, cpu: CPU) -> None:
cpu.set_reg8(self.r, cpu.get_reg8(self.r) | (1 << self.n))
def pretty(self) -> str:
return f"SET {self.n}, {self.r.value}"
@dataclass
class SET_N_HL(Insn):
n: int
@staticmethod
def signature() -> List[Operand]:
return [Operand.IMM3]
@staticmethod
def cycles() -> int:
return 16
def exec(self, cpu: CPU) -> None:
hl = cpu.get_reg16(R16.HL)
cpu.set_mem8(hl, cpu.get_mem8(hl) | (1 << self.n))
def pretty(self) -> str:
return f"SET {self.n}, (HL)"
@dataclass
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
def exec(self, cpu: CPU) -> None:
cpu.set_reg8(self.r, cpu.get_reg8(self.r) & ~(1 << self.n))
def pretty(self) -> str:
return f"RES {self.n}, (HL)"
@dataclass
class RES_N_HL(Insn):
n: int
@staticmethod
def signature() -> List[Operand]:
return [Operand.IMM3]
@staticmethod
def cycles() -> int:
return 16
def exec(self, cpu: CPU) -> None:
hl = cpu.get_reg16(R16.HL)
cpu.set_mem8(hl, cpu.get_mem8(hl) & ~(1 << self.n))
def pretty(self) -> str:
return f"RES {self.n}, (HL)"
@dataclass
class CCF(Insn):
@staticmethod
def signature() -> List[Operand]:
return []
@staticmethod
def cycles() -> int:
return 4
def exec(self, cpu: CPU) -> None:
cpu.state.carry = cpu.state.carry ^ 1
def pretty(self) -> str:
return "CCF"
@dataclass
class SCF(Insn):
@staticmethod
def signature() -> List[Operand]:
return []
@staticmethod
def cycles() -> int:
return 4
def exec(self, cpu: CPU) -> None:
cpu.state.carry = 1
def pretty(self) -> str:
return "SCF"
@dataclass
class UNUSED(Insn):
@staticmethod
def signature() -> List[Operand]:
return []
@staticmethod
def cycles() -> int:
return 0
def exec(self, cpu: CPU) -> None:
pass
def pretty(self) -> str:
return "UNUSED"
ALL_INSN_CLASSES: List[Type[Insn]] = [
cls for cls in Insn.__subclasses__() if cls not in {Insn, UNUSED} # 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)