From 505478b840d4080672c3af067e004d151a903942 Mon Sep 17 00:00:00 2001 From: MadMaurice Date: Mon, 28 Aug 2023 21:56:33 +0200 Subject: [PATCH] Improve Cpu class and implement interrupts --- cpu/cpu.cpp | 123 ++++++++++++++++++++++++++++++++++++++++++++++++ cpu/cpu.h | 52 +++++++++++++++----- cpu/decoder.cpp | 83 +++++--------------------------- cpu/panic.h | 10 ++++ 4 files changed, 183 insertions(+), 85 deletions(-) create mode 100644 cpu/panic.h diff --git a/cpu/cpu.cpp b/cpu/cpu.cpp index ea74ffa..088669c 100644 --- a/cpu/cpu.cpp +++ b/cpu/cpu.cpp @@ -18,6 +18,101 @@ u16 Cpu_state::getAF() (carry ? 0x10 : 0); } +Cpu::Cpu(Mem_device* bus) + : bus(bus) +{ + reset(); +} + +void Cpu::step() +{ + handleInterrupts(); + executeInstruction(); +} + +void Cpu::reset() +{ + state.BC = 0; + state.DE = 0; + state.HL = 0; + state.A = 0; + state.SP = 0; + state.PC = 0; + + state.zero = false; + state.subtract = false; + state.halfcarry = false; + state.carry = false; + + state.IME = IME_OFF; + state.IE = 0; + state.IF = 0; +} + +u8 Cpu::readPC8() +{ + u8 data = bus->read8(state.PC); + state.PC++; + return data; +} + +u16 Cpu::readPC16() +{ + u16 data = bus->read16(state.PC); + state.PC+=2; + return data; +} + +void Cpu::pushStack8(u8 data) +{ + bus->write8(state.SP, data); + state.SP--; +} + +u8 Cpu::popStack8() +{ + u8 data = bus->read8(state.SP); + state.SP++; + return data; +} + +void Cpu::pushStack16(u16 data) +{ + bus->write16(state.SP,data); + state.SP-=2; +} + +u16 Cpu::popStack16() +{ + u16 data = bus->read16(state.SP); + state.SP+=2; + return data; +} + +void Cpu::doCall(u16 target) +{ + pushStack16(state.PC); + state.PC = target; +} + +void Cpu::doRet() +{ + state.PC = popStack16(); +} + +bool Cpu::decodeCond(u8 cc) +{ + switch(cc) + { + case COND_NZ: return !state.zero; break; + case COND_Z: return state.zero; break; + case COND_NC: return !state.carry; break; + case COND_C: return state.carry; break; + } + + return false; +} + void Cpu::aluop8(AluOp op, u8 lhs, u8 rhs, u8& out, bool update_carry) { u16 rhs16 = rhs; @@ -89,3 +184,31 @@ void Cpu::aluop8(AluOp op, u8 lhs, u8 rhs, u8& out, bool update_carry) if (op != CP) out = res; } + +void Cpu::handleInterrupts() +{ + // servicable interrupts (assuming IME is on) + u8 si = state.IE & state.IF & INT_MASK; + + if (state.IME == IME_SCHEDULED) + state.IME = IME_DELAYED; + else if (state.IME == IME_DELAYED) + state.IME = IME_ON; + else if (state.IME == IME_ON && si != 0) + { + u16 isr; + InterruptType it; + + if (si & INT_VBlank) { it = INT_VBlank; isr = 0x40; } + else if (si & INT_LCDSTAT) { it = INT_LCDSTAT; isr = 0x48; } + else if (si & INT_Timer) { it = INT_Timer; isr = 0x50; } + else if (si & INT_Serial) { it = INT_Serial; isr = 0x58; } + else if (si & INT_Joypad) { it = INT_Joypad; isr = 0x60; } + + state.IME = IME_OFF; // Disable IME + state.IF &= ~it; // clear interrupt in IF + doCall(isr); // Call interrupt service routine + + processed_mcycles += 5; + } +} diff --git a/cpu/cpu.h b/cpu/cpu.h index ebe17cf..36bb1ce 100644 --- a/cpu/cpu.h +++ b/cpu/cpu.h @@ -2,8 +2,9 @@ #include "types.h" -class Cpu; -class Mem_device; +#include "memory/mem_device.h" + +typedef u8 opcode_t; enum Flags { F_ZERO = 0x8, @@ -31,6 +32,24 @@ enum CC COND_C = 3, }; +enum InterruptType : u8 +{ + INT_VBlank = 0x1, + INT_LCDSTAT = 0x2, + INT_Timer = 0x4, + INT_Serial = 0x8, + INT_Joypad = 0x10, + + INT_MASK = 0x1F, +}; + +enum IME_state +{ + IME_OFF, + IME_SCHEDULED, + IME_DELAYED, + IME_ON, +}; struct Cpu_state { // Registers @@ -56,23 +75,16 @@ struct Cpu_state { bool halfcarry; bool carry; - bool IME; // interrupts enabled/disabled - bool IME_scheduled; // interrupts about to be enabled + IME_state IME; - - bool bootRomEnabled; // Whether boot ROM is visible + u8 IE; + u8 IF; void setAF(u16 v); u16 getAF(); }; class Cpu { -private: - Cpu_state state; - Mem_device* bus; - - typedef u8 opcode_t; - private: u8 readPC8(); u16 readPC16(); @@ -96,8 +108,22 @@ private: bool decodeCond(u8 cc); + void handleInterrupts(); + void executeInstruction(); + + void reset(); + public: - Cpu(); + Cpu(Mem_device* bus); + + Cpu_state state; + Mem_device* bus; + unsigned long processed_mcycles; + + + void signal(InterruptType it); + void setIE(u8 val); + u8 getIE(); void step(); }; diff --git a/cpu/decoder.cpp b/cpu/decoder.cpp index f0f2a50..a93443a 100644 --- a/cpu/decoder.cpp +++ b/cpu/decoder.cpp @@ -1,4 +1,5 @@ #include "cpu/cpu.h" +#include "cpu/panic.h" #include "memory/mem_device.h" static inline u16 make_u16(u8 msb, u8 lsb) @@ -6,71 +7,7 @@ static inline u16 make_u16(u8 msb, u8 lsb) return (((u16)msb << 8) | (u16)lsb); } -u8 Cpu::readPC8() -{ - u8 data = bus->read8(state.PC); - state.PC++; - return data; -} - -u16 Cpu::readPC16() -{ - u16 data = bus->read16(state.PC); - state.PC+=2; - return data; -} - -void Cpu::pushStack8(u8 data) -{ - bus->write8(state.SP, data); - state.SP--; -} - -u8 Cpu::popStack8() -{ - u8 data = bus->read8(state.SP); - state.SP++; - return data; -} - -void Cpu::pushStack16(u16 data) -{ - bus->write16(state.SP,data); - state.SP-=2; -} - -u16 Cpu::popStack16() -{ - u16 data = bus->read16(state.SP); - state.SP+=2; - return data; -} - -void Cpu::doCall(u16 target) -{ - pushStack16(state.PC); - state.PC = target; -} - -void Cpu::doRet() -{ - state.PC = popStack16(); -} - -bool Cpu::decodeCond(u8 cc) -{ - switch(cc) - { - case COND_NZ: return !state.zero; break; - case COND_Z: return state.zero; break; - case COND_NC: return !state.carry; break; - case COND_C: return state.carry; break; - } - - return false; -} - -void Cpu::step() +void Cpu::executeInstruction() { opcode_t op = readPC8(); int mcycles = 1; @@ -403,9 +340,7 @@ void Cpu::step() state.halfcarry = false; break; - case 0x27: // DAA - // TODO - break; + // TODO: case 0x27: break; // DAA case 0x2F: // CPL Complement accumulator state.A = ~state.A; @@ -433,15 +368,19 @@ void Cpu::step() case 0xC9: // RET doRet(); mcycles = 4; break; case 0xD9: // RETI - doRet(); state.IME=true; mcycles = 4; break; + doRet(); state.IME = IME_ON; mcycles = 4; break; case 0xF3: // DI - state.IME = false; - state.IME_scheduled = false; + state.IME = IME_OFF; break; case 0xFB: // EI - state.IME_scheduled = true; + state.IME = IME_SCHEDULED; break; + + default: + panic("Unknown opcode 0x%x\n",op); } } + + processed_mcycles += mcycles; } diff --git a/cpu/panic.h b/cpu/panic.h new file mode 100644 index 0000000..250fd67 --- /dev/null +++ b/cpu/panic.h @@ -0,0 +1,10 @@ +#include +#include + + +template +inline void panic(Args... args) +{ + printf(args...); + exit(1); +}