2023-08-26 19:04:02 +02:00
|
|
|
#include "cpu/cpu.h"
|
2023-08-28 21:56:33 +02:00
|
|
|
#include "cpu/panic.h"
|
2023-08-26 21:17:47 +02:00
|
|
|
#include "memory/mem_device.h"
|
2023-08-26 19:04:02 +02:00
|
|
|
|
2023-08-27 22:19:02 +02:00
|
|
|
static inline u16 make_u16(u8 msb, u8 lsb)
|
|
|
|
{
|
|
|
|
return (((u16)msb << 8) | (u16)lsb);
|
|
|
|
}
|
|
|
|
|
2023-08-28 21:56:33 +02:00
|
|
|
void Cpu::executeInstruction()
|
2023-08-26 19:04:02 +02:00
|
|
|
{
|
2023-08-28 22:31:52 +02:00
|
|
|
u16 currentpc = state.PC;
|
2023-08-26 21:17:47 +02:00
|
|
|
opcode_t op = readPC8();
|
2023-08-26 19:04:02 +02:00
|
|
|
int mcycles = 1;
|
2023-08-26 21:17:47 +02:00
|
|
|
|
2023-08-28 23:08:33 +02:00
|
|
|
#if 0
|
2023-08-28 22:31:52 +02:00
|
|
|
printf("@0x%04x: opcode %02X\n",currentpc,op);
|
2023-08-28 23:08:33 +02:00
|
|
|
#endif
|
2023-08-28 22:31:52 +02:00
|
|
|
|
|
|
|
if ((op & 0xC0) == 0x40 && op != 0x76) // LD r, r'
|
2023-08-26 21:17:47 +02:00
|
|
|
{
|
2023-08-27 00:15:12 +02:00
|
|
|
u8 tmp;
|
2023-08-26 23:51:51 +02:00
|
|
|
switch(op & 0x07)
|
|
|
|
{
|
|
|
|
case 0x6: tmp = bus->read8(state.HL); break;
|
2023-08-29 16:53:20 +02:00
|
|
|
default: tmp = state.reg8(op & 0x07); break;
|
2023-08-26 23:51:51 +02:00
|
|
|
};
|
|
|
|
|
2023-08-27 00:15:12 +02:00
|
|
|
switch((op >> 3) & 0x7)
|
2023-08-26 23:51:51 +02:00
|
|
|
{
|
2023-08-27 00:15:12 +02:00
|
|
|
case 0x6: bus->write8(state.HL, tmp); break;
|
2023-08-29 16:53:20 +02:00
|
|
|
default: state.reg8((op >> 3) & 0x7) = tmp; break;
|
2023-08-26 23:51:51 +02:00
|
|
|
}
|
2023-08-26 21:17:47 +02:00
|
|
|
}
|
2023-08-28 22:31:52 +02:00
|
|
|
else if((op & 0xC7) == 0x06) // LD r, n
|
2023-08-26 21:17:47 +02:00
|
|
|
{
|
2023-08-27 00:15:12 +02:00
|
|
|
u8 imm = readPC8();
|
2023-08-26 23:51:51 +02:00
|
|
|
|
2023-08-27 00:15:12 +02:00
|
|
|
switch((op >> 3) & 0x7)
|
|
|
|
{
|
2023-08-27 22:19:02 +02:00
|
|
|
case 0x6: bus->write8(state.HL, imm); break;
|
2023-08-29 16:53:20 +02:00
|
|
|
default: state.reg8((op >> 3) & 0x7) = imm; break;
|
2023-08-27 00:15:12 +02:00
|
|
|
}
|
|
|
|
}
|
2023-08-28 22:31:52 +02:00
|
|
|
else if((op & 0xC7) == 0x46 && op != 0x76) // LD r, [HL]
|
2023-08-27 00:15:12 +02:00
|
|
|
{
|
|
|
|
u8 data = bus->read8(state.HL);
|
2023-08-29 16:53:20 +02:00
|
|
|
state.reg8((op >> 3) & 0x7) = data;
|
2023-08-27 00:15:12 +02:00
|
|
|
}
|
2023-08-28 22:31:52 +02:00
|
|
|
else if((op & 0xC8) == 0x70 && op != 0x76) // LD [HL], r
|
2023-08-27 00:15:12 +02:00
|
|
|
{
|
2023-08-29 16:53:20 +02:00
|
|
|
u8 data = state.reg8(op & 0x7);
|
2023-08-27 00:15:12 +02:00
|
|
|
bus->write8(state.HL, data);
|
2023-08-26 23:51:51 +02:00
|
|
|
}
|
2023-08-28 22:31:52 +02:00
|
|
|
else if((op & 0xCF) == 0x01) // LD rr, nn
|
2023-08-26 23:51:51 +02:00
|
|
|
{
|
2023-08-27 22:19:02 +02:00
|
|
|
u16 data = readPC16();
|
2023-08-29 16:53:20 +02:00
|
|
|
state.reg16((op >> 4) & 0x3) = data;
|
2023-08-27 22:19:02 +02:00
|
|
|
mcycles = 3;
|
|
|
|
}
|
2023-08-28 22:31:52 +02:00
|
|
|
else if((op & 0xCF) == 0xC5) // PUSH rr
|
2023-08-27 22:19:02 +02:00
|
|
|
{
|
|
|
|
u16 data;
|
|
|
|
switch((op >> 4) & 0x3)
|
|
|
|
{
|
|
|
|
case 0x3: data = state.getAF(); break;
|
2023-08-29 16:53:20 +02:00
|
|
|
default: data = state.reg16((op >> 4) & 0x3);
|
2023-08-27 22:19:02 +02:00
|
|
|
}
|
2023-08-26 23:51:51 +02:00
|
|
|
|
2023-08-29 16:53:53 +02:00
|
|
|
pushStack16(data);
|
2023-08-27 22:19:02 +02:00
|
|
|
|
|
|
|
mcycles = 4;
|
|
|
|
}
|
2023-08-28 22:31:52 +02:00
|
|
|
else if((op & 0xCF) == 0xC1) // POP rr
|
2023-08-27 22:19:02 +02:00
|
|
|
{
|
2023-08-29 16:53:53 +02:00
|
|
|
u16 data = popStack16();
|
2023-08-27 22:19:02 +02:00
|
|
|
|
|
|
|
switch((op >> 4) & 0x3)
|
|
|
|
{
|
|
|
|
case 0x3: state.setAF(data); break;
|
2023-08-29 16:53:20 +02:00
|
|
|
default: state.reg16((op >> 4) & 0x3) = data; break;
|
2023-08-27 22:19:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
mcycles = 4;
|
|
|
|
}
|
2023-08-28 22:31:52 +02:00
|
|
|
else if((op & 0xC0) == 0x80) // ADD, ADC, SUB, ABC, CP, AND, OR, XOR
|
2023-08-27 22:19:02 +02:00
|
|
|
{
|
|
|
|
// SUB r: 0b10010xxx
|
|
|
|
// CP r: 0b10111xxx
|
|
|
|
// SBC r: 0b10011xxx
|
|
|
|
AluOp aluop = (AluOp)((op >> 3) & 0x3);
|
|
|
|
|
|
|
|
u8 rhs;
|
|
|
|
switch(op & 0x7)
|
|
|
|
{
|
|
|
|
case 0x6: rhs = bus->read8(state.HL); mcycles = 2; break;
|
2023-08-29 16:53:20 +02:00
|
|
|
default: rhs = state.reg8(op & 0x7); break;
|
2023-08-27 22:19:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
aluop8(aluop, rhs);
|
|
|
|
}
|
2023-08-28 22:31:52 +02:00
|
|
|
else if((op & 0xC6) == 0x04) // INC r; INC [HL]; DEC r; DEC [HL];
|
2023-08-27 22:19:02 +02:00
|
|
|
{
|
|
|
|
AluOp aluop = (op & 0x1) ? SUB : ADD;
|
|
|
|
|
|
|
|
switch((op >> 3) & 0x7)
|
|
|
|
{
|
|
|
|
case 0x6:
|
2023-08-26 23:51:51 +02:00
|
|
|
{
|
2023-08-27 22:19:02 +02:00
|
|
|
u8 tmp = bus->read8(state.HL);
|
|
|
|
aluop8(aluop, tmp, 1, tmp, false);
|
|
|
|
bus->write8(state.HL, tmp);
|
2023-08-26 23:51:51 +02:00
|
|
|
mcycles = 3;
|
|
|
|
}
|
|
|
|
break;
|
2023-08-29 16:53:20 +02:00
|
|
|
default:
|
|
|
|
{
|
|
|
|
u8& reg = state.reg8((op >> 3) & 0x7);
|
|
|
|
aluop8(aluop, reg, 1, reg, false); break;
|
|
|
|
}
|
|
|
|
break;
|
2023-08-27 22:19:02 +02:00
|
|
|
}
|
|
|
|
}
|
2023-08-28 22:31:52 +02:00
|
|
|
else if((op & 0xE7) == 0xC2) // JP cc, nn:
|
2023-08-27 22:19:02 +02:00
|
|
|
{
|
|
|
|
u16 nn = readPC16();
|
|
|
|
|
|
|
|
if (decodeCond((op >> 3) && 0x3))
|
|
|
|
{
|
|
|
|
state.PC = nn;
|
|
|
|
mcycles = 4;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
mcycles = 3;
|
|
|
|
}
|
|
|
|
}
|
2023-08-28 22:31:52 +02:00
|
|
|
else if((op & 0xE7) == 0x20) // JR cc, e
|
2023-08-27 22:19:02 +02:00
|
|
|
{
|
|
|
|
s8 e = readPC8();
|
2023-08-26 23:51:51 +02:00
|
|
|
|
2023-08-27 22:19:02 +02:00
|
|
|
bool cond;
|
|
|
|
if (decodeCond((op >> 3) & 0x3))
|
|
|
|
{
|
|
|
|
state.PC += e;
|
|
|
|
mcycles = 3;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2023-08-26 23:51:51 +02:00
|
|
|
mcycles = 2;
|
2023-08-27 22:19:02 +02:00
|
|
|
}
|
|
|
|
}
|
2023-08-28 22:31:52 +02:00
|
|
|
else if((op & 0xE7) == 0xC4) // CALL cc, nn
|
2023-08-27 22:19:02 +02:00
|
|
|
{
|
|
|
|
u16 nn = readPC16();
|
|
|
|
|
|
|
|
if(decodeCond((op >> 3) & 0x3))
|
|
|
|
{
|
|
|
|
doCall(nn);
|
|
|
|
mcycles = 6;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
mcycles = 3;
|
|
|
|
}
|
|
|
|
}
|
2023-08-28 22:31:52 +02:00
|
|
|
else if((op & 0xE7) == 0xC0) // RET cc
|
2023-08-27 22:19:02 +02:00
|
|
|
{
|
|
|
|
if(decodeCond((op >> 3) & 0x3))
|
|
|
|
{
|
|
|
|
doRet();
|
|
|
|
mcycles = 5;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
mcycles = 2;
|
|
|
|
}
|
|
|
|
}
|
2023-08-28 22:31:52 +02:00
|
|
|
else if((op & 0xC7) == 0xC7) // RST
|
2023-08-27 22:19:02 +02:00
|
|
|
{
|
2023-08-28 19:39:18 +02:00
|
|
|
u16 rst_addr = op & 0x38;
|
2023-08-27 22:19:02 +02:00
|
|
|
doCall(rst_addr);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
switch(op)
|
|
|
|
{
|
|
|
|
case 0x00: break; // NOP
|
|
|
|
case 0x0A: // Load A, [BC]
|
|
|
|
state.A = bus->read8(state.BC);
|
2023-08-26 23:51:51 +02:00
|
|
|
mcycles = 2;
|
|
|
|
break;
|
2023-08-27 22:19:02 +02:00
|
|
|
case 0x1A: // Load A, [DE]
|
|
|
|
state.A = bus->read8(state.DE);
|
2023-08-26 23:51:51 +02:00
|
|
|
mcycles = 2;
|
|
|
|
break;
|
2023-08-27 22:19:02 +02:00
|
|
|
case 0x02: // Load [BC], A
|
|
|
|
bus->write8(state.BC, state.A);
|
|
|
|
mcycles = 2;
|
2023-08-26 23:51:51 +02:00
|
|
|
break;
|
2023-08-27 22:19:02 +02:00
|
|
|
case 0x12: // Load [DE], A
|
|
|
|
bus->write8(state.DE, state.A);
|
|
|
|
mcycles = 2;
|
2023-08-26 23:51:51 +02:00
|
|
|
break;
|
2023-08-27 22:19:02 +02:00
|
|
|
case 0xFA: // LD A, [nn]
|
2023-08-26 23:51:51 +02:00
|
|
|
{
|
|
|
|
u16 addr = readPC16();
|
2023-08-27 22:19:02 +02:00
|
|
|
state.A = bus->read8(addr);
|
|
|
|
mcycles = 4;
|
2023-08-26 23:51:51 +02:00
|
|
|
}
|
|
|
|
break;
|
2023-08-27 22:19:02 +02:00
|
|
|
case 0xEA: // LD [nn], A
|
2023-08-26 23:51:51 +02:00
|
|
|
{
|
2023-08-27 22:19:02 +02:00
|
|
|
u16 addr = readPC16();
|
|
|
|
bus->write8(addr, state.A);
|
|
|
|
mcycles = 4;
|
2023-08-26 23:51:51 +02:00
|
|
|
}
|
|
|
|
break;
|
2023-08-27 22:19:02 +02:00
|
|
|
case 0xF2: // LD A, [0xFF : C]
|
|
|
|
state.A = bus->read8(make_u16(0xFFu,state.C));
|
|
|
|
mcycles = 2;
|
|
|
|
break;
|
|
|
|
case 0xE2: // LD [0xFF : C], A
|
|
|
|
bus->write8(make_u16(0xFFu,state.C), state.A);
|
|
|
|
mcycles = 2;
|
|
|
|
break;
|
|
|
|
case 0xF0: // LD A, [0xFF : n]
|
2023-08-28 22:31:52 +02:00
|
|
|
state.A = bus->read8(make_u16(0xFFu,readPC8()));
|
|
|
|
mcycles = 3;
|
2023-08-26 23:51:51 +02:00
|
|
|
break;
|
2023-08-27 22:19:02 +02:00
|
|
|
case 0xE0: // LD [0xFF : n], A
|
2023-08-28 22:31:52 +02:00
|
|
|
bus->write8(make_u16(0xFFu,readPC8()), state.A);
|
|
|
|
mcycles = 3;
|
2023-08-26 23:51:51 +02:00
|
|
|
break;
|
|
|
|
case 0x3A: // LD A, [HL-]
|
2023-08-27 22:19:02 +02:00
|
|
|
state.A = bus->read8(state.HL); state.HL--; mcycles = 2; break;
|
|
|
|
case 0x2A: // LD A, [HL+]
|
|
|
|
state.A = bus->read8(state.HL); state.HL++; mcycles = 2; break;
|
|
|
|
case 0x32: // LD [HL-], A
|
|
|
|
bus->write8(state.HL, state.A); state.HL--; mcycles = 2; break;
|
|
|
|
case 0x22: // LD [HL-], A
|
|
|
|
bus->write8(state.HL, state.A); state.HL++; mcycles = 2; break;
|
|
|
|
case 0x08: // LD [nn], SP
|
|
|
|
bus->write16(readPC16(), state.SP); mcycles = 5; break;
|
|
|
|
case 0xF9: // LD SP, HL
|
|
|
|
state.SP = state.HL; mcycles = 2; break;
|
2023-08-26 23:51:51 +02:00
|
|
|
|
2023-08-27 22:19:02 +02:00
|
|
|
case 0xC6: // ADD n
|
|
|
|
aluop8(ADD, readPC8()); mcycles = 2; break;
|
|
|
|
case 0xD6: // SUB n
|
|
|
|
aluop8(SUB, readPC8()); mcycles = 2; break;
|
|
|
|
case 0xE6: // AND n
|
|
|
|
aluop8(AND, readPC8()); mcycles = 2; break;
|
|
|
|
case 0xF6: // OR n
|
|
|
|
aluop8(OR, readPC8()); mcycles = 2; break;
|
2023-08-26 23:51:51 +02:00
|
|
|
|
2023-08-27 22:19:02 +02:00
|
|
|
case 0xCE: // ADC n
|
|
|
|
aluop8(ADC, readPC8()); mcycles = 2; break;
|
|
|
|
case 0xDE: // SBC n
|
|
|
|
aluop8(SBC, readPC8()); mcycles = 2; break;
|
|
|
|
case 0xEE: // XOR n
|
|
|
|
aluop8(XOR, readPC8()); mcycles = 2; break;
|
|
|
|
case 0xFE: // CP n
|
|
|
|
aluop8(CP, readPC8()); mcycles = 2; break;
|
|
|
|
|
|
|
|
case 0x3F: // CCF complement carry flag
|
|
|
|
state.carry = !state.carry;
|
2023-08-26 23:51:51 +02:00
|
|
|
state.subtract = false;
|
2023-08-27 22:19:02 +02:00
|
|
|
state.halfcarry = false;
|
2023-08-26 23:51:51 +02:00
|
|
|
break;
|
2023-08-27 22:19:02 +02:00
|
|
|
|
|
|
|
case 0x37: // SCF Set carry flag
|
|
|
|
state.carry = true;
|
2023-08-26 23:51:51 +02:00
|
|
|
state.subtract = false;
|
2023-08-27 22:19:02 +02:00
|
|
|
state.halfcarry = false;
|
2023-08-26 23:51:51 +02:00
|
|
|
break;
|
|
|
|
|
2023-08-28 21:56:33 +02:00
|
|
|
// TODO: case 0x27: break; // DAA
|
2023-08-27 22:19:02 +02:00
|
|
|
|
|
|
|
case 0x2F: // CPL Complement accumulator
|
|
|
|
state.A = ~state.A;
|
2023-08-26 23:51:51 +02:00
|
|
|
state.subtract = true;
|
2023-08-27 22:19:02 +02:00
|
|
|
state.halfcarry = true;
|
|
|
|
|
|
|
|
|
|
|
|
case 0xC3: // JP nn
|
|
|
|
{
|
|
|
|
u16 nn = readPC16();
|
|
|
|
state.PC = nn;
|
|
|
|
mcycles = 4;
|
|
|
|
}
|
2023-08-26 23:51:51 +02:00
|
|
|
break;
|
2023-08-27 22:19:02 +02:00
|
|
|
case 0xE9: // JP HL
|
|
|
|
state.PC = state.HL;
|
2023-08-26 23:51:51 +02:00
|
|
|
break;
|
2023-08-27 22:19:02 +02:00
|
|
|
case 0x18: // JR e
|
|
|
|
{
|
|
|
|
s8 e = readPC8();
|
|
|
|
state.PC += e;
|
|
|
|
}
|
|
|
|
case 0xCD: // CALL nn
|
|
|
|
doCall(readPC16()); mcycles = 6; break;
|
|
|
|
case 0xC9: // RET
|
|
|
|
doRet(); mcycles = 4; break;
|
|
|
|
case 0xD9: // RETI
|
2023-08-28 21:56:33 +02:00
|
|
|
doRet(); state.IME = IME_ON; mcycles = 4; break;
|
2023-08-26 23:51:51 +02:00
|
|
|
|
2023-08-27 22:19:02 +02:00
|
|
|
case 0xF3: // DI
|
2023-08-28 21:56:33 +02:00
|
|
|
state.IME = IME_OFF;
|
2023-08-27 22:19:02 +02:00
|
|
|
break;
|
|
|
|
case 0xFB: // EI
|
2023-08-28 21:56:33 +02:00
|
|
|
state.IME = IME_SCHEDULED;
|
2023-08-26 23:51:51 +02:00
|
|
|
break;
|
2023-08-28 21:56:33 +02:00
|
|
|
|
|
|
|
default:
|
|
|
|
panic("Unknown opcode 0x%x\n",op);
|
2023-08-26 23:51:51 +02:00
|
|
|
}
|
2023-08-26 21:17:47 +02:00
|
|
|
}
|
2023-08-28 21:56:33 +02:00
|
|
|
|
|
|
|
processed_mcycles += mcycles;
|
2023-08-26 19:04:02 +02:00
|
|
|
}
|