Core: Rewrite DMC/OAM DMA, tweak PPU timings, add option to simulate PPU/CPU alignments

This commit is contained in:
Sour 2019-11-10 17:35:29 -05:00
parent bc335e104d
commit a72acc9f1a
36 changed files with 341 additions and 442 deletions

View file

@ -171,7 +171,7 @@ void APU::SetNeedToRun()
bool APU::NeedToRun(uint32_t currentCycle) bool APU::NeedToRun(uint32_t currentCycle)
{ {
if(_needToRun || _deltaModulationChannel->NeedToRun()) { if(_deltaModulationChannel->NeedToRun() || _needToRun) {
//Need to run whenever we alter the length counters //Need to run whenever we alter the length counters
//Need to run every cycle when DMC is running to get accurate emulation (CPU stalling, interaction with sprite DMA, etc.) //Need to run every cycle when DMC is running to get accurate emulation (CPU stalling, interaction with sprite DMA, etc.)
_needToRun = false; _needToRun = false;
@ -210,22 +210,13 @@ void APU::EndFrame()
void APU::ProcessCpuClock() void APU::ProcessCpuClock()
{ {
if(_apuEnabled) { if(_apuEnabled) {
if(_settings->GetOverclockRate() == 100 || !_settings->GetOverclockAdjustApu()) { Exec();
Exec();
} else {
_cyclesNeeded += 1.0 / ((double)_settings->GetOverclockRate() / 100.0);
while(_cyclesNeeded >= 1.0) {
Exec();
_cyclesNeeded--;
}
}
} }
} }
void APU::Reset(bool softReset) void APU::Reset(bool softReset)
{ {
_apuEnabled = true; _apuEnabled = true;
_cyclesNeeded = 0;
_currentCycle = 0; _currentCycle = 0;
_previousCycle = 0; _previousCycle = 0;
_squareChannel[0]->Reset(softReset); _squareChannel[0]->Reset(softReset);
@ -253,7 +244,7 @@ void APU::StreamState(bool saving)
SnapshotInfo deltaModulationChannel{ _deltaModulationChannel.get() }; SnapshotInfo deltaModulationChannel{ _deltaModulationChannel.get() };
SnapshotInfo frameCounter{ _frameCounter.get() }; SnapshotInfo frameCounter{ _frameCounter.get() };
SnapshotInfo mixer{ _mixer.get() }; SnapshotInfo mixer{ _mixer.get() };
Stream(_nesModel, squareChannel0, squareChannel1, triangleChannel, noiseChannel, deltaModulationChannel, frameCounter, mixer, _cyclesNeeded); Stream(_nesModel, squareChannel0, squareChannel1, triangleChannel, noiseChannel, deltaModulationChannel, frameCounter, mixer);
} }
void APU::AddExpansionAudioDelta(AudioChannel channel, int16_t delta) void APU::AddExpansionAudioDelta(AudioChannel channel, int16_t delta)
@ -275,9 +266,14 @@ bool APU::IsApuEnabled()
return _apuEnabled; return _apuEnabled;
} }
void APU::FillDmcReadBuffer() uint16_t APU::GetDmcReadAddress()
{ {
_deltaModulationChannel->FillReadBuffer(); return _deltaModulationChannel->GetDmcReadAddress();
}
void APU::SetDmcReadBuffer(uint8_t value)
{
_deltaModulationChannel->SetDmcReadBuffer(value);
} }
ApuState APU::GetState() ApuState APU::GetState()

View file

@ -40,8 +40,6 @@ class APU : public Snapshotable, public IMemoryHandler
NesModel _nesModel; NesModel _nesModel;
double _cyclesNeeded;
private: private:
__forceinline bool NeedToRun(uint32_t currentCycle); __forceinline bool NeedToRun(uint32_t currentCycle);
@ -73,6 +71,7 @@ class APU : public Snapshotable, public IMemoryHandler
void AddExpansionAudioDelta(AudioChannel channel, int16_t delta); void AddExpansionAudioDelta(AudioChannel channel, int16_t delta);
void SetApuStatus(bool enabled); void SetApuStatus(bool enabled);
bool IsApuEnabled(); bool IsApuEnabled();
void FillDmcReadBuffer(); uint16_t GetDmcReadAddress();
void SetDmcReadBuffer(uint8_t value);
void SetNeedToRun(); void SetNeedToRun();
}; };

View file

