PPU: Approximate OAM write behavior during HBlank (fixes Uniracers)
This commit is contained in:
parent
ec306ce406
commit
42379b26d1
2 changed files with 29 additions and 16 deletions
43
Core/Ppu.cpp
43
Core/Ppu.cpp
|
@ -235,6 +235,7 @@ void Ppu::EvaluateNextLineSprites()
|
|||
continue;
|
||||
}
|
||||
|
||||
info.Index = i >> 2;
|
||||
info.TileRow = (_oamRam[addr + 2] & 0xF0) >> 4;
|
||||
info.TileColumn = _oamRam[addr + 2] & 0x0F;
|
||||
|
||||
|
@ -277,6 +278,9 @@ void Ppu::EvaluateNextLineSprites()
|
|||
uint8_t row = (info.TileRow + rowOffset) & 0x0F;
|
||||
int prevColumnOffset = -1;
|
||||
|
||||
//Keep the last address the PPU used while rendering sprites (needed for Uniracers, which writes to OAM during rendering)
|
||||
_oamRenderAddress = 0x200 + (info.Index >> 2);
|
||||
|
||||
for(int x = std::max<int16_t>(info.X, 0); x < info.X + width && x < 256; x++) {
|
||||
uint8_t xOffset;
|
||||
int columnOffset;
|
||||
|
@ -1228,13 +1232,15 @@ uint8_t Ppu::Read(uint16_t addr)
|
|||
|
||||
case 0x2138: {
|
||||
//OAMDATAREAD - Data for OAM read
|
||||
//When trying to read/write during rendering, the internal address used by the PPU's sprite rendering is used
|
||||
uint16_t addr = (_forcedVblank || (_scanline >= (_overscanMode ? 240 : 225))) ? _internalOamAddress : _oamRenderAddress;
|
||||
uint8_t value;
|
||||
if(_internalOamAddress < 512) {
|
||||
value = _oamRam[_internalOamAddress];
|
||||
_console->ProcessPpuRead(_internalOamAddress, value, SnesMemoryType::SpriteRam);
|
||||
if(addr < 512) {
|
||||
value = _oamRam[addr];
|
||||
_console->ProcessPpuRead(addr, value, SnesMemoryType::SpriteRam);
|
||||
} else {
|
||||
value = _oamRam[0x200 | (_internalOamAddress & 0x1F)];
|
||||
_console->ProcessPpuRead(0x200 | (_internalOamAddress & 0x1F), value, SnesMemoryType::SpriteRam);
|
||||
value = _oamRam[0x200 | (addr & 0x1F)];
|
||||
_console->ProcessPpuRead(0x200 | (addr & 0x1F), value, SnesMemoryType::SpriteRam);
|
||||
}
|
||||
_internalOamAddress = (_internalOamAddress + 1) & 0x3FF;
|
||||
_ppu1OpenBus = value;
|
||||
|
@ -1382,20 +1388,24 @@ void Ppu::Write(uint32_t addr, uint8_t value)
|
|||
_enableOamPriority = (value & 0x80) != 0;
|
||||
break;
|
||||
|
||||
case 0x2104:
|
||||
if(_internalOamAddress < 512) {
|
||||
if(_internalOamAddress & 0x01) {
|
||||
_console->ProcessPpuWrite(_internalOamAddress - 1, _oamWriteBuffer, SnesMemoryType::SpriteRam);
|
||||
_oamRam[_internalOamAddress - 1] = _oamWriteBuffer;
|
||||
case 0x2104: {
|
||||
//When trying to read/write during rendering, the internal address used by the PPU's sprite rendering is used
|
||||
//This is approximated by _oamRenderAddress (but is not cycle accurate) - needed for Uniracers
|
||||
uint16_t addr = (_forcedVblank || (_scanline >= (_overscanMode ? 240 : 225))) ? _internalOamAddress : _oamRenderAddress;
|
||||
|
||||
if(addr < 512) {
|
||||
if(addr & 0x01) {
|
||||
_console->ProcessPpuWrite(addr - 1, _oamWriteBuffer, SnesMemoryType::SpriteRam);
|
||||
_oamRam[addr - 1] = _oamWriteBuffer;
|
||||
|
||||
_console->ProcessPpuWrite(_internalOamAddress, value, SnesMemoryType::SpriteRam);
|
||||
_oamRam[_internalOamAddress] = value;
|
||||
_console->ProcessPpuWrite(addr, value, SnesMemoryType::SpriteRam);
|
||||
_oamRam[addr] = value;
|
||||
} else {
|
||||
_oamWriteBuffer = value;
|
||||
}
|
||||
} else {
|
||||
uint16_t address = 0x200 | (_internalOamAddress & 0x1F);
|
||||
if((_internalOamAddress & 0x01) == 0) {
|
||||
uint16_t address = 0x200 | (addr & 0x1F);
|
||||
if((addr & 0x01) == 0) {
|
||||
_oamWriteBuffer = value;
|
||||
}
|
||||
_console->ProcessPpuWrite(address, value, SnesMemoryType::SpriteRam);
|
||||
|
@ -1403,7 +1413,8 @@ void Ppu::Write(uint32_t addr, uint8_t value)
|
|||
}
|
||||
_internalOamAddress = (_internalOamAddress + 1) & 0x3FF;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
case 0x2105:
|
||||
if(_bgMode != (value & 0x07)) {
|
||||
MessageManager::Log("[Debug] Entering mode: " + std::to_string(value & 0x07) + " (SL: " + std::to_string(_scanline) + ")");
|
||||
|
@ -1700,7 +1711,7 @@ void Ppu::Serialize(Serializer &s)
|
|||
_windowMaskSub[0], _windowMaskSub[1], _windowMaskSub[2], _windowMaskSub[3], _windowMaskSub[4],
|
||||
_mode7.CenterX, _mode7.CenterY, _mode7.ExtBgEnabled, _mode7.FillWithTile0, _mode7.HorizontalMirroring,
|
||||
_mode7.HScroll, _mode7.LargeMap, _mode7.Matrix[0], _mode7.Matrix[1], _mode7.Matrix[2], _mode7.Matrix[3],
|
||||
_mode7.ValueLatch, _mode7.VerticalMirroring, _mode7.VScroll
|
||||
_mode7.ValueLatch, _mode7.VerticalMirroring, _mode7.VScroll, _oamRenderAddress
|
||||
);
|
||||
|
||||
for(int i = 0; i < 4; i++) {
|
||||
|
|
|
@ -8,6 +8,7 @@ class InternalRegisters;
|
|||
|
||||
struct SpriteInfo
|
||||
{
|
||||
uint8_t Index;
|
||||
int16_t X;
|
||||
uint8_t Y;
|
||||
bool HorizontalMirror;
|
||||
|
@ -104,6 +105,7 @@ private:
|
|||
uint16_t _oamRamAddress = 0;
|
||||
bool _enableOamPriority = false;
|
||||
|
||||
uint16_t _oamRenderAddress = 0;
|
||||
uint16_t _internalOamAddress = 0;
|
||||
uint8_t _oamWriteBuffer = 0;
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue