diff --git a/main.cpp b/main.cpp index 6feb618..b5471c8 100644 --- a/main.cpp +++ b/main.cpp @@ -1,28 +1,28 @@ #include "src/Nes.h" -#include -#include +#include "src/Window.h" + +#include +#include +#include int main() { + using namespace std::placeholders; + nes::Nes device; + nes::SdlWindow window(nes::Ppu::SCREEN_WIDTH, nes::Ppu::SCREEN_HEIGHT); -// 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(); + device.setNewFrameCallback(std::bind(&nes::SdlWindow::drawFrame, &window, _1)); + device.insertCartridge("/home/selim/Downloads/nestest.nes"); - device.runRom("/home/selim/Downloads/nestest.nes", 0xC000); + uint64_t cycles = 0; + while (cycles < 1000000000) { + device.tick(); + cycles++; + + //int64_t us = static_cast(1000000000.0/(60*nes::Ppu::SCREEN_WIDTH*nes::Ppu::SCREEN_HEIGHT)); + //std::this_thread::sleep_for(std::chrono::nanoseconds(1)); + } return 0; } diff --git a/src/Bus.cpp b/src/Bus.cpp index e322807..5abebd9 100644 --- a/src/Bus.cpp +++ b/src/Bus.cpp @@ -17,7 +17,7 @@ namespace nes { return _ram[address & 0x07FF]; } else if(address >= 0x2000 && address < 0x4000) { - std::cout << "PPU read at address: " << address << std::endl; + //std::cout << "PPU read at address: " << address << std::endl; return _ppu->read(address & 0x0007); } else if(address >= 0x8000) { diff --git a/src/Nes.cpp b/src/Nes.cpp index 37ff726..a7ff577 100644 --- a/src/Nes.cpp +++ b/src/Nes.cpp @@ -6,15 +6,16 @@ #include "Cartridge.h" #include "Cpu.h" -#include +#include namespace nes { - Nes::Nes() { + Nes::Nes(): _cycles{} { _cpu = std::make_unique(); + _ppu = std::make_shared(); } - void Nes::runRom(const fs::path &path, std::optional address) { + void Nes::insertCartridge(const fs::path &path, std::optional address) { _cartridge = std::make_shared(path); _cpu->bus()->connect(_cartridge); _cpu->bus()->connect(_ppu); @@ -23,21 +24,21 @@ namespace nes { void Nes::reset(std::optional address) { _cpu->reset(); - if(address) { _cpu->setStartAddress(address.value()); } + } - size_t ticks = 0; - while (ticks <= 15000) { - _ppu->tick(); - if(ticks % 3 == 0) { - _cpu->tick(); - } - ticks++; + void Nes::setNewFrameCallback(std::function onNewFrame) { + _ppu->onNewFrame = std::move(onNewFrame); + } + + void Nes::tick() { + _ppu->tick(); + if(_cycles % 3 == 0) { + _cpu->tick(); } - - std::cout << "The end" << std::endl; + _cycles++; } } diff --git a/src/Nes.h b/src/Nes.h index 8f607f9..066b011 100644 --- a/src/Nes.h +++ b/src/Nes.h @@ -19,10 +19,13 @@ namespace nes { class Nes { public: Nes(); - void runRom(const fs::path& path, std::optional address = std::nullopt); + void insertCartridge(const fs::path& path, std::optional address = std::nullopt); void reset(std::optional address = std::nullopt); + void setNewFrameCallback(std::function onNewFrame); + void tick(); private: + uint64_t _cycles; std::unique_ptr _cpu; std::shared_ptr _ppu; std::shared_ptr _cartridge; diff --git a/src/Ppu.cpp b/src/Ppu.cpp index bd1235c..789ab0e 100644 --- a/src/Ppu.cpp +++ b/src/Ppu.cpp @@ -12,7 +12,12 @@ namespace nes { void Ppu::tick() { - setPixel(_scanline, _column, {}); + if(_column < SCREEN_WIDTH && _scanline < SCREEN_HEIGHT && _scanline >= 0) { + Pixel black = {0, 0, 0, 100}; + Pixel white = {255, 255, 255, 100}; + Pixel pixel = std::rand() % 2 == 0 ? black : white; + setPixel(_scanline, _column, pixel); + } _column++; if(_column >= 341) { diff --git a/src/Window.cpp b/src/Window.cpp index b68117c..b90953d 100644 --- a/src/Window.cpp +++ b/src/Window.cpp @@ -6,4 +6,26 @@ namespace nes { + SdlWindow::SdlWindow(uint16_t width, uint16_t height): _width(width), _height(height) { + int res = SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER); + if(res < 0) throw std::runtime_error("Error initializing SDL"); + + Uint32 flags = SDL_WINDOW_ALLOW_HIGHDPI; //| SDL_WINDOW_VULKAN | SDL_WINDOW_METAL; + _wnd.reset(SDL_CreateWindow("NES", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, width, height, flags)); + if(!_wnd) throw std::runtime_error("Error creating SDL window"); + + _renderer.reset(SDL_CreateRenderer(_wnd.get(), -1, 0)); + if(!_renderer) throw std::runtime_error("Error creating SDL renderer"); + + _texture.reset(SDL_CreateTexture(_renderer.get(), SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STREAMING, width, height)); + if(!_texture) throw std::runtime_error("Error creating SDL texture"); + } + + void SdlWindow::drawFrame(const Pixel *buffer) { + int pitch = static_cast(_width*sizeof(Pixel)); + SDL_UpdateTexture(_texture.get(), nullptr, buffer, pitch); + SDL_RenderClear(_renderer.get()); + SDL_RenderCopy(_renderer.get(), _texture.get(), nullptr, nullptr); + SDL_RenderPresent(_renderer.get()); + } } \ No newline at end of file diff --git a/src/Window.h b/src/Window.h index f784565..a3fbd68 100644 --- a/src/Window.h +++ b/src/Window.h @@ -5,12 +5,36 @@ #ifndef NES_WINDOW_H #define NES_WINDOW_H +#include "Ppu.h" + #include +#include namespace nes { - class Window { + class SdlWindow { + public: + struct SdlDeleter { + void operator()(SDL_Window* ptr) { SDL_DestroyWindow(ptr); } + void operator()(SDL_Renderer* ptr) { SDL_DestroyRenderer(ptr); } + void operator()(SDL_Texture* ptr) { SDL_DestroyTexture(ptr); } + }; + template using SdlUniquePtr = std::unique_ptr; + using SdlWindowPtr = SdlUniquePtr; + using SdlRendererPtr = SdlUniquePtr; + using SdlTexturePtr = SdlUniquePtr; + + public: + SdlWindow(uint16_t width, uint16_t height); + void drawFrame(const Pixel* buffer); + + private: + uint16_t _width; + uint16_t _height; + SdlWindowPtr _wnd; + SdlRendererPtr _renderer; + SdlTexturePtr _texture; }; }