Created new functions for serialization and deserialization

This commit is contained in:
Sergio Martin 2024-01-18 19:56:30 +01:00
parent 17bb8c99d8
commit 4530713b01
10 changed files with 404 additions and 84 deletions

View File

@ -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.

View File

@ -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

View File

@ -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()

View File

@ -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& );

View File

@ -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();
}; };

View File

@ -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"

View File

@ -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 );
}; };

View File

@ -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

View File

@ -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);