PPU: Offset per tile mode support (mode 2/4/6)
This commit is contained in:
parent
5a45665d74
commit
b9aedafd32
2 changed files with 83 additions and 10 deletions
90
Core/Ppu.cpp
90
Core/Ppu.cpp
|
@ -512,20 +512,37 @@ void Ppu::RenderTilemap()
|
|||
/* The start address for tiles on this row */
|
||||
uint16_t baseOffset = tilemapAddr + addrVerticalScrollingOffset + ((row & 0x1F) << 5);
|
||||
|
||||
uint16_t vScroll = config.VScroll;
|
||||
uint16_t hScroll = config.HScroll;
|
||||
|
||||
//"Offset per tile" mode (modes 2, 4 and 6 support this)
|
||||
bool offsetPerTileMode = (_bgMode & 0x03) == 2;
|
||||
|
||||
/* The current pixel x position (normally 0-255, but 0-511 in hi-res mode - even on subscreen, odd on main screen) */
|
||||
uint16_t realX;
|
||||
|
||||
/* The current column index (in terms of 8x8 or 16x16 tiles) */
|
||||
uint16_t column;
|
||||
|
||||
/* The tilemap address to read the tile data from */
|
||||
uint16_t addr;
|
||||
|
||||
for(int x = 0; x < 256; x++) {
|
||||
/* The current pixel x position (normally 0-255, but 0-511 in hi-res mode - even on subscreen, odd on main screen) */
|
||||
uint16_t realX;
|
||||
if(hiResMode) {
|
||||
realX = (x << 1) + (forMainScreen ? 1 : 0);
|
||||
} else {
|
||||
realX = x;
|
||||
}
|
||||
|
||||
/* The current column index (in terms of 8x8 or 16x16 tiles) */
|
||||
uint16_t column = (realX + config.HScroll) >> (largeTileWidth ? 4 : 3);
|
||||
|
||||
/* The tilemap address to read the tile data from */
|
||||
uint32_t addr = (baseOffset + (column & 0x1F) + (config.HorizontalMirroring ? ((column & 0x20) << 5) : 0)) << 1;
|
||||
if(offsetPerTileMode) {
|
||||
ProcessOffsetMode<layerIndex, largeTileWidth, largeTileHeight>(x, realX, realY, hScroll, vScroll, addr);
|
||||
|
||||
//Need to recalculate this because vScroll may change from one tile to another
|
||||
baseYOffset = (realY + vScroll) & 0x07;
|
||||
} else {
|
||||
column = (realX + hScroll) >> (largeTileWidth ? 4 : 3);
|
||||
addr = (baseOffset + (column & 0x1F) + (config.HorizontalMirroring ? ((column & 0x20) << 5) : 0)) << 1;
|
||||
}
|
||||
|
||||
//Skip pixels that were filled by previous layers (or that don't match the priority level currently being processed)
|
||||
if(forMainScreen) {
|
||||
|
@ -563,8 +580,8 @@ void Ppu::RenderTilemap()
|
|||
if(largeTileWidth || largeTileHeight) {
|
||||
tileIndex = (
|
||||
tileIndex +
|
||||
(largeTileHeight ? (((realY + config.VScroll) & 0x08) ? (vMirror ? 0 : 16) : (vMirror ? 16 : 0)) : 0) +
|
||||
(largeTileWidth ? (((realX + config.HScroll) & 0x08) ? (hMirror ? 0 : 1) : (hMirror ? 1 : 0)) : 0)
|
||||
(largeTileHeight ? (((realY + vScroll) & 0x08) ? (vMirror ? 0 : 16) : (vMirror ? 16 : 0)) : 0) +
|
||||
(largeTileWidth ? (((realX + hScroll) & 0x08) ? (hMirror ? 0 : 1) : (hMirror ? 1 : 0)) : 0)
|
||||
) & 0x3FF;
|
||||
}
|
||||
|
||||
|
@ -573,7 +590,7 @@ void Ppu::RenderTilemap()
|
|||
uint8_t yOffset = vMirror ? (7 - baseYOffset) : baseYOffset;
|
||||
uint16_t pixelStart = tileStart + yOffset * 2;
|
||||
|
||||
uint8_t xOffset = (realX + config.HScroll) & 0x07;
|
||||
uint8_t xOffset = (realX + hScroll) & 0x07;
|
||||
uint8_t shift = hMirror ? xOffset : (7 - xOffset);
|
||||
|
||||
uint16_t color = GetTilePixelColor<bpp>(pixelStart, shift);
|
||||
|
@ -604,6 +621,59 @@ void Ppu::RenderTilemap()
|
|||
}
|
||||
}
|
||||
|
||||
template<uint8_t layerIndex, bool largeTileWidth, bool largeTileHeight>
|
||||
void Ppu::ProcessOffsetMode(uint8_t x, uint16_t realX, uint16_t realY, uint16_t &hScroll, uint16_t &vScroll, uint16_t &addr)
|
||||
{
|
||||
constexpr uint16_t enableBit = layerIndex == 0 ? 0x2000 : 0x4000;
|
||||
LayerConfig &config = _layerConfig[layerIndex];
|
||||
|
||||
hScroll = config.HScroll;
|
||||
vScroll = config.VScroll;
|
||||
|
||||
//TODO: Check+fix behavior with 16x16 tiles
|
||||
//TODO: Test mode 4/6 behavior
|
||||
|
||||
if((realX + hScroll) & ~0x07) {
|
||||
//For all tiles after the first tile on the row, check if an active offset exists and use it
|
||||
uint16_t columnOffset = (((x - 8) & ~0x07) + (_layerConfig[2].HScroll & ~0x07)) >> 3;
|
||||
uint16_t rowOffset = (_layerConfig[2].VScroll >> 3);
|
||||
|
||||
uint16_t hOffsetAddr = _layerConfig[2].TilemapAddress + (columnOffset << 1) + (rowOffset << 6);
|
||||
|
||||
if(_bgMode == 4) {
|
||||
int16_t offsetValue = _vram[hOffsetAddr] | (_vram[hOffsetAddr + 1] << 8);
|
||||
|
||||
if((offsetValue & 0x8000) == 0 && (offsetValue & enableBit)) {
|
||||
hScroll = (hScroll & 0x07) | ((x & ~0x07) + (offsetValue & 0x3F8));
|
||||
}
|
||||
if((offsetValue & 0x8000) != 0 && (offsetValue & enableBit)) {
|
||||
vScroll = (offsetValue & 0x3FF);
|
||||
}
|
||||
} else {
|
||||
uint16_t vOffsetAddr = hOffsetAddr + 0x40;
|
||||
|
||||
int16_t hOffsetValue = _vram[hOffsetAddr] | (_vram[hOffsetAddr + 1] << 8);
|
||||
int16_t vOffsetValue = _vram[vOffsetAddr] | (_vram[vOffsetAddr + 1] << 8);
|
||||
|
||||
if(hOffsetValue & enableBit) {
|
||||
hScroll = (hScroll & 0x07) | ((x & ~0x07) + (hOffsetValue & 0x3F8));
|
||||
}
|
||||
if(vOffsetValue & enableBit) {
|
||||
vScroll = (vOffsetValue & 0x3FF);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Recalculate the tile's address based on the new scroll offsets
|
||||
uint16_t tilemapAddr = config.TilemapAddress >> 1;
|
||||
uint16_t offsetModeRow = (realY + vScroll) >> (largeTileHeight ? 4 : 3);
|
||||
uint16_t offsetModeColumn = (realX + hScroll) >> (largeTileWidth ? 4 : 3);
|
||||
|
||||
uint16_t addrVerticalScrollingOffset = config.VerticalMirroring ? ((offsetModeRow & 0x20) << (config.HorizontalMirroring ? 6 : 5)) : 0;
|
||||
uint16_t offsetModeBaseAddress = tilemapAddr + addrVerticalScrollingOffset + ((offsetModeRow & 0x1F) << 5);
|
||||
addr = (offsetModeBaseAddress + (offsetModeColumn & 0x1F) + (config.HorizontalMirroring ? ((offsetModeColumn & 0x20) << 5) : 0)) << 1;
|
||||
}
|
||||
|
||||
template<bool forMainScreen>
|
||||
bool Ppu::IsRenderRequired(uint8_t layerIndex)
|
||||
{
|
||||
|
|
|
@ -171,6 +171,9 @@ private:
|
|||
template<uint8_t layerIndex, uint8_t bpp, bool processHighPriority, bool forMainScreen, uint16_t basePaletteOffset, bool hiResMode, bool largeTileWidth, bool largeTileHeight, uint8_t activeWindowCount, bool applyMosaic, bool directColorMode>
|
||||
void RenderTilemap();
|
||||
|
||||
template<uint8_t layerIndex, bool largeTileWidth, bool largeTileHeight>
|
||||
void ProcessOffsetMode(uint8_t x, uint16_t realX, uint16_t realY, uint16_t &hScroll, uint16_t &vScroll, uint16_t &addr);
|
||||
|
||||
template<bool forMainScreen>
|
||||
__forceinline bool IsRenderRequired(uint8_t layerIndex);
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue