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"