diff --git a/CMakeLists.txt b/CMakeLists.txt index 90dd475..c69d842 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -95,7 +95,13 @@ set(SOURCES sources/PPU/TileRenderer.hpp sources/PPU/Tile.hpp sources/CPU/Registers.hpp - sources/Memory/IMemoryBus.hpp sources/Models/Callback.hpp sources/Models/Logger.hpp) + sources/Memory/IMemoryBus.hpp + sources/Models/Callback.hpp + sources/Models/Logger.hpp + sources/PPU/PpuDebug.cpp + sources/PPU/PpuDebug.hpp + sources/PPU/PPURegisters.hpp + ) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTORCC ON) @@ -115,8 +121,6 @@ add_executable(comsquare sources/Renderer/QtRenderer/QtSfmlTileRenderer.hpp sources/Renderer/QtRenderer/QtSfmlTileRenderer.cpp sources/Renderer/QtRenderer/QtSfmlTileRenderer.hpp - sources/PPU/PpuDebug.cpp - sources/PPU/PpuDebug.hpp sources/Debugger/ClosableWindow.hpp sources/Debugger/CPU/CPUDebug.cpp sources/Debugger/CPU/CPUDebug.hpp @@ -182,6 +186,7 @@ add_executable(unit_tests EXCLUDE_FROM_ALL tests/CPU/testDMA.cpp tests/CPU/testAddressingMode.cpp tests/testMemoryBus.cpp + tests/PPU/testTileRenderer.cpp ) target_include_directories(unit_tests PUBLIC tests) target_compile_definitions(unit_tests PUBLIC TESTS) diff --git a/sources/Debugger/CGramDebug.cpp b/sources/Debugger/CGramDebug.cpp index 387f4f9..f77b2db 100644 --- a/sources/Debugger/CGramDebug.cpp +++ b/sources/Debugger/CGramDebug.cpp @@ -8,6 +8,7 @@ #include #include #include "Utility/Utility.hpp" +#include "PPU/PPUUtils.hpp" namespace ComSquare::Debugger { @@ -45,16 +46,14 @@ namespace ComSquare::Debugger uint8_t blue = (cgramValue & 0x7D00U) >> 10U; uint8_t green = (cgramValue & 0x03E0U) >> 5U; uint8_t red = (cgramValue & 0x001FU); - uint24_t hexColorValue = 0; + uint32_t hexColorValue = PPU::Utils::CGRAMColorToRGBA(cgramValue); this->_ui.indexLineEdit->setText(std::to_string(addr / 2).c_str()); this->_ui.valueLineEdit->setText(Utility::to_hex(cgramValue).c_str()); this->_ui.rLineEdit->setText(std::to_string(red).c_str()); this->_ui.gLineEdit->setText(std::to_string(green).c_str()); this->_ui.bLineEdit->setText(std::to_string(blue).c_str()); - hexColorValue += (red * 255U / 31U) << 16U; - hexColorValue += (green * 255U / 31U) << 8U; - hexColorValue += (blue * 255U / 31U); + hexColorValue >>= 8; this->_ui.hexLineEdit->setText(Utility::to_hex(hexColorValue).c_str()); } @@ -81,27 +80,20 @@ namespace ComSquare::Debugger QVariant CGramModel::data(const QModelIndex &index, int role) const { - u_int16_t addressValue; - uint8_t red; - uint8_t green; - uint8_t blue; if (role == Qt::TextAlignmentRole) return Qt::AlignCenter; if (role != Qt::BackgroundRole) return QVariant(); + int idDisplayTile = index.row() * 16 + index.column(); uint16_t cgramAddress = idDisplayTile / 8 * 16 + (idDisplayTile % 8 * 2); - addressValue = this->_ppu.cgramRead(cgramAddress); + uint16_t addressValue = this->_ppu.cgramRead(cgramAddress); addressValue += this->_ppu.cgramRead(cgramAddress + 1) << 8U; + uint32_t color = PPU::Utils::CGRAMColorToRGBA(addressValue); - blue = (addressValue & 0x7D00U) >> 10U; - green = (addressValue & 0x03E0U) >> 5U; - red = (addressValue & 0x001FU); - - red = red * 255U / 31U; - green = green * 255U / 31U; - blue = blue * 255U / 31U; - return QColor(red, green, blue); + return QColor(static_cast((color & 0xFF000000) >> 24), + static_cast((color & 0x00FF0000) >> 16), + static_cast((color & 0x0000FF00) >> 8)); } } \ No newline at end of file diff --git a/sources/PPU/Background.cpp b/sources/PPU/Background.cpp index e2d99e6..d7cb004 100644 --- a/sources/PPU/Background.cpp +++ b/sources/PPU/Background.cpp @@ -10,58 +10,52 @@ namespace ComSquare::PPU { - Background::Background(ComSquare::PPU::PPU &ppu, int backGroundNumber, bool hasPriority) + Background::Background(ComSquare::PPU::PPU &ppu, int backgroundNumber) : _ppu(ppu), - _tileMapsConfig(ppu.getBackgroundMirroring(backGroundNumber)), - _characterNbPixels(ppu.getCharacterSize(backGroundNumber)), - _bpp(ppu.getBPP(backGroundNumber)), + _tileMapMirroring(ppu.getBackgroundMirroring(backgroundNumber)), + _characterNbPixels(ppu.getCharacterSize(backgroundNumber)), + _bpp(ppu.getBPP(backgroundNumber)), _directColor(false), _highRes(false), - _tileMapStartAddress(ppu.getTileMapStartAddress(backGroundNumber)), - _tilesetAddress(ppu.getTilesetAddress(backGroundNumber)), - _priority(hasPriority), - _bgNumber(backGroundNumber), + _tileMapStartAddress(ppu.getTileMapStartAddress(backgroundNumber)), + _tilesetAddress(ppu.getTilesetAddress(backgroundNumber)), + _bgNumber(backgroundNumber), _tileBuffer({{{0}}}), _vram(ppu.vram), _cgram(ppu.cgram), _tileRenderer(this->_vram, this->_cgram), - buffer({{{0}}}) + buffer({{{0}}}), + tilesPriority({{{false}}}) {} void Background::renderBackground() { uint16_t vramAddress = this->_tileMapStartAddress; - Vector2 offset = this->_ppu.getBgScroll(this->_bgNumber); + //Vector2 offset = this->_ppu.getBgScroll(this->_bgNumber); + Vector2i offset = {0, 0}; this->backgroundSize.x = - static_cast(this->_tileMapsConfig.x) * this->_characterNbPixels.x * NbCharacterWidth; + (static_cast(this->_tileMapMirroring.x) + 1) * this->_characterNbPixels.x * NbCharacterWidth; this->backgroundSize.y = - static_cast(this->_tileMapsConfig.y) * this->_characterNbPixels.y * NbCharacterHeight; + (static_cast(this->_tileMapMirroring.y) + 1) * this->_characterNbPixels.y * NbCharacterHeight; this->_drawBasicTileMap(vramAddress, offset); for (int i = 1; i < 4; i++) { vramAddress += TileMapByteSize; - offset.x += NbCharacterWidth * this->_characterNbPixels.x; + offset.x++; if (i == 2) { offset.x = 0; - offset.y += NbCharacterHeight * this->_characterNbPixels.y; + offset.y++; } - if (i > 1 && !this->_tileMapsConfig.y) + if (i > 1 && !this->_tileMapMirroring.y) break; - if ((i == 1 || i == 3) && !this->_tileMapsConfig.x) + if ((i == 1 || i == 3) && !this->_tileMapMirroring.x) continue; this->_drawBasicTileMap(vramAddress, offset); } } - void Background::_drawBgTile(uint16_t data, Vector2 pos) + void Background::_drawTileFromMemoryToTileBuffer(const Utils::TileData &tileData) { - union Utils::TileMapData tileData; - - tileData.raw = data; - - if (tileData.tilePriority != this->_priority) - return; - uint16_t graphicAddress; Vector2i tileOffset = {0, 0}; // X horizontal @@ -75,25 +69,34 @@ namespace ComSquare::PPU ((tileData.posX + tileOffset.x) * this->_bpp * Tile::BaseByteSize); this->_tileRenderer.render(graphicAddress); Utils::merge2DArray(this->_tileBuffer, this->_tileRenderer.buffer, {j, i}); - tileOffset.x += 1; + tileOffset.x++; } tileOffset.x = 0; - tileOffset.y += 1; + tileOffset.y++; } + } + + void Background::_drawTile(uint16_t data, Vector2 indexOffset) + { + union Utils::TileData tileData; + + tileData.raw = data; + + this->tilesPriority[indexOffset.y][indexOffset.x] = tileData.tilePriority; + this->_drawTileFromMemoryToTileBuffer(tileData); // todo check why i need to invert vertical and horizontal flips if (tileData.verticalFlip) Utils::HFlipArray(this->_tileBuffer, {this->_characterNbPixels.x, this->_characterNbPixels.y}); if (tileData.horizontalFlip) Utils::VFlipArray(this->_tileBuffer, {this->_characterNbPixels.x, this->_characterNbPixels.y}); - for (int i = 0; i < this->_characterNbPixels.y; i++) { - for (int j = 0; j < this->_characterNbPixels.x; j++) { - this->buffer[pos.x][pos.y] = this->_tileBuffer[i][j]; - pos.x++; - } - pos.x -= this->_characterNbPixels.x; - pos.y++; - } + + Vector2 pixelPosition{indexOffset.x * this->_characterNbPixels.x, indexOffset.y * this->_characterNbPixels.y}; + std::for_each(this->_tileBuffer.begin(), this->_tileBuffer.begin() + this->_characterNbPixels.y, + [this, &pixelPosition](const auto &row) { + std::move(row.begin(), row.begin() + this->_characterNbPixels.x, + this->buffer[pixelPosition.y++].begin() + pixelPosition.x); + }); } void Background::_drawBasicTileMap(uint16_t baseAddress, Vector2 offset) @@ -105,17 +108,16 @@ namespace ComSquare::PPU // TODO function to read 2 bytes (LSB order or bits reversed) uint16_t tileMapValue = this->_vram.read(vramAddress); tileMapValue += this->_vram.read(vramAddress + 1) << 8U; - this->_drawBgTile(tileMapValue, { - (pos.x * this->_characterNbPixels.x) + offset.x, - (pos.y * this->_characterNbPixels.y) + offset.y - }); + this->_drawTile(tileMapValue, {(offset.x * NbCharacterWidth) + pos.x, + (offset.y * NbCharacterHeight) + pos.y}); vramAddress += 2; if (pos.x % 31 == 0 && pos.x) { pos.y++; pos.x = 0; } - else + else { pos.x++; + } } } @@ -143,9 +145,9 @@ namespace ComSquare::PPU this->_tileRenderer.setBpp(this->_bpp); } - void Background::setTilemaps(Vector2 tileMaps) + void Background::setTileMapMirroring(Vector2 tileMaps) { - this->_tileMapsConfig = tileMaps; + this->_tileMapMirroring = tileMaps; } int Background::getBgNumber() const @@ -153,13 +155,8 @@ namespace ComSquare::PPU return this->_bgNumber; } - void Background::setPriority(bool priority) + bool Background::isPriorityPixel(int y, int x) const { - this->_priority = priority; - } - - bool Background::getPriority() const - { - return this->_priority; + return this->tilesPriority[y / this->_characterNbPixels.y][x / this->_characterNbPixels.x]; } } \ No newline at end of file diff --git a/sources/PPU/Background.hpp b/sources/PPU/Background.hpp index 4d1e62c..358c271 100644 --- a/sources/PPU/Background.hpp +++ b/sources/PPU/Background.hpp @@ -6,10 +6,12 @@ #include #include +#include #include "Models/Vector2.hpp" -#include "TileRenderer.hpp" +#include "PPU/TileRenderer.hpp" #include "Ram/Ram.hpp" -#include "PPU.hpp" +#include "PPU/PPU.hpp" +#include "PPU/PPUUtils.hpp" namespace ComSquare::PPU { @@ -32,8 +34,8 @@ namespace ComSquare::PPU PPU &_ppu; //! @brief The tilemap configuration nb of tileMap vertically and horizontally //! @note members are set to true if the tilemap is expended in their direction - Vector2 _tileMapsConfig; - //! @brief The number of pixels of a character (x: width, y:height) + Vector2 _tileMapMirroring; + //! @brief The number of pixels of a character (x: width, y: height) Vector2 _characterNbPixels; //! @brief The number of bits per pixels to currently look for each pixel int _bpp; @@ -46,8 +48,6 @@ namespace ComSquare::PPU uint16_t _tileMapStartAddress; //! @brief The first address for tileset data uint16_t _tilesetAddress; - //! @brief If pixel from this background should be treated as primarily - bool _priority; //! @brief The bg number (used to get the corresponding scroll) int _bgNumber; //! @brief Buffer if we have tiles that are more than 8x8 @@ -59,17 +59,26 @@ namespace ComSquare::PPU //! @brief Class that actually render a tile TileRenderer _tileRenderer; //! @brief Draw a tile on the screen at x y pos - void _drawBgTile(uint16_t data, Vector2 pos); + //! @param data The VRAM value to be interpreted as a Utils::TileData + //! @param indexOffset The index offset of the Tile (ranging from 0 to 63) + void _drawTile(uint16_t data, Vector2 indexOffset); + //! @brief Draw the tile to the tile Buffer + //! @param tileData The tile data to use to render the tile + void _drawTileFromMemoryToTileBuffer(const union Utils::TileData &tileData); //! @brief draw a tileMap 32x32 starting at baseAddress //! @param baseAddress The starting address of the tileMap - //! @param offset The rendering offeset in pixels + //! @param offset The offset of the tile map (ranging from 0 to 1) void _drawBasicTileMap(uint16_t baseAddress, Vector2 offset); public: //! @brief The size of the background (x, y) Vector2 backgroundSize; //! @brief The output buffer (pixels are written on it) std::array, 1024> buffer; + //! @brief The buffer of tile priority level + std::array, 64> tilesPriority; + //! @brief Tells if a pixel has high priority + [[nodiscard]] bool isPriorityPixel(int x, int y) const; //! @brief Render a background on his internal buffer void renderBackground(); //! @brief Set the tileMap start address @@ -85,20 +94,54 @@ namespace ComSquare::PPU void setBpp(int bpp); //! @brief setter for private variable _tileMaps //! @param tileMaps The tileMaps to set - void setTilemaps(Vector2 tileMaps); + void setTileMapMirroring(Vector2 tileMaps); //! @brief Get the BackGround Number //! @return the current Background number - int getBgNumber() const; - //! @brief set the Background priority - //! @param bgNumber the new Background priority - void setPriority(bool priority); - //! @brief Get the Background priority - //! @return the current Background priority - bool getPriority() const; + [[nodiscard]] int getBgNumber() const; + + + //! @brief Add a bg buffer to another buffer + //! @tparam levelLow The priority of a low priority pixel (working like z-index CSS property) + //! @tparam levelHigh The priority of a high priority pixel (working like z-index CSS property) + //! @tparam DEST_SIZE_X The Horizontal array size + //! @tparam DEST_SIZE_Y The Vertical array size + //! @param bufferDest The destination buffer (buffer that will be written on) + //! @param pixelDestinationLevelMap The destination buffer level map to use as reference and will be updated if a pixel has an higher level than the actual one + //! @param backgroundSrc The Background to use as a source + template + static void mergeBackgroundBuffer(std::array, DEST_SIZE_X> &bufferDest, + std::array, DEST_SIZE_X> &pixelDestinationLevelMap, + const Background &backgroundSrc + ) + { + int i = 0; + int j = 0; + int pixelLevel; + + for (const auto &sourceRow : backgroundSrc.buffer) { + for (const auto &pixel : sourceRow) { + if (pixel <= 0xFF) { + j++; + continue; + } + pixelLevel = backgroundSrc.isPriorityPixel(i, j) ? levelHigh : levelLow; + auto &pixelInitialLevel = pixelDestinationLevelMap[i][j]; + + if (pixelLevel >= pixelInitialLevel) { + bufferDest[i][j] = pixel; + pixelInitialLevel = pixelLevel; + } + j++; + }; + j = 0; + i++; + }; + + } //! @brief ctor - Background(PPU &_ppu, int backGroundNumber, bool hasPriority); + Background(PPU &_ppu, int backgroundNumber); //! @brief Default copy ctor Background(const Background &) = default; //! @brief Default destructor diff --git a/sources/PPU/PPU.cpp b/sources/PPU/PPU.cpp index 893ed01..95076ec 100644 --- a/sources/PPU/PPU.cpp +++ b/sources/PPU/PPU.cpp @@ -6,6 +6,7 @@ #include #include "PPU.hpp" #include "Exceptions/InvalidAddress.hpp" +#include "PPU/Background.hpp" #include "Models/Vector2.hpp" namespace ComSquare::PPU::Utils::Debug { @@ -20,21 +21,17 @@ namespace ComSquare::PPU cgram(CGRamSize, ComSquare::CGRam, "CGRAM"), _renderer(renderer), _backgrounds{ - Background(*this, 1, false), - Background(*this, 1, true), - Background(*this, 2, false), - Background(*this, 2, true), - Background(*this, 3, false), - Background(*this, 3, true), - Background(*this, 4, false), - Background(*this, 4, true) + Background(*this, 1), + Background(*this, 2), + Background(*this, 3), + Background(*this, 4), }, _mainScreen({{{0}}}), _subScreen({{{0}}}) { this->_registers._isLowByte = true; - //Utils::Debug::populateEnvironment(*this, 0); + //Utils::Debug::populateEnvironment(*this, 1); } uint8_t PPU::read(uint24_t addr) @@ -109,9 +106,9 @@ namespace ComSquare::PPU case PpuRegisters::bgmode: this->_registers._bgmode.raw = data; // update backgrounds - for (int i = 0; i < 8; i++) { - this->_backgrounds[i].setBpp(this->getBPP((i / 2) + 1)); - this->_backgrounds[i].setCharacterSize(this->getCharacterSize((i / 2) + 1)); + for (int i = 0; i < 4; i++) { + this->_backgrounds[i].setBpp(this->getBPP(i + 1)); + this->_backgrounds[i].setCharacterSize(this->getCharacterSize(i + 1)); } break; case PpuRegisters::mosaic: @@ -125,12 +122,7 @@ namespace ComSquare::PPU // update background tilemap address this->_backgrounds[addr - PpuRegisters::bg1sc].setTileMapStartAddress( this->getTileMapStartAddress(addr - PpuRegisters::bg1sc + 1)); - this->_backgrounds[addr - PpuRegisters::bg1sc + 1].setTileMapStartAddress( - this->getTileMapStartAddress(addr - PpuRegisters::bg1sc + 1)); - this->_backgrounds[addr - PpuRegisters::bg1sc].setTilemaps( - {static_cast(this->_registers._bgsc[addr - PpuRegisters::bg1sc].tilemapHorizontalMirroring), - static_cast(this->_registers._bgsc[addr - PpuRegisters::bg1sc].tilemapVerticalMirroring)}); - this->_backgrounds[addr - PpuRegisters::bg1sc + 1].setTilemaps( + this->_backgrounds[addr - PpuRegisters::bg1sc].setTileMapMirroring( {static_cast(this->_registers._bgsc[addr - PpuRegisters::bg1sc].tilemapHorizontalMirroring), static_cast(this->_registers._bgsc[addr - PpuRegisters::bg1sc].tilemapVerticalMirroring)}); break; @@ -144,8 +136,9 @@ namespace ComSquare::PPU FALLTHROUGH case PpuRegisters::bg2hofs: case PpuRegisters::bg3hofs: - case PpuRegisters::bg4hofs: - this->_registers._bgofs[addr - PpuRegisters::bg1hofs].raw = ((data << 8) | (this->_ppuState.hvSharedScrollPrevValue & ~7) | (this->_ppuState.hScrollPrevValue & 7)) & 0x3FF; + case PpuRegisters::bg4hofs: this->_registers._bgofs[addr - PpuRegisters::bg1hofs].raw = + ((data << 8) | (this->_ppuState.hvSharedScrollPrevValue & ~7) | + (this->_ppuState.hScrollPrevValue & 7)) & 0x3FF; this->_ppuState.hScrollPrevValue = data; this->_ppuState.hvSharedScrollPrevValue = data; break; @@ -156,7 +149,7 @@ namespace ComSquare::PPU case PpuRegisters::bg2vofs: case PpuRegisters::bg3vofs: case PpuRegisters::bg4vofs: - this->_registers._bgofs[addr - PpuRegisters::bg1hofs].raw = ((data << 8) | this->_ppuState.hvSharedScrollPrevValue) & 0x3FF; + this->_registers._bgofs[addr - PpuRegisters::bg1vofs].raw = ((data << 8) | this->_ppuState.hvSharedScrollPrevValue) & 0x3FF; this->_ppuState.hvSharedScrollPrevValue = data; break; case PpuRegisters::vmain: @@ -308,20 +301,36 @@ namespace ComSquare::PPU (void)cycles; this->renderMainAndSubScreen(); - this->add_buffer(this->_screen, this->_subScreen); - this->add_buffer(this->_screen, this->_mainScreen); - //this->_backgrounds[2].renderBackground(); - //add_buffer(this->_screen, this->_backgrounds[2].buffer); + Utils::addBuffer(this->_screen, this->_subScreen); + Utils::addBuffer(this->_screen, this->_mainScreen); + + int i = 0; + int j = 0; + + for (const auto &row : this->_screen) { + for (const auto &pixel : row) { + this->_renderer.putPixel(i, j++, pixel); + }; + j = 0; + i++; + }; + + + /* + // loop used for debug for (unsigned long i = 0; i < this->_screen.size(); i++) { for (unsigned long j = 0; j < this->_screen[i].size(); j++) { - this->_renderer.putPixel(j, i, this->_screen[i][j]); + this->_renderer.putPixel(i, j, this->_screen[i][j]); } + //if (i > 500) + // break; } + */ this->_renderer.drawScreen(); - for (auto &i : this->_mainScreen) - i.fill(0XFF); - for (auto &i : this->_subScreen) - i.fill(0XFF); + for (auto &row : this->_mainScreen) + row.fill(0XFF); + for (auto &row : this->_subScreen) + row.fill(0XFF); } std::string PPU::getName() const @@ -535,62 +544,66 @@ namespace ComSquare::PPU Vector2 PPU::getBackgroundMirroring(int bgNumber) const { - Vector2 backgroundSize(false, false); - - backgroundSize.y = this->_registers._bgsc[bgNumber - 1].tilemapVerticalMirroring; - backgroundSize.x = this->_registers._bgsc[bgNumber - 1].tilemapHorizontalMirroring; - return backgroundSize; + return { + static_cast(this->_registers._bgsc[bgNumber - 1].tilemapHorizontalMirroring), + static_cast(this->_registers._bgsc[bgNumber - 1].tilemapVerticalMirroring) + }; } void PPU::renderMainAndSubScreen() { uint16_t colorPalette; // should only render backgrounds needed (depending of th bgMode) - int i = 0; for (auto &_background : this->_backgrounds) { - i++; _background.renderBackground(); } // TODO make a function getDefaultBgColor colorPalette = this->cgram.read(0); colorPalette += this->cgram.read(1) << 8U; - uint32_t color = Utils::getRealColor(colorPalette); + uint32_t color = Utils::CGRAMColorToRGBA(colorPalette); for (auto &row : this->_subScreen) row.fill(color); + for (auto &row : this->_mainScreenLevelMap) + row.fill(0); + for (auto &row : this->_subScreenLevelMap) + row.fill(0); // the buffer is overwrite if necessary by a new bg so the background priority is from back to front // the starting palette index isn't implemented switch (this->_registers._bgmode.bgMode) { case 0: - this->addToMainSubScreen(this->_backgrounds[BgName::bg4NoPriority]); - this->addToMainSubScreen(this->_backgrounds[BgName::bg3NoPriority]); + this->addToMainSubScreen<0, 15>(this->_backgrounds[BgName::Background4]); + this->addToMainSubScreen<10, 16>(this->_backgrounds[BgName::Background3]); //sprites priority 0 - this->addToMainSubScreen(this->_backgrounds[BgName::bg4Priority]); - this->addToMainSubScreen(this->_backgrounds[BgName::bg3Priority]); + // this->addToMainSubScreen(this->_backgrounds[BgName::bg4Priority]); + // this->addToMainSubScreen(this->_backgrounds[BgName::bg3Priority]); //sprites priority 1 - this->addToMainSubScreen(this->_backgrounds[BgName::bg2NoPriority]); - this->addToMainSubScreen(this->_backgrounds[BgName::bg1NoPriority]); + this->addToMainSubScreen<20, 35>(this->_backgrounds[BgName::Background2]); + this->addToMainSubScreen<30, 36>(this->_backgrounds[BgName::Background1]); //sprites priority 2 - this->addToMainSubScreen(this->_backgrounds[BgName::bg2Priority]); - this->addToMainSubScreen(this->_backgrounds[BgName::bg1Priority]); + // this->addToMainSubScreen(this->_backgrounds[BgName::bg2Priority]); + // this->addToMainSubScreen(this->_backgrounds[BgName::bg1Priority]); //sprites priority 3 break; case 1: - this->addToMainSubScreen(this->_backgrounds[BgName::bg3NoPriority]); - //sprites priority 0 if (!this->_registers._bgmode.mode1Bg3PriorityBit) - this->addToMainSubScreen(this->_backgrounds[BgName::bg3Priority]); + this->addToMainSubScreen<0, 5>(this->_backgrounds[BgName::Background3]); + else + this->addToMainSubScreen<0, 30>(this->_backgrounds[BgName::Background3]); + //sprites priority 0 + // if (!this->_registers._bgmode.mode1Bg3PriorityBit) + // this->addToMainSubScreen(this->_backgrounds[BgName::bg3Priority]); //sprites priority 1 - this->addToMainSubScreen(this->_backgrounds[BgName::bg2NoPriority]); - this->addToMainSubScreen(this->_backgrounds[BgName::bg1NoPriority]); + this->addToMainSubScreen<10, 25>(this->_backgrounds[BgName::Background2]); + this->addToMainSubScreen<20, 26>(this->_backgrounds[BgName::Background1]); //sprites priority 2 - this->addToMainSubScreen(this->_backgrounds[BgName::bg2Priority]); - this->addToMainSubScreen(this->_backgrounds[BgName::bg1Priority]); + // this->addToMainSubScreen(this->_backgrounds[BgName::bg2Priority]); + // this->addToMainSubScreen(this->_backgrounds[BgName::bg1Priority]); //sprites priority 3 - if (this->_registers._bgmode.mode1Bg3PriorityBit) - this->addToMainSubScreen(this->_backgrounds[BgName::bg3Priority]); + // if (this->_registers._bgmode.mode1Bg3PriorityBit) + // this->addToMainSubScreen(this->_backgrounds[BgName::bg3Priority]); break; - case 2: + /* case 2: this->addToMainSubScreen(this->_backgrounds[BgName::bg2NoPriority]); //sprites priority 0 this->addToMainSubScreen(this->_backgrounds[BgName::bg1NoPriority]); @@ -637,23 +650,15 @@ namespace ComSquare::PPU //sprites priority 2 this->addToMainSubScreen(this->_backgrounds[BgName::bg1Priority]); //sprites priority - break; + break;*/ case 7: // Not implemented throw std::runtime_error("not implemented"); default: - break; + throw std::runtime_error("Bg mode not implemented or commented (bg nb " + std::to_string(this->_registers._bgmode.bgMode) + ")"); } } - void PPU::addToMainSubScreen(Background &bg) - { - if (this->_registers._t[0].raw & (1U << (bg.getBgNumber() - 1U))) - this->add_buffer(this->_mainScreen, bg.buffer); - if (this->_registers._t[1].raw & (1U << (bg.getBgNumber() - 1U))) - this->add_buffer(this->_subScreen, bg.buffer); - } - int PPU::getBgMode() const { return this->_registers._bgmode.bgMode; diff --git a/sources/PPU/PPU.hpp b/sources/PPU/PPU.hpp index b5a2b90..55567e6 100644 --- a/sources/PPU/PPU.hpp +++ b/sources/PPU/PPU.hpp @@ -10,8 +10,10 @@ #include "Renderer/IRenderer.hpp" #include "Ram/Ram.hpp" #include "Models/Vector2.hpp" +#include #include "Background.hpp" -#include "PPUUtils.hpp" +#include "PPU/PPUUtils.hpp" +#include "PPU/PPURegisters.hpp" #ifdef DEBUGGER_ENABLED #include "Debugger/TileViewer/RAMTileRenderer.hpp" @@ -34,524 +36,10 @@ namespace ComSquare::PPU class Background; //! @brief Enum to access more easily the ppu background array enum BgName { - bg1NoPriority = 0, - bg1Priority, - bg2NoPriority, - bg2Priority, - bg3NoPriority, - bg3Priority, - bg4NoPriority, - bg4Priority - }; - - enum PpuRegisters { - //! @brief INIDISP Register (F-blank and Brightness) - inidisp = 0x00, - //! @brief OBSEL Register (Object Size and Character Address) - obsel = 0x01, - //! @brief OAMADDL (OAM Address low byte) - oamaddl = 0x02, - //! @brief OAMADDH (OAM Address high bit and Obj Priority) - oamaddh = 0x03, - //! @brief OAMDATA (Data for OAM write) - oamdata = 0x04, - //! @brief BGMODE (BG Mode and Character Size) - bgmode = 0x05, - //! @brief MOSAIC (Screen Pixelation) - mosaic = 0x06, - //! @brief BG1SC (BG1 Tilemap Address and Size) - bg1sc = 0x07, - //! @brief BG2SC (BG2 Tilemap Address and Size) - bg2sc = 0x08, - //! @brief BG3SC (BG3 Tilemap Address and Size) - bg3sc = 0x09, - //! @brief BG4SC (BG4 Tilemap Address and Size) - bg4sc = 0x0A, - //! @brief BG12NBA (BG1 and 2 Chr Address) - bg12nba = 0x0B, - //! @brief BG34NBA (BG3 and 4 Chr Address) - bg34nba = 0x0C, - //! @brief BG1HOFS (BG1 Horizontal Scroll) - //! @brief M7HOFS (Mode 7 BG Horizontal Scroll) - //! @info When bg mode is 7 the register is used as M7HOFS - bg1hofs = 0x0D, - //! @brief BG1VOFS (BG1 Vertical Scroll) - //! @brief M7VOFS (Mode 7 BG Vertical Scroll) - //! @info When bg mode is 7 the register is used as M7VOFS - bg1vofs = 0x0E, - //! @brief BG2HOFS (BG2 Horizontal Scroll) - bg2hofs = 0x0F, - //! @brief BG2VOFS (BG2 Vertical Scroll) - bg2vofs = 0x10, - //! @brief BG3HOFS (BG3 Horizontal Scroll) - bg3hofs = 0x11, - //! @brief BG3VOFS (BG3 Vertical Scroll) - bg3vofs = 0x12, - //! @brief BG4HOFS (BG4 Horizontal Scroll) - bg4hofs = 0x13, - //! @brief BG4VOFS (BG4 Vertical Scroll) - bg4vofs = 0x14, - //! @brief VMAIN (Video Port Control) - vmain = 0x15, - //! @brief VMADDL (VRAM Address low byte) - vmaddl = 0x16, - //! @brief VMADDH (VRAM Address high byte) - vmaddh = 0x17, - //! @brief VMDATAL (VRAM Data Write low byte) - vmdatal = 0x18, - //! @brief VMDATAH (VRAM Data Write high byte) - vmdatah = 0x19, - //! @brief M7SEL (Mode 7 Settings) - m7sel = 0x1A, - //! @brief M7A (Mode 7 Matrix A) also used with $2134/6 - m7a = 0x1B, - //! @brief M7B (Mode 7 Matrix B) also used with $2134/6 - m7b = 0x1C, - //! @brief M7C (Mode 7 Matrix C) - m7c = 0x1D, - //! @brief M7D (Mode 7 Matrix D) - m7d = 0x1E, - //! @brief M7X (Mode 7 Center X) - m7x = 0x1F, - //! @brief M7Y (Mode 7 Center Y) - m7y = 0x20, - //! @brief CGADD (CGRAM Address) - cgadd = 0x21, - //! @brief CGDATA (CGRAM Data write) - cgdata = 0x22, - //! @brief W12SEL (Window Mask Settings for BG1 and BG2) - w12sel = 0x23, - //! @brief W34SEL (Window Mask Settings for BG3 and BG4) - w34sel = 0x24, - //! @brief WOBJSEL (Window Mask Settings for OBJ and Color Window) - wobjsel = 0x25, - //! @brief WH0 (Window 1 Left Position) - wh0 = 0x26, - //! @brief WH1 (Window 1 Right Position) - wh1 = 0x27, - //! @brief WH2 (Window 2 Left Position) - wh2 = 0x28, - //! @brief WH3 (Window 2 Right Position) - wh3 = 0x29, - //! @brief WBGLOG (Window mask logic for BGs) - wbjlog = 0x2A, - //! @brief WOBJLOG (Window mask logic for OBJs and Color Window) - wobjlog = 0x2B, - //! @brief TM (Main Screen Designation) - tm = 0x2C, - //! @brief TS (Subscreen Designation) - ts = 0x2D, - //! @brief TMW (Window Mask Designation for the Main Screen) - tmw = 0x2E, - //! @brief TSW (Window Mask Designation for the Subscreen) - tsw = 0x2F, - //! @brief CGWSEL (Color Addition Select) - cgwsel = 0x30, - //! @brief CGADSUB (Color math designation) - cgadsub = 0x31, - //! @brief COLDATA (Fixed Color Data) - coldata = 0x32, - //! @brief SETINI (Screen Mode/Video Select) - setini = 0x33, - //! @brief MPYL (Multiplication Result low byte) - mpyl = 0x34, - //! @brief MPYM (Multiplication Result middle byte) - mpym = 0x35, - //! @brief MPYH (Multiplication Result high byte) - mpyh = 0x36, - //! @brief SLHV (Software Latch for H/V Counter) - slhv = 0x37, - //! @brief OAMDATAREAD (Data for OAM read) - oamdataread = 0x38, - //! @brief VMDATALREAD (VRAM Data Read low byte) - vmdatalread = 0x39, - //! @brief VMDATAHREAD (VRAM Data Read high byte) - vmdatahread = 0x3A, - //! @brief CGDATAREAD (CGRAM Data read) - cgdataread = 0x3B, - //! @brief OPHCT (Horizontal Scanline Location) - ophct = 0x3C, - //! @brief OPVCT (Vertical Scanline Location) - opvct = 0x3D, - //! @brief STAT77 (PPU Status Flag and Version) - stat77 = 0x3E, - //! @brief STAT78 (PPU Status Flag and Version) - stat78 = 0x3F - }; - - struct Registers { - //! @brief INIDISP Register (F-blank and Brightness) - union { - struct { - //! @brief Store the brightness value (F = max, 0 = off) - uint8_t brightness: 4; - uint8_t _: 3; - //! @brief Store the FBlank status - uint8_t fblank: 1; - }; - uint8_t raw = 0; - } _inidisp; - //! @brief OBSEL Register (Object Size and Character Address) - union { - struct { - //! @brief Stores the location of the first sprite table - uint8_t nameBaseSelect: 3; - //! @brief Stores the offset of the second sprite table - uint8_t nameSelect: 2; - //! @brief Stores the resolution preset of the sprites - uint8_t objectSize: 3; - }; - uint8_t raw = 0; - } _obsel; - //! @brief OAMADD Register (OAM Address and Obj Priority) - union { - struct { - //! @brief Stores the address to write with OAMDATA register - uint16_t oamAddress: 9; - uint16_t _: 6; - //! @brief When Obj Priority activation bit is set, an Obj other than Sprite 0 may be given priority - uint16_t objPriorityActivationBit: 1; - }; - struct { - //! @brief Stores the data written on the OAMADDL register - uint16_t oamaddl: 8; - //! @brief Stores the data written on the OAMADDH register - uint16_t oamaddh: 8; - }; - uint16_t raw = 0; - } _oamadd; - //! @brief OAMDATA Register (Data for OAM write) - uint8_t _oamdata = 0; - //! @brief BGMODE Register (OAM Address and Obj Priority) - union { - struct { - //! @brief Stores the current BG Mode (0 - 7) - uint8_t bgMode: 3; - //! @brief When Mode 1 BG3 priority bit is set the BG3 is draw - uint8_t mode1Bg3PriorityBit: 1; - //! @brief When The bit is set character size will 16x16 otherwise it is 8x8 - uint8_t characterSizeBg1: 1; - //! @brief When The bit is set character size will 16x16 otherwise it is 8x8 - uint8_t characterSizeBg2: 1; - //! @brief When The bit is set character size will 16x16 otherwise it is 8x8 - uint8_t characterSizeBg3: 1; - //! @brief When The bit is set character size will 16x16 otherwise it is 8x8 - uint8_t characterSizeBg4: 1; - }; - uint8_t raw = 0; - } _bgmode; - //! @brief MOSAIC Register (Screen Pixelation) - union { - struct { - //! @brief Apply mosaic to the BG1 - uint8_t affectBg1: 1; - //! @brief Apply mosaic to the BG2 - uint8_t affectBg2: 1; - //! @brief Apply mosaic to the BG3 - uint8_t affectBg3: 1; - //! @brief Apply mosaic to the BG4 - uint8_t affectBg4: 1; - //! @brief Stores the pixel size (0 = 1x1, F = 16x16) - uint8_t pixelSize: 4; - }; - uint8_t raw = 0; - } _mosaic; - //! @brief BGSC Registers (BG Tilemap Address and Size) - union { - struct { - //! @brief When tilemap horizontal mirroring bit is set the tilemap is mirrored horizontally - uint8_t tilemapHorizontalMirroring: 1; - //! @brief When tilemap vertically mirroring bit is set the tilemap is mirrored vertically - uint8_t tilemapVerticalMirroring: 1; - //! @brief Address of the tilemap Address (0, 0) - uint8_t tilemapAddress: 6; - }; - uint8_t raw = 0; - } _bgsc[4]; - //! @brief BGNBA Registers (BG1/2/3/4 Chr Address) - union { - struct { - //! @brief The address let us know where to search for BG1/3 characters - uint8_t baseAddressBg1a3: 4; - //! @brief The address let us know where to search for BG2/4 characters - uint8_t baseAddressBg2a4: 4; - }; - uint8_t raw = 0; - } _bgnba[2]; - //! @brief BGXXOFS Register (BG1/2/3/4 Horizontal and Vertical Scrolls) - union { - struct { - uint16_t offsetBg: 10; - uint16_t _ : 6; - }; - uint16_t raw = 0; - } _bgofs[8]; - //! @brief M7HOFS Register (Mode 7 BG Horizontal Scroll) - //! @brief M7VOFS Register (Mode 7 BG Vertical Scroll) - union { - struct { - uint16_t offsetBg : 13; - uint16_t _ : 3; - }; - uint16_t raw = 0; - } _m7ofs[2]; - //! @brief VMAIN Register (Video Port Control) - union { - struct { - uint8_t incrementAmount: 2; - uint8_t addressRemapping: 2; - uint8_t _ : 3; - uint8_t incrementMode: 1; - }; - uint8_t raw = 0; - } _vmain; - //! @brief Store the real value of the increment Amount (1, 32, 128) instead of 0,1 or 2 - uint8_t _incrementAmount = 1; - //! @brief VMADD Register (VRAM Address) - union { - struct { - uint16_t vmaddl: 8; - uint16_t vmaddh: 8; - }; - uint16_t vmadd = 0; - } _vmadd; - //! @brief VMDATA Register (VRAM Data Write) - union { - struct { - uint16_t vmdatal: 8; - uint16_t vmdatah: 8; - }; - uint16_t vmdata = 0; - } _vmdata; - //! @brief TODO M7SEL Register (Mode 7 Settings) - union { - struct { - uint8_t horizontalMirroring: 1; - uint8_t verticalMirroring: 1; - uint8_t _: 4; - uint8_t emptySpaceFill: 1; - uint8_t playingFieldSize: 1; - }; - uint8_t raw = 0; - } _m7sel; - //! @brief M7A M7B M7C M7C registers, M7A and M7B are also used with ($2134/6) (multiplactions registers) - union { - struct { - uint16_t m7l: 8; - uint16_t m7h: 8; - }; - uint16_t m7 = 0; - } _m7[4]; - // - - //! @brief M7X Register (Mode 7 Center X) - union { // Not sure if it is done correctly - struct { - uint16_t _: 3; - uint16_t value: 13; - }; - uint16_t raw = 0; - } _m7x; - //! @brief M7Y Register (Mode 7 Center Y) - union { // Not sure if it is done correctly - struct { - uint16_t _: 3; - uint16_t value: 13; - }; - uint16_t raw = 0; - } _m7y; - //! @brief CGADD Register (CGRAM Address) - uint8_t _cgadd = 0; - //! @brief CGDATA Register (CGRAM Data write) - union { - struct { - uint16_t red: 5; - uint16_t green: 5; - uint16_t blue: 5; - uint16_t _: 1; - }; - struct { - uint16_t cgdatal: 8; - uint16_t cgdatah: 8; - }; - uint16_t raw = 0; - } _cgdata; - //! @brief This bool is used for writing either the low byte of the data (first call) or the high byte of the data (second call) - //! @info This bool is set to True when writing to $2121 (CGADD) - bool _isLowByte = false; - //! @brief W12SEL - W34SEL Registers (Window Mask Settings for BGs) and WOBJSEL Register (Window Mask Settings for OBJ and Color Window) - union { - struct { - uint8_t enableWindow2ForBg2Bg4Color: 1; - uint8_t window2InversionForBg2Bg4Color: 1; - uint8_t enableWindow1ForBg2Bg4Color: 1; - uint8_t window1InversionForBg2Bg4Color: 1; - uint8_t enableWindow2ForBg1Bg3Obj: 1; - uint8_t window2InversionForBg1Bg3Obj: 1; - uint8_t enableWindow1ForBg1Bg3Obj: 1; - uint8_t window1InversionForBg1Bg3Obj: 1; - }; - uint8_t raw = 0; - } _wsel[3]; - //! @brief WH0 Register (Window 1 Left Position) - //! @brief WH1 Register (Window 1 Right Position) - //! @brief WH2 Register (Window 2 Left Position) - //! @brief WH3 Register (Window 2 Right Position) - uint8_t _wh[4] = {0}; - //! @brief WBGLOG Register (Window mask logic for BGs) - union { - struct { - uint8_t maskLogicBg4: 2; - uint8_t maskLogicBg3: 2; - uint8_t maskLogicBg2: 2; - uint8_t maskLogicBg1: 2; - }; - uint8_t raw = 0; - } _wbglog; - //! @brief WOBJLOG Register (Window mask logic for OBJs and Color Window) - union { - struct { - uint8_t maskLogicObj: 2; - uint8_t maskLogicColor: 2; - uint8_t _: 4; - }; - uint8_t raw = 0; - } _wobjlog; - //! @brief TM - TS Registers (Main & Sub Screen Designation) - union { - struct { - uint8_t enableWindowDisplayBg1: 1; - uint8_t enableWindowDisplayBg2: 1; - uint8_t enableWindowDisplayBg3: 1; - uint8_t enableWindowDisplayBg4: 1; - uint8_t enableWindowDisplayObj: 1; - uint8_t _: 3; - }; - uint8_t raw = 0; - } _t[2]; - //! @brief TMW - TSW Registers (Window Mask Designation for the Main/Sub Screen) - union { - struct { - uint8_t enableWindowMaskingBg1: 1; - uint8_t enableWindowMaskingBg2: 1; - uint8_t enableWindowMaskingBg3: 1; - uint8_t enableWindowMaskingBg4: 1; - uint8_t enableWindowMaskingObj: 1; - uint8_t _: 3; - }; - uint8_t raw = 0; - } _tw[2]; - //! @brief CGWSEL Register (Color Addition Select) - union { - struct { - uint8_t directColorMode: 1; - uint8_t addSubscreen: 1; - uint8_t _: 2; - uint8_t preventColorMath: 2; - uint8_t clipColorToBlackBeforeMath: 2; - }; - uint8_t raw = 0; - } _cgwsel; - //! @brief CGADSUB Register (Color Math designation) - union { - struct { - uint8_t enableColorMathBg1: 1; - uint8_t enableColorMathBg2: 1; - uint8_t enableColorMathBg3: 1; - uint8_t enableColorMathBg4: 1; - uint8_t enableColorMathObj: 1; - uint8_t enableColorMathBackdrop: 1; - uint8_t halfColorMath: 1; - uint8_t addSubtractSelect: 1; - }; - uint8_t raw = 0; - } _cgadsub; - //! @brief COLDATA Register (Fixed Color Data) - union { - struct { - uint8_t colorIntensity: 5; - uint8_t red: 1; - uint8_t green: 1; - uint8_t blue: 1; - }; - uint8_t raw = 0; - } _coldata; - //! @brief SETINI Register (Screen Mode/Video Select) - union { - struct { - uint8_t screenInterlace: 1; - uint8_t objInterlace: 1; - uint8_t overscanMode: 1; - uint8_t enablePseudoHiresMode: 1; - uint8_t _: 2; - uint8_t mode7ExtBg: 1; - uint8_t externalSync: 1; - }; - uint8_t raw = 0; - } _setini; - - // - - //! @brief MPYL - MPYM - MPYH Registers (Multiplication Result) - union { - struct { - uint32_t mpyl: 8; - uint32_t mpym: 8; - uint32_t mpyh: 8; - uint32_t _: 8; - }; - uint32_t mpy = 0; - } _mpy; - //! @brief SLHV - Software Latch for H/V Counter - uint8_t _slhv = 0; - //! @brief OAMDATAREAD - Data for OAM read - uint8_t _oamdataread = 0; - //! @brief VMDATALREAD/VMDATAHREAD - VRAM Data Read low/high byte - union { - struct { - uint16_t vmDataLRead: 8; - uint16_t vmDataHRead: 8; - }; - uint16_t raw = 0; - } _vmdataread; - //! @brief CGRAM Data read - union { - struct { - uint16_t cgDataLRead: 8; - uint16_t cgDataHRead: 8; - }; - uint16_t raw = 0; - } _cgdataread; - //! @brief OPHCT/OPVCT - Horizontal/Vertical Scanline Location - union { - struct { - uint16_t opct: 9; - uint16_t _: 7; - }; - uint16_t raw = 0; - } _opct; - //! @brief STAT77 - PPU Status Flag and Version - union { - struct { - uint8_t chipVersionNumber: 4; - uint8_t _: 1; - uint8_t modeSelect: 1; - uint8_t rangeOverFlag: 1; - uint8_t timeOverFlag: 1; - }; - uint8_t raw = 0; - } _stat77; - //! @brief STAT78 - PPU Status Flag and Version - union { - struct { - uint8_t chipVersionNumber: 4; - uint8_t mode: 1; - uint8_t _: 1; - uint8_t externalLatchFlag: 1; - uint8_t interlaceField: 1; - }; - uint8_t raw = 0; - } _stat78; + Background1 = 0, + Background2, + Background3, + Background4 }; //! @brief The class containing all the registers of the PPU @@ -566,10 +54,12 @@ namespace ComSquare::PPU Registers _registers{}; Renderer::IRenderer &_renderer; //! @brief Backgrounds buffers - Background _backgrounds[8]; + Background _backgrounds[4]; //! @brief Main Screen buffer std::array, 1024> _mainScreen; + std::array, 1024> _mainScreenLevelMap; //! @brief Sub Screen buffer + std::array, 1024> _subScreenLevelMap; std::array, 1024> _subScreen; //! @brief Final Screen buffer std::array, 1024> _screen; @@ -624,22 +114,17 @@ namespace ComSquare::PPU [[nodiscard]] Vector2 getBackgroundMirroring(int bgNumber) const; //! @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_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 + template + void addToMainSubScreen(Background &bg) { - // TODO use std::ranges - 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 + offset.x ][j + offset.y] = bufferSrc[i][j]; - } + if (this->_registers._t[0].raw & (1U << (bg.getBgNumber() - 1U))) { + Background::mergeBackgroundBuffer(this->_mainScreen, this->_mainScreenLevelMap, bg); + } + if (this->_registers._t[1].raw & (1U << (bg.getBgNumber() - 1U))) { + Background::mergeBackgroundBuffer(this->_subScreen, this->_subScreenLevelMap, bg); } } - //! @brief Add a bg to the sub and/or main screen - void addToMainSubScreen(Background &bg); //! @brief Get the current background Mode [[nodiscard]] int getBgMode() const; //! @brief update the Vram buffer @@ -648,19 +133,5 @@ namespace ComSquare::PPU [[nodiscard]] Vector2 getBgScroll(int bgNumber) const; //! @brief Allow to look the value of each write register (used by Register debugger) [[nodiscard]] 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]; - } - } - } }; } diff --git a/sources/PPU/PPURegisters.hpp b/sources/PPU/PPURegisters.hpp new file mode 100644 index 0000000..d46fd6c --- /dev/null +++ b/sources/PPU/PPURegisters.hpp @@ -0,0 +1,519 @@ +// +// Created by cbihan on 13/07/2021. +// + +#pragma once + + +namespace ComSquare::PPU +{ + enum PpuRegisters { + //! @brief INIDISP Register (F-blank and Brightness) + inidisp = 0x00, + //! @brief OBSEL Register (Object Size and Character Address) + obsel = 0x01, + //! @brief OAMADDL (OAM Address low byte) + oamaddl = 0x02, + //! @brief OAMADDH (OAM Address high bit and Obj Priority) + oamaddh = 0x03, + //! @brief OAMDATA (Data for OAM write) + oamdata = 0x04, + //! @brief BGMODE (BG Mode and Character Size) + bgmode = 0x05, + //! @brief MOSAIC (Screen Pixelation) + mosaic = 0x06, + //! @brief BG1SC (BG1 Tilemap Address and Size) + bg1sc = 0x07, + //! @brief BG2SC (BG2 Tilemap Address and Size) + bg2sc = 0x08, + //! @brief BG3SC (BG3 Tilemap Address and Size) + bg3sc = 0x09, + //! @brief BG4SC (BG4 Tilemap Address and Size) + bg4sc = 0x0A, + //! @brief BG12NBA (BG1 and 2 Chr Address) + bg12nba = 0x0B, + //! @brief BG34NBA (BG3 and 4 Chr Address) + bg34nba = 0x0C, + //! @brief BG1HOFS (BG1 Horizontal Scroll) + //! @brief M7HOFS (Mode 7 BG Horizontal Scroll) + //! @info When bg mode is 7 the register is used as M7HOFS + bg1hofs = 0x0D, + //! @brief BG1VOFS (BG1 Vertical Scroll) + //! @brief M7VOFS (Mode 7 BG Vertical Scroll) + //! @info When bg mode is 7 the register is used as M7VOFS + bg1vofs = 0x0E, + //! @brief BG2HOFS (BG2 Horizontal Scroll) + bg2hofs = 0x0F, + //! @brief BG2VOFS (BG2 Vertical Scroll) + bg2vofs = 0x10, + //! @brief BG3HOFS (BG3 Horizontal Scroll) + bg3hofs = 0x11, + //! @brief BG3VOFS (BG3 Vertical Scroll) + bg3vofs = 0x12, + //! @brief BG4HOFS (BG4 Horizontal Scroll) + bg4hofs = 0x13, + //! @brief BG4VOFS (BG4 Vertical Scroll) + bg4vofs = 0x14, + //! @brief VMAIN (Video Port Control) + vmain = 0x15, + //! @brief VMADDL (VRAM Address low byte) + vmaddl = 0x16, + //! @brief VMADDH (VRAM Address high byte) + vmaddh = 0x17, + //! @brief VMDATAL (VRAM Data Write low byte) + vmdatal = 0x18, + //! @brief VMDATAH (VRAM Data Write high byte) + vmdatah = 0x19, + //! @brief M7SEL (Mode 7 Settings) + m7sel = 0x1A, + //! @brief M7A (Mode 7 Matrix A) also used with $2134/6 + m7a = 0x1B, + //! @brief M7B (Mode 7 Matrix B) also used with $2134/6 + m7b = 0x1C, + //! @brief M7C (Mode 7 Matrix C) + m7c = 0x1D, + //! @brief M7D (Mode 7 Matrix D) + m7d = 0x1E, + //! @brief M7X (Mode 7 Center X) + m7x = 0x1F, + //! @brief M7Y (Mode 7 Center Y) + m7y = 0x20, + //! @brief CGADD (CGRAM Address) + cgadd = 0x21, + //! @brief CGDATA (CGRAM Data write) + cgdata = 0x22, + //! @brief W12SEL (Window Mask Settings for BG1 and BG2) + w12sel = 0x23, + //! @brief W34SEL (Window Mask Settings for BG3 and BG4) + w34sel = 0x24, + //! @brief WOBJSEL (Window Mask Settings for OBJ and Color Window) + wobjsel = 0x25, + //! @brief WH0 (Window 1 Left Position) + wh0 = 0x26, + //! @brief WH1 (Window 1 Right Position) + wh1 = 0x27, + //! @brief WH2 (Window 2 Left Position) + wh2 = 0x28, + //! @brief WH3 (Window 2 Right Position) + wh3 = 0x29, + //! @brief WBGLOG (Window mask logic for BGs) + wbjlog = 0x2A, + //! @brief WOBJLOG (Window mask logic for OBJs and Color Window) + wobjlog = 0x2B, + //! @brief TM (Main Screen Designation) + tm = 0x2C, + //! @brief TS (Subscreen Designation) + ts = 0x2D, + //! @brief TMW (Window Mask Designation for the Main Screen) + tmw = 0x2E, + //! @brief TSW (Window Mask Designation for the Subscreen) + tsw = 0x2F, + //! @brief CGWSEL (Color Addition Select) + cgwsel = 0x30, + //! @brief CGADSUB (Color math designation) + cgadsub = 0x31, + //! @brief COLDATA (Fixed Color Data) + coldata = 0x32, + //! @brief SETINI (Screen Mode/Video Select) + setini = 0x33, + //! @brief MPYL (Multiplication Result low byte) + mpyl = 0x34, + //! @brief MPYM (Multiplication Result middle byte) + mpym = 0x35, + //! @brief MPYH (Multiplication Result high byte) + mpyh = 0x36, + //! @brief SLHV (Software Latch for H/V Counter) + slhv = 0x37, + //! @brief OAMDATAREAD (Data for OAM read) + oamdataread = 0x38, + //! @brief VMDATALREAD (VRAM Data Read low byte) + vmdatalread = 0x39, + //! @brief VMDATAHREAD (VRAM Data Read high byte) + vmdatahread = 0x3A, + //! @brief CGDATAREAD (CGRAM Data read) + cgdataread = 0x3B, + //! @brief OPHCT (Horizontal Scanline Location) + ophct = 0x3C, + //! @brief OPVCT (Vertical Scanline Location) + opvct = 0x3D, + //! @brief STAT77 (PPU Status Flag and Version) + stat77 = 0x3E, + //! @brief STAT78 (PPU Status Flag and Version) + stat78 = 0x3F + }; + + struct Registers { + //! @brief INIDISP Register (F-blank and Brightness) + union { + struct { + //! @brief Store the brightness value (F = max, 0 = off) + uint8_t brightness: 4; + uint8_t _: 3; + //! @brief Store the FBlank status + uint8_t fblank: 1; + }; + uint8_t raw = 0; + } _inidisp; + //! @brief OBSEL Register (Object Size and Character Address) + union { + struct { + //! @brief Stores the location of the first sprite table + uint8_t nameBaseSelect: 3; + //! @brief Stores the offset of the second sprite table + uint8_t nameSelect: 2; + //! @brief Stores the resolution preset of the sprites + uint8_t objectSize: 3; + }; + uint8_t raw = 0; + } _obsel; + //! @brief OAMADD Register (OAM Address and Obj Priority) + union { + struct { + //! @brief Stores the address to write with OAMDATA register + uint16_t oamAddress: 9; + uint16_t _: 6; + //! @brief When Obj Priority activation bit is set, an Obj other than Sprite 0 may be given priority + uint16_t objPriorityActivationBit: 1; + }; + struct { + //! @brief Stores the data written on the OAMADDL register + uint16_t oamaddl: 8; + //! @brief Stores the data written on the OAMADDH register + uint16_t oamaddh: 8; + }; + uint16_t raw = 0; + } _oamadd; + //! @brief OAMDATA Register (Data for OAM write) + uint8_t _oamdata = 0; + //! @brief BGMODE Register (OAM Address and Obj Priority) + union { + struct { + //! @brief Stores the current BG Mode (0 - 7) + uint8_t bgMode: 3; + //! @brief When Mode 1 BG3 priority bit is set the BG3 is draw + uint8_t mode1Bg3PriorityBit: 1; + //! @brief When The bit is set character size will 16x16 otherwise it is 8x8 + uint8_t characterSizeBg1: 1; + //! @brief When The bit is set character size will 16x16 otherwise it is 8x8 + uint8_t characterSizeBg2: 1; + //! @brief When The bit is set character size will 16x16 otherwise it is 8x8 + uint8_t characterSizeBg3: 1; + //! @brief When The bit is set character size will 16x16 otherwise it is 8x8 + uint8_t characterSizeBg4: 1; + }; + uint8_t raw = 0; + } _bgmode; + //! @brief MOSAIC Register (Screen Pixelation) + union { + struct { + //! @brief Apply mosaic to the BG1 + uint8_t affectBg1: 1; + //! @brief Apply mosaic to the BG2 + uint8_t affectBg2: 1; + //! @brief Apply mosaic to the BG3 + uint8_t affectBg3: 1; + //! @brief Apply mosaic to the BG4 + uint8_t affectBg4: 1; + //! @brief Stores the pixel size (0 = 1x1, F = 16x16) + uint8_t pixelSize: 4; + }; + uint8_t raw = 0; + } _mosaic; + //! @brief BGSC Registers (BG Tilemap Address and Size) + union { + struct { + //! @brief When tilemap horizontal mirroring bit is set the tilemap is mirrored horizontally + uint8_t tilemapHorizontalMirroring: 1; + //! @brief When tilemap vertically mirroring bit is set the tilemap is mirrored vertically + uint8_t tilemapVerticalMirroring: 1; + //! @brief Address of the tilemap Address (0, 0) + uint8_t tilemapAddress: 6; + }; + uint8_t raw = 0; + } _bgsc[4]; + //! @brief BGNBA Registers (BG1/2/3/4 Chr Address) + union { + struct { + //! @brief The address let us know where to search for BG1/3 characters + uint8_t baseAddressBg1a3: 4; + //! @brief The address let us know where to search for BG2/4 characters + uint8_t baseAddressBg2a4: 4; + }; + uint8_t raw = 0; + } _bgnba[2]; + //! @brief BGXXOFS Register (BG1/2/3/4 Horizontal and Vertical Scrolls) + union { + struct { + uint16_t offsetBg: 10; + uint16_t _ : 6; + }; + uint16_t raw = 0; + } _bgofs[8]; + //! @brief M7HOFS Register (Mode 7 BG Horizontal Scroll) + //! @brief M7VOFS Register (Mode 7 BG Vertical Scroll) + union { + struct { + uint16_t offsetBg : 13; + uint16_t _ : 3; + }; + uint16_t raw = 0; + } _m7ofs[2]; + //! @brief VMAIN Register (Video Port Control) + union { + struct { + uint8_t incrementAmount: 2; + uint8_t addressRemapping: 2; + uint8_t _ : 3; + uint8_t incrementMode: 1; + }; + uint8_t raw = 0; + } _vmain; + //! @brief Store the real value of the increment Amount (1, 32, 128) instead of 0,1 or 2 + uint8_t _incrementAmount = 1; + //! @brief VMADD Register (VRAM Address) + union { + struct { + uint16_t vmaddl: 8; + uint16_t vmaddh: 8; + }; + uint16_t vmadd = 0; + } _vmadd; + //! @brief VMDATA Register (VRAM Data Write) + union { + struct { + uint16_t vmdatal: 8; + uint16_t vmdatah: 8; + }; + uint16_t vmdata = 0; + } _vmdata; + //! @brief TODO M7SEL Register (Mode 7 Settings) + union { + struct { + uint8_t horizontalMirroring: 1; + uint8_t verticalMirroring: 1; + uint8_t _: 4; + uint8_t emptySpaceFill: 1; + uint8_t playingFieldSize: 1; + }; + uint8_t raw = 0; + } _m7sel; + //! @brief M7A M7B M7C M7C registers, M7A and M7B are also used with ($2134/6) (multiplactions registers) + union { + struct { + uint16_t m7l: 8; + uint16_t m7h: 8; + }; + uint16_t m7 = 0; + } _m7[4]; + // + + //! @brief M7X Register (Mode 7 Center X) + union { // Not sure if it is done correctly + struct { + uint16_t _: 3; + uint16_t value: 13; + }; + uint16_t raw = 0; + } _m7x; + //! @brief M7Y Register (Mode 7 Center Y) + union { // Not sure if it is done correctly + struct { + uint16_t _: 3; + uint16_t value: 13; + }; + uint16_t raw = 0; + } _m7y; + //! @brief CGADD Register (CGRAM Address) + uint8_t _cgadd = 0; + //! @brief CGDATA Register (CGRAM Data write) + union { + struct { + uint16_t red: 5; + uint16_t green: 5; + uint16_t blue: 5; + uint16_t _: 1; + }; + struct { + uint16_t cgdatal: 8; + uint16_t cgdatah: 8; + }; + uint16_t raw = 0; + } _cgdata; + //! @brief This bool is used for writing either the low byte of the data (first call) or the high byte of the data (second call) + //! @info This bool is set to True when writing to $2121 (CGADD) + bool _isLowByte = false; + //! @brief W12SEL - W34SEL Registers (Window Mask Settings for BGs) and WOBJSEL Register (Window Mask Settings for OBJ and Color Window) + union { + struct { + uint8_t enableWindow2ForBg2Bg4Color: 1; + uint8_t window2InversionForBg2Bg4Color: 1; + uint8_t enableWindow1ForBg2Bg4Color: 1; + uint8_t window1InversionForBg2Bg4Color: 1; + uint8_t enableWindow2ForBg1Bg3Obj: 1; + uint8_t window2InversionForBg1Bg3Obj: 1; + uint8_t enableWindow1ForBg1Bg3Obj: 1; + uint8_t window1InversionForBg1Bg3Obj: 1; + }; + uint8_t raw = 0; + } _wsel[3]; + //! @brief WH0 Register (Window 1 Left Position) + //! @brief WH1 Register (Window 1 Right Position) + //! @brief WH2 Register (Window 2 Left Position) + //! @brief WH3 Register (Window 2 Right Position) + uint8_t _wh[4] = {0}; + //! @brief WBGLOG Register (Window mask logic for BGs) + union { + struct { + uint8_t maskLogicBg4: 2; + uint8_t maskLogicBg3: 2; + uint8_t maskLogicBg2: 2; + uint8_t maskLogicBg1: 2; + }; + uint8_t raw = 0; + } _wbglog; + //! @brief WOBJLOG Register (Window mask logic for OBJs and Color Window) + union { + struct { + uint8_t maskLogicObj: 2; + uint8_t maskLogicColor: 2; + uint8_t _: 4; + }; + uint8_t raw = 0; + } _wobjlog; + //! @brief TM - TS Registers (Main & Sub Screen Designation) + union { + struct { + uint8_t enableWindowDisplayBg1: 1; + uint8_t enableWindowDisplayBg2: 1; + uint8_t enableWindowDisplayBg3: 1; + uint8_t enableWindowDisplayBg4: 1; + uint8_t enableWindowDisplayObj: 1; + uint8_t _: 3; + }; + uint8_t raw = 0; + } _t[2]; + //! @brief TMW - TSW Registers (Window Mask Designation for the Main/Sub Screen) + union { + struct { + uint8_t enableWindowMaskingBg1: 1; + uint8_t enableWindowMaskingBg2: 1; + uint8_t enableWindowMaskingBg3: 1; + uint8_t enableWindowMaskingBg4: 1; + uint8_t enableWindowMaskingObj: 1; + uint8_t _: 3; + }; + uint8_t raw = 0; + } _tw[2]; + //! @brief CGWSEL Register (Color Addition Select) + union { + struct { + uint8_t directColorMode: 1; + uint8_t addSubscreen: 1; + uint8_t _: 2; + uint8_t preventColorMath: 2; + uint8_t clipColorToBlackBeforeMath: 2; + }; + uint8_t raw = 0; + } _cgwsel; + //! @brief CGADSUB Register (Color Math designation) + union { + struct { + uint8_t enableColorMathBg1: 1; + uint8_t enableColorMathBg2: 1; + uint8_t enableColorMathBg3: 1; + uint8_t enableColorMathBg4: 1; + uint8_t enableColorMathObj: 1; + uint8_t enableColorMathBackdrop: 1; + uint8_t halfColorMath: 1; + uint8_t addSubtractSelect: 1; + }; + uint8_t raw = 0; + } _cgadsub; + //! @brief COLDATA Register (Fixed Color Data) + union { + struct { + uint8_t colorIntensity: 5; + uint8_t red: 1; + uint8_t green: 1; + uint8_t blue: 1; + }; + uint8_t raw = 0; + } _coldata; + //! @brief SETINI Register (Screen Mode/Video Select) + union { + struct { + uint8_t screenInterlace: 1; + uint8_t objInterlace: 1; + uint8_t overscanMode: 1; + uint8_t enablePseudoHiresMode: 1; + uint8_t _: 2; + uint8_t mode7ExtBg: 1; + uint8_t externalSync: 1; + }; + uint8_t raw = 0; + } _setini; + + // + + //! @brief MPYL - MPYM - MPYH Registers (Multiplication Result) + union { + struct { + uint32_t mpyl: 8; + uint32_t mpym: 8; + uint32_t mpyh: 8; + uint32_t _: 8; + }; + uint32_t mpy = 0; + } _mpy; + //! @brief SLHV - Software Latch for H/V Counter + uint8_t _slhv = 0; + //! @brief OAMDATAREAD - Data for OAM read + uint8_t _oamdataread = 0; + //! @brief VMDATALREAD/VMDATAHREAD - VRAM Data Read low/high byte + union { + struct { + uint16_t vmDataLRead: 8; + uint16_t vmDataHRead: 8; + }; + uint16_t raw = 0; + } _vmdataread; + //! @brief CGRAM Data read + union { + struct { + uint16_t cgDataLRead: 8; + uint16_t cgDataHRead: 8; + }; + uint16_t raw = 0; + } _cgdataread; + //! @brief OPHCT/OPVCT - Horizontal/Vertical Scanline Location + union { + struct { + uint16_t opct: 9; + uint16_t _: 7; + }; + uint16_t raw = 0; + } _opct; + //! @brief STAT77 - PPU Status Flag and Version + union { + struct { + uint8_t chipVersionNumber: 4; + uint8_t _: 1; + uint8_t modeSelect: 1; + uint8_t rangeOverFlag: 1; + uint8_t timeOverFlag: 1; + }; + uint8_t raw = 0; + } _stat77; + //! @brief STAT78 - PPU Status Flag and Version + union { + struct { + uint8_t chipVersionNumber: 4; + uint8_t mode: 1; + uint8_t _: 1; + uint8_t externalLatchFlag: 1; + uint8_t interlaceField: 1; + }; + uint8_t raw = 0; + } _stat78; + }; +} \ No newline at end of file diff --git a/sources/PPU/PPUUtils.cpp b/sources/PPU/PPUUtils.cpp index eb41ab1..b25385a 100644 --- a/sources/PPU/PPUUtils.cpp +++ b/sources/PPU/PPUUtils.cpp @@ -7,16 +7,12 @@ namespace ComSquare::PPU::Utils { - uint32_t getRealColor(uint16_t color) + uint32_t CGRAMColorToRGBA(uint16_t CGRAMColor) { - uint8_t blue = (color & 0x7D00U) >> 10U; - uint8_t green = (color & 0x03E0U) >> 5U; - uint8_t red = (color & 0x001FU); - uint32_t pixelTmp = 0xFF; + uint8_t b = to8Bit(CGRAMColor >> 10); + uint8_t g = to8Bit((CGRAMColor >> 5) & 0x1F); + uint8_t r = to8Bit(CGRAMColor & 0x1F); - pixelTmp += (red * 255U / 31U) << 24U; - pixelTmp += (green * 255U / 31U) << 16U; - pixelTmp += (blue * 255U / 31U) << 8U; - return pixelTmp; + return (0x000000FF | (r << 24) | (g << 16) | (b << 8)); } } \ No newline at end of file diff --git a/sources/PPU/PPUUtils.hpp b/sources/PPU/PPUUtils.hpp index 0add0a5..e6b099d 100644 --- a/sources/PPU/PPUUtils.hpp +++ b/sources/PPU/PPUUtils.hpp @@ -17,11 +17,19 @@ namespace ComSquare::PPU namespace ComSquare::PPU::Utils { - - //! @brief Transform SNES color code BGR to uint32_t RGB - uint32_t getRealColor(uint16_t color); + //! @brief Transform CGRAM color data to 8bits + //! @note Used with b, g and r values + //! @return The 8 bit value of the color + inline uint8_t to8Bit(int color) + { + return static_cast((color << 3) + (color >> 2)); + } + //! @brief Transform SNES color code BGR to uint32_t RGBA + //! @param CGRAMColor The color of the CGRAM + //! @return The CGRAM color to RGBA format + uint32_t CGRAMColorToRGBA(uint16_t CGRAMColor); //! @brief Used to parse easily VRAM Tile information - union TileMapData { + union TileData { struct { //! @brief Tile X offset uint16_t posX: 4; @@ -50,12 +58,11 @@ namespace ComSquare::PPU::Utils template void merge2DArray(std::array, DEST_SIZE_Y> &bufferDest, const std::array, SRC_SIZE_Y> &bufferSrc, - const Vector2 &offset = {0, 0}) + const Vector2 &offset) { - 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]; - } + int offsetY = offset.y; + for (auto &row : bufferSrc) { + std::copy(row.begin(), row.end(), bufferDest[offsetY++].begin() + offset.x); } } @@ -77,4 +84,21 @@ namespace ComSquare::PPU::Utils std::reverse(array.begin() + offset.x, array.begin() + offset.x + size.x); } + //! @brief Add a bg buffer to another buffer + template + static void addBuffer(std::array, DEST_SIZE_X> &bufferDest, + const std::array, SRC_SIZE_X> &bufferSrc) + { + int i = 0; + int j = 0; + for (const auto &sourceRow : bufferSrc) { + for (const auto &pixel : sourceRow) { + if (pixel > 0xFF) + bufferDest[i][j] = pixel; + j++; + }; + j = 0; + i++; + }; + } } diff --git a/sources/PPU/PpuDebug.cpp b/sources/PPU/PpuDebug.cpp index 38a3959..10e1745 100644 --- a/sources/PPU/PpuDebug.cpp +++ b/sources/PPU/PpuDebug.cpp @@ -3776,18 +3776,18 @@ namespace ComSquare::PPU::Utils::Debug { ppu._registers._bgmode.bgMode = 1; ppu._backgrounds[0].setBpp(ppu.getBPP(1)); - ppu._backgrounds[1].setBpp(ppu.getBPP(1)); - ppu._backgrounds[2].setBpp(ppu.getBPP(2)); - ppu._backgrounds[3].setBpp(ppu.getBPP(2)); - ppu._backgrounds[4].setBpp(ppu.getBPP(3)); - ppu._backgrounds[5].setBpp(ppu.getBPP(3)); + ppu._backgrounds[1].setBpp(ppu.getBPP(2)); + ppu._backgrounds[2].setBpp(ppu.getBPP(3)); + ppu._backgrounds[3].setBpp(ppu.getBPP(4)); + //ppu._backgrounds[4].setBpp(ppu.getBPP(3)); + //ppu._backgrounds[5].setBpp(ppu.getBPP(3)); //ppu._registers._bgmode.characterSizeBg1 = false; //ppu._registers._bgmode.characterSizeBg2 = false; ppu._registers._bgmode.mode1Bg3PriorityBit = true; ppu._backgrounds[0].setCharacterSize(ppu.getCharacterSize(1)); - ppu._backgrounds[1].setCharacterSize(ppu.getCharacterSize(1)); - ppu._backgrounds[2].setCharacterSize(ppu.getCharacterSize(2)); - ppu._backgrounds[3].setCharacterSize(ppu.getCharacterSize(2)); + ppu._backgrounds[1].setCharacterSize(ppu.getCharacterSize(2)); + ppu._backgrounds[2].setCharacterSize(ppu.getCharacterSize(3)); + ppu._backgrounds[3].setCharacterSize(ppu.getCharacterSize(4)); ppu._registers._bgsc[0].tilemapAddress = 0x4800U >> 10U; // 0x4800 ppu._registers._bgsc[0].tilemapHorizontalMirroring = 1; @@ -3795,15 +3795,16 @@ namespace ComSquare::PPU::Utils::Debug ppu._registers._bgsc[1].tilemapHorizontalMirroring = 1; ppu._registers._bgsc[2].tilemapAddress = 0x5C00U >> 10U; ppu._backgrounds[0].setTileMapStartAddress(ppu.getTileMapStartAddress(1)); - ppu._backgrounds[0].setTilemaps(ppu.getBackgroundMirroring(1)); - ppu._backgrounds[1].setTileMapStartAddress(ppu.getTileMapStartAddress(1)); - ppu._backgrounds[1].setTilemaps(ppu.getBackgroundMirroring(1)); - ppu._backgrounds[2].setTileMapStartAddress(ppu.getTileMapStartAddress(2)); - ppu._backgrounds[2].setTilemaps(ppu.getBackgroundMirroring(2)); - ppu._backgrounds[3].setTileMapStartAddress(ppu.getTileMapStartAddress(2)); - ppu._backgrounds[3].setTilemaps(ppu.getBackgroundMirroring(2)); - ppu._backgrounds[4].setTileMapStartAddress(ppu.getTileMapStartAddress(3)); - ppu._backgrounds[5].setTileMapStartAddress(ppu.getTileMapStartAddress(3)); + ppu._backgrounds[0].setTileMapMirroring(ppu.getBackgroundMirroring(1)); + ppu._backgrounds[1].setTileMapStartAddress(ppu.getTileMapStartAddress(2)); + ppu._backgrounds[1].setTileMapMirroring(ppu.getBackgroundMirroring(2)); + ppu._backgrounds[2].setTileMapStartAddress(ppu.getTileMapStartAddress(3)); + ppu._backgrounds[2].setTileMapMirroring(ppu.getBackgroundMirroring(3)); + ppu._backgrounds[3].setTileMapStartAddress(ppu.getTileMapStartAddress(4)); + ppu._backgrounds[3].setTileMapMirroring(ppu.getBackgroundMirroring(4)); + + //ppu._backgrounds[4].setTileMapStartAddress(ppu.getTileMapStartAddress(3)); + //ppu._backgrounds[5].setTileMapStartAddress(ppu.getTileMapStartAddress(3)); //registres bgnba //ppu._registers._bgnba[0].baseAddressBg1a3 = 0x5; @@ -3814,8 +3815,8 @@ namespace ComSquare::PPU::Utils::Debug //ppu._backgrounds[1].setTilesetAddress(ppu.getTilesetAddress(1)); //ppu._backgrounds[2].setTilesetAddress(ppu.getTilesetAddress(2)); //ppu._backgrounds[3].setTilesetAddress(ppu.getTilesetAddress(2)); - ppu._backgrounds[4].setTilesetAddress(ppu.getTilesetAddress(3)); - ppu._backgrounds[5].setTilesetAddress(ppu.getTilesetAddress(3)); + ppu._backgrounds[2].setTilesetAddress(ppu.getTilesetAddress(3)); + //ppu._backgrounds[3].setTilesetAddress(ppu.getTilesetAddress(3)); ppu._registers._vmain.incrementMode = true; ppu._registers._vmain.incrementAmount = 1; @@ -3832,31 +3833,24 @@ namespace ComSquare::PPU::Utils::Debug //registers tic tac toe ppu._registers._bgmode.bgMode = 0; ppu._backgrounds[0].setBpp(ppu.getBPP(1)); - ppu._backgrounds[1].setBpp(ppu.getBPP(1)); - ppu._backgrounds[2].setBpp(ppu.getBPP(2)); - ppu._backgrounds[3].setBpp(ppu.getBPP(2)); - ppu._backgrounds[4].setBpp(ppu.getBPP(3)); - ppu._backgrounds[5].setBpp(ppu.getBPP(3)); - ppu._backgrounds[6].setBpp(ppu.getBPP(4)); - ppu._backgrounds[7].setBpp(ppu.getBPP(4)); + ppu._backgrounds[1].setBpp(ppu.getBPP(2)); + ppu._backgrounds[2].setBpp(ppu.getBPP(3)); + ppu._backgrounds[3].setBpp(ppu.getBPP(4)); ppu._registers._bgmode.characterSizeBg1 = true; ppu._registers._bgmode.characterSizeBg2 = true; ppu._backgrounds[0].setCharacterSize(ppu.getCharacterSize(1)); - ppu._backgrounds[1].setCharacterSize(ppu.getCharacterSize(1)); - ppu._backgrounds[2].setCharacterSize(ppu.getCharacterSize(2)); - ppu._backgrounds[3].setCharacterSize(ppu.getCharacterSize(2)); - ppu._backgrounds[4].setCharacterSize(ppu.getCharacterSize(3)); - ppu._backgrounds[5].setCharacterSize(ppu.getCharacterSize(3)); - ppu._backgrounds[6].setCharacterSize(ppu.getCharacterSize(4)); - ppu._backgrounds[7].setCharacterSize(ppu.getCharacterSize(4)); + ppu._backgrounds[1].setCharacterSize(ppu.getCharacterSize(2)); + ppu._backgrounds[2].setCharacterSize(ppu.getCharacterSize(3)); + ppu._backgrounds[3].setCharacterSize(ppu.getCharacterSize(4)); + ppu._registers._bgsc[0].tilemapAddress = 0x4000 >> 10U; ppu._registers._bgsc[1].tilemapAddress = 0x6000 >> 10U; ppu._backgrounds[0].setTileMapStartAddress(ppu.getTileMapStartAddress(1)); - ppu._backgrounds[1].setTileMapStartAddress(ppu.getTileMapStartAddress(1)); - ppu._backgrounds[2].setTileMapStartAddress(ppu.getTileMapStartAddress(2)); - ppu._backgrounds[3].setTileMapStartAddress(ppu.getTileMapStartAddress(2)); + ppu._backgrounds[1].setTileMapStartAddress(ppu.getTileMapStartAddress(2)); + ppu._backgrounds[2].setTileMapStartAddress(ppu.getTileMapStartAddress(3)); + ppu._backgrounds[3].setTileMapStartAddress(ppu.getTileMapStartAddress(4)); //ppu._registers._bgofs[2].raw = 0x03E0; //ppu._registers._bgofs[3].raw = 0x03DF; diff --git a/sources/PPU/TileRenderer.cpp b/sources/PPU/TileRenderer.cpp index 52c3bef..5dfa5f9 100644 --- a/sources/PPU/TileRenderer.cpp +++ b/sources/PPU/TileRenderer.cpp @@ -29,7 +29,7 @@ namespace ComSquare::PPU for (auto &row : this->buffer) { for (auto &pixel : row) { uint8_t pixelReference = this->getPixelReferenceFromTile(tileAddress, it++); - pixel = pixelReference ? Utils::getRealColor(palette[pixelReference]) : 0; + pixel = pixelReference ? Utils::CGRAMColorToRGBA(palette[pixelReference]) : 0; } } } @@ -65,7 +65,6 @@ namespace ComSquare::PPU 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); @@ -78,13 +77,11 @@ namespace ComSquare::PPU { // 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; + result += this->read2BPPValue(tileRowAddress + TileByteSizeRow * 2, pixelIndex) << 4; FALLTHROUGH case 4: result += this->read2BPPValue(tileRowAddress + TileByteSizeRow, pixelIndex) << 2; diff --git a/sources/PPU/TileRenderer.hpp b/sources/PPU/TileRenderer.hpp index dea25c3..ba0f4e5 100644 --- a/sources/PPU/TileRenderer.hpp +++ b/sources/PPU/TileRenderer.hpp @@ -11,6 +11,9 @@ namespace ComSquare::PPU { class TileRenderer { private: + //! @brief The byte size offset for 1 row in VRAM + static constexpr int TileByteSizeRow = 16; + //! @brief ram to render Ram::Ram &_ram; //! @brief cgram to access the colors @@ -28,9 +31,9 @@ namespace ComSquare::PPU //! @brief Set the bpp to render graphics void setBpp(int bpp); //! @brief Get the current bpp - int getBpp() const; + [[nodiscard]] int getBpp() const; //! @brief Get the index of the current palette used - int getPaletteIndex() const; + [[nodiscard]] 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) @@ -43,10 +46,14 @@ namespace ComSquare::PPU //! @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 + //! @param nbPalette The index of the palette wanted //! @return The array of color of the palette - //! @warning Values are CGRAM colors use PPU::getRealColor function to get the actual real color + //! @note The return and argument depends on the current bpp + //! @warning Values are CGRAM colors use PPU::CGRAMColorToRGBA 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) + //! @param tileRowAddress Address where the read is done. Usage: Address of the tile row to render + //! @param pixelIndex The offset form tileRowAddress, Usage: the pixel to read 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 diff --git a/sources/Renderer/SFRenderer.cpp b/sources/Renderer/SFRenderer.cpp index 0ac1eea..3930b95 100644 --- a/sources/Renderer/SFRenderer.cpp +++ b/sources/Renderer/SFRenderer.cpp @@ -66,8 +66,7 @@ namespace ComSquare::Renderer if (y >= this->_videoMode.height) throw InvalidPixelPosition("Height", y, this->_videoMode.height); - sf::Color pixels(rgba); - this->_pixelBuffer[this->_videoMode.width * y + x] = pixels; + this->_pixelBuffer[this->_videoMode.width * y + x] = sf::Color(rgba); } void SFRenderer::getEvents() diff --git a/tests/PPU/testTileRenderer.cpp b/tests/PPU/testTileRenderer.cpp new file mode 100644 index 0000000..85df464 --- /dev/null +++ b/tests/PPU/testTileRenderer.cpp @@ -0,0 +1,707 @@ +// +// Created by cbihan on 08/07/2021. +// + +#include +#include +#include +#include +#include +#include +#include "PPU/TileRenderer.hpp" +#include "Ram/Ram.hpp" +#include "PPU/Tile.hpp" +#include "PPU/PPUUtils.hpp" + +void fillRAM(const std::vector &values, ComSquare::Ram::Ram &ram) +{ + uint8_t value; + int i = 0; + + for (auto row : values) { + while (!row.empty()) { + value = std::stoul(row.substr(0, 2), nullptr, 16); + row.erase(0, 2); + ram.write(i++, value); + } + } +} + +void fillRAM(const std::vector &values, ComSquare::Ram::Ram &ram) +{ + int i = 0; + + for (const auto &value : values) { + ram.write(i++, value); + } +} + + +TEST_CASE("read2BPPValue", "[PPU][TileRenderer]") +{ + ComSquare::Ram::Ram vram(100, static_cast(0), "vramTest"); + ComSquare::Ram::Ram cgram(512, static_cast(0), "cgramTest"); + ComSquare::PPU::TileRenderer tileRenderer(vram, cgram); + + vram.write(0, 0xBA); + vram.write(1, 0x7C); + + CHECK(tileRenderer.read2BPPValue(0, 0) == 1); + CHECK(tileRenderer.read2BPPValue(0, 1) == 2); + CHECK(tileRenderer.read2BPPValue(0, 2) == 3); + CHECK(tileRenderer.read2BPPValue(0, 3) == 3); + CHECK(tileRenderer.read2BPPValue(0, 4) == 3); + CHECK(tileRenderer.read2BPPValue(0, 5) == 2); + CHECK(tileRenderer.read2BPPValue(0, 6) == 1); + CHECK(tileRenderer.read2BPPValue(0, 7) == 0); +} + +TEST_CASE("read2BPPValue with circular ram", "[PPU][TileRenderer]") +{ + ComSquare::Ram::Ram vram(10, static_cast(0), "vramTest"); + ComSquare::Ram::Ram cgram(512, static_cast(0), "cgramTest"); + ComSquare::PPU::TileRenderer tileRenderer(vram, cgram); + + vram.write(9, 0xBA); + vram.write(0, 0x7C); + + CHECK(tileRenderer.read2BPPValue(9, 0) == 1); + CHECK(tileRenderer.read2BPPValue(9, 1) == 2); + CHECK(tileRenderer.read2BPPValue(9, 2) == 3); + CHECK(tileRenderer.read2BPPValue(9, 3) == 3); + CHECK(tileRenderer.read2BPPValue(9, 4) == 3); + CHECK(tileRenderer.read2BPPValue(9, 5) == 2); + CHECK(tileRenderer.read2BPPValue(9, 6) == 1); + CHECK(tileRenderer.read2BPPValue(9, 7) == 0); +} + +TEST_CASE("getPixelReferenceFromTileRow 2bpp", "[PPU][TileRenderer]") +{ + ComSquare::Ram::Ram vram(10, static_cast(0), "vramTest"); + ComSquare::Ram::Ram cgram(512, static_cast(0), "cgramTest"); + ComSquare::PPU::TileRenderer tileRenderer(vram, cgram); + + tileRenderer.setBpp(2); + + vram.write(2, 0xD6); + vram.write(3, 0x00); + + CHECK(tileRenderer.getPixelReferenceFromTileRow(2, 0) == 1); + CHECK(tileRenderer.getPixelReferenceFromTileRow(2, 1) == 1); + CHECK(tileRenderer.getPixelReferenceFromTileRow(2, 2) == 0); + CHECK(tileRenderer.getPixelReferenceFromTileRow(2, 3) == 1); + CHECK(tileRenderer.getPixelReferenceFromTileRow(2, 4) == 0); + CHECK(tileRenderer.getPixelReferenceFromTileRow(2, 5) == 1); + CHECK(tileRenderer.getPixelReferenceFromTileRow(2, 6) == 1); + CHECK(tileRenderer.getPixelReferenceFromTileRow(2, 7) == 0); +} + +TEST_CASE("getPixelReferenceFromTileRow 4bpp", "[PPU][TileRenderer]") +{ + ComSquare::Ram::Ram vram(40, static_cast(0), "vramTest"); + ComSquare::Ram::Ram cgram(512, static_cast(0), "cgramTest"); + ComSquare::PPU::TileRenderer tileRenderer(vram, cgram); + + tileRenderer.setBpp(4); + + std::vector vramValues { + "7C7C82EE82FE7C7C", + "0000D68254443838", + "7C00AA1082007C00", + "1000540038000000" + }; + + fillRAM(vramValues, vram); + + char correctValues[8][8] = { + {0, 7, 7, 7, 7, 7, 0, 0}, + {7, 2, 6, 8, 6, 2, 7, 0}, + {7, 2, 2, 2, 2, 2, 7, 0}, + {0, 7, 7, 7, 7, 7, 0, 0}, + {0, 0, 0, 4, 0, 0, 0, 0}, + {3, 5, 0, 5, 0, 5, 3, 0}, + {0, 3, 4, 5, 4, 3, 0, 0}, + {0, 0, 3, 3, 3, 0, 0, 0} + }; + + int i = 0; + int j = 0; + for (const auto &row : correctValues) { + for (const auto &refValue : row) { + CHECK(tileRenderer.getPixelReferenceFromTileRow(i, j++) == refValue); + } + j = 0; + i += 2; + } +} + +TEST_CASE("getPixelReferenceFromTileRow 8bpp", "[PPU][TileRenderer]") +{ + ComSquare::Ram::Ram vram(100, static_cast(0), "vramTest"); + ComSquare::Ram::Ram cgram(512, static_cast(0), "cgramTest"); + ComSquare::PPU::TileRenderer tileRenderer(vram, cgram); + + tileRenderer.setBpp(8); + + std::vector vramValues { + "0C7C5CA0C0BC3C001010964038282018", + "0000020002007C000000820044003800", + "007C44927C82007C1010D6D67C7C3838", + "00002800000000000000000000000000" + }; + + fillRAM(vramValues, vram); + uint8_t correctValues[8][8] = { + {0x00, 0x22, 0x22, 0x22, 0x23, 0x23, 0x00, 0x00}, + {0x22, 0x11, 0x42, 0x21, 0x41, 0x11, 0x24, 0x00}, + {0x23, 0x11, 0x12, 0x12, 0x12, 0x12, 0x24, 0x00}, + {0x00, 0x24, 0x25, 0x25, 0x25, 0x25, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x00}, + {0x35, 0x32, 0x00, 0x31, 0x00, 0x31, 0x35, 0x00}, + {0x00, 0x34, 0x33, 0x31, 0x33, 0x34, 0x00, 0x00}, + {0x00, 0x00, 0x35, 0x36, 0x36, 0x00, 0x00, 0x00}, + }; + + + int i = 0; + int j = 0; + for (const auto &row : correctValues) { + for (const auto &refValue : row) { + CHECK(tileRenderer.getPixelReferenceFromTileRow(i, j++) == refValue); + } + j = 0; + i += 2; + } +} + +TEST_CASE("getPixelReferenceFromTile 2bpp", "[PPU][TileRenderer]") +{ + ComSquare::Ram::Ram vram(10, static_cast(0), "vramTest"); + ComSquare::Ram::Ram cgram(512, static_cast(0), "cgramTest"); + ComSquare::PPU::TileRenderer tileRenderer(vram, cgram); + + tileRenderer.setBpp(2); + + vram.write(2, 0xD6); + vram.write(3, 0x00); + + CHECK(tileRenderer.getPixelReferenceFromTile(2, 0) == 1); + CHECK(tileRenderer.getPixelReferenceFromTile(2, 1) == 1); + CHECK(tileRenderer.getPixelReferenceFromTile(2, 2) == 0); + CHECK(tileRenderer.getPixelReferenceFromTile(2, 3) == 1); + CHECK(tileRenderer.getPixelReferenceFromTile(2, 4) == 0); + CHECK(tileRenderer.getPixelReferenceFromTile(2, 5) == 1); + CHECK(tileRenderer.getPixelReferenceFromTile(2, 6) == 1); + CHECK(tileRenderer.getPixelReferenceFromTile(2, 7) == 0); +} + +TEST_CASE("getPixelReferenceFromTile 4bpp", "[PPU][TileRenderer]") +{ + ComSquare::Ram::Ram vram(40, static_cast(0), "vramTest"); + ComSquare::Ram::Ram cgram(512, static_cast(0), "cgramTest"); + ComSquare::PPU::TileRenderer tileRenderer(vram, cgram); + + tileRenderer.setBpp(4); + + std::vector vramValues { + "7C7C82EE82FE7C7C", + "0000D68254443838", + "7C00AA1082007C00", + "1000540038000000" + }; + + fillRAM(vramValues, vram); + + char correctValues[8][8] = { + {0, 7, 7, 7, 7, 7, 0, 0}, + {7, 2, 6, 8, 6, 2, 7, 0}, + {7, 2, 2, 2, 2, 2, 7, 0}, + {0, 7, 7, 7, 7, 7, 0, 0}, + {0, 0, 0, 4, 0, 0, 0, 0}, + {3, 5, 0, 5, 0, 5, 3, 0}, + {0, 3, 4, 5, 4, 3, 0, 0}, + {0, 0, 3, 3, 3, 0, 0, 0} + }; + + int i = 0; + int j = 0; + for (const auto &row : correctValues) { + for (const auto &refValue : row) { + CHECK(tileRenderer.getPixelReferenceFromTile(i, j++) == refValue); + } + } +} + +TEST_CASE("getPixelReferenceFromTile 8bpp", "[PPU][TileRenderer]") +{ + ComSquare::Ram::Ram vram(100, static_cast(0), "vramTest"); + ComSquare::Ram::Ram cgram(512, static_cast(0), "cgramTest"); + ComSquare::PPU::TileRenderer tileRenderer(vram, cgram); + + tileRenderer.setBpp(8); + + std::vector vramValues { + "0C7C5CA0C0BC3C001010964038282018", + "0000020002007C000000820044003800", + "007C44927C82007C1010D6D67C7C3838", + "00002800000000000000000000000000" + }; + + fillRAM(vramValues, vram); + uint8_t correctValues[8][8] = { + {0x00, 0x22, 0x22, 0x22, 0x23, 0x23, 0x00, 0x00}, + {0x22, 0x11, 0x42, 0x21, 0x41, 0x11, 0x24, 0x00}, + {0x23, 0x11, 0x12, 0x12, 0x12, 0x12, 0x24, 0x00}, + {0x00, 0x24, 0x25, 0x25, 0x25, 0x25, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x00}, + {0x35, 0x32, 0x00, 0x31, 0x00, 0x31, 0x35, 0x00}, + {0x00, 0x34, 0x33, 0x31, 0x33, 0x34, 0x00, 0x00}, + {0x00, 0x00, 0x35, 0x36, 0x36, 0x00, 0x00, 0x00}, + }; + + + int i = 0; + int j = 0; + for (const auto &row : correctValues) { + for (const auto &refValue : row) { + CHECK(tileRenderer.getPixelReferenceFromTile(i, j++) == refValue); + } + } +} + +TEST_CASE("getPalette 2bpp", "[PPU][TileRenderer]") +{ + ComSquare::Ram::Ram vram(10, static_cast(0), "vramTest"); + ComSquare::Ram::Ram cgram(512, static_cast(0), "cgramTest"); + ComSquare::PPU::TileRenderer tileRenderer(vram, cgram); + + tileRenderer.setBpp(2); + + std::vector cgram_dump = { + 0xCE, 0x69, 0xDF, 0x63, 0xDE, 0x16, 0x8B, 0x00, 0x00, 0x00, 0xBF, 0x67, 0x98, 0x42, 0x0E, 0x15, 0x00, 0x00, + 0xBF, 0x4B, 0xDD, 0x01, 0xCB, 0x0C, 0x00, 0x00, 0xDF, 0x55, 0x3C, 0x0C, 0x8B, 0x10, 0xCE, 0x69, 0x95, 0x7A, + 0x74, 0x76, 0x53, 0x72, 0x32, 0x6E, 0x11, 0x6A, 0xF0, 0x69, 0xEF, 0x69, 0xDC, 0x7B, 0x16, 0x67, 0x73, 0x5A, + 0x0F, 0x42, 0x4C, 0x31, 0xE9, 0x24, 0x76, 0x21, 0x34, 0x19, 0xCE, 0x69, 0xDF, 0x57, 0x3C, 0x47, 0xDB, 0x3A, + 0x78, 0x2E, 0x16, 0x26, 0xB4, 0x19, 0x52, 0x11, 0xA9, 0x08, 0xBE, 0x3A, 0x1B, 0x26, 0x99, 0x21, 0x76, 0x21, + 0x34, 0x19, 0x0F, 0x0D, 0xEB, 0x10, 0xCE, 0x69, 0xC7, 0x18, 0xBC, 0x3E, 0x1B, 0x2E, 0x96, 0x4E, 0x2B, 0x41, + 0xCD, 0x10, 0x39, 0x57, 0xF2, 0x35, 0x8E, 0x29, 0x09, 0x1D, 0x16, 0x26, 0x8A, 0x10, 0xE9, 0x34, 0x76, 0x21, + 0x34, 0x19, 0xDF, 0x57, 0xB2, 0x64, 0xAF, 0x54, 0xCE, 0x3C, 0x57, 0x22, 0x71, 0x19, 0xCC, 0x20, 0xF7, 0x51, + 0xCB, 0x0C, 0x15, 0x75, 0xB9, 0x21, 0x76, 0x21, 0x54, 0x1D, 0x12, 0x15, 0xF0, 0x10, 0xCD, 0x0C, 0xCE, 0x69, + 0x5F, 0x3B, 0xDC, 0x3A, 0x3B, 0x26, 0xB7, 0x19, 0x53, 0x15, 0xED, 0x10, 0xA9, 0x0C, 0xFE, 0x59, 0x5B, 0x41, + 0xF6, 0x34, 0xB3, 0x28, 0x90, 0x20, 0x6E, 0x18, 0x76, 0x21, 0x34, 0x19, 0xCE, 0x69, 0xDE, 0x5F, 0xFC, 0x3A, + 0x79, 0x26, 0xF5, 0x19, 0x71, 0x15, 0x0B, 0x11, 0xC8, 0x0C, 0x94, 0x5F, 0x29, 0x3F, 0x69, 0x3A, 0xA9, 0x35, + 0x47, 0x2D, 0x04, 0x2D, 0x76, 0x21, 0x34, 0x19, 0xCE, 0x69, 0x17, 0x2E, 0x37, 0x2E, 0xF5, 0x25, 0xB3, 0x21, + 0x71, 0x21, 0x2F, 0x25, 0x6E, 0x35, 0x11, 0x6A, 0xEE, 0x08, 0xB0, 0x51, 0xD2, 0x18, 0xD0, 0x1C, 0x11, 0x0D, + 0x7A, 0x36, 0x0E, 0x21, 0x0B, 0x46, 0x37, 0x2E, 0xBA, 0x42, 0x7D, 0x63, 0xCF, 0x04, 0x38, 0x09, 0x1C, 0x2A, + 0xDC, 0x4A, 0x1C, 0x14, 0x28, 0x44, 0x6F, 0x58, 0x52, 0x6D, 0x91, 0x1D, 0x2C, 0x11, 0xA8, 0x14, 0x00, 0x00, + 0x48, 0x2D, 0x6A, 0x00, 0xCE, 0x08, 0x78, 0x0D, 0xFC, 0x15, 0xBE, 0x1E, 0x5C, 0x63, 0x33, 0x46, 0x0A, 0x6D, + 0x46, 0x30, 0xBA, 0x14, 0xB1, 0x75, 0x13, 0x0D, 0x0C, 0x08, 0xC9, 0x38, 0x00, 0x00, 0xF0, 0x31, 0xAA, 0x04, + 0xE0, 0x14, 0x80, 0x05, 0x00, 0x53, 0x80, 0x73, 0x9C, 0x7F, 0x10, 0x00, 0x3C, 0x0C, 0xDF, 0x55, 0xFC, 0x26, + 0x9E, 0x5B, 0x5C, 0x32, 0xD7, 0x21, 0x12, 0x11, 0x8E, 0x08, 0xCE, 0x69, 0xDF, 0x57, 0x3C, 0x47, 0xDB, 0x3A, + 0x78, 0x2E, 0x16, 0x26, 0xB4, 0x19, 0x52, 0x11, 0x88, 0x08, 0xBE, 0x3A, 0x1B, 0x26, 0x99, 0x21, 0x76, 0x21, + 0x34, 0x19, 0x0F, 0x0D, 0xEB, 0x10, 0x30, 0x15, 0x0A, 0x0D, 0x2C, 0x11, 0x4D, 0x19, 0xF3, 0x25, 0x35, 0x36, + 0xB9, 0x4A, 0x74, 0x42, 0x19, 0x57, 0xF0, 0x31, 0x9C, 0x73, 0xCC, 0x00, 0x10, 0x01, 0x76, 0x01, 0x3E, 0x03, + 0x00, 0x00, 0xEA, 0x29, 0xE6, 0x00, 0x07, 0x09, 0x29, 0x11, 0x8C, 0x19, 0xEE, 0x21, 0x50, 0x2A, 0xB2, 0x32, + 0x34, 0x43, 0xA0, 0x01, 0x40, 0x02, 0xE1, 0x16, 0x80, 0x2B, 0x9C, 0x73, 0x00, 0x00, 0x00, 0x00, 0x88, 0x29, + 0x6F, 0x21, 0x98, 0x3E, 0x7D, 0x63, 0xED, 0x04, 0xEF, 0x08, 0x56, 0x15, 0x5C, 0x2E, 0xDD, 0x3E, 0x3C, 0x4F, + 0xF1, 0x00, 0x1C, 0x00, 0x3B, 0x01, 0x9E, 0x02, 0xA5, 0x14, 0x00, 0x00, 0x88, 0x29, 0xD3, 0x2D, 0x4D, 0x19, + 0xF9, 0x52, 0xAB, 0x08, 0x56, 0x15, 0x5C, 0x2E, 0x35, 0x32, 0x0D, 0x00, 0x05, 0x28, 0x66, 0x40, 0x9A, 0x10, + 0xC9, 0x0C, 0xD2, 0x25, 0xA5, 0x14, 0x00, 0x00 + }; + + fillRAM(cgram_dump, cgram); + uint16_t correctValues[64][4] = { + {0x69ce, 0x63df, 0x16de, 0x008b}, + {0x0000, 0x67bf, 0x4298, 0x150e}, + {0x0000, 0x4bbf, 0x01dd, 0x0ccb}, + {0x0000, 0x55df, 0x0c3c, 0x108b}, + {0x69ce, 0x7a95, 0x7674, 0x7253}, + {0x6e32, 0x6a11, 0x69f0, 0x69ef}, + {0x7bdc, 0x6716, 0x5a73, 0x420f}, + {0x314c, 0x24e9, 0x2176, 0x1934}, + {0x69ce, 0x57df, 0x473c, 0x3adb}, + {0x2e78, 0x2616, 0x19b4, 0x1152}, + {0x08a9, 0x3abe, 0x261b, 0x2199}, + {0x2176, 0x1934, 0x0d0f, 0x10eb}, + {0x69ce, 0x18c7, 0x3ebc, 0x2e1b}, + {0x4e96, 0x412b, 0x10cd, 0x5739}, + {0x35f2, 0x298e, 0x1d09, 0x2616}, + {0x108a, 0x34e9, 0x2176, 0x1934}, + {0x57df, 0x64b2, 0x54af, 0x3cce}, + {0x2257, 0x1971, 0x20cc, 0x51f7}, + {0x0ccb, 0x7515, 0x21b9, 0x2176}, + {0x1d54, 0x1512, 0x10f0, 0x0ccd}, + {0x69ce, 0x3b5f, 0x3adc, 0x263b}, + {0x19b7, 0x1553, 0x10ed, 0x0ca9}, + {0x59fe, 0x415b, 0x34f6, 0x28b3}, + {0x2090, 0x186e, 0x2176, 0x1934}, + {0x69ce, 0x5fde, 0x3afc, 0x2679}, + {0x19f5, 0x1571, 0x110b, 0x0cc8}, + {0x5f94, 0x3f29, 0x3a69, 0x35a9}, + {0x2d47, 0x2d04, 0x2176, 0x1934}, + {0x69ce, 0x2e17, 0x2e37, 0x25f5}, + {0x21b3, 0x2171, 0x252f, 0x356e}, + {0x6a11, 0x08ee, 0x51b0, 0x18d2}, + {0x1cd0, 0x0d11, 0x367a, 0x210e}, + {0x460b, 0x2e37, 0x42ba, 0x637d}, + {0x04cf, 0x0938, 0x2a1c, 0x4adc}, + {0x141c, 0x4428, 0x586f, 0x6d52}, + {0x1d91, 0x112c, 0x14a8, 0x0000}, + {0x2d48, 0x006a, 0x08ce, 0x0d78}, + {0x15fc, 0x1ebe, 0x635c, 0x4633}, + {0x6d0a, 0x3046, 0x14ba, 0x75b1}, + {0x0d13, 0x080c, 0x38c9, 0x0000}, + {0x31f0, 0x04aa, 0x14e0, 0x0580}, + {0x5300, 0x7380, 0x7f9c, 0x0010}, + {0x0c3c, 0x55df, 0x26fc, 0x5b9e}, + {0x325c, 0x21d7, 0x1112, 0x088e}, + {0x69ce, 0x57df, 0x473c, 0x3adb}, + {0x2e78, 0x2616, 0x19b4, 0x1152}, + {0x0888, 0x3abe, 0x261b, 0x2199}, + {0x2176, 0x1934, 0x0d0f, 0x10eb}, + {0x1530, 0x0d0a, 0x112c, 0x194d}, + {0x25f3, 0x3635, 0x4ab9, 0x4274}, + {0x5719, 0x31f0, 0x739c, 0x00cc}, + {0x0110, 0x0176, 0x033e, 0x0000}, + {0x29ea, 0x00e6, 0x0907, 0x1129}, + {0x198c, 0x21ee, 0x2a50, 0x32b2}, + {0x4334, 0x01a0, 0x0240, 0x16e1}, + {0x2b80, 0x739c, 0x0000, 0x0000}, + {0x2988, 0x216f, 0x3e98, 0x637d}, + {0x04ed, 0x08ef, 0x1556, 0x2e5c}, + {0x3edd, 0x4f3c, 0x00f1, 0x001c}, + {0x013b, 0x029e, 0x14a5, 0x0000}, + {0x2988, 0x2dd3, 0x194d, 0x52f9}, + {0x08ab, 0x1556, 0x2e5c, 0x3235}, + {0x000d, 0x2805, 0x4066, 0x109a}, + {0x0cc9, 0x25d2, 0x14a5, 0x0000} + }; + + int i = 0; + int j = 0; + for (const auto &row : correctValues) { + const auto &palette = tileRenderer.getPalette(j++); + for (const auto &refValue : row) { + CHECK(palette.at(i++) == refValue); + } + i = 0; + } +} + +TEST_CASE("getPalette 4bpp", "[PPU][TileRenderer]") +{ + ComSquare::Ram::Ram vram(10, static_cast(0), "vramTest"); + ComSquare::Ram::Ram cgram(512, static_cast(0), "cgramTest"); + ComSquare::PPU::TileRenderer tileRenderer(vram, cgram); + + tileRenderer.setBpp(4); + + std::vector cgram_dump = { + 0xCE, 0x69, 0xDF, 0x63, 0xDE, 0x16, 0x8B, 0x00, 0x00, 0x00, 0xBF, 0x67, 0x98, 0x42, 0x0E, 0x15, 0x00, 0x00, + 0xBF, 0x4B, 0xDD, 0x01, 0xCB, 0x0C, 0x00, 0x00, 0xDF, 0x55, 0x3C, 0x0C, 0x8B, 0x10, 0xCE, 0x69, 0x95, 0x7A, + 0x74, 0x76, 0x53, 0x72, 0x32, 0x6E, 0x11, 0x6A, 0xF0, 0x69, 0xEF, 0x69, 0xDC, 0x7B, 0x16, 0x67, 0x73, 0x5A, + 0x0F, 0x42, 0x4C, 0x31, 0xE9, 0x24, 0x76, 0x21, 0x34, 0x19, 0xCE, 0x69, 0xDF, 0x57, 0x3C, 0x47, 0xDB, 0x3A, + 0x78, 0x2E, 0x16, 0x26, 0xB4, 0x19, 0x52, 0x11, 0xA9, 0x08, 0xBE, 0x3A, 0x1B, 0x26, 0x99, 0x21, 0x76, 0x21, + 0x34, 0x19, 0x0F, 0x0D, 0xEB, 0x10, 0xCE, 0x69, 0xC7, 0x18, 0xBC, 0x3E, 0x1B, 0x2E, 0x96, 0x4E, 0x2B, 0x41, + 0xCD, 0x10, 0x39, 0x57, 0xF2, 0x35, 0x8E, 0x29, 0x09, 0x1D, 0x16, 0x26, 0x8A, 0x10, 0xE9, 0x34, 0x76, 0x21, + 0x34, 0x19, 0xDF, 0x57, 0xB2, 0x64, 0xAF, 0x54, 0xCE, 0x3C, 0x57, 0x22, 0x71, 0x19, 0xCC, 0x20, 0xF7, 0x51, + 0xCB, 0x0C, 0x15, 0x75, 0xB9, 0x21, 0x76, 0x21, 0x54, 0x1D, 0x12, 0x15, 0xF0, 0x10, 0xCD, 0x0C, 0xCE, 0x69, + 0x5F, 0x3B, 0xDC, 0x3A, 0x3B, 0x26, 0xB7, 0x19, 0x53, 0x15, 0xED, 0x10, 0xA9, 0x0C, 0xFE, 0x59, 0x5B, 0x41, + 0xF6, 0x34, 0xB3, 0x28, 0x90, 0x20, 0x6E, 0x18, 0x76, 0x21, 0x34, 0x19, 0xCE, 0x69, 0xDE, 0x5F, 0xFC, 0x3A, + 0x79, 0x26, 0xF5, 0x19, 0x71, 0x15, 0x0B, 0x11, 0xC8, 0x0C, 0x94, 0x5F, 0x29, 0x3F, 0x69, 0x3A, 0xA9, 0x35, + 0x47, 0x2D, 0x04, 0x2D, 0x76, 0x21, 0x34, 0x19, 0xCE, 0x69, 0x17, 0x2E, 0x37, 0x2E, 0xF5, 0x25, 0xB3, 0x21, + 0x71, 0x21, 0x2F, 0x25, 0x6E, 0x35, 0x11, 0x6A, 0xEE, 0x08, 0xB0, 0x51, 0xD2, 0x18, 0xD0, 0x1C, 0x11, 0x0D, + 0x7A, 0x36, 0x0E, 0x21, 0x0B, 0x46, 0x37, 0x2E, 0xBA, 0x42, 0x7D, 0x63, 0xCF, 0x04, 0x38, 0x09, 0x1C, 0x2A, + 0xDC, 0x4A, 0x1C, 0x14, 0x28, 0x44, 0x6F, 0x58, 0x52, 0x6D, 0x91, 0x1D, 0x2C, 0x11, 0xA8, 0x14, 0x00, 0x00, + 0x48, 0x2D, 0x6A, 0x00, 0xCE, 0x08, 0x78, 0x0D, 0xFC, 0x15, 0xBE, 0x1E, 0x5C, 0x63, 0x33, 0x46, 0x0A, 0x6D, + 0x46, 0x30, 0xBA, 0x14, 0xB1, 0x75, 0x13, 0x0D, 0x0C, 0x08, 0xC9, 0x38, 0x00, 0x00, 0xF0, 0x31, 0xAA, 0x04, + 0xE0, 0x14, 0x80, 0x05, 0x00, 0x53, 0x80, 0x73, 0x9C, 0x7F, 0x10, 0x00, 0x3C, 0x0C, 0xDF, 0x55, 0xFC, 0x26, + 0x9E, 0x5B, 0x5C, 0x32, 0xD7, 0x21, 0x12, 0x11, 0x8E, 0x08, 0xCE, 0x69, 0xDF, 0x57, 0x3C, 0x47, 0xDB, 0x3A, + 0x78, 0x2E, 0x16, 0x26, 0xB4, 0x19, 0x52, 0x11, 0x88, 0x08, 0xBE, 0x3A, 0x1B, 0x26, 0x99, 0x21, 0x76, 0x21, + 0x34, 0x19, 0x0F, 0x0D, 0xEB, 0x10, 0x30, 0x15, 0x0A, 0x0D, 0x2C, 0x11, 0x4D, 0x19, 0xF3, 0x25, 0x35, 0x36, + 0xB9, 0x4A, 0x74, 0x42, 0x19, 0x57, 0xF0, 0x31, 0x9C, 0x73, 0xCC, 0x00, 0x10, 0x01, 0x76, 0x01, 0x3E, 0x03, + 0x00, 0x00, 0xEA, 0x29, 0xE6, 0x00, 0x07, 0x09, 0x29, 0x11, 0x8C, 0x19, 0xEE, 0x21, 0x50, 0x2A, 0xB2, 0x32, + 0x34, 0x43, 0xA0, 0x01, 0x40, 0x02, 0xE1, 0x16, 0x80, 0x2B, 0x9C, 0x73, 0x00, 0x00, 0x00, 0x00, 0x88, 0x29, + 0x6F, 0x21, 0x98, 0x3E, 0x7D, 0x63, 0xED, 0x04, 0xEF, 0x08, 0x56, 0x15, 0x5C, 0x2E, 0xDD, 0x3E, 0x3C, 0x4F, + 0xF1, 0x00, 0x1C, 0x00, 0x3B, 0x01, 0x9E, 0x02, 0xA5, 0x14, 0x00, 0x00, 0x88, 0x29, 0xD3, 0x2D, 0x4D, 0x19, + 0xF9, 0x52, 0xAB, 0x08, 0x56, 0x15, 0x5C, 0x2E, 0x35, 0x32, 0x0D, 0x00, 0x05, 0x28, 0x66, 0x40, 0x9A, 0x10, + 0xC9, 0x0C, 0xD2, 0x25, 0xA5, 0x14, 0x00, 0x00 + }; + + fillRAM(cgram_dump, cgram); + uint16_t correctValues[16][16] = { + {0x69ce, 0x63df, 0x16de, 0x008b, 0x0000, 0x67bf, 0x4298, 0x150e, 0x0000, 0x4bbf, 0x01dd, 0x0ccb, 0x0000, 0x55df, 0x0c3c, 0x108b}, + {0x69ce, 0x7a95, 0x7674, 0x7253, 0x6e32, 0x6a11, 0x69f0, 0x69ef, 0x7bdc, 0x6716, 0x5a73, 0x420f, 0x314c, 0x24e9, 0x2176, 0x1934}, + {0x69ce, 0x57df, 0x473c, 0x3adb, 0x2e78, 0x2616, 0x19b4, 0x1152, 0x08a9, 0x3abe, 0x261b, 0x2199, 0x2176, 0x1934, 0x0d0f, 0x10eb}, + {0x69ce, 0x18c7, 0x3ebc, 0x2e1b, 0x4e96, 0x412b, 0x10cd, 0x5739, 0x35f2, 0x298e, 0x1d09, 0x2616, 0x108a, 0x34e9, 0x2176, 0x1934}, + {0x57df, 0x64b2, 0x54af, 0x3cce, 0x2257, 0x1971, 0x20cc, 0x51f7, 0x0ccb, 0x7515, 0x21b9, 0x2176, 0x1d54, 0x1512, 0x10f0, 0x0ccd}, + {0x69ce, 0x3b5f, 0x3adc, 0x263b, 0x19b7, 0x1553, 0x10ed, 0x0ca9, 0x59fe, 0x415b, 0x34f6, 0x28b3, 0x2090, 0x186e, 0x2176, 0x1934}, + {0x69ce, 0x5fde, 0x3afc, 0x2679, 0x19f5, 0x1571, 0x110b, 0x0cc8, 0x5f94, 0x3f29, 0x3a69, 0x35a9, 0x2d47, 0x2d04, 0x2176, 0x1934}, + {0x69ce, 0x2e17, 0x2e37, 0x25f5, 0x21b3, 0x2171, 0x252f, 0x356e, 0x6a11, 0x08ee, 0x51b0, 0x18d2, 0x1cd0, 0x0d11, 0x367a, 0x210e}, + {0x460b, 0x2e37, 0x42ba, 0x637d, 0x04cf, 0x0938, 0x2a1c, 0x4adc, 0x141c, 0x4428, 0x586f, 0x6d52, 0x1d91, 0x112c, 0x14a8, 0x0000}, + {0x2d48, 0x006a, 0x08ce, 0x0d78, 0x15fc, 0x1ebe, 0x635c, 0x4633, 0x6d0a, 0x3046, 0x14ba, 0x75b1, 0x0d13, 0x080c, 0x38c9, 0x0000}, + {0x31f0, 0x04aa, 0x14e0, 0x0580, 0x5300, 0x7380, 0x7f9c, 0x0010, 0x0c3c, 0x55df, 0x26fc, 0x5b9e, 0x325c, 0x21d7, 0x1112, 0x088e}, + {0x69ce, 0x57df, 0x473c, 0x3adb, 0x2e78, 0x2616, 0x19b4, 0x1152, 0x0888, 0x3abe, 0x261b, 0x2199, 0x2176, 0x1934, 0x0d0f, 0x10eb}, + {0x1530, 0x0d0a, 0x112c, 0x194d, 0x25f3, 0x3635, 0x4ab9, 0x4274, 0x5719, 0x31f0, 0x739c, 0x00cc, 0x0110, 0x0176, 0x033e, 0x0000}, + {0x29ea, 0x00e6, 0x0907, 0x1129, 0x198c, 0x21ee, 0x2a50, 0x32b2, 0x4334, 0x01a0, 0x0240, 0x16e1, 0x2b80, 0x739c, 0x0000, 0x0000}, + {0x2988, 0x216f, 0x3e98, 0x637d, 0x04ed, 0x08ef, 0x1556, 0x2e5c, 0x3edd, 0x4f3c, 0x00f1, 0x001c, 0x013b, 0x029e, 0x14a5, 0x0000}, + {0x2988, 0x2dd3, 0x194d, 0x52f9, 0x08ab, 0x1556, 0x2e5c, 0x3235, 0x000d, 0x2805, 0x4066, 0x109a, 0x0cc9, 0x25d2, 0x14a5, 0x0000} + }; + + int i = 0; + int j = 0; + for (const auto &row : correctValues) { + const auto &palette = tileRenderer.getPalette(j++); + for (const auto &refValue : row) { + CHECK(palette.at(i++) == refValue); + } + i = 0; + } +} + +TEST_CASE("getPalette 8bpp", "[PPU][TileRenderer]") +{ + ComSquare::Ram::Ram vram(10, static_cast(0), "vramTest"); + ComSquare::Ram::Ram cgram(512, static_cast(0), "cgramTest"); + ComSquare::PPU::TileRenderer tileRenderer(vram, cgram); + + tileRenderer.setBpp(8); + + std::vector cgram_dump = { + 0xCE, 0x69, 0xDF, 0x63, 0xDE, 0x16, 0x8B, 0x00, 0x00, 0x00, 0xBF, 0x67, 0x98, 0x42, 0x0E, 0x15, 0x00, 0x00, + 0xBF, 0x4B, 0xDD, 0x01, 0xCB, 0x0C, 0x00, 0x00, 0xDF, 0x55, 0x3C, 0x0C, 0x8B, 0x10, 0xCE, 0x69, 0x95, 0x7A, + 0x74, 0x76, 0x53, 0x72, 0x32, 0x6E, 0x11, 0x6A, 0xF0, 0x69, 0xEF, 0x69, 0xDC, 0x7B, 0x16, 0x67, 0x73, 0x5A, + 0x0F, 0x42, 0x4C, 0x31, 0xE9, 0x24, 0x76, 0x21, 0x34, 0x19, 0xCE, 0x69, 0xDF, 0x57, 0x3C, 0x47, 0xDB, 0x3A, + 0x78, 0x2E, 0x16, 0x26, 0xB4, 0x19, 0x52, 0x11, 0xA9, 0x08, 0xBE, 0x3A, 0x1B, 0x26, 0x99, 0x21, 0x76, 0x21, + 0x34, 0x19, 0x0F, 0x0D, 0xEB, 0x10, 0xCE, 0x69, 0xC7, 0x18, 0xBC, 0x3E, 0x1B, 0x2E, 0x96, 0x4E, 0x2B, 0x41, + 0xCD, 0x10, 0x39, 0x57, 0xF2, 0x35, 0x8E, 0x29, 0x09, 0x1D, 0x16, 0x26, 0x8A, 0x10, 0xE9, 0x34, 0x76, 0x21, + 0x34, 0x19, 0xDF, 0x57, 0xB2, 0x64, 0xAF, 0x54, 0xCE, 0x3C, 0x57, 0x22, 0x71, 0x19, 0xCC, 0x20, 0xF7, 0x51, + 0xCB, 0x0C, 0x15, 0x75, 0xB9, 0x21, 0x76, 0x21, 0x54, 0x1D, 0x12, 0x15, 0xF0, 0x10, 0xCD, 0x0C, 0xCE, 0x69, + 0x5F, 0x3B, 0xDC, 0x3A, 0x3B, 0x26, 0xB7, 0x19, 0x53, 0x15, 0xED, 0x10, 0xA9, 0x0C, 0xFE, 0x59, 0x5B, 0x41, + 0xF6, 0x34, 0xB3, 0x28, 0x90, 0x20, 0x6E, 0x18, 0x76, 0x21, 0x34, 0x19, 0xCE, 0x69, 0xDE, 0x5F, 0xFC, 0x3A, + 0x79, 0x26, 0xF5, 0x19, 0x71, 0x15, 0x0B, 0x11, 0xC8, 0x0C, 0x94, 0x5F, 0x29, 0x3F, 0x69, 0x3A, 0xA9, 0x35, + 0x47, 0x2D, 0x04, 0x2D, 0x76, 0x21, 0x34, 0x19, 0xCE, 0x69, 0x17, 0x2E, 0x37, 0x2E, 0xF5, 0x25, 0xB3, 0x21, + 0x71, 0x21, 0x2F, 0x25, 0x6E, 0x35, 0x11, 0x6A, 0xEE, 0x08, 0xB0, 0x51, 0xD2, 0x18, 0xD0, 0x1C, 0x11, 0x0D, + 0x7A, 0x36, 0x0E, 0x21, 0x0B, 0x46, 0x37, 0x2E, 0xBA, 0x42, 0x7D, 0x63, 0xCF, 0x04, 0x38, 0x09, 0x1C, 0x2A, + 0xDC, 0x4A, 0x1C, 0x14, 0x28, 0x44, 0x6F, 0x58, 0x52, 0x6D, 0x91, 0x1D, 0x2C, 0x11, 0xA8, 0x14, 0x00, 0x00, + 0x48, 0x2D, 0x6A, 0x00, 0xCE, 0x08, 0x78, 0x0D, 0xFC, 0x15, 0xBE, 0x1E, 0x5C, 0x63, 0x33, 0x46, 0x0A, 0x6D, + 0x46, 0x30, 0xBA, 0x14, 0xB1, 0x75, 0x13, 0x0D, 0x0C, 0x08, 0xC9, 0x38, 0x00, 0x00, 0xF0, 0x31, 0xAA, 0x04, + 0xE0, 0x14, 0x80, 0x05, 0x00, 0x53, 0x80, 0x73, 0x9C, 0x7F, 0x10, 0x00, 0x3C, 0x0C, 0xDF, 0x55, 0xFC, 0x26, + 0x9E, 0x5B, 0x5C, 0x32, 0xD7, 0x21, 0x12, 0x11, 0x8E, 0x08, 0xCE, 0x69, 0xDF, 0x57, 0x3C, 0x47, 0xDB, 0x3A, + 0x78, 0x2E, 0x16, 0x26, 0xB4, 0x19, 0x52, 0x11, 0x88, 0x08, 0xBE, 0x3A, 0x1B, 0x26, 0x99, 0x21, 0x76, 0x21, + 0x34, 0x19, 0x0F, 0x0D, 0xEB, 0x10, 0x30, 0x15, 0x0A, 0x0D, 0x2C, 0x11, 0x4D, 0x19, 0xF3, 0x25, 0x35, 0x36, + 0xB9, 0x4A, 0x74, 0x42, 0x19, 0x57, 0xF0, 0x31, 0x9C, 0x73, 0xCC, 0x00, 0x10, 0x01, 0x76, 0x01, 0x3E, 0x03, + 0x00, 0x00, 0xEA, 0x29, 0xE6, 0x00, 0x07, 0x09, 0x29, 0x11, 0x8C, 0x19, 0xEE, 0x21, 0x50, 0x2A, 0xB2, 0x32, + 0x34, 0x43, 0xA0, 0x01, 0x40, 0x02, 0xE1, 0x16, 0x80, 0x2B, 0x9C, 0x73, 0x00, 0x00, 0x00, 0x00, 0x88, 0x29, + 0x6F, 0x21, 0x98, 0x3E, 0x7D, 0x63, 0xED, 0x04, 0xEF, 0x08, 0x56, 0x15, 0x5C, 0x2E, 0xDD, 0x3E, 0x3C, 0x4F, + 0xF1, 0x00, 0x1C, 0x00, 0x3B, 0x01, 0x9E, 0x02, 0xA5, 0x14, 0x00, 0x00, 0x88, 0x29, 0xD3, 0x2D, 0x4D, 0x19, + 0xF9, 0x52, 0xAB, 0x08, 0x56, 0x15, 0x5C, 0x2E, 0x35, 0x32, 0x0D, 0x00, 0x05, 0x28, 0x66, 0x40, 0x9A, 0x10, + 0xC9, 0x0C, 0xD2, 0x25, 0xA5, 0x14, 0x00, 0x00 + }; + + fillRAM(cgram_dump, cgram); + uint16_t correctValues[1][256] = { + {0x69ce, 0x63df, 0x16de, 0x008b, 0x0000, 0x67bf, 0x4298, 0x150e, 0x0000, 0x4bbf, 0x01dd, 0x0ccb, 0x0000, 0x55df, 0x0c3c, 0x108b, + 0x69ce, 0x7a95, 0x7674, 0x7253, 0x6e32, 0x6a11, 0x69f0, 0x69ef, 0x7bdc, 0x6716, 0x5a73, 0x420f, 0x314c, 0x24e9, 0x2176, 0x1934, + 0x69ce, 0x57df, 0x473c, 0x3adb, 0x2e78, 0x2616, 0x19b4, 0x1152, 0x08a9, 0x3abe, 0x261b, 0x2199, 0x2176, 0x1934, 0x0d0f, 0x10eb, + 0x69ce, 0x18c7, 0x3ebc, 0x2e1b, 0x4e96, 0x412b, 0x10cd, 0x5739, 0x35f2, 0x298e, 0x1d09, 0x2616, 0x108a, 0x34e9, 0x2176, 0x1934, + 0x57df, 0x64b2, 0x54af, 0x3cce, 0x2257, 0x1971, 0x20cc, 0x51f7, 0x0ccb, 0x7515, 0x21b9, 0x2176, 0x1d54, 0x1512, 0x10f0, 0x0ccd, + 0x69ce, 0x3b5f, 0x3adc, 0x263b, 0x19b7, 0x1553, 0x10ed, 0x0ca9, 0x59fe, 0x415b, 0x34f6, 0x28b3, 0x2090, 0x186e, 0x2176, 0x1934, + 0x69ce, 0x5fde, 0x3afc, 0x2679, 0x19f5, 0x1571, 0x110b, 0x0cc8, 0x5f94, 0x3f29, 0x3a69, 0x35a9, 0x2d47, 0x2d04, 0x2176, 0x1934, + 0x69ce, 0x2e17, 0x2e37, 0x25f5, 0x21b3, 0x2171, 0x252f, 0x356e, 0x6a11, 0x08ee, 0x51b0, 0x18d2, 0x1cd0, 0x0d11, 0x367a, 0x210e, + 0x460b, 0x2e37, 0x42ba, 0x637d, 0x04cf, 0x0938, 0x2a1c, 0x4adc, 0x141c, 0x4428, 0x586f, 0x6d52, 0x1d91, 0x112c, 0x14a8, 0x0000, + 0x2d48, 0x006a, 0x08ce, 0x0d78, 0x15fc, 0x1ebe, 0x635c, 0x4633, 0x6d0a, 0x3046, 0x14ba, 0x75b1, 0x0d13, 0x080c, 0x38c9, 0x0000, + 0x31f0, 0x04aa, 0x14e0, 0x0580, 0x5300, 0x7380, 0x7f9c, 0x0010, 0x0c3c, 0x55df, 0x26fc, 0x5b9e, 0x325c, 0x21d7, 0x1112, 0x088e, + 0x69ce, 0x57df, 0x473c, 0x3adb, 0x2e78, 0x2616, 0x19b4, 0x1152, 0x0888, 0x3abe, 0x261b, 0x2199, 0x2176, 0x1934, 0x0d0f, 0x10eb, + 0x1530, 0x0d0a, 0x112c, 0x194d, 0x25f3, 0x3635, 0x4ab9, 0x4274, 0x5719, 0x31f0, 0x739c, 0x00cc, 0x0110, 0x0176, 0x033e, 0x0000, + 0x29ea, 0x00e6, 0x0907, 0x1129, 0x198c, 0x21ee, 0x2a50, 0x32b2, 0x4334, 0x01a0, 0x0240, 0x16e1, 0x2b80, 0x739c, 0x0000, 0x0000, + 0x2988, 0x216f, 0x3e98, 0x637d, 0x04ed, 0x08ef, 0x1556, 0x2e5c, 0x3edd, 0x4f3c, 0x00f1, 0x001c, 0x013b, 0x029e, 0x14a5, 0x0000, + 0x2988, 0x2dd3, 0x194d, 0x52f9, 0x08ab, 0x1556, 0x2e5c, 0x3235, 0x000d, 0x2805, 0x4066, 0x109a, 0x0cc9, 0x25d2, 0x14a5, 0x0000} + }; + + int i = 0; + int j = 0; + for (const auto &row : correctValues) { + const auto &palette = tileRenderer.getPalette(j++); + for (const auto &refValue : row) { + CHECK(palette.at(i++) == refValue); + } + i = 0; + } +} + +TEST_CASE("render 2bpp", "[PPU][TileRenderer]") +{ + ComSquare::Ram::Ram vram(100, static_cast(0), "vramTest"); + ComSquare::Ram::Ram cgram(512, static_cast(0), "cgramTest"); + ComSquare::PPU::TileRenderer tileRenderer(vram, cgram); + + tileRenderer.setBpp(2); + tileRenderer.setPaletteIndex(1); + + std::vector vramValues { + "7C00BA7C827C7C00", + "1000D6007C003800" + }; + + std::vector cgramValues = { + 0xCE, 0x69, 0xDF, 0x63, 0xDE, 0x16, 0x8B, 0x00, + 0x00, 0x00, 0xBF, 0x67, 0x98, 0x42, 0x0E, 0x15 + }; + //{0x3600, 0x67bf, 0x4298, 0x150e}, +// 0 FFEFCE C6A584 734229 + uint32_t correctValues[8][8] = { + {0x00000000, 0xFFEFCEFF, 0xFFEFCEFF, 0xFFEFCEFF, 0xFFEFCEFF, 0xFFEFCEFF, 0x00000000, 0x00000000}, + {0xFFEFCEFF, 0xC6A584FF, 0x734229FF, 0x734229FF, 0x734229FF, 0xC6A584FF, 0xFFEFCEFF, 0x00000000}, + {0xFFEFCEFF, 0xC6A584FF, 0xC6A584FF, 0xC6A584FF, 0xC6A584FF, 0xC6A584FF, 0xFFEFCEFF, 0x00000000}, + {0x00000000, 0xFFEFCEFF, 0xFFEFCEFF, 0xFFEFCEFF, 0xFFEFCEFF, 0xFFEFCEFF, 0x00000000, 0x00000000}, + {0x00000000, 0x00000000, 0x00000000, 0xFFEFCEFF, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0xFFEFCEFF, 0xFFEFCEFF, 0x00000000, 0xFFEFCEFF, 0x00000000, 0xFFEFCEFF, 0xFFEFCEFF, 0x00000000}, + {0x00000000, 0xFFEFCEFF, 0xFFEFCEFF, 0xFFEFCEFF, 0xFFEFCEFF, 0xFFEFCEFF, 0x00000000, 0x00000000}, + {0x00000000, 0x00000000, 0xFFEFCEFF, 0xFFEFCEFF, 0xFFEFCEFF, 0x00000000, 0x00000000, 0x00000000} + }; + + fillRAM(vramValues, vram); + fillRAM(cgramValues, cgram); + + tileRenderer.render(0); + + int i = 0; + int j = 0; + for (const auto &row : tileRenderer.buffer) { + for (const auto &pixel : row) { + CHECK(correctValues[i][j++] == pixel); + } + j = 0; + i++; + } +} + +TEST_CASE("render 4bpp", "[PPU][TileRenderer]") +{ + ComSquare::Ram::Ram vram(100, static_cast(0), "vramTest"); + ComSquare::Ram::Ram cgram(512, static_cast(0), "cgramTest"); + ComSquare::PPU::TileRenderer tileRenderer(vram, cgram); + + tileRenderer.setBpp(4); + tileRenderer.setPaletteIndex(0); + + std::vector vramValues { + "7C7C82EE82FE7C7C", + "0000D68254443838", + "7C00AA1082007C00", + "1000540038000000" + }; + + std::vector cgramValues = { + 0x00, 0x00, + 0x00, 0x00, + 0xFF, 0x7F, + 0x21, 0x0E, + 0xE2, 0x0E, + 0xE3, 0x0F, + 0x79, 0x0C, + 0xFD, 0x11, + 0x9F, 0x07, + 0xBF, 0x4B, + 0xDD, 0x01, + 0xCB, 0x0C, + 0x00, 0x00, + 0xDF, 0x55, + 0x3C, 0x0C, + 0x8B, 0x10, + 0xCE, 0x69, + 0x95, 0x7A + }; + + uint32_t correctValues[8][8] = { + {0x00000000, 0xef7b21ff, 0xef7b21ff, 0xef7b21ff, 0xef7b21ff, 0xef7b21ff, 0x00000000, 0x00000000}, + {0xef7b21ff, 0xffffffff, 0xce1818ff, 0xffe708ff, 0xce1818ff, 0xffffffff, 0xef7b21ff, 0x00000000}, + {0xef7b21ff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xef7b21ff, 0x00000000}, + {0x00000000, 0xef7b21ff, 0xef7b21ff, 0xef7b21ff, 0xef7b21ff, 0xef7b21ff, 0x00000000, 0x00000000}, + {0x00000000, 0x00000000, 0x00000000, 0x10bd18ff, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x088c18ff, 0x18ff18ff, 0x00000000, 0x18ff18ff, 0x00000000, 0x18ff18ff, 0x088c18ff, 0x00000000}, + {0x00000000, 0x088c18ff, 0x10bd18ff, 0x18ff18ff, 0x10bd18ff, 0x088c18ff, 0x00000000, 0x00000000}, + {0x00000000, 0x00000000, 0x088c18ff, 0x088c18ff, 0x088c18ff, 0x00000000, 0x00000000, 0x00000000} + }; + + fillRAM(vramValues, vram); + fillRAM(cgramValues, cgram); + + tileRenderer.render(0); + + int i = 0; + int j = 0; + for (const auto &row : tileRenderer.buffer) { + for (const auto &pixel : row) { + CHECK(correctValues[i][j++] == pixel); + } + j = 0; + i++; + } +} + +TEST_CASE("render 8bpp", "[PPU][TileRenderer]") +{ + ComSquare::Ram::Ram vram(100, static_cast(0), "vramTest"); + ComSquare::Ram::Ram cgram(512, static_cast(0), "cgramTest"); + ComSquare::PPU::TileRenderer tileRenderer(vram, cgram); + std::srand(std::time(nullptr)); + + tileRenderer.setBpp(8); + tileRenderer.setPaletteIndex(0); + + std::vector vramValues{ + "0C7C5CA0C0BC3C001010964038282018", + "0000020002007C000000820044003800", + "007C44927C82007C1010D6D67C7C3838", + "00002800000000000000000000000000" + }; + + uint8_t correctReferences[8][8] = { + {0x00, 0x22, 0x22, 0x22, 0x23, 0x23, 0x00, 0x00}, + {0x22, 0x11, 0x42, 0x21, 0x41, 0x11, 0x24, 0x00}, + {0x23, 0x11, 0x12, 0x12, 0x12, 0x12, 0x24, 0x00}, + {0x00, 0x24, 0x25, 0x25, 0x25, 0x25, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x00}, + {0x35, 0x32, 0x00, 0x31, 0x00, 0x31, 0x35, 0x00}, + {0x00, 0x34, 0x33, 0x31, 0x33, 0x34, 0x00, 0x00}, + {0x00, 0x00, 0x35, 0x36, 0x36, 0x00, 0x00, 0x00}, + }; + + for (int i = 0; i < 512; i++) { + cgram.write(i, std::rand()); + } + + uint32_t correctValues[8][8] = {{0}}; + + int k = 0; + int l = 0; + for (const auto &row : correctReferences) { + for (const auto &reference : row) { + if (reference == 0) { + l++; + continue; + } + correctValues[k][l++] = ComSquare::PPU::Utils::CGRAMColorToRGBA( + cgram.read(reference * 2) + | (cgram.read((reference * 2) + 1) << 8) + ); + } + l = 0; + k++; + } + + fillRAM(vramValues, vram); + tileRenderer.render(0); + + int i = 0; + int j = 0; + for (const auto &row : tileRenderer.buffer) { + for (const auto &pixel : row) { + CHECK(correctValues[i][j++] == pixel); + } + j = 0; + i++; + } +} \ No newline at end of file diff --git a/utils/pythonvram.py b/utils/pythonvram.py new file mode 100755 index 0000000..e5babb5 --- /dev/null +++ b/utils/pythonvram.py @@ -0,0 +1,80 @@ +#!/usr/bin/env python3 + + +# python script to convert tile indexes to correct values in vram depending on the bpp + +def getVRAMValue2bpp(nums): + vramValues = [] + + valHex1 = 0 + valHex2 = 0 + for i in nums: + valHex1 = 0 + valHex2 = 0 + shift = 7 + for j in i: + valHex1 += (j & 1) << shift + valHex2 += ((j & 2) >> 1) << shift + shift -= 1 + + vramValues.append((valHex1 << 8) + valHex2) + + padding = 4 + for i in vramValues: + print(f"{i:0{padding}X}", end=' ') + print("") + + +def getVRAMValue4bpp(nums): + getVRAMValue2bpp(extract2BppBitPlans(nums, 0)) + getVRAMValue2bpp(extract2BppBitPlans(nums, 1)) + + +def getVRAMValue8bpp(nums): + getVRAMValue2bpp(extract2BppBitPlans(nums, 0)) + getVRAMValue2bpp(extract2BppBitPlans(nums, 1)) + getVRAMValue2bpp(extract2BppBitPlans(nums, 2)) + getVRAMValue2bpp(extract2BppBitPlans(nums, 3)) + + +def extract2BppBitPlans(nums, index): + high = [] + for i in nums: + high.append([(j & (0x3 << (2 * index))) >> (index * 2) for j in i]) + return high + + +result2 = [ + [0, 1, 1, 1, 1, 1, 0, 0], + [1, 2, 3, 3, 3, 2, 1, 0], + [1, 2, 3, 3, 3, 2, 1, 0], + [0, 1, 1, 1, 1, 1, 0, 0], + [0, 0, 0, 1, 0, 0, 0, 0], + [1, 1, 0, 1, 0, 1, 1, 0], + [0, 1, 1, 1, 1, 1, 0, 0], + [0, 0, 1, 1, 1, 0, 0, 0] +] + +result2 = [ + [0, 7, 7, 7, 7, 7, 0, 0], + [7, 2, 6, 8, 6, 2, 7, 0], + [7, 2, 2, 2, 2, 2, 7, 0], + [0, 7, 7, 7, 7, 7, 0, 0], + [0, 0, 0, 4, 0, 0, 0, 0], + [3, 5, 0, 5, 0, 5, 3, 0], + [0, 3, 4, 5, 4, 3, 0, 0], + [0, 0, 3, 3, 3, 0, 0, 0], +] + +result2 = [ + [0x00, 0x22, 0x22, 0x22, 0x23, 0x23, 0x00, 0x00], + [0x22, 0x11, 0x42, 0x21, 0x41, 0x11, 0x24, 0x00], + [0x23, 0x11, 0x12, 0x12, 0x12, 0x12, 0x24, 0x00], + [0x00, 0x24, 0x25, 0x25, 0x25, 0x25, 0x00, 0x00], + [0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x00], + [0x35, 0x32, 0x00, 0x31, 0x00, 0x31, 0x35, 0x00], + [0x00, 0x34, 0x33, 0x31, 0x33, 0x34, 0x00, 0x00], + [0x00, 0x00, 0x35, 0x36, 0x36, 0x00, 0x00, 0x00] +] + +getVRAMValue8bpp(result2)