#include "cpu/cpu.h" #include "cpu/panic.h" #include "memory/mem_device.h" static inline u16 make_u16(u8 msb, u8 lsb) { return (((u16)msb << 8) | (u16)lsb); } void Cpu::executeInstruction() { 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; // TODO: case 0x27: break; // DAA 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 = IME_ON; mcycles = 4; break; case 0xF3: // DI state.IME = IME_OFF; break; case 0xFB: // EI state.IME = IME_SCHEDULED; break; default: panic("Unknown opcode 0x%x\n",op); } } processed_mcycles += mcycles; }