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)
{
if(_needToRun || _deltaModulationChannel->NeedToRun()) {
if(_deltaModulationChannel->NeedToRun() || _needToRun) {
//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.)
_needToRun = false;
@ -210,22 +210,13 @@ void APU::EndFrame()
void APU::ProcessCpuClock()
{
if(_apuEnabled) {
if(_settings->GetOverclockRate() == 100 || !_settings->GetOverclockAdjustApu()) {
Exec();
} else {
_cyclesNeeded += 1.0 / ((double)_settings->GetOverclockRate() / 100.0);
while(_cyclesNeeded >= 1.0) {
Exec();
_cyclesNeeded--;
}
}
Exec();
}
}
void APU::Reset(bool softReset)
{
_apuEnabled = true;
_cyclesNeeded = 0;
_currentCycle = 0;
_previousCycle = 0;
_squareChannel[0]->Reset(softReset);
@ -253,7 +244,7 @@ void APU::StreamState(bool saving)
SnapshotInfo deltaModulationChannel{ _deltaModulationChannel.get() };
SnapshotInfo frameCounter{ _frameCounter.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)
@ -275,9 +266,14 @@ bool APU::IsApuEnabled()
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()

View file

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

View file

@ -64,8 +64,7 @@ public:
void StreamState(bool saving) override
{
int32_t unusednextIrqCycle = 0;
Stream(unusednextIrqCycle, _previousCycle, _currentStep, _stepMode, _inhibitIRQ, _nesModel, _blockFrameCounterTick, _writeDelayCounter, _newValue);
Stream(_previousCycle, _currentStep, _stepMode, _inhibitIRQ, _nesModel, _blockFrameCounterTick, _writeDelayCounter, _newValue);
if(!saving) {
SetNesModel(_nesModel);
@ -185,11 +184,11 @@ public:
//Reset sequence after $4017 is written to
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. "
_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;

View file

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

View file

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

View file

@ -1,4 +1,6 @@
#include "stdafx.h"
#include <random>
#include <assert.h>
#include "CPU.h"
#include "PPU.h"
#include "APU.h"
@ -60,12 +62,15 @@ CPU::CPU(shared_ptr<Console> console)
_state = {};
_cycleCount = 0;
_operand = 0;
_spriteDmaCounter = 0;
_spriteDmaTransfer = false;
_dmcCounter = 0;
_spriteDmaOffset = 0;
_needHalt = false;
_ppuOffset = 0;
_startClockCount = 6;
_endClockCount = 6;
_masterClock = 0;
_dmcDmaRunning = false;
_cpuWrite = false;
_writeAddr = 0;
_irqMask = 0;
_state = {};
_prevRunIrq = false;
@ -76,12 +81,10 @@ void CPU::Reset(bool softReset, NesModel model)
{
_state.NMIFlag = false;
_state.IRQFlag = 0;
_cycleCount = -1;
_spriteDmaTransfer = false;
_spriteDmaCounter = 0;
_dmcCounter = -1;
_spriteDmaOffset = 0;
_needHalt = false;
_dmcDmaRunning = false;
_warnOnCrash = true;
@ -106,13 +109,50 @@ void CPU::Reset(bool softReset, NesModel model)
_runIrq = false;
}
//The CPU takes some cycles before starting its execution after a reset/power up
for(int i = 0; i < (model == NesModel::NTSC ? 28 : 30); i++) {
_console->GetPpu()->Exec();
if(!softReset) {
uint8_t ppuDivider;
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++) {
_console->GetApu()->ProcessCpuClock();
//The CPU takes 8 cycles before it starts executing the ROM's code after a reset/power up
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++;
}
#else
_cpuWrite = true;;
_writeAddr = addr;
IncCycleCount();
while(_dmcDmaRunning) {
IncCycleCount();
}
_cpuWrite = true;
StartCpuCycle(false);
_memoryManager->Write(addr, value, operationType);
//DMA DMC might have started after a write to $4015, stall CPU if needed
while(_dmcDmaRunning) {
IncCycleCount();
}
EndCpuCycle(false);
_cpuWrite = false;
#endif
}
@ -228,20 +259,11 @@ uint8_t CPU::MemoryRead(uint16_t addr, MemoryOperationType operationType) {
}
return value;
#else
IncCycleCount();
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();
}
ProcessPendingDma(addr);
StartCpuCycle(true);
uint8_t value = _memoryManager->Read(addr, operationType);
EndCpuCycle(true);
return value;
#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) {
//CPU is being stalled by the DMC's DMA transfer
_dmcCounter--;
if(_dmcCounter == 0) {
//Update the DMC buffer when the stall period is completed
_dmcDmaRunning = false;
#ifndef DUMMYCPU
_console->GetApu()->FillDmcReadBuffer();
_console->DebugAddTrace("DMC DMA End");
#endif
}
//"it's really the status of the interrupt lines at the end of the second-to-last cycle that matters."
//Keep the irq lines values from the previous cycle. The before-to-last cycle's values will be used
_prevRunIrq = _runIrq;
_runIrq = _state.NMIFlag || ((_state.IRQFlag & _irqMask) > 0 && !CheckFlag(PSFlags::Interrupt));
}
void CPU::StartCpuCycle(bool forRead)
{
_masterClock += forRead ? (_startClockCount - 1) : (_startClockCount + 1);
_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) {
//IRQ flags are ignored during Sprite DMA - fixes irq_and_dma
uint16_t spriteDmaCounter = 0;
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."
//Keep the irq lines values from the previous cycle. The before-to-last cycle's values will be used
_prevRunIrq = _runIrq;
_runIrq = _state.NMIFlag || ((_state.IRQFlag & _irqMask) > 0 && !CheckFlag(PSFlags::Interrupt));
auto processCycle = [this] {
//Sprite DMA cycles count as halt/dummy cycles for the DMC DMA when both run at the same time
if(_needHalt) {
_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)
{
_console->DebugAddTrace("Sprite DMA Start");
_spriteDmaTransfer = true;
//"The CPU is suspended during the transfer, which will take 513 or 514 cycles after the $4014 write tick."
//"(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");
_spriteDmaOffset = offsetValue;
_needHalt = true;
}
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;
if(_spriteDmaTransfer) {
if(_spriteDmaCounter == 2) {
_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;
}
}
_needDummyRead = true;
_needHalt = true;
}
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)
{
EmulationSettings* settings = _console->GetSettings();
uint32_t overclockRate = settings->GetOverclockRateSetting();
bool overclockAdjustApu = settings->GetOverclockAdjustApu();
uint32_t extraScanlinesBeforeNmi = settings->GetPpuExtraScanlinesBeforeNmi();
uint32_t extraScanlinesAfterNmi = settings->GetPpuExtraScanlinesAfterNmi();
uint32_t dipSwitches = _console->GetSettings()->GetDipSwitches();
Stream(_state.PC, _state.SP, _state.PS, _state.A, _state.X, _state.Y, _cycleCount, _state.NMIFlag,
_state.IRQFlag, _dmcCounter, _dmcDmaRunning, _spriteDmaCounter, _spriteDmaTransfer,
overclockRate, overclockAdjustApu, extraScanlinesBeforeNmi, extraScanlinesBeforeNmi, dipSwitches);
_state.IRQFlag, _dmcDmaRunning, _spriteDmaTransfer,
extraScanlinesBeforeNmi, extraScanlinesBeforeNmi, dipSwitches,
_needDummyRead, _needHalt, _startClockCount, _endClockCount, _ppuOffset, _masterClock);
if(!saving) {
settings->SetOverclockRate(overclockRate, overclockAdjustApu);
settings->SetPpuNmiConfig(extraScanlinesBeforeNmi, extraScanlinesAfterNmi);
settings->SetDipSwitches(dipSwitches);
}

View file

@ -32,19 +32,23 @@ private:
typedef void(CPU::*Func)();
uint64_t _cycleCount;
uint64_t _masterClock;
uint8_t _ppuOffset;
uint8_t _startClockCount;
uint8_t _endClockCount;
uint16_t _operand;
Func _opTable[256];
AddrMode _addrMode[256];
AddrMode _instAddrMode;
uint16_t _spriteDmaCounter;
bool _spriteDmaTransfer;
bool _needHalt = false;
bool _spriteDmaTransfer = false;
bool _dmcDmaRunning = false;
bool _needDummyRead = false;
uint8_t _spriteDmaOffset;
int8_t _dmcCounter;
bool _dmcDmaRunning;
bool _cpuWrite = false;
uint16_t _writeAddr = 0;
uint8_t _irqMask;
@ -69,8 +73,10 @@ private:
bool _isDummyRead[10];
#endif
void IncCycleCount();
uint16_t FetchOperand();
__forceinline void StartCpuCycle(bool forRead);
__forceinline void ProcessPendingDma(uint16_t readAddress);
__forceinline uint16_t FetchOperand();
__forceinline void EndCpuCycle(bool forRead);
void IRQ();
uint8_t GetOPCode()
@ -778,6 +784,7 @@ public:
CPU(shared_ptr<Console> console);
uint64_t GetCycleCount() { return _cycleCount; }
void SetMasterClockDivider(NesModel region);
void SetNmiFlag() { _state.NMIFlag = true; }
void ClearNmiFlag() { _state.NMIFlag = false; }
void SetIrqMask(uint8_t mask) { _irqMask = mask; }
@ -832,11 +839,13 @@ public:
_cycleCount = c->_cycleCount;
_operand = c->_operand;
_spriteDmaCounter = c->_spriteDmaCounter;
_spriteDmaTransfer = c->_spriteDmaTransfer;
_dmcCounter = c->_dmcCounter;
_needHalt = c->_needHalt;
_dmcDmaRunning = c->_dmcDmaRunning;
_cpuWrite = c->_cpuWrite;
_needDummyRead = c->_needDummyRead;
_needHalt = c->_needHalt;
_spriteDmaOffset = c->_spriteDmaOffset;
_irqMask = c->_irqMask;
_prevRunIrq = c->_prevRunIrq;
_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 messageTitle = MessageManager::Localize("GameLoaded") + " (" + modelName + ")";
MessageManager::DisplayMessage(messageTitle, FolderUtilities::GetFilename(GetRomInfo().RomName, false));
if(_settings->GetOverclockRate() != 100) {
MessageManager::DisplayMessage("ClockRate", std::to_string(_settings->GetOverclockRate()) + "%");
}
_settings->ClearFlags(EmulationFlags::ForceMaxSpeed);
if(_slave) {
@ -451,7 +448,6 @@ bool Console::Initialize(VirtualFile &romFile, VirtualFile &patchFile)
void Console::ProcessCpuClock()
{
_mapper->ProcessCpuClock();
_ppu->ProcessCpuClock();
_apu->ProcessCpuClock();
}
@ -967,6 +963,7 @@ void Console::UpdateNesModel(bool sendNotification)
}
}
_cpu->SetMasterClockDivider(model);
_mapper->SetNesModel(model);
_ppu->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) {
_readBuffer = _console->GetMemoryManager()->Read(_currentAddr, MemoryOperationType::DmcRead);
_readBuffer = value;
_bufferEmpty = false;
//"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;
} else if(_bytesRemaining == 0) {
InitSample();
StartDmcTransfer();
_needInit = true;
}
}
bool DeltaModulationChannel::NeedToRun()
{
if(_needInit && (_console->GetCpu()->GetCycleCount() & 0x01) == 0) {
StartDmcTransfer();
_needInit = false;
}
return _needToRun;
}

View file

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

View file

@ -95,6 +95,8 @@ enum EmulationFlags : uint64_t
VsDualMuteMaster = 0x200000000000000,
VsDualMuteSlave = 0x400000000000000,
RandomizeCpuPpuAlignment = 0x800000000000000,
ForceMaxSpeed = 0x4000000000000000,
ConsoleMode = 0x8000000000000000,
};
@ -648,13 +650,9 @@ private:
uint32_t _rewindBufferSize = 300;
bool _hasOverclock = false;
uint32_t _overclockRate = 100;
bool _overclockAdjustApu = true;
bool _disableOverclocking = false;
uint32_t _extraScanlinesBeforeNmi = 0;
uint32_t _extraScanlinesAfterNmi = 0;
double _effectiveOverclockRate = 100;
OverscanDimensions _overscan;
VideoFilterType _videoFilterType = VideoFilterType::None;
@ -1041,55 +1039,11 @@ public:
}
uint32_t GetEmulationSpeed(bool ignoreTurbo = false);
void UpdateEffectiveOverclockRate()
{
if(_disableOverclocking) {
_effectiveOverclockRate = 100;
} else {
_effectiveOverclockRate = _overclockRate;
}
_hasOverclock = _effectiveOverclockRate != 100;
_audioSettingsChanged = true;
}
void DisableOverclocking(bool disabled)
{
if(_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;
_extraScanlinesAfterNmi = extraScanlinesAfterNmi;
UpdateEffectiveOverclockRate();
}
}

View file

@ -197,7 +197,7 @@ void MemoryAccessCounter::GetNametableChangedData(bool ntChangedData[])
uint64_t cpuCycle = _debugger->GetConsole()->GetCpu()->GetCycleCount();
NesModel model = _debugger->GetConsole()->GetModel();
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);
for(int i = 0; i < 0x1000; i++) {

View file

@ -247,14 +247,6 @@ void MesenMovie::ApplySettings()
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(
LoadInt(_settings, MovieKeys::ExtraScanlinesBeforeNmi),
LoadInt(_settings, MovieKeys::ExtraScanlinesAfterNmi)

View file

@ -107,14 +107,10 @@ void MovieRecorder::GetGameSettings(stringstream &out)
WriteString(out, MovieKeys::ExpansionDevice, ExpansionPortDeviceNames[(int)settings->GetExpansionDevice()]);
}
WriteInt(out, MovieKeys::CpuClockRate, settings->GetOverclockRateSetting());
WriteInt(out, MovieKeys::ExtraScanlinesBeforeNmi, settings->GetPpuExtraScanlinesBeforeNmi());
WriteInt(out, MovieKeys::ExtraScanlinesAfterNmi, settings->GetPpuExtraScanlinesAfterNmi());
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::DisablePaletteRead, settings->CheckFlag(EmulationFlags::DisablePaletteRead));
WriteBool(out, MovieKeys::DisableOamAddrBug, settings->CheckFlag(EmulationFlags::DisableOamAddrBug));

