Adding configurable light states

This commit is contained in:
Sergio Martin 2024-01-20 16:48:38 +01:00
parent 9eccd5084b
commit 0afe500701
11 changed files with 2008 additions and 118 deletions

View File

@ -154,6 +154,9 @@ class EmuInstance
virtual void deserializeLiteState(const uint8_t *state) = 0;
virtual size_t getFullStateSize() const = 0;
virtual size_t getLiteStateSize() const = 0;
virtual void enableLiteStateBlock(const std::string& block) = 0;
virtual void disableLiteStateBlock(const std::string& block) = 0;
virtual void doSoftReset() = 0;
virtual void doHardReset() = 0;
virtual std::string getCoreName() const = 0;

View File

@ -49,6 +49,9 @@ class QuickNESInstance : public EmuInstance
void deserializeLiteState(const uint8_t *state) override { deserializeFullState(state); }
inline size_t getLiteStateSize() const override { return getFullStateSize(); }
void enableLiteStateBlock(const std::string& block) override {};
void disableLiteStateBlock(const std::string& block) override {};
void serializeFullState(uint8_t *state) const override
{
Mem_Writer w(state, _fullStateSize, 0);

View File

@ -80,6 +80,20 @@ class Core : private Cpu
typedef Cpu cpu;
public:
// Flags for lite state storage
bool TIMEBlockEnabled = true;
bool CPURBlockEnabled = true;
bool PPURBlockEnabled = true;
bool APURBlockEnabled = true;
bool CTRLBlockEnabled = true;
bool MAPRBlockEnabled = true;
bool LRAMBlockEnabled = true;
bool SPRTBlockEnabled = true;
bool NTABBlockEnabled = true;
bool CHRRBlockEnabled = true;
bool SRAMBlockEnabled = true;
Core() : ppu(this)
{
cart = NULL;
@ -412,80 +426,153 @@ class Core : private Cpu
return pos; // Bytes read
}
void enableLiteStateBlock(const std::string& block)
{
bool recognizedBlock = false;
if (block == "TIME") { TIMEBlockEnabled = true; recognizedBlock = true; }
if (block == "CPUR") { CPURBlockEnabled = true; recognizedBlock = true; }
if (block == "PPUR") { PPURBlockEnabled = true; recognizedBlock = true; }
if (block == "APUR") { APURBlockEnabled = true; recognizedBlock = true; }
if (block == "CTRL") { CTRLBlockEnabled = true; recognizedBlock = true; }
if (block == "MAPR") { MAPRBlockEnabled = true; recognizedBlock = true; }
if (block == "LRAM") { LRAMBlockEnabled = true; recognizedBlock = true; }
if (block == "SPRT") { SPRTBlockEnabled = true; recognizedBlock = true; }
if (block == "NTAB") { NTABBlockEnabled = true; recognizedBlock = true; }
if (block == "CHRR") { CHRRBlockEnabled = true; recognizedBlock = true; }
if (block == "SRAM") { SRAMBlockEnabled = true; recognizedBlock = true; }
if (recognizedBlock == false) { fprintf(stderr, "Unrecognized block type: %s\n", block.c_str()); exit(-1);}
};
void disableLiteStateBlock(const std::string& block)
{
bool recognizedBlock = false;
if (block == "TIME") { TIMEBlockEnabled = false; recognizedBlock = true; }
if (block == "CPUR") { CPURBlockEnabled = false; recognizedBlock = true; }
if (block == "PPUR") { PPURBlockEnabled = false; recognizedBlock = true; }
if (block == "APUR") { APURBlockEnabled = false; recognizedBlock = true; }
if (block == "CTRL") { CTRLBlockEnabled = false; recognizedBlock = true; }
if (block == "MAPR") { MAPRBlockEnabled = false; recognizedBlock = true; }
if (block == "LRAM") { LRAMBlockEnabled = false; recognizedBlock = true; }
if (block == "SPRT") { SPRTBlockEnabled = false; recognizedBlock = true; }
if (block == "NTAB") { NTABBlockEnabled = false; recognizedBlock = true; }
if (block == "CHRR") { CHRRBlockEnabled = false; recognizedBlock = true; }
if (block == "SRAM") { SRAMBlockEnabled = false; recognizedBlock = true; }
if (recognizedBlock == false) { fprintf(stderr, "Unrecognized block type: %s\n", block.c_str()); exit(-1);}
};
size_t serializeLiteState(uint8_t *buffer) const
{
size_t pos = 0;
uint32_t blockSize = 0;
void *dataSource;
nes_state_lite_t state;
state.timestamp = nes.timestamp;
state.frame_count = (uint8_t)nes.frame_count;
state.timestamp *= 5;
blockSize = sizeof(nes_state_lite_t);
dataSource = (void *)&state;
if (buffer != nullptr) memcpy(&buffer[pos], dataSource, blockSize);
pos += blockSize;
blockSize = sizeof(cpu::registers_t);
dataSource = (void *)&r;
if (buffer != nullptr) memcpy(&buffer[pos], dataSource, blockSize);
pos += blockSize;
blockSize = sizeof(ppu_state_t);
dataSource = (void *)&ppu;
if (buffer != nullptr) memcpy(&buffer[pos], dataSource, blockSize);
pos += blockSize;
Apu::apu_state_t apuState;
impl->apu.save_state(&apuState);
blockSize = sizeof(Apu::apu_state_t);
if (buffer != nullptr) memcpy(&buffer[pos], &apuState, blockSize);
pos += blockSize;
blockSize = sizeof(joypad_state_t);
dataSource = (void *)&joypad;
if (buffer != nullptr) memcpy(&buffer[pos], dataSource, blockSize);
pos += blockSize;
blockSize = mapper->state_size;
dataSource = (void *)mapper->state;
if (buffer != nullptr) memcpy(&buffer[pos], dataSource, blockSize);
pos += blockSize;
blockSize = low_ram_size;
dataSource = (void *)low_mem;
if (buffer != nullptr) memcpy(&buffer[pos], dataSource, blockSize);
pos += blockSize;
blockSize = Ppu::spr_ram_size;
dataSource = (void *)ppu.spr_ram;
if (buffer != nullptr) memcpy(&buffer[pos], dataSource, blockSize);
pos += blockSize;
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;
if (buffer != nullptr) memcpy(&buffer[pos], dataSource, blockSize);
pos += blockSize;
if (ppu.chr_is_writable)
if (TIMEBlockEnabled == true)
{
blockSize = ppu.chr_size;
dataSource = (void *)ppu.impl->chr_ram;
nes_state_lite_t state;
state.timestamp = nes.timestamp;
state.frame_count = (uint8_t)nes.frame_count;
state.timestamp *= 5;
blockSize = sizeof(nes_state_lite_t);
dataSource = (void *)&state;
if (buffer != nullptr) memcpy(&buffer[pos], dataSource, blockSize);
pos += blockSize;
}
if (sram_present)
if (CPURBlockEnabled == true)
{
blockSize = impl->sram_size;
dataSource = (void *)impl->sram;
blockSize = sizeof(cpu::registers_t);
dataSource = (void *)&r;
if (buffer != nullptr) memcpy(&buffer[pos], dataSource, blockSize);
pos += blockSize;
}
if (PPURBlockEnabled == true)
{
blockSize = sizeof(ppu_state_t);
dataSource = (void *)&ppu;
if (buffer != nullptr) memcpy(&buffer[pos], dataSource, blockSize);
pos += blockSize;
}
if (APURBlockEnabled == true)
{
Apu::apu_state_t apuState;
impl->apu.save_state(&apuState);
blockSize = sizeof(Apu::apu_state_t);
if (buffer != nullptr) memcpy(&buffer[pos], &apuState, blockSize);
pos += blockSize;
}
if (CTRLBlockEnabled == true)
{
blockSize = sizeof(joypad_state_t);
dataSource = (void *)&joypad;
if (buffer != nullptr) memcpy(&buffer[pos], dataSource, blockSize);
pos += blockSize;
}
if (MAPRBlockEnabled == true)
{
blockSize = mapper->state_size;
dataSource = (void *)mapper->state;
if (buffer != nullptr) memcpy(&buffer[pos], dataSource, blockSize);
pos += blockSize;
}
if (LRAMBlockEnabled == true)
{
blockSize = low_ram_size;
dataSource = (void *)low_mem;
if (buffer != nullptr) memcpy(&buffer[pos], dataSource, blockSize);
pos += blockSize;
}
if (SPRTBlockEnabled == true)
{
blockSize = Ppu::spr_ram_size;
dataSource = (void *)ppu.spr_ram;
if (buffer != nullptr) memcpy(&buffer[pos], dataSource, blockSize);
pos += blockSize;
}
if (NTABBlockEnabled == true)
{
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;
if (buffer != nullptr) memcpy(&buffer[pos], dataSource, blockSize);
pos += blockSize;
}
if (CHRRBlockEnabled == true)
{
if (ppu.chr_is_writable)
{
blockSize = ppu.chr_size;
dataSource = (void *)ppu.impl->chr_ram;
if (buffer != nullptr) memcpy(&buffer[pos], dataSource, blockSize);
pos += blockSize;
}
}
if (SRAMBlockEnabled == true)
{
if (sram_present)
{
blockSize = impl->sram_size;
dataSource = (void *)impl->sram;
if (buffer != nullptr) memcpy(&buffer[pos], dataSource, blockSize);
pos += blockSize;
}
}
return pos; // Bytes written
}
@ -499,78 +586,112 @@ size_t serializeLiteState(uint8_t *buffer) const
uint32_t blockSize = 0;
// TIME Block
nes_state_lite_t nesState;
blockSize = sizeof(nes_state_lite_t);
memcpy(&nesState, &buffer[pos], blockSize);
pos += blockSize;
nes.frame_count = nesState.frame_count;
nes.timestamp = nesState.timestamp;
nes.timestamp /= 5;
if (TIMEBlockEnabled == true)
{
nes_state_lite_t nesState;
blockSize = sizeof(nes_state_lite_t);
memcpy(&nesState, &buffer[pos], blockSize);
pos += blockSize;
nes.frame_count = nesState.frame_count;
nes.timestamp = nesState.timestamp;
nes.timestamp /= 5;
}
// CPUR Block
blockSize = sizeof(cpu::registers_t);
memcpy((void *)&r, &buffer[pos], blockSize);
pos += blockSize;
if (CPURBlockEnabled == true)
{
blockSize = sizeof(cpu::registers_t);
memcpy((void *)&r, &buffer[pos], blockSize);
pos += blockSize;
}
// PPUR Block
blockSize = sizeof(ppu_state_t);
memcpy((void *)&ppu, &buffer[pos], blockSize);
pos += blockSize;
if (PPURBlockEnabled == true)
{
blockSize = sizeof(ppu_state_t);
memcpy((void *)&ppu, &buffer[pos], blockSize);
pos += blockSize;
}
// APUR Block
Apu::apu_state_t apuState;
blockSize = sizeof(Apu::apu_state_t);
memcpy(&apuState, &buffer[pos], blockSize);
pos += blockSize;
impl->apu.load_state(apuState);
impl->apu.end_frame(-(int)nes.timestamp / ppu_overclock);
if (APURBlockEnabled == true)
{
Apu::apu_state_t apuState;
blockSize = sizeof(Apu::apu_state_t);
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);
memcpy((void *)&joypad, &buffer[pos], blockSize);
pos += blockSize;
if (CTRLBlockEnabled == true)
{
blockSize = sizeof(joypad_state_t);
memcpy((void *)&joypad, &buffer[pos], blockSize);
pos += blockSize;
}
// MAPR Block
mapper->default_reset_state();
blockSize = mapper->state_size;
memcpy((void *)mapper->state, &buffer[pos], blockSize);
pos += blockSize;
mapper->apply_mapping();
if (MAPRBlockEnabled == true)
{
mapper->default_reset_state();
blockSize = mapper->state_size;
memcpy((void *)mapper->state, &buffer[pos], blockSize);
pos += blockSize;
mapper->apply_mapping();
}
// LRAM Block
blockSize = low_ram_size;
memcpy((void *)low_mem, &buffer[pos], blockSize);
pos += blockSize;
if (LRAMBlockEnabled == true)
{
blockSize = low_ram_size;
memcpy((void *)low_mem, &buffer[pos], blockSize);
pos += blockSize;
}
// SPRT Block
blockSize = Ppu::spr_ram_size;
memcpy((void *)ppu.spr_ram, &buffer[pos], blockSize);
pos += blockSize;
if (SPRTBlockEnabled == true)
{
blockSize = Ppu::spr_ram_size;
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;
memcpy((void *)ppu.impl->nt_ram, &buffer[pos], blockSize);
pos += blockSize;
if (ppu.chr_is_writable)
if (NTABBlockEnabled == true)
{
// CHRR Block
blockSize = ppu.chr_size;
memcpy((void *)ppu.impl->chr_ram, &buffer[pos], blockSize);
size_t nametable_size = 0x800;
if (ppu.nt_banks[3] >= &ppu.impl->nt_ram[0xC00]) nametable_size = 0x1000;
blockSize = nametable_size;
memcpy((void *)ppu.impl->nt_ram, &buffer[pos], blockSize);
pos += blockSize;
}
if (sram_present)
if (CHRRBlockEnabled == true)
{
// SRAM Block
blockSize = impl->sram_size;
memcpy((void *)impl->sram, &buffer[pos], blockSize);
pos += blockSize;
enable_sram(true);
if (ppu.chr_is_writable)
{
// CHRR Block
blockSize = ppu.chr_size;
memcpy((void *)ppu.impl->chr_ram, &buffer[pos], blockSize);
pos += blockSize;
}
}
if (SRAMBlockEnabled == true)
{
if (sram_present)
{
// SRAM Block
blockSize = impl->sram_size;
memcpy((void *)impl->sram, &buffer[pos], blockSize);
pos += blockSize;
}
}
if (sram_present) enable_sram(true);
return pos; // Bytes read
}

View File

@ -54,6 +54,9 @@ class Emu
size_t getLiteStateSize() const { return emu.serializeLiteState(nullptr); }
size_t getFullStateSize() const { return emu.serializeFullState(nullptr); }
void enableLiteStateBlock(const std::string& block) { emu.enableLiteStateBlock(block); };
void disableLiteStateBlock(const std::string& block) { emu.disableLiteStateBlock(block); };
// Basic emulation
// Emulate one video frame using joypad1 and joypad2 as input. Afterwards, image

View File

@ -45,6 +45,9 @@ class QuickerNESInstance : public EmuInstance
size_t getFullStateSize() const override { return _nes->getFullStateSize(); }
size_t getLiteStateSize() const override { return _nes->getLiteStateSize(); }
void enableLiteStateBlock(const std::string& block) override { _nes->enableLiteStateBlock(block); };
void disableLiteStateBlock(const std::string& block) override { _nes->disableLiteStateBlock(block); };
void advanceStateImpl(const inputType controller1, const inputType controller2) override
{
if (_doRendering == true) _nes->emulate_frame(controller1, controller2);

View File

@ -4,6 +4,8 @@
#include "utils.hpp"
#include <chrono>
#include <sstream>
#include <vector>
#include <string>
#ifdef _USE_QUICKNES
#include "quickNESInstance.hpp"
@ -76,6 +78,18 @@ int main(int argc, char *argv[])
if (scriptJson["Expected ROM SHA1"].is_string() == false) EXIT_WITH_ERROR("Script file 'Expected ROM SHA1' entry is not a string\n");
std::string expectedROMSHA1 = scriptJson["Expected ROM SHA1"].get<std::string>();
// Parsing disabled blocks in lite state serialization
std::vector<std::string> liteStateDisabledBlocks;
std::string liteStateDisabledBlocksOutput;
if (scriptJson.contains("Disable Lite State Blocks") == false) EXIT_WITH_ERROR("Script file missing 'Disable Lite State Blocks' entry\n");
if (scriptJson["Disable Lite State Blocks"].is_array() == false) EXIT_WITH_ERROR("Script file 'Disable Lite State Blocks' is not an array\n");
for (const auto& entry : scriptJson["Disable Lite State Blocks"])
{
if (entry.is_string() == false) EXIT_WITH_ERROR("Script file 'Disable Lite State Blocks' entry is not a string\n");
liteStateDisabledBlocks.push_back(entry.get<std::string>());
liteStateDisabledBlocksOutput += entry.get<std::string>() + std::string(" ");
}
// Creating emulator instance
#ifdef _USE_QUICKNES
auto e = QuickNESInstance();
@ -124,16 +138,17 @@ int main(int argc, char *argv[])
// Printing test information
printf("[] -----------------------------------------\n");
printf("[] Running Script: '%s'\n", scriptFilePath.c_str());
printf("[] Cycle Type: '%s'\n", cycleType.c_str());
printf("[] Emulation Core: '%s'\n", emulationCoreName.c_str());
printf("[] ROM File: '%s'\n", romFilePath.c_str());
printf("[] ROM SHA1: '%s'\n", romSHA1.c_str());
printf("[] Sequence File: '%s'\n", sequenceFilePath.c_str());
printf("[] Sequence Length: %lu\n", sequenceLength);
printf("[] Initial State Hash: 0x%lX%lX\n", initialHash.first, initialHash.second);
printf("[] Full State Size: %lu bytes\n", fullStateSize);
printf("[] Lite State Size: %lu bytes\n", liteStateSize);
printf("[] Running Script: '%s'\n", scriptFilePath.c_str());
printf("[] Cycle Type: '%s'\n", cycleType.c_str());
printf("[] Emulation Core: '%s'\n", emulationCoreName.c_str());
printf("[] ROM File: '%s'\n", romFilePath.c_str());
printf("[] ROM SHA1: '%s'\n", romSHA1.c_str());
printf("[] Sequence File: '%s'\n", sequenceFilePath.c_str());
printf("[] Sequence Length: %lu\n", sequenceLength);
printf("[] Initial State Hash: 0x%lX%lX\n", initialHash.first, initialHash.second);
printf("[] Full State Size: %lu bytes\n", fullStateSize);
printf("[] Lite State Size: %lu bytes\n", liteStateSize);
printf("[] Lite State Disabled Blocks: [ %s ]\n", liteStateDisabledBlocksOutput.c_str());
printf("[] ********** Running Test **********\n");
fflush(stdout);

View File

@ -2,5 +2,6 @@
"Rom File": "../roms/Arkanoid (U) [!].nes",
"Expected ROM SHA1": "B2B30C4F30DD853C215C17B0C67CFE63D61A3062",
"Initial State File": "",
"Sequence File": "warpless.sol"
"Sequence File": "warpless.sol",
"Disable Lite State Blocks": [ ]
}

View File

@ -15,6 +15,7 @@ subdir('ninjaGaiden2')
subdir('ironSword')
subdir('solarJetman')
subdir('tennis')
subdir('metroid')
subdir('nigelMansell')
subdir('galaga')
subdir('saintSeiyaOugonDensetsu')

View File

@ -0,0 +1,4 @@
game = 'metroid'
goal = 'playaround'
test(goal, bash, workdir : meson.current_source_dir(), timeout: testTimeout, args : [ testCommands, goal + '.test', '--cycleType', 'Full'], suite: [ game, goal ] )

1730
tests/metroid/playaround.sol Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,6 @@
{
"Rom File": "../roms/Metroid (U) (PRG0) [!].nes",
"Expected ROM SHA1": "ECF39EC5A33E6A6F832F03E8FFC61C5D53F4F90B",
"Initial State File": "",
"Sequence File": "playaround.sol"
}