vgbc/cpu/cpu.h

192 lines
3 KiB
C
Raw Normal View History

2023-08-26 19:04:02 +02:00
#pragma once
2023-08-29 23:16:09 +02:00
#include <misc/types.h>
2023-09-01 15:11:27 +02:00
#include <misc/exception.h>
2023-08-26 19:04:02 +02:00
#include <memory/device.h>
typedef u8 opcode_t;
2023-08-26 19:04:02 +02:00
enum Flags {
F_ZERO = 0x8,
F_SUB = 0x4,
F_HALF = 0x2,
F_CARRY = 0x1,
};
2023-08-27 22:19:02 +02:00
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,
};
2023-08-27 22:19:02 +02:00
struct opcode {
const opcode_t value;
inline operator opcode_t() const
{ return value; }
inline u8 reg8idxlo() const
{ return value & 0x7; }
inline u8 reg8idxhi() const
{ return (value >> 3) & 0x7; }
inline u8 reg16idx() const
{ return (value >> 4) & 0x3; }
inline AluOp aluop() const
{ return (AluOp)((value >> 3) & 0x7); }
inline u8 cc() const
{ return (value >> 3) & 0x3; }
inline u16 rst_addr() const
{ return (u16)(value & 0x38); }
};
2023-09-01 15:11:27 +02:00
class Cpu;
2023-08-26 19:04:02 +02:00
struct Cpu_state {
// Registers
union {
u16 BC;
struct { u8 C; u8 B; };
2023-08-26 19:04:02 +02:00
};
union {
u16 DE;
struct { u8 E; u8 D; };
2023-08-26 19:04:02 +02:00
};
union {
u16 HL;
struct { u8 L; u8 H; };
2023-08-26 19:04:02 +02:00
};
u8 A;
u16 SP;
u16 PC;
bool zero;
bool subtract;
bool halfcarry;
bool carry;
2023-08-27 22:19:02 +02:00
IME_state IME;
2023-08-27 22:19:02 +02:00
u8 IE;
u8 IF;
2023-08-27 22:19:02 +02:00
2023-09-01 15:11:27 +02:00
// servicable interrupts
inline u8 SI() const
{ return INT_MASK & IE & IF; }
2023-08-29 23:30:31 +02:00
bool halted;
bool haltbug;
2023-08-29 23:46:36 +02:00
bool stopped;
2023-08-29 23:30:31 +02:00
2023-08-27 22:19:02 +02:00
void setAF(u16 v);
u16 getAF();
2023-09-01 15:11:27 +02:00
u8& reg8(Cpu& cpu, u8 idx);
u16& reg16(Cpu& cpu, u8 idx);
2023-08-26 19:04:02 +02:00
};
class Cpu {
private:
2023-08-26 21:17:47 +02:00
u8 readPC8();
u16 readPC16();
2023-08-26 19:04:02 +02:00
2023-08-26 21:17:47 +02:00
void pushStack8(u8 data);
u8 popStack8();
2023-08-26 19:04:02 +02:00
2023-08-26 21:17:47 +02:00
void pushStack16(u16 data);
u16 popStack16();
2023-08-27 22:19:02 +02:00
void aluop8(AluOp op, u8 lhs, u8 rhs, u8& out, bool update_carry = true);
2023-09-01 15:11:27 +02:00
void add16(u16& out, u16 lhs, u16 rhs);
2023-08-27 22:19:02 +02:00
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();
2023-08-26 19:04:02 +02:00
public:
Cpu(Mem_device* bus);
Cpu_state state;
Mem_device* bus;
unsigned long processed_mcycles;
2023-09-01 15:11:27 +02:00
u16 last_instr_addr;
void signalInterrupt(InterruptType it);
2023-08-26 19:04:02 +02:00
void step();
};
2023-09-01 15:11:27 +02:00
class CpuException : public EmulatorException {
private:
Cpu_state state;
u16 instaddr;
u8 instmem[4];
public:
CpuException(Cpu& cpu, const char* msg);
virtual const char* what() const noexcept override;
};