Adding mapper0 and new CPU instructions
This commit is contained in:
parent
2e6ebe201a
commit
40e2f45177
@ -3,4 +3,4 @@ project(nes)
|
|||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 23)
|
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)
|
||||||
|
|||||||
30
main.cpp
30
main.cpp
@ -6,21 +6,23 @@ int main() {
|
|||||||
|
|
||||||
nes::Nes device;
|
nes::Nes device;
|
||||||
|
|
||||||
std::stringstream ss;
|
// 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";
|
// 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;
|
// uint16_t nOffset = 0x8000;
|
||||||
while (!ss.eof())
|
// while (!ss.eof())
|
||||||
{
|
// {
|
||||||
std::string b;
|
// std::string b;
|
||||||
ss >> b;
|
// ss >> b;
|
||||||
device.write(nOffset++, (uint8_t)std::stoul(b, nullptr, 16));
|
// 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.runRom("/home/selim/Downloads/nestest.nes", 0xC000);
|
||||||
device.write(0xFFFC, 0x00);
|
|
||||||
device.write(0xFFFD, 0x80);
|
|
||||||
|
|
||||||
device.reset();
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
32
src/Bus.cpp
32
src/Bus.cpp
@ -3,22 +3,42 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
#include "Bus.h"
|
#include "Bus.h"
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
namespace nes {
|
namespace nes {
|
||||||
|
|
||||||
Bus::Bus(): _rom{nullptr} {
|
Bus::Bus(): _cartridge{nullptr} {
|
||||||
_ram = std::make_unique<uint8_t[]>(64*1024);
|
_ram = std::make_unique<uint8_t[]>(2*1024);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t Bus::read(uint16_t address) {
|
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) {
|
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) {
|
void Bus::connect(Cartridge *cartridge) {
|
||||||
_rom = rom;
|
_cartridge = cartridge;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -5,7 +5,7 @@
|
|||||||
#ifndef NES_BUS_H
|
#ifndef NES_BUS_H
|
||||||
#define NES_BUS_H
|
#define NES_BUS_H
|
||||||
|
|
||||||
#include "Rom.h"
|
#include "Cartridge.h"
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
@ -17,11 +17,11 @@ namespace nes {
|
|||||||
Bus();
|
Bus();
|
||||||
uint8_t read(uint16_t address);
|
uint8_t read(uint16_t address);
|
||||||
void write(uint16_t address, uint8_t value);
|
void write(uint16_t address, uint8_t value);
|
||||||
void connect(Rom* rom);
|
void connect(Cartridge* cartridge);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<uint8_t[]> _ram;
|
std::unique_ptr<uint8_t[]> _ram;
|
||||||
Rom* _rom;
|
Cartridge* _cartridge;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
43
src/Cartridge.cpp
Normal file
43
src/Cartridge.cpp
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
//
|
||||||
|
// Created by Selim Mustafaev on 09.08.2023.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "Cartridge.h"
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace nes {
|
||||||
|
|
||||||
|
Cartridge::Cartridge(const fs::path &path) {
|
||||||
|
auto romSize = fs::file_size(path);
|
||||||
|
_romData = std::make_unique<uint8_t[]>(romSize);
|
||||||
|
|
||||||
|
std::ifstream rom(path);
|
||||||
|
rom.read(reinterpret_cast<char*>(_romData.get()), static_cast<std::streamsize>(romSize));
|
||||||
|
|
||||||
|
// UB here
|
||||||
|
// Should be std::start_lifetime_as<RomHeader>(_romData);
|
||||||
|
// when it become available
|
||||||
|
_header = reinterpret_cast<RomHeader*>(_romData.get());
|
||||||
|
|
||||||
|
const size_t prgSize = _header->prgChunks*PRG_CHUNK_SIZE;
|
||||||
|
const size_t chrSize = _header->chrChunks*CHR_CHUNK_SIZE;
|
||||||
|
|
||||||
|
_prgRom = std::span<uint8_t>(_romData.get() + sizeof(RomHeader), prgSize);
|
||||||
|
_chrRom = std::span<uint8_t>(_romData.get() + sizeof(RomHeader) + prgSize, chrSize);
|
||||||
|
|
||||||
|
_mapper = std::make_unique<Mapper0>(_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];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -2,8 +2,10 @@
|
|||||||
// Created by Selim Mustafaev on 09.08.2023.
|
// Created by Selim Mustafaev on 09.08.2023.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef NES_ROM_H
|
#ifndef NES_CARTRIDGE_H
|
||||||
#define NES_ROM_H
|
#define NES_CARTRIDGE_H
|
||||||
|
|
||||||
|
#include "Mapper/Mapper0.h"
|
||||||
|
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
@ -24,17 +26,22 @@ namespace nes {
|
|||||||
uint8_t reserved[10];
|
uint8_t reserved[10];
|
||||||
};
|
};
|
||||||
|
|
||||||
class Rom {
|
class Cartridge {
|
||||||
public:
|
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:
|
private:
|
||||||
std::unique_ptr<std::byte[]> _romData;
|
std::unique_ptr<uint8_t[]> _romData;
|
||||||
|
std::unique_ptr<Mapper> _mapper;
|
||||||
RomHeader* _header;
|
RomHeader* _header;
|
||||||
std::span<std::byte> _prgRom;
|
std::span<uint8_t> _prgRom;
|
||||||
std::span<std::byte> _chrRom;
|
std::span<uint8_t> _chrRom;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif //NES_ROM_H
|
#endif //NES_CARTRIDGE_H
|
||||||
355
src/Cpu.cpp
355
src/Cpu.cpp
@ -9,16 +9,61 @@ namespace nes {
|
|||||||
|
|
||||||
Cpu::Cpu(Bus* bus): _ticks{}, _bus{bus}, A{}, X{}, Y{}, PC{}, SP{}, flags{} {
|
Cpu::Cpu(Bus* bus): _ticks{}, _bus{bus}, A{}, X{}, Y{}, PC{}, SP{}, flags{} {
|
||||||
_instructions = std::vector<Instruction>(256);
|
_instructions = std::vector<Instruction>(256);
|
||||||
|
_instructions[0x00] = {"BRK", &Cpu::BRK, &Cpu::IMP, 7, false};
|
||||||
_instructions[0xA2] = {"LDX", &Cpu::LDX, &Cpu::IMM, 2, 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[0x8E] = {"STX", &Cpu::STX, &Cpu::ABS, 4, false};
|
||||||
_instructions[0xAC] = {"LDY", &Cpu::LDY, &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[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[0x18] = {"CLC", &Cpu::CLC, &Cpu::IMP, 2, false};
|
||||||
_instructions[0x6D] = {"ADC", &Cpu::ADC, &Cpu::ABS, 4, 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[0x88] = {"DEY", &Cpu::DEY, &Cpu::IMP, 2, false};
|
||||||
_instructions[0xD0] = {"BNE", &Cpu::BNE, &Cpu::REL, 2, false};
|
_instructions[0xD0] = {"BNE", &Cpu::BNE, &Cpu::REL, 2, false};
|
||||||
_instructions[0x8D] = {"STA", &Cpu::STA, &Cpu::ABS, 4, false};
|
_instructions[0x8D] = {"STA", &Cpu::STA, &Cpu::ABS, 4, false};
|
||||||
_instructions[0xEA] = {"NOP", &Cpu::NOP, &Cpu::IMP, 2, 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() {
|
void Cpu::reset() {
|
||||||
@ -40,13 +85,14 @@ namespace nes {
|
|||||||
auto instruction = _instructions[opcode];
|
auto instruction = _instructions[opcode];
|
||||||
|
|
||||||
if(instruction.getAddress == nullptr) {
|
if(instruction.getAddress == nullptr) {
|
||||||
|
std::cout << "Unknown instruction: " << (int)opcode << std::endl;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto args = (this->*instruction.getAddress)();
|
auto args = (this->*instruction.getAddress)();
|
||||||
(this->*instruction.process)(args);
|
(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;
|
_ticks = instruction.cycles;
|
||||||
if(instruction.variableCycles) {
|
if(instruction.variableCycles) {
|
||||||
@ -69,6 +115,56 @@ namespace nes {
|
|||||||
return flags & flag;
|
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
|
// CPU instructions
|
||||||
|
|
||||||
void Cpu::LDA(Cpu::InstructionArgs args) {
|
void Cpu::LDA(Cpu::InstructionArgs args) {
|
||||||
@ -126,55 +222,254 @@ namespace nes {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Cpu::DEY(Cpu::InstructionArgs args) {
|
void Cpu::DEY(Cpu::InstructionArgs args) {
|
||||||
Y -= 1;
|
Y--;
|
||||||
setFlag(Negative, Y & 0x80);
|
setFlag(Negative, Y & 0x80);
|
||||||
setFlag(Zero, Y == 0);
|
setFlag(Zero, Y == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cpu::BNE(Cpu::InstructionArgs args) {
|
void Cpu::BNE(Cpu::InstructionArgs args) {
|
||||||
if(!getFlag(Zero)) {
|
if(!getFlag(Zero)) {
|
||||||
_ticks++;
|
branch(args);
|
||||||
|
|
||||||
uint16_t address = PC + args.address;
|
|
||||||
uint16_t srcPage = PC & 0xFF00;
|
|
||||||
uint16_t dstPage = address & 0xFF00;
|
|
||||||
|
|
||||||
if(srcPage != dstPage) {
|
|
||||||
_ticks++;
|
|
||||||
}
|
|
||||||
|
|
||||||
PC = address;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cpu::NOP(Cpu::InstructionArgs args) {
|
void Cpu::NOP(Cpu::InstructionArgs args) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Addressing modes
|
void Cpu::SEI(Cpu::InstructionArgs args) {
|
||||||
|
setFlag(InterruptDisable, true);
|
||||||
Cpu::InstructionArgs Cpu::IMM() {
|
|
||||||
return {PC++, 0};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Cpu::InstructionArgs Cpu::ABS() {
|
void Cpu::CLD(Cpu::InstructionArgs args) {
|
||||||
uint8_t lo = _bus->read(PC++);
|
setFlag(DecimalMode, false);
|
||||||
uint8_t hi = _bus->read(PC++);
|
|
||||||
uint16_t address = (hi << 8) | lo;
|
|
||||||
return {address, 0};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Cpu::InstructionArgs Cpu::IMP() {
|
void Cpu::TXS(Cpu::InstructionArgs args) {
|
||||||
return {0, 0};
|
SP = X;
|
||||||
}
|
}
|
||||||
|
|
||||||
Cpu::InstructionArgs Cpu::REL() {
|
void Cpu::BPL(Cpu::InstructionArgs args) {
|
||||||
uint16_t address = _bus->read(PC++);
|
if(!getFlag(Negative)) {
|
||||||
|
branch(args);
|
||||||
if (address & 0x80) {
|
|
||||||
address |= 0xFF00;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
57
src/Cpu.h
57
src/Cpu.h
@ -37,12 +37,15 @@ namespace nes {
|
|||||||
bool variableCycles;
|
bool variableCycles;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static constexpr uint16_t STACK_BASE = 0x0100;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit Cpu(Bus* bus);
|
explicit Cpu(Bus* bus);
|
||||||
void reset();
|
void reset();
|
||||||
void tick();
|
void tick();
|
||||||
void setFlag(CpuFlags flag, bool value);
|
void setFlag(CpuFlags flag, bool value);
|
||||||
bool getFlag(CpuFlags flag);
|
bool getFlag(CpuFlags flag);
|
||||||
|
void setStartAddress(uint16_t address);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
size_t _ticks;
|
size_t _ticks;
|
||||||
@ -59,6 +62,16 @@ namespace nes {
|
|||||||
uint8_t SP; // Stack Pointer
|
uint8_t SP; // Stack Pointer
|
||||||
uint8_t flags;
|
uint8_t flags;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void branch(InstructionArgs args);
|
||||||
|
|
||||||
|
private:
|
||||||
|
InstructionArgs IMM();
|
||||||
|
InstructionArgs ABS();
|
||||||
|
InstructionArgs IMP();
|
||||||
|
InstructionArgs REL();
|
||||||
|
InstructionArgs ZP0();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void LDA(InstructionArgs args);
|
void LDA(InstructionArgs args);
|
||||||
void LDX(InstructionArgs args);
|
void LDX(InstructionArgs args);
|
||||||
@ -71,12 +84,44 @@ namespace nes {
|
|||||||
void DEY(InstructionArgs args);
|
void DEY(InstructionArgs args);
|
||||||
void BNE(InstructionArgs args);
|
void BNE(InstructionArgs args);
|
||||||
void NOP(InstructionArgs args);
|
void NOP(InstructionArgs args);
|
||||||
|
void SEI(InstructionArgs args);
|
||||||
private:
|
void CLD(InstructionArgs args);
|
||||||
InstructionArgs IMM();
|
void TXS(InstructionArgs args);
|
||||||
InstructionArgs ABS();
|
void BPL(InstructionArgs args);
|
||||||
InstructionArgs IMP();
|
void JMP(InstructionArgs args);
|
||||||
InstructionArgs REL();
|
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);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
12
src/Mapper/Mapper.cpp
Normal file
12
src/Mapper/Mapper.cpp
Normal file
@ -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) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
28
src/Mapper/Mapper.h
Normal file
28
src/Mapper/Mapper.h
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
//
|
||||||
|
// Created by selim on 8/19/23.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef NES_MAPPER_H
|
||||||
|
#define NES_MAPPER_H
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
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
|
||||||
16
src/Mapper/Mapper0.cpp
Normal file
16
src/Mapper/Mapper0.cpp
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
24
src/Mapper/Mapper0.h
Normal file
24
src/Mapper/Mapper0.h
Normal file
@ -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
|
||||||
28
src/Nes.cpp
28
src/Nes.cpp
@ -3,9 +3,11 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
#include "Nes.h"
|
#include "Nes.h"
|
||||||
#include "Rom.h"
|
#include "Cartridge.h"
|
||||||
#include "Cpu.h"
|
#include "Cpu.h"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
namespace nes {
|
namespace nes {
|
||||||
|
|
||||||
Nes::Nes() {
|
Nes::Nes() {
|
||||||
@ -13,10 +15,10 @@ namespace nes {
|
|||||||
_cpu = std::make_unique<Cpu>(_bus.get());
|
_cpu = std::make_unique<Cpu>(_bus.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Nes::runRom(const fs::path &path) {
|
void Nes::runRom(const fs::path &path, std::optional<uint16_t> address) {
|
||||||
_rom = std::make_unique<Rom>(path);
|
_cartridge = std::make_unique<Cartridge>(path);
|
||||||
_bus->connect(_rom.get());
|
_bus->connect(_cartridge.get());
|
||||||
reset();
|
reset(address);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t Nes::read(uint16_t address) {
|
uint8_t Nes::read(uint16_t address) {
|
||||||
@ -27,14 +29,20 @@ namespace nes {
|
|||||||
_bus->write(address, value);
|
_bus->write(address, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Nes::reset() {
|
void Nes::reset(std::optional<uint16_t> address) {
|
||||||
_cpu->reset();
|
_cpu->reset();
|
||||||
|
|
||||||
size_t ticks = 5000;
|
if(address) {
|
||||||
while (ticks > 0) {
|
_cpu->setStartAddress(address.value());
|
||||||
_cpu->tick();
|
|
||||||
ticks--;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t ticks = 0;
|
||||||
|
while (ticks <= 2000) {
|
||||||
|
_cpu->tick();
|
||||||
|
ticks++;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "The end" << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,8 +6,9 @@
|
|||||||
#define NES_NES_H
|
#define NES_NES_H
|
||||||
|
|
||||||
#include "Cpu.h"
|
#include "Cpu.h"
|
||||||
#include "Rom.h"
|
#include "Cartridge.h"
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
namespace nes {
|
namespace nes {
|
||||||
|
|
||||||
@ -16,8 +17,8 @@ namespace nes {
|
|||||||
class Nes {
|
class Nes {
|
||||||
public:
|
public:
|
||||||
Nes();
|
Nes();
|
||||||
void runRom(const fs::path& path);
|
void runRom(const fs::path& path, std::optional<uint16_t> address = std::nullopt);
|
||||||
void reset();
|
void reset(std::optional<uint16_t> address = std::nullopt);
|
||||||
|
|
||||||
public: // For debug
|
public: // For debug
|
||||||
uint8_t read(uint16_t address);
|
uint8_t read(uint16_t address);
|
||||||
@ -26,7 +27,7 @@ namespace nes {
|
|||||||
private:
|
private:
|
||||||
std::unique_ptr<Bus> _bus;
|
std::unique_ptr<Bus> _bus;
|
||||||
std::unique_ptr<Cpu> _cpu;
|
std::unique_ptr<Cpu> _cpu;
|
||||||
std::unique_ptr<Rom> _rom;
|
std::unique_ptr<Cartridge> _cartridge;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
31
src/Rom.cpp
31
src/Rom.cpp
@ -1,31 +0,0 @@
|
|||||||
//
|
|
||||||
// Created by Selim Mustafaev on 09.08.2023.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "Rom.h"
|
|
||||||
#include <fstream>
|
|
||||||
#include <iostream>
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
namespace nes {
|
|
||||||
|
|
||||||
Rom::Rom(const fs::path &path) {
|
|
||||||
auto romSize = fs::file_size(path);
|
|
||||||
_romData = std::make_unique<std::byte[]>(romSize);
|
|
||||||
|
|
||||||
std::ifstream rom(path);
|
|
||||||
rom.read(reinterpret_cast<char*>(_romData.get()), static_cast<std::streamsize>(romSize));
|
|
||||||
|
|
||||||
// UB here
|
|
||||||
// Should be std::start_lifetime_as<RomHeader>(_romData);
|
|
||||||
// when it become available
|
|
||||||
_header = reinterpret_cast<RomHeader*>(_romData.get());
|
|
||||||
|
|
||||||
const size_t prgSize = _header->prgChunks*PRG_CHUNK_SIZE;
|
|
||||||
const size_t chrSize = _header->chrChunks*CHR_CHUNK_SIZE;
|
|
||||||
|
|
||||||
_prgRom = std::span<std::byte>(_romData.get() + sizeof(RomHeader), prgSize);
|
|
||||||
_chrRom = std::span<std::byte>(_romData.get() + sizeof(RomHeader) + prgSize, chrSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
Loading…
Reference in New Issue
Block a user