@ -64,8 +64,7 @@ public:
void StreamState(bool saving) override void StreamState(bool saving) override
{ {
int32_t unusednextIrqCycle = 0; Stream(_previousCycle, _currentStep, _stepMode, _inhibitIRQ, _nesModel, _blockFrameCounterTick, _writeDelayCounter, _newValue);
Stream(unusednextIrqCycle, _previousCycle, _currentStep, _stepMode, _inhibitIRQ, _nesModel, _blockFrameCounterTick, _writeDelayCounter, _newValue);
if(!saving) { if(!saving) {
SetNesModel(_nesModel); SetNesModel(_nesModel);
@ -185,11 +184,11 @@ public:
//Reset sequence after $4017 is written to //Reset sequence after $4017 is written to
if(_console->GetCpu()->GetCycleCount() & 0x01) { if(_console->GetCpu()->GetCycleCount() & 0x01) {
//"If the write occurs during an APU cycle, the effects occur 3 CPU cycles after the $4017 write cycle"
_writeDelayCounter = 3;
} else {
//"If the write occurs between APU cycles, the effects occur 4 CPU cycles after the write cycle. " //"If the write occurs between APU cycles, the effects occur 4 CPU cycles after the write cycle. "
_writeDelayCounter = 4; _writeDelayCounter = 4;
} else {
//"If the write occurs during an APU cycle, the effects occur 3 CPU cycles after the $4017 write cycle"
_writeDelayCounter = 3;
} }
_inhibitIRQ = (value & 0x40) == 0x40; _inhibitIRQ = (value & 0x40) == 0x40;

View file

@ -10,20 +10,11 @@ BaseExpansionAudio::BaseExpansionAudio(shared_ptr<Console> console)
void BaseExpansionAudio::StreamState(bool saving) void BaseExpansionAudio::StreamState(bool saving)
{ {
Stream(_clocksNeeded);
} }
void BaseExpansionAudio::Clock() void BaseExpansionAudio::Clock()
{ {
if(_console->GetApu()->IsApuEnabled()) { if(_console->GetApu()->IsApuEnabled()) {
if(_console->GetSettings()->GetOverclockRate() == 100 || !_console->GetSettings()->GetOverclockAdjustApu()) { ClockAudio();
ClockAudio();
} else {
_clocksNeeded += 1.0 / ((double)_console->GetSettings()->GetOverclockRate() / 100);
while(_clocksNeeded >= 1.0) {
ClockAudio();
_clocksNeeded--;
}
}
} }
} }

View file

@ -7,9 +7,6 @@ class MemoryManager;
class BaseExpansionAudio : public Snapshotable class BaseExpansionAudio : public Snapshotable
{ {
private:
double _clocksNeeded = 0;
protected: protected:
shared_ptr<Console> _console = nullptr; shared_ptr<Console> _console = nullptr;

View file

@ -1,4 +1,6 @@
#include "stdafx.h" #include "stdafx.h"
#include <random>
#include <assert.h>
#include "CPU.h" #include "CPU.h"
#include "PPU.h" #include "PPU.h"
#include "APU.h" #include "APU.h"
@ -60,12 +62,15 @@ CPU::CPU(shared_ptr<Console> console)
_state = {}; _state = {};
_cycleCount = 0; _cycleCount = 0;
_operand = 0; _operand = 0;
_spriteDmaCounter = 0;
_spriteDmaTransfer = false; _spriteDmaTransfer = false;
_dmcCounter = 0; _spriteDmaOffset = 0;
_needHalt = false;
_ppuOffset = 0;
_startClockCount = 6;
_endClockCount = 6;
_masterClock = 0;
_dmcDmaRunning = false; _dmcDmaRunning = false;
_cpuWrite = false; _cpuWrite = false;
_writeAddr = 0;
_irqMask = 0; _irqMask = 0;
_state = {}; _state = {};
_prevRunIrq = false; _prevRunIrq = false;
@ -76,12 +81,10 @@ void CPU::Reset(bool softReset, NesModel model)
{ {
_state.NMIFlag = false; _state.NMIFlag = false;
_state.IRQFlag = 0; _state.IRQFlag = 0;
_cycleCount = -1;
_spriteDmaTransfer = false; _spriteDmaTransfer = false;
_spriteDmaCounter = 0; _spriteDmaOffset = 0;
_needHalt = false;
_dmcCounter = -1;
_dmcDmaRunning = false; _dmcDmaRunning = false;
_warnOnCrash = true; _warnOnCrash = true;
@ -106,13 +109,50 @@ void CPU::Reset(bool softReset, NesModel model)
_runIrq = false; _runIrq = false;
} }
//The CPU takes some cycles before starting its execution after a reset/power up if(!softReset) {
for(int i = 0; i < (model == NesModel::NTSC ? 28 : 30); i++) { uint8_t ppuDivider;
_console->GetPpu()->Exec(); uint8_t cpuDivider;
switch(model) {
default:
case NesModel::NTSC:
ppuDivider = 4;
cpuDivider = 12;
break;
case NesModel::PAL:
ppuDivider = 5;
cpuDivider = 16;
break;
case NesModel::Dendy:
ppuDivider = 5;
cpuDivider = 15;
break;
}
_cycleCount = -1;
_masterClock = 0;
uint8_t cpuOffset = 0;
if(_console->GetSettings()->CheckFlag(EmulationFlags::RandomizeCpuPpuAlignment)) {
std::random_device rd;
std::mt19937 mt(rd());
std::uniform_int_distribution<> distPpu(0, ppuDivider - 1);
std::uniform_int_distribution<> distCpu(0, cpuDivider - 1);
_ppuOffset = distPpu(mt);
cpuOffset += distCpu(mt);
} else {
_ppuOffset = 2;
cpuOffset = 0;
}
_masterClock += cpuDivider + cpuOffset;
} }
for(int i = 0; i < 10; i++) { //The CPU takes 8 cycles before it starts executing the ROM's code after a reset/power up
_console->GetApu()->ProcessCpuClock(); for(int i = 0; i < 8; i++) {
StartCpuCycle(true);
EndCpuCycle(true);
} }
} }
@ -200,19 +240,10 @@ void CPU::MemoryWrite(uint16_t addr, uint8_t value, MemoryOperationType operatio
_writeCounter++; _writeCounter++;
} }
#else #else
_cpuWrite = true;; _cpuWrite = true;
_writeAddr = addr; StartCpuCycle(false);
IncCycleCount();
while(_dmcDmaRunning) {
IncCycleCount();
}
_memoryManager->Write(addr, value, operationType); _memoryManager->Write(addr, value, operationType);
EndCpuCycle(false);
//DMA DMC might have started after a write to $4015, stall CPU if needed
while(_dmcDmaRunning) {
IncCycleCount();
}
_cpuWrite = false; _cpuWrite = false;
#endif #endif
} }
@ -228,20 +259,11 @@ uint8_t CPU::MemoryRead(uint16_t addr, MemoryOperationType operationType) {
} }
return value; return value;
#else #else
IncCycleCount(); ProcessPendingDma(addr);
while(_dmcDmaRunning) {
//Stall CPU until we can process a DMC read
if((addr != 0x4016 && addr != 0x4017 && (_cycleCount & 0x01)) || _dmcCounter == 1) {
//While the CPU is stalled, reads are performed on the current address
//Reads are only performed every other cycle? This fixes "dma_2007_read" test
//This behavior causes the $4016/7 data corruption when a DMC is running.
//When reading $4016/7, only the last read counts (because this only occurs to low-to-high transitions, i.e once in this case)
_memoryManager->Read(addr);
}
IncCycleCount();
}
StartCpuCycle(true);
uint8_t value = _memoryManager->Read(addr, operationType); uint8_t value = _memoryManager->Read(addr, operationType);
EndCpuCycle(true);
return value; return value;
#endif #endif
} }
@ -292,90 +314,112 @@ uint16_t CPU::FetchOperand()
} }
void CPU::IncCycleCount() void CPU::EndCpuCycle(bool forRead)
{ {
_cycleCount++; _masterClock += forRead ? (_endClockCount + 1) : (_endClockCount - 1);
_console->GetPpu()->Run(_masterClock - _ppuOffset);
if(_dmcDmaRunning) { //"it's really the status of the interrupt lines at the end of the second-to-last cycle that matters."
//CPU is being stalled by the DMC's DMA transfer //Keep the irq lines values from the previous cycle. The before-to-last cycle's values will be used
_dmcCounter--; _prevRunIrq = _runIrq;
if(_dmcCounter == 0) { _runIrq = _state.NMIFlag || ((_state.IRQFlag & _irqMask) > 0 && !CheckFlag(PSFlags::Interrupt));
//Update the DMC buffer when the stall period is completed }
_dmcDmaRunning = false;
#ifndef DUMMYCPU void CPU::StartCpuCycle(bool forRead)
_console->GetApu()->FillDmcReadBuffer(); {
_console->DebugAddTrace("DMC DMA End"); _masterClock += forRead ? (_startClockCount - 1) : (_startClockCount + 1);
#endif _cycleCount++;
} _console->GetPpu()->Run(_masterClock - _ppuOffset);
_console->ProcessCpuClock();
}
void CPU::ProcessPendingDma(uint16_t readAddress)
{
if(!_needHalt) {
return;
} }
_console->ProcessCpuClock(); //"If this cycle is a read, hijack the read, discard the value, and prevent all other actions that occur on this cycle (PC not incremented, etc)"
StartCpuCycle(true);
_memoryManager->Read(readAddress, MemoryOperationType::DummyRead);
EndCpuCycle(true);
_needHalt = false;
if(!_spriteDmaTransfer && !_dmcDmaRunning) { uint16_t spriteDmaCounter = 0;
//IRQ flags are ignored during Sprite DMA - fixes irq_and_dma uint8_t spriteReadAddr = 0;
uint8_t readValue = 0;
bool skipDummyReads = (readAddress == 0x4016 || readAddress == 0x4017);
//"it's really the status of the interrupt lines at the end of the second-to-last cycle that matters." auto processCycle = [this] {
//Keep the irq lines values from the previous cycle. The before-to-last cycle's values will be used //Sprite DMA cycles count as halt/dummy cycles for the DMC DMA when both run at the same time
_prevRunIrq = _runIrq; if(_needHalt) {
_runIrq = _state.NMIFlag || ((_state.IRQFlag & _irqMask) > 0 && !CheckFlag(PSFlags::Interrupt)); _needHalt = false;
} else if(_needDummyRead) {
_needDummyRead = false;
}
StartCpuCycle(true);
};
while(_dmcDmaRunning || _spriteDmaTransfer) {
bool getCycle = (_cycleCount & 0x01) == 0;
if(getCycle) {
if(_dmcDmaRunning && !_needHalt && !_needDummyRead) {
//DMC DMA is ready to read a byte (both halt and dummy read cycles were performed before this)
processCycle();
readValue = _memoryManager->Read(_console->GetApu()->GetDmcReadAddress(), MemoryOperationType::DmcRead);
EndCpuCycle(true);
_console->GetApu()->SetDmcReadBuffer(readValue);
_dmcDmaRunning = false;
} else if(_spriteDmaTransfer) {
//DMC DMA is not running, or not ready, run sprite DMA
processCycle();
readValue = _memoryManager->Read(_spriteDmaOffset * 0x100 + spriteReadAddr);
EndCpuCycle(true);
spriteReadAddr++;
spriteDmaCounter++;
} else {
//DMC DMA is running, but not ready (need halt/dummy read) and sprite DMA isn't runnnig, perform a dummy read
assert(_needHalt || _needDummyRead);
processCycle();
if(!skipDummyReads) {
_memoryManager->Read(readAddress, MemoryOperationType::DummyRead);
}
EndCpuCycle(true);
}
} else {
if(_spriteDmaTransfer && (spriteDmaCounter & 0x01)) {
//Sprite DMA write cycle (only do this if a sprite dma read was performed last cycle)
processCycle();
_memoryManager->Write(0x2004, readValue, MemoryOperationType::Write);
EndCpuCycle(true);
spriteDmaCounter++;
if(spriteDmaCounter == 0x200) {
_spriteDmaTransfer = false;
}
} else {
//Align to read cycle before starting sprite DMA (or align to perform DMC read)
processCycle();
if(!skipDummyReads) {
_memoryManager->Read(readAddress, MemoryOperationType::DummyRead);
}
EndCpuCycle(true);
}
}
} }
} }
void CPU::RunDMATransfer(uint8_t offsetValue) void CPU::RunDMATransfer(uint8_t offsetValue)
{ {
_console->DebugAddTrace("Sprite DMA Start");
_spriteDmaTransfer = true; _spriteDmaTransfer = true;
_spriteDmaOffset = offsetValue;
//"The CPU is suspended during the transfer, which will take 513 or 514 cycles after the $4014 write tick." _needHalt = true;
//"(1 dummy read cycle while waiting for writes to complete, +1 if on an odd CPU cycle, then 256 alternating read/write cycles.)"
if(_cycleCount % 2 != 0) {
DummyRead();
}
DummyRead();
_spriteDmaCounter = 256;
//DMA transfer starts at SpriteRamAddr and wraps around
for(int i = 0; i < 0x100; i++) {
//Read value
uint8_t readValue = MemoryRead(offsetValue * 0x100 + i);
//Write to sprite ram via $2004 ("DMA is implemented in the 2A03/7 chip and works by repeatedly writing to OAMDATA")
MemoryWrite(0x2004, readValue);
_spriteDmaCounter--;
}
_spriteDmaTransfer = false;
_console->DebugAddTrace("Sprite DMA End");
} }
void CPU::StartDmcTransfer() void CPU::StartDmcTransfer()
{ {
//"DMC DMA adds 4 cycles normally, 2 if it lands on the $4014 write or during OAM DMA"
//3 cycles if it lands on the last write cycle of any instruction
_console->DebugAddTrace("DMC DMA Start");
_dmcDmaRunning = true; _dmcDmaRunning = true;
if(_spriteDmaTransfer) { _needDummyRead = true;
if(_spriteDmaCounter == 2) { _needHalt = true;
_dmcCounter = 1;
} else if(_spriteDmaCounter == 1) {
_dmcCounter = 3;
} else {
_dmcCounter = 2;
}
} else {
if(_cpuWrite) {
if(_writeAddr == 0x4014) {
_dmcCounter = 2;
} else {
_dmcCounter = 3;
}
} else {
_dmcCounter = 4;
}
}
} }
uint32_t CPU::GetClockRate(NesModel model) uint32_t CPU::GetClockRate(NesModel model)
@ -388,21 +432,39 @@ uint32_t CPU::GetClockRate(NesModel model)
} }
} }
void CPU::SetMasterClockDivider(NesModel region)
{
switch(region) {
case NesModel::NTSC:
_startClockCount = 6;
_endClockCount = 6;
break;
case NesModel::PAL:
_startClockCount = 8;
_endClockCount = 8;
break;
case NesModel::Dendy:
_startClockCount = 7;
_endClockCount = 8;
break;
}
}
void CPU::StreamState(bool saving) void CPU::StreamState(bool saving)
{ {
EmulationSettings* settings = _console->GetSettings(); EmulationSettings* settings = _console->GetSettings();
uint32_t overclockRate = settings->GetOverclockRateSetting();
bool overclockAdjustApu = settings->GetOverclockAdjustApu();
uint32_t extraScanlinesBeforeNmi = settings->GetPpuExtraScanlinesBeforeNmi(); uint32_t extraScanlinesBeforeNmi = settings->GetPpuExtraScanlinesBeforeNmi();
uint32_t extraScanlinesAfterNmi = settings->GetPpuExtraScanlinesAfterNmi(); uint32_t extraScanlinesAfterNmi = settings->GetPpuExtraScanlinesAfterNmi();
uint32_t dipSwitches = _console->GetSettings()->GetDipSwitches(); uint32_t dipSwitches = _console->GetSettings()->GetDipSwitches();
Stream(_state.PC, _state.SP, _state.PS, _state.A, _state.X, _state.Y, _cycleCount, _state.NMIFlag, Stream(_state.PC, _state.SP, _state.PS, _state.A, _state.X, _state.Y, _cycleCount, _state.NMIFlag,
_state.IRQFlag, _dmcCounter, _dmcDmaRunning, _spriteDmaCounter, _spriteDmaTransfer, _state.IRQFlag, _dmcDmaRunning, _spriteDmaTransfer,
overclockRate, overclockAdjustApu, extraScanlinesBeforeNmi, extraScanlinesBeforeNmi, dipSwitches); extraScanlinesBeforeNmi, extraScanlinesBeforeNmi, dipSwitches,
_needDummyRead, _needHalt, _startClockCount, _endClockCount, _ppuOffset, _masterClock);
if(!saving) { if(!saving) {
settings->SetOverclockRate(overclockRate, overclockAdjustApu);
settings->SetPpuNmiConfig(extraScanlinesBeforeNmi, extraScanlinesAfterNmi); settings->SetPpuNmiConfig(extraScanlinesBeforeNmi, extraScanlinesAfterNmi);
settings->SetDipSwitches(dipSwitches); settings->SetDipSwitches(dipSwitches);
} }

View file

@ -32,19 +32,23 @@ private:
typedef void(CPU::*Func)(); typedef void(CPU::*Func)();
uint64_t _cycleCount; uint64_t _cycleCount;
uint64_t _masterClock;
uint8_t _ppuOffset;
uint8_t _startClockCount;
uint8_t _endClockCount;
uint16_t _operand; uint16_t _operand;
Func _opTable[256]; Func _opTable[256];
AddrMode _addrMode[256]; AddrMode _addrMode[256];
AddrMode _instAddrMode; AddrMode _instAddrMode;
uint16_t _spriteDmaCounter; bool _needHalt = false;
bool _spriteDmaTransfer; bool _spriteDmaTransfer = false;
bool _dmcDmaRunning = false;
bool _needDummyRead = false;
uint8_t _spriteDmaOffset;
int8_t _dmcCounter;
bool _dmcDmaRunning;
bool _cpuWrite = false; bool _cpuWrite = false;
uint16_t _writeAddr = 0;
uint8_t _irqMask; uint8_t _irqMask;
@ -69,8 +73,10 @@ private:
bool _isDummyRead[10]; bool _isDummyRead[10];
#endif #endif
void IncCycleCount(); __forceinline void StartCpuCycle(bool forRead);
uint16_t FetchOperand(); __forceinline void ProcessPendingDma(uint16_t readAddress);
__forceinline uint16_t FetchOperand();
__forceinline void EndCpuCycle(bool forRead);
void IRQ(); void IRQ();
uint8_t GetOPCode() uint8_t GetOPCode()
@ -778,6 +784,7 @@ public:
CPU(shared_ptr<Console> console); CPU(shared_ptr<Console> console);
uint64_t GetCycleCount() { return _cycleCount; } uint64_t GetCycleCount() { return _cycleCount; }
void SetMasterClockDivider(NesModel region);
void SetNmiFlag() { _state.NMIFlag = true; } void SetNmiFlag() { _state.NMIFlag = true; }
void ClearNmiFlag() { _state.NMIFlag = false; } void ClearNmiFlag() { _state.NMIFlag = false; }
void SetIrqMask(uint8_t mask) { _irqMask = mask; } void SetIrqMask(uint8_t mask) { _irqMask = mask; }
@ -832,11 +839,13 @@ public:
_cycleCount = c->_cycleCount; _cycleCount = c->_cycleCount;
_operand = c->_operand; _operand = c->_operand;
_spriteDmaCounter = c->_spriteDmaCounter;
_spriteDmaTransfer = c->_spriteDmaTransfer; _spriteDmaTransfer = c->_spriteDmaTransfer;
_dmcCounter = c->_dmcCounter; _needHalt = c->_needHalt;
_dmcDmaRunning = c->_dmcDmaRunning; _dmcDmaRunning = c->_dmcDmaRunning;
_cpuWrite = c->_cpuWrite; _cpuWrite = c->_cpuWrite;
_needDummyRead = c->_needDummyRead;
_needHalt = c->_needHalt;
_spriteDmaOffset = c->_spriteDmaOffset;
_irqMask = c->_irqMask; _irqMask = c->_irqMask;
_prevRunIrq = c->_prevRunIrq; _prevRunIrq = c->_prevRunIrq;
_runIrq = c->_runIrq; _runIrq = c->_runIrq;

View file

@ -421,9 +421,6 @@ bool Console::Initialize(VirtualFile &romFile, VirtualFile &patchFile)
string modelName = _model == NesModel::PAL ? "PAL" : (_model == NesModel::Dendy ? "Dendy" : "NTSC"); string modelName = _model == NesModel::PAL ? "PAL" : (_model == NesModel::Dendy ? "Dendy" : "NTSC");
string messageTitle = MessageManager::Localize("GameLoaded") + " (" + modelName + ")"; string messageTitle = MessageManager::Localize("GameLoaded") + " (" + modelName + ")";
MessageManager::DisplayMessage(messageTitle, FolderUtilities::GetFilename(GetRomInfo().RomName, false)); MessageManager::DisplayMessage(messageTitle, FolderUtilities::GetFilename(GetRomInfo().RomName, false));
if(_settings->GetOverclockRate() != 100) {
MessageManager::DisplayMessage("ClockRate", std::to_string(_settings->GetOverclockRate()) + "%");
}
_settings->ClearFlags(EmulationFlags::ForceMaxSpeed); _settings->ClearFlags(EmulationFlags::ForceMaxSpeed);
if(_slave) { if(_slave) {
@ -451,7 +448,6 @@ bool Console::Initialize(VirtualFile &romFile, VirtualFile &patchFile)
void Console::ProcessCpuClock() void Console::ProcessCpuClock()
{ {
_mapper->ProcessCpuClock(); _mapper->ProcessCpuClock();
_ppu->ProcessCpuClock();
_apu->ProcessCpuClock(); _apu->ProcessCpuClock();
} }
@ -967,6 +963,7 @@ void Console::UpdateNesModel(bool sendNotification)
} }
} }
_cpu->SetMasterClockDivider(model);
_mapper->SetNesModel(model); _mapper->SetNesModel(model);
_ppu->SetNesModel(model); _ppu->SetNesModel(model);
_apu->SetNesModel(model); _apu->SetNesModel(model);

View file

@ -58,10 +58,15 @@ void DeltaModulationChannel::StartDmcTransfer()
} }
} }
void DeltaModulationChannel::FillReadBuffer() uint16_t DeltaModulationChannel::GetDmcReadAddress()
{
return _currentAddr;
}
void DeltaModulationChannel::SetDmcReadBuffer(uint8_t value)
{ {
if(_bytesRemaining > 0) { if(_bytesRemaining > 0) {
_readBuffer = _console->GetMemoryManager()->Read(_currentAddr, MemoryOperationType::DmcRead); _readBuffer = value;
_bufferEmpty = false; _bufferEmpty = false;
//"The address is incremented; if it exceeds $FFFF, it is wrapped around to $8000." //"The address is incremented; if it exceeds $FFFF, it is wrapped around to $8000."
@ -204,12 +209,16 @@ void DeltaModulationChannel::SetEnabled(bool enabled)
_needToRun = false; _needToRun = false;
} else if(_bytesRemaining == 0) { } else if(_bytesRemaining == 0) {
InitSample(); InitSample();
StartDmcTransfer(); _needInit = true;
} }
} }
bool DeltaModulationChannel::NeedToRun() bool DeltaModulationChannel::NeedToRun()
{ {
if(_needInit && (_console->GetCpu()->GetCycleCount() & 0x01) == 0) {
StartDmcTransfer();
_needInit = false;
}
return _needToRun; return _needToRun;
} }

View file

@ -26,6 +26,7 @@ private:
uint8_t _bitsRemaining = 0; uint8_t _bitsRemaining = 0;
bool _silenceFlag = true; bool _silenceFlag = true;
bool _needToRun = false; bool _needToRun = false;
bool _needInit = false;
uint8_t _lastValue4011 = 0; uint8_t _lastValue4011 = 0;
@ -47,7 +48,8 @@ public:
void SetEnabled(bool enabled); void SetEnabled(bool enabled);
void StartDmcTransfer(); void StartDmcTransfer();
void FillReadBuffer(); uint16_t GetDmcReadAddress();
void SetDmcReadBuffer(uint8_t value);
ApuDmcState GetState(); ApuDmcState GetState();
}; };

View file

@ -95,6 +95,8 @@ enum EmulationFlags : uint64_t
VsDualMuteMaster = 0x200000000000000, VsDualMuteMaster = 0x200000000000000,
VsDualMuteSlave = 0x400000000000000, VsDualMuteSlave = 0x400000000000000,
RandomizeCpuPpuAlignment = 0x800000000000000,
ForceMaxSpeed = 0x4000000000000000, ForceMaxSpeed = 0x4000000000000000,
ConsoleMode = 0x8000000000000000, ConsoleMode = 0x8000000000000000,
}; };
@ -648,13 +650,9 @@ private:
uint32_t _rewindBufferSize = 300; uint32_t _rewindBufferSize = 300;
bool _hasOverclock = false;
uint32_t _overclockRate = 100;
bool _overclockAdjustApu = true;
bool _disableOverclocking = false; bool _disableOverclocking = false;
uint32_t _extraScanlinesBeforeNmi = 0; uint32_t _extraScanlinesBeforeNmi = 0;
uint32_t _extraScanlinesAfterNmi = 0; uint32_t _extraScanlinesAfterNmi = 0;
double _effectiveOverclockRate = 100;
OverscanDimensions _overscan; OverscanDimensions _overscan;
VideoFilterType _videoFilterType = VideoFilterType::None; VideoFilterType _videoFilterType = VideoFilterType::None;
@ -1041,55 +1039,11 @@ public:
} }
uint32_t GetEmulationSpeed(bool ignoreTurbo = false); uint32_t GetEmulationSpeed(bool ignoreTurbo = false);
void UpdateEffectiveOverclockRate()
{
if(_disableOverclocking) {
_effectiveOverclockRate = 100;
} else {
_effectiveOverclockRate = _overclockRate;
}
_hasOverclock = _effectiveOverclockRate != 100;
_audioSettingsChanged = true;
}
void DisableOverclocking(bool disabled) void DisableOverclocking(bool disabled)
{ {
if(_disableOverclocking != disabled) { if(_disableOverclocking != disabled) {
_disableOverclocking = disabled; _disableOverclocking = disabled;
UpdateEffectiveOverclockRate();
}
}
uint32_t GetOverclockRateSetting()
{
return _overclockRate;
}
bool HasOverclock()
{
return _hasOverclock;
}
double GetOverclockRate()
{
return _effectiveOverclockRate;
}
bool GetOverclockAdjustApu()
{
return _overclockAdjustApu;
}
void SetOverclockRate(uint32_t overclockRate, bool adjustApu)
{
if(_overclockRate != overclockRate || _overclockAdjustApu != adjustApu) {
_overclockRate = overclockRate;
_overclockAdjustApu = adjustApu;
UpdateEffectiveOverclockRate();
MessageManager::DisplayMessage("ClockRate", std::to_string((uint32_t)GetOverclockRate()) + "%");
} }
} }
@ -1112,8 +1066,6 @@ public:
_extraScanlinesBeforeNmi = extraScanlinesBeforeNmi; _extraScanlinesBeforeNmi = extraScanlinesBeforeNmi;
_extraScanlinesAfterNmi = extraScanlinesAfterNmi; _extraScanlinesAfterNmi = extraScanlinesAfterNmi;
UpdateEffectiveOverclockRate();
} }
} }

View file

@ -197,7 +197,7 @@ void MemoryAccessCounter::GetNametableChangedData(bool ntChangedData[])
uint64_t cpuCycle = _debugger->GetConsole()->GetCpu()->GetCycleCount(); uint64_t cpuCycle = _debugger->GetConsole()->GetCpu()->GetCycleCount();
NesModel model = _debugger->GetConsole()->GetModel(); NesModel model = _debugger->GetConsole()->GetModel();
double frameRate = model == NesModel::NTSC ? 60.1 : 50.01; double frameRate = model == NesModel::NTSC ? 60.1 : 50.01;
double overclockRate = _debugger->GetConsole()->GetPpu()->GetOverclockRate() * (_debugger->GetConsole()->GetSettings()->GetOverclockRate() / 100); double overclockRate = _debugger->GetConsole()->GetPpu()->GetOverclockRate() * 100;
int32_t cyclesPerFrame = (int32_t)(_debugger->GetConsole()->GetCpu()->GetClockRate(model) / frameRate * overclockRate); int32_t cyclesPerFrame = (int32_t)(_debugger->GetConsole()->GetCpu()->GetClockRate(model) / frameRate * overclockRate);
for(int i = 0; i < 0x1000; i++) { for(int i = 0; i < 0x1000; i++) {

View file

@ -247,14 +247,6 @@ void MesenMovie::ApplySettings()
settings->SetZapperDetectionRadius(LoadInt(_settings, MovieKeys::ZapperDetectionRadius)); settings->SetZapperDetectionRadius(LoadInt(_settings, MovieKeys::ZapperDetectionRadius));
uint32_t cpuClockRate = LoadInt(_settings, MovieKeys::CpuClockRate);
if(cpuClockRate != 100) {
bool adjustApu = LoadBool(_settings, MovieKeys::OverclockAdjustApu);
settings->SetOverclockRate(cpuClockRate, adjustApu);
} else {
settings->SetOverclockRate(100, true);
}
settings->SetPpuNmiConfig( settings->SetPpuNmiConfig(
LoadInt(_settings, MovieKeys::ExtraScanlinesBeforeNmi), LoadInt(_settings, MovieKeys::ExtraScanlinesBeforeNmi),
LoadInt(_settings, MovieKeys::ExtraScanlinesAfterNmi) LoadInt(_settings, MovieKeys::ExtraScanlinesAfterNmi)

View file

@ -107,14 +107,10 @@ void MovieRecorder::GetGameSettings(stringstream &out)
WriteString(out, MovieKeys::ExpansionDevice, ExpansionPortDeviceNames[(int)settings->GetExpansionDevice()]); WriteString(out, MovieKeys::ExpansionDevice, ExpansionPortDeviceNames[(int)settings->GetExpansionDevice()]);
} }
WriteInt(out, MovieKeys::CpuClockRate, settings->GetOverclockRateSetting());
WriteInt(out, MovieKeys::ExtraScanlinesBeforeNmi, settings->GetPpuExtraScanlinesBeforeNmi()); WriteInt(out, MovieKeys::ExtraScanlinesBeforeNmi, settings->GetPpuExtraScanlinesBeforeNmi());
WriteInt(out, MovieKeys::ExtraScanlinesAfterNmi, settings->GetPpuExtraScanlinesAfterNmi()); WriteInt(out, MovieKeys::ExtraScanlinesAfterNmi, settings->GetPpuExtraScanlinesAfterNmi());
WriteInt(out, MovieKeys::InputPollScanline, settings->GetInputPollScanline()); WriteInt(out, MovieKeys::InputPollScanline, settings->GetInputPollScanline());
if(settings->GetOverclockRateSetting() != 100) {
WriteBool(out, MovieKeys::OverclockAdjustApu, settings->GetOverclockAdjustApu());
}
WriteBool(out, MovieKeys::DisablePpu2004Reads, settings->CheckFlag(EmulationFlags::DisablePpu2004Reads)); WriteBool(out, MovieKeys::DisablePpu2004Reads, settings->CheckFlag(EmulationFlags::DisablePpu2004Reads));
WriteBool(out, MovieKeys::DisablePaletteRead, settings->CheckFlag(EmulationFlags::DisablePaletteRead)); WriteBool(out, MovieKeys::DisablePaletteRead, settings->CheckFlag(EmulationFlags::DisablePaletteRead));
WriteBool(out, MovieKeys::DisableOamAddrBug, settings->CheckFlag(EmulationFlags::DisableOamAddrBug)); WriteBool(out, MovieKeys::DisableOamAddrBug, settings->CheckFlag(EmulationFlags::DisableOamAddrBug));

View file

@ -71,10 +71,8 @@ namespace MovieKeys
constexpr const char* Controller3 = "Controller3"; constexpr const char* Controller3 = "Controller3";
constexpr const char* Controller4 = "Controller4"; constexpr const char* Controller4 = "Controller4";
constexpr const char* ExpansionDevice = "ExpansionDevice"; constexpr const char* ExpansionDevice = "ExpansionDevice";
constexpr const char* CpuClockRate = "CpuClockRate";
constexpr const char* ExtraScanlinesBeforeNmi = "ExtraScanlinesBeforeNmi"; constexpr const char* ExtraScanlinesBeforeNmi = "ExtraScanlinesBeforeNmi";
constexpr const char* ExtraScanlinesAfterNmi = "ExtraScanlinesAfterNmi"; constexpr const char* ExtraScanlinesAfterNmi = "ExtraScanlinesAfterNmi";
constexpr const char* OverclockAdjustApu = "OverclockAdjustApu";
constexpr const char* DisablePpu2004Reads = "DisablePpu2004Reads"; constexpr const char* DisablePpu2004Reads = "DisablePpu2004Reads";
constexpr const char* DisablePaletteRead = "DisablePaletteRead"; constexpr const char* DisablePaletteRead = "DisablePaletteRead";
constexpr const char* DisableOamAddrBug = "DisableOamAddrBug"; constexpr const char* DisableOamAddrBug = "DisableOamAddrBug";

View file

@ -15,6 +15,8 @@
PPU::PPU(shared_ptr<Console> console) PPU::PPU(shared_ptr<Console> console)
{ {
_console = console; _console = console;
_masterClock = 0;
_masterClockDivider = 4;
_settings = _console->GetSettings(); _settings = _console->GetSettings();
_outputBuffers[0] = new uint16_t[256 * 240]; _outputBuffers[0] = new uint16_t[256 * 240];
@ -42,7 +44,7 @@ PPU::~PPU()
void PPU::Reset() void PPU::Reset()
{ {
_cyclesNeeded = 0; _preventVblFlag = false;
_needStateUpdate = false; _needStateUpdate = false;
_prevRenderingEnabled = false; _prevRenderingEnabled = false;
@ -73,7 +75,6 @@ void PPU::Reset()
_oamCopyDone = false; _oamCopyDone = false;
_renderingEnabled = false; _renderingEnabled = false;
_prevRenderingEnabled = false; _prevRenderingEnabled = false;
_cyclesNeeded = 0.0;
memset(_hasSprite, 0, sizeof(_hasSprite)); memset(_hasSprite, 0, sizeof(_hasSprite));
memset(_spriteTiles, 0, sizeof(_spriteTiles)); memset(_spriteTiles, 0, sizeof(_spriteTiles));
@ -118,18 +119,21 @@ void PPU::SetNesModel(NesModel model)
_vblankEnd = 260; _vblankEnd = 260;
_standardNmiScanline = 241; _standardNmiScanline = 241;
_standardVblankEnd = 260; _standardVblankEnd = 260;
_masterClockDivider = 4;
break; break;
case NesModel::PAL: case NesModel::PAL:
_nmiScanline = 241; _nmiScanline = 241;
_vblankEnd = 310; _vblankEnd = 310;
_standardNmiScanline = 241; _standardNmiScanline = 241;
_standardVblankEnd = 310; _standardVblankEnd = 310;
_masterClockDivider = 5;
break; break;
case NesModel::Dendy: case NesModel::Dendy:
_nmiScanline = 291; _nmiScanline = 291;
_vblankEnd = 310; _vblankEnd = 310;
_standardNmiScanline = 291; _standardNmiScanline = 291;
_standardVblankEnd = 310; _standardVblankEnd = 310;
_masterClockDivider = 5;
break; break;
} }
@ -561,8 +565,7 @@ void PPU::UpdateStatusFlag()
_console->GetCpu()->ClearNmiFlag(); _console->GetCpu()->ClearNmiFlag();
if(_cycle == 0) { if(_cycle == 0) {
//"Reading one PPU clock before reads it as clear and never sets the flag or generates NMI for that frame. " _preventVblFlag = true;
_state.Status = ((uint8_t)_statusFlags.SpriteOverflow << 5) | ((uint8_t)_statusFlags.Sprite0Hit << 6);
} }
} }
} }
@ -639,8 +642,8 @@ void PPU::WriteVram(uint16_t addr, uint8_t value)
void PPU::LoadTileInfo() void PPU::LoadTileInfo()
{ {
if(IsRenderingEnabled()) { if(IsRenderingEnabled()) {
switch((_cycle - 1) & 0x07) { switch(_cycle & 0x07) {
case 0: { case 1: {
_previousTile = _currentTile; _previousTile = _currentTile;
_currentTile = _nextTile; _currentTile = _nextTile;
@ -653,18 +656,18 @@ void PPU::LoadTileInfo()
break; break;
} }
case 2: { case 3: {
uint8_t shift = ((_state.VideoRamAddr >> 4) & 0x04) | (_state.VideoRamAddr & 0x02); uint8_t shift = ((_state.VideoRamAddr >> 4) & 0x04) | (_state.VideoRamAddr & 0x02);
_nextTile.PaletteOffset = ((ReadVram(GetAttributeAddr()) >> shift) & 0x03) << 2; _nextTile.PaletteOffset = ((ReadVram(GetAttributeAddr()) >> shift) & 0x03) << 2;
break; break;
} }
case 3: case 5:
_nextTile.LowByte = ReadVram(_nextTile.TileAddr); _nextTile.LowByte = ReadVram(_nextTile.TileAddr);
_nextTile.AbsoluteTileAddr = _console->GetMapper()->ToAbsoluteChrAddress(_nextTile.TileAddr); _nextTile.AbsoluteTileAddr = _console->GetMapper()->ToAbsoluteChrAddress(_nextTile.TileAddr);
break; break;
case 5: case 7:
_nextTile.HighByte = ReadVram(_nextTile.TileAddr + 8); _nextTile.HighByte = ReadVram(_nextTile.TileAddr + 8);
break; break;
} }
@ -864,10 +867,10 @@ void PPU::UpdateGrayscaleAndIntensifyBits()
int pixelNumber; int pixelNumber;
if(_scanline >= 240) { if(_scanline >= 240) {
pixelNumber = 61439; pixelNumber = 61439;
} else if(_cycle < 3) { } else if(_cycle < 4) {
pixelNumber = (_scanline << 8) - 1; pixelNumber = (_scanline << 8) - 1;
} else if(_cycle <= 258) { } else if(_cycle <= 258) {
pixelNumber = (_scanline << 8) + _cycle - 3; pixelNumber = (_scanline << 8) + _cycle - 4;
} else { } else {
pixelNumber = (_scanline << 8) + 255; pixelNumber = (_scanline << 8) + 255;
} }
@ -932,7 +935,7 @@ void PPU::ProcessScanline()
//"OAMADDR is set to 0 during each of ticks 257-320 (the sprite tile loading interval) of the pre-render and visible scanlines." (When rendering) //"OAMADDR is set to 0 during each of ticks 257-320 (the sprite tile loading interval) of the pre-render and visible scanlines." (When rendering)
_state.SpriteRamAddr = 0; _state.SpriteRamAddr = 0;
if((_cycle - 260) % 8 == 0) { if((_cycle - 261) % 8 == 0) {
//Cycle 260, 268, etc. This is an approximation (each tile is actually loaded in 8 steps (e.g from 257 to 264)) //Cycle 260, 268, etc. This is an approximation (each tile is actually loaded in 8 steps (e.g from 257 to 264))
LoadSpriteTileInfo(); LoadSpriteTileInfo();
} else if((_cycle - 257) % 8 == 0) { } else if((_cycle - 257) % 8 == 0) {
@ -1157,7 +1160,6 @@ void PPU::BeginVBlank()
void PPU::TriggerNmi() void PPU::TriggerNmi()
{ {
_statusFlags.VerticalBlank = true;
if(_flags.VBlank) { if(_flags.VBlank) {
_console->GetCpu()->SetNmiFlag(); _console->GetCpu()->SetNmiFlag();
} }
@ -1222,7 +1224,6 @@ void PPU::Exec()
SendFrame(); SendFrame();
_frameCount++; _frameCount++;
} else if(_scanline == _nmiScanline) { } else if(_scanline == _nmiScanline) {
BeginVBlank();
} }
} else { } else {
//Cycle > 0 //Cycle > 0
@ -1231,6 +1232,12 @@ void PPU::Exec()
_console->DebugProcessPpuCycle(); _console->DebugProcessPpuCycle();
if(_scanline < 240) { if(_scanline < 240) {
ProcessScanline(); ProcessScanline();
} else if(_cycle == 1 && _scanline == _nmiScanline) {
if(!_preventVblFlag) {
_statusFlags.VerticalBlank = true;
BeginVBlank();
}
_preventVblFlag = false;
} else if(_nesModel == NesModel::PAL && _scanline >= _palSpriteEvalScanline) { } else if(_nesModel == NesModel::PAL && _scanline >= _palSpriteEvalScanline) {
//"On a PAL machine, because of its extended vertical blank, the PPU begins refreshing OAM roughly 21 scanlines after NMI[2], to prevent it //"On a PAL machine, because of its extended vertical blank, the PPU begins refreshing OAM roughly 21 scanlines after NMI[2], to prevent it
//from decaying during the longer hiatus of rendering. Additionally, it will continue to refresh during the visible portion of the screen //from decaying during the longer hiatus of rendering. Additionally, it will continue to refresh during the visible portion of the screen
@ -1299,31 +1306,6 @@ void PPU::UpdateState()
} }
} }
void PPU::ProcessCpuClock()
{
if(!_settings->HasOverclock()) {
Exec();
Exec();
Exec();
if(_nesModel == NesModel::PAL && _console->GetCpu()->GetCycleCount() % 5 == 0) {
//PAL PPU runs 3.2 clocks for every CPU clock, so we need to run an extra clock every 5 CPU clocks
Exec();
}
} else {
if(_nesModel == NesModel::PAL) {
//PAL PPU runs 3.2 clocks for every CPU clock, so we need to run an extra clock every 5 CPU clocks
_cyclesNeeded += 3.2 / (_settings->GetOverclockRate() / 100.0);
} else {
_cyclesNeeded += 3.0 / (_settings->GetOverclockRate() / 100.0);
}
while(_cyclesNeeded >= 1.0) {
Exec();
_cyclesNeeded--;
}
}
}
uint8_t* PPU::GetSpriteRam() uint8_t* PPU::GetSpriteRam()
{ {
//Used by debugger //Used by debugger
@ -1371,8 +1353,8 @@ void PPU::StreamState(bool saving)
_nextTile.PaletteOffset, _nextTile.TileAddr, _previousTile.LowByte, _previousTile.HighByte, _previousTile.PaletteOffset, _spriteIndex, _spriteCount, _nextTile.PaletteOffset, _nextTile.TileAddr, _previousTile.LowByte, _previousTile.HighByte, _previousTile.PaletteOffset, _spriteIndex, _spriteCount,
_secondaryOAMAddr, _sprite0Visible, _oamCopybuffer, _spriteInRange, _sprite0Added, _spriteAddrH, _spriteAddrL, _oamCopyDone, _nesModel, _secondaryOAMAddr, _sprite0Visible, _oamCopybuffer, _spriteInRange, _sprite0Added, _spriteAddrH, _spriteAddrL, _oamCopyDone, _nesModel,
_prevRenderingEnabled, _renderingEnabled, _openBus, _ignoreVramRead, paletteRam, spriteRam, secondarySpriteRam, _prevRenderingEnabled, _renderingEnabled, _openBus, _ignoreVramRead, paletteRam, spriteRam, secondarySpriteRam,
openBusDecayStamp, _cyclesNeeded, disablePpu2004Reads, disablePaletteRead, disableOamAddrBug, _overflowBugCounter, _updateVramAddr, _updateVramAddrDelay, openBusDecayStamp, disablePpu2004Reads, disablePaletteRead, disableOamAddrBug, _overflowBugCounter, _updateVramAddr, _updateVramAddrDelay,
_needStateUpdate, _ppuBusAddress); _needStateUpdate, _ppuBusAddress, _preventVblFlag, _masterClock);
for(int i = 0; i < 64; i++) { 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); Stream(_spriteTiles[i].SpriteX, _spriteTiles[i].LowByte, _spriteTiles[i].HighByte, _spriteTiles[i].PaletteOffset, _spriteTiles[i].HorizontalMirror, _spriteTiles[i].BackgroundPriority);

View file

@ -36,6 +36,8 @@ class PPU : public IMemoryHandler, public Snapshotable
int32_t _scanline; int32_t _scanline;
uint32_t _cycle; uint32_t _cycle;
uint32_t _frameCount; uint32_t _frameCount;
uint64_t _masterClock;
uint8_t _masterClockDivider;
uint8_t _memoryReadBuffer; uint8_t _memoryReadBuffer;
uint8_t _paletteRAM[0x20]; uint8_t _paletteRAM[0x20];
@ -53,7 +55,7 @@ class PPU : public IMemoryHandler, public Snapshotable
uint16_t _vblankEnd; uint16_t _vblankEnd;
uint16_t _nmiScanline; uint16_t _nmiScanline;
uint16_t _palSpriteEvalScanline; uint16_t _palSpriteEvalScanline;
PPUControlFlags _flags; PPUControlFlags _flags;
PPUStatusFlags _statusFlags; PPUStatusFlags _statusFlags;
@ -91,8 +93,7 @@ class PPU : public IMemoryHandler, public Snapshotable
bool _needStateUpdate; bool _needStateUpdate;
bool _renderingEnabled; bool _renderingEnabled;
bool _prevRenderingEnabled; bool _prevRenderingEnabled;
bool _preventVblFlag;
double _cyclesNeeded;
uint16_t _updateVramAddr; uint16_t _updateVramAddr;
uint8_t _updateVramAddrDelay; uint8_t _updateVramAddrDelay;
@ -199,7 +200,7 @@ class PPU : public IMemoryHandler, public Snapshotable
double GetOverclockRate(); double GetOverclockRate();
void Exec(); void Exec();
void ProcessCpuClock(); __forceinline void Run(uint64_t runTo);
uint32_t GetFrameCount() uint32_t GetFrameCount()
{ {
@ -240,3 +241,11 @@ class PPU : public IMemoryHandler, public Snapshotable
return _currentOutputBuffer[y << 8 | x]; return _currentOutputBuffer[y << 8 | x];
} }
}; };
void PPU::Run(uint64_t runTo)
{
while(_masterClock + _masterClockDivider <= runTo) {
Exec();
_masterClock += _masterClockDivider;
}
}

View file

@ -129,7 +129,7 @@ bool SaveStateManager::LoadState(istream &stream, bool hashCheckRequired)
} }
stream.read((char*)&fileFormatVersion, sizeof(fileFormatVersion)); stream.read((char*)&fileFormatVersion, sizeof(fileFormatVersion));
if(fileFormatVersion <= 10) { if(fileFormatVersion <= 11) {
MessageManager::DisplayMessage("SaveStates", "SaveStateIncompatibleVersion"); MessageManager::DisplayMessage("SaveStates", "SaveStateIncompatibleVersion");
return false; return false;
} else { } else {

View file

@ -14,7 +14,7 @@ private:
string GetStateFilepath(int stateIndex); string GetStateFilepath(int stateIndex);
public: public:
static constexpr uint32_t FileFormatVersion = 11; static constexpr uint32_t FileFormatVersion = 12;
SaveStateManager(shared_ptr<Console> console); SaveStateManager(shared_ptr<Console> console);

View file

@ -203,9 +203,6 @@ void SoundMixer::SetNesModel(NesModel model)
void SoundMixer::UpdateRates(bool forceUpdate) void SoundMixer::UpdateRates(bool forceUpdate)
{ {
uint32_t newRate = _console->GetCpu()->GetClockRate(_model); uint32_t newRate = _console->GetCpu()->GetClockRate(_model);
if(!_settings->GetOverclockAdjustApu()) {
newRate = (uint32_t)(newRate * (double)_settings->GetOverclockRate() / 100);
}
if(_settings->CheckFlag(EmulationFlags::IntegerFpsMode)) { if(_settings->CheckFlag(EmulationFlags::IntegerFpsMode)) {
//Adjust sample rate when running at 60.0 fps instead of 60.1 //Adjust sample rate when running at 60.0 fps instead of 60.1

View file

@ -22,12 +22,10 @@ namespace Mesen.GUI.Config
public bool EnableOamDecay = false; public bool EnableOamDecay = false;
public bool UseNes101Hvc101Behavior = false; public bool UseNes101Hvc101Behavior = false;
public bool EnableMapperRandomPowerOnState = false; public bool EnableMapperRandomPowerOnState = false;
public bool RandomizeCpuPpuAlignment = false;
public bool UseAlternativeMmc3Irq = false; public bool UseAlternativeMmc3Irq = false;
[MinMax(1, 1000)] public UInt32 OverclockRate = 100;
public bool OverclockAdjustApu = true;
[MinMax(0, 1000)] public UInt32 PpuExtraScanlinesBeforeNmi = 0; [MinMax(0, 1000)] public UInt32 PpuExtraScanlinesBeforeNmi = 0;
[MinMax(0, 1000)] public UInt32 PpuExtraScanlinesAfterNmi = 0; [MinMax(0, 1000)] public UInt32 PpuExtraScanlinesAfterNmi = 0;
@ -60,8 +58,8 @@ namespace Mesen.GUI.Config
InteropEmu.SetFlag(EmulationFlags.EnableOamDecay, emulationInfo.EnableOamDecay); InteropEmu.SetFlag(EmulationFlags.EnableOamDecay, emulationInfo.EnableOamDecay);
InteropEmu.SetFlag(EmulationFlags.UseNes101Hvc101Behavior, emulationInfo.UseNes101Hvc101Behavior); InteropEmu.SetFlag(EmulationFlags.UseNes101Hvc101Behavior, emulationInfo.UseNes101Hvc101Behavior);
InteropEmu.SetFlag(EmulationFlags.RandomizeMapperPowerOnState, emulationInfo.EnableMapperRandomPowerOnState); InteropEmu.SetFlag(EmulationFlags.RandomizeMapperPowerOnState, emulationInfo.EnableMapperRandomPowerOnState);
InteropEmu.SetFlag(EmulationFlags.RandomizeCpuPpuAlignment, emulationInfo.RandomizeCpuPpuAlignment);
InteropEmu.SetOverclockRate(emulationInfo.OverclockRate, emulationInfo.OverclockAdjustApu);
InteropEmu.SetPpuNmiConfig(emulationInfo.PpuExtraScanlinesBeforeNmi, emulationInfo.PpuExtraScanlinesAfterNmi); InteropEmu.SetPpuNmiConfig(emulationInfo.PpuExtraScanlinesBeforeNmi, emulationInfo.PpuExtraScanlinesAfterNmi);
InteropEmu.SetRamPowerOnState(emulationInfo.RamPowerOnState); InteropEmu.SetRamPowerOnState(emulationInfo.RamPowerOnState);

View file

@ -321,6 +321,7 @@
<Control ID="chkDisablePpuReset">No reestableixis la PPU al reiniciar la consola (comportament de la Famicom)</Control> <Control ID="chkDisablePpuReset">No reestableixis la PPU al reiniciar la consola (comportament de la Famicom)</Control>
<Control ID="chkUseNes101Hvc101Behavior">Simula el comportament de la NES/HVC-101 (Top-loader / AV Famicom)</Control> <Control ID="chkUseNes101Hvc101Behavior">Simula el comportament de la NES/HVC-101 (Top-loader / AV Famicom)</Control>
<Control ID="chkMapperRandomPowerOnState">Randomize power-on state for mappers</Control> <Control ID="chkMapperRandomPowerOnState">Randomize power-on state for mappers</Control>
<Control ID="chkRandomizeCpuPpuAlignment">Randomize power-on CPU/PPU alignment</Control>
<Control ID="tpgOverclocking">Forçament</Control> <Control ID="tpgOverclocking">Forçament</Control>
<Control ID="grpOverclocking">Forçament de CPU</Control> <Control ID="grpOverclocking">Forçament de CPU</Control>

View file

@ -320,6 +320,7 @@
<Control ID="chkDisablePpuReset">Do not reset PPU when resetting console (Famicom behavior)</Control> <Control ID="chkDisablePpuReset">Do not reset PPU when resetting console (Famicom behavior)</Control>
<Control ID="chkUseNes101Hvc101Behavior">Use NES/HVC-101 (Top-loader / AV Famicom) behavior</Control> <Control ID="chkUseNes101Hvc101Behavior">Use NES/HVC-101 (Top-loader / AV Famicom) behavior</Control>
<Control ID="chkMapperRandomPowerOnState">Randomize power-on state for mappers</Control> <Control ID="chkMapperRandomPowerOnState">Randomize power-on state for mappers</Control>
<Control ID="chkRandomizeCpuPpuAlignment">Randomize power-on CPU/PPU alignment</Control>
<Control ID="lblRamPowerOnState">Default power on state for RAM:</Control> <Control ID="lblRamPowerOnState">Default power on state for RAM:</Control>

View file

@ -320,6 +320,7 @@
<Control ID="chkDisablePpuReset">No resetear la PPU al resetear la consola (Famicom)</Control> <Control ID="chkDisablePpuReset">No resetear la PPU al resetear la consola (Famicom)</Control>
<Control ID="chkUseNes101Hvc101Behavior">Simular el comportamiento de NES/HVC-101 (Top-loader / AV Famicom)</Control> <Control ID="chkUseNes101Hvc101Behavior">Simular el comportamiento de NES/HVC-101 (Top-loader / AV Famicom)</Control>
<Control ID="chkMapperRandomPowerOnState">Aleatorizar el estado de encendido para los mappers</Control> <Control ID="chkMapperRandomPowerOnState">Aleatorizar el estado de encendido para los mappers</Control>
<Control ID="chkRandomizeCpuPpuAlignment">Randomize power-on CPU/PPU alignment</Control>
<Control ID="tpgOverclocking">Overclocking</Control> <Control ID="tpgOverclocking">Overclocking</Control>
<Control ID="grpOverclocking">Overclocking de CPU</Control> <Control ID="grpOverclocking">Overclocking de CPU</Control>

View file

@ -320,6 +320,7 @@
<Control ID="chkDisablePpuReset">Ne pas faire un reset du PPU lors du reset de la console (Famicom)</Control> <Control ID="chkDisablePpuReset">Ne pas faire un reset du PPU lors du reset de la console (Famicom)</Control>
<Control ID="chkUseNes101Hvc101Behavior">Simuler le comportement du NES/HVC-101 (Top Loader / AV Famicom)</Control> <Control ID="chkUseNes101Hvc101Behavior">Simuler le comportement du NES/HVC-101 (Top Loader / AV Famicom)</Control>
<Control ID="chkMapperRandomPowerOnState">Démarrer le jeu avec le mapper dans un état aléatoire</Control> <Control ID="chkMapperRandomPowerOnState">Démarrer le jeu avec le mapper dans un état aléatoire</Control>
<Control ID="chkRandomizeCpuPpuAlignment">Démarrer le jeu avec un alignement CPU/PPU aléatoire</Control>
<Control ID="lblRamPowerOnState">État initial de la mémoire au démarrage : </Control> <Control ID="lblRamPowerOnState">État initial de la mémoire au démarrage : </Control>

View file

@ -320,6 +320,7 @@
<Control ID="chkDisablePpuReset">Non resettare la PPU quando resetti la console (Famicom)</Control> <Control ID="chkDisablePpuReset">Non resettare la PPU quando resetti la console (Famicom)</Control>
<Control ID="chkUseNes101Hvc101Behavior">Usa NES/HVC-101 (Top-loader / AV Famicom)</Control> <Control ID="chkUseNes101Hvc101Behavior">Usa NES/HVC-101 (Top-loader / AV Famicom)</Control>
<Control ID="chkMapperRandomPowerOnState">Valori casuali durante accensione per i mapper</Control> <Control ID="chkMapperRandomPowerOnState">Valori casuali durante accensione per i mapper</Control>
<Control ID="chkRandomizeCpuPpuAlignment">Randomize power-on CPU/PPU alignment</Control>
<Control ID="lblRamPowerOnState">Stato di accensione predefinito per la RAM:</Control> <Control ID="lblRamPowerOnState">Stato di accensione predefinito per la RAM:</Control>

View file

@ -318,7 +318,8 @@
<Control ID="chkDisablePaletteRead">PPUのパレットラムを読み込み不可能にする</Control> <Control ID="chkDisablePaletteRead">PPUのパレットラムを読み込み不可能にする</Control>
<Control ID="chkDisablePpuReset">ゲーム機をリセットする時に、PPUをリセットしない ファミコン同様</Control> <Control ID="chkDisablePpuReset">ゲーム機をリセットする時に、PPUをリセットしない ファミコン同様</Control>
<Control ID="chkUseNes101Hvc101Behavior">AV仕様ファミコンHVC-101とNES-101の仕様を使う</Control> <Control ID="chkUseNes101Hvc101Behavior">AV仕様ファミコンHVC-101とNES-101の仕様を使う</Control>
<Control ID="chkMapperRandomPowerOnState">ランダムな状態でゲームを起動する</Control> <Control ID="chkMapperRandomPowerOnState">ランダムなMapper状態でゲームを起動する</Control>
<Control ID="chkRandomizeCpuPpuAlignment">ランダムなCPU/PPUアラインメントでゲームを起動する</Control>
<Control ID="lblRamPowerOnState">起動時のメモリの状態 : </Control> <Control ID="lblRamPowerOnState">起動時のメモリの状態 : </Control>

View file

@ -320,6 +320,7 @@
<Control ID="chkDisablePpuReset">Não reiniciar a PPU ao reiniciar o console (Comportamento do Famicom)</Control> <Control ID="chkDisablePpuReset">Não reiniciar a PPU ao reiniciar o console (Comportamento do Famicom)</Control>
<Control ID="chkUseNes101Hvc101Behavior">Utilizar comportamento do NES/HVC-101 (Top-loader / AV Famicom)</Control> <Control ID="chkUseNes101Hvc101Behavior">Utilizar comportamento do NES/HVC-101 (Top-loader / AV Famicom)</Control>
<Control ID="chkMapperRandomPowerOnState">Aleatorizar o estado de ativação para mapeadores</Control> <Control ID="chkMapperRandomPowerOnState">Aleatorizar o estado de ativação para mapeadores</Control>
<Control ID="chkRandomizeCpuPpuAlignment">Randomize power-on CPU/PPU alignment</Control>
<Control ID="lblRamPowerOnState">Estado inicial da RAM durante o início:</Control> <Control ID="lblRamPowerOnState">Estado inicial da RAM durante o início:</Control>

View file

@ -320,6 +320,7 @@
<Control ID="chkDisablePpuReset">Do not reset PPU when resetting console (Famicom behavior)</Control> <Control ID="chkDisablePpuReset">Do not reset PPU when resetting console (Famicom behavior)</Control>
<Control ID="chkUseNes101Hvc101Behavior">Use NES/HVC-101 (Top-loader / AV Famicom) behavior</Control> <Control ID="chkUseNes101Hvc101Behavior">Use NES/HVC-101 (Top-loader / AV Famicom) behavior</Control>
<Control ID="chkMapperRandomPowerOnState">Randomize power-on state for mappers</Control> <Control ID="chkMapperRandomPowerOnState">Randomize power-on state for mappers</Control>
<Control ID="chkRandomizeCpuPpuAlignment">Randomize power-on CPU/PPU alignment</Control>
<Control ID="tpgOverclocking">Разгон</Control> <Control ID="tpgOverclocking">Разгон</Control>
<Control ID="grpOverclocking">Разгон CPU</Control> <Control ID="grpOverclocking">Разгон CPU</Control>

View file

@ -320,6 +320,7 @@
<Control ID="chkDisablePpuReset">Не перезавантажити PPU при скиданні консолі (Famicom)</Control> <Control ID="chkDisablePpuReset">Не перезавантажити PPU при скиданні консолі (Famicom)</Control>
<Control ID="chkUseNes101Hvc101Behavior">Імітувати поведінку NES/HVC-101 (Top-loader / AV Famicom) поведінку</Control> <Control ID="chkUseNes101Hvc101Behavior">Імітувати поведінку NES/HVC-101 (Top-loader / AV Famicom) поведінку</Control>
<Control ID="chkMapperRandomPowerOnState">Почати гру з маппером у випадковому стані</Control> <Control ID="chkMapperRandomPowerOnState">Почати гру з маппером у випадковому стані</Control>
<Control ID="chkRandomizeCpuPpuAlignment">Randomize power-on CPU/PPU alignment</Control>
<Control ID="tpgOverclocking">Розгін</Control> <Control ID="tpgOverclocking">Розгін</Control>
<Control ID="grpOverclocking">Розгін CPU</Control> <Control ID="grpOverclocking">Розгін CPU</Control>

View file

@ -346,6 +346,7 @@
<Control ID="chkDisablePpuReset">PPU 不随主机重设 (FC 真机)</Control> <Control ID="chkDisablePpuReset">PPU 不随主机重设 (FC 真机)</Control>
<Control ID="chkUseNes101Hvc101Behavior">模拟 HVC-101/NES-101 (Top-Loader/带 AV 端子主机)</Control> <Control ID="chkUseNes101Hvc101Behavior">模拟 HVC-101/NES-101 (Top-Loader/带 AV 端子主机)</Control>
<Control ID="chkMapperRandomPowerOnState">随机 Mapper 开机状态</Control> <Control ID="chkMapperRandomPowerOnState">随机 Mapper 开机状态</Control>
<Control ID="chkRandomizeCpuPpuAlignment">Randomize power-on CPU/PPU alignment</Control>
<Control ID="lblRamPowerOnState">默认开机 RAM 状态:</Control> <Control ID="lblRamPowerOnState">默认开机 RAM 状态:</Control>
<Control ID="tpgOverclocking">超频</Control> <Control ID="tpgOverclocking">超频</Control>

View file

@ -48,6 +48,7 @@ namespace Mesen.GUI.Forms.Config
this.lblRewindSpeedHint = new System.Windows.Forms.Label(); this.lblRewindSpeedHint = new System.Windows.Forms.Label();
this.tpgAdvanced = new System.Windows.Forms.TabPage(); this.tpgAdvanced = new System.Windows.Forms.TabPage();
this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
this.chkRandomizeCpuPpuAlignment = new Mesen.GUI.Controls.ctrlRiskyOption();
this.lblMiscSettings = new System.Windows.Forms.Label(); this.lblMiscSettings = new System.Windows.Forms.Label();
this.chkMapperRandomPowerOnState = new Mesen.GUI.Controls.ctrlRiskyOption(); this.chkMapperRandomPowerOnState = new Mesen.GUI.Controls.ctrlRiskyOption();
this.chkEnableOamDecay = new Mesen.GUI.Controls.ctrlRiskyOption(); this.chkEnableOamDecay = new Mesen.GUI.Controls.ctrlRiskyOption();
@ -69,15 +70,9 @@ namespace Mesen.GUI.Forms.Config
this.flowLayoutPanel4 = new System.Windows.Forms.FlowLayoutPanel(); this.flowLayoutPanel4 = new System.Windows.Forms.FlowLayoutPanel();
this.lblEffectiveClockRateDendy = new System.Windows.Forms.Label(); this.lblEffectiveClockRateDendy = new System.Windows.Forms.Label();
this.lblEffectiveClockRateValueDendy = new System.Windows.Forms.Label(); this.lblEffectiveClockRateValueDendy = new System.Windows.Forms.Label();
this.chkOverclockAdjustApu = new System.Windows.Forms.CheckBox();
this.flowLayoutPanel3 = new System.Windows.Forms.FlowLayoutPanel(); this.flowLayoutPanel3 = new System.Windows.Forms.FlowLayoutPanel();
this.lblEffectiveClockRatePal = new System.Windows.Forms.Label(); this.lblEffectiveClockRatePal = new System.Windows.Forms.Label();
this.lblEffectiveClockRateValuePal = new System.Windows.Forms.Label(); this.lblEffectiveClockRateValuePal = new System.Windows.Forms.Label();
this.grpOverclocking = new System.Windows.Forms.GroupBox();
this.tableLayoutPanel2 = new System.Windows.Forms.TableLayoutPanel();
this.lblClockRate = new System.Windows.Forms.Label();
this.nudOverclockRate = new Mesen.GUI.Controls.MesenNumericUpDown();
this.lblClockRatePercent = new System.Windows.Forms.Label();
this.grpPpuTiming = new System.Windows.Forms.GroupBox(); this.grpPpuTiming = new System.Windows.Forms.GroupBox();
this.tableLayoutPanel5 = new System.Windows.Forms.TableLayoutPanel(); this.tableLayoutPanel5 = new System.Windows.Forms.TableLayoutPanel();
this.nudExtraScanlinesAfterNmi = new Mesen.GUI.Controls.MesenNumericUpDown(); this.nudExtraScanlinesAfterNmi = new Mesen.GUI.Controls.MesenNumericUpDown();
@ -105,8 +100,6 @@ namespace Mesen.GUI.Forms.Config
this.tableLayoutPanel3.SuspendLayout(); this.tableLayoutPanel3.SuspendLayout();
this.flowLayoutPanel4.SuspendLayout(); this.flowLayoutPanel4.SuspendLayout();
this.flowLayoutPanel3.SuspendLayout(); this.flowLayoutPanel3.SuspendLayout();
this.grpOverclocking.SuspendLayout();
this.tableLayoutPanel2.SuspendLayout();
this.grpPpuTiming.SuspendLayout(); this.grpPpuTiming.SuspendLayout();
this.tableLayoutPanel5.SuspendLayout(); this.tableLayoutPanel5.SuspendLayout();
this.flowLayoutPanel2.SuspendLayout(); this.flowLayoutPanel2.SuspendLayout();
@ -115,7 +108,7 @@ namespace Mesen.GUI.Forms.Config
// //
// baseConfigPanel // baseConfigPanel
// //
this.baseConfigPanel.Location = new System.Drawing.Point(0, 321); this.baseConfigPanel.Location = new System.Drawing.Point(0, 338);
this.baseConfigPanel.Size = new System.Drawing.Size(533, 29); this.baseConfigPanel.Size = new System.Drawing.Size(533, 29);
// //
// tabMain // tabMain
@ -127,7 +120,7 @@ namespace Mesen.GUI.Forms.Config
this.tabMain.Location = new System.Drawing.Point(0, 0); this.tabMain.Location = new System.Drawing.Point(0, 0);
this.tabMain.Name = "tabMain"; this.tabMain.Name = "tabMain";
this.tabMain.SelectedIndex = 0; this.tabMain.SelectedIndex = 0;
this.tabMain.Size = new System.Drawing.Size(533, 321); this.tabMain.Size = new System.Drawing.Size(533, 338);
this.tabMain.TabIndex = 2; this.tabMain.TabIndex = 2;
// //
// tpgGeneral // tpgGeneral
@ -136,7 +129,7 @@ namespace Mesen.GUI.Forms.Config
this.tpgGeneral.Location = new System.Drawing.Point(4, 22); this.tpgGeneral.Location = new System.Drawing.Point(4, 22);
this.tpgGeneral.Name = "tpgGeneral"; this.tpgGeneral.Name = "tpgGeneral";
this.tpgGeneral.Padding = new System.Windows.Forms.Padding(3); this.tpgGeneral.Padding = new System.Windows.Forms.Padding(3);
this.tpgGeneral.Size = new System.Drawing.Size(525, 295); this.tpgGeneral.Size = new System.Drawing.Size(525, 312);
this.tpgGeneral.TabIndex = 0; this.tpgGeneral.TabIndex = 0;
this.tpgGeneral.Text = "General"; this.tpgGeneral.Text = "General";
this.tpgGeneral.UseVisualStyleBackColor = true; this.tpgGeneral.UseVisualStyleBackColor = true;
@ -161,7 +154,7 @@ namespace Mesen.GUI.Forms.Config
this.tableLayoutPanel4.RowStyles.Add(new System.Windows.Forms.RowStyle()); this.tableLayoutPanel4.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel4.RowStyles.Add(new System.Windows.Forms.RowStyle()); this.tableLayoutPanel4.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel4.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tableLayoutPanel4.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.tableLayoutPanel4.Size = new System.Drawing.Size(519, 289); this.tableLayoutPanel4.Size = new System.Drawing.Size(519, 306);
this.tableLayoutPanel4.TabIndex = 0; this.tableLayoutPanel4.TabIndex = 0;
// //
// flowLayoutPanel9 // flowLayoutPanel9
@ -356,7 +349,7 @@ namespace Mesen.GUI.Forms.Config
this.tpgAdvanced.Location = new System.Drawing.Point(4, 22); this.tpgAdvanced.Location = new System.Drawing.Point(4, 22);
this.tpgAdvanced.Name = "tpgAdvanced"; this.tpgAdvanced.Name = "tpgAdvanced";
this.tpgAdvanced.Padding = new System.Windows.Forms.Padding(3); this.tpgAdvanced.Padding = new System.Windows.Forms.Padding(3);
this.tpgAdvanced.Size = new System.Drawing.Size(525, 295); this.tpgAdvanced.Size = new System.Drawing.Size(525, 312);
this.tpgAdvanced.TabIndex = 1; this.tpgAdvanced.TabIndex = 1;
this.tpgAdvanced.Text = "Advanced"; this.tpgAdvanced.Text = "Advanced";
this.tpgAdvanced.UseVisualStyleBackColor = true; this.tpgAdvanced.UseVisualStyleBackColor = true;
@ -365,25 +358,27 @@ namespace Mesen.GUI.Forms.Config
// //
this.tableLayoutPanel1.ColumnCount = 1; this.tableLayoutPanel1.ColumnCount = 1;
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.tableLayoutPanel1.Controls.Add(this.lblMiscSettings, 0, 4); this.tableLayoutPanel1.Controls.Add(this.chkRandomizeCpuPpuAlignment, 0, 3);
this.tableLayoutPanel1.Controls.Add(this.lblMiscSettings, 0, 5);
this.tableLayoutPanel1.Controls.Add(this.chkMapperRandomPowerOnState, 0, 2); this.tableLayoutPanel1.Controls.Add(this.chkMapperRandomPowerOnState, 0, 2);
this.tableLayoutPanel1.Controls.Add(this.chkEnableOamDecay, 0, 1); this.tableLayoutPanel1.Controls.Add(this.chkEnableOamDecay, 0, 1);
this.tableLayoutPanel1.Controls.Add(this.flowLayoutPanel8, 0, 3); this.tableLayoutPanel1.Controls.Add(this.flowLayoutPanel8, 0, 4);
this.tableLayoutPanel1.Controls.Add(this.chkDisablePaletteRead, 0, 10); this.tableLayoutPanel1.Controls.Add(this.chkDisablePaletteRead, 0, 11);
this.tableLayoutPanel1.Controls.Add(this.chkDisableOamAddrBug, 0, 8); this.tableLayoutPanel1.Controls.Add(this.chkDisableOamAddrBug, 0, 9);
this.tableLayoutPanel1.Controls.Add(this.chkDisablePpuReset, 0, 7); this.tableLayoutPanel1.Controls.Add(this.chkDisablePpuReset, 0, 8);
this.tableLayoutPanel1.Controls.Add(this.chkDisablePpu2004Reads, 0, 9); this.tableLayoutPanel1.Controls.Add(this.chkDisablePpu2004Reads, 0, 10);
this.tableLayoutPanel1.Controls.Add(this.chkUseNes101Hvc101Behavior, 0, 6); this.tableLayoutPanel1.Controls.Add(this.chkUseNes101Hvc101Behavior, 0, 7);
this.tableLayoutPanel1.Controls.Add(this.chkAllowInvalidInput, 0, 11); this.tableLayoutPanel1.Controls.Add(this.chkAllowInvalidInput, 0, 12);
this.tableLayoutPanel1.Controls.Add(this.chkUseAlternativeMmc3Irq, 0, 5); this.tableLayoutPanel1.Controls.Add(this.chkUseAlternativeMmc3Irq, 0, 6);
this.tableLayoutPanel1.Controls.Add(this.lblDeveloperSettings, 0, 0); this.tableLayoutPanel1.Controls.Add(this.lblDeveloperSettings, 0, 0);
this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill;
this.tableLayoutPanel1.Location = new System.Drawing.Point(3, 3); this.tableLayoutPanel1.Location = new System.Drawing.Point(3, 3);
this.tableLayoutPanel1.Name = "tableLayoutPanel1"; this.tableLayoutPanel1.Name = "tableLayoutPanel1";
this.tableLayoutPanel1.RowCount = 13; this.tableLayoutPanel1.RowCount = 14;
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F)); this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F));
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F));
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F)); this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F));
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
@ -394,15 +389,28 @@ namespace Mesen.GUI.Forms.Config
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.tableLayoutPanel1.Size = new System.Drawing.Size(519, 289); this.tableLayoutPanel1.Size = new System.Drawing.Size(519, 306);
this.tableLayoutPanel1.TabIndex = 0; this.tableLayoutPanel1.TabIndex = 0;
// //
// chkRandomizeCpuPpuAlignment
//
this.chkRandomizeCpuPpuAlignment.AutoSize = true;
this.chkRandomizeCpuPpuAlignment.Checked = false;
this.chkRandomizeCpuPpuAlignment.Dock = System.Windows.Forms.DockStyle.Fill;
this.chkRandomizeCpuPpuAlignment.Location = new System.Drawing.Point(10, 66);
this.chkRandomizeCpuPpuAlignment.Margin = new System.Windows.Forms.Padding(10, 0, 0, 0);
this.chkRandomizeCpuPpuAlignment.MinimumSize = new System.Drawing.Size(0, 23);
this.chkRandomizeCpuPpuAlignment.Name = "chkRandomizeCpuPpuAlignment";
this.chkRandomizeCpuPpuAlignment.Size = new System.Drawing.Size(509, 23);
this.chkRandomizeCpuPpuAlignment.TabIndex = 36;
this.chkRandomizeCpuPpuAlignment.Text = "Randomize power-on CPU/PPU alignment";
//
// lblMiscSettings // lblMiscSettings
// //
this.lblMiscSettings.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.lblMiscSettings.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
this.lblMiscSettings.AutoSize = true; this.lblMiscSettings.AutoSize = true;
this.lblMiscSettings.ForeColor = System.Drawing.SystemColors.GrayText; this.lblMiscSettings.ForeColor = System.Drawing.SystemColors.GrayText;
this.lblMiscSettings.Location = new System.Drawing.Point(0, 98); this.lblMiscSettings.Location = new System.Drawing.Point(0, 118);
this.lblMiscSettings.Margin = new System.Windows.Forms.Padding(0, 0, 3, 2); this.lblMiscSettings.Margin = new System.Windows.Forms.Padding(0, 0, 3, 2);
this.lblMiscSettings.Name = "lblMiscSettings"; this.lblMiscSettings.Name = "lblMiscSettings";
this.lblMiscSettings.Size = new System.Drawing.Size(115, 13); this.lblMiscSettings.Size = new System.Drawing.Size(115, 13);
@ -439,7 +447,7 @@ namespace Mesen.GUI.Forms.Config
this.flowLayoutPanel8.Controls.Add(this.lblRamPowerOnState); this.flowLayoutPanel8.Controls.Add(this.lblRamPowerOnState);
this.flowLayoutPanel8.Controls.Add(this.cboRamPowerOnState); this.flowLayoutPanel8.Controls.Add(this.cboRamPowerOnState);
this.flowLayoutPanel8.Dock = System.Windows.Forms.DockStyle.Fill; this.flowLayoutPanel8.Dock = System.Windows.Forms.DockStyle.Fill;
this.flowLayoutPanel8.Location = new System.Drawing.Point(7, 66); this.flowLayoutPanel8.Location = new System.Drawing.Point(7, 86);
this.flowLayoutPanel8.Margin = new System.Windows.Forms.Padding(7, 0, 0, 0); this.flowLayoutPanel8.Margin = new System.Windows.Forms.Padding(7, 0, 0, 0);
this.flowLayoutPanel8.Name = "flowLayoutPanel8"; this.flowLayoutPanel8.Name = "flowLayoutPanel8";
this.flowLayoutPanel8.Size = new System.Drawing.Size(512, 27); this.flowLayoutPanel8.Size = new System.Drawing.Size(512, 27);
@ -468,7 +476,7 @@ namespace Mesen.GUI.Forms.Config
// //
this.chkDisablePaletteRead.Checked = false; this.chkDisablePaletteRead.Checked = false;
this.chkDisablePaletteRead.Dock = System.Windows.Forms.DockStyle.Fill; this.chkDisablePaletteRead.Dock = System.Windows.Forms.DockStyle.Fill;
this.chkDisablePaletteRead.Location = new System.Drawing.Point(10, 228); this.chkDisablePaletteRead.Location = new System.Drawing.Point(10, 248);
this.chkDisablePaletteRead.Margin = new System.Windows.Forms.Padding(10, 0, 0, 0); this.chkDisablePaletteRead.Margin = new System.Windows.Forms.Padding(10, 0, 0, 0);
this.chkDisablePaletteRead.MinimumSize = new System.Drawing.Size(0, 21); this.chkDisablePaletteRead.MinimumSize = new System.Drawing.Size(0, 21);
this.chkDisablePaletteRead.Name = "chkDisablePaletteRead"; this.chkDisablePaletteRead.Name = "chkDisablePaletteRead";
@ -480,7 +488,7 @@ namespace Mesen.GUI.Forms.Config
// //
this.chkDisableOamAddrBug.Checked = false; this.chkDisableOamAddrBug.Checked = false;
this.chkDisableOamAddrBug.Dock = System.Windows.Forms.DockStyle.Fill; this.chkDisableOamAddrBug.Dock = System.Windows.Forms.DockStyle.Fill;
this.chkDisableOamAddrBug.Location = new System.Drawing.Point(10, 182); this.chkDisableOamAddrBug.Location = new System.Drawing.Point(10, 202);
this.chkDisableOamAddrBug.Margin = new System.Windows.Forms.Padding(10, 0, 0, 0); this.chkDisableOamAddrBug.Margin = new System.Windows.Forms.Padding(10, 0, 0, 0);
this.chkDisableOamAddrBug.MinimumSize = new System.Drawing.Size(0, 21); this.chkDisableOamAddrBug.MinimumSize = new System.Drawing.Size(0, 21);
this.chkDisableOamAddrBug.Name = "chkDisableOamAddrBug"; this.chkDisableOamAddrBug.Name = "chkDisableOamAddrBug";
@ -492,7 +500,7 @@ namespace Mesen.GUI.Forms.Config
// //
this.chkDisablePpuReset.Checked = false; this.chkDisablePpuReset.Checked = false;
this.chkDisablePpuReset.Dock = System.Windows.Forms.DockStyle.Fill; this.chkDisablePpuReset.Dock = System.Windows.Forms.DockStyle.Fill;
this.chkDisablePpuReset.Location = new System.Drawing.Point(10, 159); this.chkDisablePpuReset.Location = new System.Drawing.Point(10, 179);
this.chkDisablePpuReset.Margin = new System.Windows.Forms.Padding(10, 0, 0, 0); this.chkDisablePpuReset.Margin = new System.Windows.Forms.Padding(10, 0, 0, 0);
this.chkDisablePpuReset.MinimumSize = new System.Drawing.Size(0, 21); this.chkDisablePpuReset.MinimumSize = new System.Drawing.Size(0, 21);
this.chkDisablePpuReset.Name = "chkDisablePpuReset"; this.chkDisablePpuReset.Name = "chkDisablePpuReset";
@ -504,7 +512,7 @@ namespace Mesen.GUI.Forms.Config
// //
this.chkDisablePpu2004Reads.Checked = false; this.chkDisablePpu2004Reads.Checked = false;
this.chkDisablePpu2004Reads.Dock = System.Windows.Forms.DockStyle.Fill; this.chkDisablePpu2004Reads.Dock = System.Windows.Forms.DockStyle.Fill;
this.chkDisablePpu2004Reads.Location = new System.Drawing.Point(10, 205); this.chkDisablePpu2004Reads.Location = new System.Drawing.Point(10, 225);
this.chkDisablePpu2004Reads.Margin = new System.Windows.Forms.Padding(10, 0, 0, 0); this.chkDisablePpu2004Reads.Margin = new System.Windows.Forms.Padding(10, 0, 0, 0);
this.chkDisablePpu2004Reads.MinimumSize = new System.Drawing.Size(0, 21); this.chkDisablePpu2004Reads.MinimumSize = new System.Drawing.Size(0, 21);
this.chkDisablePpu2004Reads.Name = "chkDisablePpu2004Reads"; this.chkDisablePpu2004Reads.Name = "chkDisablePpu2004Reads";
@ -515,7 +523,7 @@ namespace Mesen.GUI.Forms.Config
// chkUseNes101Hvc101Behavior // chkUseNes101Hvc101Behavior
// //
this.chkUseNes101Hvc101Behavior.AutoSize = true; this.chkUseNes101Hvc101Behavior.AutoSize = true;
this.chkUseNes101Hvc101Behavior.Location = new System.Drawing.Point(13, 139); this.chkUseNes101Hvc101Behavior.Location = new System.Drawing.Point(13, 159);
this.chkUseNes101Hvc101Behavior.Margin = new System.Windows.Forms.Padding(13, 3, 3, 3); this.chkUseNes101Hvc101Behavior.Margin = new System.Windows.Forms.Padding(13, 3, 3, 3);
this.chkUseNes101Hvc101Behavior.Name = "chkUseNes101Hvc101Behavior"; this.chkUseNes101Hvc101Behavior.Name = "chkUseNes101Hvc101Behavior";
this.chkUseNes101Hvc101Behavior.Size = new System.Drawing.Size(292, 17); this.chkUseNes101Hvc101Behavior.Size = new System.Drawing.Size(292, 17);
@ -528,7 +536,7 @@ namespace Mesen.GUI.Forms.Config
this.chkAllowInvalidInput.AutoSize = true; this.chkAllowInvalidInput.AutoSize = true;
this.chkAllowInvalidInput.Checked = false; this.chkAllowInvalidInput.Checked = false;
this.chkAllowInvalidInput.Dock = System.Windows.Forms.DockStyle.Fill; this.chkAllowInvalidInput.Dock = System.Windows.Forms.DockStyle.Fill;
this.chkAllowInvalidInput.Location = new System.Drawing.Point(10, 251); this.chkAllowInvalidInput.Location = new System.Drawing.Point(10, 271);
this.chkAllowInvalidInput.Margin = new System.Windows.Forms.Padding(10, 0, 0, 0); this.chkAllowInvalidInput.Margin = new System.Windows.Forms.Padding(10, 0, 0, 0);
this.chkAllowInvalidInput.MinimumSize = new System.Drawing.Size(0, 23); this.chkAllowInvalidInput.MinimumSize = new System.Drawing.Size(0, 23);
this.chkAllowInvalidInput.Name = "chkAllowInvalidInput"; this.chkAllowInvalidInput.Name = "chkAllowInvalidInput";
@ -539,7 +547,7 @@ namespace Mesen.GUI.Forms.Config
// chkUseAlternativeMmc3Irq // chkUseAlternativeMmc3Irq
// //
this.chkUseAlternativeMmc3Irq.AutoSize = true; this.chkUseAlternativeMmc3Irq.AutoSize = true;
this.chkUseAlternativeMmc3Irq.Location = new System.Drawing.Point(13, 116); this.chkUseAlternativeMmc3Irq.Location = new System.Drawing.Point(13, 136);
this.chkUseAlternativeMmc3Irq.Margin = new System.Windows.Forms.Padding(13, 3, 3, 3); this.chkUseAlternativeMmc3Irq.Margin = new System.Windows.Forms.Padding(13, 3, 3, 3);
this.chkUseAlternativeMmc3Irq.Name = "chkUseAlternativeMmc3Irq"; this.chkUseAlternativeMmc3Irq.Name = "chkUseAlternativeMmc3Irq";
this.chkUseAlternativeMmc3Irq.Size = new System.Drawing.Size(197, 17); this.chkUseAlternativeMmc3Irq.Size = new System.Drawing.Size(197, 17);
@ -566,7 +574,7 @@ namespace Mesen.GUI.Forms.Config
this.tpgOverclocking.Location = new System.Drawing.Point(4, 22); this.tpgOverclocking.Location = new System.Drawing.Point(4, 22);
this.tpgOverclocking.Name = "tpgOverclocking"; this.tpgOverclocking.Name = "tpgOverclocking";
this.tpgOverclocking.Padding = new System.Windows.Forms.Padding(3); this.tpgOverclocking.Padding = new System.Windows.Forms.Padding(3);
this.tpgOverclocking.Size = new System.Drawing.Size(525, 295); this.tpgOverclocking.Size = new System.Drawing.Size(525, 312);
this.tpgOverclocking.TabIndex = 2; this.tpgOverclocking.TabIndex = 2;
this.tpgOverclocking.Text = "Overclocking"; this.tpgOverclocking.Text = "Overclocking";
this.tpgOverclocking.UseVisualStyleBackColor = true; this.tpgOverclocking.UseVisualStyleBackColor = true;
@ -576,7 +584,7 @@ namespace Mesen.GUI.Forms.Config
this.picHint.Anchor = System.Windows.Forms.AnchorStyles.Left; this.picHint.Anchor = System.Windows.Forms.AnchorStyles.Left;
this.picHint.BackgroundImage = global::Mesen.GUI.Properties.Resources.Help; this.picHint.BackgroundImage = global::Mesen.GUI.Properties.Resources.Help;
this.picHint.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Center; this.picHint.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Center;
this.picHint.Location = new System.Drawing.Point(12, 15); this.picHint.Location = new System.Drawing.Point(12, 24);
this.picHint.Margin = new System.Windows.Forms.Padding(3, 5, 3, 3); this.picHint.Margin = new System.Windows.Forms.Padding(3, 5, 3, 3);
this.picHint.Name = "picHint"; this.picHint.Name = "picHint";
this.picHint.Size = new System.Drawing.Size(16, 16); this.picHint.Size = new System.Drawing.Size(16, 16);
@ -591,9 +599,7 @@ namespace Mesen.GUI.Forms.Config
this.tableLayoutPanel3.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F)); this.tableLayoutPanel3.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F));
this.tableLayoutPanel3.Controls.Add(this.lblOverclockHint, 0, 0); this.tableLayoutPanel3.Controls.Add(this.lblOverclockHint, 0, 0);
this.tableLayoutPanel3.Controls.Add(this.flowLayoutPanel4, 0, 5); this.tableLayoutPanel3.Controls.Add(this.flowLayoutPanel4, 0, 5);
this.tableLayoutPanel3.Controls.Add(this.chkOverclockAdjustApu, 0, 6);
this.tableLayoutPanel3.Controls.Add(this.flowLayoutPanel3, 0, 4); this.tableLayoutPanel3.Controls.Add(this.flowLayoutPanel3, 0, 4);
this.tableLayoutPanel3.Controls.Add(this.grpOverclocking, 0, 2);
this.tableLayoutPanel3.Controls.Add(this.grpPpuTiming, 0, 1); this.tableLayoutPanel3.Controls.Add(this.grpPpuTiming, 0, 1);
this.tableLayoutPanel3.Controls.Add(this.flowLayoutPanel2, 0, 3); this.tableLayoutPanel3.Controls.Add(this.flowLayoutPanel2, 0, 3);
this.tableLayoutPanel3.Controls.Add(this.flowLayoutPanel7, 0, 7); this.tableLayoutPanel3.Controls.Add(this.flowLayoutPanel7, 0, 7);
@ -609,7 +615,7 @@ namespace Mesen.GUI.Forms.Config
this.tableLayoutPanel3.RowStyles.Add(new System.Windows.Forms.RowStyle()); this.tableLayoutPanel3.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel3.RowStyles.Add(new System.Windows.Forms.RowStyle()); this.tableLayoutPanel3.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel3.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tableLayoutPanel3.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.tableLayoutPanel3.Size = new System.Drawing.Size(519, 289); this.tableLayoutPanel3.Size = new System.Drawing.Size(519, 306);
this.tableLayoutPanel3.TabIndex = 0; this.tableLayoutPanel3.TabIndex = 0;
// //
// lblOverclockHint // lblOverclockHint
@ -619,7 +625,7 @@ namespace Mesen.GUI.Forms.Config
this.lblOverclockHint.Location = new System.Drawing.Point(3, 0); this.lblOverclockHint.Location = new System.Drawing.Point(3, 0);
this.lblOverclockHint.Name = "lblOverclockHint"; this.lblOverclockHint.Name = "lblOverclockHint";
this.lblOverclockHint.Padding = new System.Windows.Forms.Padding(25, 0, 0, 0); this.lblOverclockHint.Padding = new System.Windows.Forms.Padding(25, 0, 0, 0);
this.lblOverclockHint.Size = new System.Drawing.Size(513, 41); this.lblOverclockHint.Size = new System.Drawing.Size(517, 41);
this.lblOverclockHint.TabIndex = 1; this.lblOverclockHint.TabIndex = 1;
this.lblOverclockHint.Text = resources.GetString("lblOverclockHint.Text"); this.lblOverclockHint.Text = resources.GetString("lblOverclockHint.Text");
// //
@ -628,7 +634,7 @@ namespace Mesen.GUI.Forms.Config
this.flowLayoutPanel4.Controls.Add(this.lblEffectiveClockRateDendy); this.flowLayoutPanel4.Controls.Add(this.lblEffectiveClockRateDendy);
this.flowLayoutPanel4.Controls.Add(this.lblEffectiveClockRateValueDendy); this.flowLayoutPanel4.Controls.Add(this.lblEffectiveClockRateValueDendy);
this.flowLayoutPanel4.Dock = System.Windows.Forms.DockStyle.Fill; this.flowLayoutPanel4.Dock = System.Windows.Forms.DockStyle.Fill;
this.flowLayoutPanel4.Location = new System.Drawing.Point(0, 203); this.flowLayoutPanel4.Location = new System.Drawing.Point(0, 152);
this.flowLayoutPanel4.Margin = new System.Windows.Forms.Padding(0); this.flowLayoutPanel4.Margin = new System.Windows.Forms.Padding(0);
this.flowLayoutPanel4.Name = "flowLayoutPanel4"; this.flowLayoutPanel4.Name = "flowLayoutPanel4";
this.flowLayoutPanel4.Size = new System.Drawing.Size(519, 20); this.flowLayoutPanel4.Size = new System.Drawing.Size(519, 20);
@ -653,22 +659,12 @@ namespace Mesen.GUI.Forms.Config
this.lblEffectiveClockRateValueDendy.TabIndex = 1; this.lblEffectiveClockRateValueDendy.TabIndex = 1;
this.lblEffectiveClockRateValueDendy.Text = "100%"; this.lblEffectiveClockRateValueDendy.Text = "100%";
// //
// chkOverclockAdjustApu
//
this.chkOverclockAdjustApu.AutoSize = true;
this.chkOverclockAdjustApu.Location = new System.Drawing.Point(3, 226);
this.chkOverclockAdjustApu.Name = "chkOverclockAdjustApu";
this.chkOverclockAdjustApu.Size = new System.Drawing.Size(401, 17);
this.chkOverclockAdjustApu.TabIndex = 10;
this.chkOverclockAdjustApu.Text = "Do not overclock APU (prevents sound pitch changes caused by overclocking)";
this.chkOverclockAdjustApu.UseVisualStyleBackColor = true;
//
// flowLayoutPanel3 // flowLayoutPanel3
// //
this.flowLayoutPanel3.Controls.Add(this.lblEffectiveClockRatePal); this.flowLayoutPanel3.Controls.Add(this.lblEffectiveClockRatePal);
this.flowLayoutPanel3.Controls.Add(this.lblEffectiveClockRateValuePal); this.flowLayoutPanel3.Controls.Add(this.lblEffectiveClockRateValuePal);
this.flowLayoutPanel3.Dock = System.Windows.Forms.DockStyle.Fill; this.flowLayoutPanel3.Dock = System.Windows.Forms.DockStyle.Fill;
this.flowLayoutPanel3.Location = new System.Drawing.Point(0, 186); this.flowLayoutPanel3.Location = new System.Drawing.Point(0, 135);
this.flowLayoutPanel3.Margin = new System.Windows.Forms.Padding(0); this.flowLayoutPanel3.Margin = new System.Windows.Forms.Padding(0);
this.flowLayoutPanel3.Name = "flowLayoutPanel3"; this.flowLayoutPanel3.Name = "flowLayoutPanel3";
this.flowLayoutPanel3.Size = new System.Drawing.Size(519, 17); this.flowLayoutPanel3.Size = new System.Drawing.Size(519, 17);
@ -693,89 +689,6 @@ namespace Mesen.GUI.Forms.Config
this.lblEffectiveClockRateValuePal.TabIndex = 1; this.lblEffectiveClockRateValuePal.TabIndex = 1;
this.lblEffectiveClockRateValuePal.Text = "100%"; this.lblEffectiveClockRateValuePal.Text = "100%";
// //
// grpOverclocking
//
this.grpOverclocking.Controls.Add(this.tableLayoutPanel2);
this.grpOverclocking.Dock = System.Windows.Forms.DockStyle.Fill;
this.grpOverclocking.Location = new System.Drawing.Point(3, 121);
this.grpOverclocking.Name = "grpOverclocking";
this.grpOverclocking.Size = new System.Drawing.Size(513, 45);
this.grpOverclocking.TabIndex = 6;
this.grpOverclocking.TabStop = false;
this.grpOverclocking.Text = "Overclocking";
//
// tableLayoutPanel2
//
this.tableLayoutPanel2.ColumnCount = 4;
this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.tableLayoutPanel2.Controls.Add(this.lblClockRate, 0, 0);
this.tableLayoutPanel2.Controls.Add(this.nudOverclockRate, 1, 0);
this.tableLayoutPanel2.Controls.Add(this.lblClockRatePercent, 2, 0);
this.tableLayoutPanel2.Dock = System.Windows.Forms.DockStyle.Fill;
this.tableLayoutPanel2.Location = new System.Drawing.Point(3, 16);
this.tableLayoutPanel2.Margin = new System.Windows.Forms.Padding(0);
this.tableLayoutPanel2.Name = "tableLayoutPanel2";
this.tableLayoutPanel2.RowCount = 1;
this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel2.Size = new System.Drawing.Size(507, 26);
this.tableLayoutPanel2.TabIndex = 0;
//
// lblClockRate
//
this.lblClockRate.Anchor = System.Windows.Forms.AnchorStyles.Left;
this.lblClockRate.AutoSize = true;
this.lblClockRate.Location = new System.Drawing.Point(3, 7);
this.lblClockRate.Margin = new System.Windows.Forms.Padding(3, 0, 0, 0);
this.lblClockRate.Name = "lblClockRate";
this.lblClockRate.Size = new System.Drawing.Size(107, 13);
this.lblClockRate.TabIndex = 1;
this.lblClockRate.Text = "Clock Rate Multiplier:";
//
// nudOverclockRate
//
this.nudOverclockRate.DecimalPlaces = 0;
this.nudOverclockRate.Increment = new decimal(new int[] {
1,
0,
0,
0});
this.nudOverclockRate.Location = new System.Drawing.Point(110, 3);
this.nudOverclockRate.Margin = new System.Windows.Forms.Padding(0, 3, 0, 3);
this.nudOverclockRate.Maximum = new decimal(new int[] {
1000,
0,
0,
0});
this.nudOverclockRate.MaximumSize = new System.Drawing.Size(10000, 20);
this.nudOverclockRate.Minimum = new decimal(new int[] {
1,
0,
0,
0});
this.nudOverclockRate.MinimumSize = new System.Drawing.Size(0, 21);
this.nudOverclockRate.Name = "nudOverclockRate";
this.nudOverclockRate.Size = new System.Drawing.Size(46, 21);
this.nudOverclockRate.TabIndex = 1;
this.nudOverclockRate.Value = new decimal(new int[] {
100,
0,
0,
0});
//
// lblClockRatePercent
//
this.lblClockRatePercent.Anchor = System.Windows.Forms.AnchorStyles.Left;
this.lblClockRatePercent.AutoSize = true;
this.lblClockRatePercent.Location = new System.Drawing.Point(156, 7);
this.lblClockRatePercent.Margin = new System.Windows.Forms.Padding(0, 0, 3, 0);
this.lblClockRatePercent.Name = "lblClockRatePercent";
this.lblClockRatePercent.Size = new System.Drawing.Size(90, 13);
this.lblClockRatePercent.TabIndex = 1;
this.lblClockRatePercent.Text = "% (Default: 100%)";
//
// grpPpuTiming // grpPpuTiming
// //
this.grpPpuTiming.Controls.Add(this.tableLayoutPanel5); this.grpPpuTiming.Controls.Add(this.tableLayoutPanel5);
@ -893,7 +806,7 @@ namespace Mesen.GUI.Forms.Config
this.flowLayoutPanel2.Controls.Add(this.lblEffectiveClockRate); this.flowLayoutPanel2.Controls.Add(this.lblEffectiveClockRate);
this.flowLayoutPanel2.Controls.Add(this.lblEffectiveClockRateValue); this.flowLayoutPanel2.Controls.Add(this.lblEffectiveClockRateValue);
this.flowLayoutPanel2.Dock = System.Windows.Forms.DockStyle.Fill; this.flowLayoutPanel2.Dock = System.Windows.Forms.DockStyle.Fill;
this.flowLayoutPanel2.Location = new System.Drawing.Point(0, 169); this.flowLayoutPanel2.Location = new System.Drawing.Point(0, 118);
this.flowLayoutPanel2.Margin = new System.Windows.Forms.Padding(0); this.flowLayoutPanel2.Margin = new System.Windows.Forms.Padding(0);
this.flowLayoutPanel2.Name = "flowLayoutPanel2"; this.flowLayoutPanel2.Name = "flowLayoutPanel2";
this.flowLayoutPanel2.Size = new System.Drawing.Size(519, 17); this.flowLayoutPanel2.Size = new System.Drawing.Size(519, 17);
@ -923,10 +836,10 @@ namespace Mesen.GUI.Forms.Config
this.flowLayoutPanel7.Controls.Add(this.chkShowLagCounter); this.flowLayoutPanel7.Controls.Add(this.chkShowLagCounter);
this.flowLayoutPanel7.Controls.Add(this.btnResetLagCounter); this.flowLayoutPanel7.Controls.Add(this.btnResetLagCounter);
this.flowLayoutPanel7.Dock = System.Windows.Forms.DockStyle.Fill; this.flowLayoutPanel7.Dock = System.Windows.Forms.DockStyle.Fill;
this.flowLayoutPanel7.Location = new System.Drawing.Point(0, 246); this.flowLayoutPanel7.Location = new System.Drawing.Point(0, 172);
this.flowLayoutPanel7.Margin = new System.Windows.Forms.Padding(0); this.flowLayoutPanel7.Margin = new System.Windows.Forms.Padding(0);
this.flowLayoutPanel7.Name = "flowLayoutPanel7"; this.flowLayoutPanel7.Name = "flowLayoutPanel7";
this.flowLayoutPanel7.Size = new System.Drawing.Size(519, 43); this.flowLayoutPanel7.Size = new System.Drawing.Size(519, 134);
this.flowLayoutPanel7.TabIndex = 12; this.flowLayoutPanel7.TabIndex = 12;
// //
// chkShowLagCounter // chkShowLagCounter
@ -961,12 +874,12 @@ namespace Mesen.GUI.Forms.Config
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.AutoSize = true; this.AutoSize = true;
this.ClientSize = new System.Drawing.Size(533, 350); this.ClientSize = new System.Drawing.Size(533, 367);
this.Controls.Add(this.tabMain); this.Controls.Add(this.tabMain);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
this.MaximizeBox = false; this.MaximizeBox = false;
this.MinimizeBox = false; this.MinimizeBox = false;
this.MinimumSize = new System.Drawing.Size(503, 322); this.MinimumSize = new System.Drawing.Size(503, 367);
this.Name = "frmEmulationConfig"; this.Name = "frmEmulationConfig";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
this.Text = "Emulation Settings"; this.Text = "Emulation Settings";
@ -992,14 +905,10 @@ namespace Mesen.GUI.Forms.Config
this.tpgOverclocking.PerformLayout(); this.tpgOverclocking.PerformLayout();
((System.ComponentModel.ISupportInitialize)(this.picHint)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.picHint)).EndInit();
this.tableLayoutPanel3.ResumeLayout(false); this.tableLayoutPanel3.ResumeLayout(false);
this.tableLayoutPanel3.PerformLayout();
this.flowLayoutPanel4.ResumeLayout(false); this.flowLayoutPanel4.ResumeLayout(false);
this.flowLayoutPanel4.PerformLayout(); this.flowLayoutPanel4.PerformLayout();
this.flowLayoutPanel3.ResumeLayout(false); this.flowLayoutPanel3.ResumeLayout(false);
this.flowLayoutPanel3.PerformLayout(); this.flowLayoutPanel3.PerformLayout();
this.grpOverclocking.ResumeLayout(false);
this.tableLayoutPanel2.ResumeLayout(false);
this.tableLayoutPanel2.PerformLayout();
this.grpPpuTiming.ResumeLayout(false); this.grpPpuTiming.ResumeLayout(false);
this.tableLayoutPanel5.ResumeLayout(false); this.tableLayoutPanel5.ResumeLayout(false);
this.tableLayoutPanel5.PerformLayout(); this.tableLayoutPanel5.PerformLayout();
@ -1020,11 +929,6 @@ namespace Mesen.GUI.Forms.Config
private ctrlRiskyOption chkAllowInvalidInput; private ctrlRiskyOption chkAllowInvalidInput;
private System.Windows.Forms.TabPage tpgOverclocking; private System.Windows.Forms.TabPage tpgOverclocking;
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel3; private System.Windows.Forms.TableLayoutPanel tableLayoutPanel3;
private System.Windows.Forms.GroupBox grpOverclocking;
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel2;
private System.Windows.Forms.Label lblClockRate;
private MesenNumericUpDown nudOverclockRate;
private System.Windows.Forms.Label lblClockRatePercent;
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel4; private System.Windows.Forms.TableLayoutPanel tableLayoutPanel4;
private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel6; private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel6;
private MesenNumericUpDown nudEmulationSpeed; private MesenNumericUpDown nudEmulationSpeed;
@ -1042,7 +946,6 @@ namespace Mesen.GUI.Forms.Config
private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel3; private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel3;
private System.Windows.Forms.Label lblEffectiveClockRatePal; private System.Windows.Forms.Label lblEffectiveClockRatePal;
private System.Windows.Forms.Timer tmrUpdateClockRate; private System.Windows.Forms.Timer tmrUpdateClockRate;
private System.Windows.Forms.CheckBox chkOverclockAdjustApu;
private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel4; private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel4;
private System.Windows.Forms.Label lblEffectiveClockRateDendy; private System.Windows.Forms.Label lblEffectiveClockRateDendy;
private System.Windows.Forms.Label lblEffectiveClockRateValueDendy; private System.Windows.Forms.Label lblEffectiveClockRateValueDendy;
@ -1072,5 +975,6 @@ namespace Mesen.GUI.Forms.Config
private System.Windows.Forms.CheckBox chkUseNes101Hvc101Behavior; private System.Windows.Forms.CheckBox chkUseNes101Hvc101Behavior;
private System.Windows.Forms.Label lblDeveloperSettings; private System.Windows.Forms.Label lblDeveloperSettings;
private System.Windows.Forms.Label lblMiscSettings; private System.Windows.Forms.Label lblMiscSettings;
private ctrlRiskyOption chkRandomizeCpuPpuAlignment;
} }
} }

View file

@ -37,9 +37,7 @@ namespace Mesen.GUI.Forms.Config
AddBinding("EnableOamDecay", chkEnableOamDecay); AddBinding("EnableOamDecay", chkEnableOamDecay);
AddBinding("UseNes101Hvc101Behavior", chkUseNes101Hvc101Behavior); AddBinding("UseNes101Hvc101Behavior", chkUseNes101Hvc101Behavior);
AddBinding("EnableMapperRandomPowerOnState", chkMapperRandomPowerOnState); AddBinding("EnableMapperRandomPowerOnState", chkMapperRandomPowerOnState);
AddBinding("RandomizeCpuPpuAlignment", chkRandomizeCpuPpuAlignment);
AddBinding("OverclockRate", nudOverclockRate);
AddBinding("OverclockAdjustApu", chkOverclockAdjustApu);
AddBinding("PpuExtraScanlinesBeforeNmi", nudExtraScanlinesBeforeNmi); AddBinding("PpuExtraScanlinesBeforeNmi", nudExtraScanlinesBeforeNmi);
AddBinding("PpuExtraScanlinesAfterNmi", nudExtraScanlinesAfterNmi); AddBinding("PpuExtraScanlinesAfterNmi", nudExtraScanlinesAfterNmi);
@ -57,9 +55,9 @@ namespace Mesen.GUI.Forms.Config
private void tmrUpdateClockRate_Tick(object sender, EventArgs e) private void tmrUpdateClockRate_Tick(object sender, EventArgs e)
{ {
decimal clockRateMultiplierNtsc = (nudOverclockRate.Value * (1 + (nudExtraScanlinesAfterNmi.Value + nudExtraScanlinesBeforeNmi.Value) / 262)); decimal clockRateMultiplierNtsc = (100 * (1 + (nudExtraScanlinesAfterNmi.Value + nudExtraScanlinesBeforeNmi.Value) / 262));
decimal clockRateMultiplierPal = (nudOverclockRate.Value * (1 + (nudExtraScanlinesAfterNmi.Value + nudExtraScanlinesBeforeNmi.Value) / 312)); decimal clockRateMultiplierPal = (100 * (1 + (nudExtraScanlinesAfterNmi.Value + nudExtraScanlinesBeforeNmi.Value) / 312));
decimal clockRateMultiplierDendy = (nudOverclockRate.Value * (1 + (nudExtraScanlinesAfterNmi.Value + nudExtraScanlinesBeforeNmi.Value) / 312)); decimal clockRateMultiplierDendy = (100 * (1 + (nudExtraScanlinesAfterNmi.Value + nudExtraScanlinesBeforeNmi.Value) / 312));
lblEffectiveClockRateValue.Text = (1789773 * clockRateMultiplierNtsc / 100000000).ToString("#.####") + " mhz (" + ((int)clockRateMultiplierNtsc).ToString() + "%)"; lblEffectiveClockRateValue.Text = (1789773 * clockRateMultiplierNtsc / 100000000).ToString("#.####") + " mhz (" + ((int)clockRateMultiplierNtsc).ToString() + "%)";
lblEffectiveClockRateValuePal.Text = (1662607 * clockRateMultiplierPal / 100000000).ToString("#.####") + " mhz (" + ((int)clockRateMultiplierPal).ToString() + "%)"; lblEffectiveClockRateValuePal.Text = (1662607 * clockRateMultiplierPal / 100000000).ToString("#.####") + " mhz (" + ((int)clockRateMultiplierPal).ToString() + "%)";
lblEffectiveClockRateValueDendy.Text = (1773448 * clockRateMultiplierDendy / 100000000).ToString("#.####") + " mhz (" + ((int)clockRateMultiplierDendy).ToString() + "%)"; lblEffectiveClockRateValueDendy.Text = (1773448 * clockRateMultiplierDendy / 100000000).ToString("#.####") + " mhz (" + ((int)clockRateMultiplierDendy).ToString() + "%)";

View file

@ -201,7 +201,6 @@ namespace Mesen.GUI
[DllImport(DLLPath)] public static extern void SetTurboRewindSpeed(UInt32 turboSpeed, UInt32 rewindSpeed); [DllImport(DLLPath)] public static extern void SetTurboRewindSpeed(UInt32 turboSpeed, UInt32 rewindSpeed);
[DllImport(DLLPath)] public static extern void SetRewindBufferSize(UInt32 seconds); [DllImport(DLLPath)] public static extern void SetRewindBufferSize(UInt32 seconds);
[DllImport(DLLPath)] [return: MarshalAs(UnmanagedType.I1)] public static extern bool IsRewinding(); [DllImport(DLLPath)] [return: MarshalAs(UnmanagedType.I1)] public static extern bool IsRewinding();
[DllImport(DLLPath)] public static extern void SetOverclockRate(UInt32 overclockRate, [MarshalAs(UnmanagedType.I1)]bool adjustApu);
[DllImport(DLLPath)] public static extern void SetPpuNmiConfig(UInt32 extraScanlinesBeforeNmi, UInt32 extraScanlineAfterNmi); [DllImport(DLLPath)] public static extern void SetPpuNmiConfig(UInt32 extraScanlinesBeforeNmi, UInt32 extraScanlineAfterNmi);
[DllImport(DLLPath)] public static extern void SetOverscanDimensions(UInt32 left, UInt32 right, UInt32 top, UInt32 bottom); [DllImport(DLLPath)] public static extern void SetOverscanDimensions(UInt32 left, UInt32 right, UInt32 top, UInt32 bottom);
[DllImport(DLLPath)] public static extern void SetVideoScale(double scale, ConsoleId consoleId = ConsoleId.Master); [DllImport(DLLPath)] public static extern void SetVideoScale(double scale, ConsoleId consoleId = ConsoleId.Master);
@ -1704,6 +1703,8 @@ namespace Mesen.GUI
VsDualMuteMaster = 0x200000000000000, VsDualMuteMaster = 0x200000000000000,
VsDualMuteSlave = 0x400000000000000, VsDualMuteSlave = 0x400000000000000,
RandomizeCpuPpuAlignment = 0x800000000000000,
ForceMaxSpeed = 0x4000000000000000, ForceMaxSpeed = 0x4000000000000000,
ConsoleMode = 0x8000000000000000, ConsoleMode = 0x8000000000000000,
} }

View file

@ -640,7 +640,6 @@ namespace InteropEmu {
shared_ptr<RewindManager> rewindManager = _console->GetRewindManager(); shared_ptr<RewindManager> rewindManager = _console->GetRewindManager();
return rewindManager ? rewindManager->IsRewinding() : false; return rewindManager ? rewindManager->IsRewinding() : false;
} }
DllExport void __stdcall SetOverclockRate(uint32_t overclockRate, bool adjustApu) { _settings->SetOverclockRate(overclockRate, adjustApu); }
DllExport void __stdcall SetPpuNmiConfig(uint32_t extraScanlinesBeforeNmi, uint32_t extraScanlinesAfterNmi) { _settings->SetPpuNmiConfig(extraScanlinesBeforeNmi, extraScanlinesAfterNmi); } DllExport void __stdcall SetPpuNmiConfig(uint32_t extraScanlinesBeforeNmi, uint32_t extraScanlinesAfterNmi) { _settings->SetPpuNmiConfig(extraScanlinesBeforeNmi, extraScanlinesAfterNmi); }
DllExport void __stdcall SetVideoScale(double scale, ConsoleId consoleId) { GetConsoleById(consoleId)->GetSettings()->SetVideoScale(scale); } DllExport void __stdcall SetVideoScale(double scale, ConsoleId consoleId) { GetConsoleById(consoleId)->GetSettings()->SetVideoScale(scale); }
DllExport void __stdcall SetScreenRotation(uint32_t angle) { _settings->SetScreenRotation(angle); } DllExport void __stdcall SetScreenRotation(uint32_t angle) { _settings->SetScreenRotation(angle); }

