Improve Cpu class and implement interrupts

This commit is contained in:
madmaurice 2023-08-28 21:56:33 +02:00
parent e859f8ad2c
commit 505478b840
4 changed files with 183 additions and 85 deletions

View file

@ -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;
}
}

View file

@ -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();
};

View file

@ -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;
}

10
cpu/panic.h Normal file
View file

@ -0,0 +1,10 @@
#include <cstdio>
#include <cstdlib>
template <typename... Args>
inline void panic(Args... args)
{
printf(args...);
exit(1);
}