cpu/decoder - define opcode class with methods

Instead of doing the same bit operations on the opcode everywhere, and
possibly botching it in the process, we create a datatype which has
methods return often used values extracted from the opcode.
This commit is contained in:
madmaurice 2023-08-30 18:27:53 +02:00
parent ef9bda4a30
commit 175a24c77d
2 changed files with 57 additions and 38 deletions

View file

@ -62,6 +62,31 @@ enum IME_state
IME_ON, IME_ON,
}; };
struct opcode {
const opcode_t value;
inline operator opcode_t() const
{ return value; }
inline u8 reg8idxlo() const
{ return value & 0x7; }
inline u8 reg8idxhi() const
{ return (value >> 3) & 0x7; }
inline u8 reg16idx() const
{ return (value >> 4) & 0x3; }
inline AluOp aluop() const
{ return (AluOp)((value >> 3) & 0x7); }
inline u8 cc() const
{ return (value >> 3) & 0x3; }
inline u16 rst_addr() const
{ return (u16)(value & 0x38); }
};
struct Cpu_state { struct Cpu_state {
// Registers // Registers
union { union {

View file

@ -20,7 +20,8 @@ static inline void add16(Cpu& cpu, u16& out, u16 lhs, u16 rhs)
void Cpu::executeInstruction() void Cpu::executeInstruction()
{ {
u16 currentpc = state.PC; u16 currentpc = state.PC;
opcode_t op = readPC8(); opcode op{ readPC8() };
int mcycles = 1; int mcycles = 1;
#if 0 #if 0
@ -30,41 +31,41 @@ void Cpu::executeInstruction()
if ((op & 0xC0) == 0x40 && op != 0x76) // LD r, r'; LD r, [HL]; LD [HL], r if ((op & 0xC0) == 0x40 && op != 0x76) // LD r, r'; LD r, [HL]; LD [HL], r
{ {
u8 tmp; u8 tmp;
switch(op & 0x07) switch(op.reg8idxlo())
{ {
case 0x6: tmp = bus->read8(state.HL); break; case 0x6: tmp = bus->read8(state.HL); break;
default: tmp = state.reg8(op & 0x07); break; default: tmp = state.reg8(op.reg8idxlo()); break;
}; };
switch((op >> 3) & 0x7) switch(op.reg8idxhi())
{ {
case 0x6: bus->write8(state.HL, tmp); break; case 0x6: bus->write8(state.HL, tmp); break;
default: state.reg8((op >> 3) & 0x7) = tmp; break; default: state.reg8(op.reg8idxhi()) = tmp; break;
} }
} }
else if((op & 0xC7) == 0x06) // LD r, n else if((op & 0xC7) == 0x06) // LD r, n
{ {
u8 imm = readPC8(); u8 imm = readPC8();
switch((op >> 3) & 0x7) switch(op.reg8idxhi())
{ {
case 0x6: bus->write8(state.HL, imm); break; case 0x6: bus->write8(state.HL, imm); break;
default: state.reg8((op >> 3) & 0x7) = imm; break; default: state.reg8(op.reg8idxhi()) = imm; break;
} }
} }
else if((op & 0xCF) == 0x01) // LD rr, nn else if((op & 0xCF) == 0x01) // LD rr, nn
{ {
u16 data = readPC16(); u16 data = readPC16();
state.reg16((op >> 4) & 0x3) = data; state.reg16(op.reg16idx()) = data;
mcycles = 3; mcycles = 3;
} }
else if((op & 0xCF) == 0xC5) // PUSH rr else if((op & 0xCF) == 0xC5) // PUSH rr
{ {
u16 data; u16 data;
switch((op >> 4) & 0x3) switch(op.reg16idx())
{ {
case 0x3: data = state.getAF(); break; case 0x3: data = state.getAF(); break;
default: data = state.reg16((op >> 4) & 0x3); default: data = state.reg16(op.reg16idx());
} }
pushStack16(data); pushStack16(data);
@ -75,39 +76,35 @@ void Cpu::executeInstruction()
{ {
u16 data = popStack16(); u16 data = popStack16();
switch((op >> 4) & 0x3) switch(op.reg16idx())
{ {
case 0x3: state.setAF(data); break; case 0x3: state.setAF(data); break;
default: state.reg16((op >> 4) & 0x3) = data; break; default: state.reg16(op.reg16idx()) = data; break;
} }
mcycles = 4; mcycles = 4;
} }
else if((op & 0xC0) == 0x80) // ADD, ADC, SUB, SBC, CP, AND, OR, XOR else if((op & 0xC0) == 0x80) // ADD, ADC, SUB, SBC, CP, AND, OR, XOR
{ {
AluOp aluop = (AluOp)((op >> 3) & 0x3);
u8 reg = op & 0x7;
u8 rhs; u8 rhs;
switch(reg) switch(op.reg8idxlo())
{ {
case 0x6: rhs = bus->read8(state.HL); mcycles = 2; break; case 0x6: rhs = bus->read8(state.HL); mcycles = 2; break;
default: rhs = state.reg8(reg); break; default: rhs = state.reg8(op.reg8idxlo()); break;
} }
aluop8(aluop, rhs); aluop8(op.aluop(), rhs);
} }
else if((op & 0xC7) == 0xC6) // ADD n, ADC n, SUB n, SBC n, AND n, XOR n, OR n, CP n else if((op & 0xC7) == 0xC6) // ADD n, ADC n, SUB n, SBC n, AND n, XOR n, OR n, CP n
{ {
AluOp aluop = (AluOp)((op >> 3) & 0x3); aluop8(op.aluop(), readPC8());
aluop8(aluop, readPC8());
mcycles = 2; mcycles = 2;
} }
else if((op & 0xC6) == 0x04) // INC r; INC [HL]; DEC r; DEC [HL]; else if((op & 0xC6) == 0x04) // INC r; INC [HL]; DEC r; DEC [HL];
{ {
AluOp aluop = (op & 0x1) ? SUB : ADD; AluOp aluop = (op & 0x1) ? SUB : ADD;
switch((op >> 3) & 0x7) switch(op.reg8idxhi())
{ {
case 0x6: case 0x6:
{ {
@ -119,7 +116,7 @@ void Cpu::executeInstruction()
break; break;
default: default:
{ {
u8& reg = state.reg8((op >> 3) & 0x7); u8& reg = state.reg8(op.reg8idxhi());
aluop8(aluop, reg, 1, reg, false); break; aluop8(aluop, reg, 1, reg, false); break;
} }
break; break;
@ -127,14 +124,14 @@ void Cpu::executeInstruction()
} }
else if((op & 0xC7) == 0x03) // INC rr; DEC rr else if((op & 0xC7) == 0x03) // INC rr; DEC rr
{ {
state.reg16((op >> 4) & 0x3) += ((op & 0x08) ? -1 : 1); state.reg16(op.reg16idx()) += ((op & 0x08) ? -1 : 1);
mcycles = 2; mcycles = 2;
} }
else if((op & 0xE7) == 0xC2) // JP cc, nn: else if((op & 0xE7) == 0xC2) // JP cc, nn:
{ {
u16 nn = readPC16(); u16 nn = readPC16();
if (decodeCond((op >> 3) && 0x3)) if (decodeCond(op.cc()))
{ {
state.PC = nn; state.PC = nn;
mcycles = 4; mcycles = 4;
@ -147,7 +144,7 @@ void Cpu::executeInstruction()
s8 e = readPC8(); s8 e = readPC8();
bool cond; bool cond;
if (decodeCond((op >> 3) & 0x3)) if (decodeCond(op.cc()))
{ {
state.PC += e; state.PC += e;
mcycles = 3; mcycles = 3;
@ -159,7 +156,7 @@ void Cpu::executeInstruction()
{ {
u16 nn = readPC16(); u16 nn = readPC16();
if(decodeCond((op >> 3) & 0x3)) if(decodeCond(op.cc()))
{ {
doCall(nn); doCall(nn);
mcycles = 6; mcycles = 6;
@ -169,12 +166,12 @@ void Cpu::executeInstruction()
} }
else if((op & 0xCF) == 0x09) // ADD HL, rr else if((op & 0xCF) == 0x09) // ADD HL, rr
{ {
add16(*this, state.HL, state.HL, state.reg16((op >> 4) & 0x3)); add16(*this, state.HL, state.HL, state.reg16(op.reg16idx()));
mcycles = 2; mcycles = 2;
} }
else if((op & 0xE7) == 0xC0) // RET cc else if((op & 0xE7) == 0xC0) // RET cc
{ {
if(decodeCond((op >> 3) & 0x3)) if(decodeCond(op.cc()))
{ {
doRet(); doRet();
mcycles = 5; mcycles = 5;
@ -184,29 +181,26 @@ void Cpu::executeInstruction()
} }
else if((op & 0xC7) == 0xC7) // RST else if((op & 0xC7) == 0xC7) // RST
{ {
u16 rst_addr = op & 0x38; doCall(op.rst_addr());
doCall(rst_addr);
} }
else if(op == 0xCB) // PREFIX else if(op == 0xCB) // PREFIX
{ {
currentpc = state.PC; currentpc = state.PC;
opcode_t prefix_op = readPC8(); opcode prefix_op{ readPC8() };
#if 0 #if 0
printf("@0x%04x: CB opcode %02X\n", currentpc, prefix_op); printf("@0x%04x: CB opcode %02X\n", currentpc, prefix_op);
#endif #endif
u8 reg = prefix_op & 0x7;
u8 data; u8 data;
switch(reg) switch(prefix_op.reg8idxlo())
{ {
case 0x6: data = bus->read8(state.HL); mcycles = 3; break; case 0x6: data = bus->read8(state.HL); mcycles = 3; break;
default: data = state.reg8(reg); mcycles = 2; break; default: data = state.reg8(prefix_op.reg8idxlo()); mcycles = 2; break;
} }
// For BIT, RES, SET // For BIT, RES, SET
u8 bit = (prefix_op >> 3) & 0x7; u8 bit = prefix_op.reg8idxhi();
switch(prefix_op & 0xC0) switch(prefix_op & 0xC0)
{ {
@ -278,10 +272,10 @@ void Cpu::executeInstruction()
// All ops except for BIT write the data back to where it came from // All ops except for BIT write the data back to where it came from
if ((prefix_op & 0xC0) != 0x40) if ((prefix_op & 0xC0) != 0x40)
{ {
switch(reg) switch(prefix_op.reg8idxlo())
{ {
case 0x6: bus->write8(state.HL, data); mcycles = 4; break; case 0x6: bus->write8(state.HL, data); mcycles = 4; break;
default: state.reg8(reg) = data; break; default: state.reg8(prefix_op.reg8idxlo()) = data; break;
} }
} }
} }