PPU: Mode 7 support
This commit is contained in:
parent
86326215fd
commit
39ae565aa1
3 changed files with 211 additions and 40 deletions
209
Core/Ppu.cpp
209
Core/Ppu.cpp
|
@ -9,12 +9,6 @@
|
|||
#include "VideoDecoder.h"
|
||||
#include "NotificationManager.h"
|
||||
|
||||
enum PixelFlags
|
||||
{
|
||||
Filled = 0x01,
|
||||
AllowColorMath = 0x02,
|
||||
};
|
||||
|
||||
Ppu::Ppu(shared_ptr<Console> console)
|
||||
{
|
||||
_console = console;
|
||||
|
@ -353,6 +347,17 @@ void Ppu::RenderMode4()
|
|||
RenderBgColor<forMainScreen>();
|
||||
}
|
||||
|
||||
template<bool forMainScreen>
|
||||
void Ppu::RenderMode7()
|
||||
{
|
||||
RenderSprites<3, forMainScreen>();
|
||||
RenderSprites<2, forMainScreen>();
|
||||
RenderSprites<1, forMainScreen>();
|
||||
RenderTilemapMode7<0, forMainScreen, false>();
|
||||
RenderSprites<0, forMainScreen>();
|
||||
RenderBgColor<forMainScreen>();
|
||||
}
|
||||
|
||||
void Ppu::RenderScanline()
|
||||
{
|
||||
_pixelsDrawn = 0;
|
||||
|
@ -408,9 +413,8 @@ void Ppu::RenderScanline()
|
|||
break;
|
||||
|
||||
case 7:
|
||||
MessageManager::Log("[Debug] Using mode 7");
|
||||
RenderBgColor<true>();
|
||||
RenderBgColor<false>();
|
||||
RenderMode7<true>();
|
||||
RenderMode7<false>();
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -532,6 +536,7 @@ void Ppu::RenderTilemap()
|
|||
uint16_t column = (x + config.HScroll) >> (largeTiles ? 4 : 3);
|
||||
uint32_t 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) {
|
||||
if(_rowPixelFlags[x] || ((uint8_t)processHighPriority != ((_vram[addr + 1] & 0x20) >> 5))) {
|
||||
continue;
|
||||
|
@ -543,7 +548,7 @@ void Ppu::RenderTilemap()
|
|||
}
|
||||
|
||||
if(activeWindowCount && ProcessMaskWindow<layerIndex>(activeWindowCount, x)) {
|
||||
//This pixel was masked
|
||||
//This pixel was masked, skip it
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -559,6 +564,7 @@ void Ppu::RenderTilemap()
|
|||
continue;
|
||||
}
|
||||
|
||||
//The pixel is empty, not clipped and not part of a mosaic pattern, process it
|
||||
bool vMirror = (_vram[addr + 1] & 0x80) != 0;
|
||||
bool hMirror = (_vram[addr + 1] & 0x40) != 0;
|
||||
|
||||
|
@ -603,27 +609,132 @@ void Ppu::RenderTilemap()
|
|||
if(color > 0) {
|
||||
uint8_t palette = (_vram[addr + 1] >> 2) & 0x07;
|
||||
uint16_t paletteRamOffset = basePaletteOffset + (palette * (1 << bpp) + color) * 2;
|
||||
uint16_t paletteColor = _cgram[paletteRamOffset] | (_cgram[paletteRamOffset + 1] << 8);
|
||||
|
||||
if(forMainScreen) {
|
||||
_currentBuffer[outBaseAddress | x] = paletteColor;
|
||||
_rowPixelFlags[x] = pixelFlags;
|
||||
if(applyMosaic && x % _mosaicSize == 0) {
|
||||
//This is the source for the mosaic pattern, store it for use in the next scanlines
|
||||
for(int i = 0; i < _mosaicSize && x + i < 256; i++) {
|
||||
_mosaicColor[x+i] = paletteColor;
|
||||
}
|
||||
}
|
||||
_pixelsDrawn++;
|
||||
DrawMainPixel<applyMosaic>(x, paletteRamOffset, pixelFlags);
|
||||
} else {
|
||||
_subScreenBuffer[x] = paletteColor;
|
||||
_subScreenFilled[x] = true;
|
||||
_subPixelsDrawn++;
|
||||
DrawSubPixel(x, paletteRamOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<uint8_t layerIndex, bool forMainScreen, bool applyMosaic>
|
||||
void Ppu::RenderTilemapMode7()
|
||||
{
|
||||
uint16_t realY = _mode7.VerticalMirroring ? (255 - _scanline) : _scanline;
|
||||
|
||||
if(forMainScreen) {
|
||||
if(_pixelsDrawn == 256 || ((_mainScreenLayers >> layerIndex) & 0x01) == 0) {
|
||||
//This screen is disabled, or we've drawn all pixels already
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if(_subPixelsDrawn == 256 || ((_subScreenLayers >> layerIndex) & 0x01) == 0) {
|
||||
//This screen is disabled, or we've drawn all pixels already
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr auto clip = [](int32_t val) { return (val & 0x2000) ? (val | ~0x3ff) : (val & 0x3ff); };
|
||||
|
||||
int32_t lutX[256];
|
||||
int32_t lutY[256];
|
||||
|
||||
int32_t hScroll = ((int32_t)_mode7.HScroll << 19) >> 19;
|
||||
int32_t vScroll = ((int32_t)_mode7.VScroll << 19) >> 19;
|
||||
int32_t centerX = ((int32_t)_mode7.CenterX << 19) >> 19;
|
||||
int32_t centerY = ((int32_t)_mode7.CenterY << 19) >> 19;
|
||||
|
||||
lutX[0] = (
|
||||
((_mode7.Matrix[0] * clip(hScroll - centerX)) & ~63) +
|
||||
((_mode7.Matrix[1] * realY) & ~63) +
|
||||
((_mode7.Matrix[1] * clip(vScroll - centerY)) & ~63) +
|
||||
(centerX << 8)
|
||||
);
|
||||
|
||||
lutY[0] = (
|
||||
((_mode7.Matrix[2] * clip(hScroll - centerX)) & ~63) +
|
||||
((_mode7.Matrix[3] * realY) & ~63) +
|
||||
((_mode7.Matrix[3] * clip(vScroll - centerY)) & ~63) +
|
||||
(centerY << 8)
|
||||
);
|
||||
|
||||
for(int x = 1; x < 256; x++) {
|
||||
lutX[x] = lutX[x - 1] + _mode7.Matrix[0];
|
||||
lutY[x] = lutY[x - 1] + _mode7.Matrix[2];
|
||||
}
|
||||
|
||||
uint8_t pixelFlags = PixelFlags::Filled | (((_colorMathEnabled >> layerIndex) & 0x01) ? PixelFlags::AllowColorMath : 0);
|
||||
|
||||
for(int x = 0; x < 256; x++) {
|
||||
uint16_t realX = _mode7.HorizontalMirroring ? (255 - x) : x;
|
||||
|
||||
if(forMainScreen) {
|
||||
if(_rowPixelFlags[x]) {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
if(_subScreenFilled[x]) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
int32_t xOffset = (lutX[realX] >> 8);
|
||||
int32_t yOffset = (lutY[realX] >> 8);
|
||||
|
||||
uint8_t tileMask = 0xFF;
|
||||
if(!_mode7.LargeMap) {
|
||||
yOffset &= 0x3FF;
|
||||
xOffset &= 0x3FF;
|
||||
} else {
|
||||
if(yOffset < 0 || yOffset > 0x3FF || xOffset < 0 || xOffset > 0x3FF) {
|
||||
if(_mode7.FillWithTile0) {
|
||||
tileMask = 0;
|
||||
} else {
|
||||
//Draw nothing for this pixel, we're outside the map
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t tileIndex = _vram[(((yOffset & ~0x07) << 4) | (xOffset >> 3)) << 1] & tileMask;
|
||||
uint16_t paletteRamOffset = (_vram[(((tileIndex << 6) + ((yOffset & 0x07) << 3) + (xOffset & 0x07)) << 1) + 1]) << 1;
|
||||
|
||||
if(paletteRamOffset > 0) {
|
||||
if(forMainScreen) {
|
||||
DrawMainPixel<applyMosaic>(x, paletteRamOffset, pixelFlags);
|
||||
} else {
|
||||
DrawSubPixel(x, paletteRamOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<bool applyMosaic>
|
||||
void Ppu::DrawMainPixel(uint8_t x, uint16_t paletteRamOffset, uint8_t flags)
|
||||
{
|
||||
_currentBuffer[(_scanline << 8) | x] = _cgram[paletteRamOffset] | (_cgram[paletteRamOffset + 1] << 8);
|
||||
_rowPixelFlags[x] = flags;
|
||||
|
||||
if(applyMosaic && x % _mosaicSize == 0) {
|
||||
uint16_t paletteColor = _cgram[paletteRamOffset] | (_cgram[paletteRamOffset + 1] << 8);
|
||||
//This is the source for the mosaic pattern, store it for use in the next scanlines
|
||||
for(int i = 0; i < _mosaicSize && x + i < 256; i++) {
|
||||
_mosaicColor[x + i] = paletteColor;
|
||||
}
|
||||
}
|
||||
|
||||
_pixelsDrawn++;
|
||||
}
|
||||
|
||||
void Ppu::DrawSubPixel(uint8_t x, uint16_t paletteRamOffset)
|
||||
{
|
||||
_subScreenBuffer[x] = _cgram[paletteRamOffset] | (_cgram[paletteRamOffset + 1] << 8);
|
||||
_subScreenFilled[x] = true;
|
||||
_subPixelsDrawn++;
|
||||
}
|
||||
|
||||
void Ppu::ApplyColorMath()
|
||||
{
|
||||
if(!_colorMathEnabled) {
|
||||
|
@ -791,9 +902,9 @@ void Ppu::LatchLocationValues()
|
|||
uint8_t Ppu::Read(uint16_t addr)
|
||||
{
|
||||
switch(addr) {
|
||||
case 0x2134: return ((int16_t)_mode7MatrixA * ((int16_t)_mode7MatrixB >> 8)) & 0xFF;
|
||||
case 0x2135: return (((int16_t)_mode7MatrixA * ((int16_t)_mode7MatrixB >> 8)) >> 8) & 0xFF;
|
||||
case 0x2136: return (((int16_t)_mode7MatrixA * ((int16_t)_mode7MatrixB >> 8)) >> 16) & 0xFF;
|
||||
case 0x2134: return ((int16_t)_mode7.Matrix[0] * ((int16_t)_mode7.Matrix[1] >> 8)) & 0xFF;
|
||||
case 0x2135: return (((int16_t)_mode7.Matrix[0] * ((int16_t)_mode7.Matrix[1] >> 8)) >> 8) & 0xFF;
|
||||
case 0x2136: return (((int16_t)_mode7.Matrix[0] * ((int16_t)_mode7.Matrix[1] >> 8)) >> 16) & 0xFF;
|
||||
|
||||
case 0x2137:
|
||||
//SLHV - Software Latch for H/V Counter
|
||||
|
@ -985,16 +1096,28 @@ void Ppu::Write(uint32_t addr, uint8_t value)
|
|||
break;
|
||||
|
||||
case 0x210D:
|
||||
//TODO Mode 7 portion of register
|
||||
//M7HOFS - Mode 7 BG Horizontal Scroll
|
||||
//BG1HOFS - BG1 Horizontal Scroll
|
||||
_mode7.HScroll = ((value << 8) | (_mode7.ValueLatch)) & 0x1FFF;
|
||||
_mode7.ValueLatch = value;
|
||||
//no break, keep executing to set the matching BG1 HScroll register, too
|
||||
|
||||
case 0x210F: case 0x2111: case 0x2113:
|
||||
//BGXHOFS - BG1/2/3/4 Horizontal Scroll
|
||||
_layerConfig[(addr - 0x210D) >> 1].HScroll = ((value << 8) | (_hvScrollLatchValue & ~0x07) | (_hScrollLatchValue & 0x07)) & 0x3FF;
|
||||
_hvScrollLatchValue = value;
|
||||
_hScrollLatchValue = value;
|
||||
break;
|
||||
|
||||
case 0x210E:
|
||||
//TODO Mode 7 portion of register
|
||||
//M7VOFS - Mode 7 BG Vertical Scroll
|
||||
//BG1VOFS - BG1 Vertical Scroll
|
||||
_mode7.VScroll = ((value << 8) | (_mode7.ValueLatch)) & 0x1FFF;
|
||||
_mode7.ValueLatch = value;
|
||||
//no break, keep executing to set the matching BG1 HScroll register, too
|
||||
|
||||
case 0x2110: case 0x2112: case 0x2114:
|
||||
//BGXVOFS - BG1/2/3/4 Vertical Scroll
|
||||
_layerConfig[(addr - 0x210E) >> 1].VScroll = ((value << 8) | _hvScrollLatchValue) & 0x3FF;
|
||||
_hvScrollLatchValue = value;
|
||||
break;
|
||||
|
@ -1041,16 +1164,30 @@ void Ppu::Write(uint32_t addr, uint8_t value)
|
|||
}
|
||||
break;
|
||||
|
||||
case 0x211B:
|
||||
//M7A - Mode 7 Matrix A (also used with $2134/6)
|
||||
_mode7MatrixA = (value << 8) | _mode7Latch;
|
||||
_mode7Latch = value;
|
||||
case 0x211A:
|
||||
//M7SEL - Mode 7 Settings
|
||||
_mode7.LargeMap = (value & 0x80) != 0;
|
||||
_mode7.FillWithTile0 = (value & 0x40) != 0;
|
||||
_mode7.HorizontalMirroring = (value & 0x01) != 0;
|
||||
_mode7.VerticalMirroring = (value & 0x02) != 0;
|
||||
break;
|
||||
|
||||
case 0x211C:
|
||||
//M7B - Mode 7 Matrix B (also used with $2134/6)
|
||||
_mode7MatrixB = (value << 8) | _mode7Latch;
|
||||
_mode7Latch = value;
|
||||
case 0x211B: case 0x211C: case 0x211D: case 0x211E:
|
||||
//M7A/B/C/D - Mode 7 Matrix A/B/C/D (A/B are also used with $2134/6)
|
||||
_mode7.Matrix[addr - 0x211B] = (value << 8) | _mode7.ValueLatch;
|
||||
_mode7.ValueLatch = value;
|
||||
break;
|
||||
|
||||
case 0x211F:
|
||||
//M7X - Mode 7 Center X
|
||||
_mode7.CenterX = ((value << 8) | _mode7.ValueLatch);
|
||||
_mode7.ValueLatch = value;
|
||||
break;
|
||||
|
||||
case 0x2120:
|
||||
//M7Y - Mode 7 Center Y
|
||||
_mode7.CenterY = ((value << 8) | _mode7.ValueLatch);
|
||||
_mode7.ValueLatch = value;
|
||||
break;
|
||||
|
||||
case 0x2121:
|
||||
|
|
17
Core/Ppu.h
17
Core/Ppu.h
|
@ -62,6 +62,8 @@ private:
|
|||
WindowMaskLogic _maskLogic[6];
|
||||
bool _windowMaskMain[5];
|
||||
bool _windowMaskSub[5];
|
||||
|
||||
Mode7Config _mode7;
|
||||
|
||||
uint8_t *_vram;
|
||||
uint16_t _vramAddress;
|
||||
|
@ -128,10 +130,6 @@ private:
|
|||
bool _verticalLocationToggle = false;
|
||||
bool _locationLatched = false;
|
||||
|
||||
uint16_t _mode7MatrixA = 0;
|
||||
uint16_t _mode7MatrixB = 0;
|
||||
uint8_t _mode7Latch = 0;
|
||||
|
||||
void EvaluateNextLineSprites();
|
||||
|
||||
template<uint8_t priority, bool forMainScreen>
|
||||
|
@ -152,6 +150,9 @@ private:
|
|||
template<bool forMainScreen>
|
||||
void RenderMode4();
|
||||
|
||||
template<bool forMainScreen>
|
||||
void RenderMode7();
|
||||
|
||||
void RenderScanline();
|
||||
|
||||
template<bool forMainScreen>
|
||||
|
@ -163,6 +164,14 @@ private:
|
|||
template<uint8_t layerIndex, uint8_t bpp, bool processHighPriority, bool forMainScreen, bool largeTiles, uint16_t basePaletteOffset, uint8_t activeWindowCount, bool applyMosaic>
|
||||
void RenderTilemap();
|
||||
|
||||
template<uint8_t layerIndex, bool forMainScreen, bool applyMosaic>
|
||||
void RenderTilemapMode7();
|
||||
|
||||
template<bool applyMosaic>
|
||||
__forceinline void DrawMainPixel(uint8_t x, uint16_t paletteRamOffset, uint8_t flags);
|
||||
|
||||
__forceinline void DrawSubPixel(uint8_t x, uint16_t paletteRamOffset);
|
||||
|
||||
void ApplyColorMath();
|
||||
void ApplyBrightness();
|
||||
|
||||
|
|
|
@ -22,6 +22,25 @@ struct LayerConfig
|
|||
bool LargeTiles;
|
||||
};
|
||||
|
||||
struct Mode7Config
|
||||
{
|
||||
int16_t HScroll;
|
||||
int16_t VScroll;
|
||||
|
||||
bool LargeMap;
|
||||
bool FillWithTile0;
|
||||
|
||||
bool HorizontalMirroring;
|
||||
bool VerticalMirroring;
|
||||
|
||||
int16_t CenterX;
|
||||
int16_t CenterY;
|
||||
|
||||
int16_t Matrix[4];
|
||||
|
||||
uint8_t ValueLatch;
|
||||
};
|
||||
|
||||
struct WindowConfig
|
||||
{
|
||||
bool ActiveLayers[6];
|
||||
|
@ -63,3 +82,9 @@ enum class ColorWindowMode
|
|||
InsideWindow = 2,
|
||||
Always = 3
|
||||
};
|
||||
|
||||
enum PixelFlags
|
||||
{
|
||||
Filled = 0x01,
|
||||
AllowColorMath = 0x02,
|
||||
};
|
Loading…
Add table
Reference in a new issue