View file

@ -13,6 +13,7 @@
#include "../Core/EmulationSettings.h" #include "../Core/EmulationSettings.h"
#include "../Core/CheatManager.h" #include "../Core/CheatManager.h"
#include "../Core/HdData.h" #include "../Core/HdData.h"
#include "../Core/SaveStateManager.h"
#include "../Core/DebuggerTypes.h" #include "../Core/DebuggerTypes.h"
#include "../Core/GameDatabase.h" #include "../Core/GameDatabase.h"
#include "../Utilities/FolderUtilities.h" #include "../Utilities/FolderUtilities.h"
@ -658,7 +659,7 @@ extern "C" {
RETRO_API bool retro_serialize(void *data, size_t size) RETRO_API bool retro_serialize(void *data, size_t size)
{ {
std::stringstream ss; std::stringstream ss;
_console->SaveState(ss); _console->GetSaveStateManager()->SaveState(ss);
string saveStateData = ss.str(); string saveStateData = ss.str();
memset(data, 0, size); memset(data, 0, size);
@ -669,8 +670,9 @@ extern "C" {
RETRO_API bool retro_unserialize(const void *data, size_t size) RETRO_API bool retro_unserialize(const void *data, size_t size)
{ {
_console->LoadState((uint8_t*)data, (uint32_t)size); std::stringstream ss;
return true; ss.write((char*)data, size);
return _console->GetSaveStateManager()->LoadState(ss, false);
} }
RETRO_API void retro_cheat_reset() RETRO_API void retro_cheat_reset()
@ -996,7 +998,7 @@ extern "C" {
//Retroarch doesn't like this for netplay or rewinding - it requires the states to always be the exact same size //Retroarch doesn't like this for netplay or rewinding - it requires the states to always be the exact same size
//So we need to send a large enough size to Retroarch to ensure Mesen's state will always fit within that buffer. //So we need to send a large enough size to Retroarch to ensure Mesen's state will always fit within that buffer.
std::stringstream ss; std::stringstream ss;
_console->SaveState(ss); _console->GetSaveStateManager()->SaveState(ss);
//Round up to the next 1kb multiple //Round up to the next 1kb multiple
_saveStateSize = ((ss.str().size() * 2) + 0x400) & ~0x3FF; _saveStateSize = ((ss.str().size() * 2) + 0x400) & ~0x3FF;