diff --git a/CMakeLists.txt b/CMakeLists.txt index b0b5ee0..39ff7f8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,5 +5,5 @@ SET(CMAKE_BUILD_TYPE Debug) add_subdirectory(submodules/sdl2) SET(CMAKE_EXPORT_COMPILE_COMMANDS ON) -add_executable(chocochip8 main.cpp Interpreter.cpp DisplaySDL.cpp BuzzerSDL.cpp) +add_executable(chocochip8 main.cpp Interpreter.cpp DisplaySDL.cpp BuzzerSDL.cpp CountdownTimerSDL.cpp) target_link_libraries(chocochip8 SDL2::SDL2-static) diff --git a/CountdownTimerSDL.cpp b/CountdownTimerSDL.cpp new file mode 100644 index 0000000..d31d984 --- /dev/null +++ b/CountdownTimerSDL.cpp @@ -0,0 +1,18 @@ +#include "CountdownTimerSDL.hpp" + +CountdownTimerSDL::CountdownTimerSDL(unsigned frequency): + mDesiredFrequency{frequency}, + mSDLFrequency{SDL_GetPerformanceFrequency()}, + mStartTime{0}, + mStartValue{0} {} + +void CountdownTimerSDL::set(unsigned value) { + mStartTime = SDL_GetPerformanceCounter(); + mStartValue = value; +} + +unsigned CountdownTimerSDL::get() const { + Uint64 elapsedTime = SDL_GetPerformanceCounter() - mStartTime; + Uint64 elapsedTicks = (elapsedTime * mDesiredFrequency) / mSDLFrequency; + return elapsedTicks >= mStartValue ? 0 : mStartValue - elapsedTicks; +} diff --git a/CountdownTimerSDL.hpp b/CountdownTimerSDL.hpp new file mode 100644 index 0000000..8818d70 --- /dev/null +++ b/CountdownTimerSDL.hpp @@ -0,0 +1,17 @@ +#pragma once + +#include + +#include "Peripherals.hpp" + +class CountdownTimerSDL : public chocochip8::CountdownTimer { +public: + CountdownTimerSDL(unsigned frequency); + void set(unsigned value) override; + unsigned get() const override; +private: + const Uint64 mDesiredFrequency; + const Uint64 mSDLFrequency; + Uint64 mStartTime; + Uint64 mStartValue; +}; diff --git a/Interpreter.cpp b/Interpreter.cpp index 2eaf168..248ccdd 100644 --- a/Interpreter.cpp +++ b/Interpreter.cpp @@ -44,13 +44,14 @@ constexpr uint8_t gcvLowResFontData[80] = { 0xF0, 0x80, 0xF0, 0x80, 0x80 }; -Interpreter::Interpreter(unsigned ticksPerSecond, Display &display, Buzzer &buzzer, Keypad &keypad): +Interpreter::Interpreter(Display &display, Buzzer &buzzer, Keypad &keypad, CountdownTimer &delayTimer, CountdownTimer &soundTimer): mvMemory(scMemorySize), mCallStack{}, mrDisplay{display}, mrBuzzer{buzzer}, mrKeypad{keypad}, - mcTicksPerSecond{ticksPerSecond}, + mrDelayTimer(delayTimer), + mrSoundTimer(soundTimer), mvSpecialReg{}, mvReg{}, mIsHighResMode{false} { @@ -143,16 +144,16 @@ void Interpreter::tick() { case 0xF000: // several unique instructions switch(inst & 0xF0FF) { case 0xF007: // FX07 - read timer register - mvReg[iRegDst] = (mvSpecialReg[SR_T1] * scTimerFreq + (mcTicksPerSecond - 1)) / mcTicksPerSecond; + mvReg[iRegDst] = mrDelayTimer.get(); break; case 0xF015: // FX15 - set timer register - mvSpecialReg[SR_T1] = (mcTicksPerSecond * mvReg[iRegDst]) / scTimerFreq; + mrDelayTimer.set(mvReg[iRegDst]); break; case 0xF018: // FX18 - set sound timer register - if(mvSpecialReg[SR_T2] == 0 && mvReg[iRegDst] != 0) { + if(mvReg[iRegDst] != 0) { mrBuzzer.on(); } - mvSpecialReg[SR_T2] = (mcTicksPerSecond * mvReg[iRegDst]) / scTimerFreq; + mrSoundTimer.set(mvReg[iRegDst]); break; case 0xF01E: // FX1E - add to I mvSpecialReg[SR_I] += mvReg[iRegDst]; @@ -182,15 +183,8 @@ void Interpreter::tick() { break; } - // decrement timers - if(mvSpecialReg[SR_T1] > 0) { - mvSpecialReg[SR_T1] -= 1; - } - if(mvSpecialReg[SR_T2] > 0) { - mvSpecialReg[SR_T2] -= 1; - if(mvSpecialReg[SR_T2] == 0) { - mrBuzzer.off(); - } + if(mrSoundTimer.get() == 0) { + mrBuzzer.off(); } } diff --git a/Interpreter.hpp b/Interpreter.hpp index 362da0c..98a2f2d 100644 --- a/Interpreter.hpp +++ b/Interpreter.hpp @@ -16,7 +16,6 @@ private: public: constexpr static sreg_t scLowRestFontAddr = 0x0000; constexpr static sreg_t scResetVector = 0x0200; - constexpr static sreg_t scTimerFreq = 60; constexpr static size_t scMemorySize = 4096; enum { @@ -26,7 +25,7 @@ public: }; enum { - SR_PC, SR_I, SR_T1, SR_T2, + SR_PC, SR_I, /*SR_T1, SR_T2,*/ SR_COUNT }; @@ -35,7 +34,7 @@ public: }; public: - Interpreter(unsigned ticksPerSecond, Display &display, Buzzer &buzzer, Keypad &keypad); + Interpreter(Display &display, Buzzer &buzzer, Keypad &keypad, CountdownTimer &delayTimer, CountdownTimer &soundTimer); void tick(); void loadProgram(char const* data, size_t count, size_t where = scResetVector); @@ -49,7 +48,8 @@ private: Display &mrDisplay; Buzzer &mrBuzzer; Keypad &mrKeypad; - const unsigned mcTicksPerSecond; + CountdownTimer &mrDelayTimer; + CountdownTimer &mrSoundTimer; sreg_t mvSpecialReg[SR_COUNT]; reg_t mvReg[R_COUNT]; bool mIsHighResMode; diff --git a/Peripherals.hpp b/Peripherals.hpp index 94c796a..657e1d6 100644 --- a/Peripherals.hpp +++ b/Peripherals.hpp @@ -35,4 +35,11 @@ namespace chocochip8 { virtual bool isKeyPressed(Key key) = 0; }; + class CountdownTimer { + public: + virtual ~CountdownTimer() = default; + virtual void set(unsigned value) = 0; + virtual unsigned get() const = 0; + }; + }; // namespace chocochip8 diff --git a/main.cpp b/main.cpp index 684a9df..760fc9c 100644 --- a/main.cpp +++ b/main.cpp @@ -6,11 +6,11 @@ #include #include "BuzzerSDL.hpp" +#include "CountdownTimerSDL.hpp" #include "DisplaySDL.hpp" #include "Interpreter.hpp" #include "Peripherals.hpp" #include "SDL2/SDL_events.h" -#include "SDL2/SDL_timer.h" int main(int argc, char* argv[]) { if (SDL_Init(SDL_INIT_EVERYTHING) < 0) { @@ -23,9 +23,13 @@ int main(int argc, char* argv[]) { }; BuzzerSDL buzzer(440); - DisplaySDL display(1280, 640); + int N = 4; + DisplaySDL display(128*N, 64*N); TestKeypad keypad; - chocochip8::Interpreter chip8(1000, display, buzzer, keypad); + CountdownTimerSDL delayTimer(60); + CountdownTimerSDL soundTimer(60); + CountdownTimerSDL displayTimer(20); + chocochip8::Interpreter chip8(display, buzzer, keypad, delayTimer, soundTimer); auto rom = std::vector(); auto romfile = std::ifstream(argv[1] != NULL ? argv[1] : "/dev/stdin", std::ios_base::in | std::ios_base::binary); @@ -38,8 +42,6 @@ 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) { @@ -47,16 +49,13 @@ int main(int argc, char* argv[]) { } } chip8.tick(); - display.updateWindow(); - SDL_Delay(1); - end = SDL_GetTicks64(); - if(end - start > 3) - std::cout << "Frame took too long: " << end - start << "\n"; - start = end; + if(displayTimer.get() == 0) { + display.updateWindow(); + displayTimer.set(1); + } } buzzer.off(); SDL_Quit(); return 0; } -