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)