vgbc/cpu/cpu.cpp

214 lines
3.8 KiB
C++

#include "cpu.h"
void Cpu_state::setAF(u16 v)
{
A = (u8)(v >> 8);
zero = (v & 0x80 != 0);
subtract = (v & 0x40 != 0);
halfcarry = (v & 0x20 != 0);
carry = (v & 0x10 != 0);
}
u16 Cpu_state::getAF()
{
return ((u16)A << 8) |
(zero ? 0x80 : 0) |
(subtract ? 0x40 : 0) |
(halfcarry ? 0x20 : 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)
{
u16 rhs16 = rhs;
u16 res16;
u8 res;
if ((op == ADC || op == SBC) && state.carry)
rhs16++;
u16 lhs_lower = lhs & 0x0F;
u16 lhs_upper = lhs & 0xF0;
u16 rhs_lower = rhs16 & 0x0F;
u16 rhs_upper = rhs16 & 0x1F0;
switch(op)
{
case ADD:
case ADC:
res16 = lhs_lower + rhs_lower;
break;
case SUB:
case SBC:
case CP:
res16 = lhs_lower - rhs_lower;
break;
case AND:
res16 = lhs_lower & rhs_lower;
break;
case OR:
res16 = lhs_lower | rhs_lower;
break;
case XOR:
res16 = lhs_lower ^ rhs_lower;
break;
}
state.halfcarry = (res16 & 0x10 != 0) || op == AND;
state.subtract = (op == SUB) || (op == SBC) || (op == CP);
switch(op)
{
case ADD:
case ADC:
res16 += lhs_upper + rhs_upper;
break;
case SUB:
case SBC:
case CP:
res16 += lhs_upper - rhs_upper;
break;
case AND:
res16 |= lhs_upper & rhs_upper;
break;
case OR:
res16 |= lhs_upper | rhs_upper;
break;
case XOR:
res16 |= lhs_upper ^ rhs_upper;
break;
}
res = (u8)(res16 & 0xFF);
if(update_carry)
state.carry = (res16 & 0x100 != 0);
state.zero = (res == 0);
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;
}
}