diff --git a/CMakeLists.txt b/CMakeLists.txt index 979715d..924bd4f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -125,119 +125,131 @@ set(CMAKE_AUTOUIC ON) # make app add_executable(ComSquare - sources/main.cpp - sources/SNES.cpp - sources/SNES.hpp - sources/Memory/MemoryBus.cpp - sources/Memory/MemoryBus.hpp - sources/Memory/AMemory.hpp - sources/Memory/AMemory.cpp - sources/PPU/PPU.cpp - sources/PPU/PPU.hpp - sources/CPU/CPU.cpp - sources/CPU/CPU.hpp - sources/Cartridge/Cartridge.cpp - sources/Cartridge/Cartridge.hpp - sources/Exceptions/NotImplementedException.hpp - sources/APU/APU.hpp - sources/APU/APU.cpp - sources/Exceptions/InvalidAddress.hpp - sources/Exceptions/InvalidRom.hpp - sources/Models/Int24.hpp - sources/Models/Int24.hpp - sources/Ram/Ram.cpp - sources/Ram/Ram.hpp - sources/Memory/MemoryShadow.cpp - sources/Memory/MemoryShadow.hpp - sources/Memory/ARectangleMemory.cpp - sources/Memory/ARectangleMemory.hpp - sources/APU/DSP/DSP.cpp - sources/APU/DSP/DSP.hpp - sources/Renderer/IRenderer.hpp - sources/Renderer/SFRenderer.hpp - sources/Renderer/SFRenderer.cpp - sources/Exceptions/InvalidAction.hpp - sources/Cartridge/InterruptVectors.hpp - sources/Memory/RectangleShadow.cpp - sources/Memory/RectangleShadow.hpp - sources/Exceptions/InvalidOpcode.hpp - sources/CPU/Instructions/Interrupts.cpp - sources/CPU/Instructions/MathematicalOperations.cpp - sources/APU/Instructions/Standbys.cpp - sources/APU/Instructions/ProgramStatusWord.cpp - sources/APU/Instructions/Bit.cpp - sources/CPU/Instructions/MathematicalOperations.cpp - sources/CPU/Instructions/MemoryInstructions.cpp - sources/CPU/Instructions/InternalInstruction.cpp - sources/Ram/ExtendedRam.cpp - sources/Ram/ExtendedRam.hpp - sources/Debugger/CPU/CPUDebug.cpp - sources/Debugger/CPU/CPUDebug.hpp - sources/Renderer/QtRenderer/QtSFML.cpp - sources/Renderer/QtRenderer/QtSFML.hpp - sources/Renderer/QtRenderer/QtWidgetSFML.cpp - sources/Renderer/QtRenderer/QtWidgetSFML.hpp - ui/cpuView.ui - ui/ramView.ui - ui/cartridgeView.ui - ui/apuView.ui - ui/busView.ui - resources/appResources.qrc - sources/Utility/Utility.hpp - sources/Debugger/MemoryViewer.cpp - sources/Debugger/MemoryViewer.hpp - sources/Utility/Utility.cpp - sources/Debugger/HeaderViewer.cpp - sources/Debugger/HeaderViewer.hpp - sources/CPU/Instructions/BitsInstructions.cpp - sources/Debugger/HeaderViewer.cpp - sources/Debugger/HeaderViewer.hpp - sources/Debugger/APUDebug.hpp - sources/Debugger/APUDebug.cpp - sources/APU/Instructions/Stack.cpp - sources/APU/Instructions/Subroutine.cpp - sources/APU/Instructions/ProgramFlow.cpp - sources/APU/Operand.cpp - sources/APU/Instructions/DecimalCompensation.cpp - sources/APU/Instructions/MultiplicationDivision.cpp - sources/APU/Instructions/16bitArithmetic.cpp - sources/APU/Instructions/16bitDataTransmission.cpp - sources/APU/Instructions/8bitShiftRotation.cpp - sources/APU/Instructions/8bitIncrementDecrement.cpp - sources/APU/Instructions/8bitLogical.cpp - sources/APU/Instructions/8bitArithmetic.cpp - sources/APU/Instructions/8bitDataTransmission.cpp - sources/APU/IPL/IPL.hpp - sources/APU/IPL/IPL.cpp - sources/CPU/Instructions/TransferRegisters.cpp - sources/CPU/AddressingModes.cpp - sources/Debugger/MemoryBusDebug.cpp - sources/Debugger/MemoryBusDebug.hpp - sources/Debugger/ClosableWindow.hpp - sources/Models/Components.hpp - sources/CPU/Instruction.hpp - sources/Exceptions/DebuggableError.hpp - sources/Debugger/CPU/Disassembly.cpp - sources/Models/Components.hpp - sources/Debugger/CGramDebug.cpp - sources/Debugger/CGramDebug.hpp - sources/Models/Vector2.hpp - sources/PPU/Background.cpp - sources/PPU/Background.hpp - sources/CPU/DMA/DMA.cpp - sources/CPU/DMA/DMA.hpp - ui/registersView.ui - sources/Debugger/RegisterViewer.cpp - sources/Debugger/RegisterViewer.hpp - sources/Memory/IMemory.hpp - sources/APU/DSP/Voice.cpp - sources/APU/DSP/Echo.cpp - sources/APU/DSP/Gauss.cpp - sources/APU/DSP/Envelope.cpp - sources/APU/DSP/Timer.cpp - sources/APU/DSP/BRR.cpp - sources/PPU/PPUUtils.cpp - ) + sources/main.cpp + sources/SNES.cpp + sources/SNES.hpp + sources/Memory/MemoryBus.cpp + sources/Memory/MemoryBus.hpp + sources/Memory/AMemory.hpp + sources/Memory/AMemory.cpp + sources/PPU/PPU.cpp + sources/PPU/PPU.hpp + sources/CPU/CPU.cpp + sources/CPU/CPU.hpp + sources/Cartridge/Cartridge.cpp + sources/Cartridge/Cartridge.hpp + sources/Exceptions/NotImplementedException.hpp + sources/APU/APU.hpp + sources/APU/APU.cpp + sources/Exceptions/InvalidAddress.hpp + sources/Exceptions/InvalidRom.hpp + sources/Models/Int24.hpp + sources/Models/Int24.hpp + sources/Ram/Ram.cpp + sources/Ram/Ram.hpp + sources/Memory/MemoryShadow.cpp + sources/Memory/MemoryShadow.hpp + sources/Memory/ARectangleMemory.cpp + sources/Memory/ARectangleMemory.hpp + sources/APU/DSP/DSP.cpp + sources/APU/DSP/DSP.hpp + sources/Renderer/IRenderer.hpp + sources/Renderer/SFRenderer.hpp + sources/Renderer/SFRenderer.cpp + sources/Exceptions/InvalidAction.hpp + sources/Cartridge/InterruptVectors.hpp + sources/Memory/RectangleShadow.cpp + sources/Memory/RectangleShadow.hpp + sources/Exceptions/InvalidOpcode.hpp + sources/CPU/Instructions/Interrupts.cpp + sources/CPU/Instructions/MathematicalOperations.cpp + sources/APU/Instructions/Standbys.cpp + sources/APU/Instructions/ProgramStatusWord.cpp + sources/APU/Instructions/Bit.cpp + sources/CPU/Instructions/MathematicalOperations.cpp + sources/CPU/Instructions/MemoryInstructions.cpp + sources/CPU/Instructions/InternalInstruction.cpp + sources/Ram/ExtendedRam.cpp + sources/Ram/ExtendedRam.hpp + sources/Debugger/CPU/CPUDebug.cpp + sources/Debugger/CPU/CPUDebug.hpp + sources/Renderer/QtRenderer/QtSFML.cpp + sources/Renderer/QtRenderer/QtSFML.hpp + sources/Renderer/QtRenderer/QtWidgetSFML.cpp + sources/Renderer/QtRenderer/QtWidgetSFML.hpp + ui/cpuView.ui + ui/ramView.ui + ui/cartridgeView.ui + ui/apuView.ui + ui/busView.ui + resources/appResources.qrc + sources/Utility/Utility.hpp + sources/Debugger/MemoryViewer.cpp + sources/Debugger/MemoryViewer.hpp + sources/Utility/Utility.cpp + sources/Debugger/HeaderViewer.cpp + sources/Debugger/HeaderViewer.hpp + sources/CPU/Instructions/BitsInstructions.cpp + sources/Debugger/HeaderViewer.cpp + sources/Debugger/HeaderViewer.hpp + sources/Debugger/APUDebug.hpp + sources/Debugger/APUDebug.cpp + sources/APU/Instructions/Stack.cpp + sources/APU/Instructions/Subroutine.cpp + sources/APU/Instructions/ProgramFlow.cpp + sources/APU/Operand.cpp + sources/APU/Instructions/DecimalCompensation.cpp + sources/APU/Instructions/MultiplicationDivision.cpp + sources/APU/Instructions/16bitArithmetic.cpp + sources/APU/Instructions/16bitDataTransmission.cpp + sources/APU/Instructions/8bitShiftRotation.cpp + sources/APU/Instructions/8bitIncrementDecrement.cpp + sources/APU/Instructions/8bitLogical.cpp + sources/APU/Instructions/8bitArithmetic.cpp + sources/APU/Instructions/8bitDataTransmission.cpp + sources/APU/IPL/IPL.hpp + sources/APU/IPL/IPL.cpp + sources/CPU/Instructions/TransferRegisters.cpp + sources/CPU/AddressingModes.cpp + sources/Debugger/MemoryBusDebug.cpp + sources/Debugger/MemoryBusDebug.hpp + sources/Debugger/ClosableWindow.hpp + sources/Models/Components.hpp + sources/CPU/Instruction.hpp + sources/Exceptions/DebuggableError.hpp + sources/Debugger/CPU/Disassembly.cpp + sources/Models/Components.hpp + sources/Debugger/CGramDebug.cpp + sources/Debugger/CGramDebug.hpp + sources/Models/Vector2.hpp + sources/PPU/Background.cpp + sources/PPU/Background.hpp + sources/CPU/DMA/DMA.cpp + sources/CPU/DMA/DMA.hpp + ui/registersView.ui + sources/Debugger/RegisterViewer.cpp + sources/Debugger/RegisterViewer.hpp + sources/Memory/IMemory.hpp + sources/APU/DSP/Voice.cpp + sources/APU/DSP/Echo.cpp + sources/APU/DSP/Gauss.cpp + sources/APU/DSP/Envelope.cpp + sources/APU/DSP/Timer.cpp + sources/APU/DSP/BRR.cpp + sources/PPU/PPUUtils.cpp + ui/tileView.ui + sources/Debugger/TileViewer/TileViewer.cpp + sources/Debugger/TileViewer/TileViewer.hpp + sources/Debugger/TileViewer/RAMTileRenderer.cpp + sources/Debugger/TileViewer/RAMTileRenderer.hpp + sources/PPU/Tile.hpp + sources/Renderer/QtRenderer/QtSfmlTileRenderer.cpp + sources/Renderer/QtRenderer/QtSfmlTileRenderer.hpp + sources/PPU/TileRenderer.cpp + sources/PPU/TileRenderer.hpp + ) + +include_directories(ComSquare sources) target_compile_definitions(ComSquare PUBLIC DEBUGGER_ENABLED) include_directories(ComSquare sources) diff --git a/sources/Debugger/CPU/CPUDebug.hpp b/sources/Debugger/CPU/CPUDebug.hpp index 9120cec..bcc6cab 100644 --- a/sources/Debugger/CPU/CPUDebug.hpp +++ b/sources/Debugger/CPU/CPUDebug.hpp @@ -7,7 +7,6 @@ #include #include "../../CPU/CPU.hpp" -#include "../../Renderer/SFRenderer.hpp" #include "../../SNES.hpp" #include "../../../ui/ui_cpuView.h" #include "../ClosableWindow.hpp" diff --git a/sources/Debugger/TileViewer/RAMTileRenderer.cpp b/sources/Debugger/TileViewer/RAMTileRenderer.cpp new file mode 100644 index 0000000..96c9a2f --- /dev/null +++ b/sources/Debugger/TileViewer/RAMTileRenderer.cpp @@ -0,0 +1,112 @@ +// +// Created by cbihan on 24/05/2021. +// + +#include +#include +#include "RAMTileRenderer.hpp" +#include "PPU/PPU.hpp" +#include "PPU/Tile.hpp" +#include + +namespace ComSquare::Debugger +{ + RAMTileRenderer::RAMTileRenderer() + : _ram(nullptr), + _renderSize(0x5000), + _nbColumns(16), + _ramOffset(0), + _bpp(2), + buffer({{{0}}}) + { + } + + void RAMTileRenderer::setRam(std::shared_ptr ram) + { + this->_ram = ram; + this->_tileRenderer.setRam(ram); + } + + void RAMTileRenderer::render() + { + int bufX = 0; + int bufY = 0; + int nbTilesDrawn = 0; + int resetX = bufX; + for (auto &i : buffer) + i.fill(0); + uint24_t limit = fmin(this->_ram->getSize(), this->_renderSize) + this->_ramOffset; + + for (uint24_t i = this->_ramOffset; i < limit; i += PPU::Tile::BaseByteSize * this->_bpp, nbTilesDrawn++) { + if (bufX > 1024 || bufY > 1024) + break; + + this->_tileRenderer.render(i); + if (nbTilesDrawn) { + resetX += PPU::Tile::NbPixelsWidth; + bufX = resetX; + bufY -= PPU::Tile::NbPixelsHeight; + } + if (nbTilesDrawn && nbTilesDrawn % this->_nbColumns == 0) { + nbTilesDrawn = 0; + resetX = 0; + bufX = resetX; + bufY += PPU::Tile::NbPixelsHeight; + } + + for (const auto &raw : this->_tileRenderer.buffer) { + for (const auto &pixel : raw) { + buffer[bufX++][bufY] = pixel; + } + bufY++; + bufX = resetX; + } + } + } + + void RAMTileRenderer::setPaletteIndex(int paletteIndex) + { + this->_tileRenderer.setPaletteIndex(paletteIndex); + } + + void RAMTileRenderer::setBpp(int bpp) + { + this->_bpp = bpp; + this->_tileRenderer.setBpp(bpp); + } + + void RAMTileRenderer::setCgram(std::shared_ptr ram) + { + this->_tileRenderer.setCgram(ram); + } + + void RAMTileRenderer::setRenderSize(int size) + { + this->_renderSize = size; + } + + void RAMTileRenderer::setNbColumns(int nbColumns) + { + this->_nbColumns = nbColumns; + } + + int RAMTileRenderer::getBpp() const + { + return this->_tileRenderer.getBpp(); + } + + int RAMTileRenderer::getPaletteIndex() const + { + return this->_tileRenderer.getPaletteIndex(); + } + + int RAMTileRenderer::getNbColumns() const + { + return this->_nbColumns; + } + + void RAMTileRenderer::setRamOffset(int offset) + { + this->_ramOffset = offset; + } +} \ No newline at end of file diff --git a/sources/Debugger/TileViewer/RAMTileRenderer.hpp b/sources/Debugger/TileViewer/RAMTileRenderer.hpp new file mode 100644 index 0000000..8c4eeb0 --- /dev/null +++ b/sources/Debugger/TileViewer/RAMTileRenderer.hpp @@ -0,0 +1,62 @@ +// +// Created by cbihan on 24/05/2021. +// + +#pragma once + +#include +#include "PPU/TileRenderer.hpp" +#include "Ram/Ram.hpp" + +namespace ComSquare::Debugger +{ + class RAMTileRenderer { + private: + //! @brief ram to render + std::shared_ptr _ram; + //! @brief The size to render in the ram + int _renderSize; + //! @brief The number of tile columns to display + int _nbColumns; + //! @brief Bytes to skip from the start of the ram + int _ramOffset; + //! @brief The actual bpp to render + int _bpp; + //! @brief The class tha actually render the tile + PPU::TileRenderer _tileRenderer; + public: + //! @brief internal buffer + std::array, 1024> buffer; + //! @brief Set the palette to use for render (index of palette) + void setPaletteIndex(int paletteIndex); + //! @brief Set the ram to look for color references + void setCgram(std::shared_ptr ram); + //! @brief Set the bpp to render graphics + void setBpp(int bpp); + //! @brief Set the number of maximum columns + void setNbColumns(int nbColumns); + //! @brief Set the size of ram to render + void setRenderSize(int size); + //! @brief The ram to render + void setRam(std::shared_ptr ram); + //! @brief Set the ram offset + void setRamOffset(int offset); + //! @brief Get the current bpp + int getBpp() const; + //! @brief Get the index of the current palette used + int getPaletteIndex() const; + //! @brief Get the numbr of maximum tile columns to render + int getNbColumns() const; + //! @brief render the selected ram + void render(); + //! @brief ctor + RAMTileRenderer(); + //! @brief copy ctor + RAMTileRenderer(const RAMTileRenderer &) = default; + //! @brief dtor + ~RAMTileRenderer() = default; + //! @brief assignment operator + RAMTileRenderer &operator=(const RAMTileRenderer &) = default; + }; +} + diff --git a/sources/Debugger/TileViewer/TileViewer.cpp b/sources/Debugger/TileViewer/TileViewer.cpp new file mode 100644 index 0000000..2507892 --- /dev/null +++ b/sources/Debugger/TileViewer/TileViewer.cpp @@ -0,0 +1,149 @@ +// +// Created by cbihan on 5/7/21. +// + +namespace ComSquare::Renderer +{ + class QtFullSFML; +} + +#include "Renderer/QtRenderer/QtSFML.hpp" +#include "TileViewer.hpp" +#include "SNES.hpp" +#include +#include +#include +#include +#include "Utility/Utility.hpp" +#include "RAMTileRenderer.hpp" +#include "PPU/PPU.hpp" + +namespace ComSquare::Debugger +{ + TileViewer::TileViewer(SNES &snes, ComSquare::PPU::PPU &ppu) + : _window(new ClosableWindow(*this, &TileViewer::disableViewer)), + _snes(snes), + _ui(), + _ppu(ppu), + _ramTileRenderer() + { + this->_ramTileRenderer.setRam(ppu.vram); + this->_ramTileRenderer.setCgram(ppu.cgram); + this->_window->setContextMenuPolicy(Qt::NoContextMenu); + this->_window->setAttribute(Qt::WA_QuitOnClose, false); + this->_window->setAttribute(Qt::WA_DeleteOnClose); + + this->_ui.setupUi(this->_window); + this->_sfWidget = std::make_unique(this->_ui.widget_sfml); + QMainWindow::connect(this->_ui.NbColumns, QOverload::of(&QSpinBox::valueChanged), this, [this](int nb) -> void { this->setNbColumns(nb); }); + QMainWindow::connect(this->_ui.ByteSize, QOverload::of(&QSpinBox::valueChanged), this, [this](int nb) -> void { this->setRenderSize(nb); }); + QMainWindow::connect(this->_ui.Address, QOverload::of(&QSpinBox::valueChanged), this, [this](int nb) -> void { this->setRamOffset(nb); }); + QMainWindow::connect(this->_ui.PaletteIndex, QOverload::of(&QSpinBox::valueChanged), this, [this](int nb) -> void { this->setPaletteIndex(nb); }); + QMainWindow::connect(this->_ui.BppFormat, QOverload::of(&QComboBox::currentIndexChanged), this, [this](int index) -> void { this->_bppChangeUIHandler(index); }); + + // used to setup ui restrictions + this->setBpp(this->getBpp()); + this->_window->show(); + this->internalUpdate(); + } + + void TileViewer::disableViewer() + { + this->_snes.disableTileViewerDebugging(); + } + + void TileViewer::focus() + { + this->_window->activateWindow(); + } + + bool TileViewer::isDebugger() + { + return true; + } + + uint16_t TileViewer::read(uint8_t addr) + { + return this->_ppu.cgramRead(addr); + } + + void TileViewer::setPaletteIndex(int paletteIndex) + { + this->_ramTileRenderer.setPaletteIndex(paletteIndex); + this->internalUpdate(); + } + + void TileViewer::setBpp(int bpp) + { + this->_ui.PaletteIndex->setDisabled(bpp > 4); + switch (bpp) { + case 8: + this->_ui.PaletteIndex->setValue(0); + break; + case 4: + this->_ui.PaletteIndex->setMaximum(15); + if (this->_ui.PaletteIndex->value() > 15) { + this->_ui.PaletteIndex->setValue(15); + } + break; + case 2: + default: + bpp = 2; + this->_ui.PaletteIndex->setMaximum(63); + break; + } + this->_ramTileRenderer.setBpp(bpp); + this->internalUpdate(); + } + + void TileViewer::setNbColumns(int nbColumns) + { + this->_ramTileRenderer.setNbColumns(nbColumns); + this->internalUpdate(); + } + + void TileViewer::setRenderSize(int size) + { + this->_ramTileRenderer.setRenderSize(size); + this->internalUpdate(); + } + + int TileViewer::getBpp() const + { + return this->_ramTileRenderer.getBpp(); + } + + int TileViewer::getPaletteIndex() const + { + return this->_ramTileRenderer.getPaletteIndex(); + } + + int TileViewer::getNbColumns() const + { + return this->_ramTileRenderer.getNbColumns(); + } + + void TileViewer::internalUpdate() + { + this->_ramTileRenderer.render(); + this->_sfWidget->buffer = this->_ramTileRenderer.buffer; + } + + void TileViewer::setRamOffset(int offset) + { + this->_ramTileRenderer.setRamOffset(offset); + this->internalUpdate(); + } + + void TileViewer::_bppChangeUIHandler(int index) + { + switch (index) { + case 0: return this->setBpp(2); + case 1: return this->setBpp(4); + case 2: return this->setBpp(8); + default: + break; + } + // TODO error handling + } +} \ No newline at end of file diff --git a/sources/Debugger/TileViewer/TileViewer.hpp b/sources/Debugger/TileViewer/TileViewer.hpp new file mode 100644 index 0000000..1848899 --- /dev/null +++ b/sources/Debugger/TileViewer/TileViewer.hpp @@ -0,0 +1,83 @@ +// +// Created by cbihan on 5/7/21. +// + +#pragma once + +namespace ComSquare::PPU +{ + class PPU; +} + +#include +#include +#include +#include +#include "PPU/PPU.hpp" +#include "Debugger/ClosableWindow.hpp" +#include "Renderer/QtRenderer/QtSfmlTileRenderer.hpp" +#include "../../../ui/ui_tileView.h" +#include "Ram/Ram.hpp" +#include "RAMTileRenderer.hpp" + +namespace ComSquare::Debugger +{ + + //! @brief window that allow the user to view all data going through the memory bus. + class TileViewer : public QObject { + private: + //! @brief The QT window for this debugger. + ClosableWindow *_window; + //! @brief A reference to the snes (to disable the debugger). + SNES &_snes; + //! @brief A widget that contain the whole UI. + Ui::TileViewer _ui; + //! @brief A reference to the ppu + ComSquare::PPU::PPU &_ppu; + //! @brief the window + std::unique_ptr _sfWidget; + //! @brief The ram tile renderer + RAMTileRenderer _ramTileRenderer; + //! @brief Change the bpp from the index given by the ui (QT combo box) + void _bppChangeUIHandler(int index); + public: + //! @brief Called when the window is closed. Turn off the debugger. + void disableViewer(); + //! @brief ctor + explicit TileViewer(SNES &snes, ComSquare::PPU::PPU &ppu); + //! @brief copy ctor + TileViewer(const TileViewer &) = delete; + //! @brief dtor + ~TileViewer() override = default; + //! @brief assignment operator + TileViewer &operator=(const TileViewer &) = delete; + + //! @brief Read data at the CGRAM address send it to the debugger. + //! @param addr The address to read from. + //! @return The color value in BGR, looks like this xbbbbbgggggrrrrr. + uint16_t read(uint8_t addr); + //! @brief Focus the debugger's window. + void focus(); + //! @brief Return true if the Bus is overloaded with debugging features. + bool isDebugger(); + //! @brief Set the palette to use for render (index of palette) + void setPaletteIndex(int paletteIndex); + //! @brief Set the bpp to render graphics + void setBpp(int bpp); + //! @brief Set the number of maximum columns + void setNbColumns(int nbColumns); + //! @brief Set the size of ram to render + void setRenderSize(int size); + //! @brief Set the ram offset + void setRamOffset(int offset); + //! @brief Get the current bpp + int getBpp() const; + //! @brief Get the index of the current palette used + int getPaletteIndex() const; + //! @brief Get the numbr of maximum tile columns to render + int getNbColumns() const; + //! @brief Update the tile renderer + void internalUpdate(); + + }; +} diff --git a/sources/PPU/Background.cpp b/sources/PPU/Background.cpp index 1f9601b..8e20124 100644 --- a/sources/PPU/Background.cpp +++ b/sources/PPU/Background.cpp @@ -6,6 +6,8 @@ #include "PPU.hpp" #include "Background.hpp" #include +#include "Tile.hpp" +#include "PPUUtils.hpp" #include "Models/Vector2.hpp" namespace ComSquare::PPU @@ -21,10 +23,13 @@ namespace ComSquare::PPU _tilesetAddress(ppu.getTilesetAddress(backGroundNumber)), _priority(hasPriority), _bgNumber(backGroundNumber), + _tileBuffer({{{0}}}), _vram(ppu.vram), _cgram(ppu.cgram), buffer({{{0}}}) { + this->_tileRenderer.setRam(this->_vram); + this->_tileRenderer.setCgram(this->_cgram); } void Background::renderBackground() @@ -36,7 +41,7 @@ namespace ComSquare::PPU for (int i = 0; i < 4; i++) { if (!(i == 1 && this->_tileMapsConfig.x == 1) && !(i > 1 && this->_tileMapsConfig.y == 1)) { - drawBasicTileMap(vramAddress, offset); + _drawBasicTileMap(vramAddress, offset); } vramAddress += TileMapByteSize; offset.x += NbCharacterWidth * this->_characterNbPixels.x; @@ -47,32 +52,42 @@ namespace ComSquare::PPU } } - void Background::drawBgTile(uint16_t data, Vector2 pos) + void Background::_drawBgTile(uint16_t data, Vector2 pos) { - uint16_t graphicAddress; union TileMapData tileData; - std::vector palette; - int index = 0; - uint8_t reference = 0; - uint32_t color = 0; tileData.raw = data; - palette = getPalette(tileData.palette); + + if (tileData.tilePriority != this->_priority) + return; + + uint16_t graphicAddress; + Vector2i tileOffset = {0, 0}; // X horizontal // Y vertical - graphicAddress = this->_tilesetAddress + (tileData.posY * NbTilePerRow * this->_bpp * TileBaseByteSize) + (tileData.posX * this->_bpp * TileBaseByteSize); + + this->_tileRenderer.setPaletteIndex(tileData.palette); + for (int i = 0; i < this->_characterNbPixels.y; i += Tile::NbPixelsHeight) { + for (int j = 0; j < this->_characterNbPixels.x; j += Tile::NbPixelsWidth) { + graphicAddress = this->_tilesetAddress + + ((tileData.posY + tileOffset.y) * NbTilePerRow * this->_bpp * Tile::BaseByteSize) + + ((tileData.posX + tileOffset.x) * this->_bpp * Tile::BaseByteSize); + this->_tileRenderer.render(graphicAddress); + merge2DArray(this->_tileBuffer, this->_tileRenderer.buffer, {j, i}); + tileOffset.x += 1; + } + tileOffset.x = 0; + tileOffset.y += 1; + } + + // todo check why i need to invert vertical and horizontal flips + if (tileData.verticalFlip) + HFlipArray(this->_tileBuffer, {this->_characterNbPixels.x, this->_characterNbPixels.y}); + if (tileData.horizontalFlip) + VFlipArray(this->_tileBuffer, {this->_characterNbPixels.x, this->_characterNbPixels.y}); for (int i = 0; i < this->_characterNbPixels.y; i++) { - index = i * this->_characterNbPixels.x; - if (tileData.verticalFlip) - index = (this->_characterNbPixels.y - 1 - i) * this->_characterNbPixels.x; - if (tileData.horizontalFlip) - index += this->_characterNbPixels.x - 1; for (int j = 0; j < this->_characterNbPixels.x; j++) { - reference = getPixelReferenceFromTile(graphicAddress, index); - color = getRealColor(palette[reference]); - if (tileData.tilePriority == this->_priority) // reference 0 is considered as transparency - this->buffer[pos.x][pos.y] = (reference) ? color : 0; - index += (tileData.horizontalFlip) ? -1 : 1; + this->buffer[pos.x][pos.y] = this->_tileBuffer[i][j]; pos.x++; } pos.x -= this->_characterNbPixels.x; @@ -80,74 +95,7 @@ namespace ComSquare::PPU } } - std::vector Background::getPalette(int nbPalette) - { - uint8_t nbColors = std::pow(2, this->_bpp); - uint16_t addr = nbPalette * this->_bpp * this->_bpp * 2; // 2 because it's 2 addr for 1 color - std::vector palette(nbColors); - - switch (this->_ppu.getBgMode()) { - case 0: - addr += (this->_bgNumber - 1) * (4 * 8) * 2; - break; - default: - break; - } - - for (int i = 0; i < nbColors; i++) { - palette[i] = this->_cgram->read(addr); - palette[i] += this->_cgram->read(addr + 1) << 8U; - addr += 2; - } - return palette; - } - - uint8_t Background::getPixelReferenceFromTile(uint16_t tileAddress, uint8_t pixelIndex) - { - uint8_t row = pixelIndex / this->_characterNbPixels.x; - uint8_t column = pixelIndex % this->_characterNbPixels.y; - - if (row >= TileNbPixelsHeight) { - tileAddress += 0x80 * this->_bpp; - row -= TileNbPixelsHeight; - } - if (column >= TileNbPixelsWidth) { - tileAddress += 0x8 * this->_bpp; - column -= TileNbPixelsWidth; - } - // TODO might not work with 8 bpp must check - tileAddress += 2 * row; - - return this->getPixelReferenceFromTileRow(tileAddress, column); - } - - uint8_t Background::getPixelReferenceFromTileRow(uint16_t tileAddress, uint8_t pixelIndex) - { - uint8_t highByte = this->_vram->read(tileAddress % VRAMSIZE); - uint8_t lowByte = this->_vram->read((tileAddress + 1) % VRAMSIZE); - uint8_t secondHighByte; - uint8_t secondLowByte; - uint16_t result = 0; - uint8_t shift = TileNbPixelsWidth - 1U - pixelIndex; - - switch (this->_bpp) { - case 8: - return highByte; - case 4: - secondHighByte = this->_vram->read((tileAddress + 16) % VRAMSIZE); - secondLowByte = this->_vram->read((tileAddress + 17) % VRAMSIZE); - result = ((secondHighByte & (1U << shift)) | ((secondLowByte & (1U << shift)) << 1U)); - result = (shift - 2 >= 0) ? result >> (shift - 2) : result << ((shift - 2) * -1); - FALLTHROUGH - case 2: - result += ((highByte & (1U << shift)) | ((lowByte & (1U << shift)) << 1U)) >> shift; - default: - break; - } - return result; - } - - void Background::drawBasicTileMap(uint16_t baseAddress, Vector2 offset) + void Background::_drawBasicTileMap(uint16_t baseAddress, Vector2 offset) { uint16_t tileMapValue = 0; Vector2 pos(0, 0); @@ -157,7 +105,8 @@ namespace ComSquare::PPU // TODO function to read 2 bytes (LSB order or bits reversed) tileMapValue = this->_vram->read(vramAddress); tileMapValue += this->_vram->read(vramAddress + 1) << 8U; - drawBgTile(tileMapValue, {(pos.x * this->_characterNbPixels.x) + offset.x, (pos.y * this->_characterNbPixels.y) + offset.y}); + _drawBgTile(tileMapValue, {(pos.x * this->_characterNbPixels.x) + offset.x, + (pos.y * this->_characterNbPixels.y) + offset.y}); vramAddress += 2; if (pos.x % 31 == 0 && pos.x) { pos.y++; @@ -189,6 +138,7 @@ namespace ComSquare::PPU this->_bpp = bpp; else this->_bpp = 2; + this->_tileRenderer.setBpp(this->_bpp); } void Background::setTilemaps(Vector2 tileMaps) @@ -196,11 +146,6 @@ namespace ComSquare::PPU this->_tileMapsConfig = tileMaps; } - void Background::setBgNumber(int bgNumber) - { - this->_bgNumber = bgNumber; - } - int Background::getBgNumber() const { return this->_bgNumber; diff --git a/sources/PPU/Background.hpp b/sources/PPU/Background.hpp index 07fbd6a..abbc00e 100644 --- a/sources/PPU/Background.hpp +++ b/sources/PPU/Background.hpp @@ -2,19 +2,16 @@ // Created by cbihan on 5/14/20. // -#ifndef COMSQUARE_BACKGROUND_HPP -#define COMSQUARE_BACKGROUND_HPP +#pragma once #include #include #include #include "../Models/Vector2.hpp" +#include "TileRenderer.hpp" #include "../Ram/Ram.hpp" #include "PPU.hpp" -//! @brief Transform SNES color code BGR to uint32_t RGB -uint32_t getRealColor(uint16_t color); - namespace ComSquare::PPU { class PPU; @@ -24,13 +21,6 @@ namespace ComSquare::PPU static constexpr int NbCharacterWidth = 32; //! @brief The number of character a TileMap has in height static constexpr int NbCharacterHeight = 32; - //! @brief The minimum number of pixel a tile can have in width - static constexpr int TileNbPixelsWidth = 8; - //! @brief The minimum number of pixel a tile can have in height - static constexpr int TileNbPixelsHeight = 8; - //! @brief The number of bytes used by a range of pixels (1 pixel per byte) - //! @note Used like: bpp * TileBaseByteSize to get the size of byte of 1 row of pixels - static constexpr unsigned TileBaseByteSize = 8; //! @brief The number of rows in one line of VRAM //! @note If you're lost by this description, open a tile viewer in an emulator, and set the number of tiles in width to 16 graphics static constexpr unsigned NbTilePerRow = 16; @@ -59,30 +49,20 @@ namespace ComSquare::PPU bool _priority; //! @brief The bg number (used to get the corresponding scroll) int _bgNumber; + //! @brief Class that actually render a tile + TileRenderer _tileRenderer; + //! @brief Buffer if we have tiles that are more than 8x8 + std::array, 16> _tileBuffer; //! @brief the access to vram std::shared_ptr _vram; //! @brief The access to cgram std::shared_ptr _cgram; //! @brief Draw a tile on the screen at x y pos - void drawBgTile(uint16_t data, Vector2 pos); - //! @brief Get a palette from the number of the palette - //! @param nbPalette The palette number (0 - 7) - //! @return The array of color of the palette - std::vector getPalette(int nbPalette); - //! @brief Get the color reference of a pixel from the address of the row - //! @param tileAddress The address of the line of pixel - //! @param pixelIndex The index of the pixel (0 - 7) - //! @return The color Reference - uint8_t getPixelReferenceFromTileRow(uint16_t tileAddress, uint8_t pixelIndex); - //! @brief Get the color pixel reference from the tile address - //! @param tileAddress The starting address of the tile - //! @param pixelIndex The index of the pixel (0 - 255) - //! @return The color reference - uint8_t getPixelReferenceFromTile(uint16_t tileAddress, uint8_t pixelIndex); + void _drawBgTile(uint16_t data, Vector2 pos); //! @brief draw a tileMap 32x32 starting at baseAddress //! @param baseAddress The starting address of the tileMap //! @param offset The rendering offeset in pixels - void drawBasicTileMap(uint16_t baseAddress, Vector2 offset); + void _drawBasicTileMap(uint16_t baseAddress, Vector2 offset); public: //! @brief The size of the background (x, y) Vector2 backgroundSize; @@ -105,9 +85,7 @@ namespace ComSquare::PPU //! @brief setter for private variable _tileMaps //! @param tileMaps The tileMaps to set void setTilemaps(Vector2 tileMaps); - //! @brief set the Background number - //! @param bgNumber the new Background Number - void setBgNumber(int bgNumber); + //! @brief Get the BackGround Number //! @return the current Background number int getBgNumber() const; @@ -127,7 +105,4 @@ namespace ComSquare::PPU //! @brief Delete assignment operator Background &operator=(const Background &) = delete; }; -} - - -#endif //COMSQUARE_BACKGROUND_HPP \ No newline at end of file +} \ No newline at end of file diff --git a/sources/PPU/PPU.cpp b/sources/PPU/PPU.cpp index 2b2555a..3557ed1 100644 --- a/sources/PPU/PPU.cpp +++ b/sources/PPU/PPU.cpp @@ -5,10 +5,11 @@ #include #include #include "PPU.hpp" -#include "../Exceptions/NotImplementedException.hpp" -#include "../Exceptions/InvalidAddress.hpp" -#include "../Ram/Ram.hpp" -#include "../Models/Vector2.hpp" +#include "Exceptions/NotImplementedException.hpp" +#include "Exceptions/InvalidAddress.hpp" +#include "Ram/Ram.hpp" +#include "Models/Vector2.hpp" +#include "Debugger/TileViewer/RAMTileRenderer.hpp" #include namespace ComSquare::PPU @@ -43,7 +44,7 @@ namespace ComSquare::PPU this->cgram->write(67, 0x7F); //tiles - int vram_test[] = { +/* int vram_test[] = { 00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03, @@ -74,17 +75,17 @@ namespace ComSquare::PPU 00,0x03,0x00,0x03,0x00,0x03,0x00,0x06,0x00,0x0c,0x00,0x18,0x00,0xf0,0x00,0xe0, 00,0x00,0x00,0x00,0x80,0x00,0xc0,0x00,0xe0,0x00,0xf0,0x00,0xf8,0x00,0xfc,0x00, 00,0x00,0x00,0x00,0x01,0x00,0x03,0x00,0x07,0x00,0x0f,00,0x1f,00,0x3f,00, -1 - }; - /*int *cgram_test = get_dump_cgram(); + };*/ + int *cgram_test = get_dump_cgram(); for (int i = 0; cgram_test[i] != -1; i++) { this->cgram->write(i, cgram_test[i]); - }*/ + } - // int *vram_test = get_dump_vram(); + int *vram_test = get_dump_vram(); for (int i = 0; vram_test[i] != -1; i++) { this->vram->write(i, vram_test[i]); } - int vram_test_2[] = {8, 00, 02, 00, 0x0A, 00, 02, 00, 0x0A, 00, 00, 00, 00, 00, 00, -1}; + /* int vram_test_2[] = {8, 00, 02, 00, 0x0A, 00, 02, 00, 0x0A, 00, 00, 00, 00, 00, 00, -1}; for (int i = 0; vram_test_2[i] != -1; i++) { this->vram->write(i + 0x8000, vram_test_2[i]); } @@ -143,8 +144,8 @@ namespace ComSquare::PPU //this->_registers._bgofs[3].raw = 0x03DF; this->_registers._t[0].enableWindowDisplayBg1 = true; this->_registers._t[0].enableWindowDisplayBg2 = true; +*/ - /* //registers aladin this->_registers._bgmode.bgMode = 1; @@ -199,12 +200,12 @@ namespace ComSquare::PPU this->_registers._t[0].enableWindowDisplayBg2 = true; this->_registers._t[0].enableWindowDisplayBg3 = true; -*/ + } uint8_t PPU::read(uint24_t addr) { - //return 0; + return 0; switch (addr) { case PpuRegisters::mpyl: return this->_registers._mpy.mpyl; @@ -249,7 +250,7 @@ namespace ComSquare::PPU void PPU::write(uint24_t addr, uint8_t data) { - //return; + return; switch (addr) { case PpuRegisters::inidisp: this->_registers._inidisp.raw = data; @@ -466,6 +467,7 @@ namespace ComSquare::PPU { (void)cycles; + this->renderMainAndSubScreen(); this->add_buffer(this->_screen, this->_subScreen); this->add_buffer(this->_screen, this->_mainScreen); @@ -477,6 +479,10 @@ namespace ComSquare::PPU } } this->_renderer.drawScreen(); + for (auto &i : this->_mainScreen) + i.fill(0XFF); + for (auto &i : this->_subScreen) + i.fill(0XFF); } std::string PPU::getName() const @@ -806,13 +812,15 @@ namespace ComSquare::PPU } } - template - void PPU::add_buffer(std::array, DEST_SIZE> &bufferDest, std::array, SRC_SIZE> &bufferSrc) + template + void PPU::add_buffer(std::array, DEST_SIZE_X> &bufferDest, + const std::array, SRC_SIZE_X> &bufferSrc, + const Vector2 &offset) { for (unsigned long i = 0; i < bufferSrc.size(); i++) { for (unsigned long j = 0; j < bufferSrc[i].size(); j++) { if (bufferSrc[i][j] > 0xFF) // 0xFF correspond to a black pixel with full brightness - bufferDest[i][j] = bufferSrc[i][j]; + bufferDest[i + offset.x ][j + offset.y] = bufferSrc[i][j]; } } } diff --git a/sources/PPU/PPU.hpp b/sources/PPU/PPU.hpp index 8dc2bab..7a3be53 100644 --- a/sources/PPU/PPU.hpp +++ b/sources/PPU/PPU.hpp @@ -6,17 +6,18 @@ #define COMSQUARE_PPU_HPP #include -#include "../Memory/AMemory.hpp" -#include "../Memory/MemoryBus.hpp" -#include "../Renderer/IRenderer.hpp" -#include "../Ram/Ram.hpp" -#include "../Models/Vector2.hpp" +#include "Memory/AMemory.hpp" +#include "Memory/MemoryBus.hpp" +#include "Renderer/IRenderer.hpp" +#include "Ram/Ram.hpp" +#include "Models/Vector2.hpp" #include "Background.hpp" #include "PPUUtils.hpp" +#include "Debugger/TileViewer/RAMTileRenderer.hpp" #define FALLTHROUGH __attribute__((fallthrough)); -// TODO check if it usefull to have defines instead of constepxr +// TODO check if it useful to have defines instead of constexpr #define VRAMSIZE 65536 #define CGRAMSIZE 512 #define OAMRAMSIZE 544 @@ -569,6 +570,7 @@ namespace ComSquare::PPU uint16_t _vramReadBuffer = 0; //! @brief Struct that contain all necessary vars for the use of the registers struct PpuState _ppuState; + public: explicit PPU(Renderer::IRenderer &renderer); @@ -618,8 +620,10 @@ namespace ComSquare::PPU //! @brief Render the Main and sub screen correctly void renderMainAndSubScreen(); //! @brief Add a bg buffer to another buffer - template - void add_buffer(std::array, DEST_SIZE> &bufferDest, std::array, SRC_SIZE> &bufferSrc); + template + void add_buffer(std::array, DEST_SIZE_X> &bufferDest, + const std::array, SRC_SIZE_X> &bufferSrc, + const Vector2 &offset = {0, 0}); //! @brief Add a bg to the sub and/or main screen void addToMainSubScreen(Background &bg); //! @brief Get the current background Mode @@ -630,6 +634,20 @@ namespace ComSquare::PPU Vector2 getBgScroll(int bgNumber) const; //! @brief Allow to look the value of each write register (used by Register debugger) const Registers &getWriteRegisters() const; + + template + void add_buffer(const std::array, SRC_SIZE_X> &buffer, + const Vector2 &offset = {0, 0}) + { + for (auto &i : this->_screen) + i.fill(0XFF); + for (unsigned long i = 0; i < buffer.size(); i++) { + for (unsigned long j = 0; j < buffer[i].size(); j++) { + if (buffer[i][j] > 0xFF) // 0xFF correspond to a black pixel with full brightness + this->_screen[i + offset.x][j + offset.y] = buffer[i][j]; + } + } + } }; //! @brief Transform SNES color code BGR to uint32_t RGB diff --git a/sources/PPU/PPUUtils.cpp b/sources/PPU/PPUUtils.cpp index be10b14..8a1c5d8 100644 --- a/sources/PPU/PPUUtils.cpp +++ b/sources/PPU/PPUUtils.cpp @@ -9,16 +9,11 @@ namespace ComSquare::PPU uint32_t getRealColor(uint16_t color) { - uint8_t blue; - uint8_t red; - uint8_t green; - uint32_t pixelTmp; + uint8_t blue = (color & 0x7D00U) >> 10U; + uint8_t green = (color & 0x03E0U) >> 5U; + uint8_t red = (color & 0x001FU); + uint32_t pixelTmp = 0xFF; - blue = (color & 0x7D00U) >> 10U; - green = (color & 0x03E0U) >> 5U; - red = (color & 0x001FU); - - pixelTmp = 0xFF; pixelTmp += (red * 255U / 31U) << 24U; pixelTmp += (green * 255U / 31U) << 16U; pixelTmp += (blue * 255U / 31U) << 8U; diff --git a/sources/PPU/PPUUtils.hpp b/sources/PPU/PPUUtils.hpp index 06de286..1cdb0a7 100644 --- a/sources/PPU/PPUUtils.hpp +++ b/sources/PPU/PPUUtils.hpp @@ -6,9 +6,15 @@ #define COMSQUARE_PPU_UTILS_HPP #include +#include +#include +#include "Models//Vector2.hpp" namespace ComSquare::PPU { + + //! @brief Transform SNES color code BGR to uint32_t RGB + uint32_t getRealColor(uint16_t color); //! @brief Used to parse easily VRAM Tile information union TileMapData { struct { @@ -36,5 +42,35 @@ namespace ComSquare::PPU uint8_t hScrollPrevValue; }; + template + void merge2DArray(std::array, DEST_SIZE_Y> &bufferDest, + const std::array, SRC_SIZE_Y> &bufferSrc, + const Vector2 &offset = {0, 0}) + { + for (unsigned long i = 0; i < bufferSrc.size(); i++) { + for (unsigned long j = 0; j < bufferSrc[i].size(); j++) { + bufferDest[i + offset.y][j + offset.x] = bufferSrc[i][j]; + } + } + } + + template + void VFlipArray(std::array, SRC_SIZE_Y> &array, + const Vector2 &size, + const Vector2 &offset = {0, 0}) + { + for (int i = offset.y; i < offset.y + size.y; i++) { + std::reverse(array[i].begin() + offset.x, array[i].begin() + offset.x + size.x); + } + } + + template + void HFlipArray(std::array, SRC_SIZE_Y> &array, + const Vector2 &size, + const Vector2 &offset = {0, 0}) + { + std::reverse(array.begin() + offset.x, array.begin() + offset.x + size.x); + } + } #endif //COMSQUARE_PPU_UTILS_HPP \ No newline at end of file diff --git a/sources/PPU/Tile.hpp b/sources/PPU/Tile.hpp new file mode 100644 index 0000000..779ce6e --- /dev/null +++ b/sources/PPU/Tile.hpp @@ -0,0 +1,21 @@ +// +// Created by cbihan on 25/05/2021. +// + +#pragma once + +namespace ComSquare::PPU +{ + //! @brief Info on tile struct + struct Tile + { + //! @brief The number of pixel a base tile can have in width + static constexpr int NbPixelsWidth = 8; + //! @brief The number of pixel a base tile can have in height + static constexpr int NbPixelsHeight = 8; + //! @brief A base tile size in byte is 0x8 + //! @note Used like: bpp * BaseByteSize to get the byte size of 1 tile + static constexpr int BaseByteSize = 0x8; + }; + +} \ No newline at end of file diff --git a/sources/PPU/TileRenderer.cpp b/sources/PPU/TileRenderer.cpp new file mode 100644 index 0000000..348049d --- /dev/null +++ b/sources/PPU/TileRenderer.cpp @@ -0,0 +1,134 @@ +// +// Created by cbihan on 24/05/2021. +// + +#include +#include +#include "TileRenderer.hpp" +#include "PPU/PPU.hpp" +#include "PPU/Tile.hpp" +#include + +namespace ComSquare::PPU +{ + TileRenderer::TileRenderer() + : _ram(nullptr), + _cgram(nullptr), + _bpp(2), + _paletteIndex(0), + buffer({{{0}}}) + { + } + + void TileRenderer::setRam(std::shared_ptr ram) + { + this->_ram = std::move(ram); + } + + void TileRenderer::render(uint16_t tileAddress) + { + std::vector palette = this->getPalette(this->_paletteIndex); + int it = 0; + this->buffer = {{{0}}}; + + for (auto &row : this->buffer) { + for (auto &pixel : row) { + uint8_t pixelReference = this->getPixelReferenceFromTile(tileAddress, it++); + pixel = pixelReference ? getRealColor(palette[pixelReference]) : 0; + } + } + } + + uint8_t TileRenderer::getPixelReferenceFromTile(uint16_t tileAddress, uint8_t pixelIndex) + { + uint8_t row = pixelIndex / Tile::NbPixelsWidth; + uint8_t column = pixelIndex % Tile::NbPixelsHeight; + + if (row >= Tile::NbPixelsHeight) { + tileAddress += 0x80 * this->_bpp; + row -= Tile::NbPixelsHeight; + } + if (column >= Tile::NbPixelsWidth) { + tileAddress += 0x8 * this->_bpp; + column -= Tile::NbPixelsWidth; + } + // TODO might not work with 8 bpp must check + tileAddress += 2 * row; + + return this->getPixelReferenceFromTileRow(tileAddress, column); + } + + void TileRenderer::setPaletteIndex(int paletteIndex) + { + this->_paletteIndex = paletteIndex; + } + + void TileRenderer::setBpp(int bpp) + { + this->_bpp = bpp; + } + + uint8_t TileRenderer::read2BPPValue(uint16_t tileRowAddress, uint8_t pixelIndex) + { + // TODO unit test this + size_t size = this->_ram->getSize(); + uint8_t highByte = this->_ram->read(tileRowAddress % size); + uint8_t lowByte = this->_ram->read((tileRowAddress + 1) % size); + uint8_t shift = 8 - 1U - pixelIndex; + + return ((highByte & (1U << shift)) | ((lowByte & (1U << shift)) << 1U)) >> shift; + } + + uint8_t TileRenderer::getPixelReferenceFromTileRow(uint16_t tileRowAddress, uint8_t pixelIndex) + { + // TODO unit test this + uint16_t result = 0; + // TODO do a constexpr + const int TileByteSizeRow = 16; + + switch (this->_bpp) { + case 8: + result += this->read2BPPValue(tileRowAddress + TileByteSizeRow * 2, pixelIndex) << 4; + result += this->read2BPPValue(tileRowAddress + TileByteSizeRow * 3, pixelIndex) << 6; + FALLTHROUGH + case 4: + result += this->read2BPPValue(tileRowAddress + TileByteSizeRow, pixelIndex) << 2; + FALLTHROUGH + case 2: + result += this->read2BPPValue(tileRowAddress, pixelIndex); + default: + break; + } + return result; + } + + std::vector TileRenderer::getPalette(int nbPalette) + { + // todo if needed the tile renderer could cache the palette to avoid recompute this every render + uint16_t nbColors = std::pow(2, this->_bpp); + uint16_t addr = nbPalette * this->_bpp * this->_bpp * 2; // 2 because it's 2 addr for 1 color + std::vector palette(nbColors); + + for (int i = 0; i < nbColors; i++) { + palette[i] = this->_cgram->read(addr); + palette[i] += this->_cgram->read(addr + 1) << 8U; + addr += 2; + } + return palette; + } + + void TileRenderer::setCgram(std::shared_ptr ram) + { + this->_cgram = std::move(ram); + } + + int TileRenderer::getBpp() const + { + return this->_bpp; + } + + int TileRenderer::getPaletteIndex() const + { + return this->_paletteIndex; + } +} \ No newline at end of file diff --git a/sources/PPU/TileRenderer.hpp b/sources/PPU/TileRenderer.hpp new file mode 100644 index 0000000..e5808c5 --- /dev/null +++ b/sources/PPU/TileRenderer.hpp @@ -0,0 +1,63 @@ +// +// Created by cbihan on 24/05/2021. +// + +#pragma once + +#include +#include "Ram/Ram.hpp" + +namespace ComSquare::PPU +{ + class TileRenderer { + private: + //! @brief ram to render + std::shared_ptr _ram; + //! @brief cgram to access the colors + std::shared_ptr _cgram; + //! @brief The bpp to use while rendering + int _bpp; + //! @brief The palette number to use while rendering + int _paletteIndex; + public: + // todo background or ppu should have constexpr to explain it (16) + //! @brief internal buffer max rendered tiles are 16x16 + std::array, 8> buffer; + //! @brief Set the palette to use for render (index of palette) + void setPaletteIndex(int paletteIndex); + //! @brief Set the ram to look for color references + void setCgram(std::shared_ptr ram); + //! @brief Set the bpp to render graphics + void setBpp(int bpp); + //! @brief The ram to render + void setRam(std::shared_ptr ram); + //! @brief Get the current bpp + int getBpp() const; + //! @brief Get the index of the current palette used + int getPaletteIndex() const; + //! @brief Get the color pixel reference from the tile address and pixelIndex + //! @param tileAddress The starting address of the tile + //! @param pixelIndex The index of the pixel (0 - 255) + //! @return The color reference + //! @note This function is wrapper of getPixelReferenceFromTileRow + uint8_t getPixelReferenceFromTile(uint16_t tileAddress, uint8_t pixelIndex); + //! @brief Get the color reference of a pixel from the address of the row + //! @param tileRowAddress The address of the line of pixel + //! @param pixelIndex The index of the pixel (0 - 7) + //! @return The color Reference + uint8_t getPixelReferenceFromTileRow(uint16_t tileRowAddress, uint8_t pixelIndex); + //! @brief Gives the actual selected palette with all of it's colors + //! @return The array of color of the palette + //! @warning Values are CGRAM colors use PPU::getRealColor function to get the actual real color + std::vector getPalette(int nbPalette); + //! @brief read the 2bpp value for a pixel (used multple times for 4bpp and 8bpp) + uint8_t read2BPPValue(uint16_t tileRowAddress, uint8_t pixelIndex); + //! @brief render the tile (8x8) at the tileAddress + //! @param tileAddress The address of the tile to render + void render(uint16_t tileAddress); + TileRenderer(); + TileRenderer(const TileRenderer &) = default; + ~TileRenderer() = default; + TileRenderer &operator=(const TileRenderer &) = default; + }; +} \ No newline at end of file diff --git a/sources/Renderer/QtRenderer/QtSFML.cpp b/sources/Renderer/QtRenderer/QtSFML.cpp index 950ca09..48bbfae 100644 --- a/sources/Renderer/QtRenderer/QtSFML.cpp +++ b/sources/Renderer/QtRenderer/QtSFML.cpp @@ -7,6 +7,7 @@ #include #include #include +#include "SNES.hpp" #include "../../Exceptions/DebuggableError.hpp" #include "QtSFML.hpp" @@ -17,67 +18,14 @@ namespace ComSquare::Renderer { - QtSFML::QtSFML(unsigned int h, unsigned int w) : - _window(), _sfWidget(nullptr) + QtSFML::QtSFML(QWidget *parentWidget) + : _window(parentWidget), _sfWidget(nullptr) { - this->_window.resize(w, h); - this->_window.setWindowIcon(QIcon(":/resources/Logo.png")); } void QtSFML::createWindow(SNES &snes, int maxFPS) { - this->setWindowName(snes.cartridge->header.gameName); - this->_sfWidget = std::make_unique(snes, &_window, QPoint(0, 0), QSize(this->_window.width(), this->_window.height()), maxFPS); - this->_window.setCentralWidget(this->_sfWidget.get()); - - QMenu *file = this->_window.menuBar()->addMenu("&File"); - //TODO implement rom opening from this menu. - (void)file; - - QMenu *game = this->_window.menuBar()->addMenu("&Game"); - QAction *reset = new QAction("Reset", &this->_window); - QMainWindow::connect(reset, &QAction::triggered, this->_sfWidget.get(), &QtFullSFML::reset); - game->addAction(reset); - - - - QMenu *debugger = this->_window.menuBar()->addMenu("&Debugger"); - QAction *cpuDebugger = new QAction("CPU's Debugger", &this->_window); - cpuDebugger->setShortcut(Qt::Key_F1); - QMainWindow::connect(cpuDebugger, &QAction::triggered, this->_sfWidget.get(), &QtFullSFML::enableDebugCPU); - debugger->addAction(cpuDebugger); - - QAction *ramViewer = new QAction("Memory viewer", &this->_window); - ramViewer->setShortcut(Qt::Key_F2); - QMainWindow::connect(ramViewer, &QAction::triggered, this->_sfWidget.get(), &QtFullSFML::enableRamViewer); - debugger->addAction(ramViewer); - - QAction *headerViewer = new QAction("Header viewer", &this->_window); - headerViewer->setShortcut(Qt::Key_F3); - QMainWindow::connect(headerViewer, &QAction::triggered, this->_sfWidget.get(), &QtFullSFML::enableHeaderViewer); - debugger->addAction(headerViewer); - - QAction *apuDebugger = new QAction("APU's Debugger", &this->_window); - apuDebugger->setShortcut(Qt::Key_F4); - QMainWindow::connect(apuDebugger, &QAction::triggered, this->_sfWidget.get(), &QtFullSFML::enableDebugAPU); - debugger->addAction(apuDebugger); - - QAction *busDebugger = new QAction("Memory bus Viewer", &this->_window); - busDebugger->setShortcut(Qt::Key_F5); - QMainWindow::connect(busDebugger, &QAction::triggered, this->_sfWidget.get(), &QtFullSFML::enableDebugBus); - debugger->addAction(busDebugger); - - QAction *cgramDebugger = new QAction("Palette Viewer", &this->_window); - cgramDebugger->setShortcut(Qt::Key_F6); - QMainWindow::connect(cgramDebugger, &QAction::triggered, this->_sfWidget.get(), &QtFullSFML::enableCgramViewer); - debugger->addAction(cgramDebugger); - - QAction *registerDebugger = new QAction("Registers Viewer", &this->_window); - registerDebugger->setShortcut(Qt::Key_F7); - QMainWindow::connect(registerDebugger, &QAction::triggered, this->_sfWidget.get(), &QtFullSFML::enableRegisterViewer); - debugger->addAction(registerDebugger); - - this->_window.show(); + this->_sfWidget = std::make_unique(snes, this->_window, QPoint(0, 0), QSize(this->_window->width(), this->_window->height()), maxFPS); } void QtSFML::putPixel(unsigned y, unsigned x, uint32_t rgba) @@ -94,7 +42,7 @@ namespace ComSquare::Renderer void QtSFML::setWindowName(std::string &newWindowName) { - this->_window.setWindowTitle((newWindowName + " - ComSquare").c_str()); + this->_window->setWindowTitle((newWindowName + " - ComSquare").c_str()); } QtFullSFML::QtFullSFML(SNES &snes, QWidget *parent, const QPoint &position, const QSize &size, int frameRate) : @@ -154,4 +102,77 @@ namespace ComSquare::Renderer { this->_snes.enableRegisterDebugging(); } + + void QtFullSFML::enableTileViewer() + { + this->_snes.enableTileViewerDebugging(); + } + + QtSFMLWindow::QtSFMLWindow(unsigned int height, unsigned int width) + : QtSFML(&this->_window) + { + this->_window.resize(width, height); + this->_window.setWindowIcon(QIcon(":/resources/Logo.png")); + } + + void QtSFMLWindow::createWindow(SNES &snes, int maxFPS) + { + QtSFML::createWindow(snes, maxFPS); + this->setWindowName(snes.cartridge->header.gameName); + this->_window.setCentralWidget(this->_sfWidget.get()); + + QMenu *file = this->_window.menuBar()->addMenu("&File"); + //TODO implement rom opening from this menu. + (void)file; + + QMenu *game = this->_window.menuBar()->addMenu("&Game"); + QAction *reset = new QAction("Reset", &this->_window); + QMainWindow::connect(reset, &QAction::triggered, this->_sfWidget.get(), &QtFullSFML::reset); + game->addAction(reset); + + + + QMenu *debugger = this->_window.menuBar()->addMenu("&Debugger"); + QAction *cpuDebugger = new QAction("CPU's Debugger", &this->_window); + cpuDebugger->setShortcut(Qt::Key_F1); + QMainWindow::connect(cpuDebugger, &QAction::triggered, this->_sfWidget.get(), &QtFullSFML::enableDebugCPU); + debugger->addAction(cpuDebugger); + + QAction *ramViewer = new QAction("Memory viewer", &this->_window); + ramViewer->setShortcut(Qt::Key_F2); + QMainWindow::connect(ramViewer, &QAction::triggered, this->_sfWidget.get(), &QtFullSFML::enableRamViewer); + debugger->addAction(ramViewer); + + QAction *headerViewer = new QAction("Header viewer", &this->_window); + headerViewer->setShortcut(Qt::Key_F3); + QMainWindow::connect(headerViewer, &QAction::triggered, this->_sfWidget.get(), &QtFullSFML::enableHeaderViewer); + debugger->addAction(headerViewer); + + QAction *apuDebugger = new QAction("APU's Debugger", &this->_window); + apuDebugger->setShortcut(Qt::Key_F4); + QMainWindow::connect(apuDebugger, &QAction::triggered, this->_sfWidget.get(), &QtFullSFML::enableDebugAPU); + debugger->addAction(apuDebugger); + + QAction *busDebugger = new QAction("Memory bus Viewer", &this->_window); + busDebugger->setShortcut(Qt::Key_F5); + QMainWindow::connect(busDebugger, &QAction::triggered, this->_sfWidget.get(), &QtFullSFML::enableDebugBus); + debugger->addAction(busDebugger); + + QAction *cgramDebugger = new QAction("Palette Viewer", &this->_window); + cgramDebugger->setShortcut(Qt::Key_F6); + QMainWindow::connect(cgramDebugger, &QAction::triggered, this->_sfWidget.get(), &QtFullSFML::enableCgramViewer); + debugger->addAction(cgramDebugger); + + QAction *registerDebugger = new QAction("Registers Viewer", &this->_window); + registerDebugger->setShortcut(Qt::Key_F7); + QMainWindow::connect(registerDebugger, &QAction::triggered, this->_sfWidget.get(), &QtFullSFML::enableRegisterViewer); + debugger->addAction(registerDebugger); + + QAction *tileDebugger = new QAction("Tile Viewer", &this->_window); + tileDebugger->setShortcut(Qt::Key_F8); + QMainWindow::connect(tileDebugger, &QAction::triggered, this->_sfWidget.get(), &QtFullSFML::enableTileViewer); + debugger->addAction(tileDebugger); + + this->_window.show(); + } } \ No newline at end of file diff --git a/sources/Renderer/QtRenderer/QtSFML.hpp b/sources/Renderer/QtRenderer/QtSFML.hpp index 2156052..565885c 100644 --- a/sources/Renderer/QtRenderer/QtSFML.hpp +++ b/sources/Renderer/QtRenderer/QtSFML.hpp @@ -37,6 +37,8 @@ namespace ComSquare::Renderer void enableCgramViewer(); //! @brief Action called when clicking on the enable DMA viewer button. void enableRegisterViewer(); + //! @brief Action called when clicking on the enable Tile viewer button + void enableTileViewer(); //! @brief Action called when clicking on the reset button. void reset(); @@ -44,20 +46,21 @@ namespace ComSquare::Renderer QtFullSFML(SNES &snes, QWidget* parent, const QPoint& position, const QSize& size, int frameRate = 0); QtFullSFML(const QtFullSFML &) = delete; QtFullSFML &operator=(const QtFullSFML &) = delete; - ~QtFullSFML() override = default; + ~QtFullSFML() = default; }; - //! @brief A SFML renderer inside a QT window. + //! @brief A SFML renderer inside a QT widget. class QtSFML : public IRenderer { private: //! @brief The main window that the app reside on. - QMainWindow _window; + QWidget *_window; + protected: //! @brief The SFML widget. std::unique_ptr _sfWidget = nullptr; 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; + virtual void createWindow(SNES &snes, int maxFPS) override; //! @brief Add a pixel to the buffer to the coordinates x, y with the color rgba. //! @param X horizontal index. //! @param Y vertical index. @@ -72,14 +75,29 @@ namespace ComSquare::Renderer //! @brief Set a new name to the window, if there is already a name it will be overwrite. //! @param newWindowName new title for the window. void setWindowName(std::string &newWindowName) 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(unsigned int height, unsigned int width); + //! @brief Constructor that return a SFML renderer inside a QT widget. + QtSFML(QWidget *parentWidget); QtSFML(const QtSFML &) = delete; QtSFML &operator=(const QtSFML &) = delete; ~QtSFML() = default; }; + + class QtSFMLWindow : public QtSFML { + private: + //! @brief The main window that the app reside on. + QMainWindow _window; + 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. + QtSFMLWindow(unsigned int height, unsigned int width); + QtSFMLWindow(const QtSFMLWindow &) = delete; + QtSFMLWindow &operator=(const QtSFMLWindow &) = delete; + ~QtSFMLWindow() = default; + }; } #endif //COMSQUARE_QTSFML_HPP diff --git a/sources/Renderer/QtRenderer/QtSfmlTileRenderer.cpp b/sources/Renderer/QtRenderer/QtSfmlTileRenderer.cpp new file mode 100644 index 0000000..782b3cc --- /dev/null +++ b/sources/Renderer/QtRenderer/QtSfmlTileRenderer.cpp @@ -0,0 +1,28 @@ +// +// Created by cbihan on 08/06/2021. +// + +#include +#include "QtSfmlTileRenderer.hpp" + +namespace ComSquare::Renderer +{ + + QtSFMLTileRenderer::QtSFMLTileRenderer(QWidget *parent, + int frameRate) + : QtWidgetSFML(parent, {0, 0}, {1025, 1025}, frameRate) + { + // todo the size of the sfml renderwindow should fill the parent + } + + void QtSFMLTileRenderer::_onUpdate() + { + this->_window.clear(sf::Color::Black); + for (unsigned long i = 0; i < this->buffer.size(); i++) { + for (unsigned long j = 0; j < this->buffer[i].size(); j++) { + this->putPixel(j, i, this->buffer[i][j]); + } + } + this->drawScreen(); + } +} \ No newline at end of file diff --git a/sources/Renderer/QtRenderer/QtSfmlTileRenderer.hpp b/sources/Renderer/QtRenderer/QtSfmlTileRenderer.hpp new file mode 100644 index 0000000..a3a95a9 --- /dev/null +++ b/sources/Renderer/QtRenderer/QtSfmlTileRenderer.hpp @@ -0,0 +1,31 @@ +// +// Created by cbihan on 08/06/2021. +// + +#pragma once + +#include +#include +#include "QtWidgetSFML.hpp" +#include "Debugger/TileViewer/RAMTileRenderer.hpp" + +namespace ComSquare::Renderer +{ + class QtSFMLTileRenderer : public QtWidgetSFML + { + public: + std::array, 1024> buffer; + + //! @brief Function called to update this widget. + void _onUpdate() override; + + //! @brief ctor + explicit QtSFMLTileRenderer(QWidget* parent, int frameRate = 0); + //! @brief copy ctor + QtSFMLTileRenderer(const QtSFMLTileRenderer &) = delete; + //! @brief default ctor + ~QtSFMLTileRenderer() override = default; + //! @brief assignment operator + QtSFMLTileRenderer &operator=(const QtSFMLTileRenderer &) = delete; + }; +} \ No newline at end of file diff --git a/sources/Renderer/QtRenderer/QtWidgetSFML.hpp b/sources/Renderer/QtRenderer/QtWidgetSFML.hpp index 1825d51..ce5c5c9 100644 --- a/sources/Renderer/QtRenderer/QtWidgetSFML.hpp +++ b/sources/Renderer/QtRenderer/QtWidgetSFML.hpp @@ -28,7 +28,7 @@ namespace ComSquare::Renderer //! @brief QT event that refresh the widget. (A draw screen) void paintEvent(QPaintEvent*) override; - // @brief Internal timer used for update intervals. + //! @brief Internal timer used for update intervals. QTimer _timer; //! @brief Has the SF window been created yet. bool _isInitialized = false; diff --git a/sources/Renderer/SFRenderer.cpp b/sources/Renderer/SFRenderer.cpp index b051ff7..8bb23fe 100644 --- a/sources/Renderer/SFRenderer.cpp +++ b/sources/Renderer/SFRenderer.cpp @@ -8,6 +8,7 @@ #include #include #include +#include "SNES.hpp" #include namespace ComSquare::Renderer @@ -67,11 +68,7 @@ namespace ComSquare::Renderer if (y >= this->_videoMode.height) throw InvalidPixelPosition("Height", y, this->_videoMode.height); - sf::Color pixels; - pixels.r = rgba >> 24U; - pixels.g = rgba >> 16U; - pixels.b = rgba >> 8U; - pixels.a = rgba >> 0U; + sf::Color pixels(rgba); this->_pixelBuffer[this->_videoMode.width * y + x] = pixels; } diff --git a/sources/Renderer/SFRenderer.hpp b/sources/Renderer/SFRenderer.hpp index 8221ea3..c73c1e5 100644 --- a/sources/Renderer/SFRenderer.hpp +++ b/sources/Renderer/SFRenderer.hpp @@ -6,7 +6,6 @@ #define COMSQUARE_SFRENDERER_HPP #include "IRenderer.hpp" -#include "../SNES.hpp" #include #include #include diff --git a/sources/SNES.cpp b/sources/SNES.cpp index 3ad1cb4..e52d9fa 100644 --- a/sources/SNES.cpp +++ b/sources/SNES.cpp @@ -10,6 +10,7 @@ #include "Debugger/APUDebug.hpp" #include "Debugger/MemoryBusDebug.hpp" #include "Debugger/CGramDebug.hpp" +#include "Debugger/TileViewer/TileViewer.hpp" #endif namespace ComSquare @@ -124,7 +125,7 @@ namespace ComSquare void SNES::enableMemoryBusDebugging() { - #ifdef DEBUGGER_ENABLED + #ifdef DEBUGGER_ENABLED if (this->bus->isDebugger()) std::static_pointer_cast(this->bus)->focus(); else @@ -132,9 +133,9 @@ namespace ComSquare this->bus = std::make_shared(*this, *this->bus); this->cpu->setMemoryBus(this->bus); } - #else + #else std::cerr << "Debugging features are not enabled. You can't enable the debugger." << std::endl; - #endif + #endif } void SNES::disableMemoryBusDebugging() @@ -160,7 +161,7 @@ namespace ComSquare void SNES::disableCgramDebugging() { #ifdef DEBUGGER_ENABLED - this->_cgramViewer = nullptr; + this->_cgramViewer = nullptr; #endif } @@ -180,4 +181,21 @@ namespace ComSquare this->_registerViewer = std::make_unique(*this); #endif } + + void SNES::disableTileViewerDebugging() + { + #ifdef DEBUGGER_ENABLED + this->_tileViewer = nullptr; + #endif + } + + void SNES::enableTileViewerDebugging() + { + #ifdef DEBUGGER_ENABLED + if (this->_tileViewer) + this->_tileViewer->focus(); + else + this->_tileViewer = std::make_unique(*this, *this->ppu); + } + #endif } diff --git a/sources/SNES.hpp b/sources/SNES.hpp index 4739600..133730e 100644 --- a/sources/SNES.hpp +++ b/sources/SNES.hpp @@ -19,6 +19,7 @@ #include "Debugger/HeaderViewer.hpp" #include "Debugger/CGramDebug.hpp" #include "Debugger/RegisterViewer.hpp" + #include "Debugger/TileViewer/TileViewer.hpp" #endif namespace ComSquare @@ -35,6 +36,8 @@ namespace ComSquare std::unique_ptr _cgramViewer; //! @brief The window that allow the user to view registers. std::unique_ptr _registerViewer; + //! @brief The window that allow the user to view the cgram as tiles. + std::unique_ptr _tileViewer; #endif public: //! @brief The memory bus that map addresses to components. @@ -87,6 +90,10 @@ namespace ComSquare void disableRegisterDebugging(); //! @brief Enable the Register's debugging window. void enableRegisterDebugging(); + //! @brief Disable the TileViewer's debugging window. + void disableTileViewerDebugging(); + //! @brief Enable the TileViewer's debugging window. + void enableTileViewerDebugging(); //! @brief Create all the components using a common memory bus for all of them. SNES(const std::string &ramPath, Renderer::IRenderer &renderer); diff --git a/sources/main.cpp b/sources/main.cpp index a13903b..e31bbd7 100644 --- a/sources/main.cpp +++ b/sources/main.cpp @@ -6,7 +6,6 @@ #include #include #include "SNES.hpp" -#include "Renderer/SFRenderer.hpp" #include "Renderer/QtRenderer/QtSFML.hpp" using namespace ComSquare; @@ -81,7 +80,7 @@ int main(int argc, char **argv) } QApplication app(argc, argv); QApplication::setAttribute(Qt::AA_DisableWindowContextHelpButton); - Renderer::QtSFML renderer(1100, 1100); + Renderer::QtSFMLWindow renderer(1100, 1100); try { SNES snes(argv[1], renderer); renderer.createWindow(snes, 60); diff --git a/ui/tileView.ui b/ui/tileView.ui new file mode 100644 index 0000000..eb91790 --- /dev/null +++ b/ui/tileView.ui @@ -0,0 +1,201 @@ + + + TileViewer + + + + 0 + 0 + 577 + 478 + + + + Tile Viewer + + + + :/resources/Logo.png:/resources/Logo.png + + + + + + + + + + + + + + Address + + + + + + + 0x + + + 0 + + + 999999 + + + 4096 + + + QAbstractSpinBox::DefaultStepType + + + 16 + + + + + + + SIze (bytes) + + + + + + + true + + + + + + 0x + + + 0 + + + 999999999 + + + 4096 + + + QAbstractSpinBox::DefaultStepType + + + 20480 + + + 16 + + + + + + + Format + + + + + + + 2 bpp + + + 0 + + + hjhu + + + true + + + + 2 bpp + + + + + 4 bpp + + + + + 8 bpp + + + + + + + + ArrowCursor + + + Columns + + + + + + + + + + false + + + + + + 1 + + + 16 + + + 10 + + + + + + + Palette Index + + + + + + + 255 + + + + + + + + + + + + + 0 + 0 + 577 + 24 + + + + + + + + +