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,
};
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 {
// Registers
union {

View file

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