View file

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

View file

@ -15,6 +15,8 @@
PPU::PPU(shared_ptr<Console> console)
{
_console = console;
_masterClock = 0;
_masterClockDivider = 4;
_settings = _console->GetSettings();
_outputBuffers[0] = new uint16_t[256 * 240];
@ -42,7 +44,7 @@ PPU::~PPU()
void PPU::Reset()
{
_cyclesNeeded = 0;
_preventVblFlag = false;
_needStateUpdate = false;
_prevRenderingEnabled = false;
@ -73,7 +75,6 @@ void PPU::Reset()
_oamCopyDone = false;
_renderingEnabled = false;
_prevRenderingEnabled = false;
_cyclesNeeded = 0.0;
memset(_hasSprite, 0, sizeof(_hasSprite));
memset(_spriteTiles, 0, sizeof(_spriteTiles));
@ -118,18 +119,21 @@ void PPU::SetNesModel(NesModel model)
_vblankEnd = 260;
_standardNmiScanline = 241;
_standardVblankEnd = 260;
_masterClockDivider = 4;
break;
case NesModel::PAL:
_nmiScanline = 241;
_vblankEnd = 310;
_standardNmiScanline = 241;
_standardVblankEnd = 310;
_masterClockDivider = 5;
break;
case NesModel::Dendy:
_nmiScanline = 291;
_vblankEnd = 310;
_standardNmiScanline = 291;
_standardVblankEnd = 310;
_masterClockDivider = 5;
break;
}
@ -561,8 +565,7 @@ void PPU::UpdateStatusFlag()
_console->GetCpu()->ClearNmiFlag();
if(_cycle == 0) {
//"Reading one PPU clock before reads it as clear and never sets the flag or generates NMI for that frame. "
_state.Status = ((uint8_t)_statusFlags.SpriteOverflow << 5) | ((uint8_t)_statusFlags.Sprite0Hit << 6);
_preventVblFlag = true;
}
}
}
@ -639,8 +642,8 @@ void PPU::WriteVram(uint16_t addr, uint8_t value)
void PPU::LoadTileInfo()
{
if(IsRenderingEnabled()) {
switch((_cycle - 1) & 0x07) {
case 0: {
switch(_cycle & 0x07) {
case 1: {
_previousTile = _currentTile;
_currentTile = _nextTile;
@ -653,18 +656,18 @@ void PPU::LoadTileInfo()
break;
}
case 2: {
case 3: {
uint8_t shift = ((_state.VideoRamAddr >> 4) & 0x04) | (_state.VideoRamAddr & 0x02);
_nextTile.PaletteOffset = ((ReadVram(GetAttributeAddr()) >> shift) & 0x03) << 2;
break;
}
case 3:
case 5:
_nextTile.LowByte = ReadVram(_nextTile.TileAddr);
_nextTile.AbsoluteTileAddr = _console->GetMapper()->ToAbsoluteChrAddress(_nextTile.TileAddr);
break;
case 5:
case 7:
_nextTile.HighByte = ReadVram(_nextTile.TileAddr + 8);
break;
}
@ -864,10 +867,10 @@ void PPU::UpdateGrayscaleAndIntensifyBits()
int pixelNumber;
if(_scanline >= 240) {
pixelNumber = 61439;
} else if(_cycle < 3) {
} else if(_cycle < 4) {
pixelNumber = (_scanline << 8) - 1;
} else if(_cycle <= 258) {
pixelNumber = (_scanline << 8) + _cycle - 3;
pixelNumber = (_scanline << 8) + _cycle - 4;
} else {
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)
_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))
LoadSpriteTileInfo();
} else if((_cycle - 257) % 8 == 0) {
@ -1157,7 +1160,6 @@ void PPU::BeginVBlank()
void PPU::TriggerNmi()
{
_statusFlags.VerticalBlank = true;
if(_flags.VBlank) {
_console->GetCpu()->SetNmiFlag();
}
@ -1222,7 +1224,6 @@ void PPU::Exec()
SendFrame();
_frameCount++;
} else if(_scanline == _nmiScanline) {
BeginVBlank();
}
} else {
//Cycle > 0
@ -1231,6 +1232,12 @@ void PPU::Exec()
_console->DebugProcessPpuCycle();
if(_scanline < 240) {
ProcessScanline();
} else if(_cycle == 1 && _scanline == _nmiScanline) {
if(!_preventVblFlag) {
_statusFlags.VerticalBlank = true;
BeginVBlank();
}
_preventVblFlag = false;
} 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
//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()
{
//Used by debugger
@ -1371,8 +1353,8 @@ void PPU::StreamState(bool saving)
_nextTile.PaletteOffset, _nextTile.TileAddr, _previousTile.LowByte, _previousTile.HighByte, _previousTile.PaletteOffset, _spriteIndex, _spriteCount,
_secondaryOAMAddr, _sprite0Visible, _oamCopybuffer, _spriteInRange, _sprite0Added, _spriteAddrH, _spriteAddrL, _oamCopyDone, _nesModel,
_prevRenderingEnabled, _renderingEnabled, _openBus, _ignoreVramRead, paletteRam, spriteRam, secondarySpriteRam,
openBusDecayStamp, _cyclesNeeded, disablePpu2004Reads, disablePaletteRead, disableOamAddrBug, _overflowBugCounter, _updateVramAddr, _updateVramAddrDelay,
_needStateUpdate, _ppuBusAddress);
openBusDecayStamp, disablePpu2004Reads, disablePaletteRead, disableOamAddrBug, _overflowBugCounter, _updateVramAddr, _updateVramAddrDelay,
_needStateUpdate, _ppuBusAddress, _preventVblFlag, _masterClock);
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);

View file

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

View file

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

View file

@ -203,9 +203,6 @@ void SoundMixer::SetNesModel(NesModel model)
void SoundMixer::UpdateRates(bool forceUpdate)
{
uint32_t newRate = _console->GetCpu()->GetClockRate(_model);
if(!_settings->GetOverclockAdjustApu()) {
newRate = (uint32_t)(newRate * (double)_settings->GetOverclockRate() / 100);
}
if(_settings->CheckFlag(EmulationFlags::IntegerFpsMode)) {
//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 UseNes101Hvc101Behavior = false;
public bool EnableMapperRandomPowerOnState = false;
public bool RandomizeCpuPpuAlignment = 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 PpuExtraScanlinesAfterNmi = 0;
@ -60,8 +58,8 @@ namespace Mesen.GUI.Config
InteropEmu.SetFlag(EmulationFlags.EnableOamDecay, emulationInfo.EnableOamDecay);
InteropEmu.SetFlag(EmulationFlags.UseNes101Hvc101Behavior, emulationInfo.UseNes101Hvc101Behavior);
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.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="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="chkRandomizeCpuPpuAlignment">Randomize power-on CPU/PPU alignment</Control>
<Control ID="tpgOverclocking">Forçament</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="chkUseNes101Hvc101Behavior">Use NES/HVC-101 (Top-loader / AV Famicom) behavior</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>

View file

@ -320,6 +320,7 @@
<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="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="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="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="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>

View file

@ -320,6 +320,7 @@
<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="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>

View file

@ -318,7 +318,8 @@
<Control ID="chkDisablePaletteRead">PPUのパレットラムを読み込み不可能にする</Control>
<Control ID="chkDisablePpuReset">ゲーム機をリセットする時に、PPUをリセットしない ファミコン同様</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>

View file

@ -320,6 +320,7 @@
<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="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>

View file

@ -320,6 +320,7 @@
<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="chkMapperRandomPowerOnState">Randomize power-on state for mappers</Control>
<Control ID="chkRandomizeCpuPpuAlignment">Randomize power-on CPU/PPU alignment</Control>
<Control ID="tpgOverclocking">Разгон</Control>
<Control ID="grpOverclocking">Разгон CPU</Control>

View file

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

View file

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

View file

@ -48,6 +48,7 @@ namespace Mesen.GUI.Forms.Config
this.lblRewindSpeedHint = new System.Windows.Forms.Label();
this.tpgAdvanced = new System.Windows.Forms.TabPage();
this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
this.chkRandomizeCpuPpuAlignment = new Mesen.GUI.Controls.ctrlRiskyOption();
this.lblMiscSettings = new System.Windows.Forms.Label();
this.chkMapperRandomPowerOnState = 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.lblEffectiveClockRateDendy = 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.lblEffectiveClockRatePal = 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.tableLayoutPanel5 = new System.Windows.Forms.TableLayoutPanel();
this.nudExtraScanlinesAfterNmi = new Mesen.GUI.Controls.MesenNumericUpDown();
@ -105,8 +100,6 @@ namespace Mesen.GUI.Forms.Config
this.tableLayoutPanel3.SuspendLayout();
this.flowLayoutPanel4.SuspendLayout();
this.flowLayoutPanel3.SuspendLayout();
this.grpOverclocking.SuspendLayout();
this.tableLayoutPanel2.SuspendLayout();
this.grpPpuTiming.SuspendLayout();
this.tableLayoutPanel5.SuspendLayout();
this.flowLayoutPanel2.SuspendLayout();
@ -115,7 +108,7 @@ namespace Mesen.GUI.Forms.Config
//
// 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);
//
// tabMain
@ -127,7 +120,7 @@ namespace Mesen.GUI.Forms.Config
this.tabMain.Location = new System.Drawing.Point(0, 0);
this.tabMain.Name = "tabMain";
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;
//
// tpgGeneral
@ -136,7 +129,7 @@ namespace Mesen.GUI.Forms.Config
this.tpgGeneral.Location = new System.Drawing.Point(4, 22);
this.tpgGeneral.Name = "tpgGeneral";
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.Text = "General";
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(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;
//
// flowLayoutPanel9
@ -356,7 +349,7 @@ namespace Mesen.GUI.Forms.Config
this.tpgAdvanced.Location = new System.Drawing.Point(4, 22);
this.tpgAdvanced.Name = "tpgAdvanced";
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.Text = "Advanced";
this.tpgAdvanced.UseVisualStyleBackColor = true;
@ -365,25 +358,27 @@ namespace Mesen.GUI.Forms.Config
//
this.tableLayoutPanel1.ColumnCount = 1;
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.chkEnableOamDecay, 0, 1);
this.tableLayoutPanel1.Controls.Add(this.flowLayoutPanel8, 0, 3);
this.tableLayoutPanel1.Controls.Add(this.chkDisablePaletteRead, 0, 10);
this.tableLayoutPanel1.Controls.Add(this.chkDisableOamAddrBug, 0, 8);
this.tableLayoutPanel1.Controls.Add(this.chkDisablePpuReset, 0, 7);
this.tableLayoutPanel1.Controls.Add(this.chkDisablePpu2004Reads, 0, 9);
this.tableLayoutPanel1.Controls.Add(this.chkUseNes101Hvc101Behavior, 0, 6);
this.tableLayoutPanel1.Controls.Add(this.chkAllowInvalidInput, 0, 11);
this.tableLayoutPanel1.Controls.Add(this.chkUseAlternativeMmc3Irq, 0, 5);
this.tableLayoutPanel1.Controls.Add(this.flowLayoutPanel8, 0, 4);
this.tableLayoutPanel1.Controls.Add(this.chkDisablePaletteRead, 0, 11);
this.tableLayoutPanel1.Controls.Add(this.chkDisableOamAddrBug, 0, 9);
this.tableLayoutPanel1.Controls.Add(this.chkDisablePpuReset, 0, 8);
this.tableLayoutPanel1.Controls.Add(this.chkDisablePpu2004Reads, 0, 10);
this.tableLayoutPanel1.Controls.Add(this.chkUseNes101Hvc101Behavior, 0, 7);
this.tableLayoutPanel1.Controls.Add(this.chkAllowInvalidInput, 0, 12);
this.tableLayoutPanel1.Controls.Add(this.chkUseAlternativeMmc3Irq, 0, 6);
this.tableLayoutPanel1.Controls.Add(this.lblDeveloperSettings, 0, 0);
this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill;
this.tableLayoutPanel1.Location = new System.Drawing.Point(3, 3);
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());
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(System.Windows.Forms.SizeType.Absolute, 20F));
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(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;
//
// 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
//
this.lblMiscSettings.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
this.lblMiscSettings.AutoSize = true;
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.Name = "lblMiscSettings";
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.cboRamPowerOnState);
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.Name = "flowLayoutPanel8";
this.flowLayoutPanel8.Size = new System.Drawing.Size(512, 27);
@ -468,7 +476,7 @@ namespace Mesen.GUI.Forms.Config
//
this.chkDisablePaletteRead.Checked = false;
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.MinimumSize = new System.Drawing.Size(0, 21);
this.chkDisablePaletteRead.Name = "chkDisablePaletteRead";
@ -480,7 +488,7 @@ namespace Mesen.GUI.Forms.Config
//
this.chkDisableOamAddrBug.Checked = false;
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.MinimumSize = new System.Drawing.Size(0, 21);
this.chkDisableOamAddrBug.Name = "chkDisableOamAddrBug";
@ -492,7 +500,7 @@ namespace Mesen.GUI.Forms.Config
//
this.chkDisablePpuReset.Checked = false;
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.MinimumSize = new System.Drawing.Size(0, 21);
this.chkDisablePpuReset.Name = "chkDisablePpuReset";
@ -504,7 +512,7 @@ namespace Mesen.GUI.Forms.Config
//
this.chkDisablePpu2004Reads.Checked = false;
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.MinimumSize = new System.Drawing.Size(0, 21);
this.chkDisablePpu2004Reads.Name = "chkDisablePpu2004Reads";
@ -515,7 +523,7 @@ namespace Mesen.GUI.Forms.Config
// chkUseNes101Hvc101Behavior
//
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.Name = "chkUseNes101Hvc101Behavior";
this.chkUseNes101Hvc101Behavior.Size = new System.Drawing.Size(292, 17);
@ -528,7 +536,7 @@ namespace Mesen.GUI.Forms.Config
this.chkAllowInvalidInput.AutoSize = true;
this.chkAllowInvalidInput.Checked = false;
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.MinimumSize = new System.Drawing.Size(0, 23);
this.chkAllowInvalidInput.Name = "chkAllowInvalidInput";
@ -539,7 +547,7 @@ namespace Mesen.GUI.Forms.Config
// chkUseAlternativeMmc3Irq
//
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.Name = "chkUseAlternativeMmc3Irq";
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.Name = "tpgOverclocking";
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.Text = "Overclocking";
this.tpgOverclocking.UseVisualStyleBackColor = true;
@ -576,7 +584,7 @@ namespace Mesen.GUI.Forms.Config
this.picHint.Anchor = System.Windows.Forms.AnchorStyles.Left;
this.picHint.BackgroundImage = global::Mesen.GUI.Properties.Resources.Help;
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.Name = "picHint";
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.Controls.Add(this.lblOverclockHint, 0, 0);
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.grpOverclocking, 0, 2);
this.tableLayoutPanel3.Controls.Add(this.grpPpuTiming, 0, 1);
this.tableLayoutPanel3.Controls.Add(this.flowLayoutPanel2, 0, 3);
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(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;
//
// lblOverclockHint
@ -619,7 +625,7 @@ namespace Mesen.GUI.Forms.Config
this.lblOverclockHint.Location = new System.Drawing.Point(3, 0);
this.lblOverclockHint.Name = "lblOverclockHint";
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.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.lblEffectiveClockRateValueDendy);
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.Name = "flowLayoutPanel4";
this.flowLayoutPanel4.Size = new System.Drawing.Size(519, 20);
@ -653,22 +659,12 @@ namespace Mesen.GUI.Forms.Config
this.lblEffectiveClockRateValueDendy.TabIndex = 1;
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
//
this.flowLayoutPanel3.Controls.Add(this.lblEffectiveClockRatePal);
this.flowLayoutPanel3.Controls.Add(this.lblEffectiveClockRateValuePal);
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.Name = "flowLayoutPanel3";
this.flowLayoutPanel3.Size = new System.Drawing.Size(519, 17);
@ -693,89 +689,6 @@ namespace Mesen.GUI.Forms.Config
this.lblEffectiveClockRateValuePal.TabIndex = 1;
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
//
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.lblEffectiveClockRateValue);
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.Name = "flowLayoutPanel2";
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.btnResetLagCounter);
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.Name = "flowLayoutPanel7";
this.flowLayoutPanel7.Size = new System.Drawing.Size(519, 43);
this.flowLayoutPanel7.Size = new System.Drawing.Size(519, 134);
this.flowLayoutPanel7.TabIndex = 12;
//
// chkShowLagCounter
@ -961,12 +874,12 @@ namespace Mesen.GUI.Forms.Config
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
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.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
this.MaximizeBox = false;
this.MinimizeBox = false;
this.MinimumSize = new System.Drawing.Size(503, 322);
this.MinimumSize = new System.Drawing.Size(503, 367);
this.Name = "frmEmulationConfig";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
this.Text = "Emulation Settings";
@ -992,14 +905,10 @@ namespace Mesen.GUI.Forms.Config
this.tpgOverclocking.PerformLayout();
((System.ComponentModel.ISupportInitialize)(this.picHint)).EndInit();
this.tableLayoutPanel3.ResumeLayout(false);
this.tableLayoutPanel3.PerformLayout();
this.flowLayoutPanel4.ResumeLayout(false);
this.flowLayoutPanel4.PerformLayout();
this.flowLayoutPanel3.ResumeLayout(false);
this.flowLayoutPanel3.PerformLayout();
this.grpOverclocking.ResumeLayout(false);
this.tableLayoutPanel2.ResumeLayout(false);
this.tableLayoutPanel2.PerformLayout();
this.grpPpuTiming.ResumeLayout(false);
this.tableLayoutPanel5.ResumeLayout(false);
this.tableLayoutPanel5.PerformLayout();
@ -1020,11 +929,6 @@ namespace Mesen.GUI.Forms.Config
private ctrlRiskyOption chkAllowInvalidInput;
private System.Windows.Forms.TabPage tpgOverclocking;
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.FlowLayoutPanel flowLayoutPanel6;
private MesenNumericUpDown nudEmulationSpeed;
@ -1042,7 +946,6 @@ namespace Mesen.GUI.Forms.Config
private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel3;
private System.Windows.Forms.Label lblEffectiveClockRatePal;
private System.Windows.Forms.Timer tmrUpdateClockRate;
private System.Windows.Forms.CheckBox chkOverclockAdjustApu;
private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel4;
private System.Windows.Forms.Label lblEffectiveClockRateDendy;
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.Label lblDeveloperSettings;
private System.Windows.Forms.Label lblMiscSettings;
private ctrlRiskyOption chkRandomizeCpuPpuAlignment;
}
}

