diff --git a/DisplaySDL.cpp b/DisplaySDL.cpp index 9303a6e..4aff847 100644 --- a/DisplaySDL.cpp +++ b/DisplaySDL.cpp @@ -1,8 +1,10 @@ #include "DisplaySDL.hpp" +#include "Peripherals.hpp" #include -DisplaySDL::DisplaySDL(int w, int h, uint32_t fgColor, uint32_t bgColor) { +DisplaySDL::DisplaySDL(int w, int h, uint32_t fgColor, uint32_t bgColor): + mpFramebuffer{std::make_unique()} { // Create SDL Window mpWindow = SDL_CreateWindow( "ChocoChip-8", @@ -35,43 +37,48 @@ DisplaySDL::~DisplaySDL() { SDL_DestroyWindow(mpWindow); } -void DisplaySDL::updateWindow() const { +void DisplaySDL::clear() { + for(auto &scanline : *mpFramebuffer) { + scanline.reset(); + } + SDL_FillRect(SDL_GetWindowSurface(mpWindow), nullptr, mBgColor); +} + +int DisplaySDL::blit(const chocochip8::Scanline &spriteScanline, int y) { + using chocochip8::Scanline; + Scanline &targetScanline = mpFramebuffer->at(y); + Scanline bitsToSet = spriteScanline & ~targetScanline; + Scanline bitsToReset = spriteScanline & targetScanline; + targetScanline ^= spriteScanline; + SDL_Surface *pSurface = SDL_GetWindowSurface(mpWindow); - SDL_LockSurface(pSurface); + SDL_Rect rect; + // Map framebuffer pixel to SDL surface rectangle + auto mapChipxelToPixel = [&rect, pSurface](int x, int y) { + // Note that the MSB of an scanline's bitset is the leftmost pixel. + x = (chocochip8::gcWidth - 1) - x; + int x1 = ( x * pSurface->w) / chocochip8::gcWidth; + int x2 = ((x + 1) * pSurface->w) / chocochip8::gcWidth; + int y1 = ( y * pSurface->h) / chocochip8::gcHeight; + int y2 = ((y + 1) * pSurface->h) / chocochip8::gcHeight; + rect.x = x1; + rect.y = y1; + rect.w = x2 - x1; + rect.h = y2 - y1; + }; - int fx, fy; // ChocoChip8 Framebuffer coordinates - int sx, sy; // SDL Surface coordinates - int lx, ly; // Last-used ChocoChip8 Framebuffer coordinates - Uint32 lc; // Last-used SDL color - - // Fill the entire SDL surface, one pixel at a time - lx = -1; - ly = -1; - for(sy = 0; sy < pSurface->h; sy++) { - for(sx = 0; sx < pSurface->w; sx++) { - // Map SDL surface coordinates to Chocochip8 Framebuffer coordinates - fx = sx * (double(chocochip8::gcWidth) / pSurface->w); - fy = sy * (double(chocochip8::gcHeight) / pSurface->h); - - // Reuse color if this screen pixel maps to the same ChocoChip8 pixel as the last - if(fx != lx || fy != ly) { - lx = fx; - ly = ly; - // Read Chocochip8 Framebuffer and choose appropriate color, - // note that the MSB of an scanline's bitset is the leftmost pixel. - lc = (mpFramebuffer->at(fy)[(chocochip8::gcWidth - 1) - fx] ? mFgColor : mBgColor); - } - - // Convert (x, y) indexes into SDL Surface pixel array index - Uint32 *pPixel = static_cast(static_cast( - static_cast(pSurface->pixels) - + sy * pSurface->pitch - + sx * pSurface->format->BytesPerPixel - )); - *pPixel = lc; - } + for(int x = bitsToReset._Find_first(); x < bitsToReset.size(); x = bitsToReset._Find_next(x)) { + mapChipxelToPixel(x, y); + SDL_FillRect(pSurface, &rect, mBgColor); + } + for(int x = bitsToSet._Find_first(); x < bitsToSet.size(); x = bitsToSet._Find_next(x)) { + mapChipxelToPixel(x, y); + SDL_FillRect(pSurface, &rect, mFgColor); } - SDL_UnlockSurface(pSurface); + return bitsToReset.any(); +} + +void DisplaySDL::updateWindow() const { SDL_UpdateWindowSurface(mpWindow); } diff --git a/DisplaySDL.hpp b/DisplaySDL.hpp index 6fd87e9..ad47384 100644 --- a/DisplaySDL.hpp +++ b/DisplaySDL.hpp @@ -2,16 +2,22 @@ #include "Peripherals.hpp" +#include #include +#include #include class DisplaySDL : public chocochip8::Display { public: DisplaySDL(int w, int h, uint32_t fgCol = 0xffffff, uint32_t bgCol = 0x000000); ~DisplaySDL() override; + void clear() override; + int blit(const chocochip8::Scanline &scanline, int y) override; void updateWindow() const; private: + using Framebuffer = std::array; + std::unique_ptr mpFramebuffer; SDL_Window *mpWindow; Uint32 mFgColor; Uint32 mBgColor; diff --git a/Interpreter.cpp b/Interpreter.cpp index 213a162..2eaf168 100644 --- a/Interpreter.cpp +++ b/Interpreter.cpp @@ -87,9 +87,7 @@ void Interpreter::tick() { case 0x0000: // 0NNN - call machine language routine switch(inst) { case 0x00E0: // clear display - for(auto &scanline : *mrDisplay.mpFramebuffer) { - scanline.reset(); - } + mrDisplay.clear(); break; case 0x00EE: // return from subroutine mvSpecialReg[SR_PC] = mCallStack.top(); @@ -292,12 +290,10 @@ void Interpreter::executeDraw(uint8_t x, uint8_t y, uint8_t n) { // we draw one scanline per sprite row in high-res mode, but twice in low-res mode for(int j = 0; j < (mIsHighResMode ? 1 : 2); j++) { - Scanline &targetScanline = mrDisplay.mpFramebuffer->at(y + i * (mIsHighResMode ? 1 : 2) + j); - if((targetScanline & spriteScanline) != 0) { + // blit the sprite bitset into the screen + if(mrDisplay.blit(spriteScanline, y + i * (mIsHighResMode ? 1 : 2) + j) != 0) { collisionCount += 1; } - // blit the sprite bitset into the screen - targetScanline ^= spriteScanline; } // update state flags diff --git a/Peripherals.hpp b/Peripherals.hpp index 84b5ecb..94c796a 100644 --- a/Peripherals.hpp +++ b/Peripherals.hpp @@ -1,15 +1,12 @@ #pragma once -#include #include -#include namespace chocochip8 { constexpr size_t gcWidth = 128; constexpr size_t gcHeight = 64; using Scanline = std::bitset; - using Framebuffer = std::array; enum class Key { KEY_0, KEY_1, KEY_2, KEY_3, @@ -20,11 +17,9 @@ namespace chocochip8 { class Display { public: - friend class Interpreter; - Display(): mpFramebuffer{std::make_unique()} {} virtual ~Display() = default; - protected: - std::unique_ptr mpFramebuffer; + virtual int blit(const Scanline& spriteScanline, int y) = 0; + virtual void clear() = 0; }; class Buzzer { diff --git a/main.cpp b/main.cpp index 5e6b5d0..684a9df 100644 --- a/main.cpp +++ b/main.cpp @@ -9,8 +9,8 @@ #include "DisplaySDL.hpp" #include "Interpreter.hpp" #include "Peripherals.hpp" -#include "SDL_events.h" -#include "SDL_timer.h" +#include "SDL2/SDL_events.h" +#include "SDL2/SDL_timer.h" int main(int argc, char* argv[]) { if (SDL_Init(SDL_INIT_EVERYTHING) < 0) { @@ -25,7 +25,7 @@ int main(int argc, char* argv[]) { BuzzerSDL buzzer(440); DisplaySDL display(1280, 640); TestKeypad keypad; - chocochip8::Interpreter chip8(90, display, buzzer, keypad); + chocochip8::Interpreter chip8(1000, display, buzzer, keypad); auto rom = std::vector(); auto romfile = std::ifstream(argv[1] != NULL ? argv[1] : "/dev/stdin", std::ios_base::in | std::ios_base::binary); @@ -38,6 +38,8 @@ int main(int argc, char* argv[]) { SDL_Event event; bool done = false; + Uint64 start, end; + start = SDL_GetTicks64(); while(!done) { while(SDL_PollEvent(&event)) { if(event.type == SDL_QUIT) { @@ -46,7 +48,11 @@ int main(int argc, char* argv[]) { } chip8.tick(); display.updateWindow(); - SDL_Delay(1000.0 / 60); + SDL_Delay(1); + end = SDL_GetTicks64(); + if(end - start > 3) + std::cout << "Frame took too long: " << end - start << "\n"; + start = end; } buzzer.off();