Merge pull request #50 from AnonymusRaccoon/PPU

Ppu Cleanup and memory optimization (16Mo less mem)
This commit is contained in:
Zoe Roux
2021-07-13 23:31:43 +02:00
committed by GitHub
15 changed files with 1598 additions and 762 deletions

View File

@@ -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)

View File

@@ -8,6 +8,7 @@
#include <string>
#include <QtWidgets/QTableWidget>
#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<int>((color & 0xFF000000) >> 24),
static_cast<int>((color & 0x00FF0000) >> 16),
static_cast<int>((color & 0x0000FF00) >> 8));
}
}

View File

@@ -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<int> offset = this->_ppu.getBgScroll(this->_bgNumber);
//Vector2<int> offset = this->_ppu.getBgScroll(this->_bgNumber);
Vector2i offset = {0, 0};
this->backgroundSize.x =
static_cast<int>(this->_tileMapsConfig.x) * this->_characterNbPixels.x * NbCharacterWidth;
(static_cast<int>(this->_tileMapMirroring.x) + 1) * this->_characterNbPixels.x * NbCharacterWidth;
this->backgroundSize.y =
static_cast<int>(this->_tileMapsConfig.y) * this->_characterNbPixels.y * NbCharacterHeight;
(static_cast<int>(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<int> 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<int> 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<int> 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<int> 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<bool> tileMaps)
void Background::setTileMapMirroring(Vector2<bool> 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];
}
}

View File

@@ -6,10 +6,12 @@
#include <array>
#include <vector>
#include <iostream>
#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<bool> _tileMapsConfig;
//! @brief The number of pixels of a character (x: width, y:height)
Vector2<bool> _tileMapMirroring;
//! @brief The number of pixels of a character (x: width, y: height)
Vector2<int> _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<int> 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<int> 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<int> offset);
public:
//! @brief The size of the background (x, y)
Vector2<unsigned> backgroundSize;
//! @brief The output buffer (pixels are written on it)
std::array<std::array<uint32_t, 1024>, 1024> buffer;
//! @brief The buffer of tile priority level
std::array<std::array<bool, 64>, 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<bool> tileMaps);
void setTileMapMirroring(Vector2<bool> 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 <int levelLow, int levelHigh, std::size_t DEST_SIZE_X, std::size_t DEST_SIZE_Y>
static void mergeBackgroundBuffer(std::array<std::array<uint32_t, DEST_SIZE_Y>, DEST_SIZE_X> &bufferDest,
std::array<std::array<unsigned char, DEST_SIZE_Y>, 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

View File

@@ -6,6 +6,7 @@
#include <bitset>
#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<bool>(this->_registers._bgsc[addr - PpuRegisters::bg1sc].tilemapHorizontalMirroring),
static_cast<bool>(this->_registers._bgsc[addr - PpuRegisters::bg1sc].tilemapVerticalMirroring)});
this->_backgrounds[addr - PpuRegisters::bg1sc + 1].setTilemaps(
this->_backgrounds[addr - PpuRegisters::bg1sc].setTileMapMirroring(
{static_cast<bool>(this->_registers._bgsc[addr - PpuRegisters::bg1sc].tilemapHorizontalMirroring),
static_cast<bool>(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<bool> PPU::getBackgroundMirroring(int bgNumber) const
{
Vector2<bool> backgroundSize(false, false);
backgroundSize.y = this->_registers._bgsc[bgNumber - 1].tilemapVerticalMirroring;
backgroundSize.x = this->_registers._bgsc[bgNumber - 1].tilemapHorizontalMirroring;
return backgroundSize;
return {
static_cast<bool>(this->_registers._bgsc[bgNumber - 1].tilemapHorizontalMirroring),
static_cast<bool>(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;

View File

@@ -10,8 +10,10 @@
#include "Renderer/IRenderer.hpp"
#include "Ram/Ram.hpp"
#include "Models/Vector2.hpp"
#include <algorithm>
#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];
// <to work>
//! @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;
// <READ registers>
//! @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<std::array<uint32_t, 1024>, 1024> _mainScreen;
std::array<std::array<uint8_t, 1024>, 1024> _mainScreenLevelMap;
//! @brief Sub Screen buffer
std::array<std::array<uint8_t, 1024>, 1024> _subScreenLevelMap;
std::array<std::array<uint32_t, 1024>, 1024> _subScreen;
//! @brief Final Screen buffer
std::array<std::array<uint32_t, 1024>, 1024> _screen;
@@ -624,22 +114,17 @@ namespace ComSquare::PPU
[[nodiscard]] Vector2<bool> getBackgroundMirroring(int bgNumber) const;
//! @brief Render the Main and sub screen correctly
void renderMainAndSubScreen();
//! @brief Add a bg buffer to another buffer
template <std::size_t DEST_SIZE_X, std::size_t DEST_SIZE_Y, std::size_t SRC_SIZE_X, std::size_t SRC_SIZE_Y>
void add_buffer(std::array<std::array<uint32_t, DEST_SIZE_Y>, DEST_SIZE_X> &bufferDest,
const std::array<std::array<uint32_t, SRC_SIZE_Y>, SRC_SIZE_X> &bufferSrc,
const Vector2<int> &offset = {0, 0})
//! @brief Add a bg to the sub and/or main screen
template<int levelLow, int levelHigh>
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<levelLow, levelHigh>(this->_mainScreen, this->_mainScreenLevelMap, bg);
}
if (this->_registers._t[1].raw & (1U << (bg.getBgNumber() - 1U))) {
Background::mergeBackgroundBuffer<levelLow, levelHigh>(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<int> getBgScroll(int bgNumber) const;
//! @brief Allow to look the value of each write register (used by Register debugger)
[[nodiscard]] const Registers &getWriteRegisters() const;
template <std::size_t SRC_SIZE_Y, std::size_t SRC_SIZE_X>
void add_buffer(const std::array<std::array<uint32_t, SRC_SIZE_Y>, SRC_SIZE_X> &buffer,
const Vector2<int> &offset = {0, 0})
{
for (auto &i : this->_screen)
i.fill(0XFF);
for (unsigned long i = 0; i < buffer.size(); i++) {
for (unsigned long j = 0; j < buffer[i].size(); j++) {
if (buffer[i][j] > 0xFF) // 0xFF correspond to a black pixel with full brightness
this->_screen[i + offset.x][j + offset.y] = buffer[i][j];
}
}
}
};
}

View File

@@ -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];
// <to work>
//! @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;
// <READ registers>
//! @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;
};
}

View File

@@ -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));
}
}

