vgbc/cpu/decoder.cpp

461 lines
12 KiB
C++
Raw Normal View History

2023-08-29 23:11:31 +02:00
#include <cpu/cpu.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);
}
void Cpu::executeInstruction()
2023-08-26 19:04:02 +02:00
{
2023-09-01 15:11:27 +02:00
last_instr_addr = state.PC;
opcode 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-09-01 15:11:27 +02:00
printf("@0x%04x: opcode %02X\n", last_instr_addr, op);
2023-08-28 23:08:33 +02:00
#endif
if ((op & 0xC0) == 0x40 && op != 0x76) // LD r, r'; LD r, [HL]; LD [HL], r
2023-08-26 21:17:47 +02:00
{
2023-08-27 00:15:12 +02:00
u8 tmp;
switch(op.reg8idxlo())
2023-08-26 23:51:51 +02:00
{
case 0x6: tmp = bus->read8(state.HL); break;
2023-09-01 15:11:27 +02:00
default: tmp = state.reg8(*this, op.reg8idxlo()); break;
2023-08-26 23:51:51 +02:00
};
switch(op.reg8idxhi())
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-09-01 15:11:27 +02:00
default: state.reg8(*this, op.reg8idxhi()) = tmp; break;
2023-08-26 23:51:51 +02:00
}
2023-08-26 21:17:47 +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
switch(op.reg8idxhi())
2023-08-27 00:15:12 +02:00
{
2023-08-27 22:19:02 +02:00
case 0x6: bus->write8(state.HL, imm); break;
2023-09-01 15:11:27 +02:00
default: state.reg8(*this, op.reg8idxhi()) = imm; break;
2023-08-27 00:15:12 +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-09-01 15:11:27 +02:00
state.reg16(*this, op.reg16idx()) = data;
2023-08-27 22:19:02 +02:00
mcycles = 3;
}
else if((op & 0xCF) == 0xC5) // PUSH rr
2023-08-27 22:19:02 +02:00
{
u16 data;
switch(op.reg16idx())
2023-08-27 22:19:02 +02:00
{
case 0x3: data = state.getAF(); break;
2023-09-01 15:11:27 +02:00
default: data = state.reg16(*this, op.reg16idx());
2023-08-27 22:19:02 +02:00
}
2023-08-26 23:51:51 +02:00
pushStack16(data);
2023-08-27 22:19:02 +02:00
mcycles = 4;
}
else if((op & 0xCF) == 0xC1) // POP rr
2023-08-27 22:19:02 +02:00
{
u16 data = popStack16();
2023-08-27 22:19:02 +02:00
switch(op.reg16idx())
2023-08-27 22:19:02 +02:00
{
case 0x3: state.setAF(data); break;
2023-09-01 15:11:27 +02:00
default: state.reg16(*this, op.reg16idx()) = data; break;
2023-08-27 22:19:02 +02:00
}
mcycles = 4;
}
2023-08-30 13:40:38 +02:00
else if((op & 0xC0) == 0x80) // ADD, ADC, SUB, SBC, CP, AND, OR, XOR
2023-08-27 22:19:02 +02:00
{
u8 rhs;
switch(op.reg8idxlo())
2023-08-27 22:19:02 +02:00
{
case 0x6: rhs = bus->read8(state.HL); mcycles = 2; break;
2023-09-01 15:11:27 +02:00
default: rhs = state.reg8(*this, op.reg8idxlo()); break;
2023-08-27 22:19:02 +02:00
}
aluop8(op.aluop(), rhs);
2023-08-27 22:19:02 +02:00
}
2023-08-30 13:41:15 +02:00
else if((op & 0xC7) == 0xC6) // ADD n, ADC n, SUB n, SBC n, AND n, XOR n, OR n, CP n
{
aluop8(op.aluop(), readPC8());
2023-08-30 13:41:15 +02:00
mcycles = 2;
}
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.reg8idxhi())
2023-08-27 22:19:02 +02:00
{
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;
default:
{
2023-09-01 15:11:27 +02:00
u8& reg = state.reg8(*this, op.reg8idxhi());
aluop8(aluop, reg, 1, reg, false); break;
}
break;
2023-08-27 22:19:02 +02:00
}
}
else if((op & 0xC7) == 0x03) // INC rr; DEC rr
{
2023-09-01 15:11:27 +02:00
state.reg16(*this, op.reg16idx()) += ((op & 0x08) ? -1 : 1);
mcycles = 2;
}
else if((op & 0xE7) == 0xC2) // JP cc, nn:
2023-08-27 22:19:02 +02:00
{
u16 nn = readPC16();
if (decodeCond(op.cc()))
2023-08-27 22:19:02 +02:00
{
state.PC = nn;
mcycles = 4;
}
else
mcycles = 3;
2023-08-27 22:19:02 +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.cc()))
2023-08-27 22:19:02 +02:00
{
state.PC += e;
mcycles = 3;
}
else
mcycles = 2;
2023-08-27 22:19:02 +02:00
}
else if((op & 0xE7) == 0xC4) // CALL cc, nn
2023-08-27 22:19:02 +02:00
{
u16 nn = readPC16();
if(decodeCond(op.cc()))
2023-08-27 22:19:02 +02:00
{
doCall(nn);
mcycles = 6;
}
else
mcycles = 3;
2023-08-27 22:19:02 +02:00
}
2023-08-29 23:05:22 +02:00
else if((op & 0xCF) == 0x09) // ADD HL, rr
{
2023-09-01 15:11:27 +02:00
add16(state.HL, state.HL, state.reg16(*this, op.reg16idx()));
2023-08-29 23:05:22 +02:00
mcycles = 2;
}
else if((op & 0xE7) == 0xC0) // RET cc
2023-08-27 22:19:02 +02:00
{
if(decodeCond(op.cc()))
2023-08-27 22:19:02 +02:00
{
doRet();
mcycles = 5;
}
else
mcycles = 2;
2023-08-27 22:19:02 +02:00
}
else if((op & 0xC7) == 0xC7) // RST
2023-08-27 22:19:02 +02:00
{
doCall(op.rst_addr());
2023-08-27 22:19:02 +02:00
}
else if(op == 0xCB) // PREFIX
{
opcode prefix_op{ readPC8() };
#if 0
2023-09-01 15:11:27 +02:00
printf("@0x%04x: CB opcode %02X\n", last_instr_addr + 1, prefix_op);
#endif
u8 data;
switch(prefix_op.reg8idxlo())
{
case 0x6: data = bus->read8(state.HL); mcycles = 3; break;
2023-09-01 15:11:27 +02:00
default: data = state.reg8(*this, prefix_op.reg8idxlo()); mcycles = 2; break;
}
// For BIT, RES, SET
u8 bit = prefix_op.reg8idxhi();
switch(prefix_op & 0xC0)
{
case 0x00:
switch(prefix_op & 0xF8)
{
case 0x00: // RLC
{
bool msb_set = (data & 0x80);
data = (data << 1) | (msb_set ? 0x1 : 0x0);
state.carry = msb_set;
}
break;
case 0x08: // RRC
{
bool lsb_set = (data & 0x01);
data = (data >> 1) | (lsb_set ? 0x80 : 0x00);
state.carry = lsb_set;
}
break;
case 0x10: // RL
{
bool msb_set = (data & 0x80);
data = (data << 1) | (state.carry ? 0x1 : 0x0);
state.carry = msb_set;
}
break;
case 0x18: // RR
{
bool lsb_set = (data & 0x01) != 0;
data = (data >> 1) | (state.carry ? 0x80 : 0x00);
state.carry = lsb_set;
}
break;
case 0x20: // SLA
state.carry = (data & 0x80);
data = (data << 1);
break;
case 0x28: // SRA
state.carry = (data & 0x01);
data = (data >> 1) | (data & 0x80);
break;
case 0x30: // SWAP
data = ((data >> 4) & 0x0F) | ((data << 4) & 0xF0);
state.carry = false;
break;
case 0x38: // SRL
state.carry = (data & 0x01);
data = (data >> 1);
break;
}
state.halfcarry = false;
state.subtract = false;
state.zero = (data == 0);
break;
case 0x40: // BIT
2023-08-29 21:14:19 +02:00
state.zero = (data & (1 << bit)) == 0;
state.subtract = false;
state.halfcarry = true;
break;
case 0x80: // RES
data &= ~(1 << bit);
break;
case 0xC0: // SET
data |= (1 << bit);
break;
}
2023-08-30 13:27:04 +02:00
// All ops except for BIT write the data back to where it came from
if ((prefix_op & 0xC0) != 0x40)
{
switch(prefix_op.reg8idxlo())
{
case 0x6: bus->write8(state.HL, data); mcycles = 4; break;
2023-09-01 15:11:27 +02:00
default: state.reg8(*this, prefix_op.reg8idxlo()) = data; break;
}
}
}
2023-08-27 22:19:02 +02:00
else
{
switch(op)
{
case 0x00: // defined NOP
break;
2023-08-27 22:19:02 +02:00
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;
case 0x07: // RLCA
state.carry = (state.A & 0x80);
state.A = (state.A << 1) | (state.carry ? 0x01 : 0x00);
state.halfcarry = false;
state.subtract = false;
state.zero = false;
2023-08-30 12:32:38 +02:00
break;
case 0x17: // RLA
{
bool msb_set = (state.A & 0x80);
state.A = (state.A << 1) | (state.carry ? 0x01 : 0x00);
state.carry = msb_set;
state.halfcarry = false;
state.subtract = false;
state.zero = false;
}
2023-08-30 12:32:38 +02:00
break;
case 0x0F: // RRCA
state.carry = (state.A & 0x01);
state.A = (state.A >> 1) | (state.carry ? 0x80 : 0x00);
state.halfcarry = false;
state.subtract = false;
state.zero = false;
2023-08-30 12:32:38 +02:00
break;
case 0x1F: // RRA
{
bool lsb_set = (state.A & 0x01);
state.A = (state.A >> 1) | (state.carry ? 0x80 : 0x00);
state.carry = lsb_set;
state.halfcarry = false;
state.subtract = false;
state.zero = false;
}
2023-08-30 12:32:38 +02:00
break;
2023-08-27 22:19:02 +02:00
case 0xFA: // LD A, [nn]
state.A = bus->read8(readPC16());
mcycles = 4;
2023-08-26 23:51:51 +02:00
break;
2023-08-27 22:19:02 +02:00
case 0xEA: // LD [nn], A
bus->write8(readPC16(), 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]
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
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;
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;
case 0x27: // DAA
{
u16 corr = 0;
if (state.halfcarry || ((state.A & 0x0F) > 0x9))
corr |= 0x06;
if ((state.A & 0xF0) > 0x90)
corr |= 0x06;
u32 res16 = (u16)state.A + (state.subtract ? (-corr) : corr);
state.A = (u8)res16;
state.halfcarry = false;
state.zero = (state.A == 0);
state.carry = (res16 & 0x100);
}
break;
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;
2023-08-30 12:32:38 +02:00
break;
2023-08-27 22:19:02 +02:00
case 0xC3: // JP nn
2023-08-29 19:43:19 +02:00
state.PC = readPC16();
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
state.PC += (s8)readPC8();
break;
2023-08-27 22:19:02 +02:00
case 0xCD: // CALL nn
2023-08-29 19:43:19 +02:00
doCall(readPC16());
mcycles = 6;
break;
2023-08-27 22:19:02 +02:00
case 0xC9: // RET
2023-08-29 19:43:19 +02:00
doRet();
mcycles = 4;
break;
2023-08-27 22:19:02 +02:00
case 0xD9: // RETI
2023-08-29 19:43:19 +02:00
doRet();
state.IME = IME_ON;
2023-08-29 19:43:19 +02:00
mcycles = 4;
break;
2023-08-27 22:19:02 +02:00
case 0xF3: // DI
state.IME = IME_OFF;
2023-08-27 22:19:02 +02:00
break;
case 0xFB: // EI
state.IME = IME_SCHEDULED;
2023-08-26 23:51:51 +02:00
break;
2023-08-29 23:32:11 +02:00
case 0x76: // HALT
2023-08-30 00:01:45 +02:00
state.halted = true;
if(state.IME != IME_ON && state.SI())
state.haltbug = true;
2023-08-29 23:32:11 +02:00
break;
2023-08-29 23:47:18 +02:00
case 0x10: // STOP n8
(void)readPC8();
2023-08-30 00:01:45 +02:00
state.stopped = true;
2023-08-29 23:47:18 +02:00
break;
2023-08-29 23:06:05 +02:00
case 0xE8: // ADD SP, e8
add16_8(state.SP, state.SP, readPC8());
state.zero = false;
mcycles = 4;
2023-08-29 23:13:23 +02:00
break;
2023-08-29 23:46:09 +02:00
case 0xF8: // LD HL, SP + e8
add16_8(state.SP, state.SP, readPC8());
state.zero = false;
mcycles = 3;
2023-08-29 23:46:09 +02:00
break;
2023-08-29 23:06:05 +02:00
2023-09-01 15:11:27 +02:00
case 0xD3: // Undefined, throw exception
case 0xE3:
case 0xE4:
case 0xF4:
case 0xDB:
case 0xEB:
case 0xEC:
case 0xFC:
case 0xDD:
case 0xED:
case 0xFD:
throw CpuException(*this, "Undefined opcode");
break;
default:
2023-09-01 15:11:27 +02:00
throw CpuException(*this, "Unknown opcode");
2023-08-26 23:51:51 +02:00
}
2023-08-26 21:17:47 +02:00
}
processed_mcycles += mcycles;
2023-08-26 19:04:02 +02:00
}