Created new functions for serialization and deserialization
This commit is contained in:
parent
17bb8c99d8
commit
4530713b01
17
README.md
17
README.md
|
@ -1,32 +1,33 @@
|
||||||
quickerNES
|
quickerNES
|
||||||
============
|
-----------
|
||||||
|
|
||||||
quickerNES is an attempt to modernizing and improving the performance of quickNES, the fastest NES emulator in the interwebs (as far as I know). The goals for this project are, in order of importance:
|
quickerNES is an attempt to modernizing and improving the performance of [quickNES](https://github.com/kode54/QuickNES). The goals for this project are, in order of importance:
|
||||||
|
|
||||||
- Improve overall emulation performance even more
|
- Improve overall emulation performance for modern (x86) CPUs
|
||||||
- Modernize the code base with best programming practices, including CI tests, benchmarks, and coverage analysis
|
- Modernize the code base with best programming practices, including CI tests, benchmarks, and coverage analysis
|
||||||
- Add support for more mappers, controllers, and features supported by other emulators
|
- Add support for more mappers, controllers, and features supported by other emulators
|
||||||
- Improve accuracy, if possible
|
- Improve accuracy, if possible
|
||||||
|
|
||||||
The main aim is to improve the performance of skip (non-rendering, no-audio) frame advances for brute force botting. (See: [JaffarPlus](https://github.com/SergioMartin86/jaffarPlus)). However, if this work might help with homebrew emulation and other people having more fun, then much better!
|
The main aim is to improve the performance of headless re-recording for TASing and botting (See: [JaffarPlus](https://github.com/SergioMartin86/jaffarPlus)) purposes. However, if this work can help regular play emulation, then much better.
|
||||||
|
|
||||||
Changes
|
Changes
|
||||||
=========
|
--------
|
||||||
|
|
||||||
- Optimizations made in the CPU emulation core, including:
|
- Optimizations made in the CPU emulation core, including:
|
||||||
+ Forced alignment at the start of a page to prevent crossing cache line boundaries
|
+ Forced alignment at the start of a page to prevent crossing cache line boundaries
|
||||||
+ Simplifying instruction decode
|
+ Simplifying the 6502 CPU instruction fetching and decoding
|
||||||
- Minimize compiled code size to reduce pressure on L1i cache
|
- Minimize compiled code size to reduce pressure on L1i cache
|
||||||
- Reduce heap allocations
|
- Reduce heap allocations
|
||||||
|
- General code reorganization (make it header only to help compiler optimizations)
|
||||||
|
|
||||||
Credits
|
Credits
|
||||||
=========
|
---------
|
||||||
|
|
||||||
- quickNES was originally by Shay Green (a.k.a. [Blaarg](http://www.slack.net/~ant/)) under the GNU GPLv2 license. The source code is still located [here](https://github.com/kode54/QuickNES)
|
- quickNES was originally by Shay Green (a.k.a. [Blaarg](http://www.slack.net/~ant/)) under the GNU GPLv2 license. The source code is still located [here](https://github.com/kode54/QuickNES)
|
||||||
- The code was later improved and maintained by Christopher Snowhill (a.k.a. [kode54](https://kode54.net/))
|
- The code was later improved and maintained by Christopher Snowhill (a.k.a. [kode54](https://kode54.net/))
|
||||||
- I could trace further contributions (e.g., new mappers) by retrowertz, CaH4e3, some adaptations from the [FCEUX emulator](https://github.com/TASEmulators/fceux) (see mapper021)
|
- I could trace further contributions (e.g., new mappers) by retrowertz, CaH4e3, some adaptations from the [FCEUX emulator](https://github.com/TASEmulators/fceux) (see mapper021)
|
||||||
- The latest version of the code is maintained by Libretro's community [here](https://github.com/libretro/QuickNES_Core)
|
- The latest version of the code is maintained by Libretro's community [here](https://github.com/libretro/QuickNES_Core)
|
||||||
- For the interactive player, this project uses a modified version of [HeadlessQuickNES (HQN)](https://github.com/Bindernews/HeadlessQuickNes) by Drew (Binder News)
|
- For the interactive player, this project drew some code from [HeadlessQuickNES (HQN)](https://github.com/Bindernews/HeadlessQuickNes) by Drew (Binder News)
|
||||||
- We use some of the [NES test rom set](https://github.com/christopherpow/nes-test-roms) made by multiple authors and gathered by Christopher Pow et al.
|
- We use some of the [NES test rom set](https://github.com/christopherpow/nes-test-roms) made by multiple authors and gathered by Christopher Pow et al.
|
||||||
- We also use some movies from the [TASVideos](tasvideos.org) website for testing. These movies are copied into this repository with authorization under the Creative Commons Attribution 2.0 license.
|
- We also use some movies from the [TASVideos](tasvideos.org) website for testing. These movies are copied into this repository with authorization under the Creative Commons Attribution 2.0 license.
|
||||||
|
|
||||||
|
|
|
@ -86,15 +86,13 @@ class EmuInstance
|
||||||
}
|
}
|
||||||
|
|
||||||
inline size_t getStateSize() const { return _stateSize; }
|
inline size_t getStateSize() const { return _stateSize; }
|
||||||
|
inline size_t getLiteStateSize() const { return _liteStateSize; }
|
||||||
inline std::string getRomSHA1() const { return _romSHA1String; }
|
inline std::string getRomSHA1() const { return _romSHA1String; }
|
||||||
|
|
||||||
inline hash_t getStateHash() const
|
inline hash_t getStateHash() const
|
||||||
{
|
{
|
||||||
MetroHash128 hash;
|
MetroHash128 hash;
|
||||||
|
|
||||||
uint8_t stateData[_stateSize];
|
|
||||||
serializeState(stateData);
|
|
||||||
|
|
||||||
hash.Update(getLowMem(), _LOW_MEM_SIZE);
|
hash.Update(getLowMem(), _LOW_MEM_SIZE);
|
||||||
hash.Update(getHighMem(), _HIGH_MEM_SIZE);
|
hash.Update(getHighMem(), _HIGH_MEM_SIZE);
|
||||||
hash.Update(getNametableMem(), _NAMETABLES_MEM_SIZE);
|
hash.Update(getNametableMem(), _NAMETABLES_MEM_SIZE);
|
||||||
|
@ -137,8 +135,11 @@ inline void loadROMFile(const std::string& romFilePath)
|
||||||
status = loadROMFileImpl(_romData);
|
status = loadROMFileImpl(_romData);
|
||||||
if (status == false) EXIT_WITH_ERROR("Could not process ROM file: %s\n", romFilePath.c_str());
|
if (status == false) EXIT_WITH_ERROR("Could not process ROM file: %s\n", romFilePath.c_str());
|
||||||
|
|
||||||
// Detecting state size
|
// Detecting full state size
|
||||||
_stateSize = getStateSizeImpl();
|
_stateSize = getStateSizeImpl();
|
||||||
|
|
||||||
|
// Detecting lite state size
|
||||||
|
_liteStateSize = getLiteStateSizeImpl();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Virtual functions
|
// Virtual functions
|
||||||
|
@ -153,6 +154,7 @@ inline void loadROMFile(const std::string& romFilePath)
|
||||||
virtual void serializeState(uint8_t* state) const = 0;
|
virtual void serializeState(uint8_t* state) const = 0;
|
||||||
virtual void deserializeState(const uint8_t* state) = 0;
|
virtual void deserializeState(const uint8_t* state) = 0;
|
||||||
virtual size_t getStateSizeImpl() const = 0;
|
virtual size_t getStateSizeImpl() const = 0;
|
||||||
|
virtual size_t getLiteStateSizeImpl() const { return getStateSizeImpl(); };
|
||||||
virtual void doSoftReset() = 0;
|
virtual void doSoftReset() = 0;
|
||||||
virtual void doHardReset() = 0;
|
virtual void doHardReset() = 0;
|
||||||
virtual std::string getCoreName() const = 0;
|
virtual std::string getCoreName() const = 0;
|
||||||
|
@ -162,7 +164,10 @@ inline void loadROMFile(const std::string& romFilePath)
|
||||||
|
|
||||||
EmuInstance() = default;
|
EmuInstance() = default;
|
||||||
|
|
||||||
// Storage for the state
|
// Storage for the light state size
|
||||||
|
size_t _liteStateSize;
|
||||||
|
|
||||||
|
// Storage for the full state size
|
||||||
size_t _stateSize;
|
size_t _stateSize;
|
||||||
|
|
||||||
// Flag to determine whether to enable/disable rendering
|
// Flag to determine whether to enable/disable rendering
|
||||||
|
|
|
@ -16,13 +16,14 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||||
// Nes_Emu 0.7.0
|
// Nes_Emu 0.7.0
|
||||||
|
|
||||||
|
|
||||||
|
#include <cstdio>
|
||||||
|
#include <string>
|
||||||
#include "blargg_common.h"
|
#include "blargg_common.h"
|
||||||
#include "Nes_Apu.h"
|
#include "Nes_Apu.h"
|
||||||
#include "Nes_Cpu.h"
|
#include "Nes_Cpu.h"
|
||||||
#include "Nes_Ppu.h"
|
#include "Nes_Ppu.h"
|
||||||
#include "Nes_Mapper.h"
|
#include "Nes_Mapper.h"
|
||||||
#include "Nes_State.h"
|
#include "Nes_State.h"
|
||||||
#include <cstdio>
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
New mapping distribution by Sergio Martin (eien86)
|
New mapping distribution by Sergio Martin (eien86)
|
||||||
|
@ -216,12 +217,321 @@ public:
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t getLightweightStateSize()
|
size_t getLiteStateSize() const
|
||||||
{
|
{
|
||||||
size_t size = 0;
|
size_t size = 0;
|
||||||
|
|
||||||
|
size += sizeof(nes_state_t);
|
||||||
|
size += sizeof(registers_t);
|
||||||
|
size += sizeof(ppu_state_t);
|
||||||
|
size += sizeof(Nes_Apu::apu_state_t);
|
||||||
|
size += sizeof(joypad_state_t);
|
||||||
|
size += mapper->state_size;
|
||||||
|
size += low_ram_size;
|
||||||
|
size += Nes_Ppu::spr_ram_size;
|
||||||
|
size_t nametable_size = 0x800;
|
||||||
|
if (ppu.nt_banks [3] >= &ppu.impl->nt_ram [0xC00] ) nametable_size = 0x1000;
|
||||||
|
size += nametable_size;
|
||||||
|
if ( ppu.chr_is_writable ) size += ppu.chr_size;
|
||||||
|
if ( sram_present ) size += impl->sram_size;
|
||||||
|
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t getStateSize() const
|
||||||
|
{
|
||||||
|
size_t size = 0;
|
||||||
|
|
||||||
|
size += sizeof(char[4]); // NESS Block
|
||||||
|
size += sizeof(uint32_t); // Block Size
|
||||||
|
|
||||||
|
size += sizeof(char[4]); // TIME Block
|
||||||
|
size += sizeof(uint32_t); // Block Size
|
||||||
|
size += sizeof(nes_state_t);
|
||||||
|
|
||||||
|
size += sizeof(char[4]); // CPUR Block
|
||||||
|
size += sizeof(uint32_t); // Block Size
|
||||||
|
size += sizeof(registers_t);
|
||||||
|
|
||||||
|
size += sizeof(char[4]); // PPUR Block
|
||||||
|
size += sizeof(uint32_t); // Block Size
|
||||||
|
size += sizeof(ppu_state_t);
|
||||||
|
|
||||||
|
size += sizeof(char[4]); // APUR Block
|
||||||
|
size += sizeof(uint32_t); // Block Size
|
||||||
|
size += sizeof(Nes_Apu::apu_state_t);
|
||||||
|
|
||||||
|
size += sizeof(char[4]); // CTRL Block
|
||||||
|
size += sizeof(uint32_t); // Block Size
|
||||||
|
size += sizeof(joypad_state_t);
|
||||||
|
|
||||||
|
size += sizeof(char[4]); // MAPR Block
|
||||||
|
size += sizeof(uint32_t); // Block Size
|
||||||
|
size += mapper->state_size;
|
||||||
|
|
||||||
|
size += sizeof(char[4]); // LRAM Block
|
||||||
|
size += sizeof(uint32_t); // Block Size
|
||||||
|
size += low_ram_size;
|
||||||
|
|
||||||
|
size += sizeof(char[4]); // SPRT Block
|
||||||
|
size += sizeof(uint32_t); // Block Size
|
||||||
|
size += Nes_Ppu::spr_ram_size;
|
||||||
|
|
||||||
|
size += sizeof(char[4]); // NTAB Block
|
||||||
|
size += sizeof(uint32_t); // Block Size
|
||||||
|
size_t nametable_size = 0x800;
|
||||||
|
if (ppu.nt_banks [3] >= &ppu.impl->nt_ram [0xC00] ) nametable_size = 0x1000;
|
||||||
|
size += nametable_size;
|
||||||
|
|
||||||
|
if ( ppu.chr_is_writable )
|
||||||
|
{
|
||||||
|
size += sizeof(char[4]); // CHRR Block
|
||||||
|
size += sizeof(uint32_t); // Block Size
|
||||||
|
size += ppu.chr_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( sram_present )
|
||||||
|
{
|
||||||
|
size += sizeof(char[4]); // SRAM Block
|
||||||
|
size += sizeof(uint32_t); // Block Size
|
||||||
|
size += impl->sram_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
size += sizeof(char[4]); // gend Block
|
||||||
|
size += sizeof(uint32_t); // Block Size
|
||||||
|
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t serializeState(uint8_t* buffer) const
|
||||||
|
{
|
||||||
|
size_t pos = 0;
|
||||||
|
std::string headerCode;
|
||||||
|
const uint32_t headerSize = sizeof(char) * 4;
|
||||||
|
uint32_t blockSize = 0;
|
||||||
|
void* dataSource;
|
||||||
|
|
||||||
|
headerCode = "NESS"; // NESS Block
|
||||||
|
blockSize = 0xFFFFFFFF;
|
||||||
|
memcpy(&buffer[pos], headerCode.data(), headerSize); pos += headerSize;
|
||||||
|
memcpy(&buffer[pos], &blockSize, headerSize); pos += headerSize;
|
||||||
|
|
||||||
|
headerCode = "TIME"; // TIME Block
|
||||||
|
nes_state_t state = nes;
|
||||||
|
state.timestamp *= 5;
|
||||||
|
blockSize = sizeof(nes_state_t);
|
||||||
|
dataSource = (void*) &state;
|
||||||
|
memcpy(&buffer[pos], headerCode.data(), headerSize); pos += headerSize;
|
||||||
|
memcpy(&buffer[pos], &blockSize, headerSize); pos += headerSize;
|
||||||
|
memcpy(&buffer[pos], dataSource, blockSize); pos += blockSize;
|
||||||
|
|
||||||
|
headerCode = "CPUR"; // CPUR Block
|
||||||
|
cpu_state_t s;
|
||||||
|
memset( &s, 0, sizeof s );
|
||||||
|
s.pc = r.pc;
|
||||||
|
s.s = r.sp;
|
||||||
|
s.a = r.a;
|
||||||
|
s.x = r.x;
|
||||||
|
s.y = r.y;
|
||||||
|
s.p = r.status;
|
||||||
|
blockSize = sizeof(cpu_state_t);
|
||||||
|
dataSource = (void*) &s;
|
||||||
|
memcpy(&buffer[pos], headerCode.data(), headerSize); pos += headerSize;
|
||||||
|
memcpy(&buffer[pos], &blockSize, headerSize); pos += headerSize;
|
||||||
|
memcpy(&buffer[pos], dataSource, blockSize); pos += blockSize;
|
||||||
|
|
||||||
|
headerCode = "PPUR"; // PPUR Block
|
||||||
|
blockSize = sizeof(ppu_state_t);
|
||||||
|
dataSource = (void*) &ppu;
|
||||||
|
memcpy(&buffer[pos], headerCode.data(), headerSize); pos += headerSize;
|
||||||
|
memcpy(&buffer[pos], &blockSize, headerSize); pos += headerSize;
|
||||||
|
memcpy(&buffer[pos], dataSource, blockSize); pos += blockSize;
|
||||||
|
|
||||||
|
headerCode = "APUR"; // APUR Block
|
||||||
|
Nes_Apu::apu_state_t apuState;
|
||||||
|
impl->apu.save_state(&apuState);
|
||||||
|
blockSize = sizeof(Nes_Apu::apu_state_t);
|
||||||
|
memcpy(&buffer[pos], headerCode.data(), headerSize); pos += headerSize;
|
||||||
|
memcpy(&buffer[pos], &blockSize, headerSize); pos += headerSize;
|
||||||
|
memcpy(&buffer[pos], &apuState, blockSize); pos += blockSize;
|
||||||
|
|
||||||
|
headerCode = "CTRL"; // CTRL Block
|
||||||
|
blockSize = sizeof(joypad_state_t);
|
||||||
|
dataSource = (void*) &joypad;
|
||||||
|
memcpy(&buffer[pos], headerCode.data(), headerSize); pos += headerSize;
|
||||||
|
memcpy(&buffer[pos], &blockSize, headerSize); pos += headerSize;
|
||||||
|
memcpy(&buffer[pos], dataSource, blockSize); pos += blockSize;
|
||||||
|
|
||||||
|
headerCode = "MAPR"; // MAPR Block
|
||||||
|
blockSize = mapper->state_size;
|
||||||
|
dataSource = (void*) mapper->state;
|
||||||
|
memcpy(&buffer[pos], headerCode.data(), headerSize); pos += headerSize;
|
||||||
|
memcpy(&buffer[pos], &blockSize, headerSize); pos += headerSize;
|
||||||
|
memcpy(&buffer[pos], dataSource, blockSize); pos += blockSize;
|
||||||
|
|
||||||
|
headerCode = "LRAM"; // LRAM Block
|
||||||
|
blockSize = low_ram_size;
|
||||||
|
dataSource = (void*) low_mem;
|
||||||
|
memcpy(&buffer[pos], headerCode.data(), headerSize); pos += headerSize;
|
||||||
|
memcpy(&buffer[pos], &blockSize, headerSize); pos += headerSize;
|
||||||
|
memcpy(&buffer[pos], dataSource, blockSize); pos += blockSize;
|
||||||
|
|
||||||
|
headerCode = "SPRT"; // SPRT Block
|
||||||
|
blockSize = Nes_Ppu::spr_ram_size;
|
||||||
|
dataSource = (void*) ppu.spr_ram;
|
||||||
|
memcpy(&buffer[pos], headerCode.data(), headerSize); pos += headerSize;
|
||||||
|
memcpy(&buffer[pos], &blockSize, headerSize); pos += headerSize;
|
||||||
|
memcpy(&buffer[pos], dataSource, blockSize); pos += blockSize;
|
||||||
|
|
||||||
|
headerCode = "NTAB"; // NTAB Block
|
||||||
|
size_t nametable_size = 0x800;
|
||||||
|
if (ppu.nt_banks [3] >= &ppu.impl->nt_ram [0xC00] ) nametable_size = 0x1000;
|
||||||
|
blockSize = nametable_size;
|
||||||
|
dataSource = (void*) ppu.impl->nt_ram;
|
||||||
|
memcpy(&buffer[pos], headerCode.data(), headerSize); pos += headerSize;
|
||||||
|
memcpy(&buffer[pos], &blockSize, headerSize); pos += headerSize;
|
||||||
|
memcpy(&buffer[pos], dataSource, blockSize); pos += blockSize;
|
||||||
|
|
||||||
|
if ( ppu.chr_is_writable )
|
||||||
|
{
|
||||||
|
headerCode = "CHRR"; // CHRR Block
|
||||||
|
blockSize = ppu.chr_size;
|
||||||
|
dataSource = (void*) ppu.impl->chr_ram;
|
||||||
|
memcpy(&buffer[pos], headerCode.data(), headerSize); pos += headerSize;
|
||||||
|
memcpy(&buffer[pos], &blockSize, headerSize); pos += headerSize;
|
||||||
|
memcpy(&buffer[pos], dataSource, blockSize); pos += blockSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( sram_present )
|
||||||
|
{
|
||||||
|
headerCode = "SRAM"; // SRAM Block
|
||||||
|
blockSize = impl->sram_size;
|
||||||
|
dataSource = (void*) impl->sram;
|
||||||
|
memcpy(&buffer[pos], headerCode.data(), headerSize); pos += headerSize;
|
||||||
|
memcpy(&buffer[pos], &blockSize, headerSize); pos += headerSize;
|
||||||
|
memcpy(&buffer[pos], dataSource, blockSize); pos += blockSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
headerCode = "gend"; // gend Block
|
||||||
|
blockSize = 0;
|
||||||
|
memcpy(&buffer[pos], headerCode.data(), headerSize); pos += headerSize;
|
||||||
|
memcpy(&buffer[pos], &blockSize, headerSize); pos += headerSize;
|
||||||
|
|
||||||
|
return pos; // Bytes written
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t deserializeState(const uint8_t* buffer)
|
||||||
|
{
|
||||||
|
disable_rendering();
|
||||||
|
error_count = 0;
|
||||||
|
ppu.burst_phase = 0; // avoids shimmer when seeking to same time over and over
|
||||||
|
|
||||||
|
size_t pos = 0;
|
||||||
|
const uint32_t headerSize = sizeof(char) * 4;
|
||||||
|
uint32_t blockSize = 0;
|
||||||
|
|
||||||
|
// NESS Block
|
||||||
|
pos += headerSize;
|
||||||
|
pos += headerSize;
|
||||||
|
|
||||||
|
// TIME Block
|
||||||
|
nes_state_t nesState;
|
||||||
|
pos += headerSize;
|
||||||
|
pos += headerSize;
|
||||||
|
blockSize = sizeof(nes_state_t);
|
||||||
|
memcpy(&nesState, &buffer[pos], blockSize); pos += blockSize;
|
||||||
|
nes = nesState;
|
||||||
|
nes.timestamp /= 5;
|
||||||
|
|
||||||
|
// CPUR Block
|
||||||
|
cpu_state_t s;
|
||||||
|
blockSize = sizeof(cpu_state_t);
|
||||||
|
pos += headerSize;
|
||||||
|
pos += headerSize;
|
||||||
|
memcpy((void*) &s, &buffer[pos], blockSize); pos += blockSize;
|
||||||
|
r.pc = s.pc;
|
||||||
|
r.sp = s.s;
|
||||||
|
r.a = s.a;
|
||||||
|
r.x = s.x;
|
||||||
|
r.y = s.y;
|
||||||
|
r.status = s.p;
|
||||||
|
|
||||||
|
// PPUR Block
|
||||||
|
blockSize = sizeof(ppu_state_t);
|
||||||
|
pos += headerSize;
|
||||||
|
pos += headerSize;
|
||||||
|
memcpy((void*) &ppu, &buffer[pos], blockSize); pos += blockSize;
|
||||||
|
|
||||||
|
// APUR Block
|
||||||
|
Nes_Apu::apu_state_t apuState;
|
||||||
|
blockSize = sizeof(Nes_Apu::apu_state_t);
|
||||||
|
pos += headerSize;
|
||||||
|
pos += headerSize;
|
||||||
|
memcpy(&apuState, &buffer[pos], blockSize);
|
||||||
|
pos += blockSize;
|
||||||
|
impl->apu.load_state(apuState);
|
||||||
|
impl->apu.end_frame( -(int) nes.timestamp / ppu_overclock );
|
||||||
|
|
||||||
|
// CTRL Block
|
||||||
|
blockSize = sizeof(joypad_state_t);
|
||||||
|
pos += headerSize;
|
||||||
|
pos += headerSize;
|
||||||
|
memcpy((void*) &joypad, &buffer[pos], blockSize); pos += blockSize;
|
||||||
|
|
||||||
|
// MAPR Block
|
||||||
|
mapper->default_reset_state();
|
||||||
|
blockSize = mapper->state_size;
|
||||||
|
pos += headerSize;
|
||||||
|
pos += headerSize;
|
||||||
|
memcpy((void*) mapper->state, &buffer[pos], blockSize); pos += blockSize;
|
||||||
|
mapper->apply_mapping();
|
||||||
|
|
||||||
|
// LRAM Block
|
||||||
|
blockSize = low_ram_size;
|
||||||
|
pos += headerSize;
|
||||||
|
pos += headerSize;
|
||||||
|
memcpy((void*) low_mem, &buffer[pos], blockSize); pos += blockSize;
|
||||||
|
|
||||||
|
// SPRT Block
|
||||||
|
blockSize = Nes_Ppu::spr_ram_size;
|
||||||
|
pos += headerSize;
|
||||||
|
pos += headerSize;
|
||||||
|
memcpy((void*) ppu.spr_ram, &buffer[pos], blockSize); pos += blockSize;
|
||||||
|
|
||||||
|
// NTAB Block
|
||||||
|
size_t nametable_size = 0x800;
|
||||||
|
if (ppu.nt_banks [3] >= &ppu.impl->nt_ram [0xC00] ) nametable_size = 0x1000;
|
||||||
|
blockSize = nametable_size;
|
||||||
|
pos += headerSize;
|
||||||
|
pos += headerSize;
|
||||||
|
memcpy((void*) ppu.impl->nt_ram, &buffer[pos], blockSize); pos += blockSize;
|
||||||
|
|
||||||
|
if ( ppu.chr_is_writable )
|
||||||
|
{
|
||||||
|
// CHRR Block
|
||||||
|
blockSize = ppu.chr_size;
|
||||||
|
pos += headerSize;
|
||||||
|
pos += headerSize;
|
||||||
|
memcpy((void*) ppu.impl->chr_ram, &buffer[pos], blockSize); pos += blockSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( sram_present )
|
||||||
|
{
|
||||||
|
// SRAM Block
|
||||||
|
blockSize = impl->sram_size;
|
||||||
|
pos += headerSize;
|
||||||
|
pos += headerSize;
|
||||||
|
memcpy((void*) impl->sram, &buffer[pos], blockSize); pos += blockSize;
|
||||||
|
enable_sram(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// headerCode = "gend"; // gend Block
|
||||||
|
pos += headerSize;
|
||||||
|
pos += headerSize;
|
||||||
|
|
||||||
|
return pos; // Bytes read
|
||||||
|
}
|
||||||
|
|
||||||
void reset( bool full_reset, bool erase_battery_ram )
|
void reset( bool full_reset, bool erase_battery_ram )
|
||||||
{
|
{
|
||||||
if ( full_reset )
|
if ( full_reset )
|
||||||
|
@ -347,50 +657,50 @@ public:
|
||||||
|
|
||||||
void load_state( Nes_State_ const& in )
|
void load_state( Nes_State_ const& in )
|
||||||
{
|
{
|
||||||
disable_rendering();
|
// disable_rendering();
|
||||||
error_count = 0;
|
// error_count = 0;
|
||||||
|
|
||||||
if ( in.nes_valid )
|
// if ( in.nes_valid )
|
||||||
nes = in.nes;
|
// nes = in.nes;
|
||||||
|
|
||||||
// always use frame count
|
// // always use frame count
|
||||||
ppu.burst_phase = 0; // avoids shimmer when seeking to same time over and over
|
// ppu.burst_phase = 0; // avoids shimmer when seeking to same time over and over
|
||||||
nes.frame_count = in.nes.frame_count;
|
// nes.frame_count = in.nes.frame_count;
|
||||||
if ( (frame_count_t) nes.frame_count == invalid_frame_count )
|
// if ( (frame_count_t) nes.frame_count == invalid_frame_count )
|
||||||
nes.frame_count = 0;
|
// nes.frame_count = 0;
|
||||||
|
|
||||||
if ( in.cpu_valid )
|
// if ( in.cpu_valid )
|
||||||
cpu::r = *in.cpu;
|
// cpu::r = *in.cpu;
|
||||||
|
|
||||||
if ( in.joypad_valid )
|
// if ( in.joypad_valid )
|
||||||
joypad = *in.joypad;
|
// joypad = *in.joypad;
|
||||||
|
|
||||||
if ( in.apu_valid )
|
// if ( in.apu_valid )
|
||||||
{
|
// {
|
||||||
impl->apu.load_state( *in.apu );
|
// impl->apu.load_state( *in.apu );
|
||||||
// prevent apu from running extra at beginning of frame
|
// // prevent apu from running extra at beginning of frame
|
||||||
impl->apu.end_frame( -(int) nes.timestamp / ppu_overclock );
|
// impl->apu.end_frame( -(int) nes.timestamp / ppu_overclock );
|
||||||
}
|
// }
|
||||||
else
|
// else
|
||||||
{
|
// {
|
||||||
impl->apu.reset();
|
// impl->apu.reset();
|
||||||
}
|
// }
|
||||||
|
|
||||||
ppu.load_state( in );
|
// ppu.load_state( in );
|
||||||
|
|
||||||
if ( in.ram_valid )
|
// if ( in.ram_valid )
|
||||||
memcpy( cpu::low_mem, in.ram, in.ram_size );
|
// memcpy( cpu::low_mem, in.ram, in.ram_size );
|
||||||
|
|
||||||
sram_present = false;
|
// sram_present = false;
|
||||||
if ( in.sram_size )
|
// if ( in.sram_size )
|
||||||
{
|
// {
|
||||||
sram_present = true;
|
// sram_present = true;
|
||||||
memcpy( impl->sram, in.sram, min( (int) in.sram_size, (int) sizeof impl->sram ) );
|
// // memcpy( impl->sram, in.sram, min( (int) in.sram_size, (int) sizeof impl->sram ) );
|
||||||
enable_sram( true ); // mapper can override (read-only, unmapped, etc.)
|
// enable_sram( true ); // mapper can override (read-only, unmapped, etc.)
|
||||||
}
|
// }
|
||||||
|
|
||||||
if ( in.mapper_valid ) // restore last since it might reconfigure things
|
// if ( in.mapper_valid ) // restore last since it might reconfigure things
|
||||||
mapper->load_state( *in.mapper );
|
// mapper->load_state( *in.mapper );
|
||||||
}
|
}
|
||||||
|
|
||||||
void irq_changed()
|
void irq_changed()
|
||||||
|
|
|
@ -42,7 +42,8 @@ public:
|
||||||
|
|
||||||
const uint8_t* getHostPixels () const { return emu.ppu.host_pixels; }
|
const uint8_t* getHostPixels () const { return emu.ppu.host_pixels; }
|
||||||
|
|
||||||
size_t getLightweightStateSize() { return emu.getLightweightStateSize(); }
|
size_t getLiteStateSize() const { return emu.getLiteStateSize(); }
|
||||||
|
size_t getStateSize() const { return emu.getStateSize(); }
|
||||||
|
|
||||||
// Basic emulation
|
// Basic emulation
|
||||||
|
|
||||||
|
@ -140,6 +141,8 @@ public:
|
||||||
// Save emulator state
|
// Save emulator state
|
||||||
void save_state( Nes_State* s ) const { emu.save_state( s ); }
|
void save_state( Nes_State* s ) const { emu.save_state( s ); }
|
||||||
const char * save_state( Auto_File_Writer ) const;
|
const char * save_state( Auto_File_Writer ) const;
|
||||||
|
size_t serializeState (uint8_t* buffer) const { return emu.serializeState(buffer); }
|
||||||
|
size_t deserializeState (const uint8_t* buffer) { return emu.deserializeState(buffer); }
|
||||||
|
|
||||||
// Load state into emulator
|
// Load state into emulator
|
||||||
void load_state( Nes_State const& );
|
void load_state( Nes_State const& );
|
||||||
|
|
|
@ -68,6 +68,9 @@ public:
|
||||||
// $2006 and $2007 accesses (but not due to PPU scanline rendering).
|
// $2006 and $2007 accesses (but not due to PPU scanline rendering).
|
||||||
virtual void a12_clocked();
|
virtual void a12_clocked();
|
||||||
|
|
||||||
|
void* state;
|
||||||
|
unsigned state_size;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// Services provided for derived mapper classes
|
// Services provided for derived mapper classes
|
||||||
Nes_Mapper();
|
Nes_Mapper();
|
||||||
|
@ -147,9 +150,7 @@ protected:
|
||||||
// apply_mapping().
|
// apply_mapping().
|
||||||
virtual void read_state( mapper_state_t const& );
|
virtual void read_state( mapper_state_t const& );
|
||||||
|
|
||||||
// Apply current mapping state to hardware. Called after reading mapper state
|
|
||||||
// from a snapshot.
|
|
||||||
virtual void apply_mapping() = 0;
|
|
||||||
|
|
||||||
// Called by default reset() before apply_mapping() is called.
|
// Called by default reset() before apply_mapping() is called.
|
||||||
virtual void reset_state() { }
|
virtual void reset_state() { }
|
||||||
|
@ -161,11 +162,9 @@ protected:
|
||||||
Nes_Cart const* cart_;
|
Nes_Cart const* cart_;
|
||||||
Nes_Core* emu_;
|
Nes_Core* emu_;
|
||||||
|
|
||||||
private:
|
// Apply current mapping state to hardware. Called after reading mapper state
|
||||||
|
// from a snapshot.
|
||||||
void* state;
|
virtual void apply_mapping() = 0;
|
||||||
unsigned state_size;
|
|
||||||
|
|
||||||
|
|
||||||
void default_reset_state();
|
void default_reset_state();
|
||||||
};
|
};
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
#include "Nes_Ppu_Impl.h"
|
#include "Nes_Ppu_Impl.h"
|
||||||
|
|
||||||
|
#include <cstdio>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include "blargg_endian.h"
|
#include "blargg_endian.h"
|
||||||
#include "Nes_State.h"
|
#include "Nes_State.h"
|
||||||
|
|
|
@ -30,6 +30,10 @@ public:
|
||||||
static const uint16_t buffer_height = image_height;
|
static const uint16_t buffer_height = image_height;
|
||||||
enum { spr_ram_size = 0x100 };
|
enum { spr_ram_size = 0x100 };
|
||||||
|
|
||||||
|
uint8_t* nt_banks [4];
|
||||||
|
bool chr_is_writable;
|
||||||
|
long chr_size;
|
||||||
|
|
||||||
int write_2007( int );
|
int write_2007( int );
|
||||||
|
|
||||||
// Host palette
|
// Host palette
|
||||||
|
@ -67,9 +71,10 @@ public:
|
||||||
|
|
||||||
uint8_t* getSpriteRAM () { return spr_ram; }
|
uint8_t* getSpriteRAM () { return spr_ram; }
|
||||||
uint16_t getSpriteRAMSize () { return spr_ram_size; }
|
uint16_t getSpriteRAMSize () { return spr_ram_size; }
|
||||||
|
|
||||||
protected:
|
|
||||||
uint8_t spr_ram [spr_ram_size];
|
uint8_t spr_ram [spr_ram_size];
|
||||||
|
void all_tiles_modified();
|
||||||
|
protected:
|
||||||
|
|
||||||
void begin_frame();
|
void begin_frame();
|
||||||
void run_hblank( int );
|
void run_hblank( int );
|
||||||
int sprite_height() const { return (w2000 >> 2 & 8) + 8; }
|
int sprite_height() const { return (w2000 >> 2 & 8) + 8; }
|
||||||
|
@ -90,7 +95,6 @@ protected: //friend class Nes_Ppu_Rendering; private:
|
||||||
void capture_palette();
|
void capture_palette();
|
||||||
|
|
||||||
bool any_tiles_modified;
|
bool any_tiles_modified;
|
||||||
bool chr_is_writable;
|
|
||||||
void update_tiles( int first_tile );
|
void update_tiles( int first_tile );
|
||||||
|
|
||||||
typedef uint32_t cache_t;
|
typedef uint32_t cache_t;
|
||||||
|
@ -128,7 +132,7 @@ private:
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
uint8_t* nt_banks [4];
|
|
||||||
|
|
||||||
bool mmc24_enabled;
|
bool mmc24_enabled;
|
||||||
uint8_t mmc24_latched [2];
|
uint8_t mmc24_latched [2];
|
||||||
|
@ -136,7 +140,6 @@ private:
|
||||||
// CHR data
|
// CHR data
|
||||||
uint8_t const* chr_data; // points to chr ram when there is no read-only data
|
uint8_t const* chr_data; // points to chr ram when there is no read-only data
|
||||||
uint8_t* chr_ram; // always points to impl->chr_ram; makes write_2007() faster
|
uint8_t* chr_ram; // always points to impl->chr_ram; makes write_2007() faster
|
||||||
long chr_size;
|
|
||||||
uint8_t const* map_chr( int addr ) { return &chr_data [map_chr_addr( addr )]; }
|
uint8_t const* map_chr( int addr ) { return &chr_data [map_chr_addr( addr )]; }
|
||||||
|
|
||||||
// CHR cache
|
// CHR cache
|
||||||
|
@ -147,7 +150,7 @@ private:
|
||||||
uint8_t modified_tiles [chr_tile_count / 8];
|
uint8_t modified_tiles [chr_tile_count / 8];
|
||||||
uint32_t align_;
|
uint32_t align_;
|
||||||
};
|
};
|
||||||
void all_tiles_modified();
|
|
||||||
void update_tile( int index );
|
void update_tile( int index );
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -26,9 +26,6 @@ class QuickerNESInstance : public EmuInstance
|
||||||
{
|
{
|
||||||
// Loading rom data
|
// Loading rom data
|
||||||
auto result = _nes->load_ines((uint8_t*)romData.data());
|
auto result = _nes->load_ines((uint8_t*)romData.data());
|
||||||
|
|
||||||
// printf("Lightweight size: %lu vs Full Size: %lu\n", _nes->getLightweightStateSize(), getStateSizeImpl());
|
|
||||||
// exit(0);
|
|
||||||
return result == 0;
|
return result == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,16 +37,12 @@ class QuickerNESInstance : public EmuInstance
|
||||||
|
|
||||||
void serializeState(uint8_t* state) const
|
void serializeState(uint8_t* state) const
|
||||||
{
|
{
|
||||||
Mem_Writer w(state, _stateSize, 0);
|
_nes->serializeState(state);
|
||||||
Auto_File_Writer a(w);
|
|
||||||
_nes->save_state(a);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void deserializeState(const uint8_t* state)
|
void deserializeState(const uint8_t* state)
|
||||||
{
|
{
|
||||||
Mem_File_Reader r(state, _stateSize);
|
_nes->deserializeState(state);
|
||||||
Auto_File_Reader a(r);
|
|
||||||
_nes->load_state(a);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void advanceStateImpl(const inputType controller1, const inputType controller2) override
|
void advanceStateImpl(const inputType controller1, const inputType controller2) override
|
||||||
|
@ -66,13 +59,14 @@ class QuickerNESInstance : public EmuInstance
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
inline size_t getStateSizeImpl() const
|
inline size_t getStateSizeImpl() const override
|
||||||
{
|
{
|
||||||
// Using dry writer to just obtain the state size
|
return _nes->getStateSize();
|
||||||
Dry_Writer w;
|
}
|
||||||
Auto_File_Writer a(w);
|
|
||||||
_nes->save_state(a);
|
inline size_t getLiteStateSizeImpl() const override
|
||||||
return w.size();
|
{
|
||||||
|
return _nes->getLiteStateSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Video buffer
|
// Video buffer
|
||||||
|
|
|
@ -92,9 +92,12 @@ int main(int argc, char *argv[])
|
||||||
// Getting initial hash
|
// Getting initial hash
|
||||||
auto initialHash = e.getStateHash();
|
auto initialHash = e.getStateHash();
|
||||||
|
|
||||||
// Getting state size
|
// Getting full state size
|
||||||
const auto stateSize = e.getStateSize();
|
const auto stateSize = e.getStateSize();
|
||||||
|
|
||||||
|
// Getting lite state size
|
||||||
|
const auto liteStateSize = e.getLiteStateSize();
|
||||||
|
|
||||||
// Getting actual ROM SHA1
|
// Getting actual ROM SHA1
|
||||||
auto romSHA1 = e.getRomSHA1();
|
auto romSHA1 = e.getRomSHA1();
|
||||||
|
|
||||||
|
@ -124,7 +127,8 @@ int main(int argc, char *argv[])
|
||||||
printf("[] Sequence File: '%s'\n", sequenceFilePath.c_str());
|
printf("[] Sequence File: '%s'\n", sequenceFilePath.c_str());
|
||||||
printf("[] Sequence Length: %lu\n", sequenceLength);
|
printf("[] Sequence Length: %lu\n", sequenceLength);
|
||||||
printf("[] Initial State Hash: 0x%lX%lX\n", initialHash.first, initialHash.second);
|
printf("[] Initial State Hash: 0x%lX%lX\n", initialHash.first, initialHash.second);
|
||||||
printf("[] State Size: %lu bytes\n", stateSize);
|
printf("[] Full State Size: %lu bytes\n", stateSize);
|
||||||
|
printf("[] Lite State Size: %lu bytes\n", liteStateSize);
|
||||||
printf("[] ********** Running Test **********\n");
|
printf("[] ********** Running Test **********\n");
|
||||||
|
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
|
|
Loading…
Reference in New Issue