From 40e2f45177132e1c51f17d703970449936c2c6de Mon Sep 17 00:00:00 2001 From: Selim Mustafaev Date: Sun, 20 Aug 2023 11:36:03 +0300 Subject: [PATCH] Adding mapper0 and new CPU instructions --- CMakeLists.txt | 2 +- main.cpp | 30 ++-- src/Bus.cpp | 32 +++- src/Bus.h | 6 +- src/Cartridge.cpp | 43 +++++ src/{Rom.h => Cartridge.h} | 23 ++- src/Cpu.cpp | 355 +++++++++++++++++++++++++++++++++---- src/Cpu.h | 57 +++++- src/Mapper/Mapper.cpp | 12 ++ src/Mapper/Mapper.h | 28 +++ src/Mapper/Mapper0.cpp | 16 ++ src/Mapper/Mapper0.h | 24 +++ src/Nes.cpp | 28 +-- src/Nes.h | 9 +- src/Rom.cpp | 31 ---- 15 files changed, 583 insertions(+), 113 deletions(-) create mode 100644 src/Cartridge.cpp rename src/{Rom.h => Cartridge.h} (54%) create mode 100644 src/Mapper/Mapper.cpp create mode 100644 src/Mapper/Mapper.h create mode 100644 src/Mapper/Mapper0.cpp create mode 100644 src/Mapper/Mapper0.h delete mode 100644 src/Rom.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 43d5c98..8f6e7a5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,4 +3,4 @@ project(nes) set(CMAKE_CXX_STANDARD 23) -add_executable(nes main.cpp src/Rom.cpp src/Rom.h src/Nes.cpp src/Nes.h src/Cpu.cpp src/Cpu.h src/Bus.cpp src/Bus.h) +add_executable(nes main.cpp src/Cartridge.cpp src/Cartridge.h src/Nes.cpp src/Nes.h src/Cpu.cpp src/Cpu.h src/Bus.cpp src/Bus.h src/Mapper/Mapper.cpp src/Mapper/Mapper.h src/Mapper/Mapper0.cpp src/Mapper/Mapper0.h) diff --git a/main.cpp b/main.cpp index 604ad60..6feb618 100644 --- a/main.cpp +++ b/main.cpp @@ -6,21 +6,23 @@ int main() { nes::Nes device; - std::stringstream ss; - ss << "A2 0A 8E 00 00 A2 03 8E 01 00 AC 00 00 A9 00 18 6D 01 00 88 D0 FA 8D 02 00 EA EA EA"; - uint16_t nOffset = 0x8000; - while (!ss.eof()) - { - std::string b; - ss >> b; - device.write(nOffset++, (uint8_t)std::stoul(b, nullptr, 16)); - } +// std::stringstream ss; +// ss << "A2 0A 8E 00 00 A2 03 8E 01 00 AC 00 00 A9 00 18 6D 01 00 88 D0 FA 8D 02 00 EA EA EA"; +// uint16_t nOffset = 0x8000; +// while (!ss.eof()) +// { +// std::string b; +// ss >> b; +// device.write(nOffset++, (uint8_t)std::stoul(b, nullptr, 16)); +// } +// +// // Set Reset Vector +// device.write(0xFFFC, 0x00); +// device.write(0xFFFD, 0x80); +// +// device.reset(); - // Set Reset Vector - device.write(0xFFFC, 0x00); - device.write(0xFFFD, 0x80); - - device.reset(); + device.runRom("/home/selim/Downloads/nestest.nes", 0xC000); return 0; } diff --git a/src/Bus.cpp b/src/Bus.cpp index 84913c7..dcbf6c6 100644 --- a/src/Bus.cpp +++ b/src/Bus.cpp @@ -3,22 +3,42 @@ // #include "Bus.h" +#include namespace nes { - Bus::Bus(): _rom{nullptr} { - _ram = std::make_unique(64*1024); + Bus::Bus(): _cartridge{nullptr} { + _ram = std::make_unique(2*1024); } uint8_t Bus::read(uint16_t address) { - return _ram[address]; + if(address < 0x2000) { + return _ram[address & 0x07FF]; + } + else if(address >= 0x2000 && address < 0x4000) { + std::cout << "PPU read at address: " << address << std::endl; + return 0; + } + else if(address >= 0x8000) { + return _cartridge->readPrg(address); + } + + return 0; } void Bus::write(uint16_t address, uint8_t value) { - _ram[address] = value; + if(address < 0x2000) { + _ram[address & 0x07FF] = value; + } + else if(address >= 0x2000 && address < 0x4000) { + std::cout << "PPU write at address: " << address << std::endl; + } + else if(address >= 0x8000) { + std::cout << "Cartridge write at address: " << address << std::endl; + } } - void Bus::connect(Rom *rom) { - _rom = rom; + void Bus::connect(Cartridge *cartridge) { + _cartridge = cartridge; } } \ No newline at end of file diff --git a/src/Bus.h b/src/Bus.h index 7163608..5ca2e85 100644 --- a/src/Bus.h +++ b/src/Bus.h @@ -5,7 +5,7 @@ #ifndef NES_BUS_H #define NES_BUS_H -#include "Rom.h" +#include "Cartridge.h" #include #include @@ -17,11 +17,11 @@ namespace nes { Bus(); uint8_t read(uint16_t address); void write(uint16_t address, uint8_t value); - void connect(Rom* rom); + void connect(Cartridge* cartridge); private: std::unique_ptr _ram; - Rom* _rom; + Cartridge* _cartridge; }; } diff --git a/src/Cartridge.cpp b/src/Cartridge.cpp new file mode 100644 index 0000000..fb787b2 --- /dev/null +++ b/src/Cartridge.cpp @@ -0,0 +1,43 @@ +// +// Created by Selim Mustafaev on 09.08.2023. +// + +#include "Cartridge.h" +#include +#include +#include + +namespace nes { + + Cartridge::Cartridge(const fs::path &path) { + auto romSize = fs::file_size(path); + _romData = std::make_unique(romSize); + + std::ifstream rom(path); + rom.read(reinterpret_cast(_romData.get()), static_cast(romSize)); + + // UB here + // Should be std::start_lifetime_as(_romData); + // when it become available + _header = reinterpret_cast(_romData.get()); + + const size_t prgSize = _header->prgChunks*PRG_CHUNK_SIZE; + const size_t chrSize = _header->chrChunks*CHR_CHUNK_SIZE; + + _prgRom = std::span(_romData.get() + sizeof(RomHeader), prgSize); + _chrRom = std::span(_romData.get() + sizeof(RomHeader) + prgSize, chrSize); + + _mapper = std::make_unique(_header->prgChunks, _header->chrChunks); + } + + uint8_t Cartridge::readPrg(uint16_t address) { + uint32_t mappedAddress = _mapper->mapPrg(address); + return _prgRom[mappedAddress]; + } + + uint8_t Cartridge::readChr(uint16_t address) { + uint32_t mappedAddress = _mapper->mapChr(address); + return _chrRom[mappedAddress]; + } + +} diff --git a/src/Rom.h b/src/Cartridge.h similarity index 54% rename from src/Rom.h rename to src/Cartridge.h index 555b9b8..1d3f47e 100644 --- a/src/Rom.h +++ b/src/Cartridge.h @@ -2,8 +2,10 @@ // Created by Selim Mustafaev on 09.08.2023. // -#ifndef NES_ROM_H -#define NES_ROM_H +#ifndef NES_CARTRIDGE_H +#define NES_CARTRIDGE_H + +#include "Mapper/Mapper0.h" #include #include @@ -24,17 +26,22 @@ namespace nes { uint8_t reserved[10]; }; - class Rom { + class Cartridge { public: - explicit Rom(const fs::path& path); + explicit Cartridge(const fs::path& path); + + public: + uint8_t readPrg(uint16_t address); + uint8_t readChr(uint16_t address); private: - std::unique_ptr _romData; + std::unique_ptr _romData; + std::unique_ptr _mapper; RomHeader* _header; - std::span _prgRom; - std::span _chrRom; + std::span _prgRom; + std::span _chrRom; }; } -#endif //NES_ROM_H +#endif //NES_CARTRIDGE_H diff --git a/src/Cpu.cpp b/src/Cpu.cpp index 72383d4..00aa1b3 100644 --- a/src/Cpu.cpp +++ b/src/Cpu.cpp @@ -9,16 +9,61 @@ 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[0x6D] = {"ADC", &Cpu::ADC, &Cpu::ABS, 4, false}; + _instructions[0x69] = {"ADC", &Cpu::ADC, &Cpu::IMM, 2, false}; _instructions[0x88] = {"DEY", &Cpu::DEY, &Cpu::IMP, 2, false}; _instructions[0xD0] = {"BNE", &Cpu::BNE, &Cpu::REL, 2, false}; _instructions[0x8D] = {"STA", &Cpu::STA, &Cpu::ABS, 4, 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[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}; + _instructions[0x90] = {"BCC", &Cpu::BCC, &Cpu::REL, 2, false}; + _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[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[0xC9] = {"CMP", &Cpu::CMP, &Cpu::IMM, 2, false}; + _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[0xE0] = {"CPX", &Cpu::CPX, &Cpu::IMM, 2, false}; + _instructions[0xE9] = {"SBC", &Cpu::SBC, &Cpu::IMM, 2, false}; + _instructions[0x84] = {"STY", &Cpu::STY, &Cpu::ZP0, 3, 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}; + _instructions[0xA8] = {"TAY", &Cpu::TAY, &Cpu::IMP, 2, false}; + _instructions[0xAA] = {"TAX", &Cpu::TAX, &Cpu::IMP, 2, false}; + _instructions[0x98] = {"TYA", &Cpu::TYA, &Cpu::IMP, 2, false}; + _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}; } void Cpu::reset() { @@ -40,13 +85,14 @@ namespace nes { auto instruction = _instructions[opcode]; if(instruction.getAddress == nullptr) { + std::cout << "Unknown instruction: " << (int)opcode << std::endl; return; } auto args = (this->*instruction.getAddress)(); (this->*instruction.process)(args); - std::cout << instruction.name << ", A: " << (int)A << ", X: " << (int)X << ", Y: " << (int)Y << std::endl; + std::cout << instruction.name << std::hex << ", PC: " << (int)PC << ", A: " << (int)A << ", X: " << (int)X << ", Y: " << (int)Y << std::endl; _ticks = instruction.cycles; if(instruction.variableCycles) { @@ -69,6 +115,56 @@ namespace nes { return flags & flag; } + void Cpu::setStartAddress(uint16_t address) { + PC = address; + } + + void Cpu::branch(Cpu::InstructionArgs args) { + _ticks++; + + uint16_t address = PC + args.address; + uint16_t srcPage = PC & 0xFF00; + uint16_t dstPage = address & 0xFF00; + + if(srcPage != dstPage) { + _ticks++; + } + + PC = address; + } + + // Addressing modes + + Cpu::InstructionArgs Cpu::IMM() { + return {++PC, 0}; + } + + Cpu::InstructionArgs Cpu::ABS() { + uint8_t lo = _bus->read(PC++); + uint8_t hi = _bus->read(PC++); + uint16_t address = (hi << 8) | lo; + return {address, 0}; + } + + Cpu::InstructionArgs Cpu::IMP() { + return {0, 0}; + } + + Cpu::InstructionArgs Cpu::REL() { + uint16_t address = _bus->read(PC++); + + if (address & 0x80) { + address |= 0xFF00; + } + + return {address, 0}; + } + + Cpu::InstructionArgs Cpu::ZP0() { + uint16_t address = _bus->read(PC++); + return {address, 0}; + } + // CPU instructions void Cpu::LDA(Cpu::InstructionArgs args) { @@ -126,55 +222,254 @@ namespace nes { } void Cpu::DEY(Cpu::InstructionArgs args) { - Y -= 1; + Y--; setFlag(Negative, Y & 0x80); setFlag(Zero, Y == 0); } void Cpu::BNE(Cpu::InstructionArgs args) { if(!getFlag(Zero)) { - _ticks++; - - uint16_t address = PC + args.address; - uint16_t srcPage = PC & 0xFF00; - uint16_t dstPage = address & 0xFF00; - - if(srcPage != dstPage) { - _ticks++; - } - - PC = address; + branch(args); } } void Cpu::NOP(Cpu::InstructionArgs args) { } - // Addressing modes - - Cpu::InstructionArgs Cpu::IMM() { - return {PC++, 0}; + void Cpu::SEI(Cpu::InstructionArgs args) { + setFlag(InterruptDisable, true); } - Cpu::InstructionArgs Cpu::ABS() { - uint8_t lo = _bus->read(PC++); - uint8_t hi = _bus->read(PC++); - uint16_t address = (hi << 8) | lo; - return {address, 0}; + void Cpu::CLD(Cpu::InstructionArgs args) { + setFlag(DecimalMode, false); } - Cpu::InstructionArgs Cpu::IMP() { - return {0, 0}; + void Cpu::TXS(Cpu::InstructionArgs args) { + SP = X; } - Cpu::InstructionArgs Cpu::REL() { - uint16_t address = _bus->read(PC++); - - if (address & 0x80) { - address |= 0xFF00; + void Cpu::BPL(Cpu::InstructionArgs args) { + if(!getFlag(Negative)) { + branch(args); } + } - return {address, 0}; + void Cpu::JMP(Cpu::InstructionArgs args) { + PC = args.address; + } + + void Cpu::JSR(Cpu::InstructionArgs args) { + std::cout << "+++ >>> Jumping from: " << PC << std::endl; + PC--; + _bus->write(STACK_BASE + SP--, PC >> 8); + _bus->write(STACK_BASE + SP--, PC & 0x00FF); + PC = args.address; + } + + void Cpu::SEC(Cpu::InstructionArgs args) { + setFlag(Carry, true); + } + + void Cpu::BCS(Cpu::InstructionArgs args) { + if(getFlag(Carry)) { + branch(args); + } + } + + void Cpu::BCC(Cpu::InstructionArgs args) { + if(!getFlag(Carry)) { + branch(args); + } + } + + void Cpu::BEQ(Cpu::InstructionArgs args) { + if(getFlag(Zero)) { + branch(args); + } + } + + void Cpu::BIT(Cpu::InstructionArgs args) { + uint8_t value = _bus->read(args.address); + setFlag(Zero, (A & value) == 0); + setFlag(Negative, value & (1 << 7)); + setFlag(Overflow, value & (1 << 6)); + } + + void Cpu::BVS(Cpu::InstructionArgs args) { + if(getFlag(Overflow)) { + branch(args); + } + } + + void Cpu::BVC(Cpu::InstructionArgs args) { + if(!getFlag(Overflow)) { + branch(args); + } + } + + void Cpu::RTS(Cpu::InstructionArgs args) { + uint16_t lo = _bus->read(STACK_BASE + ++SP); + uint16_t hi = _bus->read(STACK_BASE + ++SP); + + PC = (hi << 8) | lo; + PC++; + + std::cout << "+++ <<< return to: " << PC << std::endl; + } + + void Cpu::SED(Cpu::InstructionArgs args) { + setFlag(DecimalMode, true); + } + + void Cpu::PHP(Cpu::InstructionArgs args) { + _bus->write(STACK_BASE + SP--, flags | Break | Unused); + setFlag(Break, false); + setFlag(Unused, false); + } + + void Cpu::PLA(Cpu::InstructionArgs args) { + A = _bus->read(STACK_BASE + ++SP); + setFlag(Zero, A == 0); + setFlag(Negative, A & 0x80); + } + + void Cpu::AND(Cpu::InstructionArgs args) { + A &= _bus->read(args.address);; + setFlag(Zero, A == 0); + setFlag(Negative, A & 0x80); + } + + void Cpu::CMP(Cpu::InstructionArgs args) { + uint16_t value = _bus->read(args.address); + uint16_t diff = A - value; + setFlag(Carry, A > value); + setFlag(Zero, (diff & 0x00FF) == 0); + setFlag(Negative, diff & 0x0080); + } + + void Cpu::BMI(Cpu::InstructionArgs args) { + if(getFlag(Negative)) { + branch(args); + } + } + + void Cpu::PHA(Cpu::InstructionArgs args) { + _bus->write(STACK_BASE + SP--, A); + } + + void Cpu::PLP(Cpu::InstructionArgs args) { + flags = _bus->read(STACK_BASE + ++SP); + setFlag(Unused, true); + } + + void Cpu::ORA(Cpu::InstructionArgs args) { + A |= _bus->read(args.address); + setFlag(Zero, A == 0); + setFlag(Negative, A & 0x80); + } + + void Cpu::CLV(Cpu::InstructionArgs args) { + setFlag(Overflow, false); + } + + void Cpu::EOR(Cpu::InstructionArgs args) { + A ^= _bus->read(args.address); + setFlag(Zero, A == 0); + setFlag(Negative, A & 0x80); + } + + void Cpu::CPY(Cpu::InstructionArgs args) { + uint16_t value = _bus->read(args.address); + uint16_t diff = Y - value; + setFlag(Carry, Y > value); + setFlag(Zero, (diff & 0x00FF) == 0); + setFlag(Negative, diff & 0x0080); + } + + void Cpu::CPX(Cpu::InstructionArgs args) { + uint16_t value = _bus->read(args.address); + uint16_t diff = X - value; + setFlag(Carry, X > value); + setFlag(Zero, (diff & 0x00FF) == 0); + setFlag(Negative, diff & 0x0080); + } + + void Cpu::STY(Cpu::InstructionArgs args) { + _bus->write(args.address, Y); + } + + void Cpu::INY(Cpu::InstructionArgs args) { + Y++; + setFlag(Zero, Y == 0); + setFlag(Negative, Y & 0x80); + } + + void Cpu::INX(Cpu::InstructionArgs args) { + X++; + setFlag(Zero, X == 0); + setFlag(Negative, X & 0x80); + } + + void Cpu::DEX(Cpu::InstructionArgs args) { + X--; + setFlag(Zero, X == 0); + setFlag(Negative, X & 0x80); + } + + void Cpu::TAY(Cpu::InstructionArgs args) { + Y = A; + setFlag(Zero, Y == 0); + setFlag(Negative, Y & 0x80); + } + + void Cpu::TAX(Cpu::InstructionArgs args) { + X = A; + setFlag(Zero, X == 0); + setFlag(Negative, X & 0x80); + } + + void Cpu::TYA(Cpu::InstructionArgs args) { + A = Y; + setFlag(Zero, A == 0); + setFlag(Negative, A & 0x80); + } + + void Cpu::TXA(Cpu::InstructionArgs args) { + A = X; + setFlag(Zero, A == 0); + setFlag(Negative, A & 0x80); + } + + void Cpu::TSX(Cpu::InstructionArgs args) { + X = SP; + setFlag(Zero, X == 0); + setFlag(Negative, X & 0x80); + } + + void Cpu::BRK(Cpu::InstructionArgs args) { + PC++; + setFlag(InterruptDisable, true); + + _bus->write(STACK_BASE + SP--, PC >> 8); + _bus->write(STACK_BASE + SP--, PC & 0x00FF); + + setFlag(Break, true); + _bus->write(STACK_BASE + SP--, flags); + setFlag(Break, false); + + uint16_t lo = _bus->read(0xFFFE); + uint16_t hi = _bus->read(0xFFFF); + PC = (hi << 8) | lo; + } + + void Cpu::RTI(Cpu::InstructionArgs args) { + flags = _bus->read(STACK_BASE + ++SP); + setFlag(Break, false); + setFlag(Unused, false); + + uint16_t lo = _bus->read(STACK_BASE + ++SP); + uint16_t hi = _bus->read(STACK_BASE + ++SP); + PC = (hi << 8) | lo; } } diff --git a/src/Cpu.h b/src/Cpu.h index fa5aa4f..5b5b934 100644 --- a/src/Cpu.h +++ b/src/Cpu.h @@ -37,12 +37,15 @@ namespace nes { bool variableCycles; }; + static constexpr uint16_t STACK_BASE = 0x0100; + public: explicit Cpu(Bus* bus); void reset(); void tick(); void setFlag(CpuFlags flag, bool value); bool getFlag(CpuFlags flag); + void setStartAddress(uint16_t address); private: size_t _ticks; @@ -59,6 +62,16 @@ namespace nes { uint8_t SP; // Stack Pointer uint8_t flags; + private: + void branch(InstructionArgs args); + + private: + InstructionArgs IMM(); + InstructionArgs ABS(); + InstructionArgs IMP(); + InstructionArgs REL(); + InstructionArgs ZP0(); + private: void LDA(InstructionArgs args); void LDX(InstructionArgs args); @@ -71,12 +84,44 @@ namespace nes { void DEY(InstructionArgs args); void BNE(InstructionArgs args); void NOP(InstructionArgs args); - - private: - InstructionArgs IMM(); - InstructionArgs ABS(); - InstructionArgs IMP(); - InstructionArgs REL(); + void SEI(InstructionArgs args); + void CLD(InstructionArgs args); + void TXS(InstructionArgs args); + void BPL(InstructionArgs args); + void JMP(InstructionArgs args); + void JSR(InstructionArgs args); + void SEC(InstructionArgs args); + void BCS(InstructionArgs args); + void BCC(InstructionArgs args); + void BEQ(InstructionArgs args); + void BIT(InstructionArgs args); + void BVS(InstructionArgs args); + void BVC(InstructionArgs args); + void RTS(InstructionArgs args); + void SED(InstructionArgs args); + void PHP(InstructionArgs args); + void PLA(InstructionArgs args); + void AND(InstructionArgs args); + void CMP(InstructionArgs args); + void BMI(InstructionArgs args); + void PHA(InstructionArgs args); + void PLP(InstructionArgs args); + void ORA(InstructionArgs args); + void CLV(InstructionArgs args); + void EOR(InstructionArgs args); + void CPY(InstructionArgs args); + void CPX(InstructionArgs args); + void STY(InstructionArgs args); + void INY(InstructionArgs args); + void INX(InstructionArgs args); + void DEX(InstructionArgs args); + void TAY(InstructionArgs args); + void TAX(InstructionArgs args); + void TYA(InstructionArgs args); + void TXA(InstructionArgs args); + void TSX(InstructionArgs args); + void BRK(InstructionArgs args); + void RTI(InstructionArgs args); }; } diff --git a/src/Mapper/Mapper.cpp b/src/Mapper/Mapper.cpp new file mode 100644 index 0000000..f475194 --- /dev/null +++ b/src/Mapper/Mapper.cpp @@ -0,0 +1,12 @@ +// +// Created by selim on 8/19/23. +// + +#include "Mapper.h" + +namespace nes { + + Mapper::Mapper(uint8_t prgBanks, uint8_t chrBanks): _prgBanks(prgBanks), _chrBanks(chrBanks) { + + } +} \ No newline at end of file diff --git a/src/Mapper/Mapper.h b/src/Mapper/Mapper.h new file mode 100644 index 0000000..7443235 --- /dev/null +++ b/src/Mapper/Mapper.h @@ -0,0 +1,28 @@ +// +// Created by selim on 8/19/23. +// + +#ifndef NES_MAPPER_H +#define NES_MAPPER_H + +#include + +namespace nes { + + class Mapper { + public: + Mapper(uint8_t prgBanks, uint8_t chrBanks); + virtual ~Mapper() = default; + + public: + virtual uint32_t mapPrg(uint16_t address) = 0; + virtual uint32_t mapChr(uint16_t address) = 0; + + protected: + uint8_t _prgBanks; + uint8_t _chrBanks; + }; + +} + +#endif //NES_MAPPER_H diff --git a/src/Mapper/Mapper0.cpp b/src/Mapper/Mapper0.cpp new file mode 100644 index 0000000..09b16f3 --- /dev/null +++ b/src/Mapper/Mapper0.cpp @@ -0,0 +1,16 @@ +// +// Created by selim on 8/19/23. +// + +#include "Mapper0.h" + +namespace nes { + + uint32_t Mapper0::mapPrg(uint16_t address) { + return address & (_prgBanks > 1 ? 0x7FFF : 0x3FFF); + } + + uint32_t Mapper0::mapChr(uint16_t address) { + return address; + } +} \ No newline at end of file diff --git a/src/Mapper/Mapper0.h b/src/Mapper/Mapper0.h new file mode 100644 index 0000000..c9d234f --- /dev/null +++ b/src/Mapper/Mapper0.h @@ -0,0 +1,24 @@ +// +// Created by selim on 8/19/23. +// + +#ifndef NES_MAPPER0_H +#define NES_MAPPER0_H + +#include "Mapper.h" + +namespace nes { + + class Mapper0: public Mapper { + public: + using Mapper::Mapper; + ~Mapper0() override = default; + + public: + uint32_t mapPrg(uint16_t address) override; + uint32_t mapChr(uint16_t address) override; + }; + +} + +#endif //NES_MAPPER0_H diff --git a/src/Nes.cpp b/src/Nes.cpp index 5987b76..de14263 100644 --- a/src/Nes.cpp +++ b/src/Nes.cpp @@ -3,9 +3,11 @@ // #include "Nes.h" -#include "Rom.h" +#include "Cartridge.h" #include "Cpu.h" +#include + namespace nes { Nes::Nes() { @@ -13,10 +15,10 @@ namespace nes { _cpu = std::make_unique(_bus.get()); } - void Nes::runRom(const fs::path &path) { - _rom = std::make_unique(path); - _bus->connect(_rom.get()); - reset(); + void Nes::runRom(const fs::path &path, std::optional address) { + _cartridge = std::make_unique(path); + _bus->connect(_cartridge.get()); + reset(address); } uint8_t Nes::read(uint16_t address) { @@ -27,14 +29,20 @@ namespace nes { _bus->write(address, value); } - void Nes::reset() { + void Nes::reset(std::optional address) { _cpu->reset(); - size_t ticks = 5000; - while (ticks > 0) { - _cpu->tick(); - ticks--; + if(address) { + _cpu->setStartAddress(address.value()); } + + size_t ticks = 0; + while (ticks <= 2000) { + _cpu->tick(); + ticks++; + } + + std::cout << "The end" << std::endl; } } diff --git a/src/Nes.h b/src/Nes.h index 310a36f..b254c8f 100644 --- a/src/Nes.h +++ b/src/Nes.h @@ -6,8 +6,9 @@ #define NES_NES_H #include "Cpu.h" -#include "Rom.h" +#include "Cartridge.h" #include +#include namespace nes { @@ -16,8 +17,8 @@ namespace nes { class Nes { public: Nes(); - void runRom(const fs::path& path); - void reset(); + void runRom(const fs::path& path, std::optional address = std::nullopt); + void reset(std::optional address = std::nullopt); public: // For debug uint8_t read(uint16_t address); @@ -26,7 +27,7 @@ namespace nes { private: std::unique_ptr _bus; std::unique_ptr _cpu; - std::unique_ptr _rom; + std::unique_ptr _cartridge; }; } diff --git a/src/Rom.cpp b/src/Rom.cpp deleted file mode 100644 index 4ad4e42..0000000 --- a/src/Rom.cpp +++ /dev/null @@ -1,31 +0,0 @@ -// -// Created by Selim Mustafaev on 09.08.2023. -// - -#include "Rom.h" -#include -#include -#include - -namespace nes { - - Rom::Rom(const fs::path &path) { - auto romSize = fs::file_size(path); - _romData = std::make_unique(romSize); - - std::ifstream rom(path); - rom.read(reinterpret_cast(_romData.get()), static_cast(romSize)); - - // UB here - // Should be std::start_lifetime_as(_romData); - // when it become available - _header = reinterpret_cast(_romData.get()); - - const size_t prgSize = _header->prgChunks*PRG_CHUNK_SIZE; - const size_t chrSize = _header->chrChunks*CHR_CHUNK_SIZE; - - _prgRom = std::span(_romData.get() + sizeof(RomHeader), prgSize); - _chrRom = std::span(_romData.get() + sizeof(RomHeader) + prgSize, chrSize); - } - -}