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);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
52
cpu/cpu.h
52
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();
|
||||
};
|
||||
|
|
|
@ -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
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