#pragma once #include "types.h" #include "memory/mem_device.h" typedef u8 opcode_t; enum Flags { F_ZERO = 0x8, F_SUB = 0x4, F_HALF = 0x2, F_CARRY = 0x1, }; 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 Cpu_state { // Registers union { u16 BC; struct { u8 B; u8 C; }; }; union { u16 DE; struct { u8 D; u8 E; }; }; union { u16 HL; struct { u8 H; u8 L; }; }; u8 A; u16 SP; u16 PC; bool zero; bool subtract; bool halfcarry; bool carry; IME_state IME; u8 IE; u8 IF; void setAF(u16 v); u16 getAF(); }; 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); 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; void signalInterrupt(InterruptType it); void step(); };