diff --git a/Makeconf b/Makeconf index edb02e6..deb6dd6 100644 --- a/Makeconf +++ b/Makeconf @@ -1,6 +1,9 @@ -modules := test \ - memory/bus \ - memory/ram \ - cpu/decoder +modules := memory/mem_device \ + memory/bus \ + memory/ram \ + cpu/cpu \ + cpu/decoder + +modules += test CXX_FLAGS := -I $(CURDIR) diff --git a/cpu/cpu.cpp b/cpu/cpu.cpp new file mode 100644 index 0000000..ea74ffa --- /dev/null +++ b/cpu/cpu.cpp @@ -0,0 +1,91 @@ +#include "cpu.h" + +void Cpu_state::setAF(u16 v) +{ + A = (u8)(v >> 8); + zero = (v & 0x80 != 0); + subtract = (v & 0x40 != 0); + halfcarry = (v & 0x20 != 0); + carry = (v & 0x10 != 0); +} + +u16 Cpu_state::getAF() +{ + return ((u16)A << 8) | + (zero ? 0x80 : 0) | + (subtract ? 0x40 : 0) | + (halfcarry ? 0x20 : 0) | + (carry ? 0x10 : 0); +} + +void Cpu::aluop8(AluOp op, u8 lhs, u8 rhs, u8& out, bool update_carry) +{ + u16 rhs16 = rhs; + u16 res16; + u8 res; + + if ((op == ADC || op == SBC) && state.carry) + rhs16++; + + u16 lhs_lower = lhs & 0x0F; + u16 lhs_upper = lhs & 0xF0; + u16 rhs_lower = rhs16 & 0x0F; + u16 rhs_upper = rhs16 & 0x1F0; + + switch(op) + { + case ADD: + case ADC: + res16 = lhs_lower + rhs_lower; + break; + case SUB: + case SBC: + case CP: + res16 = lhs_lower - rhs_lower; + break; + case AND: + res16 = lhs_lower & rhs_lower; + break; + case OR: + res16 = lhs_lower | rhs_lower; + break; + case XOR: + res16 = lhs_lower ^ rhs_lower; + break; + } + + state.halfcarry = (res16 & 0x10 != 0) || op == AND; + state.subtract = (op == SUB) || (op == SBC) || (op == CP); + + switch(op) + { + case ADD: + case ADC: + res16 += lhs_upper + rhs_upper; + break; + case SUB: + case SBC: + case CP: + res16 += lhs_upper - rhs_upper; + break; + case AND: + res16 |= lhs_upper & rhs_upper; + break; + case OR: + res16 |= lhs_upper | rhs_upper; + break; + case XOR: + res16 |= lhs_upper ^ rhs_upper; + break; + } + + res = (u8)(res16 & 0xFF); + + if(update_carry) + state.carry = (res16 & 0x100 != 0); + + state.zero = (res == 0); + + if (op != CP) + out = res; +} diff --git a/cpu/cpu.h b/cpu/cpu.h index c4a91fd..ebe17cf 100644 --- a/cpu/cpu.h +++ b/cpu/cpu.h @@ -12,6 +12,26 @@ enum Flags { F_CARRY = 0x1, }; +enum AluOp : int { + ADD = 0, + ADC = 1, + SUB = 2, + SBC = 3, + AND = 4, + XOR = 5, + OR = 6, + CP = 7, +}; + +enum CC +{ + COND_NZ = 0, + COND_Z = 1, + COND_NC = 2, + COND_C = 3, +}; + + struct Cpu_state { // Registers union { @@ -35,6 +55,15 @@ struct Cpu_state { bool subtract; bool halfcarry; bool carry; + + bool IME; // interrupts enabled/disabled + bool IME_scheduled; // interrupts about to be enabled + + + bool bootRomEnabled; // Whether boot ROM is visible + + void setAF(u16 v); + u16 getAF(); }; class Cpu { @@ -53,6 +82,20 @@ private: void pushStack16(u16 data); u16 popStack16(); + + void aluop8(AluOp op, u8 lhs, u8 rhs, u8& out, bool update_carry = true); + + inline + void aluop8(AluOp op, u8 rhs, bool update_carry = true) + { + aluop8(op, state.A, rhs, state.A, update_carry); + } + + void doCall(u16 target); + void doRet(); + + bool decodeCond(u8 cc); + public: Cpu(); diff --git a/cpu/decoder.cpp b/cpu/decoder.cpp index 17a7282..9e0d257 100644 --- a/cpu/decoder.cpp +++ b/cpu/decoder.cpp @@ -1,6 +1,11 @@ #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); @@ -41,6 +46,30 @@ u16 Cpu::popStack16() 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(); @@ -85,7 +114,7 @@ void Cpu::step() case 0x3: state.E = imm; break; case 0x4: state.H = imm; break; case 0x5: state.L = imm; break; - case 0x6: bus->write8(state.HL, tmp); break; + case 0x6: bus->write8(state.HL, imm); break; case 0x7: state.A = imm; break; } } @@ -95,13 +124,13 @@ void Cpu::step() 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 0x7: state.A = imm; break; + 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 @@ -120,272 +149,311 @@ void Cpu::step() 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) + { + u16 rst_addr; + switch((op >> 3) & 0x7) + { + case 0x0: rst_addr = 0x00; break; + case 0x1: rst_addr = 0x08; break; + case 0x2: rst_addr = 0x10; break; + case 0x3: rst_addr = 0x18; break; + case 0x4: rst_addr = 0x20; break; + case 0x5: rst_addr = 0x28; break; + case 0x6: rst_addr = 0x30; break; + case 0x7: rst_addr = 0x38; break; + } + + doCall(rst_addr); + } else { switch(op) { case 0x00: break; // NOP - - case 0x01: // LD BC, n16 - state.BC = readPC16(); mcycles = 12; break; - case 0x11: // LD DE, n16 - state.DE = readPC16(); mcycles = 12; break; - case 0x21: // LD HL, n16 - state.HL = readPC16(); mcycles = 12; break; - case 0x31: // LD SP, n16 - state.SP = readPC16(); mcycles = 12; break; - - case 0x02: // LD [BC], A - bus->write8(state.BC, state.A); mcycles = 8; break; - case 0x12: // LD [DE], A - bus->write8(state.DE, state.A); mcycles = 8; break; - case 0x22: // LD [HL+], A - bus->write8(state.HL, state.A); state.HL++; mcycles = 8; break; - case 0x32: // LD [HL-], A - bus->write8(state.HL, state.A); state.HL--; mcycles = 8; break; - - case 0x03: // INC BC - state.BC++; mcycles = 2; break; - case 0x13: // INC DE - state.DE++; mcycles = 2; break; - case 0x23: // INC HL - state.HL++; mcycles = 2; break; - case 0x33: // INC SP - state.SP++; mcycles = 2; break; - - case 0x04: // INC B - state.B++; - state.zero = (state.B == 0); - state.subtract = false; - state.halfcarry = (state.B & 0x0F == 0); - break; - case 0x14: // INC D - state.D++; - state.zero = (state.D == 0); - state.subtract = false; - state.halfcarry = (state.D & 0x0F == 0); - break; - case 0x24: // INC H - state.H++; - state.zero = (state.H == 0); - state.subtract = false; - state.halfcarry = (state.H & 0x0F == 0); - break; - case 0x34: // INC [HL] - { - u8 data = bus->read8(state.HL); - data++; - bus->write8(state.HL, data); - state.zero = (data == 0); - state.subtract = false; - state.halfcarry = (data & 0x0F == 0); - mcycles = 3; - } - break; - - case 0x05: // INC B - state.B--; - state.zero = (state.B == 0); - state.subtract = true; - state.halfcarry = (state.B & 0x0F == 0x0F); - break; - case 0x15: // INC D - state.D--; - state.zero = (state.D == 0); - state.subtract = true; - state.halfcarry = (state.D & 0x0F == 0x0F); - break; - case 0x25: // INC H - state.H--; - state.zero = (state.H == 0); - state.subtract = true; - state.halfcarry = (state.H & 0x0F == 0x0F); - break; - case 0x35: // INC [HL] - { - u8 data = bus->read8(state.HL); - data--; - bus->write8(state.HL, data); - state.zero = (data == 0); - state.subtract = true; - state.halfcarry = (data & 0x0F == 0x0F); - mcycles = 3; - } - break; - - case 0x06: // LD B, n8 - state.B = readPC8(); + case 0x0A: // Load A, [BC] + state.A = bus->read8(state.BC); mcycles = 2; break; - case 0x16: // LD D, n8 - state.D = readPC8(); + case 0x1A: // Load A, [DE] + state.A = bus->read8(state.DE); mcycles = 2; break; - case 0x26: // LD H, n8 - state.H = readPC8(); + case 0x02: // Load [BC], A + bus->write8(state.BC, state.A); mcycles = 2; break; - case 0x36: // LD [HL], n8 - bus->write8(state.HL, readPC8()); - mcycles = 3; + case 0x12: // Load [DE], A + bus->write8(state.DE, state.A); + mcycles = 2; break; - - case 0x07: // RLCA - case 0x17: // RLA - case 0x27: // DAA - case 0x37: // SCF - break; - - case 0x08: // LD [a16], SP + case 0xFA: // LD A, [nn] { u16 addr = readPC16(); - bus->write16(addr, state.SP); - mcycles = 5; + state.A = bus->read8(addr); + mcycles = 4; } break; - case 0x18: // JR e8 + case 0xEA: // LD [nn], A { - s8 rel = readPC8(); - state.PC = state.PC + rel; + 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 0x28: // JR Z, e8 + case 0xE0: // LD [0xFF : n], A { - s8 rel = readPC8(); - if (state.zero) - { - state.PC = state.PC + rel; - mcycles = 3; - } - else - { - mcycles = 2; - } + u8 n = readPC8(); + bus->write8(make_u16(0xFFu,n), state.A); + mcycles = 3; } break; - case 0x38: // JR C, e8 - { - s8 rel = readPC8(); - if (state.carry) - { - state.PC = state.PC + rel; - mcycles = 3; - } - else - { - mcycles = 2; - } - } - break; - - case 0x09: // ADD HL, BC - case 0x19: // ADD HL, DE - case 0x29: // ADD HL, HL - case 0x39: // ADD HL, SP - // TODO - break; - - case 0x0A: // LD A, [BC] - state.A = bus->read8(state.BC); - break; - case 0x1A: // LD A, [DE] - state.A = bus->read8(state.DE); - break; - case 0x2A: // LD A, [HL+] - state.A = bus->read8(state.HL); - state.HL++; - break; case 0x3A: // LD A, [HL-] - state.A = bus->read8(state.HL); - state.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 0x0B: // DEC BC - state.BC--; - break; - case 0x1B: // DEC DE - state.DE--; - break; - case 0x2B: // DEC HL - state.HL--; - break; - case 0x3B: // DEC SP - state.SP--; + case 0x37: // SCF Set carry flag + state.carry = true; + state.subtract = false; + state.halfcarry = false; break; - case 0x0C: // INC C - state.C++; - state.zero = (state.C == 0); - state.subtract = false; - state.halfcarry = (state.C & 0x0F == 0); - break; - case 0x1C: // INC E - state.E++; - state.zero = (state.E == 0); - state.subtract = false; - state.halfcarry = (state.E & 0x0F == 0); - break; - case 0x2C: // INC L - state.L++; - state.zero = (state.L == 0); - state.subtract = false; - state.halfcarry = (state.L & 0x0F == 0); - break; - case 0x3C: // INC A - state.A++; - state.zero = (state.A == 0); - state.subtract = false; - state.halfcarry = (state.A & 0x0F == 0); - break; - - case 0x0D: // DEC C - state.C--; - state.zero = (state.C == 0); - state.subtract = true; - state.halfcarry = (state.C & 0x0F == 0x0F); - break; - case 0x1D: // DEC E - state.E--; - state.zero = (state.E == 0); - state.subtract = true; - state.halfcarry = (state.E & 0x0F == 0x0F); - break; - case 0x2D: // DEC L - state.L--; - state.zero = (state.L == 0); - state.subtract = true; - state.halfcarry = (state.L & 0x0F == 0x0F); - break; - case 0x3D: // DEC A - state.A--; - state.zero = (state.A == 0); - state.subtract = true; - state.halfcarry = (state.A & 0x0F == 0x0F); - break; - case 0x0E: // LD C, n8 - state.C = readPC8(); - mcycles = 2; - break; - case 0x1E: // LD E, n8 - state.E = readPC8(); - mcycles = 2; - break; - case 0x2E: // LD L, n8 - state.L = readPC8(); - mcycles = 2; - break; - case 0x3E: // LD A, n8 - state.A = readPC8(); - mcycles = 2; - break; - - case 0x0F: // RRCA - case 0x1F: // RRA - case 0x2F: // CPL - case 0x3F: // CCF + 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; } } } diff --git a/memory/mem_device.cpp b/memory/mem_device.cpp new file mode 100644 index 0000000..10201db --- /dev/null +++ b/memory/mem_device.cpp @@ -0,0 +1,14 @@ +#include "memory/mem_device.h" + +void Mem_device::write16(u16 addr, u16 data) +{ + write8(addr, (u8)(data & 0xFF)); + write8(addr+1, (u8)((data >> 8) & 0xFF)); +} + +u16 Mem_device::read16(u16 addr) +{ + u8 lsb = read8(addr); + u8 msb = read8(addr+1); + return ((u16)msb << 8) | (u16)lsb; +} diff --git a/memory/mem_device.h b/memory/mem_device.h index 039d3a3..70aa118 100644 --- a/memory/mem_device.h +++ b/memory/mem_device.h @@ -7,6 +7,6 @@ public: virtual void write8(u16 addr, u8 data) = 0; virtual u8 read8(u16 addr) = 0; - virtual void write16(u16 addr, u16 data) = 0; - virtual u16 read16(u16 addr) = 0; + virtual void write16(u16 addr, u16 data); + virtual u16 read16(u16 addr); }; diff --git a/memory/ram.cpp b/memory/ram.cpp index d84ea6e..d430a43 100644 --- a/memory/ram.cpp +++ b/memory/ram.cpp @@ -17,15 +17,3 @@ u8 RAM::read8(u16 addr) { if (addr >= size) return 0xFFu; return memory[addr]; } - -void RAM::write16(u16 addr, u16 data) { - if(addr >= size - 1 || readonly) return; - u16 *ptr = (u16*)&memory[addr]; - *ptr = data; -} - -u16 RAM::read16(u16 addr) { - if(addr >= size - 1) return 0xFFFFu; - u16 *ptr = (u16*)&memory[addr]; - return *ptr; -} diff --git a/memory/ram.h b/memory/ram.h index b16ad18..aea65ab 100644 --- a/memory/ram.h +++ b/memory/ram.h @@ -14,7 +14,4 @@ public: virtual void write8(u16 addr, u8 data); virtual u8 read8(u16 addr); - - virtual void write16(u16 addr, u16 data); - virtual u16 read16(u16 addr); };