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:
parent
ef9bda4a30
commit
175a24c77d
2 changed files with 57 additions and 38 deletions
25
cpu/cpu.h
25
cpu/cpu.h
|
@ -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 {
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue