PPU: Simulate the PPU's address bus a bit better (fixes Touhou Rououmu homebrew that detected Mesen as an emulator because of it)
This commit is contained in:
parent
76ad3946c0
commit
aad8b2bd47
6 changed files with 90 additions and 65 deletions
|
@ -686,6 +686,11 @@ void BaseMapper::SetNametable(uint8_t index, uint8_t nametableIndex)
|
|||
_nametableIndexes[index] = nametableIndex;
|
||||
|
||||
SetPpuMemoryMapping(0x2000 + index * 0x400, 0x2000 + (index + 1) * 0x400 - 1, GetNametable(nametableIndex));
|
||||
|
||||
//Mirror $2000-$2FFF to $3000-$3FFF, while keeping a distinction between the addresses
|
||||
//Previously, $3000-$3FFF was being "redirected" to $2000-$2FFF to avoid MMC3 IRQ issues (which is incorrect)
|
||||
//More info here: https://forums.nesdev.com/viewtopic.php?p=132145#p132145
|
||||
SetPpuMemoryMapping(0x3000 + index * 0x400, 0x3000 + (index + 1) * 0x400 - 1, GetNametable(nametableIndex));
|
||||
}
|
||||
|
||||
void BaseMapper::SetNametables(uint8_t nametable1Index, uint8_t nametable2Index, uint8_t nametable3Index, uint8_t nametable4Index)
|
||||
|
@ -790,16 +795,6 @@ void BaseMapper::WritePrgRam(uint16_t addr, uint8_t value)
|
|||
}
|
||||
}
|
||||
|
||||
void BaseMapper::ProcessVramAccess(uint16_t &addr)
|
||||
{
|
||||
addr &= 0x3FFF;
|
||||
if(addr >= 0x3000) {
|
||||
//Need to mirror 0x3000 writes to 0x2000, this appears to be how hardware behaves
|
||||
//Required for proper MMC3 IRQ timing in Burai Fighter
|
||||
addr -= 0x1000;
|
||||
}
|
||||
}
|
||||
|
||||
void BaseMapper::NotifyVRAMAddressChange(uint16_t addr)
|
||||
{
|
||||
//This is called when the VRAM addr on the PPU memory bus changes
|
||||
|
@ -818,7 +813,7 @@ uint8_t BaseMapper::InternalReadVRAM(uint16_t addr)
|
|||
|
||||
uint8_t BaseMapper::DebugReadVRAM(uint16_t addr, bool disableSideEffects)
|
||||
{
|
||||
ProcessVramAccess(addr);
|
||||
addr &= 0x3FFF;
|
||||
if(!disableSideEffects) {
|
||||
NotifyVRAMAddressChange(addr);
|
||||
}
|
||||
|
@ -832,7 +827,7 @@ uint8_t BaseMapper::MapperReadVRAM(uint16_t addr, MemoryOperationType operationT
|
|||
|
||||
void BaseMapper::DebugWriteVRAM(uint16_t addr, uint8_t value, bool disableSideEffects)
|
||||
{
|
||||
ProcessVramAccess(addr);
|
||||
addr &= 0x3FFF;
|
||||
if(disableSideEffects) {
|
||||
if(_chrPages[addr >> 8]) {
|
||||
//Always allow writes when side-effects are disabled
|
||||
|
@ -848,9 +843,7 @@ void BaseMapper::DebugWriteVRAM(uint16_t addr, uint8_t value, bool disableSideEf
|
|||
|
||||
void BaseMapper::WriteVRAM(uint16_t addr, uint8_t value)
|
||||
{
|
||||
ProcessVramAccess(addr);
|
||||
Debugger::ProcessVramWriteOperation(addr, value);
|
||||
NotifyVRAMAddressChange(addr);
|
||||
|
||||
if(_chrPageAccessType[addr >> 8] & MemoryAccessType::Write) {
|
||||
_chrPages[addr >> 8][(uint8_t)addr] = value;
|
||||
|
|
|
@ -185,9 +185,6 @@ public:
|
|||
|
||||
__forceinline uint8_t ReadVRAM(uint16_t addr, MemoryOperationType type = MemoryOperationType::PpuRenderingRead)
|
||||
{
|
||||
ProcessVramAccess(addr);
|
||||
NotifyVRAMAddressChange(addr);
|
||||
|
||||
uint8_t value = MapperReadVRAM(addr, type);
|
||||
Debugger::ProcessVramReadOperation(type, addr, value);
|
||||
return value;
|
||||
|
@ -196,7 +193,6 @@ public:
|
|||
void DebugWriteVRAM(uint16_t addr, uint8_t value, bool disableSideEffects = true);
|
||||
void WriteVRAM(uint16_t addr, uint8_t value);
|
||||
|
||||
__forceinline void ProcessVramAccess(uint16_t &addr);
|
||||
uint8_t DebugReadVRAM(uint16_t addr, bool disableSideEffects = true);
|
||||
|
||||
static void InitializeRam(void* data, uint32_t length);
|
||||
|
|
|
@ -228,6 +228,7 @@ protected:
|
|||
for(int i = 0; i < 4; i++) {
|
||||
if(_disableNtRam || (_ntLowRegs[i] & 0x80) != (_ntRamSelectBit & 0x80)) {
|
||||
SetPpuMemoryMapping(0x2000 + 0x400 * i, 0x23FF + 0x400 * i, _ntLowRegs[i] | (_ntHighRegs[i] << 8), ChrMemoryType::ChrRom);
|
||||
SetPpuMemoryMapping(0x3000 + 0x400 * i, 0x33FF + 0x400 * i, _ntLowRegs[i] | (_ntHighRegs[i] << 8), ChrMemoryType::ChrRom);
|
||||
} else {
|
||||
SetNametable(i, _ntLowRegs[i] & 0x01);
|
||||
}
|
||||
|
|
62
Core/PPU.cpp
62
Core/PPU.cpp
|
@ -60,6 +60,7 @@ void PPU::Reset()
|
|||
_currentTile = {};
|
||||
_nextTile = {};
|
||||
|
||||
_ppuBusAddress = 0;
|
||||
_intensifyColorBits = 0;
|
||||
_paletteRamMask = 0x3F;
|
||||
_lastUpdatedPixel = -1;
|
||||
|
@ -170,11 +171,11 @@ void PPU::SetState(PPUDebugState &state)
|
|||
void PPU::UpdateVideoRamAddr()
|
||||
{
|
||||
if(_scanline >= 240 || !IsRenderingEnabled()) {
|
||||
_state.VideoRamAddr += _flags.VerticalWrite ? 32 : 1;
|
||||
_state.VideoRamAddr = (_state.VideoRamAddr + (_flags.VerticalWrite ? 32 : 1)) & 0x7FFF;
|
||||
|
||||
//Trigger memory read when setting the vram address - needed by MMC3 IRQ counter
|
||||
//"Should be clocked when A12 changes to 1 via $2007 read/write"
|
||||
_mapper->ReadVRAM(_state.VideoRamAddr, MemoryOperationType::Read);
|
||||
SetBusAddress(_state.VideoRamAddr & 0x3FFF);
|
||||
} else {
|
||||
//"During rendering (on the pre-render line and the visible lines 0-239, provided either background or sprite rendering is enabled), "
|
||||
//it will update v in an odd way, triggering a coarse X increment and a Y increment simultaneously"
|
||||
|
@ -268,7 +269,7 @@ uint8_t PPU::ReadRAM(uint16_t addr)
|
|||
openBusMask = 0xFF;
|
||||
} else {
|
||||
returnValue = _memoryReadBuffer;
|
||||
_memoryReadBuffer = _mapper->ReadVRAM(_state.VideoRamAddr, MemoryOperationType::Read);
|
||||
_memoryReadBuffer = ReadVram(_ppuBusAddress & 0x3FFF, MemoryOperationType::Read);
|
||||
|
||||
if((_state.VideoRamAddr & 0x3FFF) >= 0x3F00 && !EmulationSettings::CheckFlag(EmulationFlags::DisablePaletteRead)) {
|
||||
returnValue = ReadPaletteRAM(_state.VideoRamAddr) | (_openBus & 0xC0);
|
||||
|
@ -360,7 +361,7 @@ void PPU::WriteRAM(uint16_t addr, uint8_t value)
|
|||
WritePaletteRAM(_state.VideoRamAddr, value);
|
||||
Debugger::ProcessVramWriteOperation(_state.VideoRamAddr & 0x3FFF, value);
|
||||
} else {
|
||||
_mapper->WriteVRAM(_state.VideoRamAddr, value);
|
||||
_mapper->WriteVRAM(_ppuBusAddress & 0x3FFF, value);
|
||||
}
|
||||
UpdateVideoRamAddr();
|
||||
break;
|
||||
|
@ -541,6 +542,24 @@ uint16_t PPU::GetAttributeAddr()
|
|||
return 0x23C0 | (_state.VideoRamAddr & 0x0C00) | ((_state.VideoRamAddr >> 4) & 0x38) | ((_state.VideoRamAddr >> 2) & 0x07);
|
||||
}
|
||||
|
||||
void PPU::SetBusAddress(uint16_t addr)
|
||||
{
|
||||
_ppuBusAddress = addr;
|
||||
_mapper->NotifyVRAMAddressChange(addr);
|
||||
}
|
||||
|
||||
uint8_t PPU::ReadVram(uint16_t addr, MemoryOperationType type)
|
||||
{
|
||||
SetBusAddress(addr);
|
||||
return _mapper->ReadVRAM(addr, type);
|
||||
}
|
||||
|
||||
void PPU::WriteVram(uint16_t addr, uint8_t value)
|
||||
{
|
||||
SetBusAddress(addr);
|
||||
_mapper->WriteVRAM(addr, value);
|
||||
}
|
||||
|
||||
void PPU::LoadTileInfo()
|
||||
{
|
||||
if(IsRenderingEnabled()) {
|
||||
|
@ -552,7 +571,7 @@ void PPU::LoadTileInfo()
|
|||
_state.LowBitShift |= _nextTile.LowByte;
|
||||
_state.HighBitShift |= _nextTile.HighByte;
|
||||
|
||||
uint8_t tileIndex = _mapper->ReadVRAM(GetNameTableAddr());
|
||||
uint8_t tileIndex = ReadVram(GetNameTableAddr());
|
||||
_nextTile.TileAddr = (tileIndex << 4) | (_state.VideoRamAddr >> 12) | _flags.BackgroundPatternAddr;
|
||||
_nextTile.OffsetY = _state.VideoRamAddr >> 12;
|
||||
break;
|
||||
|
@ -560,17 +579,17 @@ void PPU::LoadTileInfo()
|
|||
|
||||
case 2: {
|
||||
uint8_t shift = ((_state.VideoRamAddr >> 4) & 0x04) | (_state.VideoRamAddr & 0x02);
|
||||
_nextTile.PaletteOffset = ((_mapper->ReadVRAM(GetAttributeAddr()) >> shift) & 0x03) << 2;
|
||||
_nextTile.PaletteOffset = ((ReadVram(GetAttributeAddr()) >> shift) & 0x03) << 2;
|
||||
break;
|
||||
}
|
||||
|
||||
case 3:
|
||||
_nextTile.LowByte = _mapper->ReadVRAM(_nextTile.TileAddr);
|
||||
_nextTile.LowByte = ReadVram(_nextTile.TileAddr);
|
||||
_nextTile.AbsoluteTileAddr = _mapper->ToAbsoluteChrAddress(_nextTile.TileAddr);
|
||||
break;
|
||||
|
||||
case 5:
|
||||
_nextTile.HighByte = _mapper->ReadVRAM(_nextTile.TileAddr + 8);
|
||||
_nextTile.HighByte = ReadVram(_nextTile.TileAddr + 8);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -609,8 +628,8 @@ void PPU::LoadSprite(uint8_t spriteY, uint8_t tileIndex, uint8_t attributes, uin
|
|||
info.HighByte = _mapper->DebugReadVRAM(tileAddr + 8);
|
||||
} else {
|
||||
fetchLastSprite = false;
|
||||
info.LowByte = _mapper->ReadVRAM(tileAddr);
|
||||
info.HighByte = _mapper->ReadVRAM(tileAddr + 8);
|
||||
info.LowByte = ReadVram(tileAddr);
|
||||
info.HighByte = ReadVram(tileAddr + 8);
|
||||
}
|
||||
info.TileAddr = tileAddr;
|
||||
info.AbsoluteTileAddr = _mapper->ToAbsoluteChrAddress(tileAddr);
|
||||
|
@ -635,8 +654,8 @@ void PPU::LoadSprite(uint8_t spriteY, uint8_t tileIndex, uint8_t attributes, uin
|
|||
tileAddr = ((tileIndex << 4) | _flags.SpritePatternAddr) + lineOffset;
|
||||
}
|
||||
|
||||
_mapper->ReadVRAM(tileAddr);
|
||||
_mapper->ReadVRAM(tileAddr + 8);
|
||||
ReadVram(tileAddr);
|
||||
ReadVram(tileAddr + 8);
|
||||
}
|
||||
|
||||
_spriteIndex++;
|
||||
|
@ -841,10 +860,10 @@ void PPU::ProcessScanline()
|
|||
LoadSpriteTileInfo();
|
||||
} else if((_cycle - 257) % 8 == 0) {
|
||||
//Garbage NT sprite fetch (257, 265, 273, etc.) - Required for proper MC-ACC IRQs (MMC3 clone)
|
||||
_mapper->ReadVRAM(GetNameTableAddr());
|
||||
ReadVram(GetNameTableAddr());
|
||||
} else if((_cycle - 259) % 8 == 0) {
|
||||
//Garbage AT sprite fetch
|
||||
_mapper->ReadVRAM(GetAttributeAddr());
|
||||
ReadVram(GetAttributeAddr());
|
||||
}
|
||||
|
||||
if(_scanline == -1 && _cycle >= 280 && _cycle <= 304) {
|
||||
|
@ -868,7 +887,7 @@ void PPU::ProcessScanline()
|
|||
}
|
||||
} else if(_cycle == 337 || _cycle == 339) {
|
||||
if(IsRenderingEnabled()) {
|
||||
_mapper->ReadVRAM(GetNameTableAddr());
|
||||
ReadVram(GetNameTableAddr());
|
||||
|
||||
if(_scanline == -1 && _nesModel == NesModel::NTSC && _cycle == 339 && (_frameCount & 0x01)) {
|
||||
//This behavior is NTSC-specific - PAL frames are always the same number of cycles
|
||||
|
@ -1158,9 +1177,13 @@ void PPU::UpdateState()
|
|||
if(_updateVramAddrDelay == 0) {
|
||||
_state.VideoRamAddr = _updateVramAddr;
|
||||
|
||||
//Trigger memory read when setting the vram address - needed by MMC3 IRQ counter
|
||||
//"4) Should be clocked when A12 changes to 1 via $2006 write"
|
||||
_mapper->ReadVRAM(_state.VideoRamAddr, MemoryOperationType::Read);
|
||||
if(_scanline >= 240 || !IsRenderingEnabled()) {
|
||||
//Only set the VRAM address on the bus if the PPU is rendering
|
||||
//More info here: https://forums.nesdev.com/viewtopic.php?p=132145#p132145
|
||||
//Trigger bus address change when setting the vram address - needed by MMC3 IRQ counter
|
||||
//"4) Should be clocked when A12 changes to 1 via $2006 write"
|
||||
SetBusAddress(_state.VideoRamAddr & 0x3FFF);
|
||||
}
|
||||
} else {
|
||||
_needStateUpdate = true;
|
||||
}
|
||||
|
@ -1228,7 +1251,8 @@ void PPU::StreamState(bool saving)
|
|||
_nextTile.PaletteOffset, _nextTile.TileAddr, _previousTile.LowByte, _previousTile.HighByte, _previousTile.PaletteOffset, _spriteIndex, _spriteCount,
|
||||
_secondaryOAMAddr, _sprite0Visible, _oamCopybuffer, _spriteInRange, _sprite0Added, _spriteAddrH, _spriteAddrL, _oamCopyDone, _nesModel, unusedSpriteDmaAddr,
|
||||
unusedSpriteDmaCounter, _prevRenderingEnabled, _renderingEnabled, _openBus, _ignoreVramRead, unusedSkipScrollIncrement, paletteRam, spriteRam, secondarySpriteRam,
|
||||
openBusDecayStamp, _cyclesNeeded, disablePpu2004Reads, disablePaletteRead, disableOamAddrBug, _overflowBugCounter, _updateVramAddr, _updateVramAddrDelay, _needStateUpdate);
|
||||
openBusDecayStamp, _cyclesNeeded, disablePpu2004Reads, disablePaletteRead, disableOamAddrBug, _overflowBugCounter, _updateVramAddr, _updateVramAddrDelay,
|
||||
_needStateUpdate, _ppuBusAddress);
|
||||
|
||||
for(int i = 0; i < 64; i++) {
|
||||
Stream(_spriteTiles[i].SpriteX, _spriteTiles[i].LowByte, _spriteTiles[i].HighByte, _spriteTiles[i].PaletteOffset, _spriteTiles[i].HorizontalMirror, _spriteTiles[i].BackgroundPriority);
|
||||
|
|
|
@ -63,6 +63,7 @@ class PPU : public IMemoryHandler, public Snapshotable
|
|||
|
||||
SpriteInfo *_lastSprite; //used by HD ppu
|
||||
|
||||
uint16_t _ppuBusAddress;
|
||||
TileInfo _currentTile;
|
||||
TileInfo _nextTile;
|
||||
TileInfo _previousTile;
|
||||
|
@ -156,6 +157,10 @@ class PPU : public IMemoryHandler, public Snapshotable
|
|||
}
|
||||
}
|
||||
|
||||
__forceinline void SetBusAddress(uint16_t addr);
|
||||
__forceinline uint8_t ReadVram(uint16_t addr, MemoryOperationType type = MemoryOperationType::PpuRenderingRead);
|
||||
__forceinline void WriteVram(uint16_t addr, uint8_t value);
|
||||
|
||||
void StreamState(bool saving) override;
|
||||
|
||||
public:
|
||||
|
|
62
Core/VRC6.h
62
Core/VRC6.h
|
@ -54,6 +54,12 @@ protected:
|
|||
_audio.Clock();
|
||||
}
|
||||
|
||||
void SetPpuMapping(uint8_t bank, uint8_t page)
|
||||
{
|
||||
SetPpuMemoryMapping(0x2000 + bank * 0x400, 0x23FF + bank * 0x400, page);
|
||||
SetPpuMemoryMapping(0x3000 + bank * 0x400, 0x33FF + bank * 0x400, page);
|
||||
}
|
||||
|
||||
void UpdatePpuBanking()
|
||||
{
|
||||
uint8_t mask = (_bankingMode & 0x20) ? 0xFE : 0xFF;
|
||||
|
@ -99,34 +105,34 @@ protected:
|
|||
switch(_bankingMode & 0x2F) {
|
||||
case 0x20:
|
||||
case 0x27:
|
||||
SetPpuMemoryMapping(0x2000, 0x23FF, _chrRegisters[6] & 0xFE);
|
||||
SetPpuMemoryMapping(0x2400, 0x27FF, (_chrRegisters[6] & 0xFE) | 1);
|
||||
SetPpuMemoryMapping(0x2800, 0x2BFF, _chrRegisters[7] & 0xFE);
|
||||
SetPpuMemoryMapping(0x2C00, 0x2FFF, (_chrRegisters[7] & 0xFE) | 1);
|
||||
SetPpuMapping(0, _chrRegisters[6] & 0xFE);
|
||||
SetPpuMapping(1, (_chrRegisters[6] & 0xFE) | 1);
|
||||
SetPpuMapping(2, _chrRegisters[7] & 0xFE);
|
||||
SetPpuMapping(3, (_chrRegisters[7] & 0xFE) | 1);
|
||||
break;
|
||||
|
||||
case 0x23:
|
||||
case 0x24:
|
||||
SetPpuMemoryMapping(0x2000, 0x23FF, (_chrRegisters[6] & 0xFE));
|
||||
SetPpuMemoryMapping(0x2400, 0x27FF, (_chrRegisters[7] & 0xFE));
|
||||
SetPpuMemoryMapping(0x2800, 0x2BFF, (_chrRegisters[6] & 0xFE) | 1);
|
||||
SetPpuMemoryMapping(0x2C00, 0x2FFF, (_chrRegisters[7] & 0xFE) | 1);
|
||||
SetPpuMapping(0, (_chrRegisters[6] & 0xFE));
|
||||
SetPpuMapping(1, (_chrRegisters[7] & 0xFE));
|
||||
SetPpuMapping(2, (_chrRegisters[6] & 0xFE) | 1);
|
||||
SetPpuMapping(3, (_chrRegisters[7] & 0xFE) | 1);
|
||||
break;
|
||||
|
||||
case 0x28:
|
||||
case 0x2F:
|
||||
SetPpuMemoryMapping(0x2000, 0x23FF, _chrRegisters[6] & 0xFE);
|
||||
SetPpuMemoryMapping(0x2400, 0x27FF, _chrRegisters[6] & 0xFE);
|
||||
SetPpuMemoryMapping(0x2800, 0x2BFF, _chrRegisters[7] & 0xFE);
|
||||
SetPpuMemoryMapping(0x2C00, 0x2FFF, _chrRegisters[7] & 0xFE);
|
||||
SetPpuMapping(0, _chrRegisters[6] & 0xFE);
|
||||
SetPpuMapping(1, _chrRegisters[6] & 0xFE);
|
||||
SetPpuMapping(2, _chrRegisters[7] & 0xFE);
|
||||
SetPpuMapping(3, _chrRegisters[7] & 0xFE);
|
||||
break;
|
||||
|
||||
case 0x2B:
|
||||
case 0x2C:
|
||||
SetPpuMemoryMapping(0x2000, 0x23FF, (_chrRegisters[6] & 0xFE) | 1);
|
||||
SetPpuMemoryMapping(0x2400, 0x27FF, (_chrRegisters[7] & 0xFE) | 1);
|
||||
SetPpuMemoryMapping(0x2800, 0x2BFF, (_chrRegisters[6] & 0xFE) | 1);
|
||||
SetPpuMemoryMapping(0x2C00, 0x2FFF, (_chrRegisters[7] & 0xFE) | 1);
|
||||
SetPpuMapping(0, (_chrRegisters[6] & 0xFE) | 1);
|
||||
SetPpuMapping(1, (_chrRegisters[7] & 0xFE) | 1);
|
||||
SetPpuMapping(2, (_chrRegisters[6] & 0xFE) | 1);
|
||||
SetPpuMapping(3, (_chrRegisters[7] & 0xFE) | 1);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -134,27 +140,27 @@ protected:
|
|||
case 0:
|
||||
case 6:
|
||||
case 7:
|
||||
SetPpuMemoryMapping(0x2000, 0x23FF, _chrRegisters[6]);
|
||||
SetPpuMemoryMapping(0x2400, 0x27FF, _chrRegisters[6]);
|
||||
SetPpuMemoryMapping(0x2800, 0x2BFF, _chrRegisters[7]);
|
||||
SetPpuMemoryMapping(0x2C00, 0x2FFF, _chrRegisters[7]);
|
||||
SetPpuMapping(0, _chrRegisters[6]);
|
||||
SetPpuMapping(1, _chrRegisters[6]);
|
||||
SetPpuMapping(2, _chrRegisters[7]);
|
||||
SetPpuMapping(3, _chrRegisters[7]);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
case 5:
|
||||
SetPpuMemoryMapping(0x2000, 0x23FF, _chrRegisters[4]);
|
||||
SetPpuMemoryMapping(0x2400, 0x27FF, _chrRegisters[5]);
|
||||
SetPpuMemoryMapping(0x2800, 0x2BFF, _chrRegisters[6]);
|
||||
SetPpuMemoryMapping(0x2C00, 0x2FFF, _chrRegisters[7]);
|
||||
SetPpuMapping(0, _chrRegisters[4]);
|
||||
SetPpuMapping(1, _chrRegisters[5]);
|
||||
SetPpuMapping(2, _chrRegisters[6]);
|
||||
SetPpuMapping(3, _chrRegisters[7]);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
SetPpuMemoryMapping(0x2000, 0x23FF, _chrRegisters[6]);
|
||||
SetPpuMemoryMapping(0x2400, 0x27FF, _chrRegisters[7]);
|
||||
SetPpuMemoryMapping(0x2800, 0x2BFF, _chrRegisters[6]);
|
||||
SetPpuMemoryMapping(0x2C00, 0x2FFF, _chrRegisters[7]);
|
||||
SetPpuMapping(0, _chrRegisters[6]);
|
||||
SetPpuMapping(1, _chrRegisters[7]);
|
||||
SetPpuMapping(2, _chrRegisters[6]);
|
||||
SetPpuMapping(3, _chrRegisters[7]);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
|
Loading…
Add table
Reference in a new issue