MMC5: Improved accuracy (scanline counter, split screen mode, etc.)
This commit is contained in:
parent
ae4a2f29d3
commit
17c8294f5d
8 changed files with 213 additions and 138 deletions
|
@ -579,6 +579,7 @@
|
|||
<ClInclude Include="MMC3_198.h" />
|
||||
<ClInclude Include="MMC3_208.h" />
|
||||
<ClInclude Include="MMC3_224.h" />
|
||||
<ClInclude Include="MMC5MemoryHandler.h" />
|
||||
<ClInclude Include="MovieRecorder.h" />
|
||||
<ClInclude Include="AsciiTurboFile.h" />
|
||||
<ClInclude Include="NESHeader.h" />
|
||||
|
|
|
@ -1496,6 +1496,9 @@
|
|||
<ClInclude Include="StudyBoxLoader.h">
|
||||
<Filter>Nes\RomLoader</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="MMC5MemoryHandler.h">
|
||||
<Filter>Nes\Mappers\MMC</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="stdafx.cpp">
|
||||
|
|
286
Core/MMC5.h
286
Core/MMC5.h
|
@ -3,6 +3,7 @@
|
|||
#include "BaseMapper.h"
|
||||
#include "PPU.h"
|
||||
#include "MMC5Audio.h"
|
||||
#include "MMC5MemoryHandler.h"
|
||||
|
||||
class MMC5 : public BaseMapper
|
||||
{
|
||||
|
@ -13,6 +14,7 @@ private:
|
|||
static constexpr uint8_t NtFillModeIndex = 3;
|
||||
|
||||
unique_ptr<MMC5Audio> _audio;
|
||||
unique_ptr<MMC5MemoryHandler> _mmc5MemoryHandler;
|
||||
|
||||
uint8_t _prgRamProtect1;
|
||||
uint8_t _prgRamProtect2;
|
||||
|
@ -50,18 +52,19 @@ private:
|
|||
uint8_t _chrUpperBits;
|
||||
uint16_t _chrBanks[12];
|
||||
uint16_t _lastChrReg;
|
||||
bool _spriteFetch;
|
||||
bool _largeSprites;
|
||||
bool _prevChrA;
|
||||
|
||||
//IRQ counter related fields
|
||||
uint8_t _irqCounterTarget;
|
||||
bool _irqEnabled;
|
||||
int16_t _previousScanline;
|
||||
uint8_t _irqCounter;
|
||||
uint8_t _scanlineCounter;
|
||||
bool _irqPending;
|
||||
bool _ppuInFrame;
|
||||
|
||||
MemoryOperationType _lastVramOperationType;
|
||||
bool _needInFrame;
|
||||
bool _ppuInFrame;
|
||||
uint8_t _ppuIdleCounter;
|
||||
uint16_t _lastPpuReadAddr;
|
||||
uint8_t _ntReadCounter;
|
||||
|
||||
void SwitchPrgBank(uint16_t reg, uint8_t value)
|
||||
{
|
||||
|
@ -173,26 +176,29 @@ private:
|
|||
|
||||
void SwitchChrBank(uint16_t reg, uint8_t value)
|
||||
{
|
||||
_chrBanks[reg - 0x5120] = value | (_chrUpperBits << 8);
|
||||
|
||||
if(_largeSprites) {
|
||||
uint16_t newValue = value | (_chrUpperBits << 8);
|
||||
if(newValue != _chrBanks[reg - 0x5120] || _lastChrReg != reg) {
|
||||
_chrBanks[reg - 0x5120] = newValue;
|
||||
_lastChrReg = reg;
|
||||
} else {
|
||||
//Using 8x8 sprites resets the last written to bank logic
|
||||
//Unsure about this part (hasn't been tested specifically, but would make sense)
|
||||
_lastChrReg = 0;
|
||||
UpdateChrBanks(true);
|
||||
}
|
||||
UpdateChrBanks();
|
||||
}
|
||||
|
||||
void UpdateChrBanks()
|
||||
void UpdateChrBanks(bool forceUpdate)
|
||||
{
|
||||
if(!_largeSprites) {
|
||||
bool largeSprites = (_mmc5MemoryHandler->GetReg(0x2000) & 0x20) != 0;
|
||||
|
||||
if(!largeSprites) {
|
||||
//Using 8x8 sprites resets the last written to bank logic
|
||||
_lastChrReg = 0;
|
||||
}
|
||||
|
||||
bool chrA = !_largeSprites || (_largeSprites && _spriteFetch) || (_lastVramOperationType != MemoryOperationType::PpuRenderingRead && _lastChrReg <= 0x5127);
|
||||
bool chrA = !largeSprites || (_splitTileNumber >= 32 && _splitTileNumber < 40) || (!_ppuInFrame && _lastChrReg <= 0x5127);
|
||||
if(!forceUpdate && chrA == _prevChrA) {
|
||||
return;
|
||||
}
|
||||
_prevChrA = chrA;
|
||||
|
||||
if(_chrMode == 0) {
|
||||
SelectChrPage8x(0, _chrBanks[chrA ? 0x07 : 0x0B] << 3);
|
||||
} else if(_chrMode == 1) {
|
||||
|
@ -218,36 +224,14 @@ private:
|
|||
void ProcessCpuClock() override
|
||||
{
|
||||
_audio->Clock();
|
||||
}
|
||||
|
||||
virtual void NotifyVRAMAddressChange(uint16_t addr) override
|
||||
{
|
||||
PPU* ppu = _console->GetPpu();
|
||||
if(ppu->GetControlFlags().BackgroundEnabled || ppu->GetControlFlags().SpritesEnabled) {
|
||||
int16_t currentScanline = ppu->GetCurrentScanline();
|
||||
if(currentScanline != _previousScanline) {
|
||||
if(currentScanline >= 239 || currentScanline < 0) {
|
||||
_ppuInFrame = false;
|
||||
} else {
|
||||
if(!_ppuInFrame) {
|
||||
_ppuInFrame = true;
|
||||
_irqCounter = 0;
|
||||
_irqPending = false;
|
||||
_console->GetCpu()->ClearIrqSource(IRQSource::External);
|
||||
} else {
|
||||
_irqCounter++;
|
||||
if(_irqCounter == _irqCounterTarget) {
|
||||
_irqPending = true;
|
||||
if(_irqEnabled) {
|
||||
_console->GetCpu()->SetIrqSource(IRQSource::External);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_previousScanline = currentScanline;
|
||||
if(_ppuIdleCounter) {
|
||||
_ppuIdleCounter--;
|
||||
if(_ppuIdleCounter == 0) {
|
||||
//"The "in-frame" flag is cleared when the PPU is no longer rendering. This is detected when 3 CPU cycles pass without a PPU read having occurred (PPU /RD has not been low during the last 3 M2 rises)."
|
||||
_ppuInFrame = false;
|
||||
UpdateChrBanks(true);
|
||||
}
|
||||
} else {
|
||||
_ppuInFrame = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -312,11 +296,6 @@ private:
|
|||
memset(GetNametable(NtFillModeIndex) + 32 * 30, attributeByte, 64); //Attribute table is 64 bytes
|
||||
}
|
||||
|
||||
bool IsSpriteFetch()
|
||||
{
|
||||
return _console->GetPpu()->GetCurrentCycle() >= 257 && _console->GetPpu()->GetCurrentCycle() < 321;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual uint16_t GetPRGPageSize() override { return 0x2000; }
|
||||
virtual uint16_t GetCHRPageSize() override { return 0x400; }
|
||||
|
@ -369,8 +348,18 @@ protected:
|
|||
|
||||
virtual void InitMapper() override
|
||||
{
|
||||
AddRegisterRange(0xFFFA, 0xFFFB, MemoryOperation::Read);
|
||||
|
||||
_audio.reset(new MMC5Audio(_console));
|
||||
|
||||
//Override the 2000-2007 registers to catch all writes to the PPU registers (but not their mirrors)
|
||||
_mmc5MemoryHandler.reset(new MMC5MemoryHandler(_console.get()));
|
||||
|
||||
_ppuIdleCounter = 0;
|
||||
_lastPpuReadAddr = 0;
|
||||
_ntReadCounter = 0;
|
||||
_prevChrA = false;
|
||||
|
||||
_chrMode = 0;
|
||||
_prgRamProtect1 = 0;
|
||||
_prgRamProtect2 = 0;
|
||||
|
@ -388,8 +377,6 @@ protected:
|
|||
_chrUpperBits = 0;
|
||||
memset(_chrBanks, 0, sizeof(_chrBanks));
|
||||
_lastChrReg = 0;
|
||||
_spriteFetch = false;
|
||||
_largeSprites = false;
|
||||
|
||||
_exAttrLastFetchCounter = 0;
|
||||
_exAttributeLastNametableFetch = 0;
|
||||
|
@ -397,12 +384,10 @@ protected:
|
|||
|
||||
_irqPending = false;
|
||||
_irqCounterTarget = 0;
|
||||
_irqCounter = 0;
|
||||
_scanlineCounter = 0;
|
||||
_irqEnabled = false;
|
||||
_previousScanline = -1;
|
||||
_ppuInFrame = false;
|
||||
|
||||
_lastVramOperationType = MemoryOperationType::Read;
|
||||
_needInFrame = false;
|
||||
|
||||
_splitInSplitRegion = false;
|
||||
_splitVerticalScroll = 0;
|
||||
|
@ -418,12 +403,23 @@ protected:
|
|||
|
||||
//"Games seem to expect $5117 to be $FF on powerup (last PRG page swapped in)."
|
||||
WriteRegister(0x5117, 0xFF);
|
||||
|
||||
UpdateChrBanks(true);
|
||||
}
|
||||
|
||||
void Reset(bool softReset) override
|
||||
{
|
||||
_console->GetMemoryManager()->RegisterWriteHandler(_mmc5MemoryHandler.get(), 0x2000, 0x2007);
|
||||
}
|
||||
|
||||
void StreamState(bool saving) override
|
||||
{
|
||||
BaseMapper::StreamState(saving);
|
||||
|
||||
int16_t unusedPreviousScanline = 0;
|
||||
bool unusedSpriteFetch = false;
|
||||
bool unusedLargeSprites = false;
|
||||
|
||||
ArrayInfo<uint8_t> prgBanks = { _prgBanks, 5 };
|
||||
ArrayInfo<uint16_t> chrBanks = { _chrBanks, 12 };
|
||||
SnapshotInfo audio{ _audio.get() };
|
||||
|
@ -431,8 +427,8 @@ protected:
|
|||
_verticalSplitDelimiterTile, _verticalSplitScroll, _verticalSplitBank, _multiplierValue1, _multiplierValue2,
|
||||
_nametableMapping, _extendedRamMode, _exAttributeLastNametableFetch, _exAttrLastFetchCounter, _exAttrSelectedChrBank,
|
||||
_prgMode, prgBanks, _chrMode, _chrUpperBits, chrBanks, _lastChrReg,
|
||||
_spriteFetch, _largeSprites, _irqCounterTarget, _irqEnabled, _previousScanline, _irqCounter, _irqPending, _ppuInFrame, audio,
|
||||
_splitInSplitRegion, _splitVerticalScroll, _splitTile, _splitTileNumber, _lastVramOperationType);
|
||||
unusedSpriteFetch, unusedLargeSprites, _irqCounterTarget, _irqEnabled, unusedPreviousScanline, _scanlineCounter, _irqPending, _ppuInFrame, audio,
|
||||
_splitInSplitRegion, _splitVerticalScroll, _splitTile, _splitTileNumber, _needInFrame);
|
||||
|
||||
if(!saving) {
|
||||
UpdatePrgBanks();
|
||||
|
@ -442,103 +438,127 @@ protected:
|
|||
|
||||
virtual void WriteRAM(uint16_t addr, uint8_t value) override
|
||||
{
|
||||
if(addr >= 0x5C00 && addr <= 0x5FFF && _extendedRamMode <= 1) {
|
||||
PPUControlFlags flags = _console->GetPpu()->GetControlFlags();
|
||||
if(!flags.BackgroundEnabled && !flags.SpritesEnabled) {
|
||||
//Expansion RAM ($5C00-$5FFF, read/write)
|
||||
//Mode 0/1 - Not readable (returns open bus), can only be written while the PPU is rendering (otherwise, 0 is written)
|
||||
value = 0;
|
||||
}
|
||||
if(addr >= 0x5C00 && addr <= 0x5FFF && _extendedRamMode <= 1 && !_ppuInFrame) {
|
||||
//Expansion RAM ($5C00-$5FFF, read/write)
|
||||
//Mode 0/1 - Not readable (returns open bus), can only be written while the PPU is rendering (otherwise, 0 is written)
|
||||
value = 0;
|
||||
}
|
||||
BaseMapper::WriteRAM(addr, value);
|
||||
}
|
||||
|
||||
void DetectScanlineStart(uint16_t addr)
|
||||
{
|
||||
if(addr >= 0x2000 && addr <= 0x2FFF) {
|
||||
if(_lastPpuReadAddr == addr) {
|
||||
//Count consecutive identical reads
|
||||
_ntReadCounter++;
|
||||
} else {
|
||||
_ntReadCounter = 0;
|
||||
}
|
||||
|
||||
if(_ntReadCounter >= 2) {
|
||||
if(!_ppuInFrame && !_needInFrame) {
|
||||
_needInFrame = true;
|
||||
_scanlineCounter = 0;
|
||||
} else {
|
||||
_scanlineCounter++;
|
||||
if(_irqCounterTarget == _scanlineCounter) {
|
||||
_irqPending = true;
|
||||
if(_irqEnabled) {
|
||||
_console->GetCpu()->SetIrqSource(IRQSource::External);
|
||||
}
|
||||
}
|
||||
}
|
||||
_splitTileNumber = 0;
|
||||
}
|
||||
} else {
|
||||
_ntReadCounter = 0;
|
||||
}
|
||||
}
|
||||
|
||||
virtual uint8_t MapperReadVRAM(uint16_t addr, MemoryOperationType memoryOperationType) override
|
||||
{
|
||||
PPU* ppu = _console->GetPpu();
|
||||
if(_spriteFetch != IsSpriteFetch() || _largeSprites != ppu->GetControlFlags().LargeSprites || _lastVramOperationType != memoryOperationType) {
|
||||
_lastVramOperationType = memoryOperationType;
|
||||
_spriteFetch = IsSpriteFetch();
|
||||
_largeSprites = ppu->GetControlFlags().LargeSprites;
|
||||
UpdateChrBanks();
|
||||
bool isNtFetch = addr >= 0x2000 && addr <= 0x2FFF && (addr & 0x3FF) < 0x3C0;
|
||||
if(isNtFetch) {
|
||||
//Nametable data, not an attribute fetch
|
||||
_splitInSplitRegion = false;
|
||||
_splitTileNumber++;
|
||||
|
||||
if(_ppuInFrame) {
|
||||
UpdateChrBanks(false);
|
||||
} else if(_needInFrame) {
|
||||
_needInFrame = false;
|
||||
_ppuInFrame = true;
|
||||
UpdateChrBanks(false);
|
||||
}
|
||||
}
|
||||
DetectScanlineStart(addr);
|
||||
|
||||
if(_extendedRamMode <= 1 && _verticalSplitEnabled && memoryOperationType == MemoryOperationType::PpuRenderingRead) {
|
||||
uint32_t cycle = ppu->GetCurrentCycle();
|
||||
int32_t scanline = ppu->GetCurrentScanline();
|
||||
if(cycle == 321) {
|
||||
_splitTileNumber = -1;
|
||||
if(scanline == -1) {
|
||||
_splitVerticalScroll = _verticalSplitScroll;
|
||||
} else if(scanline < 240) {
|
||||
_splitVerticalScroll++;
|
||||
}
|
||||
if(_splitVerticalScroll >= 240) {
|
||||
_splitVerticalScroll -= 240;
|
||||
}
|
||||
}
|
||||
_ppuIdleCounter = 3;
|
||||
_lastPpuReadAddr = addr;
|
||||
|
||||
if((cycle - 1) % 8 == 0 && cycle != 337) {
|
||||
_splitTileNumber++;
|
||||
}
|
||||
|
||||
if(cycle < 256 || cycle >= 321) {
|
||||
if(_extendedRamMode <= 1 && _ppuInFrame) {
|
||||
if(_verticalSplitEnabled) {
|
||||
uint16_t verticalSplitScroll = (_verticalSplitScroll + _scanlineCounter) % 240;
|
||||
if(addr >= 0x2000) {
|
||||
if((addr & 0x3FF) < 0x3C0) {
|
||||
if((_verticalSplitRightSide && _splitTileNumber >= _verticalSplitDelimiterTile) || (!_verticalSplitRightSide && _splitTileNumber < _verticalSplitDelimiterTile)) {
|
||||
//Split region
|
||||
if(isNtFetch) {
|
||||
uint8_t tileNumber = (_splitTileNumber + 2) % 42;
|
||||
if(tileNumber <= 32 && (_verticalSplitRightSide && tileNumber >= _verticalSplitDelimiterTile) || (!_verticalSplitRightSide && tileNumber < _verticalSplitDelimiterTile)) {
|
||||
//Split region (for next 3 fetches, attribute + 2x tile data)
|
||||
_splitInSplitRegion = true;
|
||||
_splitTile = ((_splitVerticalScroll & 0xF8) << 2) | _splitTileNumber;
|
||||
_splitTile = ((verticalSplitScroll & 0xF8) << 2) | tileNumber;
|
||||
return InternalReadRam(0x5C00 + _splitTile);
|
||||
} else {
|
||||
//Regular data, result can get modified by ex ram mode code below
|
||||
//Outside of split region (or sprite data), result can get modified by ex ram mode code below
|
||||
_splitInSplitRegion = false;
|
||||
}
|
||||
} else if(_splitInSplitRegion) {
|
||||
return InternalReadRam(0x5FC0 + ((_splitTile >> 4) & ~0x07) + ((_splitTile & 0x3F) >> 2));
|
||||
return InternalReadRam(0x5FC0 | ((_splitTile & 0x380) >> 4) | ((_splitTile & 0x1F) >> 2));
|
||||
}
|
||||
} else if(_splitInSplitRegion) {
|
||||
return _chrRom[(_verticalSplitBank % (GetCHRPageCount() / 4)) * 0x1000 + (((addr & ~0x07) | (_splitVerticalScroll & 0x07)) & 0xFFF)];
|
||||
//CHR tile fetches for split region
|
||||
return _chrRom[(_verticalSplitBank % (GetCHRPageCount() / 4)) * 0x1000 + (((addr & ~0x07) | (verticalSplitScroll & 0x07)) & 0xFFF)];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(_extendedRamMode == 1 && !IsSpriteFetch() && memoryOperationType == MemoryOperationType::PpuRenderingRead) {
|
||||
//"In Mode 1, nametable fetches are processed normally, and can come from CIRAM nametables, fill mode, or even Expansion RAM, but attribute fetches are replaced by data from Expansion RAM."
|
||||
//"Each byte of Expansion RAM is used to enhance the tile at the corresponding address in every nametable"
|
||||
if(_extendedRamMode == 1 && (_splitTileNumber < 32 || _splitTileNumber >= 40)) {
|
||||
//"In Mode 1, nametable fetches are processed normally, and can come from CIRAM nametables, fill mode, or even Expansion RAM, but attribute fetches are replaced by data from Expansion RAM."
|
||||
//"Each byte of Expansion RAM is used to enhance the tile at the corresponding address in every nametable"
|
||||
|
||||
//When fetching NT data, we set a flag and then alter the VRAM values read by the PPU on the following 3 cycles (palette, tile low/high byte)
|
||||
if(addr >= 0x2000 && (addr & 0x3FF) < 0x3C0) {
|
||||
//Nametable fetches
|
||||
_exAttributeLastNametableFetch = addr & 0x03FF;
|
||||
_exAttrLastFetchCounter = 3;
|
||||
} else if(_exAttrLastFetchCounter > 0) {
|
||||
//Attribute fetches
|
||||
_exAttrLastFetchCounter--;
|
||||
switch(_exAttrLastFetchCounter) {
|
||||
case 2:
|
||||
{
|
||||
//PPU palette fetch
|
||||
//Check work ram (expansion ram) to see which tile/palette to use
|
||||
//Use InternalReadRam to bypass the fact that the ram is supposed to be write-only in mode 0/1
|
||||
uint8_t value = InternalReadRam(0x5C00 + _exAttributeLastNametableFetch);
|
||||
//When fetching NT data, we set a flag and then alter the VRAM values read by the PPU on the following 3 cycles (palette, tile low/high byte)
|
||||
if(isNtFetch) {
|
||||
//Nametable fetches
|
||||
_exAttributeLastNametableFetch = addr & 0x03FF;
|
||||
_exAttrLastFetchCounter = 3;
|
||||
} else if(_exAttrLastFetchCounter > 0) {
|
||||
//Attribute fetches
|
||||
_exAttrLastFetchCounter--;
|
||||
switch(_exAttrLastFetchCounter) {
|
||||
case 2:
|
||||
{
|
||||
//PPU palette fetch
|
||||
//Check work ram (expansion ram) to see which tile/palette to use
|
||||
//Use InternalReadRam to bypass the fact that the ram is supposed to be write-only in mode 0/1
|
||||
uint8_t value = InternalReadRam(0x5C00 + _exAttributeLastNametableFetch);
|
||||
|
||||
//"The pattern fetches ignore the standard CHR banking bits, and instead use the top two bits of $5130 and the bottom 6 bits from Expansion RAM to choose a 4KB bank to select the tile from."
|
||||
_exAttrSelectedChrBank = ((value & 0x3F) | (_chrUpperBits << 6)) % (_chrRomSize / 0x1000);
|
||||
//"The pattern fetches ignore the standard CHR banking bits, and instead use the top two bits of $5130 and the bottom 6 bits from Expansion RAM to choose a 4KB bank to select the tile from."
|
||||
_exAttrSelectedChrBank = ((value & 0x3F) | (_chrUpperBits << 6)) % (_chrRomSize / 0x1000);
|
||||
|
||||
//Return a byte containing the same palette 4 times - this allows the PPU to select the right palette no matter the shift value
|
||||
uint8_t palette = (value & 0xC0) >> 6;
|
||||
return palette | palette << 2 | palette << 4 | palette << 6;
|
||||
//Return a byte containing the same palette 4 times - this allows the PPU to select the right palette no matter the shift value
|
||||
uint8_t palette = (value & 0xC0) >> 6;
|
||||
return palette | palette << 2 | palette << 4 | palette << 6;
|
||||
}
|
||||
|
||||
case 1:
|
||||
case 0:
|
||||
//PPU tile data fetch (high byte & low byte)
|
||||
return _chrRom[_exAttrSelectedChrBank * 0x1000 + (addr & 0xFFF)];
|
||||
}
|
||||
|
||||
case 1:
|
||||
case 0:
|
||||
//PPU tile data fetch (high byte & low byte)
|
||||
return _chrRom[_exAttrSelectedChrBank * 0x1000 + (addr & 0xFFF)];
|
||||
}
|
||||
}
|
||||
}
|
||||
return BaseMapper::MapperReadVRAM(addr, memoryOperationType);
|
||||
|
||||
return InternalReadVRAM(addr);
|
||||
}
|
||||
|
||||
void WriteRegister(uint16_t addr, uint8_t value) override
|
||||
|
@ -554,7 +574,7 @@ protected:
|
|||
break;
|
||||
|
||||
case 0x5100: _prgMode = value & 0x03; UpdatePrgBanks(); break;
|
||||
case 0x5101: _chrMode = value & 0x03; UpdateChrBanks(); break;
|
||||
case 0x5101: _chrMode = value & 0x03; UpdateChrBanks(true); break;
|
||||
case 0x5102: _prgRamProtect1 = value & 0x03; UpdatePrgBanks(); break;
|
||||
case 0x5103: _prgRamProtect2 = value & 0x03; UpdatePrgBanks(); break;
|
||||
case 0x5104: SetExtendedRamMode(value & 0x03); break;
|
||||
|
@ -603,6 +623,16 @@ protected:
|
|||
|
||||
case 0x5205: return (_multiplierValue1*_multiplierValue2) & 0xFF;
|
||||
case 0x5206: return (_multiplierValue1*_multiplierValue2) >> 8;
|
||||
|
||||
case 0xFFFA:
|
||||
case 0xFFFB:
|
||||
_ppuInFrame = false;
|
||||
UpdateChrBanks(true);
|
||||
_lastPpuReadAddr = 0;
|
||||
_scanlineCounter = 0;
|
||||
_irqPending = false;
|
||||
_console->GetCpu()->ClearIrqSource(IRQSource::External);
|
||||
return DebugReadRAM(addr);
|
||||
}
|
||||
|
||||
return _console->GetMemoryManager()->GetOpenBus();
|
||||
|
|
31
Core/MMC5MemoryHandler.h
Normal file
31
Core/MMC5MemoryHandler.h
Normal file
|
@ -0,0 +1,31 @@
|
|||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include "IMemoryHandler.h"
|
||||
#include "Console.h"
|
||||
|
||||
class MMC5MemoryHandler : public IMemoryHandler
|
||||
{
|
||||
Console* _console;
|
||||
uint8_t _ppuRegs[8];
|
||||
|
||||
public:
|
||||
MMC5MemoryHandler(Console* console)
|
||||
{
|
||||
_console = console;
|
||||
memset(_ppuRegs, 0, sizeof(_ppuRegs));
|
||||
}
|
||||
|
||||
uint8_t GetReg(uint16_t addr)
|
||||
{
|
||||
return _ppuRegs[addr & 0x07];
|
||||
}
|
||||
|
||||
void GetMemoryRanges(MemoryRanges& ranges) override {}
|
||||
uint8_t ReadRAM(uint16_t addr) override { return 0; }
|
||||
|
||||
void WriteRAM(uint16_t addr, uint8_t value) override
|
||||
{
|
||||
_console->GetPpu()->WriteRAM(addr, value);
|
||||
_ppuRegs[addr & 0x07] = value;
|
||||
}
|
||||
};
|
|
@ -63,6 +63,13 @@ void MemoryManager::RegisterIODevice(IMemoryHandler *handler)
|
|||
InitializeMemoryHandlers(_ramWriteHandlers, handler, ranges.GetRAMWriteAddresses(), ranges.GetAllowOverride());
|
||||
}
|
||||
|
||||
void MemoryManager::RegisterWriteHandler(IMemoryHandler* handler, uint32_t start, uint32_t end)
|
||||
{
|
||||
for(uint32_t i = start; i < end; i++) {
|
||||
_ramWriteHandlers[i] = handler;
|
||||
}
|
||||
}
|
||||
|
||||
void MemoryManager::UnregisterIODevice(IMemoryHandler *handler)
|
||||
{
|
||||
MemoryRanges ranges;
|
||||
|
|
|
@ -40,6 +40,7 @@ class MemoryManager : public Snapshotable
|
|||
|
||||
void Reset(bool softReset);
|
||||
void RegisterIODevice(IMemoryHandler *handler);
|
||||
void RegisterWriteHandler(IMemoryHandler* handler, uint32_t start, uint32_t end);
|
||||
void UnregisterIODevice(IMemoryHandler *handler);
|
||||
|
||||
uint8_t DebugRead(uint16_t addr, bool disableSideEffects = true);
|
||||
|
|
19
Core/PPU.cpp
19
Core/PPU.cpp
|
@ -82,7 +82,6 @@ void PPU::Reset()
|
|||
_spriteCount = 0;
|
||||
_secondaryOAMAddr = 0;
|
||||
_sprite0Visible = false;
|
||||
_overflowSpriteAddr = 0;
|
||||
_spriteIndex = 0;
|
||||
_openBus = 0;
|
||||
memset(_openBusDecayStamp, 0, sizeof(_openBusDecayStamp));
|
||||
|
@ -783,7 +782,7 @@ void PPU::LoadExtraSprites()
|
|||
}
|
||||
|
||||
if(loadExtraSprites) {
|
||||
for(uint32_t i = _overflowSpriteAddr; i < 0x100; i += 4) {
|
||||
for(uint32_t i = (_lastVisibleSpriteAddr + 4) & 0xFF; i != _firstVisibleSpriteAddr; i = (i + 4) & 0xFF) {
|
||||
uint8_t spriteY = _spriteRAM[i];
|
||||
if(_scanline >= spriteY && _scanline < spriteY + (_flags.LargeSprites ? 16 : 8)) {
|
||||
LoadSprite(spriteY, _spriteRAM[i + 1], _spriteRAM[i + 2], _spriteRAM[i + 3], true);
|
||||
|
@ -975,19 +974,22 @@ void PPU::ProcessScanline()
|
|||
}
|
||||
}
|
||||
} else if(_cycle >= 321 && _cycle <= 336) {
|
||||
LoadTileInfo();
|
||||
if(_cycle == 321) {
|
||||
if(IsRenderingEnabled()) {
|
||||
LoadExtraSprites();
|
||||
_oamCopybuffer = _secondarySpriteRAM[0];
|
||||
}
|
||||
LoadTileInfo();
|
||||
if(_scanline == -1) {
|
||||
_console->DebugSetLastFramePpuScroll(_state.VideoRamAddr, _state.XScroll, false);
|
||||
}
|
||||
} else if(_prevRenderingEnabled && (_cycle == 328 || _cycle == 336)) {
|
||||
LoadTileInfo();
|
||||
_state.LowBitShift <<= 8;
|
||||
_state.HighBitShift <<= 8;
|
||||
IncHorizontalScrolling();
|
||||
} else {
|
||||
LoadTileInfo();
|
||||
}
|
||||
} else if(_cycle == 337 || _cycle == 339) {
|
||||
if(IsRenderingEnabled()) {
|
||||
|
@ -1014,12 +1016,15 @@ void PPU::ProcessSpriteEvaluation()
|
|||
_sprite0Added = false;
|
||||
_spriteInRange = false;
|
||||
_secondaryOAMAddr = 0;
|
||||
_overflowSpriteAddr = 0;
|
||||
|
||||
_overflowBugCounter = 0;
|
||||
|
||||
_oamCopyDone = false;
|
||||
_spriteAddrH = (_state.SpriteRamAddr >> 2) & 0x3F;
|
||||
_spriteAddrL = _state.SpriteRamAddr & 0x03;
|
||||
|
||||
_firstVisibleSpriteAddr = _spriteAddrH * 4;
|
||||
_lastVisibleSpriteAddr = _firstVisibleSpriteAddr;
|
||||
} else if(_cycle == 256) {
|
||||
_sprite0Visible = _sprite0Added;
|
||||
_spriteCount = (_secondaryOAMAddr >> 2);
|
||||
|
@ -1056,6 +1061,7 @@ void PPU::ProcessSpriteEvaluation()
|
|||
//Done copying all 4 bytes
|
||||
_spriteInRange = false;
|
||||
_spriteAddrL = 0;
|
||||
_lastVisibleSpriteAddr = _spriteAddrH * 4;
|
||||
_spriteAddrH = (_spriteAddrH + 1) & 0x3F;
|
||||
if(_spriteAddrH == 0) {
|
||||
_oamCopyDone = true;
|
||||
|
@ -1073,11 +1079,6 @@ void PPU::ProcessSpriteEvaluation()
|
|||
_oamCopybuffer = _secondarySpriteRAM[_secondaryOAMAddr & 0x1F];
|
||||
|
||||
//8 sprites have been found, check next sprite for overflow + emulate PPU bug
|
||||
if(_overflowSpriteAddr == 0) {
|
||||
//Used to remove sprite limit
|
||||
_overflowSpriteAddr = _spriteAddrH * 4;
|
||||
}
|
||||
|
||||
if(_spriteInRange) {
|
||||
//Sprite is visible, consider this to be an overflow
|
||||
_statusFlags.SpriteOverflow = true;
|
||||
|
|
|
@ -75,7 +75,8 @@ class PPU : public IMemoryHandler, public Snapshotable
|
|||
uint32_t _secondaryOAMAddr;
|
||||
bool _sprite0Visible;
|
||||
|
||||
uint32_t _overflowSpriteAddr;
|
||||
uint8_t _firstVisibleSpriteAddr;
|
||||
uint8_t _lastVisibleSpriteAddr;
|
||||
uint32_t _spriteIndex;
|
||||
|
||||
uint8_t _openBus;
|
||||
|
|
Loading…
Add table
Reference in a new issue