nes/src/System.cpp

140 lines
3.4 KiB
C++

//
// Created by Selim Mustafaev on 11.08.2023.
//
#include "System.h"
#include "Cartridge.h"
#include "Cpu.h"
#include "Ppu.h"
#include "Controller.h"
#include <utility>
#include <iostream>
namespace nes {
System::System():
_cycles{}
#ifdef NES_LOGGING
,_logger(500*1024*1024)
#endif
{
_ram = std::make_unique<uint8_t[]>(2*1024);
_cpu = std::make_unique<Cpu>(this);
_ppu = std::make_unique<Ppu>();
_dma = std::make_unique<Dma>(this, _ppu.get());
}
void System::insertCartridge(const fs::path &path, std::optional<uint16_t> address) {
_cartridge = std::make_unique<Cartridge>(path);
_ppu->connect(_cartridge.get());
reset(address);
}
void System::connect(std::shared_ptr<Controller> controller) {
_controller1 = std::move(controller);
}
void System::reset(std::optional<uint16_t> address) {
_cpu->reset();
_ppu->reset();
if(address) {
_cpu->setStartAddress(address.value());
}
}
void System::setNewFrameCallback(std::function<void(const Pixel *)> onNewFrame) {
_ppu->onNewFrame = std::move(onNewFrame);
}
void System::tick() {
bool needInterrupt = _ppu->tick();
#ifdef NES_LOGGING
_logger.addLine(_cycles, _ppu->state());
#endif
if(_cycles % 3 == 0) {
if(_dma->active()) {
_dma->tick(_cycles);
} else {
bool instructionExecuted = _cpu->tick();
#ifdef NES_LOGGING
if(instructionExecuted) {
_logger.addLine(_cycles, _cpu->state());
}
#endif
}
}
if(needInterrupt) {
_cpu->nmi();
#ifdef NES_LOGGING
_logger.addLine(_cycles, "NMI");
_logger.addLine(_cycles, _cpu->state());
#endif
}
#ifdef NES_LOGGING
if(_cycles == 3000000) {
_logger.dump("/home/selim/Documents/log.txt");
}
#endif
_cycles++;
}
uint8_t System::read(uint16_t address) {
if(address < 0x2000) {
return _ram[address & 0x07FF];
}
else if(address >= 0x2000 && address < 0x4000) {
return _ppu->read(address & 0x0007);
}
else if(address >= 0x8000) {
return _cartridge->readPrg(address);
}
else if(address == 0x4016) {
return _controller1->read();
}
else if(address == 0x4017) {
// Controller 2
return 0;
}
return 0;
}
void System::write(uint16_t address, uint8_t value) {
if(address < 0x2000) {
_ram[address & 0x07FF] = value;
}
else if(address >= 0x2000 && address < 0x4000) {
_ppu->write(address & 0x0007, value);
}
else if(address >= 0x8000) {
std::cout << "Cartridge write at address: " << address << std::endl;
}
else if(address == 0x4014) {
_dma->start(value);
}
else if(address == 0x4016) {
_controller1->poll();
}
else if(address == 0x4017) {
// Controller 2
}
}
// For debug
// Calc "hash" - just sum of the bytes of RAM
// Can be useful for detecting changes in memory
uint32_t System::zpHash() const {
uint32_t sum = 0;
for(size_t i = 0; i < 2048; ++i) {
sum += _ram[i];
}
return sum;
}
}