View file

@ -37,9 +37,7 @@ namespace Mesen.GUI.Forms.Config
AddBinding("EnableOamDecay", chkEnableOamDecay);
AddBinding("UseNes101Hvc101Behavior", chkUseNes101Hvc101Behavior);
AddBinding("EnableMapperRandomPowerOnState", chkMapperRandomPowerOnState);
AddBinding("OverclockRate", nudOverclockRate);
AddBinding("OverclockAdjustApu", chkOverclockAdjustApu);
AddBinding("RandomizeCpuPpuAlignment", chkRandomizeCpuPpuAlignment);
AddBinding("PpuExtraScanlinesBeforeNmi", nudExtraScanlinesBeforeNmi);
AddBinding("PpuExtraScanlinesAfterNmi", nudExtraScanlinesAfterNmi);
@ -57,9 +55,9 @@ namespace Mesen.GUI.Forms.Config
private void tmrUpdateClockRate_Tick(object sender, EventArgs e)
{
decimal clockRateMultiplierNtsc = (nudOverclockRate.Value * (1 + (nudExtraScanlinesAfterNmi.Value + nudExtraScanlinesBeforeNmi.Value) / 262));
decimal clockRateMultiplierPal = (nudOverclockRate.Value * (1 + (nudExtraScanlinesAfterNmi.Value + nudExtraScanlinesBeforeNmi.Value) / 312));
decimal clockRateMultiplierDendy = (nudOverclockRate.Value * (1 + (nudExtraScanlinesAfterNmi.Value + nudExtraScanlinesBeforeNmi.Value) / 312));
decimal clockRateMultiplierNtsc = (100 * (1 + (nudExtraScanlinesAfterNmi.Value + nudExtraScanlinesBeforeNmi.Value) / 262));
decimal clockRateMultiplierPal = (100 * (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() + "%)";
lblEffectiveClockRateValuePal.Text = (1662607 * clockRateMultiplierPal / 100000000).ToString("#.####") + " mhz (" + ((int)clockRateMultiplierPal).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 SetRewindBufferSize(UInt32 seconds);
[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 SetOverscanDimensions(UInt32 left, UInt32 right, UInt32 top, UInt32 bottom);
[DllImport(DLLPath)] public static extern void SetVideoScale(double scale, ConsoleId consoleId = ConsoleId.Master);
@ -1704,6 +1703,8 @@ namespace Mesen.GUI
VsDualMuteMaster = 0x200000000000000,
VsDualMuteSlave = 0x400000000000000,
RandomizeCpuPpuAlignment = 0x800000000000000,
ForceMaxSpeed = 0x4000000000000000,
ConsoleMode = 0x8000000000000000,
}

View file

@ -640,7 +640,6 @@ namespace InteropEmu {
shared_ptr<RewindManager> rewindManager = _console->GetRewindManager();
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 SetVideoScale(double scale, ConsoleId consoleId) { GetConsoleById(consoleId)->GetSettings()->SetVideoScale(scale); }
DllExport void __stdcall SetScreenRotation(uint32_t angle) { _settings->SetScreenRotation(angle); }

View file

@ -13,6 +13,7 @@
#include "../Core/EmulationSettings.h"
#include "../Core/CheatManager.h"
#include "../Core/HdData.h"
#include "../Core/SaveStateManager.h"
#include "../Core/DebuggerTypes.h"
#include "../Core/GameDatabase.h"
#include "../Utilities/FolderUtilities.h"
@ -658,7 +659,7 @@ extern "C" {
RETRO_API bool retro_serialize(void *data, size_t size)
{
std::stringstream ss;
_console->SaveState(ss);
_console->GetSaveStateManager()->SaveState(ss);
string saveStateData = ss.str();
memset(data, 0, size);
@ -669,8 +670,9 @@ extern "C" {
RETRO_API bool retro_unserialize(const void *data, size_t size)
{
_console->LoadState((uint8_t*)data, (uint32_t)size);
return true;
std::stringstream ss;
ss.write((char*)data, size);
return _console->GetSaveStateManager()->LoadState(ss, false);
}
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
//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;
_console->SaveState(ss);
_console->GetSaveStateManager()->SaveState(ss);
//Round up to the next 1kb multiple
_saveStateSize = ((ss.str().size() * 2) + 0x400) & ~0x3FF;