Improve Cpu class and implement interrupts
This commit is contained in:
parent
e859f8ad2c
commit
505478b840
4 changed files with 183 additions and 85 deletions
123
cpu/cpu.cpp
123
cpu/cpu.cpp
|
@ -18,6 +18,101 @@ u16 Cpu_state::getAF()
|
||||||
(carry ? 0x10 : 0);
|
(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)
|
void Cpu::aluop8(AluOp op, u8 lhs, u8 rhs, u8& out, bool update_carry)
|
||||||
{
|
{
|
||||||
u16 rhs16 = rhs;
|
u16 rhs16 = rhs;
|
||||||
|
@ -89,3 +184,31 @@ void Cpu::aluop8(AluOp op, u8 lhs, u8 rhs, u8& out, bool update_carry)
|
||||||
if (op != CP)
|
if (op != CP)
|
||||||
out = res;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
52
cpu/cpu.h
52
cpu/cpu.h
|
@ -2,8 +2,9 @@
|
||||||
|
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
|
||||||
class Cpu;
|
#include "memory/mem_device.h"
|
||||||
class Mem_device;
|
|
||||||
|
typedef u8 opcode_t;
|
||||||
|
|
||||||
enum Flags {
|
enum Flags {
|
||||||
F_ZERO = 0x8,
|
F_ZERO = 0x8,
|
||||||
|
@ -31,6 +32,24 @@ enum CC
|
||||||
COND_C = 3,
|
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 {
|
struct Cpu_state {
|
||||||
// Registers
|
// Registers
|
||||||
|
@ -56,23 +75,16 @@ struct Cpu_state {
|
||||||
bool halfcarry;
|
bool halfcarry;
|
||||||
bool carry;
|
bool carry;
|
||||||
|
|
||||||
bool IME; // interrupts enabled/disabled
|
IME_state IME;
|
||||||
bool IME_scheduled; // interrupts about to be enabled
|
|
||||||
|
|
||||||
|
u8 IE;
|
||||||
bool bootRomEnabled; // Whether boot ROM is visible
|
u8 IF;
|
||||||
|
|
||||||
void setAF(u16 v);
|
void setAF(u16 v);
|
||||||
u16 getAF();
|
u16 getAF();
|
||||||
};
|
};
|
||||||
|
|
||||||
class Cpu {
|
class Cpu {
|
||||||
private:
|
|
||||||
Cpu_state state;
|
|
||||||
Mem_device* bus;
|
|
||||||
|
|
||||||
typedef u8 opcode_t;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
u8 readPC8();
|
u8 readPC8();
|
||||||
u16 readPC16();
|
u16 readPC16();
|
||||||
|
@ -96,8 +108,22 @@ private:
|
||||||
|
|
||||||
bool decodeCond(u8 cc);
|
bool decodeCond(u8 cc);
|
||||||
|
|
||||||
|
void handleInterrupts();
|
||||||
|
void executeInstruction();
|
||||||
|
|
||||||
|
void reset();
|
||||||
|
|
||||||
public:
|
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();
|
void step();
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#include "cpu/cpu.h"
|
#include "cpu/cpu.h"
|
||||||
|
#include "cpu/panic.h"
|
||||||
#include "memory/mem_device.h"
|
#include "memory/mem_device.h"
|
||||||
|
|
||||||
static inline u16 make_u16(u8 msb, u8 lsb)
|
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);
|
return (((u16)msb << 8) | (u16)lsb);
|
||||||
}
|
}
|
||||||
|
|
||||||
u8 Cpu::readPC8()
|
void Cpu::executeInstruction()
|
||||||
{
|
|
||||||
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()
|
|
||||||
{
|
{
|
||||||
opcode_t op = readPC8();
|
opcode_t op = readPC8();
|
||||||
int mcycles = 1;
|
int mcycles = 1;
|
||||||
|
@ -403,9 +340,7 @@ void Cpu::step()
|
||||||
state.halfcarry = false;
|
state.halfcarry = false;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x27: // DAA
|
// TODO: case 0x27: break; // DAA
|
||||||
// TODO
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 0x2F: // CPL Complement accumulator
|
case 0x2F: // CPL Complement accumulator
|
||||||
state.A = ~state.A;
|
state.A = ~state.A;
|
||||||
|
@ -433,15 +368,19 @@ void Cpu::step()
|
||||||
case 0xC9: // RET
|
case 0xC9: // RET
|
||||||
doRet(); mcycles = 4; break;
|
doRet(); mcycles = 4; break;
|
||||||
case 0xD9: // RETI
|
case 0xD9: // RETI
|
||||||
doRet(); state.IME=true; mcycles = 4; break;
|
doRet(); state.IME = IME_ON; mcycles = 4; break;
|
||||||
|
|
||||||
case 0xF3: // DI
|
case 0xF3: // DI
|
||||||
state.IME = false;
|
state.IME = IME_OFF;
|
||||||
state.IME_scheduled = false;
|
|
||||||
break;
|
break;
|
||||||
case 0xFB: // EI
|
case 0xFB: // EI
|
||||||
state.IME_scheduled = true;
|
state.IME = IME_SCHEDULED;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
panic("Unknown opcode 0x%x\n",op);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
processed_mcycles += mcycles;
|
||||||
}
|
}
|
||||||
|
|
10
cpu/panic.h
Normal file
10
cpu/panic.h
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
inline void panic(Args... args)
|
||||||
|
{
|
||||||
|
printf(args...);
|
||||||
|
exit(1);
|
||||||
|
}
|
Loading…
Reference in a new issue