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.
 

869 lines
18 KiB

from abc import ABC, abstractmethod
from dataclasses import dataclass
from gbso.cpu import CPU
from gbso.regs import R16, R8
class Insn(ABC):
@abstractmethod
def exec(self, cpu: CPU) -> None:
pass
# TODO: Make abstract once all subclasses implement
def pretty(self) -> str:
return ""
@dataclass
class LD_R_R(Insn):
dst: R8
src: R8
def exec(self, cpu: CPU) -> None:
cpu.set_reg8(self.dst, cpu.get_reg8(self.src))
cpu.cycles += 4
def pretty(self) -> str:
return f"LD {self.dst}, {self.src}"
@dataclass
class LD_R_N8(Insn):
dst: R8
imm: int
def exec(self, cpu: CPU) -> None:
cpu.set_reg8(self.dst, self.imm)
cpu.cycles += 8
def pretty(self) -> str:
return f"LD {self.dst}, {hex(self.imm)}"
@dataclass
class LD_R_HL(Insn):
dst: R8
def exec(self, cpu: CPU) -> None:
cpu.set_reg8(self.dst, cpu.get_mem8(cpu.get_reg16(R16.HL)))
cpu.cycles += 8
def pretty(self) -> str:
return f"LD {self.dst}, (HL)"
@dataclass
class LD_HL_R(Insn):
src: R8
def exec(self, cpu: CPU) -> None:
cpu.deref_hl_set(cpu.get_reg8(self.src))
cpu.cycles += 8
def pretty(self) -> str:
return f"LD (HL), {self.src}"
@dataclass
class LD_HL_N(Insn):
imm: int
def exec(self, cpu: CPU) -> None:
cpu.set_mem8(cpu.get_reg16(R16.HL), self.imm)
cpu.cycles += 12
def pretty(self) -> str:
return f"LD (HL), {hex(self.imm & 0xff)}"
@dataclass
class LD_A_BC(Insn):
def exec(self, cpu: CPU) -> None:
cpu.set_reg8(R8.A, cpu.get_mem8(cpu.get_reg16(R16.BC)))
cpu.cycles += 8
def pretty(self) -> str:
return "LD A, (BC)"
@dataclass
class LD_A_DE(Insn):
def exec(self, cpu: CPU) -> None:
cpu.set_reg8(R8.A, cpu.get_mem8(cpu.get_reg16(R16.DE)))
cpu.cycles += 8
def pretty(self) -> str:
return "LD A, (DE)"
@dataclass
class LD_A_NN(Insn):
nn: int
def exec(self, cpu: CPU) -> None:
cpu.set_reg8(R8.A, cpu.get_mem8(self.nn))
cpu.cycles += 16
def pretty(self) -> str:
return f"LD A, ({hex(self.nn)})"
@dataclass
class LD_BC_A(Insn):
def exec(self, cpu: CPU) -> None:
cpu.set_mem8(cpu.get_reg16(R16.BC), cpu.get_reg8(R8.A))
cpu.cycles += 8
def pretty(self) -> str:
return "LD (BC), A"
@dataclass
class LD_DE_A(Insn):
def exec(self, cpu: CPU) -> None:
cpu.set_mem8(cpu.get_reg16(R16.DE), cpu.get_reg8(R8.A))
cpu.cycles += 8
def pretty(self) -> str:
return "LD (DE), A"
@dataclass
class LD_NN_A(Insn):
nn: int
def exec(self, cpu: CPU) -> None:
cpu.set_mem8(self.nn, cpu.get_reg8(R8.A))
cpu.cycles += 16
def pretty(self) -> str:
return f"LD ({hex(self.nn)}), A"
@dataclass
class LD_A_FF_N(Insn):
n: int
def exec(self, cpu: CPU) -> None:
cpu.set_reg8(R8.A, cpu.get_mem8(0xFF00 + self.n))
cpu.cycles += 12
def pretty(self) -> str:
return f"LD A, (0xFF00 + {hex(self.n)})"
@dataclass
class LD_FF_N_A(Insn):
n: int
def exec(self, cpu: CPU) -> None:
cpu.set_mem8(0xFF00 + self.n, cpu.get_reg8(R8.A))
cpu.cycles += 12
def pretty(self) -> str:
return f"LD (0xFF00 + {hex(self.n)}), A"
@dataclass
class LD_A_FF_C(Insn):
def exec(self, cpu: CPU) -> None:
cpu.set_reg8(R8.A, cpu.get_mem8(0xFF00 + cpu.get_reg8(R8.C)))
cpu.cycles += 8
def pretty(self) -> str:
return "LD A, (0xFF00 + C)"
@dataclass
class LD_FF_C_A(Insn):
def exec(self, cpu: CPU) -> None:
cpu.set_mem8(0xFF00 + cpu.get_reg8(R8.C), cpu.get_reg8(R8.A))
cpu.cycles += 8
def pretty(self) -> str:
return "LD (0xFF00 + C), A"
@dataclass
class LDI_HL_A(Insn):
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)
cpu.cycles += 8
def pretty(self) -> str:
return "LDI (HL), A"
@dataclass
class LDI_A_HL(Insn):
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)
cpu.cycles += 8
def pretty(self) -> str:
return "LDI A, (HL)"
@dataclass
class LDD_HL_A(Insn):
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)
cpu.cycles += 8
def pretty(self) -> str:
return "LDD (HL), A"
@dataclass
class LDD_A_HL(Insn):
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)
cpu.cycles += 8
def pretty(self) -> str:
return "LDD A, (HL)"
@dataclass
class LD_RR_NN(Insn):
rr: R16
nn: int
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
def exec(self, cpu: CPU) -> None:
cpu.set_mem16(self.nn, cpu.get_reg16(R16.SP))
@dataclass
class LD_SP_HL(Insn):
def exec(self, cpu: CPU) -> None:
cpu.set_reg16(R16.SP, cpu.get_reg16(R16.HL))
@dataclass
class PUSH_RR(Insn):
rr: R16
def exec(self, cpu: CPU) -> None:
sp = cpu.get_reg16(R16.SP)
cpu.set_reg16(R16.SP, sp - 2)
cpu.set_mem16(sp - 2, cpu.get_reg16(self.rr))
@dataclass
class POP_RR(Insn):
rr: R16
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)
@dataclass
class ADD_A_R(Insn):
r: R8
def exec(self, cpu: CPU) -> None:
a = cpu.get_reg8(R8.A) + cpu.get_reg8(self.r)
cpu.carry = 1 if a > 0xFF else 0
cpu.set_reg8(R8.A, a)
@dataclass
class ADD_A_N(Insn):
n: int
def exec(self, cpu: CPU) -> None:
a = cpu.get_reg8(R8.A) + self.n
cpu.carry = 1 if a > 0xFF else 0
cpu.set_reg8(R8.A, a)
@dataclass
class ADD_A_HL(Insn):
def exec(self, cpu: CPU) -> None:
a = cpu.get_reg8(R8.A) + cpu.get_mem8(cpu.get_reg16(R16.HL))
cpu.carry = 1 if a > 0xFF else 0
cpu.set_reg8(R8.A, a)
@dataclass
class ADC_A_R(Insn):
r: R8
def exec(self, cpu: CPU) -> None:
a = cpu.get_reg8(R8.A) + cpu.get_reg8(self.r) + cpu.carry
cpu.carry = 1 if a > 0xFF else 0
cpu.set_reg8(R8.A, a)
@dataclass
class ADC_A_N(Insn):
n: int
def exec(self, cpu: CPU) -> None:
a = cpu.get_reg8(R8.A) + self.n + cpu.carry
cpu.carry = 1 if a > 0xFF else 0
cpu.set_reg8(R8.A, a)
@dataclass
class ADC_A_HL(Insn):
def exec(self, cpu: CPU) -> None:
a = cpu.get_reg8(R8.A) + cpu.get_mem8(cpu.get_reg16(R16.HL)) + cpu.carry
cpu.carry = 1 if a > 0xFF else 0
cpu.set_reg8(R8.A, a)
@dataclass
class SUB_A_R(Insn):
r: R8
def exec(self, cpu: CPU) -> None:
a = cpu.get_reg8(R8.A) - cpu.get_reg8(self.r)
cpu.carry = 1 if a < 0 else 0
cpu.set_reg8(R8.A, a)
@dataclass
class SUB_A_N(Insn):
n: int
def exec(self, cpu: CPU) -> None:
a = cpu.get_reg8(R8.A) - self.n
cpu.carry = 1 if a < 0 else 0
cpu.set_reg8(R8.A, a)
@dataclass
class SUB_A_HL(Insn):
def exec(self, cpu: CPU) -> None:
a = cpu.get_reg8(R8.A) - cpu.get_mem8(cpu.get_reg16(R16.HL))
cpu.carry = 1 if a < 0 else 0
cpu.set_reg8(R8.A, a)
@dataclass
class SBC_A_R(Insn):
r: R8
def exec(self, cpu: CPU) -> None:
a = cpu.get_reg8(R8.A) - cpu.get_reg8(self.r) - cpu.carry
cpu.carry = 1 if a < 0 else 0
cpu.set_reg8(R8.A, a)
@dataclass
class SBC_A_N(Insn):
n: int
def exec(self, cpu: CPU) -> None:
a = cpu.get_reg8(R8.A) - self.n - cpu.carry
cpu.carry = 1 if a < 0 else 0
cpu.set_reg8(R8.A, a)
@dataclass
class SBC_A_HL(Insn):
def exec(self, cpu: CPU) -> None:
a = cpu.get_reg8(R8.A) - cpu.get_mem8(cpu.get_reg16(R16.HL)) - cpu.carry
cpu.carry = 1 if a < 0 else 0
cpu.set_reg8(R8.A, a)
@dataclass
class AND_A_R(Insn):
r: R8
def exec(self, cpu: CPU) -> None:
a = cpu.get_reg8(R8.A) & cpu.get_reg8(self.r)
cpu.set_reg8(R8.A, a)
cpu.carry = 0
@dataclass
class AND_A_N(Insn):
n: int
def exec(self, cpu: CPU) -> None:
a = cpu.get_reg8(R8.A) & self.n
cpu.set_reg8(R8.A, a)
cpu.carry = 0
@dataclass
class AND_A_HL(Insn):
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.carry = 0
def pretty(self) -> str:
return "AND A, (HL)"
@dataclass
class CP_A_R(Insn):
r: R8
def exec(self, cpu: CPU) -> None:
cpu.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
def exec(self, cpu: CPU) -> None:
cpu.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):
def exec(self, cpu: CPU) -> None:
cpu.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
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):
r: R8
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
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):
r: R8
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)"
@dataclass
class CPL(Insn):
def exec(self, cpu: CPU) -> None:
cpu.set_reg8(R8.A, cpu.get_reg8(R8.A) ^ 0xFF)
@dataclass
class ADD_HL_RR(Insn):
rr: R16
def exec(self, cpu: CPU) -> None:
hl = cpu.get_reg16(R16.HL) + cpu.get_reg16(self.rr)
cpu.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
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
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
def exec(self, cpu: CPU) -> None:
sp = cpu.get_reg16(R16.SP) + self.dd
cpu.carry = 1 if sp > 0xFFFF or sp < 0 else 0
cpu.set_reg16(R16.SP, sp)
def pretty(self) -> str:
return f"ADD SP, {self.dd}"
@dataclass
class LD_HL_SP_DD(Insn):
dd: int
def exec(self, cpu: CPU) -> None:
sp = cpu.get_reg16(R16.SP) + self.dd
cpu.carry = 1 if sp > 0xFFFF or sp < 0 else 0
cpu.set_reg16(R16.HL, sp)
def pretty(self) -> str:
return f"LD HL, SP + {self.dd}"
@dataclass
class RLCA(Insn):
def exec(self, cpu: CPU) -> None:
a = cpu.get_reg8(R8.A)
cpu.carry = (a >> 7) & 1
cpu.set_reg8(R8.A, ((a << 1) & 0xFF) | cpu.carry)
def pretty(self) -> str:
return "RLCA"
@dataclass
class RLA(Insn):
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.carry)
cpu.carry = next_carry
def pretty(self) -> str:
return "RLA"
@dataclass
class RRCA(Insn):
def exec(self, cpu: CPU) -> None:
a = cpu.get_reg8(R8.A)
cpu.carry = a & 1
cpu.set_reg8(R8.A, (a >> 1) | (cpu.carry << 7))
def pretty(self) -> str:
return "RRCA"
@dataclass
class RRA(Insn):
def exec(self, cpu: CPU) -> None:
a = cpu.get_reg8(R8.A)
next_carry = a & 1
cpu.set_reg8(R8.A, (a >> 1) | (cpu.carry << 7))
cpu.carry = next_carry
def pretty(self) -> str:
return "RRA"
@dataclass
class RLC_R(Insn):
r: R8
def exec(self, cpu: CPU) -> None:
r = cpu.get_reg8(self.r)
cpu.carry = (r >> 7) & 1
cpu.set_reg8(self.r, ((r << 1) & 0xFF) | cpu.carry)
def pretty(self) -> str:
return f"RLC {self.r}"
@dataclass
class RLC_HL(Insn):
def exec(self, cpu: CPU) -> None:
hl = cpu.deref_hl()
cpu.carry = (hl >> 7) & 1
cpu.deref_hl_set(((hl << 1) & 0xFF) | cpu.carry)
def pretty(self) -> str:
return f"RLC (HL)"
@dataclass
class RL_R(Insn):
r: R8
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.carry)
cpu.carry = next_carry
def pretty(self) -> str:
return f"RL {self.r}"
@dataclass
class RL_HL(Insn):
def exec(self, cpu: CPU) -> None:
hl = cpu.deref_hl()
next_carry = (hl >> 7) & 1
cpu.deref_hl_set(((hl << 1) & 0xFF) | cpu.carry)
cpu.carry = next_carry
def pretty(self) -> str:
return "RL (HL)"
@dataclass
class RRC_R(Insn):
r: R8
def exec(self, cpu: CPU) -> None:
r = cpu.get_reg8(self.r)
cpu.carry = r & 1
cpu.set_reg8(self.r, (r >> 1) | (cpu.carry << 7))
def pretty(self) -> str:
return f"RRC {self.r}"
@dataclass
class RRC_HL(Insn):
def exec(self, cpu: CPU) -> None:
hl = cpu.deref_hl()
cpu.carry = hl & 1
cpu.deref_hl_set((hl >> 1) | (cpu.carry << 7))
def pretty(self) -> str:
return "RRC (HL)"
@dataclass
class RR_R(Insn):
r: R8
def exec(self, cpu: CPU) -> None:
r = cpu.get_reg8(self.r)
next_carry = r & 1
cpu.set_reg8(self.r, (r >> 1) | (cpu.carry << 7))
cpu.carry = next_carry
def pretty(self) -> str:
return f"RR {self.r}"
@dataclass
class RR_HL(Insn):
def exec(self, cpu: CPU) -> None:
hl = cpu.deref_hl()
next_carry = hl & 1
cpu.deref_hl_set((hl >> 1) | (cpu.carry << 7))
cpu.carry = next_carry
def pretty(self) -> str:
return "RR (HL)"
@dataclass
class SLA_R(Insn):
r: R8
def exec(self, cpu: CPU) -> None:
r = cpu.get_reg8(self.r)
cpu.set_reg8(self.r, (r << 1) & 0xFF)
def pretty(self) -> str:
return f"SLA {self.r}"
@dataclass
class SLA_HL(Insn):
def exec(self, cpu: CPU) -> None:
hl = cpu.deref_hl()
cpu.deref_hl_set((hl << 1) & 0xFF)
def pretty(self) -> str:
return "SLA (HL)"
@dataclass
class SWAP_R(Insn):
r: R8
def exec(self, cpu: CPU) -> None:
r = cpu.get_reg8(self.r)
cpu.set_reg8(self.r, ((r << 4) & 0xFF) | (r >> 4))
def pretty(self) -> str:
return f"SWAP {self.r}"
@dataclass
class SWAP_HL(Insn):
def exec(self, cpu: CPU) -> None:
hl = cpu.deref_hl()
cpu.deref_hl_set(((hl << 4) & 0xFF) | (hl >> 4))
def pretty(self) -> str:
return "SWAP (HL)"
@dataclass
class SRA_R(Insn):
r: R8
def exec(self, cpu: CPU) -> None:
r = cpu.get_reg8(self.r)
cpu.set_reg8(self.r, (r >> 1) | (r & (1 << 7)))
def pretty(self) -> str:
return f"SRA {self.r}"
@dataclass
class SRA_HL(Insn):
def exec(self, cpu: CPU) -> None:
hl = cpu.deref_hl()
cpu.deref_hl_set((hl >> 1) | (hl & (1 << 7)))
def pretty(self) -> str:
return "SRA (HL)"
@dataclass
class SRL_R(Insn):
r: R8
def exec(self, cpu: CPU) -> None:
r = cpu.get_reg8(self.r)
cpu.set_reg8(self.r, r >> 1)
def pretty(self) -> str:
return f"SRL {self.r}"
@dataclass
class SRL_HL(Insn):
def exec(self, cpu: CPU) -> None:
hl = cpu.deref_hl()
cpu.deref_hl_set(hl >> 1)
def pretty(self) -> str:
return "SRL (HL)"
# TODO: Remaining rotate and shift instructions
@dataclass
class SET_N_R(Insn):
n: int
r: R8
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}, (HL)"
@dataclass
class SET_N_HL(Insn):
n: int
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
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
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):
def exec(self, cpu: CPU) -> None:
cpu.carry = cpu.carry ^ 1
def pretty(self) -> str:
return "CCF"
@dataclass
class SCF(Insn):
def exec(self, cpu: CPU) -> None:
cpu.carry = 1
def pretty(self) -> str:
return "SCF"