Merge pull request #39 from AnonymusRaccoon/PPUDebugger

Ppu debugger
This commit is contained in:
Clément Le Bihan
2021-06-23 00:25:41 +02:00
committed by GitHub
26 changed files with 1285 additions and 354 deletions
+125 -113
View File
@@ -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)
-1
View File
@@ -7,7 +7,6 @@
#include <QtWidgets/QStyledItemDelegate>
#include "../../CPU/CPU.hpp"
#include "../../Renderer/SFRenderer.hpp"
#include "../../SNES.hpp"
#include "../../../ui/ui_cpuView.h"
#include "../ClosableWindow.hpp"
@@ -0,0 +1,112 @@
//
// Created by cbihan on 24/05/2021.
//
#include <complex>
#include <cmath>
#include "RAMTileRenderer.hpp"
#include "PPU/PPU.hpp"
#include "PPU/Tile.hpp"
#include <iostream>
namespace ComSquare::Debugger
{
RAMTileRenderer::RAMTileRenderer()
: _ram(nullptr),
_renderSize(0x5000),
_nbColumns(16),
_ramOffset(0),
_bpp(2),
buffer({{{0}}})
{
}
void RAMTileRenderer::setRam(std::shared_ptr<Ram::Ram> 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::Ram> 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;
}
}
@@ -0,0 +1,62 @@
//
// Created by cbihan on 24/05/2021.
//
#pragma once
#include <array>
#include "PPU/TileRenderer.hpp"
#include "Ram/Ram.hpp"
namespace ComSquare::Debugger
{
class RAMTileRenderer {
private:
//! @brief ram to render
std::shared_ptr<Ram::Ram> _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<std::array<uint32_t, 1024>, 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::Ram> 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::Ram> 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;
};
}
+149
View File
@@ -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 <QColor>
#include <string>
#include <iostream>
#include <QtWidgets/QTableWidget>
#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<TileViewer>(*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<Renderer::QtSFMLTileRenderer>(this->_ui.widget_sfml);
QMainWindow::connect(this->_ui.NbColumns, QOverload<int>::of(&QSpinBox::valueChanged), this, [this](int nb) -> void { this->setNbColumns(nb); });
QMainWindow::connect(this->_ui.ByteSize, QOverload<int>::of(&QSpinBox::valueChanged), this, [this](int nb) -> void { this->setRenderSize(nb); });
QMainWindow::connect(this->_ui.Address, QOverload<int>::of(&QSpinBox::valueChanged), this, [this](int nb) -> void { this->setRamOffset(nb); });
QMainWindow::connect(this->_ui.PaletteIndex, QOverload<int>::of(&QSpinBox::valueChanged), this, [this](int nb) -> void { this->setPaletteIndex(nb); });
QMainWindow::connect(this->_ui.BppFormat, QOverload<int>::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
}
}
@@ -0,0 +1,83 @@
//
// Created by cbihan on 5/7/21.
//
#pragma once
namespace ComSquare::PPU
{
class PPU;
}
#include <QtCore/QSortFilterProxyModel>
#include <QEvent>
#include <QMouseEvent>
#include <array>
#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<TileViewer> *_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<Renderer::QtSFMLTileRenderer> _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();
};
}
+38 -93
View File
@@ -6,6 +6,8 @@
#include "PPU.hpp"
#include "Background.hpp"
#include <cmath>
#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<int> pos)
void Background::_drawBgTile(uint16_t data, Vector2<int> pos)
{
uint16_t graphicAddress;
union TileMapData tileData;
std::vector<uint16_t> 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<uint16_t> 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<uint16_t> 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<int> offset)
void Background::_drawBasicTileMap(uint16_t baseAddress, Vector2<int> offset)
{
uint16_t tileMapValue = 0;
Vector2<int> 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<int> 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;
+10 -35
View File
@@ -2,19 +2,16 @@
// Created by cbihan on 5/14/20.
//
#ifndef COMSQUARE_BACKGROUND_HPP
#define COMSQUARE_BACKGROUND_HPP
#pragma once
#include <stdint-gcc.h>
#include <array>
#include <vector>
#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<std::array<uint32_t, 16>, 16> _tileBuffer;
//! @brief the access to vram
std::shared_ptr<Ram::Ram> _vram;
//! @brief The access to cgram
std::shared_ptr<Ram::Ram> _cgram;
//! @brief Draw a tile on the screen at x y pos
void drawBgTile(uint16_t data, Vector2<int> 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<uint16_t> 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<int> 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<int> offset);
void _drawBasicTileMap(uint16_t baseAddress, Vector2<int> offset);
public:
//! @brief The size of the background (x, y)
Vector2<unsigned> backgroundSize;
@@ -105,9 +85,7 @@ namespace ComSquare::PPU
//! @brief setter for private variable _tileMaps
//! @param tileMaps The tileMaps to set
void setTilemaps(Vector2<int> 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
}
+25 -17
View File
@@ -5,10 +5,11 @@
#include <iostream>
#include <bitset>
#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 <random>
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 <std::size_t DEST_SIZE, std::size_t SRC_SIZE>
void PPU::add_buffer(std::array<std::array<uint32_t, DEST_SIZE>, DEST_SIZE> &bufferDest, std::array<std::array<uint32_t, SRC_SIZE>, SRC_SIZE> &bufferSrc)
template <std::size_t DEST_SIZE_X, std::size_t DEST_SIZE_Y, std::size_t SRC_SIZE_X, std::size_t SRC_SIZE_Y>
void PPU::add_buffer(std::array<std::array<uint32_t, DEST_SIZE_Y>, DEST_SIZE_X> &bufferDest,
const std::array<std::array<uint32_t, SRC_SIZE_Y>, SRC_SIZE_X> &bufferSrc,
const Vector2<int> &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];
}
}
}
+26 -8
View File
@@ -6,17 +6,18 @@
#define COMSQUARE_PPU_HPP
#include <cstdint>
#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 <std::size_t DEST_SIZE, std::size_t SRC_SIZE>
void add_buffer(std::array<std::array<uint32_t, DEST_SIZE>, DEST_SIZE> &bufferDest, std::array<std::array<uint32_t, SRC_SIZE>, SRC_SIZE> &bufferSrc);
template <std::size_t DEST_SIZE_X, std::size_t DEST_SIZE_Y, std::size_t SRC_SIZE_X, std::size_t SRC_SIZE_Y>
void add_buffer(std::array<std::array<uint32_t, DEST_SIZE_Y>, DEST_SIZE_X> &bufferDest,
const std::array<std::array<uint32_t, SRC_SIZE_Y>, SRC_SIZE_X> &bufferSrc,
const Vector2<int> &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<int> getBgScroll(int bgNumber) const;
//! @brief Allow to look the value of each write register (used by Register debugger)
const Registers &getWriteRegisters() const;
template <std::size_t SRC_SIZE_Y, std::size_t SRC_SIZE_X>
void add_buffer(const std::array<std::array<uint32_t, SRC_SIZE_Y>, SRC_SIZE_X> &buffer,
const Vector2<int> &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
+4 -9
View File
@@ -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;
+36
View File
@@ -6,9 +6,15 @@
#define COMSQUARE_PPU_UTILS_HPP
#include <stdint-gcc.h>
#include <cstddef>
#include <array>
#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 <std::size_t DEST_SIZE_Y, std::size_t DEST_SIZE_X, std::size_t SRC_SIZE_Y, std::size_t SRC_SIZE_X>
void merge2DArray(std::array<std::array<uint32_t, DEST_SIZE_X>, DEST_SIZE_Y> &bufferDest,
const std::array<std::array<uint32_t, SRC_SIZE_X>, SRC_SIZE_Y> &bufferSrc,
const Vector2<int> &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 <std::size_t SRC_SIZE_Y, std::size_t SRC_SIZE_X>
void VFlipArray(std::array<std::array<uint32_t, SRC_SIZE_X>, SRC_SIZE_Y> &array,
const Vector2<int> &size,
const Vector2<int> &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 <std::size_t SRC_SIZE_Y, std::size_t SRC_SIZE_X>
void HFlipArray(std::array<std::array<uint32_t, SRC_SIZE_X>, SRC_SIZE_Y> &array,
const Vector2<int> &size,
const Vector2<int> &offset = {0, 0})
{
std::reverse(array.begin() + offset.x, array.begin() + offset.x + size.x);
}
}
#endif //COMSQUARE_PPU_UTILS_HPP
+21
View File
@@ -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;
};
}
+134
View File
@@ -0,0 +1,134 @@
//
// Created by cbihan on 24/05/2021.
//
#include <complex>
#include <cmath>
#include "TileRenderer.hpp"
#include "PPU/PPU.hpp"
#include "PPU/Tile.hpp"
#include <iostream>
namespace ComSquare::PPU
{
TileRenderer::TileRenderer()
: _ram(nullptr),
_cgram(nullptr),
_bpp(2),
_paletteIndex(0),
buffer({{{0}}})
{
}
void TileRenderer::setRam(std::shared_ptr<Ram::Ram> ram)
{
this->_ram = std::move(ram);
}
void TileRenderer::render(uint16_t tileAddress)
{
std::vector<uint16_t> 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<uint16_t> 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<uint16_t> 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::Ram> ram)
{
this->_cgram = std::move(ram);
}
int TileRenderer::getBpp() const
{
return this->_bpp;
}
int TileRenderer::getPaletteIndex() const
{
return this->_paletteIndex;
}
}
+63
View File
@@ -0,0 +1,63 @@
//
// Created by cbihan on 24/05/2021.
//
#pragma once
#include <array>
#include "Ram/Ram.hpp"
namespace ComSquare::PPU
{
class TileRenderer {
private:
//! @brief ram to render
std::shared_ptr<Ram::Ram> _ram;
//! @brief cgram to access the colors
std::shared_ptr<Ram::Ram> _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<std::array<uint32_t, 8>, 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::Ram> ram);
//! @brief Set the bpp to render graphics
void setBpp(int bpp);
//! @brief The ram to render
void setRam(std::shared_ptr<Ram::Ram> 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<uint16_t> 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;
};
}
+78 -57
View File
@@ -7,6 +7,7 @@
#include <QIcon>
#include <QMenuBar>
#include <iostream>
#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<QtFullSFML>(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<QtFullSFML>(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();
}
}
+26 -8
View File
@@ -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<QtFullSFML> _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
@@ -0,0 +1,28 @@
//
// Created by cbihan on 08/06/2021.
//
#include <iostream>
#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();
}
}
@@ -0,0 +1,31 @@
//
// Created by cbihan on 08/06/2021.
//
#pragma once
#include <array>
#include <QtWidgets/QWidget>
#include "QtWidgetSFML.hpp"
#include "Debugger/TileViewer/RAMTileRenderer.hpp"
namespace ComSquare::Renderer
{
class QtSFMLTileRenderer : public QtWidgetSFML
{
public:
std::array<std::array<uint32_t, 1024>, 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;
};
}
+1 -1
View File
@@ -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;
+2 -5
View File
@@ -8,6 +8,7 @@
#include <SFML/System.hpp>
#include <SFML/Window.hpp>
#include <SFML/Graphics/RenderWindow.hpp>
#include "SNES.hpp"
#include <iostream>
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;
}
-1
View File
@@ -6,7 +6,6 @@
#define COMSQUARE_SFRENDERER_HPP
#include "IRenderer.hpp"
#include "../SNES.hpp"
#include <SFML/Graphics.hpp>
#include <SFML/Audio.hpp>
#include <SFML/System.hpp>
+22 -4
View File
@@ -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<Debugger::MemoryBusDebug>(this->bus)->focus();
else
@@ -132,9 +133,9 @@ namespace ComSquare
this->bus = std::make_shared<Debugger::MemoryBusDebug>(*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<Debugger::RegisterViewer>(*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<Debugger::TileViewer>(*this, *this->ppu);
}
#endif
}
+7
View File
@@ -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<Debugger::CGramDebug> _cgramViewer;
//! @brief The window that allow the user to view registers.
std::unique_ptr<Debugger::RegisterViewer> _registerViewer;
//! @brief The window that allow the user to view the cgram as tiles.
std::unique_ptr<Debugger::TileViewer> _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);
+1 -2
View File
@@ -6,7 +6,6 @@
#include <QtWidgets/QApplication>
#include <getopt.h>
#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);
+201
View File
@@ -0,0 +1,201 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>TileViewer</class>
<widget class="QMainWindow" name="TileViewer">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>577</width>
<height>478</height>
</rect>
</property>
<property name="windowTitle">
<string>Tile Viewer</string>
</property>
<property name="windowIcon">
<iconset resource="../resources/appResources.qrc">
<normaloff>:/resources/Logo.png</normaloff>:/resources/Logo.png</iconset>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QWidget" name="widget_sfml" native="true"/>
</item>
<item>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Address</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QSpinBox" name="Address">
<property name="prefix">
<string notr="true">0x</string>
</property>
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>999999</number>
</property>
<property name="singleStep">
<number>4096</number>
</property>
<property name="stepType">
<enum>QAbstractSpinBox::DefaultStepType</enum>
</property>
<property name="displayIntegerBase">
<number>16</number>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>SIze (bytes)</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QSpinBox" name="ByteSize">
<property name="enabled">
<bool>true</bool>
</property>
<property name="suffix">
<string/>
</property>
<property name="prefix">
<string notr="true">0x</string>
</property>
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>999999999</number>
</property>
<property name="singleStep">
<number>4096</number>
</property>
<property name="stepType">
<enum>QAbstractSpinBox::DefaultStepType</enum>
</property>
<property name="value">
<number>20480</number>
</property>
<property name="displayIntegerBase">
<number>16</number>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Format</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QComboBox" name="BppFormat">
<property name="currentText">
<string>2 bpp</string>
</property>
<property name="currentIndex">
<number>0</number>
</property>
<property name="placeholderText">
<string>hjhu</string>
</property>
<property name="frame">
<bool>true</bool>
</property>
<item>
<property name="text">
<string>2 bpp</string>
</property>
</item>
<item>
<property name="text">
<string>4 bpp</string>
</property>
</item>
<item>
<property name="text">
<string>8 bpp</string>
</property>
</item>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_2">
<property name="cursor">
<cursorShape>ArrowCursor</cursorShape>
</property>
<property name="text">
<string>Columns</string>
</property>
<property name="buddy">
<cstring></cstring>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QSpinBox" name="NbColumns">
<property name="readOnly">
<bool>false</bool>
</property>
<property name="specialValueText">
<string/>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="value">
<number>16</number>
</property>
<property name="displayIntegerBase">
<number>10</number>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Palette Index</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QSpinBox" name="PaletteIndex">
<property name="maximum">
<number>255</number>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menuBar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>577</width>
<height>24</height>
</rect>
</property>
</widget>
</widget>
<resources>
<include location="../resources/appResources.qrc"/>
</resources>
<connections/>
</ui>