diff --git a/src/Cpu.cpp b/src/Cpu.cpp index fb34685..47e1e56 100644 --- a/src/Cpu.cpp +++ b/src/Cpu.cpp @@ -4,32 +4,87 @@ #include "Cpu.h" #include +#include namespace nes { Cpu::Cpu(Bus* bus): _ticks{}, _bus{bus}, A{}, X{}, Y{}, PC{}, SP{}, flags{} { _instructions = std::vector(256); _instructions[0x00] = {"BRK", &Cpu::BRK, &Cpu::IMP, 7, false}; - _instructions[0xA2] = {"LDX", &Cpu::LDX, &Cpu::IMM, 2, false}; - _instructions[0xAE] = {"LDX", &Cpu::LDX, &Cpu::ABS, 4, false}; - _instructions[0x86] = {"STX", &Cpu::STX, &Cpu::ZP0, 3, false}; - _instructions[0x8E] = {"STX", &Cpu::STX, &Cpu::ABS, 4, false}; - _instructions[0xAC] = {"LDY", &Cpu::LDY, &Cpu::ABS, 4, false}; - _instructions[0xA0] = {"LDY", &Cpu::LDY, &Cpu::IMM, 2, false}; - _instructions[0xA9] = {"LDA", &Cpu::LDA, &Cpu::IMM, 2, false}; - _instructions[0xAD] = {"LDA", &Cpu::LDA, &Cpu::ABS, 4, false}; - _instructions[0x18] = {"CLC", &Cpu::CLC, &Cpu::IMP, 2, false}; + _instructions[0x01] = {"ORA", &Cpu::ORA, &Cpu::IZX, 6, false}; + _instructions[0x05] = {"ORA", &Cpu::ORA, &Cpu::ZP0, 3, false}; + _instructions[0x06] = {"ASL", &Cpu::ASL, &Cpu::ZP0, 5, false}; + _instructions[0x09] = {"ORA", &Cpu::ORA, &Cpu::IMM, 2, false}; + _instructions[0x0A] = {"ASL", &Cpu::ASL_ACC, &Cpu::IMP, 2, false}; + _instructions[0x0D] = {"ORA", &Cpu::ORA, &Cpu::ABS, 4, false}; + _instructions[0x11] = {"ORA", &Cpu::ORA, &Cpu::IZY, 5, true}; + _instructions[0x19] = {"ORA", &Cpu::ORA, &Cpu::ABY, 4, true}; + _instructions[0x15] = {"ORA", &Cpu::ORA, &Cpu::ZPX, 4, false}; + _instructions[0x1D] = {"ORA", &Cpu::ORA, &Cpu::ABX, 4, true}; + _instructions[0x0E] = {"ASL", &Cpu::ASL, &Cpu::ABS, 6, false}; + _instructions[0x16] = {"ASL", &Cpu::ASL, &Cpu::ZPX, 6, false}; + _instructions[0x1E] = {"ASL", &Cpu::ASL, &Cpu::ABX, 7, false}; + _instructions[0x21] = {"AND", &Cpu::AND, &Cpu::IZX, 6, false}; + _instructions[0x25] = {"AND", &Cpu::AND, &Cpu::ZP0, 3, false}; + _instructions[0x29] = {"AND", &Cpu::AND, &Cpu::IMM, 2, false}; + _instructions[0x2D] = {"AND", &Cpu::AND, &Cpu::ABS, 4, false}; + _instructions[0x31] = {"AND", &Cpu::AND, &Cpu::IZY, 5, true}; + _instructions[0x35] = {"AND", &Cpu::AND, &Cpu::ZPX, 4, false}; + _instructions[0x39] = {"AND", &Cpu::AND, &Cpu::ABY, 4, true}; + _instructions[0x3D] = {"AND", &Cpu::AND, &Cpu::ABX, 4, true}; + _instructions[0x41] = {"EOR", &Cpu::EOR, &Cpu::IZX, 6, false}; + _instructions[0x45] = {"EOR", &Cpu::EOR, &Cpu::ZP0, 3, false}; + _instructions[0x49] = {"EOR", &Cpu::EOR, &Cpu::IMM, 2, false}; + _instructions[0x4D] = {"EOR", &Cpu::EOR, &Cpu::ABS, 4, false}; + _instructions[0x51] = {"EOR", &Cpu::EOR, &Cpu::IZY, 5, true}; + _instructions[0x55] = {"EOR", &Cpu::EOR, &Cpu::ZPX, 4, false}; + _instructions[0x59] = {"EOR", &Cpu::EOR, &Cpu::ABY, 4, true}; + _instructions[0x5D] = {"EOR", &Cpu::EOR, &Cpu::ABX, 4, true}; + _instructions[0x61] = {"ADC", &Cpu::ADC, &Cpu::IZX, 6, false}; + _instructions[0x65] = {"ADC", &Cpu::ADC, &Cpu::ZP0, 3, false}; _instructions[0x6D] = {"ADC", &Cpu::ADC, &Cpu::ABS, 4, false}; _instructions[0x69] = {"ADC", &Cpu::ADC, &Cpu::IMM, 2, false}; + _instructions[0x71] = {"ADC", &Cpu::ADC, &Cpu::IZY, 5, true}; + _instructions[0x75] = {"ADC", &Cpu::ADC, &Cpu::ZPX, 4, false}; + _instructions[0x79] = {"ADC", &Cpu::ADC, &Cpu::ABY, 4, true}; + _instructions[0x7D] = {"ADC", &Cpu::ADC, &Cpu::ABX, 4, true}; + _instructions[0xA2] = {"LDX", &Cpu::LDX, &Cpu::IMM, 2, false}; + _instructions[0xA6] = {"LDX", &Cpu::LDX, &Cpu::ZP0, 3, false}; + _instructions[0xAE] = {"LDX", &Cpu::LDX, &Cpu::ABS, 4, false}; + _instructions[0xB6] = {"LDX", &Cpu::LDX, &Cpu::ZPY, 4, false}; + _instructions[0xBE] = {"LDX", &Cpu::LDX, &Cpu::ABY, 4, true}; + _instructions[0x86] = {"STX", &Cpu::STX, &Cpu::ZP0, 3, false}; + _instructions[0x8E] = {"STX", &Cpu::STX, &Cpu::ABS, 4, false}; + _instructions[0x96] = {"STX", &Cpu::STX, &Cpu::ZPY, 4, false}; + _instructions[0xA4] = {"LDY", &Cpu::LDY, &Cpu::ZP0, 3, false}; + _instructions[0xAC] = {"LDY", &Cpu::LDY, &Cpu::ABS, 4, false}; + _instructions[0xA0] = {"LDY", &Cpu::LDY, &Cpu::IMM, 2, false}; + _instructions[0xB4] = {"LDY", &Cpu::LDY, &Cpu::ZPX, 4, false}; + _instructions[0xBC] = {"LDY", &Cpu::LDY, &Cpu::ABX, 4, true}; + _instructions[0xA1] = {"LDA", &Cpu::LDA, &Cpu::IZX, 6, false}; + _instructions[0xA5] = {"LDA", &Cpu::LDA, &Cpu::ZP0, 3, false}; + _instructions[0xA9] = {"LDA", &Cpu::LDA, &Cpu::IMM, 2, false}; + _instructions[0xAD] = {"LDA", &Cpu::LDA, &Cpu::ABS, 4, false}; + _instructions[0xB1] = {"LDA", &Cpu::LDA, &Cpu::IZY, 5, true}; + _instructions[0xB5] = {"LDA", &Cpu::LDA, &Cpu::ZPX, 4, false}; + _instructions[0xB9] = {"LDA", &Cpu::LDA, &Cpu::ABY, 4, true}; + _instructions[0xBD] = {"LDA", &Cpu::LDA, &Cpu::ABX, 4, true}; + _instructions[0x18] = {"CLC", &Cpu::CLC, &Cpu::IMP, 2, false}; _instructions[0x88] = {"DEY", &Cpu::DEY, &Cpu::IMP, 2, false}; _instructions[0xD0] = {"BNE", &Cpu::BNE, &Cpu::REL, 2, false}; + _instructions[0x81] = {"STA", &Cpu::STA, &Cpu::IZX, 6, false}; _instructions[0x8D] = {"STA", &Cpu::STA, &Cpu::ABS, 4, false}; + _instructions[0x91] = {"STA", &Cpu::STA, &Cpu::IZY, 5, true}; + _instructions[0x95] = {"STA", &Cpu::STA, &Cpu::ZPX, 4, false}; + _instructions[0x99] = {"STA", &Cpu::STA, &Cpu::ABY, 5, false}; + _instructions[0x9D] = {"STA", &Cpu::STA, &Cpu::ABX, 5, false}; _instructions[0xEA] = {"NOP", &Cpu::NOP, &Cpu::IMP, 2, false}; _instructions[0x78] = {"SEI", &Cpu::SEI, &Cpu::IMP, 2, false}; _instructions[0xD8] = {"CLD", &Cpu::CLD, &Cpu::IMP, 2, false}; _instructions[0x9A] = {"TXS", &Cpu::TXS, &Cpu::IMP, 2, false}; _instructions[0x10] = {"BPL", &Cpu::BPL, &Cpu::REL, 2, false}; _instructions[0x4C] = {"JMP", &Cpu::JMP, &Cpu::ABS, 3, false}; + _instructions[0x6C] = {"JMP", &Cpu::JMP, &Cpu::IND, 5, false}; _instructions[0x20] = {"JSR", &Cpu::JSR, &Cpu::ABS, 6, false}; _instructions[0x38] = {"SEC", &Cpu::SEC, &Cpu::IMP, 2, false}; _instructions[0xB0] = {"BCS", &Cpu::BCS, &Cpu::REL, 2, false}; @@ -37,24 +92,42 @@ namespace nes { _instructions[0xF0] = {"BEQ", &Cpu::BEQ, &Cpu::REL, 2, false}; _instructions[0x85] = {"STA", &Cpu::STA, &Cpu::ZP0, 3, false}; _instructions[0x24] = {"BIT", &Cpu::BIT, &Cpu::ZP0, 3, false}; + _instructions[0x2C] = {"BIT", &Cpu::BIT, &Cpu::ABS, 4, false}; _instructions[0x70] = {"BVS", &Cpu::BVS, &Cpu::REL, 2, false}; _instructions[0x50] = {"BVC", &Cpu::BVC, &Cpu::REL, 2, false}; _instructions[0x60] = {"RTS", &Cpu::RTS, &Cpu::IMP, 6, false}; _instructions[0xF8] = {"SED", &Cpu::SED, &Cpu::IMP, 2, false}; _instructions[0x08] = {"PHP", &Cpu::PHP, &Cpu::IMP, 3, false}; _instructions[0x68] = {"PLA", &Cpu::PLA, &Cpu::IMP, 4, false}; - _instructions[0x29] = {"AND", &Cpu::AND, &Cpu::IMM, 2, false}; + _instructions[0xC1] = {"CMP", &Cpu::CMP, &Cpu::IZX, 6, false}; + _instructions[0xC5] = {"CMP", &Cpu::CMP, &Cpu::ZP0, 3, false}; _instructions[0xC9] = {"CMP", &Cpu::CMP, &Cpu::IMM, 2, false}; + _instructions[0xCD] = {"CMP", &Cpu::CMP, &Cpu::ABS, 4, false}; + _instructions[0xD1] = {"CMP", &Cpu::CMP, &Cpu::IZY, 5, true}; + _instructions[0xD5] = {"CMP", &Cpu::CMP, &Cpu::ZPX, 4, false}; + _instructions[0xD9] = {"CMP", &Cpu::CMP, &Cpu::ABY, 4, true}; + _instructions[0xDD] = {"CMP", &Cpu::CMP, &Cpu::ABX, 4, true}; _instructions[0x30] = {"BMI", &Cpu::BMI, &Cpu::REL, 2, false}; _instructions[0x48] = {"PHA", &Cpu::PHA, &Cpu::IMP, 3, false}; _instructions[0x28] = {"PLP", &Cpu::PLP, &Cpu::IMP, 4, false}; - _instructions[0x09] = {"ORA", &Cpu::ORA, &Cpu::IMM, 2, false}; _instructions[0xB8] = {"CLV", &Cpu::CLV, &Cpu::IMP, 2, false}; - _instructions[0x49] = {"EOR", &Cpu::EOR, &Cpu::IMM, 2, false}; _instructions[0xC0] = {"CPY", &Cpu::CPY, &Cpu::IMM, 2, false}; + _instructions[0xC4] = {"CPY", &Cpu::CPY, &Cpu::ZP0, 3, false}; + _instructions[0xCC] = {"CPY", &Cpu::CPY, &Cpu::ABS, 4, false}; _instructions[0xE0] = {"CPX", &Cpu::CPX, &Cpu::IMM, 2, false}; + _instructions[0xE4] = {"CPX", &Cpu::CPX, &Cpu::ZP0, 3, false}; + _instructions[0xEC] = {"CPX", &Cpu::CPX, &Cpu::ABS, 4, false}; + _instructions[0xE1] = {"SBC", &Cpu::SBC, &Cpu::IZX, 6, false}; + _instructions[0xE5] = {"SBC", &Cpu::SBC, &Cpu::ZP0, 3, false}; _instructions[0xE9] = {"SBC", &Cpu::SBC, &Cpu::IMM, 2, false}; + _instructions[0xED] = {"SBC", &Cpu::SBC, &Cpu::ABS, 4, false}; + _instructions[0xF1] = {"SBC", &Cpu::SBC, &Cpu::IZY, 5, true}; + _instructions[0xF5] = {"SBC", &Cpu::SBC, &Cpu::ZPX, 4, false}; + _instructions[0xF9] = {"SBC", &Cpu::SBC, &Cpu::ABY, 4, true}; + _instructions[0xFD] = {"SBC", &Cpu::SBC, &Cpu::ABX, 4, true}; _instructions[0x84] = {"STY", &Cpu::STY, &Cpu::ZP0, 3, false}; + _instructions[0x8C] = {"STY", &Cpu::STY, &Cpu::ABS, 4, false}; + _instructions[0x94] = {"STY", &Cpu::STY, &Cpu::ZPX, 4, false}; _instructions[0xC8] = {"INY", &Cpu::INY, &Cpu::IMP, 2, false}; _instructions[0xE8] = {"INX", &Cpu::INX, &Cpu::IMP, 2, false}; _instructions[0xCA] = {"DEX", &Cpu::DEX, &Cpu::IMP, 2, false}; @@ -64,6 +137,29 @@ namespace nes { _instructions[0x8A] = {"TXA", &Cpu::TXA, &Cpu::IMP, 2, false}; _instructions[0xBA] = {"TSX", &Cpu::TSX, &Cpu::IMP, 2, false}; _instructions[0x40] = {"RTI", &Cpu::RTI, &Cpu::IMP, 6, false}; + _instructions[0x46] = {"LSR", &Cpu::LSR, &Cpu::ZP0, 5, false}; + _instructions[0x4E] = {"LSR", &Cpu::LSR, &Cpu::ABS, 6, false}; + _instructions[0x4A] = {"LSR", &Cpu::LSR_ACC, &Cpu::IMP, 2, false}; + _instructions[0x56] = {"LSR", &Cpu::LSR, &Cpu::ZPX, 6, false}; + _instructions[0x5E] = {"LSR", &Cpu::LSR, &Cpu::ABX, 7, false}; + _instructions[0x66] = {"ROR", &Cpu::ROR, &Cpu::ZP0, 5, false}; + _instructions[0x6A] = {"ROR", &Cpu::ROR_ACC, &Cpu::IMP, 2, false}; + _instructions[0x6E] = {"ROR", &Cpu::ROR, &Cpu::ABS, 6, false}; + _instructions[0x76] = {"ROR", &Cpu::ROR, &Cpu::ZPX, 6, false}; + _instructions[0x7E] = {"ROR", &Cpu::ROR, &Cpu::ABX, 7, false}; + _instructions[0x26] = {"ROL", &Cpu::ROL, &Cpu::ZP0, 5, false}; + _instructions[0x2A] = {"ROL", &Cpu::ROL_ACC, &Cpu::IMP, 2, false}; + _instructions[0x2E] = {"ROL", &Cpu::ROL, &Cpu::ABS, 6, false}; + _instructions[0x36] = {"ROL", &Cpu::ROL, &Cpu::ZPX, 6, false}; + _instructions[0x3E] = {"ROL", &Cpu::ROL, &Cpu::ABX, 7, false}; + _instructions[0xE6] = {"INC", &Cpu::INC, &Cpu::ZP0, 5, false}; + _instructions[0xEE] = {"INC", &Cpu::INC, &Cpu::ABS, 6, false}; + _instructions[0xF6] = {"INC", &Cpu::INC, &Cpu::ZPX, 6, false}; + _instructions[0xFE] = {"INC", &Cpu::INC, &Cpu::ABX, 7, false}; + _instructions[0xC6] = {"DEC", &Cpu::DEC, &Cpu::ZP0, 5, false}; + _instructions[0xCE] = {"DEC", &Cpu::DEC, &Cpu::ABS, 6, false}; + _instructions[0xD6] = {"DEC", &Cpu::DEC, &Cpu::ZPX, 6, false}; + _instructions[0xDE] = {"DEC", &Cpu::DEC, &Cpu::ABX, 7, false}; } void Cpu::reset() { @@ -115,7 +211,7 @@ namespace nes { } } - bool Cpu::getFlag(CpuFlags flag) { + bool Cpu::getFlag(CpuFlags flag) const { return flags & flag; } @@ -169,6 +265,85 @@ namespace nes { return {address, 0}; } + Cpu::InstructionArgs Cpu::IZX() { + uint8_t ptrAddress = _bus->read(PC++) + X; + uint8_t lo = _bus->read(ptrAddress); + uint8_t hi = _bus->read(++ptrAddress); + uint16_t address = (hi << 8) | lo; + return {address, 0}; + } + + Cpu::InstructionArgs Cpu::IZY() { + uint8_t ptrAddress = _bus->read(PC++); + uint8_t lo = _bus->read(ptrAddress); + uint8_t hi = _bus->read(++ptrAddress); + uint16_t address = (hi << 8) | lo; + address += Y; + + uint8_t additionalCycles = 0; + if((address & 0xFF00) != (hi << 8)) { + additionalCycles = 1; + } + + return {address, additionalCycles}; + } + + Cpu::InstructionArgs Cpu::IND() { + uint8_t ptrLo = _bus->read(PC++); + uint8_t ptrHi = _bus->read(PC++); + uint16_t ptrAddress = (ptrHi << 8) | ptrLo; + + uint8_t lo = _bus->read(ptrAddress); + uint8_t hi = 0; + + if(ptrLo == 0xFF) { + hi = _bus->read(ptrAddress & 0xFF00); + } else { + hi = _bus->read(++ptrAddress); + } + + uint16_t address = (hi << 8) | lo; + return {address, 0}; + } + + Cpu::InstructionArgs Cpu::ABX() { + uint8_t lo = _bus->read(PC++); + uint8_t hi = _bus->read(PC++); + uint16_t address = (hi << 8) | lo; + address += X; + + uint8_t additionalCycles = 0; + if((address & 0xFF00) != (hi << 8)) { + additionalCycles = 1; + } + + return {address, additionalCycles}; + } + + Cpu::InstructionArgs Cpu::ABY() { + uint8_t lo = _bus->read(PC++); + uint8_t hi = _bus->read(PC++); + uint16_t address = (hi << 8) | lo; + address += Y; + + uint8_t additionalCycles = 0; + if((address & 0xFF00) != (hi << 8)) { + additionalCycles = 1; + } + + return {address, additionalCycles}; + } + + Cpu::InstructionArgs Cpu::ZPX() { + uint8_t address = _bus->read(PC++) + X; + return {address, 0}; + } + + Cpu::InstructionArgs Cpu::ZPY() { + uint8_t address = _bus->read(PC++) + Y; + return {address, 0}; + } + // CPU instructions void Cpu::LDA(Cpu::InstructionArgs args) { @@ -473,4 +648,86 @@ namespace nes { PC = (hi << 8) | lo; } + void Cpu::LSR_ACC(Cpu::InstructionArgs args) { + setFlag(Carry, A & 0x01); + A = A >> 1; + setFlag(Zero, A == 0); + setFlag(Negative, A & 0x80); + } + + void Cpu::LSR(Cpu::InstructionArgs args) { + uint8_t value = _bus->read(args.address); + setFlag(Carry, value & 0x01); + value = value >> 1; + setFlag(Zero, value == 0); + setFlag(Negative, value & 0x80); + _bus->write(args.address, value); + } + + void Cpu::ASL_ACC(Cpu::InstructionArgs args) { + setFlag(Carry, A & 0x80); + A = A << 1; + setFlag(Zero, A == 0); + setFlag(Negative, A & 0x80); + } + + void Cpu::ASL(Cpu::InstructionArgs args) { + uint8_t value = _bus->read(args.address); + setFlag(Carry, value & 0x80); + value = value << 1; + setFlag(Zero, value == 0); + setFlag(Negative, value & 0x80); + _bus->write(args.address, value); + } + + void Cpu::ROR_ACC(Cpu::InstructionArgs args) { + uint8_t carryOriginal = getFlag(Carry); + setFlag(Carry, A & 0x01); + A = (A >> 1) | (carryOriginal << 7); + setFlag(Zero, A == 0); + setFlag(Negative, A & 0x80); + } + + void Cpu::ROR(Cpu::InstructionArgs args) { + uint8_t value = _bus->read(args.address); + uint8_t carryOriginal = getFlag(Carry); + setFlag(Carry, value & 0x01); + value = (value >> 1) | (carryOriginal << 7); + setFlag(Zero, value == 0); + setFlag(Negative, value & 0x80); + _bus->write(args.address, value); + } + + void Cpu::ROL_ACC(Cpu::InstructionArgs args) { + uint8_t carryOriginal = getFlag(Carry); + setFlag(Carry, A & 0x80); + A = (A << 1) | carryOriginal; + setFlag(Zero, A == 0); + setFlag(Negative, A & 0x80); + } + + void Cpu::ROL(Cpu::InstructionArgs args) { + uint8_t value = _bus->read(args.address); + uint8_t carryOriginal = getFlag(Carry); + setFlag(Carry, value & 0x80); + value = (value << 1) | carryOriginal; + setFlag(Zero, value == 0); + setFlag(Negative, value & 0x80); + _bus->write(args.address, value); + } + + void Cpu::INC(Cpu::InstructionArgs args) { + uint8_t value = _bus->read(args.address); + _bus->write(args.address, ++value); + setFlag(Zero, value == 0); + setFlag(Negative, value & 0x80); + } + + void Cpu::DEC(Cpu::InstructionArgs args) { + uint8_t value = _bus->read(args.address); + _bus->write(args.address, --value); + setFlag(Zero, value == 0); + setFlag(Negative, value & 0x80); + } + } diff --git a/src/Cpu.h b/src/Cpu.h index 5b5b934..6f127d3 100644 --- a/src/Cpu.h +++ b/src/Cpu.h @@ -44,7 +44,7 @@ namespace nes { void reset(); void tick(); void setFlag(CpuFlags flag, bool value); - bool getFlag(CpuFlags flag); + bool getFlag(CpuFlags flag) const; void setStartAddress(uint16_t address); private: @@ -71,6 +71,13 @@ namespace nes { InstructionArgs IMP(); InstructionArgs REL(); InstructionArgs ZP0(); + InstructionArgs IZX(); + InstructionArgs IZY(); + InstructionArgs IND(); + InstructionArgs ABX(); + InstructionArgs ABY(); + InstructionArgs ZPX(); + InstructionArgs ZPY(); private: void LDA(InstructionArgs args); @@ -122,6 +129,16 @@ namespace nes { void TSX(InstructionArgs args); void BRK(InstructionArgs args); void RTI(InstructionArgs args); + void LSR_ACC(InstructionArgs args); + void LSR(InstructionArgs args); + void ASL_ACC(InstructionArgs args); + void ASL(InstructionArgs args); + void ROR_ACC(InstructionArgs args); + void ROR(InstructionArgs args); + void ROL_ACC(InstructionArgs args); + void ROL(InstructionArgs args); + void INC(InstructionArgs args); + void DEC(InstructionArgs args); }; } diff --git a/src/Nes.cpp b/src/Nes.cpp index 366d699..282280e 100644 --- a/src/Nes.cpp +++ b/src/Nes.cpp @@ -37,7 +37,7 @@ namespace nes { } size_t ticks = 0; - while (ticks <= 5000) { + while (ticks <= 15000) { _cpu->tick(); ticks++; }