From beb9184806841bfbe5e4f5e898e1ee18fa7d2ef3 Mon Sep 17 00:00:00 2001 From: JustDoom <61824552+JustDoom@users.noreply.github.com> Date: Sat, 6 Apr 2024 18:06:01 +1000 Subject: [PATCH 01/28] Basic SDL Port without menu --- .gitmodules | 3 + assets/icon.bmp | Bin 0 -> 17194 bytes dependencies/CMakeLists.txt | 3 +- dependencies/sdl | 1 + src/CMakeLists.txt | 3 +- src/emulator/Keyboard.h | 18 -- src/main.cpp | 8 + src/sdl/CMakeLists.txt | 5 + src/sdl/emulator/CMakeLists.txt | 7 + .../emulator/SdlKeyboard.cpp} | 7 +- src/sdl/emulator/SdlKeyboard.h | 32 ++++ src/sdl/emulator/SdlRenderer.cpp | 17 ++ src/sdl/emulator/SdlRenderer.h | 19 ++ src/sdl/emulator/SdlSpeaker.cpp | 13 ++ src/sdl/emulator/SdlSpeaker.h | 15 ++ src/sdl/sdl.cpp | 145 ++++++++++++++++ src/sdl/sdl.h | 12 ++ src/sfml/CMakeLists.txt | 8 - src/sfml/Emulator.cpp | 75 -------- src/sfml/Emulator.h | 14 -- src/sfml/MainMenu.cpp | 162 ------------------ src/sfml/MainMenu.h | 24 --- src/sfml/emulator/CMakeLists.txt | 7 - src/sfml/emulator/SfmlKeyboard.h | 12 -- src/sfml/emulator/SfmlRenderer.cpp | 19 -- src/sfml/emulator/SfmlRenderer.h | 20 --- src/sfml/emulator/SfmlSpeaker.cpp | 27 --- src/sfml/emulator/SfmlSpeaker.h | 18 -- src/sfml/ui/CMakeLists.txt | 3 - src/sfml/ui/TextButton.cpp | 66 ------- src/sfml/ui/TextButton.h | 44 ----- 31 files changed, 285 insertions(+), 522 deletions(-) create mode 100644 assets/icon.bmp create mode 160000 dependencies/sdl create mode 100644 src/sdl/CMakeLists.txt create mode 100644 src/sdl/emulator/CMakeLists.txt rename src/{sfml/emulator/SfmlKeyboard.cpp => sdl/emulator/SdlKeyboard.cpp} (75%) create mode 100644 src/sdl/emulator/SdlKeyboard.h create mode 100644 src/sdl/emulator/SdlRenderer.cpp create mode 100644 src/sdl/emulator/SdlRenderer.h create mode 100644 src/sdl/emulator/SdlSpeaker.cpp create mode 100644 src/sdl/emulator/SdlSpeaker.h create mode 100644 src/sdl/sdl.cpp create mode 100644 src/sdl/sdl.h delete mode 100644 src/sfml/CMakeLists.txt delete mode 100644 src/sfml/Emulator.cpp delete mode 100644 src/sfml/Emulator.h delete mode 100644 src/sfml/MainMenu.cpp delete mode 100644 src/sfml/MainMenu.h delete mode 100644 src/sfml/emulator/CMakeLists.txt delete mode 100644 src/sfml/emulator/SfmlKeyboard.h delete mode 100644 src/sfml/emulator/SfmlRenderer.cpp delete mode 100644 src/sfml/emulator/SfmlRenderer.h delete mode 100644 src/sfml/emulator/SfmlSpeaker.cpp delete mode 100644 src/sfml/emulator/SfmlSpeaker.h delete mode 100644 src/sfml/ui/CMakeLists.txt delete mode 100644 src/sfml/ui/TextButton.cpp delete mode 100644 src/sfml/ui/TextButton.h diff --git a/.gitmodules b/.gitmodules index 506b79d..82f5761 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,3 +7,6 @@ [submodule "dependencies/libconfig"] path = dependencies/libconfig url = git@github.com:hyperrealm/libconfig.git +[submodule "dependencies/sdl"] + path = dependencies/sdl + url = git@github.com:libsdl-org/SDL.git diff --git a/assets/icon.bmp b/assets/icon.bmp new file mode 100644 index 0000000000000000000000000000000000000000..c9a47e237dd53a87e19da1a07b9d66dbbeeb890a GIT binary patch literal 17194 zcmeI4Pl#1@6vxkumMKaL4I)GannWU9I2E-BrpXXNVKM_P7tJ3>4QCu?M%uV6gfT4tSpp0ICbLsH@6SG^wqwH_Pzc4 z$i&3N?uCVg{pq=XeSLlVb31NrIdJy+;k(Uk*qZ*oI%#%uRI9^ycmu;57~a6}28K5< zyn%k-z*BErX_Y3$>gF{DIER@pOlC zW1idH51+XiXhj2v&noCWInCM&2fys?hT89OPJh!Hej~=+{mAtWy7}fT?riEc_T!m- zaovrogZWM(QE8F;uH7ul1G1WMm*;Za`o6t-m7Q&xbNs6E$nf0j^<@? z^ijs=JoXLpZy@@24Z6RIrW`-c;XXm;7NO1@lz+7NE}p7-RFi6r{mf+-7vy^aPvcwWS>!!4RYJr6b=!Q9*; zU0SD4YgOZ1FE#bkZ618()bS11=P;}4ZrxcYW)G;n)S76(QyuiZ{n)Kuah;`pmuZVV3gT>fXq!>Z^#?>s{3KtbFyYar9P}*Y(6M^{jE(K0Q}Iy~JpVs5`2} z+yQgckH>Ka{K(63V=s9xeq#@MBdRX;RmGgQTrWFy*S+@d_kj*YH05+tQ=inpZf9^D z*~vaoIk`B#$0j)%BBI67&r}CoIC@qm_BqT@ESu59Z>|?VimJzBv__6=<&2&({hY@> z`W@j!%;|4hQ%}UIr6@*Q^g$ieb83!Sk2{p7aZ|5F{rCo>o?MQu=z}`Qx#!H`tPU1w z?^hk~AX*jC3&(T(!GqIJ?3c%~Irxf*H|peg)H|S7hTc=g@$)jx{XWNI4)G1TKK1VP zGEdB9Mz@}Rd#gGRjSe5s<|k39$vvBuGa_U zsqWn9wObzj;MwcMcz29^^12%D1;3l`c!m!=SM`4{d^Ft=y;jY+lN>hHv>E+Xedl=G z!Di?l)XtpGSR2&t;oRX33~yj~1H&5_-a!5aMsHr4TU^~bX|}kuwsP|5)Y$1Wua0f| zEq#VyuZ`Mvdv<1Sb@}<}>Ero+um1g`jX3^r&}Vs6@I+?z(FuEtz$IeSU zEAiZgmBqw)GI7S|7njof(bJuH-ednP!}#K*D@j}X=MHzCTYBZ?gyZSiYe(&vov`Wj z$%a|GVi)a_t=YkZOR2JIC)4>YU3Z7 zJ@}T{7vFBwX5Tma@|xMhKQGU$%(PeghbTrD7R>(q%xu>gv)$jD?Rc$oZ|Tg>?bBOk gr_x;=|ITdvL$m$o)9kYaz8^>p|J_?x&fWX_7r81K0{{R3 literal 0 HcmV?d00001 diff --git a/dependencies/CMakeLists.txt b/dependencies/CMakeLists.txt index 13e3af2..ec3afa5 100644 --- a/dependencies/CMakeLists.txt +++ b/dependencies/CMakeLists.txt @@ -5,4 +5,5 @@ endif() add_subdirectory(sfml) add_subdirectory(nativefiledialog) -add_subdirectory(libconfig) \ No newline at end of file +add_subdirectory(libconfig) +add_subdirectory(sdl) \ No newline at end of file diff --git a/dependencies/sdl b/dependencies/sdl new file mode 160000 index 0000000..2f4a7bb --- /dev/null +++ b/dependencies/sdl @@ -0,0 +1 @@ +Subproject commit 2f4a7bbcedaaf49c8bf87e34081120a6a49a0d26 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e9102f5..90fdc6c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,5 +1,6 @@ add_executable(8ChocChip) +add_subdirectory(sdl) add_subdirectory(sfml) add_subdirectory(util) add_subdirectory(emulator) @@ -12,7 +13,7 @@ else() set(libname "config") endif() -target_link_libraries(8ChocChip PRIVATE sfml-graphics sfml-audio sfml-window sfml-system nativefiledialog ${libname}++) +target_link_libraries(8ChocChip PRIVATE sfml-graphics sfml-audio sfml-window sfml-system nativefiledialog ${libname}++ SDL2) # Copy DLLs needed for runtime on Windows if(WIN32) diff --git a/src/emulator/Keyboard.h b/src/emulator/Keyboard.h index 675d569..239ac8a 100644 --- a/src/emulator/Keyboard.h +++ b/src/emulator/Keyboard.h @@ -10,24 +10,6 @@ class Keyboard { public: std::unordered_map keysPressed; std::function onNextKeyPress; - std::unordered_map KEYMAP = { - {27, 0x1}, // 1 - {28, 0x2}, // 2 - {29, 0x3}, // 3 - {30, 0xc}, // 4 - {16, 0x4}, // Q - {22, 0x5}, // W - {4, 0x6}, // E - {17, 0xD}, // R - {0, 0x7}, // A - {18, 0x8}, // S - {3, 0x9}, // D - {5, 0xE}, // F - {25, 0xA}, // Z - {23, 0x0}, // X - {2, 0xB}, // C - {21, 0xF} // V - }; void setOnNextKeyPress(std::function callback); virtual void handleKeyDown(uint8_t keyCode) = 0; diff --git a/src/main.cpp b/src/main.cpp index 0aee35f..69bc725 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -13,7 +13,15 @@ #include "libconfig.h" #include "libconfig.hh" +#include "sdl/sdl.h" + int main(int argc, char **argv) { + + sdl sdl; + sdl.launch("/home/doom/Documents/8ChipRoms/BLINKY"); + + return 5; + bool quickRom = false; std::string rom; diff --git a/src/sdl/CMakeLists.txt b/src/sdl/CMakeLists.txt new file mode 100644 index 0000000..b7a3c5c --- /dev/null +++ b/src/sdl/CMakeLists.txt @@ -0,0 +1,5 @@ +target_sources(8ChocChip PRIVATE + sdl.cpp + sdl.h) + +add_subdirectory(emulator) \ No newline at end of file diff --git a/src/sdl/emulator/CMakeLists.txt b/src/sdl/emulator/CMakeLists.txt new file mode 100644 index 0000000..161b3f7 --- /dev/null +++ b/src/sdl/emulator/CMakeLists.txt @@ -0,0 +1,7 @@ +target_sources(8ChocChip PRIVATE + SdlSpeaker.cpp + SdlSpeaker.h + SdlRenderer.cpp + SdlRenderer.h + SdlKeyboard.cpp + SdlKeyboard.h) \ No newline at end of file diff --git a/src/sfml/emulator/SfmlKeyboard.cpp b/src/sdl/emulator/SdlKeyboard.cpp similarity index 75% rename from src/sfml/emulator/SfmlKeyboard.cpp rename to src/sdl/emulator/SdlKeyboard.cpp index f4a4322..67abe06 100644 --- a/src/sfml/emulator/SfmlKeyboard.cpp +++ b/src/sdl/emulator/SdlKeyboard.cpp @@ -1,6 +1,7 @@ -#include "SfmlKeyboard.h" +#include "SdlKeyboard.h" -void SfmlKeyboard::handleKeyDown(uint8_t keyCode) { +void SdlKeyboard::handleKeyDown(uint8_t keyCode) { + std::cout << +keyCode << std::endl; auto keyMapIter = this->KEYMAP.find(keyCode); if (keyMapIter != this->KEYMAP.end()) { uint8_t key = keyMapIter->second; @@ -12,7 +13,7 @@ void SfmlKeyboard::handleKeyDown(uint8_t keyCode) { } } -void SfmlKeyboard::handleKeyUp(uint8_t keyCode) { +void SdlKeyboard::handleKeyUp(uint8_t keyCode) { auto keyMapIter = this->KEYMAP.find(keyCode); if (keyMapIter != this->KEYMAP.end()) { uint8_t key = keyMapIter->second; diff --git a/src/sdl/emulator/SdlKeyboard.h b/src/sdl/emulator/SdlKeyboard.h new file mode 100644 index 0000000..7950b17 --- /dev/null +++ b/src/sdl/emulator/SdlKeyboard.h @@ -0,0 +1,32 @@ +#ifndef INC_8CHOCCHIP_SDLKEYBOARD_H +#define INC_8CHOCCHIP_SDLKEYBOARD_H + +#include "../../emulator/Keyboard.h" +#include + +class SdlKeyboard : public Keyboard { +private: + std::unordered_map KEYMAP = { + {49, 0x1}, // 1 + {50, 0x2}, // 2 + {51, 0x3}, // 3 + {52, 0xc}, // 4 + {113, 0x4}, // Q + {119, 0x5}, // W + {101, 0x6}, // E + {114, 0xD}, // R + {97, 0x7}, // A + {115, 0x8}, // S + {100, 0x9}, // D + {102, 0xE}, // F + {122, 0xA}, // Z + {120, 0x0}, // X + {99, 0xB}, // C + {118, 0xF} // V + }; +public: + void handleKeyDown(uint8_t keyCode) override; + void handleKeyUp(uint8_t keyCode) override; +}; + +#endif // INC_8CHOCCHIP_SDLKEYBOARD_H diff --git a/src/sdl/emulator/SdlRenderer.cpp b/src/sdl/emulator/SdlRenderer.cpp new file mode 100644 index 0000000..7ba78b3 --- /dev/null +++ b/src/sdl/emulator/SdlRenderer.cpp @@ -0,0 +1,17 @@ +#include "SdlRenderer.h" + +SdlRenderer::SdlRenderer(SDL_Renderer* window) { + this->window = window; + + this->scale = 15; // Scale up because 64 x 32 would be tiny on our screens now +} + +void SdlRenderer::drawPixel(uint16_t x, uint16_t y) { + SDL_Rect rect = {x * scale, y * scale, scale, scale}; + SDL_SetRenderDrawColor(window, 255, 255, 255, 255); // Set color to white + SDL_RenderFillRect(window, &rect); +} + +uint8_t SdlRenderer::getScale() const { + return this->scale; +} \ No newline at end of file diff --git a/src/sdl/emulator/SdlRenderer.h b/src/sdl/emulator/SdlRenderer.h new file mode 100644 index 0000000..9809cf2 --- /dev/null +++ b/src/sdl/emulator/SdlRenderer.h @@ -0,0 +1,19 @@ +#ifndef INC_8CHOCCHIP_SDLRENDERER_H +#define INC_8CHOCCHIP_SDLRENDERER_H + +#include "../../emulator/Renderer.h" +#include "SDL2/SDL.h" + +class SdlRenderer : public Renderer { +private: + SDL_Renderer* window; + + uint8_t scale; +public: + SdlRenderer(SDL_Renderer* window); + + void drawPixel(uint16_t x, uint16_t y) override; + uint8_t getScale() const; +}; + +#endif // INC_8CHOCCHIP_SDLRENDERER_H diff --git a/src/sdl/emulator/SdlSpeaker.cpp b/src/sdl/emulator/SdlSpeaker.cpp new file mode 100644 index 0000000..553f1b7 --- /dev/null +++ b/src/sdl/emulator/SdlSpeaker.cpp @@ -0,0 +1,13 @@ +#include "SdlSpeaker.h" +#include + +SdlSpeaker::SdlSpeaker() { + +} + +void SdlSpeaker::play() { +} + +void SdlSpeaker::stop() { +} + diff --git a/src/sdl/emulator/SdlSpeaker.h b/src/sdl/emulator/SdlSpeaker.h new file mode 100644 index 0000000..f7941ee --- /dev/null +++ b/src/sdl/emulator/SdlSpeaker.h @@ -0,0 +1,15 @@ +#ifndef INC_8CHOCCHIP_SDLSPEAKER_H +#define INC_8CHOCCHIP_SDLSPEAKER_H + +#include "../../emulator/Speaker.h" + +class SdlSpeaker : public Speaker { +private: +public: + SdlSpeaker(); + + void play() override; + void stop() override; +}; + +#endif // INC_8CHOCCHIP_SDLSPEAKER_H diff --git a/src/sdl/sdl.cpp b/src/sdl/sdl.cpp new file mode 100644 index 0000000..8e04c66 --- /dev/null +++ b/src/sdl/sdl.cpp @@ -0,0 +1,145 @@ +#include "sdl.h" + +#include +#include +#include + +#include "../emulator/Cpu.h" +#include "emulator/SdlKeyboard.h" +#include "emulator/SdlRenderer.h" +#include "emulator/SdlSpeaker.h" + +#include "SDL2/SDL.h" + +int sdl::launch(const std::string &rom) { + if (rom.empty()) { + std::cerr << "No ROM file has been specified :(" << std::endl; + return 1; + } + + std::ifstream file(rom, std::ios::binary | std::ios::ate); + if (!file.good()) { + std::cerr << "Can not find file " << rom << std::endl; + return 1; + } + + if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0) { + std::cerr << "SDL could not initialize! SDL_Error: " << SDL_GetError() << std::endl; + return -1; + } + + SDL_Window *window = SDL_CreateWindow("8ChocChip - CHIP-8 Emulator", SDL_WINDOWPOS_UNDEFINED, + SDL_WINDOWPOS_UNDEFINED, 64 * 15, 32 * 15, SDL_WINDOW_SHOWN); + + if (window == nullptr) { + std::cerr << "Window could not be created! SDL_Error: " << SDL_GetError() << std::endl; + return -1; + } + + // Set window icon + SDL_Surface *icon = SDL_LoadBMP("../../assets/icon.bmp"); + if (icon == nullptr) { + std::cerr << "Icon could not be loaded! SDL_Error: " << SDL_GetError() << std::endl; + return -1; + } + SDL_SetWindowIcon(window, icon); + SDL_FreeSurface(icon); + + // Create renderer + SDL_Renderer *windowRenderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED); + if (windowRenderer == nullptr) { + std::cerr << "Renderer could not be created! SDL_Error: " << SDL_GetError() << std::endl; + return -1; + } + + SdlRenderer renderer(windowRenderer); + SdlSpeaker speaker; + SdlKeyboard keyboard; + Cpu cpu(&renderer, &keyboard, &speaker); + + cpu.loadSpritesIntoMemory(); + + cpu.loadProgramIntoMemory(&file); + + bool focus; + + auto start = std::chrono::high_resolution_clock::now(); + auto end = std::chrono::high_resolution_clock::now(); + std::chrono::duration elapsed_seconds; + + // Variables to count frames + int frames = 0; + double fps; + + const int SCREEN_TICKS_PER_FRAME = 1000 / 60; // Time per frame in milliseconds + + Uint32 startTicks; + + while (true) { + startTicks = SDL_GetTicks(); + + SDL_Event event; + while (SDL_PollEvent(&event)) { + if (event.type == SDL_QUIT) { + SDL_DestroyRenderer(windowRenderer); + SDL_DestroyWindow(window); + SDL_Quit(); + return 0; + } else if (event.type == SDL_WINDOWEVENT) { + if (event.window.event == SDL_WINDOWEVENT_FOCUS_GAINED) { + focus = true; + } else if (event.window.event == SDL_WINDOWEVENT_FOCUS_LOST) { + focus = false; + } + } + + if (!focus) continue; + + // Handle keyboard inputs + if (event.type == SDL_KEYDOWN) { + keyboard.handleKeyDown(event.key.keysym.sym); + } else if (event.type == SDL_KEYUP) { + keyboard.handleKeyUp(event.key.keysym.sym); + } + } + + // Run a cycle of the emulator + cpu.cycle(); + + // Set renderer draw color + SDL_SetRenderDrawColor(windowRenderer, 0, 0, 0, 255); + // Clear the window + SDL_RenderClear(windowRenderer); + + // Render the window + renderer.render(); + SDL_RenderPresent(windowRenderer); + + // Calculate time spent in frame + int frameTicks = SDL_GetTicks() - startTicks; + + // If frame finished early + if (frameTicks < SCREEN_TICKS_PER_FRAME) { + // Wait remaining time + SDL_Delay(SCREEN_TICKS_PER_FRAME - frameTicks); + } + + frames++; + + end = std::chrono::high_resolution_clock::now(); + elapsed_seconds = end - start; + + // If elapsed time is greater than or equal to 1 second + if (elapsed_seconds.count() >= 1.0) { + // Calculate FPS + fps = frames / elapsed_seconds.count(); + + // Output FPS + std::cout << "FPS: " << fps << std::endl; + + // Reset frame count and start time + frames = 0; + start = std::chrono::high_resolution_clock::now(); + } + } +} \ No newline at end of file diff --git a/src/sdl/sdl.h b/src/sdl/sdl.h new file mode 100644 index 0000000..c816ac2 --- /dev/null +++ b/src/sdl/sdl.h @@ -0,0 +1,12 @@ +#ifndef INC_8CHOCCHIP_SDL_H +#define INC_8CHOCCHIP_SDL_H + +#include + +class sdl { +public: + int launch(const std::string &rom); +}; + + +#endif // INC_8CHOCCHIP_SDL_H diff --git a/src/sfml/CMakeLists.txt b/src/sfml/CMakeLists.txt deleted file mode 100644 index 2dc3730..0000000 --- a/src/sfml/CMakeLists.txt +++ /dev/null @@ -1,8 +0,0 @@ -target_sources(8ChocChip PRIVATE - MainMenu.cpp - MainMenu.h - Emulator.cpp - Emulator.h) - -add_subdirectory(ui) -add_subdirectory(emulator) \ No newline at end of file diff --git a/src/sfml/Emulator.cpp b/src/sfml/Emulator.cpp deleted file mode 100644 index 5c3b8d8..0000000 --- a/src/sfml/Emulator.cpp +++ /dev/null @@ -1,75 +0,0 @@ -#include "Emulator.h" - -#include "fstream" -#include "iostream" - -#include "../emulator/Cpu.h" -#include "emulator/SfmlKeyboard.h" -#include "emulator/SfmlRenderer.h" -#include "emulator/SfmlSpeaker.h" - -int Emulator::launch(const std::string &rom) { - if (rom.empty()) { - std::cerr << "No ROM file has been specified :(" << std::endl; - return 1; - } - - std::ifstream file(rom, std::ios::binary | std::ios::ate); - if (!file.good()) { - std::cerr << "Can not find file " << rom << std::endl; - return 1; - } - - sf::RenderWindow window(sf::VideoMode(64 * 15, 32 * 15), "8ChocChip - CHIP-8 Emulator", sf::Style::Titlebar | sf::Style::Close); - sf::Image icon; - icon.loadFromFile("../assets/icon.png"); - window.setIcon(64, 64, icon.getPixelsPtr()); - window.setFramerateLimit(60); - - SfmlRenderer renderer(&window); - - SfmlSpeaker speaker; - SfmlKeyboard keyboard; - Cpu cpu(&renderer, &keyboard, &speaker); - - cpu.loadSpritesIntoMemory(); - - cpu.loadProgramIntoMemory(&file); - - bool focus; - - while (window.isOpen()) { - sf::Event event{}; - while (window.pollEvent(event)) { - if (event.type == sf::Event::Closed) { - window.close(); - } - - if (event.type == sf::Event::LostFocus) - focus = false; - else if (event.type == sf::Event::GainedFocus) - focus = true; - - if (!focus) continue; - - // Handle keyboard inputs - if (event.type == sf::Event::KeyPressed) { - keyboard.handleKeyDown(event.key.code); - } else if (event.type == sf::Event::KeyReleased) { - keyboard.handleKeyUp(event.key.code); - } - } - - // Run a cycle of the emulator - cpu.cycle(); - - // Clear the window - window.clear(sf::Color::Black); - - // Render the window - renderer.render(); - window.display(); - } - - return 0; -} diff --git a/src/sfml/Emulator.h b/src/sfml/Emulator.h deleted file mode 100644 index 3f4f1df..0000000 --- a/src/sfml/Emulator.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef INC_8CHOCCHIP_EMULATOR_H -#define INC_8CHOCCHIP_EMULATOR_H - -#include "SFML/Graphics.hpp" -#include - -class Emulator { -private: -public: - int launch(const std::string& rom); -}; - - -#endif // INC_8CHOCCHIP_EMULATOR_H diff --git a/src/sfml/MainMenu.cpp b/src/sfml/MainMenu.cpp deleted file mode 100644 index 31e0146..0000000 --- a/src/sfml/MainMenu.cpp +++ /dev/null @@ -1,162 +0,0 @@ -#include "MainMenu.h" - -#include - -#include "ui/TextButton.h" - -#include "Emulator.h" -#include "../util/MiscUtil.h" - -#include "nfd.h" -#include "libconfig.h" -#include "libconfig.hh" - -#include - -MainMenu::MainMenu(std::unordered_map>& romFiles, - std::vector& romDirectories, std::vector& windows, - std::string configFilePath) : - romDirectories(romDirectories), romFiles(romFiles), windows(windows), - window(sf::VideoMode(640, 480), "8ChocChip - Chip8 Emulator") { - - sf::Vector2u originalWindowSize = this->window.getSize(); - sf::Image icon; - icon.loadFromFile("../../assets/icon.png"); - this->window.setIcon(64, 64, icon.getPixelsPtr()); -// this->window.setVerticalSyncEnabled(true); - - sf::Font font; - font.loadFromFile("../../assets/font.ttf"); - - std::unordered_map roms; - - for (auto& thing : romFiles) { - for (std::string& file : thing.second) { - - TextButton romButton(0, 25 * roms.size(), this->window.getSize().x, 25, MiscUtil::getFileFromPath(file), &font); - - roms.emplace(file, romButton); - } - } - - TextButton button(0, 400, 640, 80, "Select ROM", &font); - - bool focus; - bool debug = false; - - auto start = std::chrono::high_resolution_clock::now(); - auto end = std::chrono::high_resolution_clock::now(); - std::chrono::duration elapsed_seconds; - - // Variables to count frames - int frames = 0; - double fps; - - while (this->window.isOpen()) { - sf::Event event; - sf::Vector2i pos = sf::Mouse::getPosition(this->window); - while (this->window.pollEvent(event)) { - if (event.type == sf::Event::Closed) { - this->window.close(); - } else if (event.type == sf::Event::LostFocus) { - focus = false; - } else if (event.type == sf::Event::GainedFocus) { - focus = true; - } else if (event.type == sf::Event::Resized) { - sf::FloatRect visibleArea(0, 0, event.size.width, event.size.height); - this->window.setView(sf::View(visibleArea)); - - for (auto& romButton : roms) { - romButton.second.updateSize(originalWindowSize, this->window.getSize()); - romButton.second.update(this->window, pos); - } - - button.updateSize(originalWindowSize, this->window.getSize()); - button.update(this->window, pos); - } else if (event.type == sf::Event::KeyPressed) { - if (event.key.code == sf::Keyboard::Key::L) { - debug = !debug; - } - } - } - - if (focus) { - for (auto& romButton : roms) { - romButton.second.update(this->window, pos); - - if (!romButton.second.isJustClicked()) continue; - - Emulator emulator; - std::thread newWindow(&Emulator::launch, &emulator, romButton.first); - newWindow.detach(); - windows.emplace_back(&newWindow); - } - button.update(this->window, pos); - - if (button.isClicked()) { - nfdchar_t* outPath = nullptr; - nfdresult_t result = NFD_PickFolder(nullptr, &outPath); - - if (result == NFD_OKAY) { - libconfig::Config config; - config.readFile(configFilePath); - - libconfig::Setting& settings = config.getRoot(); - - if (!settings.exists("directories")) { - settings.add("directories", libconfig::Setting::TypeArray); - } - - libconfig::Setting& directories = settings["directories"]; - directories.add(libconfig::Setting::TypeString) = outPath; - - romDirectories.emplace_back(outPath); - - for (const auto& file : std::filesystem::directory_iterator(outPath)) { - if (file.is_directory()) continue; // TODO: Make sure its a file that can be emulated, at least basic checks so it isn't like a word doc - - printf("Processing file - : %s\n", file.path().c_str()); - - // Check if the rom directory doesn't exist in romFiles, then add it - if (romFiles.find(&romDirectories.back()) == romFiles.end()) { - romFiles.emplace(&romDirectories.back(), std::vector()); - } - - // Add the file path to the romFiles entry - romFiles.find(&romDirectories.back())->second.emplace_back(file.path()); - - TextButton romButton(0, 25 * roms.size(), this->window.getSize().x, 25, file.path().filename().string(), &font); - roms.emplace(file.path().string(), romButton); - } - config.writeFile(configFilePath); - } - } - } - - this->window.clear(sf::Color::White); - for (auto& romButton : roms) { - romButton.second.draw(this->window); - } - button.draw(this->window); - - this->window.display(); - - frames++; - - end = std::chrono::high_resolution_clock::now(); - elapsed_seconds = end - start; - - // If elapsed time is greater than or equal to 1 second - if (debug && elapsed_seconds.count() >= 1.0) { - // Calculate FPS - fps = frames / elapsed_seconds.count(); - - // Output FPS - std::cout << "FPS: " << fps << std::endl; - - // Reset frame count and start time - frames = 0; - start = std::chrono::high_resolution_clock::now(); - } - } -} \ No newline at end of file diff --git a/src/sfml/MainMenu.h b/src/sfml/MainMenu.h deleted file mode 100644 index a0c7e10..0000000 --- a/src/sfml/MainMenu.h +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef INC_8CHOCCHIP_MAINMENU_H -#define INC_8CHOCCHIP_MAINMENU_H - -#include "SFML/Graphics.hpp" - -#include -#include -#include - -class MainMenu { -private: - sf::RenderWindow window; - - std::vector& romDirectories; - std::unordered_map>& romFiles; - std::vector& windows; -public: - MainMenu(std::unordered_map>& romFiles, - std::vector& romDirectories, std::vector& windows, - std::string configFilePath); -}; - - -#endif // INC_8CHOCCHIP_MAINMENU_H diff --git a/src/sfml/emulator/CMakeLists.txt b/src/sfml/emulator/CMakeLists.txt deleted file mode 100644 index 95f65ae..0000000 --- a/src/sfml/emulator/CMakeLists.txt +++ /dev/null @@ -1,7 +0,0 @@ -target_sources(8ChocChip PRIVATE - SfmlSpeaker.cpp - SfmlSpeaker.h - SfmlRenderer.cpp - SfmlRenderer.h - SfmlKeyboard.cpp - SfmlKeyboard.h) \ No newline at end of file diff --git a/src/sfml/emulator/SfmlKeyboard.h b/src/sfml/emulator/SfmlKeyboard.h deleted file mode 100644 index 99d8ea7..0000000 --- a/src/sfml/emulator/SfmlKeyboard.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef INC_8CHOCCHIP_SFMLKEYBOARD_H -#define INC_8CHOCCHIP_SFMLKEYBOARD_H - -#include "../../emulator/Keyboard.h" - -class SfmlKeyboard : public Keyboard { -public: - void handleKeyDown(uint8_t keyCode) override; - void handleKeyUp(uint8_t keyCode) override; -}; - -#endif // INC_8CHOCCHIP_SFMLKEYBOARD_H diff --git a/src/sfml/emulator/SfmlRenderer.cpp b/src/sfml/emulator/SfmlRenderer.cpp deleted file mode 100644 index a2551e2..0000000 --- a/src/sfml/emulator/SfmlRenderer.cpp +++ /dev/null @@ -1,19 +0,0 @@ -#include "SfmlRenderer.h" - -SfmlRenderer::SfmlRenderer(sf::RenderWindow* window) { - this->window = window; - - this->scale = 15; // Scale up because 64 x 32 would be tiny on our screens now -} - -void SfmlRenderer::drawPixel(uint16_t x, uint16_t y) { - sf::RectangleShape rectangle(sf::Vector2f(getScale(), getScale())); - rectangle.setPosition(x * getScale(), y * getScale()); - rectangle.setFillColor(sf::Color::White); - - this->window->draw(rectangle); -} - -uint8_t SfmlRenderer::getScale() const { - return this->scale; -} \ No newline at end of file diff --git a/src/sfml/emulator/SfmlRenderer.h b/src/sfml/emulator/SfmlRenderer.h deleted file mode 100644 index 0019848..0000000 --- a/src/sfml/emulator/SfmlRenderer.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef INC_8CHOCCHIP_SFMLRENDERER_H -#define INC_8CHOCCHIP_SFMLRENDERER_H - -#include "SFML/Graphics.hpp" - -#include "../../emulator/Renderer.h" - -class SfmlRenderer : public Renderer { -private: - sf::RenderWindow* window; - - uint8_t scale; -public: - SfmlRenderer(sf::RenderWindow* window); - - void drawPixel(uint16_t x, uint16_t y) override; - uint8_t getScale() const; -}; - -#endif // INC_8CHOCCHIP_SFMLRENDERER_H diff --git a/src/sfml/emulator/SfmlSpeaker.cpp b/src/sfml/emulator/SfmlSpeaker.cpp deleted file mode 100644 index fe326e5..0000000 --- a/src/sfml/emulator/SfmlSpeaker.cpp +++ /dev/null @@ -1,27 +0,0 @@ -#include "SfmlSpeaker.h" -#include - -SfmlSpeaker::SfmlSpeaker() { - - this->sound.setVolume(100.f); - this->sound.setLoop(true); - - const unsigned int sampleRate = 44100; - const unsigned int frequency = 440; - sf::Int16 samples[sampleRate]; - for (unsigned int i = 0; i < sampleRate; ++i) { - samples[i] = static_cast(32767 * std::sin(2 * 3.14159265 * frequency * i / sampleRate)); // Mmmm yum pi - } - this->soundBuffer.loadFromSamples(samples, sampleRate, 1, sampleRate); - - this->sound.setBuffer(this->soundBuffer); -} - -void SfmlSpeaker::play() { - this->sound.play(); -} - -void SfmlSpeaker::stop() { - this->sound.stop(); -} - diff --git a/src/sfml/emulator/SfmlSpeaker.h b/src/sfml/emulator/SfmlSpeaker.h deleted file mode 100644 index 584c721..0000000 --- a/src/sfml/emulator/SfmlSpeaker.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef INC_8CHOCCHIP_SFMLSPEAKER_H -#define INC_8CHOCCHIP_SFMLSPEAKER_H - -#include "../../emulator/Speaker.h" -#include "SFML/Audio.hpp" - -class SfmlSpeaker : public Speaker { -private: - sf::SoundBuffer soundBuffer; - sf::Sound sound; -public: - SfmlSpeaker(); - - void play() override; - void stop() override; -}; - -#endif // INC_8CHOCCHIP_SFMLSPEAKER_H diff --git a/src/sfml/ui/CMakeLists.txt b/src/sfml/ui/CMakeLists.txt deleted file mode 100644 index c7c212a..0000000 --- a/src/sfml/ui/CMakeLists.txt +++ /dev/null @@ -1,3 +0,0 @@ -target_sources(8ChocChip PRIVATE - TextButton.cpp - TextButton.h) \ No newline at end of file diff --git a/src/sfml/ui/TextButton.cpp b/src/sfml/ui/TextButton.cpp deleted file mode 100644 index 4d785d2..0000000 --- a/src/sfml/ui/TextButton.cpp +++ /dev/null @@ -1,66 +0,0 @@ -#include "TextButton.h" - -TextButton::TextButton(float x, float y, float width, float height, std::string buttonText, sf::Font* font) { - this->button.setSize(sf::Vector2f(width, height)); - this->button.setPosition(sf::Vector2f(x, y)); - - this->originalPosition = this->button.getPosition(); - this->originalSize = this->button.getSize(); - - this->text.setFont(*font); - this->text.setString(buttonText); - this->text.setCharacterSize(20); - this->text.setFillColor(sf::Color::Black); - this->text.setPosition(x + width / 2 - this->text.getGlobalBounds().width / 2, y + height / 2 - this->text.getGlobalBounds().height / 2); - - this->idleColor = sf::Color(192, 192, 192); - this->hoverColor = sf::Color(128, 128, 128); - this->activeColor = sf::Color(64, 64, 64); - - this->button.setFillColor(this->idleColor); - - this->isPressed = false; - this->isHovered = false; -} - -void TextButton::updateSize(const sf::Vector2u originalSize, const sf::Vector2u updatedSize) { - this->button.setSize(sf::Vector2f(this->originalSize.x / originalSize.x * updatedSize.x, this->originalSize.y / originalSize.y * updatedSize.y)); - this->button.setPosition(this->originalPosition.x / originalSize.x * updatedSize.x, this->originalPosition.y / originalSize.y * updatedSize.y); - - this->text.setPosition(this->button.getPosition().x + this->button.getSize().x / 2 - this->text.getGlobalBounds().width / 2, this->button.getPosition().y + this->button.getSize().y / 2 - this->text.getGlobalBounds().height / 2); -} - -void TextButton::update(sf::RenderWindow& window, sf::Vector2i pos) { - this->lastPressed = this->isPressed; - this->isHovered = this->button.getGlobalBounds().contains(pos.x, pos.y); - - if (this->isHovered && sf::Mouse::isButtonPressed(sf::Mouse::Left)) { - this->isPressed = true; - updateColour(this->activeColor); - } else if (this->isHovered) { - updateColour(this->hoverColor); - } else { - this->isPressed = false; - updateColour(this->idleColor); - } -} - -void TextButton::updateColour(sf::Color color) { - if (this->color == color) - return; - - this->button.setFillColor(color); -} - -void TextButton::draw(sf::RenderWindow &window) { - window.draw(this->button); - window.draw(this->text); -} - -bool TextButton::isClicked() { - return this->isPressed; -} - -bool TextButton::isJustClicked() { - return !this->lastPressed && this->isPressed; -} \ No newline at end of file diff --git a/src/sfml/ui/TextButton.h b/src/sfml/ui/TextButton.h deleted file mode 100644 index d6bf729..0000000 --- a/src/sfml/ui/TextButton.h +++ /dev/null @@ -1,44 +0,0 @@ -// -// Created by doom on 3/27/24. -// - -#ifndef TEXTBUTTON_H -#define TEXTBUTTON_H - -#include "SFML/Graphics.hpp" - - -class TextButton { -private: - sf::RectangleShape button; - sf::Vector2f originalSize; - sf::Vector2f originalPosition; - sf::Text text; - sf::Font* font; - sf::Color color; - sf::Color idleColor; - sf::Color hoverColor; - sf::Color activeColor; - bool isPressed; - bool lastPressed; - bool isHovered; - -public: - TextButton(float x, float y, float width, float height, std::string buttonText, sf::Font* font); - - void updateSize(const sf::Vector2u originalSize, const sf::Vector2u updatedSize); - - void update(sf::RenderWindow& window, sf::Vector2i pos); - - void draw(sf::RenderWindow& window); - - bool isClicked(); - - bool isJustClicked(); - - void updateColour(sf::Color color); -}; - - - -#endif //TEXTBUTTON_H From 13f0474e7bbad6e90654dda06b3d09b5bc6acb65 Mon Sep 17 00:00:00 2001 From: JustDoom <61824552+JustDoom@users.noreply.github.com> Date: Sat, 6 Apr 2024 18:10:47 +1000 Subject: [PATCH 02/28] Remove SFML submodule --- .gitmodules | 3 -- dependencies/CMakeLists.txt | 6 --- dependencies/sfml | 1 - src/CMakeLists.txt | 15 +----- src/main.cpp | 101 ------------------------------------ 5 files changed, 1 insertion(+), 125 deletions(-) delete mode 160000 dependencies/sfml diff --git a/.gitmodules b/.gitmodules index 82f5761..fd0c4be 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,3 @@ -[submodule "dependencies/sfml"] - path = dependencies/sfml - url = git@github.com:SFML/SFML.git [submodule "dependencies/nativefiledialog"] path = dependencies/nativefiledialog url = git@github.com:AlvaroBarua/nativefiledialog.git diff --git a/dependencies/CMakeLists.txt b/dependencies/CMakeLists.txt index ec3afa5..27b7ded 100644 --- a/dependencies/CMakeLists.txt +++ b/dependencies/CMakeLists.txt @@ -1,9 +1,3 @@ -if(NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/sfml/CMakeLists.txt") - message("${CMAKE_CURRENT_SOURCE_DIR}/sfml/CMakeLists.txt") - message(FATAL_ERROR "Please initialize submodules using:\n git submodule update --init --recursive") -endif() - -add_subdirectory(sfml) add_subdirectory(nativefiledialog) add_subdirectory(libconfig) add_subdirectory(sdl) \ No newline at end of file diff --git a/dependencies/sfml b/dependencies/sfml deleted file mode 160000 index 69ea0cd..0000000 --- a/dependencies/sfml +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 69ea0cd863aed1d4092b970b676924a716ff718b diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 90fdc6c..ebcb394 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,7 +1,6 @@ add_executable(8ChocChip) add_subdirectory(sdl) -add_subdirectory(sfml) add_subdirectory(util) add_subdirectory(emulator) @@ -13,16 +12,4 @@ else() set(libname "config") endif() -target_link_libraries(8ChocChip PRIVATE sfml-graphics sfml-audio sfml-window sfml-system nativefiledialog ${libname}++ SDL2) - -# Copy DLLs needed for runtime on Windows -if(WIN32) - if (BUILD_SHARED_LIBS) - add_custom_command(TARGET 8ChocChip POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy_if_different - $ - $ - $ - $) - endif() -endif() +target_link_libraries(8ChocChip PRIVATE nativefiledialog ${libname}++ SDL2) \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 69bc725..6a0f2ff 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,18 +1,3 @@ -#include -#include -#include -#include - -#include "sfml/Emulator.h" -#include "fstream" - -#include "util/MiscUtil.h" - -#include "sfml/MainMenu.h" - -#include "libconfig.h" -#include "libconfig.hh" - #include "sdl/sdl.h" int main(int argc, char **argv) { @@ -20,91 +5,5 @@ int main(int argc, char **argv) { sdl sdl; sdl.launch("/home/doom/Documents/8ChipRoms/BLINKY"); - return 5; - - bool quickRom = false; - std::string rom; - - for (int i = 0; i < argc; i++) { - std::string arg = argv[i]; - if (arg.rfind("--") != 0) continue; // TODO: Account for --longform or -sf (short form) commands. just needs a better command handler - - std::string command = MiscUtil::toLowerCase(arg); - if (command == "--rom") { - if (argc - 1 <= i) { - std::cerr << "Please include the path to the file" << std::endl; - } else { - quickRom = true; - rom = argv[i + 1]; - } - } - } - - std::string home = std::filesystem::path(getenv("HOME")).string(); - std::string configFilePath = (std::filesystem::path(home) / ".8chocchip.cfg").string(); - - std::vector windows; - - std::vector romDirectories; - std::unordered_map> romFiles; - - std::ifstream file(configFilePath, std::ios::binary | std::ios::ate); - if (file.good()) { - libconfig::Config config; - config.readFile(configFilePath); - - libconfig::Setting &settings = config.getRoot(); - - if (!settings.exists("directories")) { - settings.add("directories", libconfig::Setting::TypeArray); - } - - libconfig::Setting &directories = settings["directories"]; - for (int i = 0; i < directories.getLength(); i++) { - libconfig::Setting &string = directories[i]; - std::string directoryPath = string.c_str(); - - romDirectories.emplace_back(directoryPath); - - for (const auto& file: std::filesystem::directory_iterator(directoryPath)) { - if (file.is_directory()) - continue; // Skip directories - - printf("Processing file: %s\n", file.path().c_str()); - - // Check if the rom directory doesn't exist in romFiles, then add it - if (romFiles.find(&romDirectories.back()) == romFiles.end()) { - romFiles.emplace(&romDirectories.back(), std::vector()); - } - - // Add the file path to the romFiles entry - romFiles.find(&romDirectories.back())->second.emplace_back(file.path()); - } - } - } else { - config_t cfg; - config_init(&cfg); - - config_setting_t *root = config_root_setting(&cfg); - config_setting_t *list = config_setting_add(root, "directories", CONFIG_TYPE_LIST); - - if (config_write_file(&cfg, configFilePath.c_str()) == CONFIG_FALSE) { - std::cerr << "Error creating configuration file." << std::endl; - config_destroy(&cfg); - return 1; - } - - std::cout << "Configuration file created successfully." << std::endl; - - config_destroy(&cfg); - } - - if (quickRom) { - Emulator emulator; - return emulator.launch(rom); - } else { - MainMenu(romFiles, romDirectories, windows, configFilePath); - } - return 0; } \ No newline at end of file From a0edb78269fa1d80cd282cc29b55b2d8e1e2a8f1 Mon Sep 17 00:00:00 2001 From: JustDoom <61824552+JustDoom@users.noreply.github.com> Date: Sat, 6 Apr 2024 18:13:27 +1000 Subject: [PATCH 03/28] Update README.md --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 717e15e..2cf4e23 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # 8ChocChip +**This is a basic SDL port of the emulator. The menu and sound is not yet implementated. Gameplay frame rate increased from SFML 650 or so to about 5000 with SDL so may finish this once it is more stable** + 8ChocChip is an emulator for the Chip8 software that I am working on to learn the basics around emulation. I decided to program this in C++ because I wanted to do more in it and improve my skills. @@ -46,4 +48,4 @@ Thanks to these two blogs that helped me through creating this emulator Currently, three libraries are being used - [SFML](https://github.com/SFML/SFML) - UI, graphics, input and sounds - [NativeFileDialog](https://github.com/mlabbe/nativefiledialog) - A fork of this is used for the CMakeLists files (https://github.com/AlvaroBarua/nativefiledialog/tree/CMakeLists) -- [libconfig](https://github.com/hyperrealm/libconfig) - Library to manage save data (like directories that hold ROMs) and possible future config files \ No newline at end of file +- [libconfig](https://github.com/hyperrealm/libconfig) - Library to manage save data (like directories that hold ROMs) and possible future config files From c43847d5ad60705e42cd66ff8bdfc72d70febe02 Mon Sep 17 00:00:00 2001 From: doom <61824552+JustDoom@users.noreply.github.com> Date: Fri, 13 Dec 2024 12:00:12 +1000 Subject: [PATCH 04/28] Well it runs again now --- CMakeLists.txt | 2 +- dependencies/sdl | 2 +- src/CMakeLists.txt | 10 +- src/emulator/Cpu.cpp | 4 +- src/emulator/Keyboard.h | 18 +++ src/emulator/Renderer.cpp | 4 +- src/emulator/Renderer.h | 7 +- src/main.cpp | 125 ++++++++++++++------- src/sdl/CMakeLists.txt | 14 ++- src/sdl/Emulator.cpp | 65 +++++++++++ src/sdl/Emulator.h | 28 +++++ src/sdl/InputHandler.cpp | 16 +-- src/sdl/InputHandler.h | 30 +++-- src/sdl/MainMenu.cpp | 133 ++++++++++++++++++++++ src/sdl/MainMenu.h | 38 +++++++ src/sdl/Window.cpp | 99 ++++++++++++++++ src/sdl/Window.h | 35 ++++++ src/sdl/emulator/SdlKeyboard.h | 40 +++---- src/sdl/emulator/SdlRenderer.cpp | 17 ++- src/sdl/emulator/SdlRenderer.h | 14 +-- src/sdl/emulator/SdlSpeaker.cpp | 7 +- src/sdl/emulator/SdlSpeaker.h | 2 +- src/sdl/sdl.cpp | 145 ------------------------ src/sdl/sdl.h | 12 -- src/sdl/ui/CMakeLists.txt | 4 + src/sdl/ui/TextButton.cpp | 73 ++++++++++++ src/sdl/ui/TextButton.h | 43 +++++++ src/sfml/CMakeLists.txt | 10 -- src/sfml/Emulator.cpp | 77 ------------- src/sfml/Emulator.h | 13 --- src/sfml/MainMenu.cpp | 174 ----------------------------- src/sfml/MainMenu.h | 26 ----- src/sfml/emulator/CMakeLists.txt | 5 - src/sfml/emulator/SfmlRenderer.cpp | 17 --- src/sfml/emulator/SfmlSpeaker.cpp | 28 ----- src/sfml/emulator/SfmlSpeaker.h | 19 ---- src/sfml/ui/TextButton.cpp | 69 ------------ src/sfml/ui/TextButton.h | 41 ------- 38 files changed, 700 insertions(+), 766 deletions(-) create mode 100644 src/sdl/Emulator.cpp create mode 100644 src/sdl/Emulator.h create mode 100644 src/sdl/MainMenu.cpp create mode 100644 src/sdl/MainMenu.h create mode 100644 src/sdl/Window.cpp create mode 100644 src/sdl/Window.h delete mode 100644 src/sdl/sdl.cpp delete mode 100644 src/sdl/sdl.h create mode 100644 src/sdl/ui/CMakeLists.txt create mode 100644 src/sdl/ui/TextButton.cpp create mode 100644 src/sdl/ui/TextButton.h delete mode 100644 src/sfml/CMakeLists.txt delete mode 100644 src/sfml/Emulator.cpp delete mode 100644 src/sfml/Emulator.h delete mode 100644 src/sfml/MainMenu.cpp delete mode 100644 src/sfml/MainMenu.h delete mode 100644 src/sfml/emulator/CMakeLists.txt delete mode 100644 src/sfml/emulator/SfmlRenderer.cpp delete mode 100644 src/sfml/emulator/SfmlSpeaker.cpp delete mode 100644 src/sfml/emulator/SfmlSpeaker.h delete mode 100644 src/sfml/ui/TextButton.cpp delete mode 100644 src/sfml/ui/TextButton.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 074456b..e527244 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,7 +27,7 @@ else() set(libname "config") endif() -target_link_libraries(8ChocChip PRIVATE sfml-graphics sfml-audio sfml-window sfml-system nfd ${libname}++) +target_link_libraries(8ChocChip PRIVATE SDL3::SDL3 nfd ${libname}++) # Set the RPATH of the executable to include the directory where SFML libraries are located set_target_properties(8ChocChip PROPERTIES diff --git a/dependencies/sdl b/dependencies/sdl index 2f4a7bb..78cc5c1 160000 --- a/dependencies/sdl +++ b/dependencies/sdl @@ -1 +1 @@ -Subproject commit 2f4a7bbcedaaf49c8bf87e34081120a6a49a0d26 +Subproject commit 78cc5c173404488d80751af226d1eaf67033bcc4 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index df9402f..88e17c8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -2,12 +2,4 @@ add_subdirectory(sdl) add_subdirectory(util) add_subdirectory(emulator) -target_sources(8ChocChip PUBLIC main.cpp) - -if(CMAKE_HOST_WIN32) - set(libname "libconfig") -else() - set(libname "config") -endif() - -target_link_libraries(8ChocChip PRIVATE nfd ${libname}++ SDL2) \ No newline at end of file +target_sources(8ChocChip PUBLIC main.cpp) \ No newline at end of file diff --git a/src/emulator/Cpu.cpp b/src/emulator/Cpu.cpp index bac905e..e4704e8 100644 --- a/src/emulator/Cpu.cpp +++ b/src/emulator/Cpu.cpp @@ -71,9 +71,9 @@ void Cpu::cycle() { // Play sound until timer runs out if (this->soundTimer > 0) { - this->speaker->play(); + // this->speaker->play(); } else { - this->speaker->stop(); + // this->speaker->stop(); } } diff --git a/src/emulator/Keyboard.h b/src/emulator/Keyboard.h index 73aff39..bc1dfd2 100644 --- a/src/emulator/Keyboard.h +++ b/src/emulator/Keyboard.h @@ -9,6 +9,24 @@ class Keyboard { public: std::unordered_map keysPressed; std::function onNextKeyPress; + std::unordered_map KEYMAP = { + {30, 0x1}, // 1 + {31, 0x2}, // 2 + {32, 0x3}, // 3 + {33, 0xc}, // 4 + {20, 0x4}, // Q + {26, 0x5}, // W + {8, 0x6}, // E + {21, 0xD}, // R + {4, 0x7}, // A + {22, 0x8}, // S + {7, 0x9}, // D + {9, 0xE}, // F + {29, 0xA}, // Z + {27, 0x0}, // X + {6, 0xB}, // C + {25, 0xF} // V + }; void setOnNextKeyPress(std::function callback); void handleKeyDown(uint8_t keyCode); diff --git a/src/emulator/Renderer.cpp b/src/emulator/Renderer.cpp index a6b2b77..bcfba69 100644 --- a/src/emulator/Renderer.cpp +++ b/src/emulator/Renderer.cpp @@ -4,12 +4,12 @@ Renderer::Renderer() { this->display.resize(this->columns * this->rows); } -void Renderer::render() { +void Renderer::render(SDL_Renderer* renderer) { // Render the display for (uint16_t y = 0; y < this->rows; ++y) { for (uint16_t x = 0; x < this->columns; ++x) { if (this->display[y * this->columns + x]) { - drawPixel(x, y); + drawPixel(renderer, x, y); } } } diff --git a/src/emulator/Renderer.h b/src/emulator/Renderer.h index 97b7c6f..f7c4387 100644 --- a/src/emulator/Renderer.h +++ b/src/emulator/Renderer.h @@ -1,8 +1,9 @@ #ifndef RENDERER_H #define RENDERER_H -#include +#include #include +#include class Renderer { public: @@ -12,12 +13,12 @@ class Renderer { void clear(); - void render(); + void render(SDL_Renderer* renderer); uint16_t getColumns() const; uint16_t getRows() const; - virtual void drawPixel(uint16_t x, uint16_t y) = 0; + virtual void drawPixel(SDL_Renderer* renderer, uint16_t x, uint16_t y) = 0; private: const uint16_t columns = 64; const uint16_t rows = 32; diff --git a/src/main.cpp b/src/main.cpp index 0aee35f..fc8b5cf 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2,67 +2,75 @@ #include #include #include +#include +#include "sdl/MainMenu.h" -#include "sfml/Emulator.h" -#include "fstream" +#include #include "util/MiscUtil.h" -#include "sfml/MainMenu.h" - -#include "libconfig.h" #include "libconfig.hh" int main(int argc, char **argv) { - bool quickRom = false; std::string rom; for (int i = 0; i < argc; i++) { - std::string arg = argv[i]; + std::string_view arg = argv[i]; if (arg.rfind("--") != 0) continue; // TODO: Account for --longform or -sf (short form) commands. just needs a better command handler - std::string command = MiscUtil::toLowerCase(arg); + std::string command = MiscUtil::toLowerCase(std::string(arg)); if (command == "--rom") { - if (argc - 1 <= i) { - std::cerr << "Please include the path to the file" << std::endl; - } else { - quickRom = true; - rom = argv[i + 1]; + if (i + 1 < argc) { + // return Emulator().launch(argv[++i]); } + std::cerr << "Please include the path to the file" << std::endl; + return 0; + } + if (command == "--help") { + std::cerr << "Usage: 8chocchip --rom " << std::endl; + return 0; } } - std::string home = std::filesystem::path(getenv("HOME")).string(); - std::string configFilePath = (std::filesystem::path(home) / ".8chocchip.cfg").string(); + const char* home = nullptr; +#ifdef _WIN32 + home = std::getenv("USERPROFILE"); +#else + home = std::getenv("HOME"); +#endif + if (!home) { + std::cerr << "HOME environment variable not set!" << std::endl; + return 1; + } - std::vector windows; + std::string configFilePath = (std::filesystem::path(home) / ".8chocchip.cfg").string(); std::vector romDirectories; std::unordered_map> romFiles; - std::ifstream file(configFilePath, std::ios::binary | std::ios::ate); - if (file.good()) { - libconfig::Config config; + libconfig::Config config; + if (std::ifstream file(configFilePath, std::ios::binary | std::ios::ate); file.good()) { config.readFile(configFilePath); libconfig::Setting &settings = config.getRoot(); - if (!settings.exists("directories")) { settings.add("directories", libconfig::Setting::TypeArray); } - libconfig::Setting &directories = settings["directories"]; + libconfig::Setting& directories = settings["directories"]; + romDirectories.reserve(directories.getLength()); for (int i = 0; i < directories.getLength(); i++) { libconfig::Setting &string = directories[i]; std::string directoryPath = string.c_str(); romDirectories.emplace_back(directoryPath); - for (const auto& file: std::filesystem::directory_iterator(directoryPath)) { - if (file.is_directory()) - continue; // Skip directories + for (const auto& romFile: std::filesystem::directory_iterator(directoryPath)) { + if (romFile.is_directory()) { + continue; + } - printf("Processing file: %s\n", file.path().c_str()); + std::cout << "Processing file: " << romFile.path().c_str() << std::endl; // Check if the rom directory doesn't exist in romFiles, then add it if (romFiles.find(&romDirectories.back()) == romFiles.end()) { @@ -70,32 +78,65 @@ int main(int argc, char **argv) { } // Add the file path to the romFiles entry - romFiles.find(&romDirectories.back())->second.emplace_back(file.path()); + romFiles.find(&romDirectories.back())->second.emplace_back(romFile.path().string()); } } } else { - config_t cfg; - config_init(&cfg); - - config_setting_t *root = config_root_setting(&cfg); - config_setting_t *list = config_setting_add(root, "directories", CONFIG_TYPE_LIST); + try { + config.getRoot().add("directories", libconfig::Setting::TypeList); + config.writeFile(configFilePath.c_str()); - if (config_write_file(&cfg, configFilePath.c_str()) == CONFIG_FALSE) { - std::cerr << "Error creating configuration file." << std::endl; - config_destroy(&cfg); + std::cout << "Configuration file created successfully." << std::endl; + } catch (const libconfig::FileIOException &ioException) { + std::cerr << "I/O error while writing the configuration file." << std::endl; + return 1; + } catch (const libconfig::SettingException &settingException) { + std::cerr << "Setting error: " << settingException.what() << std::endl; return 1; } + } - std::cout << "Configuration file created successfully." << std::endl; + std::vector> windows; + MainMenu window(configFilePath, romFiles, romDirectories, windows); + windows.emplace_back(&window); + window.init(); - config_destroy(&cfg); - } + bool quit = false; + SDL_Event event; - if (quickRom) { - Emulator emulator; - return emulator.launch(rom); - } else { - MainMenu(romFiles, romDirectories, windows, configFilePath); + while (!quit) { + while (SDL_PollEvent(&event)) { + if (event.type == SDL_EVENT_QUIT) { + quit = true; + } + + for (const auto& window : windows) { + window->handleEvent(event); + } + } + + for (const auto& windowPtr : windows) { + Window* window = windowPtr.get(); + std::cout << "start " << window << std::endl; + window->update(); + std::cout << "middle" << std::endl; + window->render(); + std::cout << "enmd" << std::endl; + } + + bool allWindowsClosed = true; + for (const auto& window : windows) { + if(window->isShown()){ + allWindowsClosed = false; + break; + } + } + + if (allWindowsClosed) { + quit = true; + } + + std::cout << "end loop e" << std::endl; } return 0; diff --git a/src/sdl/CMakeLists.txt b/src/sdl/CMakeLists.txt index b7a3c5c..a4b1f3e 100644 --- a/src/sdl/CMakeLists.txt +++ b/src/sdl/CMakeLists.txt @@ -1,5 +1,13 @@ target_sources(8ChocChip PRIVATE - sdl.cpp - sdl.h) + Emulator.cpp + Emulator.h + MainMenu.cpp + MainMenu.h + InputHandler.cpp + InputHandler.h + Window.cpp + Window.h +) -add_subdirectory(emulator) \ No newline at end of file +add_subdirectory(emulator) +add_subdirectory(ui) \ No newline at end of file diff --git a/src/sdl/Emulator.cpp b/src/sdl/Emulator.cpp new file mode 100644 index 0000000..9781fae --- /dev/null +++ b/src/sdl/Emulator.cpp @@ -0,0 +1,65 @@ +#include "Emulator.h" + +Emulator::Emulator(const std::string &rom) : cpu(&renderWrapper, &keyboard, &speaker), rom(rom) {} + +void Emulator::init() { + Window::init(); + + if (this->rom.empty()) { + std::cerr << "No ROM file has been specified :(" << std::endl; + return; + } + + std::ifstream file(this->rom, std::ios::binary | std::ios::ate); + if (!file.good()) { + std::cerr << "Can not find file " << this->rom << std::endl; + return; + } + + cpu.loadSpritesIntoMemory(); + + cpu.loadProgramIntoMemory(&file); +} + +bool Emulator::handleEvent(SDL_Event& event) { + if (!Window::handleEvent(event)) { + return false; + } + + switch (event.type) { + case SDL_EVENT_KEY_DOWN: + keyboard.handleKeyDown(event.key.scancode); + break; + case SDL_EVENT_KEY_UP: + keyboard.handleKeyUp(event.key.scancode); + break; + } + + return true; +} + +// void Emulator::focus() { +// if (!mShown) { +// SDL_ShowWindow(mWindow); +// } +// +// SDL_RaiseWindow(mWindow); +// } + +void Emulator::update() { + // Run a cycle of the emulator + this->cpu.cycle(); +} + +void Emulator::render() { + // Set renderer draw color + SDL_SetRenderDrawColor(this->renderer, 0, 0, 0, 0); + // Clear the window + SDL_RenderClear(this->renderer); + + // Render the window + this->renderWrapper.render(this->renderer); + SDL_RenderPresent(this->renderer); + + SDL_Delay(16); +} diff --git a/src/sdl/Emulator.h b/src/sdl/Emulator.h new file mode 100644 index 0000000..5631254 --- /dev/null +++ b/src/sdl/Emulator.h @@ -0,0 +1,28 @@ +#ifndef INC_8CHOCCHIP_SDL_H +#define INC_8CHOCCHIP_SDL_H + +#include "Window.h" +#include "../emulator/Cpu.h" +#include "emulator/SdlKeyboard.h" +#include "emulator/SdlRenderer.h" +#include "emulator/SdlSpeaker.h" + +class Emulator : public Window { +private: + SdlRenderer renderWrapper; + SdlSpeaker speaker; + SdlKeyboard keyboard; + Cpu cpu; + + const std::string &rom; +public: + Emulator(const std::string &rom); + + void init() override; + bool handleEvent(SDL_Event& event) override; + void update() override; + void render() override; +}; + + +#endif \ No newline at end of file diff --git a/src/sdl/InputHandler.cpp b/src/sdl/InputHandler.cpp index 17254a5..da10154 100644 --- a/src/sdl/InputHandler.cpp +++ b/src/sdl/InputHandler.cpp @@ -3,13 +3,13 @@ #include #include -void InputHandler::addKey(sf::Keyboard::Key key) { +void InputHandler::addKey(SDL_Keycode key) { removeKey(key); // Do this because when a button is held down it adds many into the list and breaks functionality\ // If the key is already in the list this will remove it, if not it wont do anything this->keys.emplace_back(key); } -void InputHandler::removeKey(sf::Keyboard::Key key) { +void InputHandler::removeKey(SDL_Keycode key) { auto it = std::find(this->keys.begin(), this->keys.end(), key); if (it != this->keys.end()) { this->keys.erase(it); @@ -23,20 +23,20 @@ void InputHandler::updateLastKeys() { } } -bool InputHandler::isJustPressed(sf::Keyboard::Key key) { +bool InputHandler::isJustPressed(SDL_Keycode key) { return std::count(this->keys.begin(), this->keys.end(), key) > 0 && std::count(this->lastKeys.begin(), this->lastKeys.end(), key) == 0; } -bool InputHandler::isPressed(sf::Keyboard::Key key) { +bool InputHandler::isPressed(SDL_Keycode key) { return std::count(this->keys.begin(), this->keys.end(), key); } -void InputHandler::addButton(sf::Mouse::Button button) { +void InputHandler::addButton(SDL_Keycode button) { removeButton(button); this->mouse.emplace_back(button); } -void InputHandler::removeButton(sf::Mouse::Button button) { +void InputHandler::removeButton(SDL_Keycode button) { auto it = std::find(this->mouse.begin(), this->mouse.end(), button); if (it != this->mouse.end()) { this->mouse.erase(it); @@ -50,10 +50,10 @@ void InputHandler::updateLastMouse() { } } -bool InputHandler::isJustClicked(sf::Mouse::Button button) { +bool InputHandler::isJustClicked(SDL_Keycode button) { return std::count(this->mouse.begin(), this->mouse.end(), button) && !std::count(this->lastMouse.begin(), this->lastMouse.end(), button); } -bool InputHandler::isClicked(sf::Mouse::Button button) { +bool InputHandler::isClicked(SDL_Keycode button) { return std::count(this->mouse.begin(), this->mouse.end(), button); } \ No newline at end of file diff --git a/src/sdl/InputHandler.h b/src/sdl/InputHandler.h index eb01dfc..f3d615e 100644 --- a/src/sdl/InputHandler.h +++ b/src/sdl/InputHandler.h @@ -1,33 +1,31 @@ #ifndef INC_8CHOCCHIP_INPUTHANDLER_H #define INC_8CHOCCHIP_INPUTHANDLER_H +#include #include -#include "SFML/Window/Keyboard.hpp" -#include "SFML/Window/Mouse.hpp" - class InputHandler { private: - std::vector keys; - std::vector mouse; + std::vector keys; + std::vector mouse; - std::vector lastKeys; - std::vector lastMouse; + std::vector lastKeys; + std::vector lastMouse; public: InputHandler() = default; - void addKey(sf::Keyboard::Key key); - void removeKey(sf::Keyboard::Key key); + void addKey(SDL_Keycode key); + void removeKey(SDL_Keycode key); void updateLastKeys(); - bool isJustPressed(sf::Keyboard::Key key); - bool isPressed(sf::Keyboard::Key key); + bool isJustPressed(SDL_Keycode key); + bool isPressed(SDL_Keycode key); - void addButton(sf::Mouse::Button button); - void removeButton(sf::Mouse::Button button); + void addButton(SDL_Keycode button); + void removeButton(SDL_Keycode button); void updateLastMouse(); - bool isJustClicked(sf::Mouse::Button button); - bool isClicked(sf::Mouse::Button button); + bool isJustClicked(SDL_Keycode button); + bool isClicked(SDL_Keycode button); }; -#endif // INC_8CHOCCHIP_INPUTHANDLER_H +#endif \ No newline at end of file diff --git a/src/sdl/MainMenu.cpp b/src/sdl/MainMenu.cpp new file mode 100644 index 0000000..8bd8f6e --- /dev/null +++ b/src/sdl/MainMenu.cpp @@ -0,0 +1,133 @@ +#include "MainMenu.h" + +#include +#include +#include +#include + +#include +#include + +#include "../util/MiscUtil.h" +#include "Emulator.h" + +#define WIDTH (64 * 15) +#define HEIGHT (32 * 15) + +MainMenu::MainMenu(std::string configFilePath, std::unordered_map> &romFiles, + std::vector &romDirectories, std::vector> &windows) : + configFilePath(configFilePath), romDirectories(romDirectories), romFiles(romFiles), windows(windows), + chooseFolder(0, 400, WIDTH, 80, "Select ROM") {} + +void MainMenu::init() { + Window::init(); + + for (auto& thing: this->romFiles) { + for (std::string& file: thing.second) { + + TextButton romButton(0, 25.0f * this->roms.size(), WIDTH, 25, MiscUtil::getFileFromPath(file)); + + this->roms.emplace(file, romButton); + } + } +} + +bool MainMenu::handleEvent(SDL_Event &event) { + if (!Window::handleEvent(event)) { + return false; + } + + switch (event.type) { + case SDL_EVENT_KEY_DOWN: + this->inputHandler.addKey(event.key.scancode); + break; + case SDL_EVENT_KEY_UP: + this->inputHandler.removeKey(event.key.scancode); + break; + case SDL_EVENT_MOUSE_BUTTON_DOWN: + this->inputHandler.addButton(event.button.button); + break; + case SDL_EVENT_MOUSE_BUTTON_UP: + this->inputHandler.removeButton(event.button.button); + break; + } + + return true; +} + +void MainMenu::update() { + SDL_FPoint point{}; + SDL_GetMouseState(&point.x, &point.y); + + if (inputHandler.isJustPressed(SDL_SCANCODE_F3)) { + this->debug = !this->debug; + } + + for (auto &romButton: roms) { + romButton.second.update(this->inputHandler, point); + + if (!romButton.second.isJustClicked()) { + continue; + } + + this->windows.emplace_back(std::make_unique(romButton.first))->init(); + std::cout << 1 << std::endl; + } + this->chooseFolder.update(this->inputHandler, point); + + if (this->chooseFolder.isJustClicked()) { + + if (nfdresult_t result = PickFolder(this->outPath); result == NFD_OKAY) { + libconfig::Config config; + config.readFile(this->configFilePath); + + libconfig::Setting &settings = config.getRoot(); + + if (!settings.exists("directories")) { + settings.add("directories", libconfig::Setting::TypeArray); + } + + libconfig::Setting &directories = settings["directories"]; + directories.add(libconfig::Setting::TypeString) = outPath.get(); + + romDirectories.emplace_back(outPath.get()); + + for (const auto &file: std::filesystem::directory_iterator(outPath.get())) { + if (file.is_directory()) { + continue; // TODO: Make sure its a file that can be emulated, at least basic checks so it isn't like + // a word doc + } + + printf("Processing file - : %s\n", file.path().c_str()); + + // Check if the rom directory doesn't exist in romFiles, then add it + if (romFiles.find(&romDirectories.back()) == romFiles.end()) { + romFiles.emplace(&romDirectories.back(), std::vector()); + } + + // Add the file path to the romFiles entry + romFiles.find(&romDirectories.back())->second.emplace_back(file.path().string()); + + TextButton romButton(0, 25.0f * roms.size(), WIDTH, 25, file.path().filename().string()); + roms.emplace(file.path().string(), romButton); + } + config.writeFile(configFilePath); + } + } + + this->inputHandler.updateLastKeys(); + this->inputHandler.updateLastMouse(); + + std::cout << 2 << std::endl; +} + +void MainMenu::render() { + SDL_SetRenderDrawColor(this->renderer, 0x00, 0x00, 0x00, 0x00); + SDL_RenderClear(renderer); + for (auto &romButton: roms) { + romButton.second.draw(renderer); + } + this->chooseFolder.draw(renderer); + + SDL_RenderPresent(renderer); +} diff --git a/src/sdl/MainMenu.h b/src/sdl/MainMenu.h new file mode 100644 index 0000000..25dedd1 --- /dev/null +++ b/src/sdl/MainMenu.h @@ -0,0 +1,38 @@ +#ifndef MAINMENU_H +#define MAINMENU_H + +#include +#include + +#include + +#include "Emulator.h" +#include "InputHandler.h" +#include "Window.h" +#include "ui/TextButton.h" + +class MainMenu : public Window { +private: + std::string configFilePath; + std::vector& romDirectories; + std::unordered_map>& romFiles; + std::vector>& windows; + + NFD::Guard nfdGuard; + NFD::UniquePath outPath; + + std::unordered_map roms; + TextButton chooseFolder; + + InputHandler inputHandler{}; +public: + MainMenu(std::string configFilePath, std::unordered_map>& romFiles, + std::vector& romDirectories, std::vector>& windows); + + void init() override; + bool handleEvent(SDL_Event& event) override; + void update() override; + void render() override; +}; + +#endif \ No newline at end of file diff --git a/src/sdl/Window.cpp b/src/sdl/Window.cpp new file mode 100644 index 0000000..9e0cc7f --- /dev/null +++ b/src/sdl/Window.cpp @@ -0,0 +1,99 @@ +#include "Window.h" + +#include + +Window::Window() { +} + +Window::~Window() { + +} + +void Window::init() { + if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0) { + std::cerr << "SDL could not initialize! SDL_Error: " << SDL_GetError() << std::endl; + return; + } + + if (!SDL_CreateWindowAndRenderer("8ChocChip - CHIP-8 Emulator", 64 * 15, 32 * 15, SDL_WINDOW_RESIZABLE, &this->window, &this->renderer)) { + std::cerr << "Couldn't create window and renderer. SDL_Error: " << SDL_GetError() << std::endl; + return; + } + + // Set window icon + SDL_Surface* icon = SDL_LoadBMP("assets/icon.bmp"); + if (!icon) { + std::cerr << "Icon could not be loaded! SDL_Error: " << SDL_GetError() << std::endl; + return; + } + SDL_SetWindowIcon(this->window, icon); + SDL_DestroySurface(icon); + + this->windowId = SDL_GetWindowID(this->window); + this->shown = true; +} + +bool Window::handleEvent(SDL_Event &event) { + if (event.window.windowID == this->windowId) { + bool caption = false; + + switch (event.type) { + case SDL_EVENT_WINDOW_SHOWN: + this->shown = true; + break; + case SDL_EVENT_WINDOW_HIDDEN: + this->shown = false; + break; + case SDL_EVENT_WINDOW_RESIZED: + //TODO + break; + case SDL_EVENT_WINDOW_EXPOSED: + SDL_RenderPresent(this->renderer); + break; + case SDL_EVENT_WINDOW_MOUSE_ENTER: + this->mouseFocus = true; + caption = true; + break; + case SDL_EVENT_WINDOW_MOUSE_LEAVE: + this->mouseFocus = false; + caption = false; + break; + case SDL_EVENT_WINDOW_FOCUS_GAINED: + this->keyboardFocus = true; + caption = true; + break; + case SDL_EVENT_WINDOW_FOCUS_LOST: + this->keyboardFocus = false; + caption = true; + break; + case SDL_EVENT_WINDOW_MINIMIZED: + this->minimised = true; + break; + case SDL_EVENT_WINDOW_MAXIMIZED: + this->minimised = false; + break; + case SDL_EVENT_WINDOW_RESTORED: + this->minimised = false; + break; + case SDL_EVENT_WINDOW_CLOSE_REQUESTED: + SDL_HideWindow(this->window); + break; + } + + return true; + } + + return false; +} + + +void Window::update() { +} + +void Window::close() { + +} + +bool Window::isShown() { + return true; +} diff --git a/src/sdl/Window.h b/src/sdl/Window.h new file mode 100644 index 0000000..d835520 --- /dev/null +++ b/src/sdl/Window.h @@ -0,0 +1,35 @@ +#ifndef WINDOW_H +#define WINDOW_H + +#include + +class Window { +protected: + SDL_Window* window = nullptr; + SDL_Renderer* renderer = nullptr; + unsigned int windowId = -1; + + int height = 720; + int width = 480; + + bool mouseFocus = false; + bool keyboardFocus = false; + bool fullscreen = false; + bool minimised = false; + bool shown = false; + + bool debug = false; +public: + Window(); + virtual ~Window(); + + virtual void init(); + virtual bool handleEvent(SDL_Event& event); + virtual void update(); + virtual void render() = 0; + void close(); + + bool isShown(); +}; + +#endif \ No newline at end of file diff --git a/src/sdl/emulator/SdlKeyboard.h b/src/sdl/emulator/SdlKeyboard.h index 7950b17..3e09c58 100644 --- a/src/sdl/emulator/SdlKeyboard.h +++ b/src/sdl/emulator/SdlKeyboard.h @@ -6,27 +6,27 @@ class SdlKeyboard : public Keyboard { private: - std::unordered_map KEYMAP = { - {49, 0x1}, // 1 - {50, 0x2}, // 2 - {51, 0x3}, // 3 - {52, 0xc}, // 4 - {113, 0x4}, // Q - {119, 0x5}, // W - {101, 0x6}, // E - {114, 0xD}, // R - {97, 0x7}, // A - {115, 0x8}, // S - {100, 0x9}, // D - {102, 0xE}, // F - {122, 0xA}, // Z - {120, 0x0}, // X - {99, 0xB}, // C - {118, 0xF} // V - }; + // std::unordered_map KEYMAP = { + // {49, 0x1}, // 1 + // {50, 0x2}, // 2 + // {51, 0x3}, // 3 + // {52, 0xc}, // 4 + // {113, 0x4}, // Q + // {119, 0x5}, // W + // {101, 0x6}, // E + // {114, 0xD}, // R + // {97, 0x7}, // A + // {115, 0x8}, // S + // {100, 0x9}, // D + // {102, 0xE}, // F + // {122, 0xA}, // Z + // {120, 0x0}, // X + // {99, 0xB}, // C + // {118, 0xF} // V + // }; public: - void handleKeyDown(uint8_t keyCode) override; - void handleKeyUp(uint8_t keyCode) override; + void handleKeyDown(uint8_t keyCode); + void handleKeyUp(uint8_t keyCode); }; #endif // INC_8CHOCCHIP_SDLKEYBOARD_H diff --git a/src/sdl/emulator/SdlRenderer.cpp b/src/sdl/emulator/SdlRenderer.cpp index 7ba78b3..0657e81 100644 --- a/src/sdl/emulator/SdlRenderer.cpp +++ b/src/sdl/emulator/SdlRenderer.cpp @@ -1,17 +1,14 @@ #include "SdlRenderer.h" -SdlRenderer::SdlRenderer(SDL_Renderer* window) { - this->window = window; +#include +#include - this->scale = 15; // Scale up because 64 x 32 would be tiny on our screens now +void SdlRenderer::drawPixel(SDL_Renderer* renderer, uint16_t x, uint16_t y) { + const SDL_FRect rect = {x * scale, y * scale, scale, scale}; + SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255); // Set color to white + SDL_RenderFillRect(renderer, &rect); } -void SdlRenderer::drawPixel(uint16_t x, uint16_t y) { - SDL_Rect rect = {x * scale, y * scale, scale, scale}; - SDL_SetRenderDrawColor(window, 255, 255, 255, 255); // Set color to white - SDL_RenderFillRect(window, &rect); -} - -uint8_t SdlRenderer::getScale() const { +float SdlRenderer::getScale() const { return this->scale; } \ No newline at end of file diff --git a/src/sdl/emulator/SdlRenderer.h b/src/sdl/emulator/SdlRenderer.h index 9809cf2..4572f52 100644 --- a/src/sdl/emulator/SdlRenderer.h +++ b/src/sdl/emulator/SdlRenderer.h @@ -2,18 +2,14 @@ #define INC_8CHOCCHIP_SDLRENDERER_H #include "../../emulator/Renderer.h" -#include "SDL2/SDL.h" +#include class SdlRenderer : public Renderer { private: - SDL_Renderer* window; - - uint8_t scale; + float scale = 15; public: - SdlRenderer(SDL_Renderer* window); - - void drawPixel(uint16_t x, uint16_t y) override; - uint8_t getScale() const; + void drawPixel(SDL_Renderer* renderer, uint16_t x, uint16_t y) override; + float getScale() const; }; -#endif // INC_8CHOCCHIP_SDLRENDERER_H +#endif \ No newline at end of file diff --git a/src/sdl/emulator/SdlSpeaker.cpp b/src/sdl/emulator/SdlSpeaker.cpp index 553f1b7..ebe3f5d 100644 --- a/src/sdl/emulator/SdlSpeaker.cpp +++ b/src/sdl/emulator/SdlSpeaker.cpp @@ -1,13 +1,14 @@ #include "SdlSpeaker.h" -#include -SdlSpeaker::SdlSpeaker() { +#include -} +SdlSpeaker::SdlSpeaker() = default; void SdlSpeaker::play() { + std::cout <<"sp" << std::endl; } void SdlSpeaker::stop() { + std::cout <<"sp1" << std::endl; } diff --git a/src/sdl/emulator/SdlSpeaker.h b/src/sdl/emulator/SdlSpeaker.h index f7941ee..539b1e9 100644 --- a/src/sdl/emulator/SdlSpeaker.h +++ b/src/sdl/emulator/SdlSpeaker.h @@ -12,4 +12,4 @@ class SdlSpeaker : public Speaker { void stop() override; }; -#endif // INC_8CHOCCHIP_SDLSPEAKER_H +#endif \ No newline at end of file diff --git a/src/sdl/sdl.cpp b/src/sdl/sdl.cpp deleted file mode 100644 index 8e04c66..0000000 --- a/src/sdl/sdl.cpp +++ /dev/null @@ -1,145 +0,0 @@ -#include "sdl.h" - -#include -#include -#include - -#include "../emulator/Cpu.h" -#include "emulator/SdlKeyboard.h" -#include "emulator/SdlRenderer.h" -#include "emulator/SdlSpeaker.h" - -#include "SDL2/SDL.h" - -int sdl::launch(const std::string &rom) { - if (rom.empty()) { - std::cerr << "No ROM file has been specified :(" << std::endl; - return 1; - } - - std::ifstream file(rom, std::ios::binary | std::ios::ate); - if (!file.good()) { - std::cerr << "Can not find file " << rom << std::endl; - return 1; - } - - if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0) { - std::cerr << "SDL could not initialize! SDL_Error: " << SDL_GetError() << std::endl; - return -1; - } - - SDL_Window *window = SDL_CreateWindow("8ChocChip - CHIP-8 Emulator", SDL_WINDOWPOS_UNDEFINED, - SDL_WINDOWPOS_UNDEFINED, 64 * 15, 32 * 15, SDL_WINDOW_SHOWN); - - if (window == nullptr) { - std::cerr << "Window could not be created! SDL_Error: " << SDL_GetError() << std::endl; - return -1; - } - - // Set window icon - SDL_Surface *icon = SDL_LoadBMP("../../assets/icon.bmp"); - if (icon == nullptr) { - std::cerr << "Icon could not be loaded! SDL_Error: " << SDL_GetError() << std::endl; - return -1; - } - SDL_SetWindowIcon(window, icon); - SDL_FreeSurface(icon); - - // Create renderer - SDL_Renderer *windowRenderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED); - if (windowRenderer == nullptr) { - std::cerr << "Renderer could not be created! SDL_Error: " << SDL_GetError() << std::endl; - return -1; - } - - SdlRenderer renderer(windowRenderer); - SdlSpeaker speaker; - SdlKeyboard keyboard; - Cpu cpu(&renderer, &keyboard, &speaker); - - cpu.loadSpritesIntoMemory(); - - cpu.loadProgramIntoMemory(&file); - - bool focus; - - auto start = std::chrono::high_resolution_clock::now(); - auto end = std::chrono::high_resolution_clock::now(); - std::chrono::duration elapsed_seconds; - - // Variables to count frames - int frames = 0; - double fps; - - const int SCREEN_TICKS_PER_FRAME = 1000 / 60; // Time per frame in milliseconds - - Uint32 startTicks; - - while (true) { - startTicks = SDL_GetTicks(); - - SDL_Event event; - while (SDL_PollEvent(&event)) { - if (event.type == SDL_QUIT) { - SDL_DestroyRenderer(windowRenderer); - SDL_DestroyWindow(window); - SDL_Quit(); - return 0; - } else if (event.type == SDL_WINDOWEVENT) { - if (event.window.event == SDL_WINDOWEVENT_FOCUS_GAINED) { - focus = true; - } else if (event.window.event == SDL_WINDOWEVENT_FOCUS_LOST) { - focus = false; - } - } - - if (!focus) continue; - - // Handle keyboard inputs - if (event.type == SDL_KEYDOWN) { - keyboard.handleKeyDown(event.key.keysym.sym); - } else if (event.type == SDL_KEYUP) { - keyboard.handleKeyUp(event.key.keysym.sym); - } - } - - // Run a cycle of the emulator - cpu.cycle(); - - // Set renderer draw color - SDL_SetRenderDrawColor(windowRenderer, 0, 0, 0, 255); - // Clear the window - SDL_RenderClear(windowRenderer); - - // Render the window - renderer.render(); - SDL_RenderPresent(windowRenderer); - - // Calculate time spent in frame - int frameTicks = SDL_GetTicks() - startTicks; - - // If frame finished early - if (frameTicks < SCREEN_TICKS_PER_FRAME) { - // Wait remaining time - SDL_Delay(SCREEN_TICKS_PER_FRAME - frameTicks); - } - - frames++; - - end = std::chrono::high_resolution_clock::now(); - elapsed_seconds = end - start; - - // If elapsed time is greater than or equal to 1 second - if (elapsed_seconds.count() >= 1.0) { - // Calculate FPS - fps = frames / elapsed_seconds.count(); - - // Output FPS - std::cout << "FPS: " << fps << std::endl; - - // Reset frame count and start time - frames = 0; - start = std::chrono::high_resolution_clock::now(); - } - } -} \ No newline at end of file diff --git a/src/sdl/sdl.h b/src/sdl/sdl.h deleted file mode 100644 index c816ac2..0000000 --- a/src/sdl/sdl.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef INC_8CHOCCHIP_SDL_H -#define INC_8CHOCCHIP_SDL_H - -#include - -class sdl { -public: - int launch(const std::string &rom); -}; - - -#endif // INC_8CHOCCHIP_SDL_H diff --git a/src/sdl/ui/CMakeLists.txt b/src/sdl/ui/CMakeLists.txt new file mode 100644 index 0000000..79ff477 --- /dev/null +++ b/src/sdl/ui/CMakeLists.txt @@ -0,0 +1,4 @@ +target_sources(8ChocChip PRIVATE + TextButton.cpp + TextButton.h +) \ No newline at end of file diff --git a/src/sdl/ui/TextButton.cpp b/src/sdl/ui/TextButton.cpp new file mode 100644 index 0000000..2ca470c --- /dev/null +++ b/src/sdl/ui/TextButton.cpp @@ -0,0 +1,73 @@ +#include "TextButton.h" + +#include + +TextButton::TextButton(float x, float y, float width, float height, const std::string& buttonText) : text(text) { + this->button = SDL_FRect{x, y, width, height}; + // this->button.setPosition(sf::Vector2f(x, y)); + + // this->originalPosition = this->button.getPosition(); + // this->originalSize = this->button.getSize(); + + // this->text.setFont(*font); + // this->text.setString(buttonText); + // this->text.setCharacterSize(20); + // this->text.setFillColor(sf::Color::Black); + // this->text.setPosition(x + width / 2 - this->text.getGlobalBounds().width / 2, y + height / 2 - this->text.getGlobalBounds().height / 2); + + this->idleColor = SDL_Color{192, 192, 192}; + this->hoverColor = SDL_Color{128, 128, 128}; + this->activeColor = SDL_Color{64, 64, 64}; + + // this->button.setFillColor(this->idleColor); + + this->isPressed = false; + this->isHovered = false; +} + +void TextButton::updateSize(const SDL_Point originalSize, const SDL_Point updatedSize) { + // this->button.setSize(SDL_Point(this->originalSize.x / originalSize.x * updatedSize.x, this->originalSize.y / originalSize.y * updatedSize.y)); + // this->button.setPosition(this->originalPosition.x / originalSize.x * updatedSize.x, this->originalPosition.y / originalSize.y * updatedSize.y); + + // this->text.setPosition(this->button.getPosition().x + this->button.getSize().x / 2 - this->text.getGlobalBounds().width / 2, this->button.getPosition().y + this->button.getSize().y / 2 - this->text.getGlobalBounds().height / 2); +} + +void TextButton::update(InputHandler& inputHandler, SDL_FPoint pos) { + this->lastPressed = this->isPressed; + this->isHovered = SDL_PointInRectFloat(&pos, &this->button); + + if (this->isHovered && inputHandler.isJustClicked(SDL_BUTTON_LEFT)) { + this->isPressed = true; + updateColour(this->activeColor); + } else if (this->isHovered && inputHandler.isPressed(SDL_KMOD_LSHIFT)) { + updateColour( SDL_Color{255, 0, 0}); + } else if (this->isHovered) { + updateColour(this->hoverColor); + } else { + this->isPressed = false; + updateColour(this->idleColor); + } +} + +void TextButton::updateColour(const SDL_Color color) { + if (this->color.r == color.r && this->color.g == color.g && this->color.b == color.b) { + return; + } + + this->currentColor = color; +} + +void TextButton::draw(SDL_Renderer* window) const { + // window.draw(this->button); + + SDL_SetRenderDrawColor(window, this->currentColor.r, this->currentColor.g, this->currentColor.b, this->currentColor.a); + SDL_RenderFillRect(window, &this->button); +} + +bool TextButton::isClicked() const { + return this->isPressed; +} + +bool TextButton::isJustClicked() const { + return !this->lastPressed && this->isPressed; +} \ No newline at end of file diff --git a/src/sdl/ui/TextButton.h b/src/sdl/ui/TextButton.h new file mode 100644 index 0000000..37ce40c --- /dev/null +++ b/src/sdl/ui/TextButton.h @@ -0,0 +1,43 @@ +#ifndef TEXTBUTTON_H +#define TEXTBUTTON_H + +#include "../InputHandler.h" +#include + +#include + +class TextButton { +private: + SDL_FRect button; + SDL_Point originalSize; + SDL_Point originalPosition; + std::string& text; + // sf::Font* font{}; + SDL_Color color; + SDL_Color idleColor; + SDL_Color hoverColor; + SDL_Color activeColor; + SDL_Color currentColor; + bool isPressed; + bool lastPressed{}; + bool isHovered; + +public: + TextButton(float x, float y, float width, float height, const std::string& buttonText); + + void updateSize(SDL_Point originalSize, SDL_Point updatedSize); + + void update(InputHandler& inputHandler, SDL_FPoint pos); + + void draw(SDL_Renderer* window) const; + + bool isClicked() const; + + bool isJustClicked() const; + + void updateColour(SDL_Color color); +}; + + + +#endif \ No newline at end of file diff --git a/src/sfml/CMakeLists.txt b/src/sfml/CMakeLists.txt deleted file mode 100644 index 6e1d4d1..0000000 --- a/src/sfml/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ -target_sources(8ChocChip PRIVATE - MainMenu.cpp - MainMenu.h - Emulator.cpp - Emulator.h - InputHandler.cpp - InputHandler.h) - -add_subdirectory(ui) -add_subdirectory(emulator) \ No newline at end of file diff --git a/src/sfml/Emulator.cpp b/src/sfml/Emulator.cpp deleted file mode 100644 index 834bd84..0000000 --- a/src/sfml/Emulator.cpp +++ /dev/null @@ -1,77 +0,0 @@ -#include "Emulator.h" - -#include -#include - -#include "../emulator/Cpu.h" -#include "../emulator/Keyboard.h" -#include "emulator/SfmlRenderer.h" -#include "emulator/SfmlSpeaker.h" - -int Emulator::launch(const std::string &rom) { - if (rom.empty()) { - std::cerr << "No ROM file has been specified :(" << std::endl; - return 1; - } - - std::ifstream file(rom, std::ios::binary | std::ios::ate); - if (!file.good()) { - std::cerr << "Can not find file " << rom << std::endl; - return 1; - } - - sf::RenderWindow window(sf::VideoMode(64 * 15, 32 * 15), "8ChocChip - CHIP-8 Emulator", sf::Style::Titlebar | sf::Style::Close); - sf::Image icon; - icon.loadFromFile("../../assets/icon.png"); - window.setIcon(64, 64, icon.getPixelsPtr()); - window.setFramerateLimit(60); - - SfmlRenderer renderer(&window); - - SfmlSpeaker speaker; - Keyboard keyboard; - Cpu cpu(&renderer, &keyboard, &speaker); - - cpu.loadSpritesIntoMemory(); - - cpu.loadProgramIntoMemory(&file); - - bool focus; - - while (window.isOpen()) { - sf::Event event{}; - while (window.pollEvent(event)) { - if (event.type == sf::Event::Closed) { - window.close(); - } else if (event.type == sf::Event::LostFocus) { - focus = false; - } else if (event.type == sf::Event::GainedFocus) { - focus = true; - } - - if (!focus) continue; - - // TODO: You can hold down the button and it will run at the next possible point - // fix this but make it a little easier to time because otherwise you need to time it perfectly which doesnt feel good - // ^ if the InputHandler was to be used anyway - // Handle keyboard inputs - if (event.type == sf::Event::KeyPressed) { - keyboard.handleKeyDown(event.key.code); - } else if (event.type == sf::Event::KeyReleased) { - keyboard.handleKeyUp(event.key.code); - } - } - - // Run a cycle of the emulator - cpu.cycle(); - - // Clear the window - window.clear(sf::Color::Black); - - // Render the window - renderer.render(); - window.display(); - } - - return 0; -} diff --git a/src/sfml/Emulator.h b/src/sfml/Emulator.h deleted file mode 100644 index 46ed1cc..0000000 --- a/src/sfml/Emulator.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef INC_8CHOCCHIP_EMULATOR_H -#define INC_8CHOCCHIP_EMULATOR_H - -#include - -class Emulator { -private: -public: - int launch(const std::string& rom); -}; - - -#endif // INC_8CHOCCHIP_EMULATOR_H diff --git a/src/sfml/MainMenu.cpp b/src/sfml/MainMenu.cpp deleted file mode 100644 index eb13f24..0000000 --- a/src/sfml/MainMenu.cpp +++ /dev/null @@ -1,174 +0,0 @@ -#include "MainMenu.h" - -#include -#include - -#include "libconfig.h++" -#include "nfd.hpp" - -#include "Emulator.h" -#include "ui/TextButton.h" -#include "../util/MiscUtil.h" - -MainMenu::MainMenu(std::unordered_map>& romFiles, - std::vector& romDirectories, std::vector>& windows, - std::string configFilePath) : - window(sf::VideoMode(640, 480), "8ChocChip - Chip8 Emulator"), windows(windows), - romDirectories(romDirectories), romFiles(romFiles), inputHandler() { - - NFD::Guard nfdGuard; - - // auto-freeing memory - NFD::UniquePath outPath; - - sf::Vector2u originalWindowSize = this->window.getSize(); - sf::Image icon; - icon.loadFromFile("assets/icon.png"); - this->window.setIcon(64, 64, icon.getPixelsPtr()); -// this->window.setVerticalSyncEnabled(true); - - sf::Font font; - font.loadFromFile("assets/font.ttf"); - - std::unordered_map roms; - - for (auto& thing : romFiles) { - for (std::string& file : thing.second) { - - TextButton romButton(0, 25.0f * roms.size(), this->window.getSize().x, 25, MiscUtil::getFileFromPath(file), &font); - - roms.emplace(file, romButton); - } - } - - TextButton button(0, 400, 640, 80, "Select ROM", &font); - - bool focus; - bool debug = false; - - auto start = std::chrono::high_resolution_clock::now(); - auto end = std::chrono::high_resolution_clock::now(); - std::chrono::duration elapsed_seconds{}; - - // Variables to count frames - int frames = 0; - double fps; - - while (this->window.isOpen()) { - sf::Event event{}; - sf::Vector2i pos = sf::Mouse::getPosition(this->window); - - this->inputHandler.updateLastKeys(); - this->inputHandler.updateLastMouse(); - - while (this->window.pollEvent(event)) { - if (event.type == sf::Event::Closed) { - this->window.close(); - } else if (event.type == sf::Event::LostFocus) { - focus = false; - } else if (event.type == sf::Event::GainedFocus) { - focus = true; - } else if (event.type == sf::Event::Resized) { - sf::FloatRect visibleArea(0, 0, event.size.width, event.size.height); - this->window.setView(sf::View(visibleArea)); - - for (auto& romButton : roms) { - romButton.second.updateSize(originalWindowSize, this->window.getSize()); - romButton.second.update(&this->inputHandler, pos); - } - - button.updateSize(originalWindowSize, this->window.getSize()); - button.update(&this->inputHandler, pos); - } else if (event.type == sf::Event::KeyPressed) { - this->inputHandler.addKey(event.key.code); - } else if (event.type == sf::Event::KeyReleased) { - this->inputHandler.removeKey(event.key.code); - } else if (event.type == sf::Event::MouseButtonPressed) { - this->inputHandler.addButton(event.mouseButton.button); - } else if (event.type == sf::Event::MouseButtonReleased) { - this->inputHandler.removeButton(event.mouseButton.button); - } - } - - if (focus) { - if (this->inputHandler.isJustPressed(sf::Keyboard::F3)) { - debug = !debug; - } - - for (auto& romButton : roms) { - romButton.second.update(&this->inputHandler, pos); - - if (!romButton.second.isJustClicked()) continue; - - Emulator emulator; - std::thread newWindow(&Emulator::launch, &emulator, romButton.first); - newWindow.detach(); - windows.emplace_back(&newWindow); - } - button.update(&this->inputHandler, pos); - - if (button.isJustClicked()) { - - if (nfdresult_t result = PickFolder(outPath); result == NFD_OKAY) { - libconfig::Config config; - config.readFile(configFilePath); - - libconfig::Setting& settings = config.getRoot(); - - if (!settings.exists("directories")) { - settings.add("directories", libconfig::Setting::TypeArray); - } - - libconfig::Setting& directories = settings["directories"]; - directories.add(libconfig::Setting::TypeString) = outPath.get(); - - romDirectories.emplace_back(outPath.get()); - - for (const auto& file : std::filesystem::directory_iterator(outPath.get())) { - if (file.is_directory()) continue; // TODO: Make sure its a file that can be emulated, at least basic checks so it isn't like a word doc - - printf("Processing file - : %s\n", file.path().c_str()); - - // Check if the rom directory doesn't exist in romFiles, then add it - if (romFiles.find(&romDirectories.back()) == romFiles.end()) { - romFiles.emplace(&romDirectories.back(), std::vector()); - } - - // Add the file path to the romFiles entry - romFiles.find(&romDirectories.back())->second.emplace_back(file.path().string()); - - TextButton romButton(0, 25.0f * roms.size(), this->window.getSize().x, 25, file.path().filename().string(), &font); - roms.emplace(file.path().string(), romButton); - } - config.writeFile(configFilePath); - } - } - } - - this->window.clear(sf::Color::White); - for (auto& romButton : roms) { - romButton.second.draw(this->window); - } - button.draw(this->window); - - this->window.display(); - - frames++; - - end = std::chrono::high_resolution_clock::now(); - elapsed_seconds = end - start; - - // If elapsed time is greater than or equal to 1 second - if (debug && elapsed_seconds.count() >= 1.0) { - // Calculate FPS - fps = frames / elapsed_seconds.count(); - - // Output FPS - std::cout << "FPS: " << fps << std::endl; - - // Reset frame count and start time - frames = 0; - start = std::chrono::high_resolution_clock::now(); - } - } -} diff --git a/src/sfml/MainMenu.h b/src/sfml/MainMenu.h deleted file mode 100644 index 9d5ec51..0000000 --- a/src/sfml/MainMenu.h +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef INC_8CHOCCHIP_MAINMENU_H -#define INC_8CHOCCHIP_MAINMENU_H - -#include -#include - -#include "SFML/Graphics.hpp" -#include "InputHandler.h" - -class MainMenu { -private: - sf::RenderWindow window; - std::vector>& windows; - - std::vector& romDirectories; - std::unordered_map>& romFiles; - - InputHandler inputHandler; -public: - MainMenu(std::unordered_map>& romFiles, - std::vector& romDirectories, std::vector>& windows, - std::string configFilePath); -}; - - -#endif // INC_8CHOCCHIP_MAINMENU_H diff --git a/src/sfml/emulator/CMakeLists.txt b/src/sfml/emulator/CMakeLists.txt deleted file mode 100644 index 9c17dbd..0000000 --- a/src/sfml/emulator/CMakeLists.txt +++ /dev/null @@ -1,5 +0,0 @@ -target_sources(8ChocChip PRIVATE - SfmlSpeaker.cpp - SfmlSpeaker.h - SfmlRenderer.cpp - SfmlRenderer.h) \ No newline at end of file diff --git a/src/sfml/emulator/SfmlRenderer.cpp b/src/sfml/emulator/SfmlRenderer.cpp deleted file mode 100644 index cfbeacc..0000000 --- a/src/sfml/emulator/SfmlRenderer.cpp +++ /dev/null @@ -1,17 +0,0 @@ -#include "SfmlRenderer.h" - -SfmlRenderer::SfmlRenderer(sf::RenderWindow* window) : window(window) { - this->scale = 15; // Scale up because 64 x 32 would be tiny on our screens now -} - -void SfmlRenderer::drawPixel(const uint16_t x, const uint16_t y) { - sf::RectangleShape rectangle(sf::Vector2f(getScale(), getScale())); - rectangle.setPosition(x * getScale(), y * getScale()); - rectangle.setFillColor(sf::Color::White); - - this->window->draw(rectangle); -} - -uint8_t SfmlRenderer::getScale() const { - return this->scale; -} \ No newline at end of file diff --git a/src/sfml/emulator/SfmlSpeaker.cpp b/src/sfml/emulator/SfmlSpeaker.cpp deleted file mode 100644 index 280b5bd..0000000 --- a/src/sfml/emulator/SfmlSpeaker.cpp +++ /dev/null @@ -1,28 +0,0 @@ -#include "SfmlSpeaker.h" - -#include - -SfmlSpeaker::SfmlSpeaker() { - - this->sound.setVolume(100.f); - this->sound.setLoop(true); - - constexpr unsigned int sampleRate = 44100; - sf::Int16 samples[sampleRate]; - for (unsigned int i = 0; i < sampleRate; ++i) { - constexpr unsigned int frequency = 440; - samples[i] = static_cast(32767 * std::sin(2 * 3.14159265 * frequency * i / sampleRate)); // Mmmm yum pi - } - this->soundBuffer.loadFromSamples(samples, sampleRate, 1, sampleRate); - - this->sound.setBuffer(this->soundBuffer); -} - -void SfmlSpeaker::play() { - this->sound.play(); -} - -void SfmlSpeaker::stop() { - this->sound.stop(); -} - diff --git a/src/sfml/emulator/SfmlSpeaker.h b/src/sfml/emulator/SfmlSpeaker.h deleted file mode 100644 index 83888d8..0000000 --- a/src/sfml/emulator/SfmlSpeaker.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef INC_8CHOCCHIP_SFMLSPEAKER_H -#define INC_8CHOCCHIP_SFMLSPEAKER_H - -#include "SFML/Audio.hpp" - -#include "../../emulator/Speaker.h" - -class SfmlSpeaker : public Speaker { -private: - sf::SoundBuffer soundBuffer; - sf::Sound sound; -public: - SfmlSpeaker(); - - void play() override; - void stop() override; -}; - -#endif // INC_8CHOCCHIP_SFMLSPEAKER_H diff --git a/src/sfml/ui/TextButton.cpp b/src/sfml/ui/TextButton.cpp deleted file mode 100644 index 7c56856..0000000 --- a/src/sfml/ui/TextButton.cpp +++ /dev/null @@ -1,69 +0,0 @@ -#include "TextButton.h" - -TextButton::TextButton(float x, float y, float width, float height, const std::string& buttonText, sf::Font* font) { - this->button.setSize(sf::Vector2f(width, height)); - this->button.setPosition(sf::Vector2f(x, y)); - - this->originalPosition = this->button.getPosition(); - this->originalSize = this->button.getSize(); - - this->text.setFont(*font); - this->text.setString(buttonText); - this->text.setCharacterSize(20); - this->text.setFillColor(sf::Color::Black); - this->text.setPosition(x + width / 2 - this->text.getGlobalBounds().width / 2, y + height / 2 - this->text.getGlobalBounds().height / 2); - - this->idleColor = sf::Color(192, 192, 192); - this->hoverColor = sf::Color(128, 128, 128); - this->activeColor = sf::Color(64, 64, 64); - - this->button.setFillColor(this->idleColor); - - this->isPressed = false; - this->isHovered = false; -} - -void TextButton::updateSize(const sf::Vector2u originalSize, const sf::Vector2u updatedSize) { - this->button.setSize(sf::Vector2f(this->originalSize.x / originalSize.x * updatedSize.x, this->originalSize.y / originalSize.y * updatedSize.y)); - this->button.setPosition(this->originalPosition.x / originalSize.x * updatedSize.x, this->originalPosition.y / originalSize.y * updatedSize.y); - - this->text.setPosition(this->button.getPosition().x + this->button.getSize().x / 2 - this->text.getGlobalBounds().width / 2, this->button.getPosition().y + this->button.getSize().y / 2 - this->text.getGlobalBounds().height / 2); -} - -void TextButton::update(InputHandler* inputHandler, sf::Vector2i pos) { - this->lastPressed = this->isPressed; - this->isHovered = this->button.getGlobalBounds().contains(pos.x, pos.y); - - if (this->isHovered && inputHandler->isJustClicked(sf::Mouse::Left)) { - this->isPressed = true; - updateColour(this->activeColor); - } else if (this->isHovered && inputHandler->isPressed(sf::Keyboard::Key::LShift)) { - updateColour( sf::Color(255, 0, 0)); - } else if (this->isHovered) { - updateColour(this->hoverColor); - } else { - this->isPressed = false; - updateColour(this->idleColor); - } -} - -void TextButton::updateColour(const sf::Color color) { - if (this->color == color) { - return; - } - - this->button.setFillColor(color); -} - -void TextButton::draw(sf::RenderWindow &window) const { - window.draw(this->button); - window.draw(this->text); -} - -bool TextButton::isClicked() const { - return this->isPressed; -} - -bool TextButton::isJustClicked() const { - return !this->lastPressed && this->isPressed; -} \ No newline at end of file diff --git a/src/sfml/ui/TextButton.h b/src/sfml/ui/TextButton.h deleted file mode 100644 index 7e3491a..0000000 --- a/src/sfml/ui/TextButton.h +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef TEXTBUTTON_H -#define TEXTBUTTON_H - -#include "SFML/Graphics.hpp" - -#include "../InputHandler.h" - -class TextButton { -private: - sf::RectangleShape button; - sf::Vector2f originalSize; - sf::Vector2f originalPosition; - sf::Text text; - sf::Font* font{}; - sf::Color color; - sf::Color idleColor; - sf::Color hoverColor; - sf::Color activeColor; - bool isPressed; - bool lastPressed{}; - bool isHovered; - -public: - TextButton(float x, float y, float width, float height, const std::string& buttonText, sf::Font *font); - - void updateSize(sf::Vector2u originalSize, sf::Vector2u updatedSize); - - void update(InputHandler* inputHandler, sf::Vector2i pos); - - void draw(sf::RenderWindow& window) const; - - bool isClicked() const; - - bool isJustClicked() const; - - void updateColour(sf::Color color); -}; - - - -#endif //TEXTBUTTON_H From f15e77af0a22f65e64ff994946b158e8676ec173 Mon Sep 17 00:00:00 2001 From: doom <61824552+JustDoom@users.noreply.github.com> Date: Fri, 13 Dec 2024 12:16:07 +1000 Subject: [PATCH 05/28] Fix multiple windows crashing --- src/main.cpp | 16 ++++------------ src/sdl/MainMenu.cpp | 3 --- src/sdl/emulator/SdlKeyboard.cpp | 1 - 3 files changed, 4 insertions(+), 16 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index fc8b5cf..6504bd6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -97,9 +97,7 @@ int main(int argc, char **argv) { } std::vector> windows; - MainMenu window(configFilePath, romFiles, romDirectories, windows); - windows.emplace_back(&window); - window.init(); + windows.emplace_back(std::make_unique(configFilePath, romFiles, romDirectories, windows))->init(); bool quit = false; SDL_Event event; @@ -115,13 +113,9 @@ int main(int argc, char **argv) { } } - for (const auto& windowPtr : windows) { - Window* window = windowPtr.get(); - std::cout << "start " << window << std::endl; - window->update(); - std::cout << "middle" << std::endl; - window->render(); - std::cout << "enmd" << std::endl; + for (size_t i = 0; i < windows.size(); ++i) { + windows[i]->update(); + windows[i]->render(); } bool allWindowsClosed = true; @@ -135,8 +129,6 @@ int main(int argc, char **argv) { if (allWindowsClosed) { quit = true; } - - std::cout << "end loop e" << std::endl; } return 0; diff --git a/src/sdl/MainMenu.cpp b/src/sdl/MainMenu.cpp index 8bd8f6e..d77c979 100644 --- a/src/sdl/MainMenu.cpp +++ b/src/sdl/MainMenu.cpp @@ -71,7 +71,6 @@ void MainMenu::update() { } this->windows.emplace_back(std::make_unique(romButton.first))->init(); - std::cout << 1 << std::endl; } this->chooseFolder.update(this->inputHandler, point); @@ -117,8 +116,6 @@ void MainMenu::update() { this->inputHandler.updateLastKeys(); this->inputHandler.updateLastMouse(); - - std::cout << 2 << std::endl; } void MainMenu::render() { diff --git a/src/sdl/emulator/SdlKeyboard.cpp b/src/sdl/emulator/SdlKeyboard.cpp index 67abe06..4a51224 100644 --- a/src/sdl/emulator/SdlKeyboard.cpp +++ b/src/sdl/emulator/SdlKeyboard.cpp @@ -1,7 +1,6 @@ #include "SdlKeyboard.h" void SdlKeyboard::handleKeyDown(uint8_t keyCode) { - std::cout << +keyCode << std::endl; auto keyMapIter = this->KEYMAP.find(keyCode); if (keyMapIter != this->KEYMAP.end()) { uint8_t key = keyMapIter->second; From da6fa01144835cc46a4a9f87c35469a03a10b154 Mon Sep 17 00:00:00 2001 From: doom <61824552+JustDoom@users.noreply.github.com> Date: Sun, 15 Dec 2024 22:59:35 +1000 Subject: [PATCH 06/28] Mostly port to SDL Needs cleanup, small fixes and sound added back --- .gitmodules | 3 ++ CMakeLists.txt | 2 +- dependencies/CMakeLists.txt | 1 + dependencies/sdl_ttf | 1 + src/emulator/Cpu.cpp | 4 +- src/main.cpp | 16 ++++++- src/sdl/Emulator.cpp | 4 ++ src/sdl/Emulator.h | 1 + src/sdl/MainMenu.cpp | 83 +++++++++++++++++++++++++++++---- src/sdl/MainMenu.h | 15 ++++-- src/sdl/Window.cpp | 9 +++- src/sdl/Window.h | 2 + src/sdl/emulator/SdlSpeaker.cpp | 10 ++-- src/sdl/ui/TextButton.cpp | 44 +++++++++-------- src/sdl/ui/TextButton.h | 13 +++--- 15 files changed, 159 insertions(+), 49 deletions(-) create mode 160000 dependencies/sdl_ttf diff --git a/.gitmodules b/.gitmodules index 75dbdc4..669f44f 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,3 +7,6 @@ [submodule "dependencies/libconfig"] path = dependencies/libconfig url = https://github.com/hyperrealm/libconfig.git +[submodule "dependencies/sdl_ttf"] + path = dependencies/sdl_ttf + url = git@github.com:libsdl-org/SDL_ttf.git diff --git a/CMakeLists.txt b/CMakeLists.txt index e527244..e5ec41f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,7 +27,7 @@ else() set(libname "config") endif() -target_link_libraries(8ChocChip PRIVATE SDL3::SDL3 nfd ${libname}++) +target_link_libraries(8ChocChip PRIVATE SDL3::SDL3 SDL3_ttf::SDL3_ttf nfd ${libname}++) # Set the RPATH of the executable to include the directory where SFML libraries are located set_target_properties(8ChocChip PROPERTIES diff --git a/dependencies/CMakeLists.txt b/dependencies/CMakeLists.txt index 56ccc44..382d9d7 100644 --- a/dependencies/CMakeLists.txt +++ b/dependencies/CMakeLists.txt @@ -1,3 +1,4 @@ add_subdirectory(nativefiledialog) add_subdirectory(libconfig) add_subdirectory(sdl) +add_subdirectory(sdl_ttf) \ No newline at end of file diff --git a/dependencies/sdl_ttf b/dependencies/sdl_ttf new file mode 160000 index 0000000..a90b693 --- /dev/null +++ b/dependencies/sdl_ttf @@ -0,0 +1 @@ +Subproject commit a90b6938ddf2104f53d682d9c8388c6c173e98f0 diff --git a/src/emulator/Cpu.cpp b/src/emulator/Cpu.cpp index e4704e8..bac905e 100644 --- a/src/emulator/Cpu.cpp +++ b/src/emulator/Cpu.cpp @@ -71,9 +71,9 @@ void Cpu::cycle() { // Play sound until timer runs out if (this->soundTimer > 0) { - // this->speaker->play(); + this->speaker->play(); } else { - // this->speaker->stop(); + this->speaker->stop(); } } diff --git a/src/main.cpp b/src/main.cpp index 6504bd6..fb839f8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,4 +1,3 @@ -#include #include #include #include @@ -11,6 +10,8 @@ #include "libconfig.hh" +#include + int main(int argc, char **argv) { std::string rom; @@ -96,8 +97,19 @@ int main(int argc, char **argv) { } } + if (!TTF_Init()) { + SDL_Log("Couldn't initialize TTF: %s\n",SDL_GetError()); + return 1; + } + + TTF_Font *font = TTF_OpenFont("assets/font.ttf", 22); + if (!font) { + SDL_Log("Failed to load font: %s\n", SDL_GetError()); + return 1; + } + std::vector> windows; - windows.emplace_back(std::make_unique(configFilePath, romFiles, romDirectories, windows))->init(); + windows.emplace_back(std::make_unique(font, configFilePath, romFiles, romDirectories, windows))->init(); bool quit = false; SDL_Event event; diff --git a/src/sdl/Emulator.cpp b/src/sdl/Emulator.cpp index 9781fae..d8e7bee 100644 --- a/src/sdl/Emulator.cpp +++ b/src/sdl/Emulator.cpp @@ -63,3 +63,7 @@ void Emulator::render() { SDL_Delay(16); } + +void Emulator::resize(SDL_Event &event) { + +} diff --git a/src/sdl/Emulator.h b/src/sdl/Emulator.h index 5631254..51e7743 100644 --- a/src/sdl/Emulator.h +++ b/src/sdl/Emulator.h @@ -22,6 +22,7 @@ class Emulator : public Window { bool handleEvent(SDL_Event& event) override; void update() override; void render() override; + void resize(SDL_Event &event) override; }; diff --git a/src/sdl/MainMenu.cpp b/src/sdl/MainMenu.cpp index d77c979..8c223c3 100644 --- a/src/sdl/MainMenu.cpp +++ b/src/sdl/MainMenu.cpp @@ -6,6 +6,7 @@ #include #include +#include #include #include "../util/MiscUtil.h" @@ -14,22 +15,60 @@ #define WIDTH (64 * 15) #define HEIGHT (32 * 15) -MainMenu::MainMenu(std::string configFilePath, std::unordered_map> &romFiles, +MainMenu::MainMenu(TTF_Font* font, std::string configFilePath, std::unordered_map> &romFiles, std::vector &romDirectories, std::vector> &windows) : - configFilePath(configFilePath), romDirectories(romDirectories), romFiles(romFiles), windows(windows), - chooseFolder(0, 400, WIDTH, 80, "Select ROM") {} + configFilePath(configFilePath), romDirectories(romDirectories), romFiles(romFiles), windows(windows), font(font) {} void MainMenu::init() { Window::init(); + textEngine = TTF_CreateRendererTextEngine(this->renderer); + for (auto& thing: this->romFiles) { for (std::string& file: thing.second) { - TextButton romButton(0, 25.0f * this->roms.size(), WIDTH, 25, MiscUtil::getFileFromPath(file)); + TTF_Text* text = TTF_CreateText(textEngine, font, MiscUtil::getFileFromPath(file).c_str(), 0); + if (!text) { + SDL_Log("Failed to create text: %s\n", SDL_GetError()); + return; + } + + TextButton romButton(0, 25.0f * this->roms.size(), WIDTH, 25, text); this->roms.emplace(file, romButton); } } + + TTF_Text* text = TTF_CreateText(textEngine, font, "Select ROM", 0); + if (!text) { + SDL_Log("Failed to create text: %s\n", SDL_GetError()); + return; + } + this->chooseFolder = std::make_unique(0, 400, WIDTH, 80, text); + + // font = TTF_OpenFont("font.ttf", 32); // Ensure the path is correct + // if (!font) { + // // printf("TTF_OpenFont Error: %s\n", SDL_GetError()); + // return; + // } + // + // // Create the text surface + // SDL_Color color = {0, 255, 0, 255}; // Green color + // text = TTF_RenderText_Solid(font, "Some test", 9, color); + // if (!text) { + // // printf("TTF_RenderText_Solid Error: %s\n", TTF_GetError()); + // return; + // } + // + // // Create the text texture + // textTexture = SDL_CreateTextureFromSurface(renderer, text); + // if (!textTexture) { + // printf("SDL_CreateTextureFromSurface Error: %s\n", SDL_GetError()); + // SDL_DestroySurface(text); // Free the surface if texture creation fails + // return; + // } + // + // SDL_DestroySurface(text); } bool MainMenu::handleEvent(SDL_Event &event) { @@ -64,7 +103,7 @@ void MainMenu::update() { } for (auto &romButton: roms) { - romButton.second.update(this->inputHandler, point); + romButton.second.update(&this->inputHandler, point); if (!romButton.second.isJustClicked()) { continue; @@ -72,9 +111,9 @@ void MainMenu::update() { this->windows.emplace_back(std::make_unique(romButton.first))->init(); } - this->chooseFolder.update(this->inputHandler, point); + this->chooseFolder->update(&this->inputHandler, point); - if (this->chooseFolder.isJustClicked()) { + if (this->chooseFolder->isJustClicked()) { if (nfdresult_t result = PickFolder(this->outPath); result == NFD_OKAY) { libconfig::Config config; @@ -107,7 +146,13 @@ void MainMenu::update() { // Add the file path to the romFiles entry romFiles.find(&romDirectories.back())->second.emplace_back(file.path().string()); - TextButton romButton(0, 25.0f * roms.size(), WIDTH, 25, file.path().filename().string()); + TTF_Text* text = TTF_CreateText(textEngine, font, file.path().filename().c_str(), 0); + if (!text) { + SDL_Log("Failed to create text: %s\n", SDL_GetError()); + return; + } + + TextButton romButton(0, 25.0f * roms.size(), WIDTH, 25, text); roms.emplace(file.path().string(), romButton); } config.writeFile(configFilePath); @@ -124,7 +169,27 @@ void MainMenu::render() { for (auto &romButton: roms) { romButton.second.draw(renderer); } - this->chooseFolder.draw(renderer); + this->chooseFolder->draw(renderer); SDL_RenderPresent(renderer); } + +void MainMenu::resize(SDL_Event& event) { + SDL_Point size; + SDL_GetWindowSize(this->window, &size.x, &size.y); + + SDL_FPoint point{}; + SDL_GetMouseState(&point.x, &point.y); + + for (auto& romButton : roms) { + romButton.second.updateSize(this->originalSize, size); + romButton.second.update(&this->inputHandler, point); + } + + chooseFolder->updateSize(this->originalSize,size); + chooseFolder->update(&this->inputHandler, point); +} + +void MainMenu::refreshRoms() { + +} diff --git a/src/sdl/MainMenu.h b/src/sdl/MainMenu.h index 25dedd1..6d63f1d 100644 --- a/src/sdl/MainMenu.h +++ b/src/sdl/MainMenu.h @@ -5,6 +5,7 @@ #include #include +#include #include "Emulator.h" #include "InputHandler.h" @@ -22,17 +23,25 @@ class MainMenu : public Window { NFD::UniquePath outPath; std::unordered_map roms; - TextButton chooseFolder; + std::unique_ptr chooseFolder; InputHandler inputHandler{}; + + SDL_Surface* text = nullptr; + TTF_TextEngine* textEngine; + TTF_Font* font = nullptr; public: - MainMenu(std::string configFilePath, std::unordered_map>& romFiles, - std::vector& romDirectories, std::vector>& windows); + MainMenu(TTF_Font* font, std::string configFilePath, std::unordered_map>& romFiles, std::vector& romDirectories, + std::vector>& windows); void init() override; bool handleEvent(SDL_Event& event) override; void update() override; void render() override; + void resize(SDL_Event& event) override; + + void refreshRoms(); }; #endif \ No newline at end of file diff --git a/src/sdl/Window.cpp b/src/sdl/Window.cpp index 9e0cc7f..109ea97 100644 --- a/src/sdl/Window.cpp +++ b/src/sdl/Window.cpp @@ -31,6 +31,8 @@ void Window::init() { this->windowId = SDL_GetWindowID(this->window); this->shown = true; + + SDL_GetWindowSize(this->window, &this->originalSize.x, &this->originalSize.y); } bool Window::handleEvent(SDL_Event &event) { @@ -45,7 +47,7 @@ bool Window::handleEvent(SDL_Event &event) { this->shown = false; break; case SDL_EVENT_WINDOW_RESIZED: - //TODO + resize(event); break; case SDL_EVENT_WINDOW_EXPOSED: SDL_RenderPresent(this->renderer); @@ -90,6 +92,11 @@ bool Window::handleEvent(SDL_Event &event) { void Window::update() { } +void Window::resize(SDL_Event &event) { + +} + + void Window::close() { } diff --git a/src/sdl/Window.h b/src/sdl/Window.h index d835520..19d7eed 100644 --- a/src/sdl/Window.h +++ b/src/sdl/Window.h @@ -8,6 +8,7 @@ class Window { SDL_Window* window = nullptr; SDL_Renderer* renderer = nullptr; unsigned int windowId = -1; + SDL_Point originalSize; int height = 720; int width = 480; @@ -27,6 +28,7 @@ class Window { virtual bool handleEvent(SDL_Event& event); virtual void update(); virtual void render() = 0; + virtual void resize(SDL_Event& event); void close(); bool isShown(); diff --git a/src/sdl/emulator/SdlSpeaker.cpp b/src/sdl/emulator/SdlSpeaker.cpp index ebe3f5d..064e17c 100644 --- a/src/sdl/emulator/SdlSpeaker.cpp +++ b/src/sdl/emulator/SdlSpeaker.cpp @@ -2,13 +2,13 @@ #include -SdlSpeaker::SdlSpeaker() = default; +SdlSpeaker::SdlSpeaker() { -void SdlSpeaker::play() { - std::cout <<"sp" << std::endl; } -void SdlSpeaker::stop() { - std::cout <<"sp1" << std::endl; +void SdlSpeaker::play() { + std::cout << "forkin beepin" << std::endl; } +void SdlSpeaker::stop() { +} \ No newline at end of file diff --git a/src/sdl/ui/TextButton.cpp b/src/sdl/ui/TextButton.cpp index 2ca470c..3dcfbce 100644 --- a/src/sdl/ui/TextButton.cpp +++ b/src/sdl/ui/TextButton.cpp @@ -2,44 +2,46 @@ #include -TextButton::TextButton(float x, float y, float width, float height, const std::string& buttonText) : text(text) { +TextButton::TextButton(float x, float y, float width, float height, TTF_Text* text) : text(text) { this->button = SDL_FRect{x, y, width, height}; - // this->button.setPosition(sf::Vector2f(x, y)); - // this->originalPosition = this->button.getPosition(); - // this->originalSize = this->button.getSize(); + TTF_SetTextColor(text, 0, 0, 0, 255); + SDL_Point textSize{}; + TTF_GetTextSize(text, &textSize.x, &textSize.y); + this->textPos.x = x + width / 2 - textSize.x / 2; + this->textPos.y = y + height / 2 - textSize.y / 2 + 4; - // this->text.setFont(*font); - // this->text.setString(buttonText); - // this->text.setCharacterSize(20); - // this->text.setFillColor(sf::Color::Black); - // this->text.setPosition(x + width / 2 - this->text.getGlobalBounds().width / 2, y + height / 2 - this->text.getGlobalBounds().height / 2); + this->originalPosition = {button.x, button.y}; + this->originalSize = {button.w, button.h}; this->idleColor = SDL_Color{192, 192, 192}; this->hoverColor = SDL_Color{128, 128, 128}; this->activeColor = SDL_Color{64, 64, 64}; - // this->button.setFillColor(this->idleColor); - this->isPressed = false; this->isHovered = false; } void TextButton::updateSize(const SDL_Point originalSize, const SDL_Point updatedSize) { - // this->button.setSize(SDL_Point(this->originalSize.x / originalSize.x * updatedSize.x, this->originalSize.y / originalSize.y * updatedSize.y)); - // this->button.setPosition(this->originalPosition.x / originalSize.x * updatedSize.x, this->originalPosition.y / originalSize.y * updatedSize.y); - - // this->text.setPosition(this->button.getPosition().x + this->button.getSize().x / 2 - this->text.getGlobalBounds().width / 2, this->button.getPosition().y + this->button.getSize().y / 2 - this->text.getGlobalBounds().height / 2); + this->button.x = this->originalPosition.x / originalSize.x * updatedSize.x; + this->button.y = this->originalPosition.y / originalSize.y * updatedSize.y; + this->button.w = this->originalSize.x / originalSize.x * updatedSize.x; + this->button.h = this->originalSize.y / originalSize.y * updatedSize.y; + + SDL_Point textSize{}; + TTF_GetTextSize(text, &textSize.x, &textSize.y); + this->textPos.x = this->button.x + this->button.w / 2 - textSize.x / 2; + this->textPos.y = this->button.y + this->button.h / 2 - textSize.y / 2 + 4; } -void TextButton::update(InputHandler& inputHandler, SDL_FPoint pos) { +void TextButton::update(InputHandler* inputHandler, SDL_FPoint pos) { this->lastPressed = this->isPressed; this->isHovered = SDL_PointInRectFloat(&pos, &this->button); - if (this->isHovered && inputHandler.isJustClicked(SDL_BUTTON_LEFT)) { + if (this->isHovered && inputHandler->isJustClicked(SDL_BUTTON_LEFT)) { this->isPressed = true; updateColour(this->activeColor); - } else if (this->isHovered && inputHandler.isPressed(SDL_KMOD_LSHIFT)) { + } else if (this->isHovered && inputHandler->isPressed(SDL_KMOD_LSHIFT)) { updateColour( SDL_Color{255, 0, 0}); } else if (this->isHovered) { updateColour(this->hoverColor); @@ -58,10 +60,12 @@ void TextButton::updateColour(const SDL_Color color) { } void TextButton::draw(SDL_Renderer* window) const { - // window.draw(this->button); - SDL_SetRenderDrawColor(window, this->currentColor.r, this->currentColor.g, this->currentColor.b, this->currentColor.a); SDL_RenderFillRect(window, &this->button); + if (text == nullptr) return; + if (!TTF_DrawRendererText(text, textPos.x, textPos.y)) { + SDL_Log("Text rendering failed: %s\n", SDL_GetError()); + } } bool TextButton::isClicked() const { diff --git a/src/sdl/ui/TextButton.h b/src/sdl/ui/TextButton.h index 37ce40c..a8b56db 100644 --- a/src/sdl/ui/TextButton.h +++ b/src/sdl/ui/TextButton.h @@ -5,14 +5,15 @@ #include #include +#include class TextButton { private: SDL_FRect button; - SDL_Point originalSize; - SDL_Point originalPosition; - std::string& text; - // sf::Font* font{}; + SDL_FPoint originalSize; + SDL_FPoint originalPosition; + TTF_Text* text; + SDL_FPoint textPos; SDL_Color color; SDL_Color idleColor; SDL_Color hoverColor; @@ -23,11 +24,11 @@ class TextButton { bool isHovered; public: - TextButton(float x, float y, float width, float height, const std::string& buttonText); + TextButton(float x, float y, float width, float height, TTF_Text* text); void updateSize(SDL_Point originalSize, SDL_Point updatedSize); - void update(InputHandler& inputHandler, SDL_FPoint pos); + void update(InputHandler* inputHandler, SDL_FPoint pos); void draw(SDL_Renderer* window) const; From 87bc1ce910a8e7e7e8a5571bab89014884cf8e4e Mon Sep 17 00:00:00 2001 From: doom <61824552+JustDoom@users.noreply.github.com> Date: Sun, 15 Dec 2024 23:02:02 +1000 Subject: [PATCH 07/28] Fix button hovering on a window applying on all windows --- src/sdl/MainMenu.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sdl/MainMenu.cpp b/src/sdl/MainMenu.cpp index 8c223c3..0f37c64 100644 --- a/src/sdl/MainMenu.cpp +++ b/src/sdl/MainMenu.cpp @@ -95,6 +95,8 @@ bool MainMenu::handleEvent(SDL_Event &event) { } void MainMenu::update() { + if (!this->mouseFocus) return; + SDL_FPoint point{}; SDL_GetMouseState(&point.x, &point.y); From 95542c01880b661ddb5e6a482810836fbec712c5 Mon Sep 17 00:00:00 2001 From: doom <61824552+JustDoom@users.noreply.github.com> Date: Mon, 16 Dec 2024 10:46:53 +1000 Subject: [PATCH 08/28] Some better window shutdown --- src/main.cpp | 4 ++++ src/sdl/MainMenu.cpp | 10 +++++++++- src/sdl/MainMenu.h | 2 +- src/sdl/Window.cpp | 4 +++- src/sdl/Window.h | 2 +- 5 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index fb839f8..82828df 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -143,5 +143,9 @@ int main(int argc, char **argv) { } } + TTF_CloseFont(font); + TTF_Quit(); + SDL_Quit(); + return 0; } \ No newline at end of file diff --git a/src/sdl/MainMenu.cpp b/src/sdl/MainMenu.cpp index 0f37c64..b0c9433 100644 --- a/src/sdl/MainMenu.cpp +++ b/src/sdl/MainMenu.cpp @@ -22,7 +22,7 @@ MainMenu::MainMenu(TTF_Font* font, std::string configFilePath, std::unordered_ma void MainMenu::init() { Window::init(); - textEngine = TTF_CreateRendererTextEngine(this->renderer); + this->textEngine = TTF_CreateRendererTextEngine(this->renderer); for (auto& thing: this->romFiles) { for (std::string& file: thing.second) { @@ -192,6 +192,14 @@ void MainMenu::resize(SDL_Event& event) { chooseFolder->update(&this->inputHandler, point); } +void MainMenu::close() { + for (const auto& window : this->windows) { + window->close(); + } + TTF_DestroyRendererTextEngine(this->textEngine); + Window::close(); +} + void MainMenu::refreshRoms() { } diff --git a/src/sdl/MainMenu.h b/src/sdl/MainMenu.h index 6d63f1d..76adf03 100644 --- a/src/sdl/MainMenu.h +++ b/src/sdl/MainMenu.h @@ -27,7 +27,6 @@ class MainMenu : public Window { InputHandler inputHandler{}; - SDL_Surface* text = nullptr; TTF_TextEngine* textEngine; TTF_Font* font = nullptr; public: @@ -40,6 +39,7 @@ class MainMenu : public Window { void update() override; void render() override; void resize(SDL_Event& event) override; + void close() override; void refreshRoms(); }; diff --git a/src/sdl/Window.cpp b/src/sdl/Window.cpp index 109ea97..e42b0ee 100644 --- a/src/sdl/Window.cpp +++ b/src/sdl/Window.cpp @@ -79,6 +79,7 @@ bool Window::handleEvent(SDL_Event &event) { break; case SDL_EVENT_WINDOW_CLOSE_REQUESTED: SDL_HideWindow(this->window); + close(); break; } @@ -98,7 +99,8 @@ void Window::resize(SDL_Event &event) { void Window::close() { - + SDL_DestroyRenderer(this->renderer); + SDL_DestroyWindow(this->window); } bool Window::isShown() { diff --git a/src/sdl/Window.h b/src/sdl/Window.h index 19d7eed..5c6bb60 100644 --- a/src/sdl/Window.h +++ b/src/sdl/Window.h @@ -29,7 +29,7 @@ class Window { virtual void update(); virtual void render() = 0; virtual void resize(SDL_Event& event); - void close(); + virtual void close(); bool isShown(); }; From 34b925fc9bc16059be6a6c74f76bb22a9213ebb1 Mon Sep 17 00:00:00 2001 From: doom <61824552+JustDoom@users.noreply.github.com> Date: Mon, 16 Dec 2024 11:10:16 +1000 Subject: [PATCH 09/28] Update Cmake for SDl --- CMakeLists.txt | 30 +----------------------------- dependencies/CMakeLists.txt | 12 +++++++++++- src/main.cpp | 1 + 3 files changed, 13 insertions(+), 30 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e5ec41f..75a5031 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,19 +5,11 @@ VERSION 0.1) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) - set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) +option(SDL_VENDORED "Use vendored libraries" ON) add_executable(8ChocChip) -#option(BUILD_SHARED_LIBS "Build shared libraries" OFF) - -#file(GLOB ASSETS "${CMAKE_SOURCE_DIR}/assets/*") -# -#foreach(ASSET ${ASSETS}) -# configure_file(${ASSET} ${CMAKE_BINARY_DIR}/assets COPYONLY) -#endforeach() - add_subdirectory(dependencies) add_subdirectory(src) @@ -29,31 +21,11 @@ endif() target_link_libraries(8ChocChip PRIVATE SDL3::SDL3 SDL3_ttf::SDL3_ttf nfd ${libname}++) -# Set the RPATH of the executable to include the directory where SFML libraries are located set_target_properties(8ChocChip PROPERTIES INSTALL_RPATH "$ORIGIN" BUILD_RPATH "$ORIGIN" ) -# Copy DLLs needed for runtime on Windows -if(WIN32) - add_custom_command( - TARGET 8ChocChip - COMMENT "Copy OpenAL DLL" - PRE_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${SFML_SOURCE_DIR}/extlibs/bin/$,x64,x86>/openal32.dll $ - VERBATIM) - - if (BUILD_SHARED_LIBS) - add_custom_command(TARGET 8ChocChip POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy_if_different - $ - $ - $ - $ - $) - endif() -endif() - # Copy nfd and config++ libraries to the executable directory if (UNIX AND NOT APPLE) add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD diff --git a/dependencies/CMakeLists.txt b/dependencies/CMakeLists.txt index 382d9d7..094cb0e 100644 --- a/dependencies/CMakeLists.txt +++ b/dependencies/CMakeLists.txt @@ -1,4 +1,14 @@ add_subdirectory(nativefiledialog) add_subdirectory(libconfig) -add_subdirectory(sdl) + +if(SDL_VENDORED) + # This assumes you have added SDL as a submodule in vendored/SDL + add_subdirectory(sdl EXCLUDE_FROM_ALL) +else() + # 1. Look for a SDL3 package, + # 2. look for the SDL3-shared component, and + # 3. fail if the shared component cannot be found. + find_package(SDL3 REQUIRED CONFIG REQUIRED COMPONENTS SDL3-shared) +endif() + add_subdirectory(sdl_ttf) \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 82828df..274ff55 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -125,6 +125,7 @@ int main(int argc, char **argv) { } } + // Do not change, this makes multiple windows not crash for (size_t i = 0; i < windows.size(); ++i) { windows[i]->update(); windows[i]->render(); From fb9da19be1433071e08949054a2c124d5d188d67 Mon Sep 17 00:00:00 2001 From: doom <61824552+JustDoom@users.noreply.github.com> Date: Mon, 16 Dec 2024 11:39:56 +1000 Subject: [PATCH 10/28] Try fix windows gameinput.h issue --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a6a0604..4296f58 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -101,7 +101,7 @@ jobs: run: | mkdir -p build cd build - cmake .. -DCMAKE_BUILD_TYPE=Release + cmake .. -DCMAKE_BUILD_TYPE=Release -D CMAKE_SYSTEM_VERSION=10.0.26100.0 - name: Build CMake run: | From 5e7685d786dec4315678ac9ba0f81e17a2540f58 Mon Sep 17 00:00:00 2001 From: doom <61824552+JustDoom@users.noreply.github.com> Date: Mon, 16 Dec 2024 11:44:33 +1000 Subject: [PATCH 11/28] Maybe fix windows string error --- src/sdl/MainMenu.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sdl/MainMenu.cpp b/src/sdl/MainMenu.cpp index b0c9433..ad9d8e0 100644 --- a/src/sdl/MainMenu.cpp +++ b/src/sdl/MainMenu.cpp @@ -148,7 +148,7 @@ void MainMenu::update() { // Add the file path to the romFiles entry romFiles.find(&romDirectories.back())->second.emplace_back(file.path().string()); - TTF_Text* text = TTF_CreateText(textEngine, font, file.path().filename().c_str(), 0); + TTF_Text* text = TTF_CreateText(textEngine, font, file.path().filename().string().c_str(), 0); if (!text) { SDL_Log("Failed to create text: %s\n", SDL_GetError()); return; From d207b6eef23c88b1bc1c3eaf85386832d498c5b5 Mon Sep 17 00:00:00 2001 From: doom <61824552+JustDoom@users.noreply.github.com> Date: Mon, 16 Dec 2024 11:48:26 +1000 Subject: [PATCH 12/28] Stop libconfig building examples --- dependencies/CMakeLists.txt | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/dependencies/CMakeLists.txt b/dependencies/CMakeLists.txt index 094cb0e..bc7a5b4 100644 --- a/dependencies/CMakeLists.txt +++ b/dependencies/CMakeLists.txt @@ -1,13 +1,11 @@ add_subdirectory(nativefiledialog) + +set(BUILD_EXAMPLES OFF CACHE BOOL "Disable examples for libconfig" FORCE) add_subdirectory(libconfig) if(SDL_VENDORED) - # This assumes you have added SDL as a submodule in vendored/SDL add_subdirectory(sdl EXCLUDE_FROM_ALL) else() - # 1. Look for a SDL3 package, - # 2. look for the SDL3-shared component, and - # 3. fail if the shared component cannot be found. find_package(SDL3 REQUIRED CONFIG REQUIRED COMPONENTS SDL3-shared) endif() From 6302e0c58a0ef1cdf4304511861d625cbd3aab88 Mon Sep 17 00:00:00 2001 From: doom <61824552+JustDoom@users.noreply.github.com> Date: Mon, 16 Dec 2024 12:06:05 +1000 Subject: [PATCH 13/28] Update libconfig --- dependencies/CMakeLists.txt | 3 ++- dependencies/libconfig | 2 +- src/main.cpp | 2 +- src/sdl/MainMenu.h | 1 - 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/dependencies/CMakeLists.txt b/dependencies/CMakeLists.txt index bc7a5b4..1ad37b3 100644 --- a/dependencies/CMakeLists.txt +++ b/dependencies/CMakeLists.txt @@ -1,7 +1,8 @@ add_subdirectory(nativefiledialog) set(BUILD_EXAMPLES OFF CACHE BOOL "Disable examples for libconfig" FORCE) -add_subdirectory(libconfig) +set(BUILD_TESTS ON CACHE BOOL "Disable tests for libconfig" FORCE) +add_subdirectory(libconfig EXCLUDE_FROM_ALL) if(SDL_VENDORED) add_subdirectory(sdl EXCLUDE_FROM_ALL) diff --git a/dependencies/libconfig b/dependencies/libconfig index f9404f6..690342b 160000 --- a/dependencies/libconfig +++ b/dependencies/libconfig @@ -1 +1 @@ -Subproject commit f9404f60a435aa06321f4ccd8357364dcb216d46 +Subproject commit 690342b9cbc8b39787a1501bd890d63ca63a003c diff --git a/src/main.cpp b/src/main.cpp index 274ff55..955a58e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -8,7 +8,7 @@ #include "util/MiscUtil.h" -#include "libconfig.hh" +#include #include diff --git a/src/sdl/MainMenu.h b/src/sdl/MainMenu.h index 76adf03..21f5a6c 100644 --- a/src/sdl/MainMenu.h +++ b/src/sdl/MainMenu.h @@ -7,7 +7,6 @@ #include #include -#include "Emulator.h" #include "InputHandler.h" #include "Window.h" #include "ui/TextButton.h" From 95fb1016f9ce2b90c10c4eb6604500347e13e4de Mon Sep 17 00:00:00 2001 From: doom <61824552+JustDoom@users.noreply.github.com> Date: Mon, 16 Dec 2024 14:28:12 +1000 Subject: [PATCH 14/28] Fix random colours, no idea why it happened though --- src/sdl/MainMenu.cpp | 41 +++++++-------------------------------- src/sdl/MainMenu.h | 1 + src/sdl/ui/TextButton.cpp | 32 +++++++++++++++--------------- src/sdl/ui/TextButton.h | 9 ++++----- 4 files changed, 28 insertions(+), 55 deletions(-) diff --git a/src/sdl/MainMenu.cpp b/src/sdl/MainMenu.cpp index ad9d8e0..0d2a095 100644 --- a/src/sdl/MainMenu.cpp +++ b/src/sdl/MainMenu.cpp @@ -33,9 +33,7 @@ void MainMenu::init() { return; } - TextButton romButton(0, 25.0f * this->roms.size(), WIDTH, 25, text); - - this->roms.emplace(file, romButton); + this->roms.emplace(file, TextButton(0, 25.0f * this->roms.size(), WIDTH, 25, text)); } } @@ -45,30 +43,6 @@ void MainMenu::init() { return; } this->chooseFolder = std::make_unique(0, 400, WIDTH, 80, text); - - // font = TTF_OpenFont("font.ttf", 32); // Ensure the path is correct - // if (!font) { - // // printf("TTF_OpenFont Error: %s\n", SDL_GetError()); - // return; - // } - // - // // Create the text surface - // SDL_Color color = {0, 255, 0, 255}; // Green color - // text = TTF_RenderText_Solid(font, "Some test", 9, color); - // if (!text) { - // // printf("TTF_RenderText_Solid Error: %s\n", TTF_GetError()); - // return; - // } - // - // // Create the text texture - // textTexture = SDL_CreateTextureFromSurface(renderer, text); - // if (!textTexture) { - // printf("SDL_CreateTextureFromSurface Error: %s\n", SDL_GetError()); - // SDL_DestroySurface(text); // Free the surface if texture creation fails - // return; - // } - // - // SDL_DestroySurface(text); } bool MainMenu::handleEvent(SDL_Event &event) { @@ -105,7 +79,7 @@ void MainMenu::update() { } for (auto &romButton: roms) { - romButton.second.update(&this->inputHandler, point); + romButton.second.update(this->inputHandler, point); if (!romButton.second.isJustClicked()) { continue; @@ -113,7 +87,7 @@ void MainMenu::update() { this->windows.emplace_back(std::make_unique(romButton.first))->init(); } - this->chooseFolder->update(&this->inputHandler, point); + this->chooseFolder->update(this->inputHandler, point); if (this->chooseFolder->isJustClicked()) { @@ -154,8 +128,7 @@ void MainMenu::update() { return; } - TextButton romButton(0, 25.0f * roms.size(), WIDTH, 25, text); - roms.emplace(file.path().string(), romButton); + roms.emplace(file.path().string(), TextButton(0, 25.0f * roms.size(), WIDTH, 25, text)); } config.writeFile(configFilePath); } @@ -166,7 +139,7 @@ void MainMenu::update() { } void MainMenu::render() { - SDL_SetRenderDrawColor(this->renderer, 0x00, 0x00, 0x00, 0x00); + SDL_SetRenderDrawColor(this->renderer, 0x00, 0x00, 0x00, 255); SDL_RenderClear(renderer); for (auto &romButton: roms) { romButton.second.draw(renderer); @@ -185,11 +158,11 @@ void MainMenu::resize(SDL_Event& event) { for (auto& romButton : roms) { romButton.second.updateSize(this->originalSize, size); - romButton.second.update(&this->inputHandler, point); + romButton.second.update(this->inputHandler, point); } chooseFolder->updateSize(this->originalSize,size); - chooseFolder->update(&this->inputHandler, point); + chooseFolder->update(this->inputHandler, point); } void MainMenu::close() { diff --git a/src/sdl/MainMenu.h b/src/sdl/MainMenu.h index 21f5a6c..2f5b1cd 100644 --- a/src/sdl/MainMenu.h +++ b/src/sdl/MainMenu.h @@ -1,6 +1,7 @@ #ifndef MAINMENU_H #define MAINMENU_H +#include #include #include diff --git a/src/sdl/ui/TextButton.cpp b/src/sdl/ui/TextButton.cpp index 3dcfbce..59c213b 100644 --- a/src/sdl/ui/TextButton.cpp +++ b/src/sdl/ui/TextButton.cpp @@ -2,9 +2,14 @@ #include -TextButton::TextButton(float x, float y, float width, float height, TTF_Text* text) : text(text) { +TextButton::TextButton(float x, float y, float width, float height, TTF_Text* text) : + text(text), idleColor(SDL_Color{192, 192, 192, 255}), + hoverColor(SDL_Color{128, 128, 128, 255}), activeColor(SDL_Color{64, 64, 64, 255}), + deleteColor(SDL_Color{255, 0, 0, 255}) { this->button = SDL_FRect{x, y, width, height}; + currentColor = idleColor; + TTF_SetTextColor(text, 0, 0, 0, 255); SDL_Point textSize{}; TTF_GetTextSize(text, &textSize.x, &textSize.y); @@ -14,15 +19,11 @@ TextButton::TextButton(float x, float y, float width, float height, TTF_Text* te this->originalPosition = {button.x, button.y}; this->originalSize = {button.w, button.h}; - this->idleColor = SDL_Color{192, 192, 192}; - this->hoverColor = SDL_Color{128, 128, 128}; - this->activeColor = SDL_Color{64, 64, 64}; - this->isPressed = false; this->isHovered = false; } -void TextButton::updateSize(const SDL_Point originalSize, const SDL_Point updatedSize) { +void TextButton::updateSize(const SDL_Point& originalSize, const SDL_Point& updatedSize) { this->button.x = this->originalPosition.x / originalSize.x * updatedSize.x; this->button.y = this->originalPosition.y / originalSize.y * updatedSize.y; this->button.w = this->originalSize.x / originalSize.x * updatedSize.x; @@ -34,15 +35,15 @@ void TextButton::updateSize(const SDL_Point originalSize, const SDL_Point update this->textPos.y = this->button.y + this->button.h / 2 - textSize.y / 2 + 4; } -void TextButton::update(InputHandler* inputHandler, SDL_FPoint pos) { +void TextButton::update(InputHandler& inputHandler, SDL_FPoint& pos) { this->lastPressed = this->isPressed; this->isHovered = SDL_PointInRectFloat(&pos, &this->button); - if (this->isHovered && inputHandler->isJustClicked(SDL_BUTTON_LEFT)) { + if (this->isHovered && inputHandler.isJustClicked(SDL_BUTTON_LEFT)) { this->isPressed = true; updateColour(this->activeColor); - } else if (this->isHovered && inputHandler->isPressed(SDL_KMOD_LSHIFT)) { - updateColour( SDL_Color{255, 0, 0}); + } else if (this->isHovered && inputHandler.isPressed(SDL_SCANCODE_LSHIFT)) { + updateColour(this->deleteColor); } else if (this->isHovered) { updateColour(this->hoverColor); } else { @@ -51,18 +52,17 @@ void TextButton::update(InputHandler* inputHandler, SDL_FPoint pos) { } } -void TextButton::updateColour(const SDL_Color color) { - if (this->color.r == color.r && this->color.g == color.g && this->color.b == color.b) { - return; - } +void TextButton::updateColour(SDL_Color& color) { + // if (this->currentColor == color) { + // return; + // } this->currentColor = color; } void TextButton::draw(SDL_Renderer* window) const { - SDL_SetRenderDrawColor(window, this->currentColor.r, this->currentColor.g, this->currentColor.b, this->currentColor.a); + SDL_SetRenderDrawColor(window, currentColor.r, currentColor.g, currentColor.b, currentColor.a); SDL_RenderFillRect(window, &this->button); - if (text == nullptr) return; if (!TTF_DrawRendererText(text, textPos.x, textPos.y)) { SDL_Log("Text rendering failed: %s\n", SDL_GetError()); } diff --git a/src/sdl/ui/TextButton.h b/src/sdl/ui/TextButton.h index a8b56db..454ca85 100644 --- a/src/sdl/ui/TextButton.h +++ b/src/sdl/ui/TextButton.h @@ -2,7 +2,6 @@ #define TEXTBUTTON_H #include "../InputHandler.h" -#include #include #include @@ -14,10 +13,10 @@ class TextButton { SDL_FPoint originalPosition; TTF_Text* text; SDL_FPoint textPos; - SDL_Color color; SDL_Color idleColor; SDL_Color hoverColor; SDL_Color activeColor; + SDL_Color deleteColor; SDL_Color currentColor; bool isPressed; bool lastPressed{}; @@ -26,9 +25,9 @@ class TextButton { public: TextButton(float x, float y, float width, float height, TTF_Text* text); - void updateSize(SDL_Point originalSize, SDL_Point updatedSize); + void updateSize(const SDL_Point & originalSize, const SDL_Point & updatedSize); - void update(InputHandler* inputHandler, SDL_FPoint pos); + void update(InputHandler& inputHandler, SDL_FPoint& pos); void draw(SDL_Renderer* window) const; @@ -36,7 +35,7 @@ class TextButton { bool isJustClicked() const; - void updateColour(SDL_Color color); + void updateColour(SDL_Color& color); }; From eb860ea620ad2a81ee8cff515edc2dbbbae714b3 Mon Sep 17 00:00:00 2001 From: doom <61824552+JustDoom@users.noreply.github.com> Date: Tue, 17 Dec 2024 10:14:17 +1000 Subject: [PATCH 15/28] Handle multiple windows at 60fps --- src/CMakeLists.txt | 2 +- src/Timer.cpp | 103 +++++++++++++++++++++++++++++++++++++++++++ src/Timer.h | 36 +++++++++++++++ src/main.cpp | 25 +++++++++++ src/sdl/Emulator.cpp | 2 +- 5 files changed, 166 insertions(+), 2 deletions(-) create mode 100644 src/Timer.cpp create mode 100644 src/Timer.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 88e17c8..92bd106 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -2,4 +2,4 @@ add_subdirectory(sdl) add_subdirectory(util) add_subdirectory(emulator) -target_sources(8ChocChip PUBLIC main.cpp) \ No newline at end of file +target_sources(8ChocChip PUBLIC main.cpp Timer.cpp Timer.h) \ No newline at end of file diff --git a/src/Timer.cpp b/src/Timer.cpp new file mode 100644 index 0000000..e996b3d --- /dev/null +++ b/src/Timer.cpp @@ -0,0 +1,103 @@ +#include "Timer.h" + +Timer::Timer() +{ + //Initialize the variables + mStartTicks = 0; + mPausedTicks = 0; + + mPaused = false; + mStarted = false; +} + +void Timer::start() +{ + //Start the timer + mStarted = true; + + //Unpause the timer + mPaused = false; + + //Get the current clock time + mStartTicks = SDL_GetTicks(); + mPausedTicks = 0; +} + +void Timer::stop() +{ + //Stop the timer + mStarted = false; + + //Unpause the timer + mPaused = false; + + //Clear tick variables + mStartTicks = 0; + mPausedTicks = 0; +} + +void Timer::pause() +{ + //If the timer is running and isn't already paused + if( mStarted && !mPaused ) + { + //Pause the timer + mPaused = true; + + //Calculate the paused ticks + mPausedTicks = SDL_GetTicks() - mStartTicks; + mStartTicks = 0; + } +} + +void Timer::unpause() +{ + //If the timer is running and paused + if( mStarted && mPaused ) + { + //Unpause the timer + mPaused = false; + + //Reset the starting ticks + mStartTicks = SDL_GetTicks() - mPausedTicks; + + //Reset the paused ticks + mPausedTicks = 0; + } +} + +Uint64 Timer::getTicks() +{ + //The actual timer time + Uint64 time = 0; + + //If the timer is running + if( mStarted ) + { + //If the timer is paused + if( mPaused ) + { + //Return the number of ticks when the timer was paused + time = mPausedTicks; + } + else + { + //Return the current time minus the start time + time = SDL_GetTicks() - mStartTicks; + } + } + + return time; +} + +bool Timer::isStarted() +{ + //Timer is running and paused or unpaused + return mStarted; +} + +bool Timer::isPaused() +{ + //Timer is running and paused + return mPaused && mStarted; +} \ No newline at end of file diff --git a/src/Timer.h b/src/Timer.h new file mode 100644 index 0000000..adc32cb --- /dev/null +++ b/src/Timer.h @@ -0,0 +1,36 @@ +#ifndef TIMER_H +#define TIMER_H + +#include + +class Timer { +public: + //Initializes variables + Timer(); + + //The various clock actions + void start(); + void stop(); + void pause(); + void unpause(); + + //Gets the timer's time + Uint64 getTicks(); + + //Checks the status of the timer + bool isStarted(); + bool isPaused(); + +private: + //The clock time when the timer started + Uint64 mStartTicks; + + //The ticks stored when the timer was paused + Uint64 mPausedTicks; + + //The timer status + bool mPaused; + bool mStarted; +}; + +#endif \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 955a58e..f41cbe2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -12,6 +12,8 @@ #include +#include "Timer.h" + int main(int argc, char **argv) { std::string rom; @@ -114,7 +116,14 @@ int main(int argc, char **argv) { bool quit = false; SDL_Event event; + Timer fpsTimer; + Timer capTimer; + + int countedFrames = 0; + fpsTimer.start(); + while (!quit) { + capTimer.start(); while (SDL_PollEvent(&event)) { if (event.type == SDL_EVENT_QUIT) { quit = true; @@ -125,6 +134,13 @@ int main(int argc, char **argv) { } } + float avgFPS = countedFrames / ( fpsTimer.getTicks() / 1000.f ); + if( avgFPS > 2000000 ) { + avgFPS = 0; + } + + std::cout << "FPS Average " << avgFPS << std::endl;; + // Do not change, this makes multiple windows not crash for (size_t i = 0; i < windows.size(); ++i) { windows[i]->update(); @@ -142,6 +158,15 @@ int main(int argc, char **argv) { if (allWindowsClosed) { quit = true; } + + ++countedFrames; + + int frameTicks = capTimer.getTicks(); + if( frameTicks < 1000 / 60 ) + { + //Wait remaining time + SDL_Delay( 1000 / 60 - frameTicks ); + } } TTF_CloseFont(font); diff --git a/src/sdl/Emulator.cpp b/src/sdl/Emulator.cpp index d8e7bee..42b40de 100644 --- a/src/sdl/Emulator.cpp +++ b/src/sdl/Emulator.cpp @@ -61,7 +61,7 @@ void Emulator::render() { this->renderWrapper.render(this->renderer); SDL_RenderPresent(this->renderer); - SDL_Delay(16); + // SDL_Delay(16); } void Emulator::resize(SDL_Event &event) { From c2bea12bf444d8967415faa547c693556c3c0cd8 Mon Sep 17 00:00:00 2001 From: doom <61824552+JustDoom@users.noreply.github.com> Date: Tue, 17 Dec 2024 17:37:12 +1000 Subject: [PATCH 16/28] FPS debugger --- src/Timer.cpp | 116 +++++++++++++++++--------------------------------- src/Timer.h | 24 ++++------- src/main.cpp | 24 +++++------ 3 files changed, 60 insertions(+), 104 deletions(-) diff --git a/src/Timer.cpp b/src/Timer.cpp index e996b3d..e039ed5 100644 --- a/src/Timer.cpp +++ b/src/Timer.cpp @@ -1,103 +1,67 @@ #include "Timer.h" -Timer::Timer() -{ - //Initialize the variables - mStartTicks = 0; - mPausedTicks = 0; - - mPaused = false; - mStarted = false; -} - -void Timer::start() -{ - //Start the timer - mStarted = true; +#include - //Unpause the timer - mPaused = false; +Timer::Timer() { + this->startTicks = 0; + this->pausedTicks = 0; - //Get the current clock time - mStartTicks = SDL_GetTicks(); - mPausedTicks = 0; + this->paused = false; + this->started = false; } -void Timer::stop() -{ - //Stop the timer - mStarted = false; +void Timer::start() { + this->started = true; + this->paused = false; - //Unpause the timer - mPaused = false; + this->startTicks = SDL_GetTicks(); + this->pausedTicks = 0; +} - //Clear tick variables - mStartTicks = 0; - mPausedTicks = 0; +void Timer::stop() { + this->started = false; + this->paused = false; + this->startTicks = 0; + this->pausedTicks = 0; } -void Timer::pause() -{ - //If the timer is running and isn't already paused - if( mStarted && !mPaused ) - { - //Pause the timer - mPaused = true; - - //Calculate the paused ticks - mPausedTicks = SDL_GetTicks() - mStartTicks; - mStartTicks = 0; +void Timer::pause() { + if(this->started && !this->paused) { + this->paused = true; + + this->pausedTicks = SDL_GetTicks() - this->startTicks; + this->startTicks = 0; } } -void Timer::unpause() -{ - //If the timer is running and paused - if( mStarted && mPaused ) - { - //Unpause the timer - mPaused = false; +void Timer::unpause() { + if(this->started && this->paused) { + this->paused = false; - //Reset the starting ticks - mStartTicks = SDL_GetTicks() - mPausedTicks; + this->startTicks = SDL_GetTicks() - this->pausedTicks; - //Reset the paused ticks - mPausedTicks = 0; + this->pausedTicks = 0; } } -Uint64 Timer::getTicks() -{ - //The actual timer time - Uint64 time = 0; - - //If the timer is running - if( mStarted ) - { - //If the timer is paused - if( mPaused ) - { - //Return the number of ticks when the timer was paused - time = mPausedTicks; - } - else - { - //Return the current time minus the start time - time = SDL_GetTicks() - mStartTicks; +uint64_t Timer::getTicks() const { + uint64_t time = 0; + + if(this->started) { + if(this->paused) { + time = this->pausedTicks; + } else { + time = SDL_GetTicks() - this->startTicks; } } return time; } -bool Timer::isStarted() -{ - //Timer is running and paused or unpaused - return mStarted; +bool Timer::isStarted() const { + return this->started; } -bool Timer::isPaused() -{ - //Timer is running and paused - return mPaused && mStarted; +bool Timer::isPaused() const { + return this->paused && this->started; } \ No newline at end of file diff --git a/src/Timer.h b/src/Timer.h index adc32cb..d0d723c 100644 --- a/src/Timer.h +++ b/src/Timer.h @@ -1,36 +1,28 @@ #ifndef TIMER_H #define TIMER_H -#include +#include class Timer { public: - //Initializes variables Timer(); - //The various clock actions void start(); void stop(); void pause(); void unpause(); - //Gets the timer's time - Uint64 getTicks(); + uint64_t getTicks() const; - //Checks the status of the timer - bool isStarted(); - bool isPaused(); + bool isStarted() const; + bool isPaused() const; private: - //The clock time when the timer started - Uint64 mStartTicks; + uint64_t startTicks; + uint64_t pausedTicks; - //The ticks stored when the timer was paused - Uint64 mPausedTicks; - - //The timer status - bool mPaused; - bool mStarted; + bool paused; + bool started; }; #endif \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index f41cbe2..3cb9665 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -113,6 +113,7 @@ int main(int argc, char **argv) { std::vector> windows; windows.emplace_back(std::make_unique(font, configFilePath, romFiles, romDirectories, windows))->init(); + bool debug = false; bool quit = false; SDL_Event event; @@ -134,13 +135,6 @@ int main(int argc, char **argv) { } } - float avgFPS = countedFrames / ( fpsTimer.getTicks() / 1000.f ); - if( avgFPS > 2000000 ) { - avgFPS = 0; - } - - std::cout << "FPS Average " << avgFPS << std::endl;; - // Do not change, this makes multiple windows not crash for (size_t i = 0; i < windows.size(); ++i) { windows[i]->update(); @@ -159,13 +153,19 @@ int main(int argc, char **argv) { quit = true; } - ++countedFrames; + float avgFPS = countedFrames / (fpsTimer.getTicks() / 1000.f); + if(avgFPS > 2000000) { + avgFPS = 0; + } + if (debug) { + std::cout << "FPS: " << avgFPS << std::endl; + } + + ++countedFrames; int frameTicks = capTimer.getTicks(); - if( frameTicks < 1000 / 60 ) - { - //Wait remaining time - SDL_Delay( 1000 / 60 - frameTicks ); + if(frameTicks < 1000 / 60) { + SDL_Delay(1000 / 60 - frameTicks); } } From dd56bae2937ca94da6768f2cf7105b062becf6a8 Mon Sep 17 00:00:00 2001 From: doom <61824552+JustDoom@users.noreply.github.com> Date: Tue, 17 Dec 2024 18:08:28 +1000 Subject: [PATCH 17/28] Properly remove window on close --- src/main.cpp | 12 ++++++++---- src/sdl/Emulator.cpp | 13 ------------- src/sdl/Window.cpp | 9 +++++++-- src/sdl/Window.h | 4 +++- 4 files changed, 18 insertions(+), 20 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 3cb9665..87c6c4d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -136,7 +136,11 @@ int main(int argc, char **argv) { } // Do not change, this makes multiple windows not crash - for (size_t i = 0; i < windows.size(); ++i) { + for (int i = 0; i < windows.size(); ++i) { + if (windows[i]->isDestroyed()) { + windows.erase(windows.begin() + i); + continue; + } windows[i]->update(); windows[i]->render(); } @@ -149,12 +153,12 @@ int main(int argc, char **argv) { } } - if (allWindowsClosed) { + if (allWindowsClosed || windows.empty()) { quit = true; } float avgFPS = countedFrames / (fpsTimer.getTicks() / 1000.f); - if(avgFPS > 2000000) { + if (avgFPS > 2000000) { avgFPS = 0; } @@ -164,7 +168,7 @@ int main(int argc, char **argv) { ++countedFrames; int frameTicks = capTimer.getTicks(); - if(frameTicks < 1000 / 60) { + if (frameTicks < 1000 / 60) { SDL_Delay(1000 / 60 - frameTicks); } } diff --git a/src/sdl/Emulator.cpp b/src/sdl/Emulator.cpp index 42b40de..daefae6 100644 --- a/src/sdl/Emulator.cpp +++ b/src/sdl/Emulator.cpp @@ -38,30 +38,17 @@ bool Emulator::handleEvent(SDL_Event& event) { return true; } -// void Emulator::focus() { -// if (!mShown) { -// SDL_ShowWindow(mWindow); -// } -// -// SDL_RaiseWindow(mWindow); -// } - void Emulator::update() { // Run a cycle of the emulator this->cpu.cycle(); } void Emulator::render() { - // Set renderer draw color SDL_SetRenderDrawColor(this->renderer, 0, 0, 0, 0); - // Clear the window SDL_RenderClear(this->renderer); - // Render the window this->renderWrapper.render(this->renderer); SDL_RenderPresent(this->renderer); - - // SDL_Delay(16); } void Emulator::resize(SDL_Event &event) { diff --git a/src/sdl/Window.cpp b/src/sdl/Window.cpp index e42b0ee..cac2420 100644 --- a/src/sdl/Window.cpp +++ b/src/sdl/Window.cpp @@ -97,12 +97,17 @@ void Window::resize(SDL_Event &event) { } - void Window::close() { SDL_DestroyRenderer(this->renderer); SDL_DestroyWindow(this->window); + this->destroyed = true; } -bool Window::isShown() { +bool Window::isShown() const { return true; } + +bool Window::isDestroyed() const { + return this->destroyed; +} + diff --git a/src/sdl/Window.h b/src/sdl/Window.h index 5c6bb60..68986fc 100644 --- a/src/sdl/Window.h +++ b/src/sdl/Window.h @@ -18,6 +18,7 @@ class Window { bool fullscreen = false; bool minimised = false; bool shown = false; + bool destroyed = false; bool debug = false; public: @@ -31,7 +32,8 @@ class Window { virtual void resize(SDL_Event& event); virtual void close(); - bool isShown(); + bool isShown() const; + bool isDestroyed() const; }; #endif \ No newline at end of file From 98d94627147a5e1fbba3a4c046e55fad5845e7f8 Mon Sep 17 00:00:00 2001 From: doom <61824552+JustDoom@users.noreply.github.com> Date: Tue, 17 Dec 2024 18:32:46 +1000 Subject: [PATCH 18/28] Clean up some code/style --- src/emulator/Cpu.cpp | 2 +- src/emulator/Cpu.h | 2 +- src/emulator/Keyboard.h | 4 +-- src/emulator/Renderer.cpp | 2 +- src/emulator/Renderer.h | 2 +- src/emulator/Speaker.h | 2 +- src/main.cpp | 13 ++++------ src/sdl/Emulator.cpp | 12 ++++----- src/sdl/InputHandler.cpp | 18 ++++++------- src/sdl/InputHandler.h | 1 - src/sdl/MainMenu.cpp | 43 ++++++++++++++++---------------- src/sdl/Window.cpp | 6 ----- src/sdl/emulator/SdlKeyboard.cpp | 12 ++++----- src/sdl/emulator/SdlKeyboard.h | 22 +--------------- src/sdl/emulator/SdlRenderer.cpp | 7 ++---- src/sdl/emulator/SdlRenderer.h | 2 +- src/sdl/emulator/SdlSpeaker.h | 1 - src/sdl/ui/TextButton.cpp | 22 +++++++--------- src/sdl/ui/TextButton.h | 6 ++--- src/util/MiscUtil.h | 2 +- 20 files changed, 67 insertions(+), 114 deletions(-) diff --git a/src/emulator/Cpu.cpp b/src/emulator/Cpu.cpp index bac905e..f3c87f1 100644 --- a/src/emulator/Cpu.cpp +++ b/src/emulator/Cpu.cpp @@ -205,12 +205,12 @@ void Cpu::runInstruction(const uint16_t opcode) { break; } case 0xD000: { - const uint16_t width = 8; const uint16_t height = (opcode & 0xF); this->registers[0xF] = 0; for (uint16_t row = 0; row < height; row++) { + constexpr uint16_t width = 8; uint8_t sprite = this->memory[this->address + row]; for (uint16_t col = 0; col < width; col++) { diff --git a/src/emulator/Cpu.h b/src/emulator/Cpu.h index fd63def..090d336 100644 --- a/src/emulator/Cpu.h +++ b/src/emulator/Cpu.h @@ -40,4 +40,4 @@ class Cpu { Speaker* speaker; }; -#endif //CPU_H +#endif \ No newline at end of file diff --git a/src/emulator/Keyboard.h b/src/emulator/Keyboard.h index bc1dfd2..ef05f87 100644 --- a/src/emulator/Keyboard.h +++ b/src/emulator/Keyboard.h @@ -34,6 +34,4 @@ class Keyboard { bool isKeyPressed(int keyCode); }; - - -#endif //KEYBOARD_H +#endif \ No newline at end of file diff --git a/src/emulator/Renderer.cpp b/src/emulator/Renderer.cpp index bcfba69..28445f5 100644 --- a/src/emulator/Renderer.cpp +++ b/src/emulator/Renderer.cpp @@ -20,7 +20,7 @@ bool Renderer::setPixel(uint8_t x, uint16_t y) { x %= this->columns; y %= this->rows; - uint16_t pixelLoc = x + (y * this->columns); + const uint16_t pixelLoc = x + (y * this->columns); this->display[pixelLoc] = !this->display[pixelLoc]; return !this->display[pixelLoc]; diff --git a/src/emulator/Renderer.h b/src/emulator/Renderer.h index f7c4387..f5d868b 100644 --- a/src/emulator/Renderer.h +++ b/src/emulator/Renderer.h @@ -26,4 +26,4 @@ class Renderer { std::vector display; }; -#endif //RENDERER_H +#endif \ No newline at end of file diff --git a/src/emulator/Speaker.h b/src/emulator/Speaker.h index 7a95a8c..acc170f 100644 --- a/src/emulator/Speaker.h +++ b/src/emulator/Speaker.h @@ -8,4 +8,4 @@ class Speaker { virtual void stop() = 0; }; -#endif //SPEAKER_H +#endif \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 87c6c4d..af8d77d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,17 +1,14 @@ #include +#include #include -#include #include -#include "sdl/MainMenu.h" - -#include - -#include "util/MiscUtil.h" - -#include +#include #include +#include +#include "sdl/MainMenu.h" +#include "util/MiscUtil.h" #include "Timer.h" int main(int argc, char **argv) { diff --git a/src/sdl/Emulator.cpp b/src/sdl/Emulator.cpp index daefae6..9e30923 100644 --- a/src/sdl/Emulator.cpp +++ b/src/sdl/Emulator.cpp @@ -1,5 +1,7 @@ #include "Emulator.h" +#include + Emulator::Emulator(const std::string &rom) : cpu(&renderWrapper, &keyboard, &speaker), rom(rom) {} void Emulator::init() { @@ -16,9 +18,8 @@ void Emulator::init() { return; } - cpu.loadSpritesIntoMemory(); - - cpu.loadProgramIntoMemory(&file); + this->cpu.loadSpritesIntoMemory(); + this->cpu.loadProgramIntoMemory(&file); } bool Emulator::handleEvent(SDL_Event& event) { @@ -28,10 +29,10 @@ bool Emulator::handleEvent(SDL_Event& event) { switch (event.type) { case SDL_EVENT_KEY_DOWN: - keyboard.handleKeyDown(event.key.scancode); + this->keyboard.handleKeyDown(event.key.scancode); break; case SDL_EVENT_KEY_UP: - keyboard.handleKeyUp(event.key.scancode); + this->keyboard.handleKeyUp(event.key.scancode); break; } @@ -39,7 +40,6 @@ bool Emulator::handleEvent(SDL_Event& event) { } void Emulator::update() { - // Run a cycle of the emulator this->cpu.cycle(); } diff --git a/src/sdl/InputHandler.cpp b/src/sdl/InputHandler.cpp index da10154..0b47b0b 100644 --- a/src/sdl/InputHandler.cpp +++ b/src/sdl/InputHandler.cpp @@ -9,9 +9,8 @@ void InputHandler::addKey(SDL_Keycode key) { this->keys.emplace_back(key); } -void InputHandler::removeKey(SDL_Keycode key) { - auto it = std::find(this->keys.begin(), this->keys.end(), key); - if (it != this->keys.end()) { +void InputHandler::removeKey(const SDL_Keycode key) { + if (const auto it = std::find(this->keys.begin(), this->keys.end(), key); it != this->keys.end()) { this->keys.erase(it); } } @@ -23,11 +22,11 @@ void InputHandler::updateLastKeys() { } } -bool InputHandler::isJustPressed(SDL_Keycode key) { +bool InputHandler::isJustPressed(const SDL_Keycode key) { return std::count(this->keys.begin(), this->keys.end(), key) > 0 && std::count(this->lastKeys.begin(), this->lastKeys.end(), key) == 0; } -bool InputHandler::isPressed(SDL_Keycode key) { +bool InputHandler::isPressed(const SDL_Keycode key) { return std::count(this->keys.begin(), this->keys.end(), key); } @@ -36,9 +35,8 @@ void InputHandler::addButton(SDL_Keycode button) { this->mouse.emplace_back(button); } -void InputHandler::removeButton(SDL_Keycode button) { - auto it = std::find(this->mouse.begin(), this->mouse.end(), button); - if (it != this->mouse.end()) { +void InputHandler::removeButton(const SDL_Keycode button) { + if (const auto it = std::find(this->mouse.begin(), this->mouse.end(), button); it != this->mouse.end()) { this->mouse.erase(it); } } @@ -50,10 +48,10 @@ void InputHandler::updateLastMouse() { } } -bool InputHandler::isJustClicked(SDL_Keycode button) { +bool InputHandler::isJustClicked(const SDL_Keycode button) { return std::count(this->mouse.begin(), this->mouse.end(), button) && !std::count(this->lastMouse.begin(), this->lastMouse.end(), button); } -bool InputHandler::isClicked(SDL_Keycode button) { +bool InputHandler::isClicked(const SDL_Keycode button) { return std::count(this->mouse.begin(), this->mouse.end(), button); } \ No newline at end of file diff --git a/src/sdl/InputHandler.h b/src/sdl/InputHandler.h index f3d615e..16210b7 100644 --- a/src/sdl/InputHandler.h +++ b/src/sdl/InputHandler.h @@ -27,5 +27,4 @@ class InputHandler { bool isClicked(SDL_Keycode button); }; - #endif \ No newline at end of file diff --git a/src/sdl/MainMenu.cpp b/src/sdl/MainMenu.cpp index 0d2a095..78f0ce0 100644 --- a/src/sdl/MainMenu.cpp +++ b/src/sdl/MainMenu.cpp @@ -3,7 +3,6 @@ #include #include #include -#include #include #include @@ -27,7 +26,7 @@ void MainMenu::init() { for (auto& thing: this->romFiles) { for (std::string& file: thing.second) { - TTF_Text* text = TTF_CreateText(textEngine, font, MiscUtil::getFileFromPath(file).c_str(), 0); + TTF_Text* text = TTF_CreateText(this->textEngine, this->font, MiscUtil::getFileFromPath(file).c_str(), 0); if (!text) { SDL_Log("Failed to create text: %s\n", SDL_GetError()); return; @@ -37,7 +36,7 @@ void MainMenu::init() { } } - TTF_Text* text = TTF_CreateText(textEngine, font, "Select ROM", 0); + TTF_Text* text = TTF_CreateText(this->textEngine, this->font, "Select ROM", 0); if (!text) { SDL_Log("Failed to create text: %s\n", SDL_GetError()); return; @@ -74,11 +73,11 @@ void MainMenu::update() { SDL_FPoint point{}; SDL_GetMouseState(&point.x, &point.y); - if (inputHandler.isJustPressed(SDL_SCANCODE_F3)) { + if (this->inputHandler.isJustPressed(SDL_SCANCODE_F3)) { this->debug = !this->debug; } - for (auto &romButton: roms) { + for (auto &romButton: this->roms) { romButton.second.update(this->inputHandler, point); if (!romButton.second.isJustClicked()) { @@ -102,11 +101,11 @@ void MainMenu::update() { } libconfig::Setting &directories = settings["directories"]; - directories.add(libconfig::Setting::TypeString) = outPath.get(); + directories.add(libconfig::Setting::TypeString) = this->outPath.get(); - romDirectories.emplace_back(outPath.get()); + this->romDirectories.emplace_back(this->outPath.get()); - for (const auto &file: std::filesystem::directory_iterator(outPath.get())) { + for (const auto &file: std::filesystem::directory_iterator(this->outPath.get())) { if (file.is_directory()) { continue; // TODO: Make sure its a file that can be emulated, at least basic checks so it isn't like // a word doc @@ -115,22 +114,22 @@ void MainMenu::update() { printf("Processing file - : %s\n", file.path().c_str()); // Check if the rom directory doesn't exist in romFiles, then add it - if (romFiles.find(&romDirectories.back()) == romFiles.end()) { - romFiles.emplace(&romDirectories.back(), std::vector()); + if (this->romFiles.find(&this->romDirectories.back()) == this->romFiles.end()) { + this->romFiles.emplace(&this->romDirectories.back(), std::vector()); } // Add the file path to the romFiles entry - romFiles.find(&romDirectories.back())->second.emplace_back(file.path().string()); + this->romFiles.find(&this->romDirectories.back())->second.emplace_back(file.path().string()); - TTF_Text* text = TTF_CreateText(textEngine, font, file.path().filename().string().c_str(), 0); + TTF_Text* text = TTF_CreateText(this->textEngine, this->font, file.path().filename().string().c_str(), 0); if (!text) { SDL_Log("Failed to create text: %s\n", SDL_GetError()); return; } - roms.emplace(file.path().string(), TextButton(0, 25.0f * roms.size(), WIDTH, 25, text)); + this->roms.emplace(file.path().string(), TextButton(0, 25.0f * this->roms.size(), WIDTH, 25, text)); } - config.writeFile(configFilePath); + config.writeFile(this->configFilePath); } } @@ -140,13 +139,13 @@ void MainMenu::update() { void MainMenu::render() { SDL_SetRenderDrawColor(this->renderer, 0x00, 0x00, 0x00, 255); - SDL_RenderClear(renderer); - for (auto &romButton: roms) { - romButton.second.draw(renderer); + SDL_RenderClear(this->renderer); + for (auto &romButton: this->roms) { + romButton.second.draw(this->renderer); } - this->chooseFolder->draw(renderer); + this->chooseFolder->draw(this->renderer); - SDL_RenderPresent(renderer); + SDL_RenderPresent(this->renderer); } void MainMenu::resize(SDL_Event& event) { @@ -156,13 +155,13 @@ void MainMenu::resize(SDL_Event& event) { SDL_FPoint point{}; SDL_GetMouseState(&point.x, &point.y); - for (auto& romButton : roms) { + for (auto& romButton : this->roms) { romButton.second.updateSize(this->originalSize, size); romButton.second.update(this->inputHandler, point); } - chooseFolder->updateSize(this->originalSize,size); - chooseFolder->update(this->inputHandler, point); + this->chooseFolder->updateSize(this->originalSize,size); + this->chooseFolder->update(this->inputHandler, point); } void MainMenu::close() { diff --git a/src/sdl/Window.cpp b/src/sdl/Window.cpp index cac2420..63f88fa 100644 --- a/src/sdl/Window.cpp +++ b/src/sdl/Window.cpp @@ -37,8 +37,6 @@ void Window::init() { bool Window::handleEvent(SDL_Event &event) { if (event.window.windowID == this->windowId) { - bool caption = false; - switch (event.type) { case SDL_EVENT_WINDOW_SHOWN: this->shown = true; @@ -54,19 +52,15 @@ bool Window::handleEvent(SDL_Event &event) { break; case SDL_EVENT_WINDOW_MOUSE_ENTER: this->mouseFocus = true; - caption = true; break; case SDL_EVENT_WINDOW_MOUSE_LEAVE: this->mouseFocus = false; - caption = false; break; case SDL_EVENT_WINDOW_FOCUS_GAINED: this->keyboardFocus = true; - caption = true; break; case SDL_EVENT_WINDOW_FOCUS_LOST: this->keyboardFocus = false; - caption = true; break; case SDL_EVENT_WINDOW_MINIMIZED: this->minimised = true; diff --git a/src/sdl/emulator/SdlKeyboard.cpp b/src/sdl/emulator/SdlKeyboard.cpp index 4a51224..045bcef 100644 --- a/src/sdl/emulator/SdlKeyboard.cpp +++ b/src/sdl/emulator/SdlKeyboard.cpp @@ -1,9 +1,8 @@ #include "SdlKeyboard.h" -void SdlKeyboard::handleKeyDown(uint8_t keyCode) { - auto keyMapIter = this->KEYMAP.find(keyCode); - if (keyMapIter != this->KEYMAP.end()) { - uint8_t key = keyMapIter->second; +void SdlKeyboard::handleKeyDown(const uint8_t keyCode) { + if (const auto keyMapIter = this->KEYMAP.find(keyCode); keyMapIter != this->KEYMAP.end()) { + const uint8_t key = keyMapIter->second; this->keysPressed[key] = true; if (this->onNextKeyPress) { this->onNextKeyPress(key); @@ -13,9 +12,8 @@ void SdlKeyboard::handleKeyDown(uint8_t keyCode) { } void SdlKeyboard::handleKeyUp(uint8_t keyCode) { - auto keyMapIter = this->KEYMAP.find(keyCode); - if (keyMapIter != this->KEYMAP.end()) { - uint8_t key = keyMapIter->second; + if (const auto keyMapIter = this->KEYMAP.find(keyCode); keyMapIter != this->KEYMAP.end()) { + const uint8_t key = keyMapIter->second; this->keysPressed[key] = false; } } diff --git a/src/sdl/emulator/SdlKeyboard.h b/src/sdl/emulator/SdlKeyboard.h index 3e09c58..ca3491d 100644 --- a/src/sdl/emulator/SdlKeyboard.h +++ b/src/sdl/emulator/SdlKeyboard.h @@ -2,31 +2,11 @@ #define INC_8CHOCCHIP_SDLKEYBOARD_H #include "../../emulator/Keyboard.h" -#include class SdlKeyboard : public Keyboard { -private: - // std::unordered_map KEYMAP = { - // {49, 0x1}, // 1 - // {50, 0x2}, // 2 - // {51, 0x3}, // 3 - // {52, 0xc}, // 4 - // {113, 0x4}, // Q - // {119, 0x5}, // W - // {101, 0x6}, // E - // {114, 0xD}, // R - // {97, 0x7}, // A - // {115, 0x8}, // S - // {100, 0x9}, // D - // {102, 0xE}, // F - // {122, 0xA}, // Z - // {120, 0x0}, // X - // {99, 0xB}, // C - // {118, 0xF} // V - // }; public: void handleKeyDown(uint8_t keyCode); void handleKeyUp(uint8_t keyCode); }; -#endif // INC_8CHOCCHIP_SDLKEYBOARD_H +#endif \ No newline at end of file diff --git a/src/sdl/emulator/SdlRenderer.cpp b/src/sdl/emulator/SdlRenderer.cpp index 0657e81..fd7c6e3 100644 --- a/src/sdl/emulator/SdlRenderer.cpp +++ b/src/sdl/emulator/SdlRenderer.cpp @@ -1,10 +1,7 @@ #include "SdlRenderer.h" -#include -#include - -void SdlRenderer::drawPixel(SDL_Renderer* renderer, uint16_t x, uint16_t y) { - const SDL_FRect rect = {x * scale, y * scale, scale, scale}; +void SdlRenderer::drawPixel(SDL_Renderer* renderer, const uint16_t x, const uint16_t y) { + const SDL_FRect rect = {x * this->scale, y * this->scale, this->scale, this->scale}; SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255); // Set color to white SDL_RenderFillRect(renderer, &rect); } diff --git a/src/sdl/emulator/SdlRenderer.h b/src/sdl/emulator/SdlRenderer.h index 4572f52..408cddc 100644 --- a/src/sdl/emulator/SdlRenderer.h +++ b/src/sdl/emulator/SdlRenderer.h @@ -6,7 +6,7 @@ class SdlRenderer : public Renderer { private: - float scale = 15; + const float scale = 15; public: void drawPixel(SDL_Renderer* renderer, uint16_t x, uint16_t y) override; float getScale() const; diff --git a/src/sdl/emulator/SdlSpeaker.h b/src/sdl/emulator/SdlSpeaker.h index 539b1e9..da0d7cf 100644 --- a/src/sdl/emulator/SdlSpeaker.h +++ b/src/sdl/emulator/SdlSpeaker.h @@ -4,7 +4,6 @@ #include "../../emulator/Speaker.h" class SdlSpeaker : public Speaker { -private: public: SdlSpeaker(); diff --git a/src/sdl/ui/TextButton.cpp b/src/sdl/ui/TextButton.cpp index 59c213b..aa72757 100644 --- a/src/sdl/ui/TextButton.cpp +++ b/src/sdl/ui/TextButton.cpp @@ -2,13 +2,13 @@ #include -TextButton::TextButton(float x, float y, float width, float height, TTF_Text* text) : +TextButton::TextButton(const float x, const float y, const float width, const float height, TTF_Text* text) : text(text), idleColor(SDL_Color{192, 192, 192, 255}), hoverColor(SDL_Color{128, 128, 128, 255}), activeColor(SDL_Color{64, 64, 64, 255}), deleteColor(SDL_Color{255, 0, 0, 255}) { this->button = SDL_FRect{x, y, width, height}; - currentColor = idleColor; + this->currentColor = this->idleColor; TTF_SetTextColor(text, 0, 0, 0, 255); SDL_Point textSize{}; @@ -16,8 +16,8 @@ TextButton::TextButton(float x, float y, float width, float height, TTF_Text* te this->textPos.x = x + width / 2 - textSize.x / 2; this->textPos.y = y + height / 2 - textSize.y / 2 + 4; - this->originalPosition = {button.x, button.y}; - this->originalSize = {button.w, button.h}; + this->originalPosition = {this->button.x, this->button.y}; + this->originalSize = {this->button.w, this->button.h}; this->isPressed = false; this->isHovered = false; @@ -30,12 +30,12 @@ void TextButton::updateSize(const SDL_Point& originalSize, const SDL_Point& upda this->button.h = this->originalSize.y / originalSize.y * updatedSize.y; SDL_Point textSize{}; - TTF_GetTextSize(text, &textSize.x, &textSize.y); + TTF_GetTextSize(this->text, &textSize.x, &textSize.y); this->textPos.x = this->button.x + this->button.w / 2 - textSize.x / 2; this->textPos.y = this->button.y + this->button.h / 2 - textSize.y / 2 + 4; } -void TextButton::update(InputHandler& inputHandler, SDL_FPoint& pos) { +void TextButton::update(InputHandler& inputHandler, const SDL_FPoint & pos) { this->lastPressed = this->isPressed; this->isHovered = SDL_PointInRectFloat(&pos, &this->button); @@ -52,18 +52,14 @@ void TextButton::update(InputHandler& inputHandler, SDL_FPoint& pos) { } } -void TextButton::updateColour(SDL_Color& color) { - // if (this->currentColor == color) { - // return; - // } - +void TextButton::updateColour(const SDL_Color & color) { this->currentColor = color; } void TextButton::draw(SDL_Renderer* window) const { - SDL_SetRenderDrawColor(window, currentColor.r, currentColor.g, currentColor.b, currentColor.a); + SDL_SetRenderDrawColor(window, this->currentColor.r, this->currentColor.g, this->currentColor.b, this->currentColor.a); SDL_RenderFillRect(window, &this->button); - if (!TTF_DrawRendererText(text, textPos.x, textPos.y)) { + if (!TTF_DrawRendererText(this->text, this->textPos.x, this->textPos.y)) { SDL_Log("Text rendering failed: %s\n", SDL_GetError()); } } diff --git a/src/sdl/ui/TextButton.h b/src/sdl/ui/TextButton.h index 454ca85..10cb930 100644 --- a/src/sdl/ui/TextButton.h +++ b/src/sdl/ui/TextButton.h @@ -27,7 +27,7 @@ class TextButton { void updateSize(const SDL_Point & originalSize, const SDL_Point & updatedSize); - void update(InputHandler& inputHandler, SDL_FPoint& pos); + void update(InputHandler& inputHandler, const SDL_FPoint & pos); void draw(SDL_Renderer* window) const; @@ -35,9 +35,7 @@ class TextButton { bool isJustClicked() const; - void updateColour(SDL_Color& color); + void updateColour(const SDL_Color & color); }; - - #endif \ No newline at end of file diff --git a/src/util/MiscUtil.h b/src/util/MiscUtil.h index b2591a8..d67726e 100644 --- a/src/util/MiscUtil.h +++ b/src/util/MiscUtil.h @@ -11,4 +11,4 @@ class MiscUtil { static std::string getFileFromPath(std::string path); }; -#endif //MISCUTIL_H +#endif \ No newline at end of file From 217a471621b2f2616e0cf88bf6e9a58369240adc Mon Sep 17 00:00:00 2001 From: doom <61824552+JustDoom@users.noreply.github.com> Date: Tue, 17 Dec 2024 22:08:01 +1000 Subject: [PATCH 19/28] Reimplement sound --- src/sdl/MainMenu.cpp | 2 +- src/sdl/emulator/SdlSpeaker.cpp | 43 +++++++++++++++++++++++++++++++-- src/sdl/emulator/SdlSpeaker.h | 16 ++++++++++++ 3 files changed, 58 insertions(+), 3 deletions(-) diff --git a/src/sdl/MainMenu.cpp b/src/sdl/MainMenu.cpp index 78f0ce0..3bc70e9 100644 --- a/src/sdl/MainMenu.cpp +++ b/src/sdl/MainMenu.cpp @@ -138,7 +138,7 @@ void MainMenu::update() { } void MainMenu::render() { - SDL_SetRenderDrawColor(this->renderer, 0x00, 0x00, 0x00, 255); + SDL_SetRenderDrawColor(this->renderer, 0, 0, 0, 255); SDL_RenderClear(this->renderer); for (auto &romButton: this->roms) { romButton.second.draw(this->renderer); diff --git a/src/sdl/emulator/SdlSpeaker.cpp b/src/sdl/emulator/SdlSpeaker.cpp index 064e17c..d399c40 100644 --- a/src/sdl/emulator/SdlSpeaker.cpp +++ b/src/sdl/emulator/SdlSpeaker.cpp @@ -1,14 +1,53 @@ #include "SdlSpeaker.h" +#include #include +#include +#include -SdlSpeaker::SdlSpeaker() { +SdlSpeaker::SdlSpeaker() : amplitude(28000), sampleRate(44100), frequency(220), duration(500), sampleCount((sampleRate * duration) / 1000), beepData(sampleCount) { + SDL_AudioSpec desiredSpec; + SDL_zero(desiredSpec); + desiredSpec.freq = this->sampleRate; + desiredSpec.format = SDL_AUDIO_S16; + desiredSpec.channels = 1; + this->audioStream = SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &desiredSpec, nullptr, nullptr); + if (this->audioStream == nullptr) { + std::cerr << "Failed to open audio device stream: " << SDL_GetError() << std::endl; + return; + } + + generateSample(); +} + +SdlSpeaker::~SdlSpeaker() { + if (this->audioStream != nullptr) { + SDL_DestroyAudioStream(this->audioStream); + } + SDL_Quit(); } void SdlSpeaker::play() { - std::cout << "forkin beepin" << std::endl; + if (this->audioStream != nullptr) { + if (SDL_GetAudioStreamAvailable(this->audioStream) == 0) { + generateSample(); + } + SDL_ResumeAudioStreamDevice(this->audioStream); + } } void SdlSpeaker::stop() { + if (this->audioStream != nullptr) { + SDL_PauseAudioStreamDevice(this->audioStream); + } +} + +void SdlSpeaker::generateSample() { + for (int i = 0; i < this->sampleCount; ++i) { + this->beepData[i] = static_cast(this->amplitude * std::sin(2.0 * M_PI * this->frequency * i / this->sampleRate)); + } + if (SDL_PutAudioStreamData(this->audioStream, this->beepData.data(), this->beepData.size() * sizeof(int16_t)) < 0) { + std::cerr << "Failed to put data into audio stream: " << SDL_GetError() << std::endl; + } } \ No newline at end of file diff --git a/src/sdl/emulator/SdlSpeaker.h b/src/sdl/emulator/SdlSpeaker.h index da0d7cf..49de784 100644 --- a/src/sdl/emulator/SdlSpeaker.h +++ b/src/sdl/emulator/SdlSpeaker.h @@ -1,14 +1,30 @@ #ifndef INC_8CHOCCHIP_SDLSPEAKER_H #define INC_8CHOCCHIP_SDLSPEAKER_H +#include + +#include + #include "../../emulator/Speaker.h" class SdlSpeaker : public Speaker { +private: + int amplitude; + int sampleRate; + int frequency; + int duration; + int sampleCount; + + SDL_AudioSpec audioSpec; + SDL_AudioStream* audioStream; + std::vector beepData; public: SdlSpeaker(); + ~SdlSpeaker(); void play() override; void stop() override; + void generateSample(); }; #endif \ No newline at end of file From 0ebf0bbea758051cc4d5efe63284a7c5730a0c78 Mon Sep 17 00:00:00 2001 From: doom <61824552+JustDoom@users.noreply.github.com> Date: Tue, 17 Dec 2024 22:13:45 +1000 Subject: [PATCH 20/28] Update readme --- README.md | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 3dfdd79..98c6e78 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,18 @@ # 8ChocChip -**This is a basic SDL port of the emulator. The menu and sound is not yet implementated. Gameplay frame rate increased from SFML 650 or so to about 5000 with SDL so may finish this once it is more stable** - 8ChocChip is an emulator for the Chip8 software that I am working on to learn the basics around emulation. -The graphics and only library used in this is SFML which handles the window, inputs and sounds. +This uses SDL to handle graphics, input, audio and some other small things. libconfig is used to manage config and save data files, and NativeFileDialog-extended is used for basic popup windows like the file/directory selector. ## TODO There are a couple of things left to do until I would say it works well enough -- [x] ~~Fix some of the instructions that result in most programs failing except for `BLINKY`~~ Should be done but the test suite has issues I can not figure out how to fix. But is better than before and should work with most stuff +- [x] Proper flag compatibility unlike many out there - [ ] Cleaner/more optimised code - [x] Windows Support - [ ] MacOS Support (Builds, but unable to test) -- [ ] Switch to SDL from SFML +- [x] Switch to SDL from SFML ## Usage @@ -38,17 +36,17 @@ Some parts are just setting up ssh or installing dependencies which you may have ### Requirements -To build this it requires C++, CMake and whatever SFML and SFML Audio requires on your platform. Check build file above. +To build this it requires C++, CMake and whatever SDL/dependencies need. Check build file above. -# Credits +# Cred -Thanks to these two blogs that helped me through creating this emulator -[How to Create Your Very Own Chip-8 Emulator](https://www.freecodecamp.org/news/creating-your-very-own-chip-8-emulator/) -[Guide to making a CHIP-8 emulator ](https://tobiasvl.github.io/blog/write-a-chip-8-emulator/) +Thanks to these two blogs that helped me through creating this emulator. These are actually based on a "broken" emulator guide so not everything will work. But it is good as a base and learning how to do you own research into the functionality of the emulator. +- [How to Create Your Very Own Chip-8 Emulator](https://www.freecodecamp.org/news/creating-your-very-own-chip-8-emulator/) +- [Guide to making a CHIP-8 emulator ](https://tobiasvl.github.io/blog/write-a-chip-8-emulator/) ## Libraries Currently, three libraries are being used -- [SFML](https://github.com/SFML/SFML) - UI, graphics, input and sounds +- [SDL](https://github.com/libsdl-org/SDL) - UI, graphics, input and sounds - [NativeFileDialog-extended](https://github.com/btzy/nativefiledialog-extended) - Handles file dialogs for selecting files/directories. Fork of [nativefiledialog](https://github.com/mlabbe/nativefiledialog) which I used a fork of that added only CMake support, this new one adds that plus new fixes/features - [libconfig](https://github.com/hyperrealm/libconfig) - Library to manage save data (like directories that hold ROMs) and possible future config files From 3669ab1a5e9bc6bbd7ac6e9c8e01b74d724977ea Mon Sep 17 00:00:00 2001 From: doom <61824552+JustDoom@users.noreply.github.com> Date: Tue, 17 Dec 2024 22:14:23 +1000 Subject: [PATCH 21/28] Oops --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 98c6e78..01e4b5e 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ Some parts are just setting up ssh or installing dependencies which you may have To build this it requires C++, CMake and whatever SDL/dependencies need. Check build file above. -# Cred +# Credits Thanks to these two blogs that helped me through creating this emulator. These are actually based on a "broken" emulator guide so not everything will work. But it is good as a base and learning how to do you own research into the functionality of the emulator. - [How to Create Your Very Own Chip-8 Emulator](https://www.freecodecamp.org/news/creating-your-very-own-chip-8-emulator/) From a847be8a152c5bbc823d34a4b01e807fa14d4983 Mon Sep 17 00:00:00 2001 From: doom <61824552+JustDoom@users.noreply.github.com> Date: Tue, 17 Dec 2024 22:33:55 +1000 Subject: [PATCH 22/28] Probably don't quit al of SDL when the audio shuts down --- src/emulator/Speaker.h | 1 - src/sdl/emulator/SdlSpeaker.cpp | 1 - 2 files changed, 2 deletions(-) diff --git a/src/emulator/Speaker.h b/src/emulator/Speaker.h index acc170f..4ebcdef 100644 --- a/src/emulator/Speaker.h +++ b/src/emulator/Speaker.h @@ -3,7 +3,6 @@ class Speaker { public: - virtual ~Speaker() = default; virtual void play() = 0; virtual void stop() = 0; }; diff --git a/src/sdl/emulator/SdlSpeaker.cpp b/src/sdl/emulator/SdlSpeaker.cpp index d399c40..0ef7c73 100644 --- a/src/sdl/emulator/SdlSpeaker.cpp +++ b/src/sdl/emulator/SdlSpeaker.cpp @@ -25,7 +25,6 @@ SdlSpeaker::~SdlSpeaker() { if (this->audioStream != nullptr) { SDL_DestroyAudioStream(this->audioStream); } - SDL_Quit(); } void SdlSpeaker::play() { From 4443886e7e47387a5c19e33cac0da622a5454405 Mon Sep 17 00:00:00 2001 From: doom <61824552+JustDoom@users.noreply.github.com> Date: Tue, 17 Dec 2024 22:36:20 +1000 Subject: [PATCH 23/28] Typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 01e4b5e..b66d6a8 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ To build this it requires C++, CMake and whatever SDL/dependencies need. Check b # Credits -Thanks to these two blogs that helped me through creating this emulator. These are actually based on a "broken" emulator guide so not everything will work. But it is good as a base and learning how to do you own research into the functionality of the emulator. +Thanks to these two blogs that helped me through creating this emulator. These are actually based on a "broken" emulator guide so not everything will work. But it is good as a base and learning how to do your own research into the functionality of the emulator. - [How to Create Your Very Own Chip-8 Emulator](https://www.freecodecamp.org/news/creating-your-very-own-chip-8-emulator/) - [Guide to making a CHIP-8 emulator ](https://tobiasvl.github.io/blog/write-a-chip-8-emulator/) From 06d11563456d16135d8a7dd8998b0eb7670ca4d7 Mon Sep 17 00:00:00 2001 From: doom <61824552+JustDoom@users.noreply.github.com> Date: Tue, 17 Dec 2024 23:01:34 +1000 Subject: [PATCH 24/28] Reimplement command line rom starting. Also cleans up SDL init --- src/main.cpp | 64 ++++++++++++++++++++++++++++++++++++++++---- src/sdl/Emulator.cpp | 2 +- src/sdl/Emulator.h | 4 +-- src/sdl/Window.cpp | 5 ---- 4 files changed, 62 insertions(+), 13 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index af8d77d..85ef00d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -7,13 +7,12 @@ #include #include +#include "Timer.h" +#include "sdl/Emulator.h" #include "sdl/MainMenu.h" #include "util/MiscUtil.h" -#include "Timer.h" int main(int argc, char **argv) { - std::string rom; - for (int i = 0; i < argc; i++) { std::string_view arg = argv[i]; if (arg.rfind("--") != 0) continue; // TODO: Account for --longform or -sf (short form) commands. just needs a better command handler @@ -21,7 +20,57 @@ int main(int argc, char **argv) { std::string command = MiscUtil::toLowerCase(std::string(arg)); if (command == "--rom") { if (i + 1 < argc) { - // return Emulator().launch(argv[++i]); + if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0) { + std::cerr << "SDL could not initialize! SDL_Error: " << SDL_GetError() << std::endl; + return 1; + } + + std::string rom = argv[++i]; + Emulator emulator(rom); + emulator.init(); + + bool debug = false; + bool quit = false; + SDL_Event event; + + Timer fpsTimer; + Timer capTimer; + + int countedFrames = 0; + fpsTimer.start(); + + while (!quit) { + capTimer.start(); + while (SDL_PollEvent(&event)) { + if (event.type == SDL_EVENT_QUIT) { + quit = true; + } + + emulator.handleEvent(event); + } + + emulator.update(); + emulator.render(); + + float avgFPS = countedFrames / (fpsTimer.getTicks() / 1000.f); + if (avgFPS > 2000000) { + avgFPS = 0; + } + + if (debug) { + std::cout << "FPS: " << avgFPS << std::endl; + } + + ++countedFrames; + int frameTicks = capTimer.getTicks(); + if (frameTicks < 1000 / 60) { + SDL_Delay(1000 / 60 - frameTicks); + } + } + + SDL_Quit(); + + return 0; } std::cerr << "Please include the path to the file" << std::endl; return 0; @@ -96,8 +145,13 @@ int main(int argc, char **argv) { } } + if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0) { + std::cerr << "SDL could not initialize! SDL_Error: " << SDL_GetError() << std::endl; + return 1; + } + if (!TTF_Init()) { - SDL_Log("Couldn't initialize TTF: %s\n",SDL_GetError()); + SDL_Log("Couldn't initialize TTF: %s\n", SDL_GetError()); return 1; } diff --git a/src/sdl/Emulator.cpp b/src/sdl/Emulator.cpp index 9e30923..3144c31 100644 --- a/src/sdl/Emulator.cpp +++ b/src/sdl/Emulator.cpp @@ -2,7 +2,7 @@ #include -Emulator::Emulator(const std::string &rom) : cpu(&renderWrapper, &keyboard, &speaker), rom(rom) {} +Emulator::Emulator(const std::string& rom) : cpu(&renderWrapper, &keyboard, &speaker), rom(rom) {} void Emulator::init() { Window::init(); diff --git a/src/sdl/Emulator.h b/src/sdl/Emulator.h index 51e7743..76950f6 100644 --- a/src/sdl/Emulator.h +++ b/src/sdl/Emulator.h @@ -14,9 +14,9 @@ class Emulator : public Window { SdlKeyboard keyboard; Cpu cpu; - const std::string &rom; + const std::string& rom; public: - Emulator(const std::string &rom); + Emulator(const std::string& rom); void init() override; bool handleEvent(SDL_Event& event) override; diff --git a/src/sdl/Window.cpp b/src/sdl/Window.cpp index 63f88fa..b8b458b 100644 --- a/src/sdl/Window.cpp +++ b/src/sdl/Window.cpp @@ -10,11 +10,6 @@ Window::~Window() { } void Window::init() { - if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0) { - std::cerr << "SDL could not initialize! SDL_Error: " << SDL_GetError() << std::endl; - return; - } - if (!SDL_CreateWindowAndRenderer("8ChocChip - CHIP-8 Emulator", 64 * 15, 32 * 15, SDL_WINDOW_RESIZABLE, &this->window, &this->renderer)) { std::cerr << "Couldn't create window and renderer. SDL_Error: " << SDL_GetError() << std::endl; return; From a04a91c9fd06740d6faa46eea472cb8e30d401f2 Mon Sep 17 00:00:00 2001 From: doom <61824552+JustDoom@users.noreply.github.com> Date: Tue, 17 Dec 2024 23:06:42 +1000 Subject: [PATCH 25/28] Try fix windows not finding M_PI --- src/sdl/emulator/SdlSpeaker.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/sdl/emulator/SdlSpeaker.cpp b/src/sdl/emulator/SdlSpeaker.cpp index 0ef7c73..57bb2b5 100644 --- a/src/sdl/emulator/SdlSpeaker.cpp +++ b/src/sdl/emulator/SdlSpeaker.cpp @@ -1,10 +1,12 @@ #include "SdlSpeaker.h" -#include #include +#include #include #include +#include + SdlSpeaker::SdlSpeaker() : amplitude(28000), sampleRate(44100), frequency(220), duration(500), sampleCount((sampleRate * duration) / 1000), beepData(sampleCount) { SDL_AudioSpec desiredSpec; SDL_zero(desiredSpec); From 0a37fcdbcaf8d10c9639d50a5b40adeec6120202 Mon Sep 17 00:00:00 2001 From: doom <61824552+JustDoom@users.noreply.github.com> Date: Tue, 17 Dec 2024 23:11:46 +1000 Subject: [PATCH 26/28] Okay maybe now --- src/sdl/emulator/SdlSpeaker.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sdl/emulator/SdlSpeaker.cpp b/src/sdl/emulator/SdlSpeaker.cpp index 57bb2b5..e2c2dc6 100644 --- a/src/sdl/emulator/SdlSpeaker.cpp +++ b/src/sdl/emulator/SdlSpeaker.cpp @@ -1,7 +1,7 @@ #include "SdlSpeaker.h" #include -#include +#include #include #include From 945b3c74a72a2ebf2581c68a6e996e7279cf7cca Mon Sep 17 00:00:00 2001 From: doom <61824552+JustDoom@users.noreply.github.com> Date: Tue, 17 Dec 2024 23:16:42 +1000 Subject: [PATCH 27/28] FINE --- src/sdl/emulator/SdlSpeaker.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sdl/emulator/SdlSpeaker.cpp b/src/sdl/emulator/SdlSpeaker.cpp index e2c2dc6..a068e79 100644 --- a/src/sdl/emulator/SdlSpeaker.cpp +++ b/src/sdl/emulator/SdlSpeaker.cpp @@ -46,7 +46,7 @@ void SdlSpeaker::stop() { void SdlSpeaker::generateSample() { for (int i = 0; i < this->sampleCount; ++i) { - this->beepData[i] = static_cast(this->amplitude * std::sin(2.0 * M_PI * this->frequency * i / this->sampleRate)); + this->beepData[i] = static_cast(this->amplitude * std::sin(2.0 * std::acos(-1) * this->frequency * i / this->sampleRate)); } if (SDL_PutAudioStreamData(this->audioStream, this->beepData.data(), this->beepData.size() * sizeof(int16_t)) < 0) { std::cerr << "Failed to put data into audio stream: " << SDL_GetError() << std::endl; From 48068af7394595121a0ea0a238fb4ad9fe8be9a7 Mon Sep 17 00:00:00 2001 From: doom <61824552+JustDoom@users.noreply.github.com> Date: Wed, 18 Dec 2024 09:32:11 +1000 Subject: [PATCH 28/28] Fix some more compatibility issues --- src/emulator/Cpu.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/emulator/Cpu.cpp b/src/emulator/Cpu.cpp index f3c87f1..5582198 100644 --- a/src/emulator/Cpu.cpp +++ b/src/emulator/Cpu.cpp @@ -207,6 +207,8 @@ void Cpu::runInstruction(const uint16_t opcode) { case 0xD000: { const uint16_t height = (opcode & 0xF); + const uint8_t uX = this->registers[x]; + const uint8_t uY = this->registers[y]; this->registers[0xF] = 0; for (uint16_t row = 0; row < height; row++) { @@ -217,7 +219,7 @@ void Cpu::runInstruction(const uint16_t opcode) { // If the bit (sprite) is not 0, render/erase the pixel if ((sprite & 0x80) > 0) { // If setPixel returns 1, which means a pixel was erased, set VF to 1 - if (this->renderer->setPixel(this->registers[x] + col, this->registers[y] + row)) { + if (this->renderer->setPixel(uX + col, uY + row)) { this->registers[0xF] = 1; } } @@ -232,12 +234,12 @@ void Cpu::runInstruction(const uint16_t opcode) { case 0xE000: switch (opcode & 0xFF) { case 0x9E: - if (this->keyboard->isKeyPressed(this->registers[x])) { + if (this->keyboard->isKeyPressed(this->registers[x] & 0xF)) { this->pc += 2; } break; case 0xA1: - if (!this->keyboard->isKeyPressed(this->registers[x])) { + if (!this->keyboard->isKeyPressed(this->registers[x] & 0xF)) { this->pc += 2; } break; @@ -269,7 +271,7 @@ void Cpu::runInstruction(const uint16_t opcode) { this->address += this->registers[x]; break; case 0x29: - this->address = 0x50 + this->registers[x] * 5; + this->address = 0x50 + (this->registers[x] & 0xF) * 5; break; case 0x33: { uint8_t value = this->registers[x];