Quick save/load support
This commit is contained in:
parent
37f1d94cff
commit
9ec756fae2
21 changed files with 406 additions and 13 deletions
57
Core/APU.cpp
57
Core/APU.cpp
|
@ -3,6 +3,7 @@
|
|||
#include "stdafx.h"
|
||||
#include "APU.h"
|
||||
#include "CPU.h"
|
||||
#include "Nes_Apu\apu_snapshot.h"
|
||||
|
||||
APU* APU::Instance = nullptr;
|
||||
IAudioDevice* APU::AudioDevice = nullptr;
|
||||
|
@ -79,3 +80,59 @@ bool APU::Exec(uint32_t executedCycles)
|
|||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void APU::StreamState(bool saving)
|
||||
{
|
||||
apu_snapshot_t snapshot;
|
||||
if(saving) {
|
||||
_apu.save_snapshot(&snapshot);
|
||||
}
|
||||
|
||||
StreamArray<uint8_t>(snapshot.w40xx, 0x14);
|
||||
Stream<uint8_t>(snapshot.w4015);
|
||||
Stream<uint8_t>(snapshot.w4017);
|
||||
Stream<uint16_t>(snapshot.delay);
|
||||
Stream<uint8_t>(snapshot.step);
|
||||
Stream<uint8_t>(snapshot.irq_flag);
|
||||
|
||||
Stream<uint16_t>(snapshot.square1.delay);
|
||||
StreamArray<uint8_t>(snapshot.square1.env, 3);
|
||||
Stream<uint8_t>(snapshot.square1.length);
|
||||
Stream<uint8_t>(snapshot.square1.phase);
|
||||
Stream<uint8_t>(snapshot.square1.swp_delay);
|
||||
Stream<uint8_t>(snapshot.square1.swp_reset);
|
||||
StreamArray<uint8_t>(snapshot.square1.unused, 1);
|
||||
|
||||
Stream<uint16_t>(snapshot.square2.delay);
|
||||
StreamArray<uint8_t>(snapshot.square2.env, 3);
|
||||
Stream<uint8_t>(snapshot.square2.length);
|
||||
Stream<uint8_t>(snapshot.square2.phase);
|
||||
Stream<uint8_t>(snapshot.square2.swp_delay);
|
||||
Stream<uint8_t>(snapshot.square2.swp_reset);
|
||||
StreamArray<uint8_t>(snapshot.square2.unused, 1);
|
||||
|
||||
Stream<uint16_t>(snapshot.triangle.delay);
|
||||
Stream<uint8_t>(snapshot.triangle.length);
|
||||
Stream<uint8_t>(snapshot.triangle.phase);
|
||||
Stream<uint8_t>(snapshot.triangle.linear_counter);
|
||||
Stream<uint8_t>(snapshot.triangle.linear_mode);
|
||||
|
||||
Stream<uint16_t>(snapshot.noise.delay);
|
||||
StreamArray<uint8_t>(snapshot.noise.env, 3);
|
||||
Stream<uint8_t>(snapshot.noise.length);
|
||||
Stream<uint16_t>(snapshot.noise.shift_reg);
|
||||
|
||||
Stream<uint16_t>(snapshot.dmc.delay);
|
||||
Stream<uint16_t>(snapshot.dmc.remain);
|
||||
Stream<uint16_t>(snapshot.dmc.addr);
|
||||
Stream<uint8_t>(snapshot.dmc.buf);
|
||||
Stream<uint8_t>(snapshot.dmc.bits_remain);
|
||||
Stream<uint8_t>(snapshot.dmc.bits);
|
||||
Stream<uint8_t>(snapshot.dmc.buf_empty);
|
||||
Stream<uint8_t>(snapshot.dmc.silence);
|
||||
Stream<uint8_t>(snapshot.dmc.irq_flag);
|
||||
|
||||
if(!saving) {
|
||||
_apu.load_snapshot(snapshot);
|
||||
}
|
||||
}
|
|
@ -4,9 +4,10 @@
|
|||
#include "MemoryManager.h"
|
||||
#include "IMemoryHandler.h"
|
||||
#include "IAudioDevice.h"
|
||||
#include "Snapshotable.h"
|
||||
#include "Nes_Apu/Nes_Apu.h"
|
||||
|
||||
class APU : public IMemoryHandler
|
||||
class APU : public IMemoryHandler, public Snapshotable
|
||||
{
|
||||
private:
|
||||
static IAudioDevice* AudioDevice;
|
||||
|
@ -23,6 +24,9 @@ class APU : public IMemoryHandler
|
|||
static int DMCRead(void*, cpu_addr_t addr);
|
||||
static void IRQChanged(void* data);
|
||||
|
||||
protected:
|
||||
void StreamState(bool saving);
|
||||
|
||||
public:
|
||||
static const uint32_t SampleRate = 44100;
|
||||
static const uint32_t SamplesPerFrame = 44100 / 60;
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
#pragma once
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "Snapshotable.h"
|
||||
#include "IMemoryHandler.h"
|
||||
#include "ROMLoader.h"
|
||||
|
||||
class BaseMapper : public IMemoryHandler
|
||||
class BaseMapper : public IMemoryHandler, public Snapshotable
|
||||
{
|
||||
protected:
|
||||
uint8_t* _prgRAM;
|
||||
|
@ -12,6 +13,7 @@ class BaseMapper : public IMemoryHandler
|
|||
uint32_t _prgSize;
|
||||
uint32_t _chrSize;
|
||||
|
||||
bool _hasCHRRAM;
|
||||
bool _hasBattery;
|
||||
wstring _romFilename;
|
||||
|
||||
|
@ -20,6 +22,9 @@ class BaseMapper : public IMemoryHandler
|
|||
vector<uint8_t*> _prgPages;
|
||||
vector<uint8_t*> _chrPages;
|
||||
|
||||
uint32_t* _prgSlotPages;
|
||||
uint32_t* _chrSlotPages;
|
||||
|
||||
uint32_t _chrShift = -1;
|
||||
uint32_t _prgShift = -1;
|
||||
|
||||
|
@ -38,12 +43,14 @@ class BaseMapper : public IMemoryHandler
|
|||
{
|
||||
//std::cout << std::dec << "PRG Slot " << (short)slot << ": " << (short)page << std::endl;
|
||||
_prgPages[slot] = &_prgRAM[(page & (GetPRGPageCount() - 1)) * GetPRGPageSize()];
|
||||
_prgSlotPages[slot] = page;
|
||||
}
|
||||
|
||||
void SelectCHRPage(uint32_t slot, uint32_t page)
|
||||
{
|
||||
//std::cout << std::dec << "CHR Slot " << (short)slot << ": " << (short)page << std::endl;
|
||||
_chrPages[slot] = &_chrRAM[(page & (GetCHRPageCount() - 1)) * GetCHRPageSize()];
|
||||
_chrSlotPages[slot] = page;
|
||||
}
|
||||
|
||||
uint32_t GetPRGSlotCount()
|
||||
|
@ -107,6 +114,28 @@ class BaseMapper : public IMemoryHandler
|
|||
return filename;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void StreamState(bool saving)
|
||||
{
|
||||
StreamArray<uint32_t>(_prgSlotPages, GetPRGSlotCount());
|
||||
StreamArray<uint32_t>(_chrSlotPages, GetCHRSlotCount());
|
||||
|
||||
Stream<bool>(_hasCHRRAM);
|
||||
if(_hasCHRRAM) {
|
||||
StreamArray<uint8_t>(_chrRAM, BaseMapper::CHRSize);
|
||||
}
|
||||
|
||||
if(!saving) {
|
||||
for(int i = GetPRGSlotCount() - 1; i >= 0; i--) {
|
||||
SelectPRGPage(i, _prgSlotPages[i]);
|
||||
}
|
||||
|
||||
for(int i = GetCHRSlotCount() - 1; i >= 0; i--) {
|
||||
SelectCHRPage(i, _chrSlotPages[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
void Initialize(ROMLoader &romLoader)
|
||||
{
|
||||
|
@ -119,6 +148,7 @@ class BaseMapper : public IMemoryHandler
|
|||
_romFilename = romLoader.GetFilename();
|
||||
|
||||
if(_chrSize == 0) {
|
||||
_hasCHRRAM = true;
|
||||
_chrRAM = new uint8_t[BaseMapper::CHRSize];
|
||||
_chrSize = BaseMapper::CHRSize;
|
||||
}
|
||||
|
@ -131,6 +161,9 @@ class BaseMapper : public IMemoryHandler
|
|||
_chrPages.push_back(nullptr);
|
||||
}
|
||||
|
||||
_prgSlotPages = new uint32_t[GetPRGSlotCount()];
|
||||
_chrSlotPages = new uint32_t[GetCHRSlotCount()];
|
||||
|
||||
InitMapper();
|
||||
}
|
||||
|
||||
|
|
16
Core/CPU.cpp
16
Core/CPU.cpp
|
@ -135,3 +135,19 @@ uint32_t CPU::Exec()
|
|||
CPU::CycleCount += executedCycles;
|
||||
return executedCycles + GetCyclePenalty();
|
||||
}
|
||||
|
||||
void CPU::StreamState(bool saving)
|
||||
{
|
||||
Stream<uint16_t>(_state.PC);
|
||||
Stream<uint8_t>(_state.SP);
|
||||
Stream<uint8_t>(_state.A);
|
||||
Stream<uint8_t>(_state.X);
|
||||
Stream<uint8_t>(_state.Y);
|
||||
|
||||
Stream<uint64_t>(CPU::CycleCount);
|
||||
Stream<bool>(CPU::NMIFlag);
|
||||
Stream<uint32_t>(CPU::IRQFlag);
|
||||
|
||||
Stream<bool>(_runNMI);
|
||||
Stream<bool>(_runIRQ);
|
||||
}
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#include "stdafx.h"
|
||||
#include "MemoryManager.h"
|
||||
#include "Snapshotable.h"
|
||||
|
||||
namespace PSFlags
|
||||
{
|
||||
|
@ -35,7 +36,7 @@ struct State
|
|||
uint8_t PS;
|
||||
};
|
||||
|
||||
class CPU
|
||||
class CPU : public Snapshotable
|
||||
{
|
||||
private:
|
||||
const uint16_t NMIVector = 0xFFFA;
|
||||
|
@ -638,6 +639,9 @@ private:
|
|||
}
|
||||
#pragma endregion
|
||||
|
||||
protected:
|
||||
void StreamState(bool saving);
|
||||
|
||||
public:
|
||||
static const uint32_t ClockRate = 1789773;
|
||||
|
||||
|
|
|
@ -94,10 +94,15 @@ void Console::Run()
|
|||
uint32_t frameCount = _ppu->GetFrameCount();
|
||||
Console::CurrentFPS = (int)((frameCount - lastFrameCount) / (fpsTimer.GetElapsedMS() / 1000));
|
||||
lastFrameCount = frameCount;
|
||||
//std::cout << Console::CurrentFPS << std::endl;
|
||||
fpsTimer.Reset();
|
||||
}
|
||||
|
||||
if(!_saveStateFilename.empty()) {
|
||||
SaveState();
|
||||
} else if(!_loadStateFilename.empty()) {
|
||||
LoadState();
|
||||
}
|
||||
|
||||
if(_stop) {
|
||||
_stop = false;
|
||||
break;
|
||||
|
@ -114,6 +119,48 @@ void Console::Run()
|
|||
}
|
||||
}
|
||||
|
||||
void Console::SaveState(wstring filename)
|
||||
{
|
||||
_saveStateFilename = filename;
|
||||
}
|
||||
|
||||
void Console::SaveState()
|
||||
{
|
||||
ofstream file(_saveStateFilename, ios::out | ios::binary);
|
||||
|
||||
if(file) {
|
||||
_cpu->SaveSnapshot(&file);
|
||||
_ppu->SaveSnapshot(&file);
|
||||
_memoryManager->SaveSnapshot(&file);
|
||||
_mapper->SaveSnapshot(&file);
|
||||
_apu->SaveSnapshot(&file);
|
||||
file.close();
|
||||
}
|
||||
|
||||
_saveStateFilename.clear();
|
||||
}
|
||||
|
||||
void Console::LoadState(wstring filename)
|
||||
{
|
||||
_loadStateFilename = filename;
|
||||
}
|
||||
|
||||
void Console::LoadState()
|
||||
{
|
||||
ifstream file(_loadStateFilename, ios::out | ios::binary);
|
||||
|
||||
if(file) {
|
||||
_cpu->LoadSnapshot(&file);
|
||||
_ppu->LoadSnapshot(&file);
|
||||
_memoryManager->LoadSnapshot(&file);
|
||||
_mapper->LoadSnapshot(&file);
|
||||
_apu->LoadSnapshot(&file);
|
||||
file.close();
|
||||
}
|
||||
|
||||
_loadStateFilename.clear();
|
||||
}
|
||||
|
||||
bool Console::RunTest(uint8_t *expectedResult)
|
||||
{
|
||||
Timer timer;
|
||||
|
|
|
@ -31,6 +31,12 @@ class Console
|
|||
bool _stop = false;
|
||||
bool _reset = false;
|
||||
|
||||
wstring _loadStateFilename;
|
||||
wstring _saveStateFilename;
|
||||
|
||||
void SaveState();
|
||||
void LoadState();
|
||||
|
||||
void ResetComponents(bool softReset);
|
||||
|
||||
public:
|
||||
|
@ -43,6 +49,9 @@ class Console
|
|||
bool RunTest(uint8_t* expectedResult);
|
||||
void SaveTestResult();
|
||||
|
||||
void SaveState(wstring filename);
|
||||
void LoadState(wstring filename);
|
||||
|
||||
static bool CheckFlag(int flag);
|
||||
static void SetFlags(int flags);
|
||||
static void ClearFlags(int flags);
|
||||
|
|
|
@ -95,6 +95,7 @@
|
|||
<ClInclude Include="IControlDevice.h" />
|
||||
<ClInclude Include="IMemoryHandler.h" />
|
||||
<ClInclude Include="Console.h" />
|
||||
<ClInclude Include="Snapshotable.h" />
|
||||
<ClInclude Include="IVideoDevice.h" />
|
||||
<ClInclude Include="MapperFactory.h" />
|
||||
<ClInclude Include="MMC1.h" />
|
||||
|
|
|
@ -117,6 +117,9 @@
|
|||
<ClInclude Include="MMC3.h">
|
||||
<Filter>Header Files\Mappers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Snapshotable.h">
|
||||
<Filter>Header Files\Interfaces</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="CPU.cpp">
|
||||
|
|
22
Core/MMC1.h
22
Core/MMC1.h
|
@ -129,6 +129,28 @@ class MMC1 : public BaseMapper
|
|||
}
|
||||
|
||||
protected:
|
||||
void StreamState(bool saving)
|
||||
{
|
||||
Stream<uint8_t>(_state.Reg8000);
|
||||
Stream<uint8_t>(_state.RegA000);
|
||||
Stream<uint8_t>(_state.RegC000);
|
||||
Stream<uint8_t>(_state.RegE000);
|
||||
|
||||
Stream<uint8_t>(_writeBuffer);
|
||||
Stream<uint8_t>(_shiftCount);
|
||||
|
||||
Stream<bool>(_wramDisable);
|
||||
Stream<ChrMode>(_chrMode);
|
||||
Stream<PrgMode>(_prgMode);
|
||||
Stream<SlotSelect>(_slotSelect);
|
||||
|
||||
Stream<uint8_t>(_chrReg0);
|
||||
Stream<uint8_t>(_chrReg1);
|
||||
Stream<uint8_t>(_prgReg);
|
||||
|
||||
BaseMapper::StreamState(saving);
|
||||
}
|
||||
|
||||
virtual uint32_t GetPRGPageSize() { return 0x4000; }
|
||||
virtual uint32_t GetCHRPageSize() { return 0x1000; }
|
||||
|
||||
|
|
34
Core/MMC3.h
34
Core/MMC3.h
|
@ -26,7 +26,8 @@ class MMC3 : public BaseMapper
|
|||
bool _irqReload;
|
||||
|
||||
bool _irqEnabled;
|
||||
int32_t _lastPPUCycle;
|
||||
uint32_t _lastCycle;
|
||||
uint32_t _cyclesDown;
|
||||
|
||||
bool _wramEnabled;
|
||||
bool _wramWriteProtected;
|
||||
|
@ -51,7 +52,8 @@ class MMC3 : public BaseMapper
|
|||
_irqReloadValue = 0;
|
||||
_irqReload = false;
|
||||
_irqEnabled = false;
|
||||
_lastPPUCycle = 0xFFFF;
|
||||
_lastCycle = 0xFFFF;
|
||||
_cyclesDown = 0;
|
||||
|
||||
_wramEnabled = false;
|
||||
_wramWriteProtected = false;
|
||||
|
@ -107,6 +109,32 @@ class MMC3 : public BaseMapper
|
|||
}
|
||||
|
||||
protected:
|
||||
void StreamState(bool saving)
|
||||
{
|
||||
Stream<uint8_t>(_state.Reg8000);
|
||||
Stream<uint8_t>(_state.RegA000);
|
||||
Stream<uint8_t>(_state.RegA001);
|
||||
|
||||
Stream<uint8_t>(_currentRegister);
|
||||
StreamArray<uint8_t>(_registers, 8);
|
||||
Stream<uint8_t>(_chrMode);
|
||||
Stream<uint8_t>(_prgMode);
|
||||
|
||||
Stream<uint8_t>(_irqReloadValue);
|
||||
Stream<uint8_t>(_irqCounter);
|
||||
Stream<bool>(_irqReload);
|
||||
|
||||
Stream<bool>(_irqEnabled);
|
||||
Stream<uint32_t>(_lastCycle);
|
||||
Stream<uint32_t>(_cyclesDown);
|
||||
|
||||
Stream<bool>(_wramEnabled);
|
||||
Stream<bool>(_wramWriteProtected);
|
||||
|
||||
BaseMapper::StreamState(saving);
|
||||
}
|
||||
|
||||
|
||||
virtual uint32_t GetPRGPageSize() { return 0x2000; }
|
||||
virtual uint32_t GetCHRPageSize() { return 0x0400; }
|
||||
|
||||
|
@ -168,8 +196,6 @@ class MMC3 : public BaseMapper
|
|||
}
|
||||
}
|
||||
|
||||
uint32_t _lastCycle = 0xFFFF;
|
||||
uint32_t _cyclesDown = 0;
|
||||
virtual void NotifyVRAMAddressChange(uint16_t addr)
|
||||
{
|
||||
uint16_t cycle = PPU::GetCurrentCycle();
|
||||
|
|
|
@ -8,7 +8,7 @@ MemoryManager::MemoryManager(shared_ptr<BaseMapper> mapper)
|
|||
_internalRAM = new uint8_t[InternalRAMSize];
|
||||
_SRAM = new uint8_t[SRAMSize];
|
||||
_videoRAM = new uint8_t[VRAMSize];
|
||||
_expansionRAM = new uint8_t[0x2000];
|
||||
_expansionRAM = new uint8_t[ExpansionRAMSize];
|
||||
|
||||
_ramReadHandlers = new IMemoryHandler*[RAMSize];
|
||||
_ramWriteHandlers = new IMemoryHandler*[RAMSize];
|
||||
|
@ -18,7 +18,7 @@ MemoryManager::MemoryManager(shared_ptr<BaseMapper> mapper)
|
|||
memset(_internalRAM, 0, InternalRAMSize);
|
||||
memset(_SRAM, 0, SRAMSize);
|
||||
memset(_videoRAM, 0, VRAMSize);
|
||||
memset(_expansionRAM, 0, 0x2000);
|
||||
memset(_expansionRAM, 0, ExpansionRAMSize);
|
||||
|
||||
memset(_ramReadHandlers, 0, RAMSize * sizeof(IMemoryHandler*));
|
||||
memset(_ramWriteHandlers, 0, RAMSize * sizeof(IMemoryHandler*));
|
||||
|
@ -213,4 +213,12 @@ void MemoryManager::WriteVRAM(uint16_t addr, uint8_t value)
|
|||
throw exception("Not implemented yet");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MemoryManager::StreamState(bool saving)
|
||||
{
|
||||
StreamArray<uint8_t>(_internalRAM, MemoryManager::InternalRAMSize);
|
||||
StreamArray<uint8_t>(_expansionRAM, MemoryManager::ExpansionRAMSize);
|
||||
StreamArray<uint8_t>(_SRAM, MemoryManager::SRAMSize);
|
||||
StreamArray<uint8_t>(_videoRAM, MemoryManager::VRAMSize);
|
||||
}
|
|
@ -4,12 +4,14 @@
|
|||
#include "IMemoryHandler.h"
|
||||
#include "ROMLoader.h"
|
||||
#include "BaseMapper.h"
|
||||
#include "Snapshotable.h"
|
||||
|
||||
class MemoryManager
|
||||
class MemoryManager: public Snapshotable
|
||||
{
|
||||
private:
|
||||
const int RAMSize = 0x10000;
|
||||
const int InternalRAMSize = 0x800;
|
||||
const int ExpansionRAMSize = 0x2000;
|
||||
const int SRAMSize = 0x2000;
|
||||
const int VRAMSize = 0x4000;
|
||||
|
||||
|
@ -31,6 +33,9 @@ class MemoryManager
|
|||
uint8_t ReadMappedVRAM(uint16_t addr);
|
||||
void WriteMappedVRAM(uint16_t addr, uint8_t value);
|
||||
|
||||
protected:
|
||||
void StreamState(bool saving);
|
||||
|
||||
public:
|
||||
MemoryManager(shared_ptr<BaseMapper> mapper);
|
||||
~MemoryManager();
|
||||
|
|
53
Core/PPU.cpp
53
Core/PPU.cpp
|
@ -644,4 +644,57 @@ void PPU::Exec()
|
|||
|
||||
gap--;
|
||||
}
|
||||
}
|
||||
|
||||
void PPU::StreamState(bool saving)
|
||||
{
|
||||
Stream<uint8_t>(_state.Control);
|
||||
Stream<uint8_t>(_state.Mask);
|
||||
Stream<uint8_t>(_state.Status);
|
||||
Stream<uint32_t>(_state.SpriteRamAddr);
|
||||
Stream<uint16_t>(_state.VideoRamAddr);
|
||||
Stream<uint8_t>(_state.XScroll);
|
||||
Stream<uint16_t>(_state.TmpVideoRamAddr);
|
||||
Stream<bool>(_state.WriteToggle);
|
||||
Stream<uint16_t>(_state.HighBitShift);
|
||||
Stream<uint16_t>(_state.LowBitShift);
|
||||
|
||||
Stream<int32_t>(_scanline);
|
||||
Stream<uint32_t>(_cycle);
|
||||
Stream<uint32_t>(_frameCount);
|
||||
Stream<uint64_t>(_cycleCount);
|
||||
Stream<uint8_t>(_memoryReadBuffer);
|
||||
|
||||
StreamArray<uint8_t>(_paletteRAM, 0x100);
|
||||
StreamArray<uint8_t>(_spriteRAM, 0x100);
|
||||
StreamArray<uint8_t>(_secondarySpriteRAM, 0x20);
|
||||
|
||||
Stream<uint8_t>(_currentTile.LowByte);
|
||||
Stream<uint8_t>(_currentTile.HighByte);
|
||||
Stream<uint32_t>(_currentTile.PaletteOffset);
|
||||
|
||||
Stream<uint8_t>(_nextTile.LowByte);
|
||||
Stream<uint8_t>(_nextTile.HighByte);
|
||||
Stream<uint32_t>(_nextTile.PaletteOffset);
|
||||
|
||||
Stream<uint8_t>(_previousTile.LowByte);
|
||||
Stream<uint8_t>(_previousTile.HighByte);
|
||||
Stream<uint32_t>(_previousTile.PaletteOffset);
|
||||
|
||||
StreamArray<int32_t>(_spriteX, 0x8);
|
||||
for(int i = 0; i < 8; i++) {
|
||||
Stream<uint8_t>(_spriteTiles[i].LowByte);
|
||||
Stream<uint8_t>(_spriteTiles[i].HighByte);
|
||||
Stream<uint32_t>(_spriteTiles[i].PaletteOffset);
|
||||
Stream<bool>(_spriteTiles[i].HorizontalMirror);
|
||||
Stream<bool>(_spriteTiles[i].BackgroundPriority);
|
||||
}
|
||||
Stream<uint32_t>(_spriteCount);
|
||||
Stream<uint32_t>(_secondaryOAMAddr);
|
||||
Stream<bool>(_sprite0Visible);
|
||||
|
||||
if(!saving) {
|
||||
SetControlRegister(_state.Control);
|
||||
SetMaskRegister(_state.Mask);
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "Snapshotable.h"
|
||||
#include "MemoryManager.h"
|
||||
#include "IVideoDevice.h"
|
||||
|
||||
|
@ -73,7 +74,7 @@ struct SpriteInfo
|
|||
bool BackgroundPriority;
|
||||
};
|
||||
|
||||
class PPU : public IMemoryHandler
|
||||
class PPU : public IMemoryHandler, public Snapshotable
|
||||
{
|
||||
private:
|
||||
static PPU* Instance;
|
||||
|
@ -157,6 +158,9 @@ class PPU : public IMemoryHandler
|
|||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
void StreamState(bool saving);
|
||||
|
||||
public:
|
||||
PPU(MemoryManager *memoryManager);
|
||||
~PPU();
|
||||
|
|
71
Core/Snapshotable.h
Normal file
71
Core/Snapshotable.h
Normal file
|
@ -0,0 +1,71 @@
|
|||
#pragma once
|
||||
|
||||
#include "stdafx.h"
|
||||
|
||||
class Snapshotable
|
||||
{
|
||||
uint8_t* _stream;
|
||||
uint32_t _position;
|
||||
bool _saving;
|
||||
|
||||
protected:
|
||||
virtual void StreamState(bool saving) = 0;
|
||||
|
||||
template<typename T>
|
||||
void Stream(T &value)
|
||||
{
|
||||
if(_saving) {
|
||||
uint8_t* bytes = (uint8_t*)&value;
|
||||
int typeSize = sizeof(T);
|
||||
for(int i = 0; i < typeSize; i++) {
|
||||
_stream[_position++] = bytes[i];
|
||||
}
|
||||
} else {
|
||||
value = *((T*)(_stream + _position));
|
||||
_position += sizeof(T);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void StreamArray(T* value, uint32_t length)
|
||||
{
|
||||
uint32_t typeSize = sizeof(*value);
|
||||
if(_saving) {
|
||||
uint8_t* bytes = (uint8_t*)value;
|
||||
for(uint32_t i = 0, len = length*typeSize; i < len; i++) {
|
||||
_stream[_position++] = bytes[i];
|
||||
}
|
||||
} else {
|
||||
for(uint32_t i = 0; i < length*typeSize; i++) {
|
||||
((uint8_t*)value)[i] = _stream[_position];
|
||||
_position++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
void SaveSnapshot(ofstream* file)
|
||||
{
|
||||
_stream = new uint8_t[0xFFFF];
|
||||
memset((char*)_stream, 0, 0xFFFF);
|
||||
_position = 0;
|
||||
_saving = true;
|
||||
|
||||
StreamState(_saving);
|
||||
file->write((char*)_stream, 0xFFFF);
|
||||
|
||||
delete[] _stream;
|
||||
}
|
||||
|
||||
void LoadSnapshot(ifstream* file)
|
||||
{
|
||||
_stream = new uint8_t[0xFFFF];
|
||||
_position = 0;
|
||||
_saving = false;
|
||||
|
||||
file->read((char*)_stream, 0xFFFF);
|
||||
StreamState(_saving);
|
||||
|
||||
delete[] _stream;
|
||||
}
|
||||
};
|
BIN
GUI/GUI.rc
BIN
GUI/GUI.rc
Binary file not shown.
|
@ -195,6 +195,10 @@ namespace NES {
|
|||
SetMenuEnabled(ID_NES_STOP, true);
|
||||
SetMenuEnabled(ID_NES_RESUME, false);
|
||||
|
||||
SetMenuEnabled(ID_FILE_QUICKLOAD, true);
|
||||
|
||||
SetMenuEnabled(ID_FILE_QUICKSAVE, true);
|
||||
|
||||
_renderer->ClearFlags(UIFlags::ShowPauseScreen);
|
||||
if(IsMenuChecked(ID_OPTIONS_SHOWFPS)) {
|
||||
_renderer->SetFlags(UIFlags::ShowFPS);
|
||||
|
@ -223,6 +227,8 @@ namespace NES {
|
|||
SetMenuEnabled(ID_NES_PAUSE, false);
|
||||
SetMenuEnabled(ID_NES_RESET, !powerOff);
|
||||
SetMenuEnabled(ID_NES_STOP, !powerOff);
|
||||
SetMenuEnabled(ID_FILE_QUICKLOAD, !powerOff);
|
||||
SetMenuEnabled(ID_FILE_QUICKSAVE, !powerOff);
|
||||
SetMenuEnabled(ID_NES_RESUME, true);
|
||||
}
|
||||
|
||||
|
@ -360,6 +366,14 @@ namespace NES {
|
|||
mainWindow->Start(filename);
|
||||
}
|
||||
break;
|
||||
case ID_FILE_QUICKLOAD:
|
||||
mainWindow->_console->LoadState(mainWindow->_currentROM + L".svs");
|
||||
mainWindow->_renderer->DisplayMessage(L"State loaded.", 3000);
|
||||
break;
|
||||
case ID_FILE_QUICKSAVE:
|
||||
mainWindow->_console->SaveState(mainWindow->_currentROM + L".svs");
|
||||
mainWindow->_renderer->DisplayMessage(L"State saved.", 3000);
|
||||
break;
|
||||
case ID_FILE_EXIT:
|
||||
DestroyWindow(hWnd);
|
||||
break;
|
||||
|
|
|
@ -234,6 +234,12 @@ namespace NES
|
|||
return shaderResourceView;
|
||||
}
|
||||
|
||||
void Renderer::DisplayMessage(wstring text, uint32_t duration)
|
||||
{
|
||||
_displayMessage = text;
|
||||
_displayTimestamp = timeGetTime() + duration;
|
||||
}
|
||||
|
||||
void Renderer::DrawNESScreen()
|
||||
{
|
||||
RECT sourceRect;
|
||||
|
@ -309,7 +315,12 @@ namespace NES
|
|||
//Draw FPS counter
|
||||
if(CheckFlag(UIFlags::ShowFPS)) {
|
||||
_font->DrawString(_spriteBatch.get(), (wstring(L"FPS: ") + std::to_wstring(Console::GetFPS())).c_str(), XMFLOAT2(256 * 4 - 149, 13), Colors::Black, 0.0f, XMFLOAT2(0, 0), 1.0f);
|
||||
_font->DrawString(_spriteBatch.get(), (wstring(L"FPS: ") + std::to_wstring(Console::GetFPS())).c_str(), XMFLOAT2(256 * 4 - 150, 11), Colors::Yellow, 0.0f, XMFLOAT2(0, 0), 1.0f);
|
||||
_font->DrawString(_spriteBatch.get(), (wstring(L"FPS: ") + std::to_wstring(Console::GetFPS())).c_str(), XMFLOAT2(256 * 4 - 150, 11), Colors::AntiqueWhite, 0.0f, XMFLOAT2(0, 0), 1.0f);
|
||||
}
|
||||
|
||||
if(!_displayMessage.empty() && _displayTimestamp > timeGetTime()) {
|
||||
_font->DrawString(_spriteBatch.get(), _displayMessage.c_str(), XMFLOAT2(12, 13), Colors::Black, 0.0f, XMFLOAT2(0, 0), 1.0f);
|
||||
_font->DrawString(_spriteBatch.get(), _displayMessage.c_str(), XMFLOAT2(11, 11), Colors::AntiqueWhite, 0.0f, XMFLOAT2(0, 0), 1.0f);
|
||||
}
|
||||
|
||||
if(CheckFlag(UIFlags::ShowPauseScreen)) {
|
||||
|
|
|
@ -39,6 +39,9 @@ namespace NES {
|
|||
|
||||
uint32_t _flags = 0;
|
||||
|
||||
wstring _displayMessage = L"";
|
||||
uint32_t _displayTimestamp = 0;
|
||||
|
||||
HRESULT InitDevice();
|
||||
void CleanupDevice();
|
||||
|
||||
|
@ -51,6 +54,8 @@ namespace NES {
|
|||
~Renderer();
|
||||
|
||||
void Render();
|
||||
|
||||
void Renderer::DisplayMessage(wstring text, uint32_t duration);
|
||||
|
||||
void SetFlags(uint32_t flags)
|
||||
{
|
||||
|
|
BIN
GUI/resource.h
BIN
GUI/resource.h
Binary file not shown.
Loading…
Add table
Reference in a new issue