View File

@@ -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<uint8_t>((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 <std::size_t DEST_SIZE_Y, std::size_t DEST_SIZE_X, std::size_t SRC_SIZE_Y, std::size_t SRC_SIZE_X>
void merge2DArray(std::array<std::array<uint32_t, DEST_SIZE_X>, DEST_SIZE_Y> &bufferDest,
const std::array<std::array<uint32_t, SRC_SIZE_X>, SRC_SIZE_Y> &bufferSrc,
const Vector2<int> &offset = {0, 0})
const Vector2<int> &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 <std::size_t DEST_SIZE_X, std::size_t DEST_SIZE_Y, std::size_t SRC_SIZE_X, std::size_t SRC_SIZE_Y>
static void addBuffer(std::array<std::array<uint32_t, DEST_SIZE_Y>, DEST_SIZE_X> &bufferDest,
const std::array<std::array<uint32_t, SRC_SIZE_Y>, SRC_SIZE_X> &bufferSrc)
{
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++;
};
}
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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<uint16_t> 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

View File

@@ -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()

View File

@@ -0,0 +1,707 @@
//
// Created by cbihan on 08/07/2021.
//
#include <sstream>
#include <string>
#include <span>
#include <vector>
#include <catch2/catch_test_macros.hpp>
#include <iomanip>
#include "PPU/TileRenderer.hpp"
#include "Ram/Ram.hpp"
#include "PPU/Tile.hpp"
#include "PPU/PPUUtils.hpp"
void fillRAM(const std::vector<std::string> &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<int> &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<ComSquare::Component>(0), "vramTest");
ComSquare::Ram::Ram cgram(512, static_cast<ComSquare::Component>(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<ComSquare::Component>(0), "vramTest");
ComSquare::Ram::Ram cgram(512, static_cast<ComSquare::Component>(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<ComSquare::Component>(0), "vramTest");
ComSquare::Ram::Ram cgram(512, static_cast<ComSquare::Component>(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<ComSquare::Component>(0), "vramTest");
ComSquare::Ram::Ram cgram(512, static_cast<ComSquare::Component>(0), "cgramTest");
ComSquare::PPU::TileRenderer tileRenderer(vram, cgram);
tileRenderer.setBpp(4);
std::vector<std::string> 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<ComSquare::Component>(0), "vramTest");
ComSquare::Ram::Ram cgram(512, static_cast<ComSquare::Component>(0), "cgramTest");
ComSquare::PPU::TileRenderer tileRenderer(vram, cgram);
tileRenderer.setBpp(8);
std::vector<std::string> 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<ComSquare::Component>(0), "vramTest");
ComSquare::Ram::Ram cgram(512, static_cast<ComSquare::Component>(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<ComSquare::Component>(0), "vramTest");
ComSquare::Ram::Ram cgram(512, static_cast<ComSquare::Component>(0), "cgramTest");
ComSquare::PPU::TileRenderer tileRenderer(vram, cgram);
tileRenderer.setBpp(4);
std::vector<std::string> 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<ComSquare::Component>(0), "vramTest");
ComSquare::Ram::Ram cgram(512, static_cast<ComSquare::Component>(0), "cgramTest");
ComSquare::PPU::TileRenderer tileRenderer(vram, cgram);
tileRenderer.setBpp(8);
std::vector<std::string> 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<ComSquare::Component>(0), "vramTest");
ComSquare::Ram::Ram cgram(512, static_cast<ComSquare::Component>(0), "cgramTest");
ComSquare::PPU::TileRenderer tileRenderer(vram, cgram);
tileRenderer.setBpp(2);
std::vector<int> 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<ComSquare::Component>(0), "vramTest");
ComSquare::Ram::Ram cgram(512, static_cast<ComSquare::Component>(0), "cgramTest");
ComSquare::PPU::TileRenderer tileRenderer(vram, cgram);
tileRenderer.setBpp(4);
std::vector<int> 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<ComSquare::Component>(0), "vramTest");
ComSquare::Ram::Ram cgram(512, static_cast<ComSquare::Component>(0), "cgramTest");
ComSquare::PPU::TileRenderer tileRenderer(vram, cgram);
tileRenderer.setBpp(8);
std::vector<int> 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<ComSquare::Component>(0), "vramTest");
ComSquare::Ram::Ram cgram(512, static_cast<ComSquare::Component>(0), "cgramTest");
ComSquare::PPU::TileRenderer tileRenderer(vram, cgram);
tileRenderer.setBpp(2);
tileRenderer.setPaletteIndex(1);
std::vector<std::string> vramValues {
"7C00BA7C827C7C00",
"1000D6007C003800"
};
std::vector<int> 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<ComSquare::Component>(0), "vramTest");
ComSquare::Ram::Ram cgram(512, static_cast<ComSquare::Component>(0), "cgramTest");
ComSquare::PPU::TileRenderer tileRenderer(vram, cgram);
tileRenderer.setBpp(4);
tileRenderer.setPaletteIndex(0);
std::vector<std::string> vramValues {
"7C7C82EE82FE7C7C",
"0000D68254443838",
"7C00AA1082007C00",
"1000540038000000"
};
std::vector<int> 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<ComSquare::Component>(0), "vramTest");
ComSquare::Ram::Ram cgram(512, static_cast<ComSquare::Component>(0), "cgramTest");
ComSquare::PPU::TileRenderer tileRenderer(vram, cgram);
std::srand(std::time(nullptr));
tileRenderer.setBpp(8);
tileRenderer.setPaletteIndex(0);
std::vector<std::string> 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++;
}
}

80
utils/pythonvram.py Executable file
View File

@@ -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)