vgbc/cpu/decoder.cpp
MadMaurice e4a6b1f9b4 decoder - Simplify RST command
We can calculate the rst address directly from the op code.
2023-08-28 19:39:18 +02:00

447 lines
11 KiB
C++

#include "cpu/cpu.h"
#include "memory/mem_device.h"
static inline u16 make_u16(u8 msb, u8 lsb)
{
return (((u16)msb << 8) | (u16)lsb);
}
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::step()
{
opcode_t op = readPC8();
int mcycles = 1;
if ((op & 0xC0 == 0x40) && op != 0x76) // LD r, r'
{
u8 tmp;
switch(op & 0x07)
{
case 0x0: tmp = state.B; break;
case 0x1: tmp = state.C; break;
case 0x2: tmp = state.D; break;
case 0x3: tmp = state.E; break;
case 0x4: tmp = state.H; break;
case 0x5: tmp = state.L; break;
case 0x6: tmp = bus->read8(state.HL); break;
case 0x7: tmp = state.A; break;
};
switch((op >> 3) & 0x7)
{
case 0x0: state.B = tmp; break;
case 0x1: state.C = tmp; break;
case 0x2: state.D = tmp; break;
case 0x3: state.E = tmp; break;
case 0x4: state.H = tmp; break;
case 0x5: state.L = tmp; break;
case 0x6: bus->write8(state.HL, tmp); break;
case 0x7: state.A = tmp; break;
}
}
else if(op & 0xC7 == 0x06) // LD r, n
{
u8 imm = readPC8();
switch((op >> 3) & 0x7)
{
case 0x0: state.B = imm; break;
case 0x1: state.C = imm; break;
case 0x2: state.D = imm; break;
case 0x3: state.E = imm; break;
case 0x4: state.H = imm; break;
case 0x5: state.L = imm; break;
case 0x6: bus->write8(state.HL, imm); break;
case 0x7: state.A = imm; break;
}
}
else if(op & 0xC7 == 0x46 && op != 0x76) // LD r, [HL]
{
u8 data = bus->read8(state.HL);
switch((op >> 3) & 0x7)
{
case 0x0: state.B = data; break;
case 0x1: state.C = data; break;
case 0x2: state.D = data; break;
case 0x3: state.E = data; break;
case 0x4: state.H = data; break;
case 0x5: state.L = data; break;
case 0x7: state.A = data; break;
}
}
else if(op & 0xC8 == 0x70 && op != 0x76) // LD [HL], r
{
u8 data;
switch(op & 0x7)
{
case 0x0: data = state.B; break;
case 0x1: data = state.C; break;
case 0x2: data = state.D; break;
case 0x3: data = state.E; break;
case 0x4: data = state.H; break;
case 0x5: data = state.L; break;
case 0x7: data = state.A; break;
}
bus->write8(state.HL, data);
}
else if(op & 0xCF == 0x01) // LD rr, nn
{
u16 data = readPC16();
switch((op >> 4) & 0x3)
{
case 0x0: state.BC = data; break;
case 0x1: state.DE = data; break;
case 0x2: state.HL = data; break;
case 0x3: state.SP = data; break;
}
mcycles = 3;
}
else if(op & 0xCF == 0xC5) // PUSH rr
{
u16 data;
switch((op >> 4) & 0x3)
{
case 0x0: data = state.BC; break;
case 0x1: data = state.DE; break;
case 0x2: data = state.HL; break;
case 0x3: data = state.getAF(); break;
}
state.SP-=2;
bus->write16(state.SP, data);
mcycles = 4;
}
else if(op & 0xCF == 0xC1) // POP rr
{
u16 data = bus->read16(state.SP);
state.SP+=2;
switch((op >> 4) & 0x3)
{
case 0x0: state.BC = data; break;
case 0x1: state.DE = data; break;
case 0x2: state.HL = data; break;
case 0x3: state.setAF(data); break;
}
mcycles = 4;
}
else if(op & 0xC0 == 0x80) // ADD, ADC, SUB, ABC, CP, AND, OR, XOR
{
// SUB r: 0b10010xxx
// CP r: 0b10111xxx
// SBC r: 0b10011xxx
AluOp aluop = (AluOp)((op >> 3) & 0x3);
u8 rhs;
switch(op & 0x7)
{
case 0x0: rhs = state.B; break;
case 0x1: rhs = state.C; break;
case 0x2: rhs = state.D; break;
case 0x3: rhs = state.E; break;
case 0x4: rhs = state.H; break;
case 0x5: rhs = state.L; break;
case 0x6: rhs = bus->read8(state.HL); mcycles = 2; break;
case 0x7: rhs = state.A; break;
}
aluop8(aluop, rhs);
}
else if(op & 0xC6 == 0x04) // INC r; INC [HL]; DEC r; DEC [HL];
{
AluOp aluop = (op & 0x1) ? SUB : ADD;
switch((op >> 3) & 0x7)
{
case 0x0: aluop8(aluop, state.B, 1, state.B, false); break;
case 0x1: aluop8(aluop, state.C, 1, state.C, false); break;
case 0x2: aluop8(aluop, state.D, 1, state.D, false); break;
case 0x3: aluop8(aluop, state.E, 1, state.E, false); break;
case 0x4: aluop8(aluop, state.H, 1, state.C, false); break;
case 0x5: aluop8(aluop, state.L, 1, state.L, false); break;
case 0x7: aluop8(aluop, state.A, 1, state.A, false); break;
case 0x6:
{
u8 tmp = bus->read8(state.HL);
aluop8(aluop, tmp, 1, tmp, false);
bus->write8(state.HL, tmp);
mcycles = 3;
}
break;
}
}
else if(op & 0xE7 == 0xC2) // JP cc, nn:
{
u16 nn = readPC16();
if (decodeCond((op >> 3) && 0x3))
{
state.PC = nn;
mcycles = 4;
}
else
{
mcycles = 3;
}
}
else if(op & 0xE7 == 0x20) // JR cc, e
{
s8 e = readPC8();
bool cond;
if (decodeCond((op >> 3) & 0x3))
{
state.PC += e;
mcycles = 3;
}
else
{
mcycles = 2;
}
}
else if(op & 0xE7 == 0xC4) // CALL cc, nn
{
u16 nn = readPC16();
if(decodeCond((op >> 3) & 0x3))
{
doCall(nn);
mcycles = 6;
}
else
{
mcycles = 3;
}
}
else if(op & 0xE7 == 0xC0) // RET cc
{
if(decodeCond((op >> 3) & 0x3))
{
doRet();
mcycles = 5;
}
else
{
mcycles = 2;
}
}
else if(op & 0xC7 == 0xC7) // RST
{
u16 rst_addr = op & 0x38;
doCall(rst_addr);
}
else
{
switch(op)
{
case 0x00: break; // NOP
case 0x0A: // Load A, [BC]
state.A = bus->read8(state.BC);
mcycles = 2;
break;
case 0x1A: // Load A, [DE]
state.A = bus->read8(state.DE);
mcycles = 2;
break;
case 0x02: // Load [BC], A
bus->write8(state.BC, state.A);
mcycles = 2;
break;
case 0x12: // Load [DE], A
bus->write8(state.DE, state.A);
mcycles = 2;
break;
case 0xFA: // LD A, [nn]
{
u16 addr = readPC16();
state.A = bus->read8(addr);
mcycles = 4;
}
break;
case 0xEA: // LD [nn], A
{
u16 addr = readPC16();
bus->write8(addr, state.A);
mcycles = 4;
}
break;
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]
{
u8 n = readPC8();
state.A = bus->read8(make_u16(0xFFu,n));
mcycles = 3;
}
break;
case 0xE0: // LD [0xFF : n], A
{
u8 n = readPC8();
bus->write8(make_u16(0xFFu,n), state.A);
mcycles = 3;
}
break;
case 0x3A: // LD A, [HL-]
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 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;
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;
state.subtract = false;
state.halfcarry = false;
break;
case 0x37: // SCF Set carry flag
state.carry = true;
state.subtract = false;
state.halfcarry = false;
break;
case 0x27: // DAA
// TODO
break;
case 0x2F: // CPL Complement accumulator
state.A = ~state.A;
state.subtract = true;
state.halfcarry = true;
case 0xC3: // JP nn
{
u16 nn = readPC16();
state.PC = nn;
mcycles = 4;
}
break;
case 0xE9: // JP HL
state.PC = state.HL;
break;
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
doRet(); state.IME=true; mcycles = 4; break;
case 0xF3: // DI
state.IME = false;
state.IME_scheduled = false;
break;
case 0xFB: // EI
state.IME_scheduled = true;
break;
}
}
}