2023-08-29 23:11:31 +02:00
|
|
|
#include <cpu/cpu.h>
|
2023-08-29 23:15:00 +02:00
|
|
|
#include <misc/panic.h>
|
2023-08-27 22:19:02 +02:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2023-08-29 16:53:20 +02:00
|
|
|
u8& Cpu_state::reg8(u8 idx)
|
|
|
|
{
|
|
|
|
switch(idx)
|
|
|
|
{
|
|
|
|
case 0x0: return B; break;
|
|
|
|
case 0x1: return C; break;
|
|
|
|
case 0x2: return D; break;
|
|
|
|
case 0x3: return E; break;
|
|
|
|
case 0x4: return H; break;
|
|
|
|
case 0x5: return L; break;
|
|
|
|
case 0x7: return A; break;
|
|
|
|
default: panic("Invalid 8-bit register access idx=%d\n", idx);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
u16& Cpu_state::reg16(u8 idx)
|
|
|
|
{
|
|
|
|
switch(idx)
|
|
|
|
{
|
|
|
|
case 0x0: return BC; break;
|
|
|
|
case 0x1: return DE; break;
|
|
|
|
case 0x2: return HL; break;
|
|
|
|
case 0x3: return SP; break;
|
|
|
|
default: panic("Invalid 16-bit register access idx=%d\n", idx);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-28 21:56:33 +02:00
|
|
|
Cpu::Cpu(Mem_device* bus)
|
|
|
|
: bus(bus)
|
|
|
|
{
|
|
|
|
reset();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Cpu::step()
|
|
|
|
{
|
2023-08-30 00:01:45 +02:00
|
|
|
if(state.stopped) return;
|
2023-08-29 23:46:36 +02:00
|
|
|
|
2023-08-29 13:45:17 +02:00
|
|
|
if(!handleInterrupts()) // if no isr has been called, decode an instruction
|
|
|
|
{
|
2023-08-30 00:01:45 +02:00
|
|
|
if (state.halted)
|
2023-08-29 23:30:31 +02:00
|
|
|
processed_mcycles += 4;
|
|
|
|
else
|
|
|
|
executeInstruction();
|
2023-08-29 13:45:17 +02:00
|
|
|
}
|
2023-08-28 21:56:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
2023-08-29 23:30:31 +02:00
|
|
|
|
2023-08-30 00:01:45 +02:00
|
|
|
state.halted = false;
|
2023-08-30 21:47:38 +02:00
|
|
|
state.haltbug = false;
|
2023-08-30 00:01:45 +02:00
|
|
|
state.stopped = false;
|
2023-08-28 21:56:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
u8 Cpu::readPC8()
|
|
|
|
{
|
|
|
|
u8 data = bus->read8(state.PC);
|
2023-08-30 21:47:38 +02:00
|
|
|
if(!state.haltbug)
|
|
|
|
state.PC++;
|
|
|
|
else
|
|
|
|
state.haltbug = false;
|
2023-08-28 21:56:33 +02:00
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
|
|
|
u16 Cpu::readPC16()
|
|
|
|
{
|
2023-08-30 21:47:38 +02:00
|
|
|
u16 data;
|
|
|
|
if(state.haltbug)
|
|
|
|
{
|
|
|
|
data = bus->read8(state.PC);
|
|
|
|
data |= data << 8; // Same byte twice
|
|
|
|
state.PC++;
|
|
|
|
state.haltbug = false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
data = bus->read16(state.PC);
|
|
|
|
state.PC+=2;
|
|
|
|
}
|
2023-08-28 21:56:33 +02:00
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Cpu::pushStack8(u8 data)
|
|
|
|
{
|
|
|
|
state.SP--;
|
2023-08-29 12:10:10 +02:00
|
|
|
bus->write8(state.SP, data);
|
2023-08-28 21:56:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
u8 Cpu::popStack8()
|
|
|
|
{
|
|
|
|
u8 data = bus->read8(state.SP);
|
|
|
|
state.SP++;
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Cpu::pushStack16(u16 data)
|
|
|
|
{
|
|
|
|
state.SP-=2;
|
2023-08-29 12:10:10 +02:00
|
|
|
bus->write16(state.SP,data);
|
2023-08-28 21:56:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
2023-08-29 13:45:55 +02:00
|
|
|
void Cpu::signalInterrupt(InterruptType it)
|
|
|
|
{
|
|
|
|
state.IF |= it;
|
|
|
|
}
|
|
|
|
|
2023-08-28 21:56:33 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2023-08-27 22:19:02 +02:00
|
|
|
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;
|
|
|
|
}
|
2023-08-28 21:56:33 +02:00
|
|
|
|
2023-08-29 13:45:17 +02:00
|
|
|
bool Cpu::handleInterrupts()
|
2023-08-28 21:56:33 +02:00
|
|
|
{
|
2023-08-29 23:30:31 +02:00
|
|
|
// Once there's an interrupt we exit halt mode
|
2023-08-30 21:47:38 +02:00
|
|
|
if (state.SI())
|
2023-08-30 00:01:45 +02:00
|
|
|
state.halted = false;
|
2023-08-29 23:30:31 +02:00
|
|
|
|
2023-08-28 21:56:33 +02:00
|
|
|
if (state.IME == IME_SCHEDULED)
|
|
|
|
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; }
|
2023-08-29 13:45:17 +02:00
|
|
|
else panic("Can't find pending interrupt IE=%02x IF=%02x\n", state.IE, state.IF);
|
2023-08-28 21:56:33 +02:00
|
|
|
|
|
|
|
state.IME = IME_OFF; // Disable IME
|
|
|
|
state.IF &= ~it; // clear interrupt in IF
|
|
|
|
doCall(isr); // Call interrupt service routine
|
|
|
|
|
|
|
|
processed_mcycles += 5;
|
2023-08-29 13:45:17 +02:00
|
|
|
|
|
|
|
return true;
|
2023-08-28 21:56:33 +02:00
|
|
|
}
|
2023-08-29 13:45:17 +02:00
|
|
|
|
|
|
|
return false;
|
2023-08-28 21:56:33 +02:00
|
|
|
}
|