diff --git a/CMakeLists.txt b/CMakeLists.txt index 15e78ca..a77fe6f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -121,9 +121,9 @@ add_executable(ComSquare sources/Ram/ExtendedRam.hpp sources/Debugger/DebugCpu.cpp sources/Debugger/DebugCpu.hpp - sources/Renderer/TGUIRenderer.cpp - sources/Renderer/TGUIRenderer.h -) + sources/Renderer/QtRenderer/QtSFML.cpp sources/Renderer/QtRenderer/QtSFML.h sources/Renderer/QtRenderer/QtWidgetSFML.cpp sources/Renderer/QtRenderer/QtWidgetSFML.h) + +find_package(Qt5 COMPONENTS Widgets REQUIRED) target_link_libraries(ComSquare sfml-graphics @@ -132,4 +132,5 @@ target_link_libraries(ComSquare sfml-audio sfml-network tgui + Qt5::Widgets ) \ No newline at end of file diff --git a/main.cpp b/main.cpp index a383b9a..fe39575 100644 --- a/main.cpp +++ b/main.cpp @@ -4,8 +4,11 @@ #include #include +#include +#include #include "sources/SNES.hpp" #include "sources/Renderer/SFRenderer.hpp" +#include "sources/Renderer/QtRenderer/QtSFML.h" using namespace ComSquare; @@ -15,19 +18,10 @@ int main(int argc, char **argv) std::cout << "ComSquare:" << std::endl << "\tUsage: " << argv[0] << " rom_path" << std::endl; return 1; } - try { - Renderer::SFRenderer renderer(600, 800, 60); - SNES snes(std::make_shared(), argv[1], renderer); - snes.enableCPUDebugging(); - while (!renderer.shouldExit) { - unsigned cycleCount = snes.cpu->update(); - snes.ppu->update(cycleCount); - snes.apu->update(cycleCount); - - renderer.getEvents(); - } - } catch (std::exception &e) { - std::cerr << "An error occurred: " << e.what() << std::endl; - } - return 0; + QApplication app(argc, argv); + Renderer::QtSFML renderer(app, 600, 800); + SNES snes(std::make_shared(), argv[1], renderer); + renderer.createWindow(snes, 60); + snes.enableCPUDebugging(); + return QApplication::exec(); } \ No newline at end of file diff --git a/sources/Debugger/DebugCpu.cpp b/sources/Debugger/DebugCpu.cpp index f6fecd3..df3ba3a 100644 --- a/sources/Debugger/DebugCpu.cpp +++ b/sources/Debugger/DebugCpu.cpp @@ -8,7 +8,7 @@ namespace ComSquare::Debugger { CPUDebug::CPUDebug(ComSquare::CPU::CPU &basicCPU, SNES &snes) - : CPU::CPU(basicCPU), _renderer(600, 1000, 60), _snes(snes) + : CPU::CPU(basicCPU), _renderer(600, 1000), _snes(snes) { this->_renderer.setWindowName("CPU's Debugger"); } diff --git a/sources/Debugger/DebugCpu.hpp b/sources/Debugger/DebugCpu.hpp index edabc95..44da0b6 100644 --- a/sources/Debugger/DebugCpu.hpp +++ b/sources/Debugger/DebugCpu.hpp @@ -8,7 +8,6 @@ #include "../CPU/CPU.hpp" #include "../Renderer/SFRenderer.hpp" #include "../SNES.hpp" -#include "../Renderer/TGUIRenderer.h" namespace ComSquare::Debugger { @@ -16,7 +15,7 @@ namespace ComSquare::Debugger class CPUDebug : public CPU::CPU { private: //! @brief The debug window. - Renderer::TGUIRenderer _renderer; + Renderer::SFRenderer _renderer; //! @brief If this is set to true, the execution of the CPU will be paused. bool _isPaused = true; //! @brief A reference to the snes (to disable the debugger). diff --git a/sources/Memory/MemoryBus.hpp b/sources/Memory/MemoryBus.hpp index e1db314..ce9779f 100644 --- a/sources/Memory/MemoryBus.hpp +++ b/sources/Memory/MemoryBus.hpp @@ -12,7 +12,7 @@ namespace ComSquare { - struct SNES; + class SNES; namespace Memory { diff --git a/sources/PPU/PPU.cpp b/sources/PPU/PPU.cpp index 9a9a18c..3d6b59d 100644 --- a/sources/PPU/PPU.cpp +++ b/sources/PPU/PPU.cpp @@ -204,9 +204,9 @@ namespace ComSquare::PPU { (void)cycles; int inc = 0; - //uint32_t pixelTmp = 0xFFFFFFFF; - //pixelTmp |= this->_inidisp.brightness; - //std::cout << "update" << std::endl; + uint32_t pixelTmp = 0xFFFFFFFF; + pixelTmp |= this->_inidisp.brightness; + std::cout << "update" << std::endl; if (!this->_inidisp.fblank) { for (int x = 0; x < 448; x++) { for (int y = 0; y < 512; y++) { diff --git a/sources/PPU/PPU.hpp b/sources/PPU/PPU.hpp index 22c72ed..7cda75a 100644 --- a/sources/PPU/PPU.hpp +++ b/sources/PPU/PPU.hpp @@ -340,7 +340,7 @@ namespace ComSquare::PPU uint8_t raw; } _vmain; //! @brief Store the real value of the increment Amount (1, 32, 128) instead of 0,1 or 2 - uint8_t _incrementAmount; + uint8_t _incrementAmount = 1; //! @brief VMADD Register (VRAM Address) union { struct { diff --git a/sources/Renderer/IRenderer.hpp b/sources/Renderer/IRenderer.hpp index 875d97a..f3ea1d7 100644 --- a/sources/Renderer/IRenderer.hpp +++ b/sources/Renderer/IRenderer.hpp @@ -7,22 +7,34 @@ #include -namespace ComSquare::Renderer +namespace ComSquare { - class IRenderer { - public: - //! @brief Set a new name to the window, if there is already a name it will be overwrite - virtual void setWindowName(std::string) = 0; - //! @brief Tells to the program if the window has been closed, and therefore if he should stop - bool shouldExit = true; - //! @brief Render the buffer to the window - virtual void drawScreen() = 0; - //! @brief Set a pixel to the coordinates x, y with the color rgba - //! @param x The x position of the window (0, 0 is the top left corner). - //! @param y The y position of the window (0, 0 is the top left corner). - //! @param rgba The color of the pixel (red, green, blue, alpha). - virtual void putPixel(unsigned x, unsigned y, uint32_t rgba) = 0; - }; + class SNES; + namespace Renderer + { + class IRenderer { + public: + //! @brief Set a new name to the window, if there is already a name it will be overwrite + virtual void setWindowName(std::string) = 0; + + //! @brief Tells to the program if the window has been closed, and therefore if he should stop + bool shouldExit = true; + + //! @brief Render the buffer to the window + virtual void drawScreen() = 0; + + //! @brief Set a pixel to the coordinates x, y with the color rgba + //! @param x The x position of the window (0, 0 is the top left corner). + //! @param y The y position of the window (0, 0 is the top left corner). + //! @param rgba The color of the pixel (red, green, blue, alpha). + virtual void putPixel(unsigned x, unsigned y, uint32_t rgba) = 0; + + //! @brief Use this function to create the window. + //! @param snes The snes game object (to call the update method). + //! @param maxFPS The number of FPS you aim to run on. + virtual void createWindow(SNES &snes, int maxFPS) = 0; + }; + } } #endif //COMSQUARE_IRENDERER_HPP diff --git a/sources/Renderer/NoRenderer.cpp b/sources/Renderer/NoRenderer.cpp index c518d38..307d5bf 100644 --- a/sources/Renderer/NoRenderer.cpp +++ b/sources/Renderer/NoRenderer.cpp @@ -28,4 +28,9 @@ namespace ComSquare::Renderer (void)width; (void)maxFPS; } + + void NoRenderer::createWindow(int maxFPS) + { + (void)maxFPS. + } } \ No newline at end of file diff --git a/sources/Renderer/NoRenderer.hpp b/sources/Renderer/NoRenderer.hpp index d06d5b1..3c45f6f 100644 --- a/sources/Renderer/NoRenderer.hpp +++ b/sources/Renderer/NoRenderer.hpp @@ -24,6 +24,9 @@ namespace ComSquare::Renderer void putPixel(unsigned y, unsigned x, uint32_t rgba) override ; //! @brief Get the inputs from the Window void getEvents(); + //! @brief Use this function to create the window. + //! @param maxFPS The number of FPS you aim to run on. + void createWindow(int maxFPS) override; //! @brief Constructor that return the window component of the SFML. //! @param height height of the window. //! @param width width of the window. diff --git a/sources/Renderer/QtRenderer/QtSFML.cpp b/sources/Renderer/QtRenderer/QtSFML.cpp new file mode 100644 index 0000000..a494020 --- /dev/null +++ b/sources/Renderer/QtRenderer/QtSFML.cpp @@ -0,0 +1,39 @@ +// +// Created by anonymus-raccoon on 2/15/20. +// + +#include +#include +#include +#include +#include "QtSFML.h" + +#ifdef Q_WS_X11 + #include + #include +#endif + +namespace ComSquare::Renderer +{ + QtSFML::QtSFML(QApplication &app, unsigned int height, unsigned int width) : + SFRenderer(height, width), _app(app) + { } + + void QtSFML::createWindow(SNES &snes, int maxFPS) + { + QFrame *frame = new QFrame(); + frame->show(); + MainQTWidget *sfView = new MainQTWidget(snes, frame, QPoint(0, 0), QSize(this->_videoMode.width, this->_videoMode.height), maxFPS); + sfView->show(); + } + + MainQTWidget::MainQTWidget(SNES &snes, QWidget *parent, const QPoint &position, const QSize &size, int frameRate) : + QtWidgetSFML(parent, position, size, frameRate), + _snes(snes) + { } + + void MainQTWidget::_onUpdate() + { + this->_snes.update(); + } +} \ No newline at end of file diff --git a/sources/Renderer/QtRenderer/QtSFML.h b/sources/Renderer/QtRenderer/QtSFML.h new file mode 100644 index 0000000..8925835 --- /dev/null +++ b/sources/Renderer/QtRenderer/QtSFML.h @@ -0,0 +1,48 @@ +// +// Created by anonymus-raccoon on 2/15/20. +// + +#ifndef COMSQUARE_QTSFML_H +#define COMSQUARE_QTSFML_H + +#include +#include +#include +#include "../IRenderer.hpp" +#include "../SFRenderer.hpp" +#include "QtWidgetSFML.h" + +namespace ComSquare::Renderer +{ + //! @brief A SFML renderer inside a QT window. + class QtSFML : public SFRenderer { + private: + QApplication &_app; + public: + //! @brief Use this function to create the window. + //! @param maxFPS The number of FPS you aim to run on. + void createWindow(SNES &snes, int maxFPS) override; + //! @brief Constructor that return a SFML renderer inside a QT window. + //! @param height height of the window. + //! @param width width of the window. + QtSFML(QApplication &app, unsigned int height, unsigned int width); + QtSFML(const QtSFML &) = delete; + QtSFML &operator=(const QtSFML &) = delete; + ~QtSFML() = default; + }; + + //! @brief The SFML window that manage the update. + class MainQTWidget : public QtWidgetSFML { + private: + //! @brief The snes to update. + SNES &_snes; + void _onUpdate() override; + public: + MainQTWidget(SNES &snes, QWidget* parent, const QPoint& position, const QSize& size, int frameRate = 0); + MainQTWidget(const MainQTWidget &) = delete; + MainQTWidget &operator=(const MainQTWidget &) = delete; + ~MainQTWidget() override = default; + }; +} + +#endif //COMSQUARE_QTSFML_H diff --git a/sources/Renderer/QtRenderer/QtWidgetSFML.cpp b/sources/Renderer/QtRenderer/QtWidgetSFML.cpp new file mode 100644 index 0000000..35392f4 --- /dev/null +++ b/sources/Renderer/QtRenderer/QtWidgetSFML.cpp @@ -0,0 +1,54 @@ +// +// Created by anonymus-raccoon on 2/16/20. +// + +#include "QtWidgetSFML.h" + +namespace ComSquare::Renderer +{ + QtWidgetSFML::QtWidgetSFML(QWidget *parent, const QPoint &position, const QSize &size, int frameRate) : + QWidget(parent) + { + this->setAttribute(Qt::WA_PaintOnScreen); + this->setAttribute(Qt::WA_OpaquePaintEvent); + this->setAttribute(Qt::WA_NoSystemBackground); + + this->setFocusPolicy(Qt::StrongFocus); + + this->move(position); + this->resize(size); + + this->_timer.setInterval(frameRate); + } + + void QtWidgetSFML::showEvent(QShowEvent *) + { + if (!this->_isInitialized) { + // Under X11, we need to flush the commands sent to the server to ensure that + // SFML will get an updated view of the windows + #ifdef Q_WS_X11 + XFlush(QX11Info::display()); + #endif + this->sf::RenderWindow::create(this->winId()); + this->_onInit(); + + connect(&_timer, SIGNAL(timeout()), this, SLOT(repaint())); + this->_timer.start(); + this->_isInitialized = true; + } + } + + QPaintEngine *QtWidgetSFML::paintEngine() const + { + return nullptr; + } + + void QtWidgetSFML::paintEvent(QPaintEvent *) + { + this->clear(); + this->_onUpdate(); + this->display(); + } + + void QtWidgetSFML::_onInit(){ } +} \ No newline at end of file diff --git a/sources/Renderer/QtRenderer/QtWidgetSFML.h b/sources/Renderer/QtRenderer/QtWidgetSFML.h new file mode 100644 index 0000000..03071c7 --- /dev/null +++ b/sources/Renderer/QtRenderer/QtWidgetSFML.h @@ -0,0 +1,33 @@ +// +// Created by anonymus-raccoon on 2/16/20. +// + +#ifndef COMSQUARE_QTWIDGETSFML_H +#define COMSQUARE_QTWIDGETSFML_H + +#include +#include +#include + +namespace ComSquare::Renderer +{ + //! @brief A widget that you can put inside a QT application that render using the SFML. + class QtWidgetSFML : public QWidget, public sf::RenderWindow { + private: + virtual void _onInit(); + virtual void _onUpdate() = 0; + QPaintEngine* paintEngine() const override; + void showEvent(QShowEvent*) override; + void paintEvent(QPaintEvent*) override; + + QTimer _timer; + bool _isInitialized = false; + public: + QtWidgetSFML(QWidget* parent, const QPoint& position, const QSize& size, int frameRate = 0); + QtWidgetSFML(const QtWidgetSFML &) = delete; + QtWidgetSFML &operator=(const QtWidgetSFML &) = delete; + ~QtWidgetSFML() override = default; + }; +} + +#endif //COMSQUARE_QTWIDGETSFML_H diff --git a/sources/Renderer/SFRenderer.cpp b/sources/Renderer/SFRenderer.cpp index 48b415c..2638a0d 100644 --- a/sources/Renderer/SFRenderer.cpp +++ b/sources/Renderer/SFRenderer.cpp @@ -12,18 +12,22 @@ namespace ComSquare::Renderer { - SFRenderer::SFRenderer(unsigned int height, unsigned int width, int maxFPS) + SFRenderer::SFRenderer(unsigned int height, unsigned int width) { - sf::Image icon; this->shouldExit = false; this->_videoMode = {width, height, 32}; + this->_texture.create(width, height); + this->_sprite.setTexture(this->_texture); + this->_pixelBuffer = new sf::Color[height * width]; + } + + void SFRenderer::createWindow(SNES &, int maxFPS) + { + sf::Image icon; this->_window.create(this->_videoMode, "ComSquare Emulator", sf::Style::Default); if (icon.loadFromFile("../ressources/Logo.png")) this->_window.setIcon(314, 314, icon.getPixelsPtr()); this->_window.setFramerateLimit(maxFPS); - this->_texture.create(width, height); - this->_sprite.setTexture(this->_texture); - this->_pixelBuffer = new sf::Color[height * width]; } SFRenderer::~SFRenderer() @@ -31,7 +35,6 @@ namespace ComSquare::Renderer delete [] this->_pixelBuffer; } - void SFRenderer::setWindowName(std::string newWindowName) { this->_window.setTitle(newWindowName + " - ComSquare"); @@ -70,5 +73,4 @@ namespace ComSquare::Renderer } } } - } diff --git a/sources/Renderer/SFRenderer.hpp b/sources/Renderer/SFRenderer.hpp index f4be974..0cd23b1 100644 --- a/sources/Renderer/SFRenderer.hpp +++ b/sources/Renderer/SFRenderer.hpp @@ -6,6 +6,7 @@ #define COMSQUARE_SFRENDERER_HPP #include "IRenderer.hpp" +#include "../SNES.hpp" #include #include #include @@ -24,7 +25,7 @@ namespace ComSquare::Renderer }; class SFRenderer : public IRenderer { - protected: + protected: //! @brief The Renderer for the window. sf::RenderWindow _window; //! @brief Video Mode containing the height and width of the window. @@ -47,12 +48,15 @@ namespace ComSquare::Renderer //! @param rgba The color of the pixel. void putPixel(unsigned y, unsigned x, uint32_t rgba) override; //! @brief Get the inputs from the Window - virtual void getEvents(); + void getEvents(); + //! @brief Use this function to create the window. + //! @param snes The snes for the update (not implemented here). + //! @param maxFPS The number of FPS you aim to run on. + void createWindow(SNES &snes, int maxFPS) override; //! @brief Constructor that return the window component of the SFML. //! @param height height of the window. //! @param width width of the window. - //! @param maxFPS the number of maximum FPS for the window. - SFRenderer(unsigned int height, unsigned int width, int maxFPS); + SFRenderer(unsigned int height, unsigned int width); SFRenderer(const SFRenderer &) = delete; SFRenderer &operator=(const SFRenderer &) = delete; ~SFRenderer(); diff --git a/sources/Renderer/TGUIRenderer.cpp b/sources/Renderer/TGUIRenderer.cpp deleted file mode 100644 index e6e8d2b..0000000 --- a/sources/Renderer/TGUIRenderer.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// -// Created by anonymus-raccoon on 2/14/20. -// - -#include "TGUIRenderer.h" -#include - -namespace ComSquare::Renderer -{ - TGUIRenderer::TGUIRenderer(unsigned int height, unsigned int width, int maxFPS) : - SFRenderer(height, width, maxFPS), - _gui(this->_window) - { - _gui.loadWidgetsFromFile("../ressources/form.txt"); - } - - void TGUIRenderer::drawScreen() - { - this->_gui.draw(); - this->_window.display(); - } - - void TGUIRenderer::getEvents() - { - sf::Event event; - while (this->_window.pollEvent(event)) { - if (event.type == sf::Event::Closed) { - this->shouldExit = true; - break; - } - this->_gui.handleEvent(event); - } - } -} \ No newline at end of file diff --git a/sources/Renderer/TGUIRenderer.h b/sources/Renderer/TGUIRenderer.h deleted file mode 100644 index 08175d3..0000000 --- a/sources/Renderer/TGUIRenderer.h +++ /dev/null @@ -1,33 +0,0 @@ -// -// Created by anonymus-raccoon on 2/14/20. -// - -#ifndef _TGUIRENDERER_H_ -#define _TGUIRENDERER_H_ - -#include -#include "SFRenderer.hpp" - -namespace ComSquare::Renderer -{ - class TGUIRenderer : public SFRenderer { - private: - tgui::Gui _gui; - public: - //! @brief Constructor that return the window component of the SFML. - //! @param height height of the window. - //! @param width width of the window. - //! @param maxFPS the number of maximum FPS for the window. - TGUIRenderer(unsigned int height, unsigned int width, int maxFPS); - TGUIRenderer(const TGUIRenderer &) = delete; - TGUIRenderer &operator=(const TGUIRenderer &) = delete; - ~TGUIRenderer() = default; - - //! @brief Draw the screen and renderer ui elements. - void drawScreen() override; - //! @brief Get closing events and input for buttons... - void getEvents() override; - }; -} - -#endif //_TGUIRENDERER_H_ diff --git a/sources/SNES.cpp b/sources/SNES.cpp index 49adc22..6d2ecb5 100644 --- a/sources/SNES.cpp +++ b/sources/SNES.cpp @@ -30,4 +30,15 @@ namespace ComSquare { this->cpu = std::make_shared(*this->cpu); } + + void SNES::update() + { + try { + unsigned cycleCount = this->cpu->update(); + this->ppu->update(cycleCount); + this->apu->update(cycleCount); + } catch (std::exception &e) { + std::cerr << "An error occurred: " << e.what() << std::endl; + } + } } diff --git a/sources/SNES.hpp b/sources/SNES.hpp index 41b6d53..2348f8b 100644 --- a/sources/SNES.hpp +++ b/sources/SNES.hpp @@ -16,7 +16,7 @@ namespace ComSquare { //! @brief Container of all the components of the SNES. - struct SNES { + class SNES { public: //! @brief Cartridge containing instructions (ROM). std::shared_ptr cartridge; @@ -31,10 +31,14 @@ namespace ComSquare //! @brief Save Ram residing inside the Cartridge in a real SNES. std::shared_ptr sram; - //! @brief Enable the CPU's debugging window. - void enableCPUDebugging(); + //! @brief Call this function to update all the components + void update(); + //! @brief Disable the CPU's debugging window. void disableCPUDebugging(); + //! @brief Enable the CPU's debugging window. + void enableCPUDebugging(); + //! @brief Create all the components using a common memory bus for all of them. SNES(const std::shared_ptr &bus, const std::string &ramPath, Renderer::IRenderer &renderer); SNES(const SNES &) = default;