#pragma once #include #include #include typedef u8 opcode_t; enum Flags { F_ZERO = 0x80, F_SUB = 0x40, F_HALF = 0x20, F_CARRY = 0x10, }; enum AluOp : int { ADD = 0, ADC = 1, SUB = 2, SBC = 3, AND = 4, XOR = 5, OR = 6, CP = 7, }; enum CC { COND_NZ = 0, COND_Z = 1, COND_NC = 2, COND_C = 3, }; enum InterruptType : u8 { INT_VBlank = 0x1, INT_LCDSTAT = 0x2, INT_Timer = 0x4, INT_Serial = 0x8, INT_Joypad = 0x10, INT_MASK = 0x1F, }; /** IME - Interrupt Master Enable An EI instruction will enable the interrupts, but delayed. During the next instruction after EI, interrupts are still disabled. For this to be emulated we use a small state machine. which works as follows instruction EI - sets IME_SCHEDULED handleInterrupts -> IME_SCHEDULED to IME_ON (but no call to isr yet) instruction any - is IME_ON, but no chance for call to isr yet handleInterrupts -> is IME_ON, do a call to isr if necessary */ enum IME_state { IME_OFF, IME_SCHEDULED, IME_ON, }; struct opcode { const opcode_t value; inline operator opcode_t() const { return value; } inline u8 reg8idxlo() const { return value & 0x7; } inline u8 reg8idxhi() const { return (value >> 3) & 0x7; } inline u8 reg16idx() const { return (value >> 4) & 0x3; } inline AluOp aluop() const { return (AluOp)((value >> 3) & 0x7); } inline u8 cc() const { return (value >> 3) & 0x3; } inline u16 rst_addr() const { return (u16)(value & 0x38); } }; class Cpu; struct Cpu_state { // Registers union { u16 BC; struct { u8 C; u8 B; }; }; union { u16 DE; struct { u8 E; u8 D; }; }; union { u16 HL; struct { u8 L; u8 H; }; }; u8 A; u16 SP; u16 PC; bool zero; bool subtract; bool halfcarry; bool carry; IME_state IME; u8 IE; u8 IF; // servicable interrupts inline u8 SI() const { return INT_MASK & IE & IF; } bool halted; bool haltbug; bool stopped; void setAF(u16 v); u16 getAF(); u8 getF(); u8& reg8(Cpu& cpu, u8 idx); u16& reg16(Cpu& cpu, u8 idx); }; class Cpu { private: u8 readPC8(); u16 readPC16(); void pushStack8(u8 data); u8 popStack8(); void pushStack16(u16 data); u16 popStack16(); void aluop8(AluOp op, u8 lhs, u8 rhs, u8& out, bool update_carry = true); void add16(u16& out, u16 lhs, u16 rhs); inline void aluop8(AluOp op, u8 rhs, bool update_carry = true) { aluop8(op, state.A, rhs, state.A, update_carry); } void doCall(u16 target); void doRet(); bool decodeCond(u8 cc); bool handleInterrupts(); void executeInstruction(); void reset(); public: Cpu(Mem_device* bus); Cpu_state state; Mem_device* bus; unsigned long processed_mcycles; u16 last_instr_addr; void signalInterrupt(InterruptType it); void step(); }; class CpuException : public EmulatorException { private: Cpu_state state; u16 instaddr; u8 instmem[4]; public: CpuException(Cpu& cpu, const char* msg); virtual const char* what() const noexcept override; };