Merge pull request #3 from SergioMartin86/lightStates2

Added light states: ones that do not copy certain blocks of the NES state for faster/smaller serialization
This commit is contained in:
Sergio Martin 2024-01-20 21:02:17 +01:00 committed by GitHub
commit 9b276f8b70
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
123 changed files with 2464 additions and 570 deletions

View File

@ -27,3 +27,4 @@ jobs:
with:
name: meson-logs
path: build/meson-logs/

View File

@ -47,7 +47,7 @@ quickerNESTester = executable('quickerNESTester',
quickNESTester = executable('quickNESTester',
'source/tester.cpp',
cpp_args : [ commonCompileArgs, '-Wno-multichar', '-DDISABLE_AUTO_FILE', '-D__LIBRETRO__', '-DNDEBUG', '-DBLARGG_NONPORTABLE' ],
cpp_args : [ commonCompileArgs, '-w', '-DDISABLE_AUTO_FILE', '-D__LIBRETRO__', '-DNDEBUG', '-DBLARGG_NONPORTABLE' ],
dependencies : [ quickNESDependency, toolDependency ],
include_directories : include_directories(['../extern/json'])
)

View File

@ -64,38 +64,14 @@ class EmuInstance
std::string moveString = "|..|........|";
#endif
if (move & 0b00010000)
moveString += 'U';
else
moveString += '.';
if (move & 0b00100000)
moveString += 'D';
else
moveString += '.';
if (move & 0b01000000)
moveString += 'L';
else
moveString += '.';
if (move & 0b10000000)
moveString += 'R';
else
moveString += '.';
if (move & 0b00001000)
moveString += 'S';
else
moveString += '.';
if (move & 0b00000100)
moveString += 's';
else
moveString += '.';
if (move & 0b00000010)
moveString += 'B';
else
moveString += '.';
if (move & 0b00000001)
moveString += 'A';
else
moveString += '.';
if (move & 0b00010000) moveString += 'U'; else moveString += '.';
if (move & 0b00100000) moveString += 'D'; else moveString += '.';
if (move & 0b01000000) moveString += 'L'; else moveString += '.';
if (move & 0b10000000) moveString += 'R'; else moveString += '.';
if (move & 0b00001000) moveString += 'S'; else moveString += '.';
if (move & 0b00000100) moveString += 's'; else moveString += '.';
if (move & 0b00000010) moveString += 'B'; else moveString += '.';
if (move & 0b00000001) moveString += 'A'; else moveString += '.';
moveString += "|";
return moveString;
@ -108,8 +84,6 @@ class EmuInstance
advanceStateImpl(moveStringToCode(move), 0);
}
inline size_t getStateSize() const { return _stateSize; }
inline size_t getLiteStateSize() const { return _liteStateSize; }
inline std::string getRomSHA1() const { return _romSHA1String; }
inline hash_t getStateHash() const
@ -117,9 +91,9 @@ class EmuInstance
MetroHash128 hash;
hash.Update(getLowMem(), _LOW_MEM_SIZE);
hash.Update(getHighMem(), _HIGH_MEM_SIZE);
hash.Update(getNametableMem(), _NAMETABLES_MEM_SIZE);
hash.Update(getChrMem(), getChrMemSize());
// hash.Update(getHighMem(), _HIGH_MEM_SIZE);
// hash.Update(getNametableMem(), _NAMETABLES_MEM_SIZE);
// hash.Update(getChrMem(), getChrMemSize());
hash_t result;
hash.Finalize(reinterpret_cast<uint8_t *>(&result));
@ -134,14 +108,14 @@ class EmuInstance
std::string stateData;
bool status = loadStringFromFile(stateData, stateFilePath);
if (status == false) EXIT_WITH_ERROR("Could not find/read state file: %s\n", stateFilePath.c_str());
deserializeState((uint8_t *)stateData.data());
deserializeFullState((uint8_t *)stateData.data());
}
inline void saveStateFile(const std::string &stateFilePath) const
{
std::string stateData;
stateData.resize(_stateSize);
serializeState((uint8_t *)stateData.data());
stateData.resize(_fullStateSize);
serializeFullState((uint8_t *)stateData.data());
saveStringToFile(stateData, stateFilePath.c_str());
}
@ -159,10 +133,10 @@ class EmuInstance
if (status == false) EXIT_WITH_ERROR("Could not process ROM file: %s\n", romFilePath.c_str());
// Detecting full state size
_stateSize = getStateSizeImpl();
_fullStateSize = getFullStateSize();
// Detecting lite state size
_liteStateSize = getLiteStateSizeImpl();
_liteStateSize = getLiteStateSize();
}
// Virtual functions
@ -174,10 +148,15 @@ class EmuInstance
virtual uint8_t *getHighMem() const = 0;
virtual const uint8_t *getChrMem() const = 0;
virtual size_t getChrMemSize() const = 0;
virtual void serializeState(uint8_t *state) const = 0;
virtual void deserializeState(const uint8_t *state) = 0;
virtual size_t getStateSizeImpl() const = 0;
virtual size_t getLiteStateSizeImpl() const { return getStateSizeImpl(); };
virtual void serializeFullState(uint8_t *state) const = 0;
virtual void deserializeFullState(const uint8_t *state) = 0;
virtual void serializeLiteState(uint8_t *state) const = 0;
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;
@ -190,7 +169,7 @@ class EmuInstance
size_t _liteStateSize;
// Storage for the full state size
size_t _stateSize;
size_t _fullStateSize;
// Flag to determine whether to enable/disable rendering
bool _doRendering = true;

View File

@ -29,13 +29,16 @@ struct stepData_t
class PlaybackInstance
{
static const uint16_t image_width = 256;
static const uint16_t image_height = 240;
public:
void addStep(const std::string &input)
{
stepData_t step;
step.input = input;
step.stateData = (uint8_t *)malloc(_emu->getStateSize());
_emu->serializeState(step.stateData);
step.stateData = (uint8_t *)malloc(_emu->getFullStateSize());
_emu->serializeFullState(step.stateData);
step.hash = _emu->getStateHash();
// Adding the step into the sequence
@ -50,8 +53,8 @@ class PlaybackInstance
// Loading Emulator instance HQN
_hqnState.setEmulatorPointer(_emu->getInternalEmulatorPointer());
static uint8_t video_buffer[emulator_t::image_width * emulator_t::image_height];
_hqnState.m_emu->set_pixels(video_buffer, emulator_t::image_width + 8);
static uint8_t video_buffer[image_width * image_height];
_hqnState.m_emu->set_pixels(video_buffer, image_width + 8);
// Building sequence information
for (const auto &input : sequence)
@ -166,7 +169,7 @@ class PlaybackInstance
if (stepId > 0)
{
const auto stateData = getStateData(stepId - 1);
_emu->deserializeState(stateData);
_emu->deserializeFullState(stateData);
_emu->advanceState(getStateInput(stepId - 1));
}

View File

@ -103,7 +103,7 @@ int main(int argc, char *argv[])
auto p = PlaybackInstance(&e, sequence);
// Getting state size
auto stateSize = e.getStateSize();
auto stateSize = e.getFullStateSize();
// Flag to continue running playback
bool continueRunning = true;

View File

@ -45,20 +45,37 @@ class QuickNESInstance : public EmuInstance
const uint8_t *getChrMem() const override { return _nes->chr_mem(); };
size_t getChrMemSize() const override { return _nes->chr_size(); };
void serializeState(uint8_t *state) const override
void serializeLiteState(uint8_t *state) const override { serializeFullState(state); }
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, _stateSize, 0);
Mem_Writer w(state, _fullStateSize, 0);
Auto_File_Writer a(w);
_nes->save_state(a);
}
void deserializeState(const uint8_t *state) override
void deserializeFullState(const uint8_t *state) override
{
Mem_File_Reader r(state, _stateSize);
Mem_File_Reader r(state, _fullStateSize);
Auto_File_Reader a(r);
_nes->load_state(a);
}
inline size_t getFullStateSize() const override
{
uint8_t *data = (uint8_t *)malloc(_DUMMY_SIZE);
Mem_Writer w(data, _DUMMY_SIZE);
Auto_File_Writer a(w);
_nes->save_state(a);
free(data);
return w.size();
}
void advanceStateImpl(const inputType controller1, const inputType controller2) override
{
if (_doRendering == true) _nes->emulate_frame(controller1, controller2);
@ -72,15 +89,6 @@ class QuickNESInstance : public EmuInstance
void *getInternalEmulatorPointer() const override { return _nes; }
private:
inline size_t getStateSizeImpl() const override
{
uint8_t *data = (uint8_t *)malloc(_DUMMY_SIZE);
Mem_Writer w(data, _DUMMY_SIZE);
Auto_File_Writer a(w);
_nes->save_state(a);
free(data);
return w.size();
}
// Video buffer
uint8_t *video_buffer;

View File

@ -1,11 +1,11 @@
// Blip_Buffer 0.4.0. http://www.slack.net/~ant/
#include "Blip_Buffer.hpp"
#include <limits.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <climits>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include "blipBuffer.hpp"
/* Copyright (C) 2003-2006 Shay Green. This module is free software; you
can redistribute it and/or modify it under the terms of the GNU Lesser

View File

@ -3,8 +3,8 @@
// NES non-linear audio buffer
// Emu 0.7.0
#include "Multi_Buffer.hpp"
#include <cstdint>
#include "multiBuffer.hpp"
namespace quickerNES
{

View File

@ -3,8 +3,8 @@
// Multi-channel effects buffer with panning, echo and reverb
// Game_Music_Emu 0.3.0
#include "Multi_Buffer.hpp"
#include <stdint.h>
#include "multiBuffer.hpp"
namespace quickerNES
{

View File

@ -4,7 +4,7 @@
// Emu 0.7.0
#include <cstdint>
#include "apu/Blip_Buffer.hpp"
#include "apu/blipBuffer.hpp"
namespace quickerNES
{

View File

@ -1,8 +1,8 @@
// Blip_Buffer 0.4.0. http://www.slack.net/~ant/
#include "Multi_Buffer.hpp"
#include <cstdint>
#include "multiBuffer.hpp"
/* Copyright (C) 2003-2006 Shay Green. This module is free software; you
can redistribute it and/or modify it under the terms of the GNU Lesser

View File

@ -4,7 +4,7 @@
// Multi-channel sound buffer interface, and basic mono and stereo buffers
// Blip_Buffer 0.4.0
#include "Blip_Buffer.hpp"
#include "blipBuffer.hpp"
namespace quickerNES
{

View File

@ -2,7 +2,7 @@
// Snd_Emu 0.1.7. http://www.slack.net/~ant/
#include "apu/namco/apu.hpp"
#include "apu/Blip_Buffer.hpp"
#include "apu/blipBuffer.hpp"
/* Copyright (C) 2003-2006 Shay Green. This module is free software; you
can redistribute it and/or modify it under the terms of the GNU Lesser

View File

@ -4,7 +4,7 @@
// Private oscillators used by Apu
// Snd_Emu 0.1.7
#include "Blip_Buffer.hpp"
#include "blipBuffer.hpp"
namespace quickerNES
{

View File

@ -5,7 +5,7 @@
// Snd_Emu 0.1.7
#include <cstdint>
#include "apu/Blip_Buffer.hpp"
#include "apu/blipBuffer.hpp"
#include "apu/apu.hpp"
namespace quickerNES

View File

@ -5,7 +5,7 @@
// Snd_Emu 0.1.7. Copyright (C) 2003-2005 Shay Green. GNU LGPL license.
#include <cstdint>
#include "apu/Blip_Buffer.hpp"
#include "apu/blipBuffer.hpp"
#include "apu/vrc7/emu2413_state.hpp"
namespace quickerNES

View File

@ -48,6 +48,13 @@ struct nes_state_t
uint32_t frame_count; // number of frames emulated since power-up
};
struct nes_state_lite_t
{
uint16_t timestamp; // CPU clocks * 15 (for NTSC)
uint8_t frame_count; // number of frames emulated since power-up
};
struct joypad_state_t
{
uint32_t joypad_latches[2]; // joypad 1 & 2 shift registers
@ -73,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;
@ -129,93 +150,7 @@ class Core : private Cpu
reset(true, true);
}
size_t getLiteStateSize() const
{
size_t size = 0;
size += sizeof(nes_state_t);
size += sizeof(registers_t);
size += sizeof(ppu_state_t);
size += sizeof(Apu::apu_state_t);
size += sizeof(joypad_state_t);
size += mapper->state_size;
size += low_ram_size;
size += 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;
}
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(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 += 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 serializeFullState(uint8_t *buffer) const
{
size_t pos = 0;
std::string headerCode;
@ -225,9 +160,9 @@ class Core : private Cpu
headerCode = "NESS"; // NESS Block
blockSize = 0xFFFFFFFF;
memcpy(&buffer[pos], headerCode.data(), headerSize);
if (buffer != nullptr) memcpy(&buffer[pos], headerCode.data(), headerSize);
pos += headerSize;
memcpy(&buffer[pos], &blockSize, headerSize);
if (buffer != nullptr) memcpy(&buffer[pos], &blockSize, headerSize);
pos += headerSize;
headerCode = "TIME"; // TIME Block
@ -235,11 +170,11 @@ class Core : private Cpu
state.timestamp *= 5;
blockSize = sizeof(nes_state_t);
dataSource = (void *)&state;
memcpy(&buffer[pos], headerCode.data(), headerSize);
if (buffer != nullptr) memcpy(&buffer[pos], headerCode.data(), headerSize);
pos += headerSize;
memcpy(&buffer[pos], &blockSize, headerSize);
if (buffer != nullptr) memcpy(&buffer[pos], &blockSize, headerSize);
pos += headerSize;
memcpy(&buffer[pos], dataSource, blockSize);
if (buffer != nullptr) memcpy(&buffer[pos], dataSource, blockSize);
pos += blockSize;
headerCode = "CPUR"; // CPUR Block
@ -253,72 +188,72 @@ class Core : private Cpu
s.p = r.status;
blockSize = sizeof(cpu_state_t);
dataSource = (void *)&s;
memcpy(&buffer[pos], headerCode.data(), headerSize);
if (buffer != nullptr) memcpy(&buffer[pos], headerCode.data(), headerSize);
pos += headerSize;
memcpy(&buffer[pos], &blockSize, headerSize);
if (buffer != nullptr) memcpy(&buffer[pos], &blockSize, headerSize);
pos += headerSize;
memcpy(&buffer[pos], dataSource, blockSize);
if (buffer != nullptr) 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);
if (buffer != nullptr) memcpy(&buffer[pos], headerCode.data(), headerSize);
pos += headerSize;
memcpy(&buffer[pos], &blockSize, headerSize);
if (buffer != nullptr) memcpy(&buffer[pos], &blockSize, headerSize);
pos += headerSize;
memcpy(&buffer[pos], dataSource, blockSize);
if (buffer != nullptr) memcpy(&buffer[pos], dataSource, blockSize);
pos += blockSize;
headerCode = "APUR"; // APUR Block
Apu::apu_state_t apuState;
impl->apu.save_state(&apuState);
blockSize = sizeof(Apu::apu_state_t);
memcpy(&buffer[pos], headerCode.data(), headerSize);
if (buffer != nullptr) memcpy(&buffer[pos], headerCode.data(), headerSize);
pos += headerSize;
memcpy(&buffer[pos], &blockSize, headerSize);
if (buffer != nullptr) memcpy(&buffer[pos], &blockSize, headerSize);
pos += headerSize;
memcpy(&buffer[pos], &apuState, blockSize);
if (buffer != nullptr) 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);
if (buffer != nullptr) memcpy(&buffer[pos], headerCode.data(), headerSize);
pos += headerSize;
memcpy(&buffer[pos], &blockSize, headerSize);
if (buffer != nullptr) memcpy(&buffer[pos], &blockSize, headerSize);
pos += headerSize;
memcpy(&buffer[pos], dataSource, blockSize);
if (buffer != nullptr) 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);
if (buffer != nullptr) memcpy(&buffer[pos], headerCode.data(), headerSize);
pos += headerSize;
memcpy(&buffer[pos], &blockSize, headerSize);
if (buffer != nullptr) memcpy(&buffer[pos], &blockSize, headerSize);
pos += headerSize;
memcpy(&buffer[pos], dataSource, blockSize);
if (buffer != nullptr) 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);
if (buffer != nullptr) memcpy(&buffer[pos], headerCode.data(), headerSize);
pos += headerSize;
memcpy(&buffer[pos], &blockSize, headerSize);
if (buffer != nullptr) memcpy(&buffer[pos], &blockSize, headerSize);
pos += headerSize;
memcpy(&buffer[pos], dataSource, blockSize);
if (buffer != nullptr) memcpy(&buffer[pos], dataSource, blockSize);
pos += blockSize;
headerCode = "SPRT"; // SPRT Block
blockSize = Ppu::spr_ram_size;
dataSource = (void *)ppu.spr_ram;
memcpy(&buffer[pos], headerCode.data(), headerSize);
if (buffer != nullptr) memcpy(&buffer[pos], headerCode.data(), headerSize);
pos += headerSize;
memcpy(&buffer[pos], &blockSize, headerSize);
if (buffer != nullptr) memcpy(&buffer[pos], &blockSize, headerSize);
pos += headerSize;
memcpy(&buffer[pos], dataSource, blockSize);
if (buffer != nullptr) memcpy(&buffer[pos], dataSource, blockSize);
pos += blockSize;
headerCode = "NTAB"; // NTAB Block
@ -326,11 +261,11 @@ class Core : private Cpu
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);
if (buffer != nullptr) memcpy(&buffer[pos], headerCode.data(), headerSize);
pos += headerSize;
memcpy(&buffer[pos], &blockSize, headerSize);
if (buffer != nullptr) memcpy(&buffer[pos], &blockSize, headerSize);
pos += headerSize;
memcpy(&buffer[pos], dataSource, blockSize);
if (buffer != nullptr) memcpy(&buffer[pos], dataSource, blockSize);
pos += blockSize;
if (ppu.chr_is_writable)
@ -338,11 +273,11 @@ class Core : private Cpu
headerCode = "CHRR"; // CHRR Block
blockSize = ppu.chr_size;
dataSource = (void *)ppu.impl->chr_ram;
memcpy(&buffer[pos], headerCode.data(), headerSize);
if (buffer != nullptr) memcpy(&buffer[pos], headerCode.data(), headerSize);
pos += headerSize;
memcpy(&buffer[pos], &blockSize, headerSize);
if (buffer != nullptr) memcpy(&buffer[pos], &blockSize, headerSize);
pos += headerSize;
memcpy(&buffer[pos], dataSource, blockSize);
if (buffer != nullptr) memcpy(&buffer[pos], dataSource, blockSize);
pos += blockSize;
}
@ -351,25 +286,25 @@ class Core : private Cpu
headerCode = "SRAM"; // SRAM Block
blockSize = impl->sram_size;
dataSource = (void *)impl->sram;
memcpy(&buffer[pos], headerCode.data(), headerSize);
if (buffer != nullptr) memcpy(&buffer[pos], headerCode.data(), headerSize);
pos += headerSize;
memcpy(&buffer[pos], &blockSize, headerSize);
if (buffer != nullptr) memcpy(&buffer[pos], &blockSize, headerSize);
pos += headerSize;
memcpy(&buffer[pos], dataSource, blockSize);
if (buffer != nullptr) memcpy(&buffer[pos], dataSource, blockSize);
pos += blockSize;
}
headerCode = "gend"; // gend Block
blockSize = 0;
memcpy(&buffer[pos], headerCode.data(), headerSize);
if (buffer != nullptr) memcpy(&buffer[pos], headerCode.data(), headerSize);
pos += headerSize;
memcpy(&buffer[pos], &blockSize, headerSize);
if (buffer != nullptr) memcpy(&buffer[pos], &blockSize, headerSize);
pos += headerSize;
return pos; // Bytes written
}
size_t deserializeState(const uint8_t *buffer)
size_t deserializeFullState(const uint8_t *buffer)
{
disable_rendering();
error_count = 0;
@ -491,6 +426,275 @@ 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;
if (TIMEBlockEnabled == true)
{
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 (CPURBlockEnabled == true)
{
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
}
size_t deserializeLiteState(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;
uint32_t blockSize = 0;
// TIME Block
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
if (CPURBlockEnabled == true)
{
blockSize = sizeof(cpu::registers_t);
memcpy((void *)&r, &buffer[pos], blockSize);
pos += blockSize;
}
// PPUR Block
if (PPURBlockEnabled == true)
{
blockSize = sizeof(ppu_state_t);
memcpy((void *)&ppu, &buffer[pos], blockSize);
pos += blockSize;
}
// APUR Block
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
if (CTRLBlockEnabled == true)
{
blockSize = sizeof(joypad_state_t);
memcpy((void *)&joypad, &buffer[pos], blockSize);
pos += blockSize;
}
// MAPR Block
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
if (LRAMBlockEnabled == true)
{
blockSize = low_ram_size;
memcpy((void *)low_mem, &buffer[pos], blockSize);
pos += blockSize;
}
// SPRT Block
if (SPRTBlockEnabled == true)
{
blockSize = Ppu::spr_ram_size;
memcpy((void *)ppu.spr_ram, &buffer[pos], blockSize);
pos += blockSize;
}
// NTAB Block
if (NTABBlockEnabled == true)
{
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 (CHRRBlockEnabled == 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
}
void reset(bool full_reset, bool erase_battery_ram)
{
if (full_reset)

View File

@ -121,10 +121,11 @@ imm##op: \
#define BRANCH( cond ) \
{ \
pc++; \
if ( (cond) == false ) [[likely]] {clock_count--; goto loop; } \
int32_t offset = (int8_t) data; \
int32_t extra_clock = (pc & 0xFF) + offset; \
int offset = (int8_t) data; \
int extra_clock = (pc & 0xFF) + offset; \
if ( !(cond) ) {clock_count--; goto loop; } \
pc += offset; \
pc = uint16_t( pc ); \
clock_count += (extra_clock >> 8) & 1; \
goto loop; \
}
@ -201,7 +202,26 @@ inline void Cpu::write( nes_addr_t addr, int value )
}
// status flags
extern uint8_t clock_table [256];
uint8_t clock_table [256] = {
// 0 1 2 3 4 5 6 7 8 9 A B C D E F
7,6,2,8,3,3,5,5,3,2,2,2,4,4,6,6,// 0
3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 1
6,6,2,8,3,3,5,5,4,2,2,2,4,4,6,6,// 2
3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 3
6,6,2,8,3,3,5,5,3,2,2,2,3,4,6,6,// 4
3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 5
6,6,2,8,3,3,5,5,4,2,2,2,5,4,6,6,// 6
3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 7
2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4,// 8
3,6,2,6,4,4,4,4,2,5,2,5,5,5,5,5,// 9
2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4,// A
3,5,2,5,4,4,4,4,2,4,2,4,4,4,4,4,// B
2,6,2,8,3,3,5,5,2,2,2,2,4,4,6,6,// C
3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// D
2,6,2,8,3,3,5,5,2,2,2,2,4,4,6,6,// E
3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7 // F
};
__attribute__((optimize("align-functions=" _PAGE_SIZE)))
Cpu::result_t Cpu::run ( nes_time_t end )
@ -1148,24 +1168,5 @@ end:
return result;
}
uint8_t clock_table [256] = {
// 0 1 2 3 4 5 6 7 8 9 A B C D E F
7,6,2,8,3,3,5,5,3,2,2,2,4,4,6,6,// 0
3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 1
6,6,2,8,3,3,5,5,4,2,2,2,4,4,6,6,// 2
3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 3
6,6,2,8,3,3,5,5,3,2,2,2,3,4,6,6,// 4
3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 5
6,6,2,8,3,3,5,5,4,2,2,2,5,4,6,6,// 6
3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 7
2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4,// 8
3,6,2,6,4,4,4,4,2,5,2,5,5,5,5,5,// 9
2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4,// A
3,5,2,5,4,4,4,4,2,4,2,4,4,4,4,4,// B
2,6,2,8,3,3,5,5,2,2,2,2,4,4,6,6,// C
3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// D
2,6,2,8,3,3,5,5,2,2,2,2,4,4,6,6,// E
3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7 // F
};
} // namespace quickNES

View File

@ -6,7 +6,7 @@
#include "cart.hpp"
#include "core.hpp"
#include "apu/Multi_Buffer.hpp"
#include "apu/multiBuffer.hpp"
namespace quickerNES
{
@ -44,8 +44,18 @@ class Emu
const uint8_t *getHostPixels() const { return emu.ppu.host_pixels; }
size_t getLiteStateSize() const { return emu.getLiteStateSize(); }
size_t getStateSize() const { return emu.getStateSize(); }
// Save emulator state variants
size_t serializeFullState(uint8_t *buffer) const { return emu.serializeFullState(buffer); }
size_t serializeLiteState(uint8_t *buffer) const { return emu.serializeLiteState(buffer); }
size_t deserializeFullState(const uint8_t *buffer) { return emu.deserializeFullState(buffer); }
size_t deserializeLiteState(const uint8_t *buffer) { return emu.deserializeLiteState(buffer); }
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
@ -141,10 +151,6 @@ class Emu
// File save/load
// Save emulator state
size_t serializeState(uint8_t *buffer) const { return emu.serializeState(buffer); }
size_t deserializeState(const uint8_t *buffer) { return emu.deserializeState(buffer); }
// True if current cartridge claims it uses battery-backed memory
bool has_battery_ram() const { return cart()->has_battery_ram(); }
@ -231,8 +237,8 @@ class Emu
virtual const char *init_();
virtual void loading_state(State const &) {}
long timestamp() const { return emu.nes.frame_count; }
void set_timestamp(long t) { emu.nes.frame_count = t; }
long timestamp() const { return 0; }
void set_timestamp(long t) { }
private:
// noncopyable

View File

@ -4,10 +4,10 @@ quickerNESAPUSrc = [
'apu/apu.cpp',
'apu/oscs.cpp',
'apu/buffer.cpp',
'apu/Blip_Buffer.cpp',
'apu/blipBuffer.cpp',
'apu/NESEffectsBuffer.cpp',
'apu/effectsBuffer.cpp',
'apu/Multi_Buffer.cpp',
'apu/multiBuffer.cpp',
'apu/namco/apu.cpp',
'apu/vrc6/apu.cpp',
'apu/vrc7/emu2413.cpp',

View File

@ -36,15 +36,17 @@ class QuickerNESInstance : public EmuInstance
const uint8_t *getChrMem() const override { return _nes->chr_mem(); };
size_t getChrMemSize() const override { return _nes->chr_size(); };
void serializeState(uint8_t *state) const override
{
_nes->serializeState(state);
}
void serializeFullState(uint8_t *state) const override { _nes->serializeFullState(state); }
void deserializeFullState(const uint8_t *state) override { _nes->deserializeFullState(state); }
void deserializeState(const uint8_t *state) override
{
_nes->deserializeState(state);
}
void serializeLiteState(uint8_t *state) const override { _nes->serializeLiteState(state); }
void deserializeLiteState(const uint8_t *state) override { _nes->deserializeLiteState(state); }
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
{
@ -58,17 +60,6 @@ class QuickerNESInstance : public EmuInstance
void *getInternalEmulatorPointer() const override { return _nes; }
private:
inline size_t getStateSizeImpl() const override
{
return _nes->getStateSize();
}
inline size_t getLiteStateSizeImpl() const override
{
return _nes->getLiteStateSize();
}
// Video buffer
uint8_t *video_buffer;

View File

@ -4,6 +4,8 @@
#include "utils.hpp"
#include <chrono>
#include <sstream>
#include <vector>
#include <string>
#ifdef _USE_QUICKNES
#include "quickNESInstance.hpp"
@ -76,14 +78,29 @@ 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>();
// Creating emulator instance
#ifdef _USE_QUICKNES
auto e = QuickNESInstance();
#endif
// Parsing disabled blocks in lite state serialization
std::vector<std::string> stateDisabledBlocks;
std::string stateDisabledBlocksOutput;
if (scriptJson.contains("Disable State Blocks") == false) EXIT_WITH_ERROR("Script file missing 'Disable State Blocks' entry\n");
if (scriptJson["Disable State Blocks"].is_array() == false) EXIT_WITH_ERROR("Script file 'Disable State Blocks' is not an array\n");
for (const auto& entry : scriptJson["Disable State Blocks"])
{
if (entry.is_string() == false) EXIT_WITH_ERROR("Script file 'Disable State Blocks' entry is not a string\n");
stateDisabledBlocks.push_back(entry.get<std::string>());
stateDisabledBlocksOutput += entry.get<std::string>() + std::string(" ");
}
#ifdef _USE_QUICKERNES
// Creating emulator instance
#ifdef _USE_QUICKNES
auto e = QuickNESInstance();
#endif
#ifdef _USE_QUICKERNES
auto e = quickerNES::QuickerNESInstance();
#endif
#endif
// Disabling requested blocks from light state serialization
for (const auto& block : stateDisabledBlocks) e.disableLiteStateBlock(block);
// Loading ROM File
e.loadROMFile(romFilePath);
@ -94,12 +111,6 @@ int main(int argc, char *argv[])
// Disable rendering
e.disableRendering();
// Getting initial hash
auto initialHash = e.getStateHash();
// Getting full state size
const auto stateSize = e.getStateSize();
// Getting lite state size
const auto liteStateSize = e.getLiteStateSize();
@ -128,19 +139,22 @@ int main(int argc, char *argv[])
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("[] 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", stateSize);
printf("[] Lite State Size: %lu bytes\n", liteStateSize);
#ifdef _USE_QUICKNES
printf("[] State Size: %lu bytes\n", e.getFullStateSize());
#endif
#ifdef _USE_QUICKERNES
printf("[] State Size: %lu bytes - Disabled Blocks: [ %s ]\n", e.getLiteStateSize(), stateDisabledBlocksOutput.c_str());
#endif
printf("[] ********** Running Test **********\n");
fflush(stdout);
// Serializing initial state
uint8_t *currentState = (uint8_t *)malloc(stateSize);
e.serializeState(currentState);
uint8_t *currentState = (uint8_t *)malloc(liteStateSize);
e.serializeLiteState(currentState);
// Check whether to perform each action
bool doPreAdvance = cycleType == "Full";
@ -152,9 +166,9 @@ int main(int argc, char *argv[])
for (const std::string &input : sequence)
{
if (doPreAdvance == true) e.advanceState(input);
if (doDeserialize == true) e.deserializeState(currentState);
if (doDeserialize == true) e.deserializeLiteState(currentState);
e.advanceState(input);
if (doSerialize == true) e.serializeState(currentState);
if (doSerialize == true) e.serializeLiteState(currentState);
}
auto tf = std::chrono::high_resolution_clock::now();

1
tests/.gitignore vendored
View File

@ -5,4 +5,5 @@
test.sol
*.hash
*.out
*.sh

View File

@ -1 +1,20 @@
Some of the sequences hosted in this folder correspond to movies published in tasvideos.org under the Creative Commons 2.0 license. These movies were created by the author of this code (Sergio Martin, a.k.a. eien86), unless stated otherwise.
Some of the sequences hosted in this folder correspond to movies published in tasvideos.org under the Creative Commons 2.0 license. These movies were created by the author of this code (Sergio Martin, a.k.a. eien86) and collaborators, unless otherwise stated.
Notes:
Sprilo is a open-source free game. This rom was obtained from
- https://itch.io/jam/game-off-2017
- https://github.com/cbrwn/gameoff
- We use the movie https://tasvideos.org/3557G by MrTASer distributed under the CC2.0 license
Nova The Squirrel is a open-source free game. This rom was obtained from
- https://github.com/NovaSquirrel/NovaTheSquirrel
- https://novasquirrel.itch.io/nova-the-squirrel
- We use the movie https://tasvideos.org/5246M by Cephla distributed under the CC2.0 license
For Saiyuuki World
- We use the movie https://tasvideos.org/4422M by aiqiyou & J.Y distributed under the CC2.0 license
For Metroid
- We use part of the movie https://tasvideos.org/3666M by The8bitbeast distributed under the CC2.0 license

View File

@ -0,0 +1,7 @@
{
"Rom File": "roms/Arkanoid (U) [!].nes",
"Expected ROM SHA1": "B2B30C4F30DD853C215C17B0C67CFE63D61A3062",
"Initial State File": "",
"Sequence File": "arkanoid.warpless.sol",
"Disable State Blocks": [ "SRAM", "CHRR", "NTAB", "SPRT", "MAPR", "CTRL", "APUR" ]
}

View File

@ -0,0 +1,7 @@
{
"Rom File": "roms/Arkanoid (U) [!].nes",
"Expected ROM SHA1": "B2B30C4F30DD853C215C17B0C67CFE63D61A3062",
"Initial State File": "",
"Sequence File": "arkanoid.warps.sol",
"Disable State Blocks": [ "SRAM", "CHRR", "NTAB", "SPRT", "MAPR", "CTRL", "APUR" ]
}

View File

@ -1,9 +0,0 @@
game = 'arkanoid'
bash = find_program('bash')
goal = 'warpless'
test(goal, bash, workdir : meson.current_source_dir(), timeout: testTimeout, args : [ testCommands, goal + '.test', '--cycleType', 'Full'], suite: [ game, goal ] )
goal = 'warps'
test(goal, bash, workdir : meson.current_source_dir(), timeout: testTimeout, args : [ testCommands, goal + '.test', '--cycleType', 'Full'], suite: [ game, goal ] )

View File

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

View File

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

View File

@ -0,0 +1,7 @@
{
"Rom File": "roms/Castlevania (U) (PRG0) [!].nes",
"Expected ROM SHA1": "A31B8BD5B370A9103343C866F3C2B2998E889341",
"Initial State File": "",
"Sequence File": "castlevania1.anyPercent.sol",
"Disable State Blocks": [ "SRAM", "CHRR", "NTAB", "SPRT", "CTRL" ]
}

View File

@ -0,0 +1,7 @@
{
"Rom File": "roms/Castlevania (U) (PRG0) [!].nes",
"Expected ROM SHA1": "A31B8BD5B370A9103343C866F3C2B2998E889341",
"Initial State File": "",
"Sequence File": "castlevania1.pacifist.sol",
"Disable State Blocks": [ "SRAM", "CHRR", "NTAB", "SPRT", "CTRL" ]
}

View File

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

View File

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

View File

@ -1,7 +0,0 @@
{
"Rom File": "../roms/Castlevania (U) (PRG0) [!].nes",
"Expected ROM SHA1": "A31B8BD5B370A9103343C866F3C2B2998E889341",
"Initial State File": "",
"Verification State File": "pacifist.final.state",
"Sequence File": "pacifist.sol"
}

View File

@ -0,0 +1,7 @@
{
"Rom File": "roms/Galaga - Demons of Death (U) [!].nes",
"Expected ROM SHA1": "DA54C223D79FA59EB95437854B677CF69B5CAC8A",
"Initial State File": "",
"Sequence File": "galaga.anyPercent.sol",
"Disable State Blocks": [ "SRAM", "CHRR", "NTAB", "SPRT", "CTRL" ]
}

View File

@ -1,6 +0,0 @@
{
"Rom File": "../roms/Galaga - Demons of Death (U) [!].nes",
"Expected ROM SHA1": "DA54C223D79FA59EB95437854B677CF69B5CAC8A",
"Initial State File": "",
"Sequence File": "anyPercent.sol"
}

View File

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

View File

@ -0,0 +1,7 @@
{
"Rom File": "roms/Ironsword - Wizards & Warriors II (U) [!].nes",
"Expected ROM SHA1": "97B79E432F62403FB9F877090850C41112A9A168",
"Initial State File": "",
"Sequence File": "ironSword.anyPercent.sol",
"Disable State Blocks": [ "SRAM", "CHRR", "NTAB", "SPRT", "CTRL" ]
}

View File

@ -1,6 +0,0 @@
{
"Rom File": "../roms/Ironsword - Wizards & Warriors II (U) [!].nes",
"Expected ROM SHA1": "97B79E432F62403FB9F877090850C41112A9A168",
"Initial State File": "",
"Sequence File": "anyPercent.sol"
}

View File

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

View File

@ -1,30 +1,59 @@
nomalloc = environment({'MALLOC_PERTURB_': '0'})
bash = find_program('bash')
testCommands = ['../run_test.sh', quickerNESTester.path(), quickNESTester.path() ]
testCommands = ['run_test.sh', quickerNESTester.path(), quickNESTester.path() ]
testTimeout = 120
# Tests for copyrighted game roms (only for local testing or own CI runners)
protectedTestSet = [
'arkanoid.warpless.test',
'arkanoid.warps.test',
'castlevania1.anyPercent.test',
'castlevania1.pacifist.test',
'galaga.anyPercent.test',
'ironSword.anyPercent.test',
'metroid.playaround.test',
'nigelMansell.anyPercent.test',
'ninjaGaiden.anyPercent.test',
'ninjaGaiden.pacifist.test',
'ninjaGaiden2.anyPercent.test',
'ninjaGaiden2.pacifist.test',
'novaTheSquirrel.anyPercent.test',
'princeOfPersia.anyPercent.test',
'saintSeiyaKanketsuHen.anyPercent.test',
'saintSeiyaOugonDensetsu.anyPercent.test',
'saiyuukiWorld.anyPercent.test',
'saiyuukiWorld.lastHalf.test',
'solarJetman.anyPercent.test',
'sprilo.anyPercent.test',
'superMarioBros.warpless.test',
'superMarioBros.warps.test',
'superMarioBros3.warps.test',
'superOffroad.anyPercent.test',
'tennis.anyPercent.test',
]
# Tests for open source free roms (for public cloud testing)
openSourceTestSet = [
'novaTheSquirrel.anyPercent.test',
'sprilo.anyPercent.test',
]
# Creating test set based on whether copyrighted roms are to be used
testSet = openSourceTestSet
if get_option('onlyOpenSource') == false
subdir('arkanoid')
subdir('castlevania1')
subdir('superOffroad')
subdir('princeOfPersia')
subdir('ninjaGaiden')
subdir('ninjaGaiden2')
subdir('ironSword')
subdir('solarJetman')
subdir('tennis')
subdir('nigelMansell')
subdir('galaga')
subdir('saintSeiyaOugonDensetsu')
subdir('saintSeiyaKanketsuHen')
subdir('superMarioBros')
subdir('superMarioBros3')
subdir('saiyuukiWorld')
testSet += protectedTestSet
endif
# Open source games
subdir('sprilo')
subdir('novaTheSquirrel')
# Adding tests to the suite
foreach testFile : testSet
testSuite = testFile.split('.')[0]
testName = testFile.split('.')[1]
test(testName,
bash,
workdir : meson.current_source_dir(),
timeout: testTimeout,
args : [ testCommands, testFile, '--cycleType', 'Full'],
suite : [ testSuite ])
endforeach

1730
tests/metroid.playaround.sol Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,7 @@
{
"Rom File": "roms/Metroid (U) (PRG0) [!].nes",
"Expected ROM SHA1": "ECF39EC5A33E6A6F832F03E8FFC61C5D53F4F90B",
"Initial State File": "",
"Sequence File": "metroid.playaround.sol",
"Disable State Blocks": [ "CHRR", "NTAB", "SPRT", "CTRL" ]
}

View File

@ -0,0 +1,7 @@
{
"Rom File": "roms/Nigel Mansell's World Championship Challenge (U) [!].nes",
"Expected ROM SHA1": "BBE5CF2DFA0B5422776A530D6F1B617238A8569F",
"Initial State File": "",
"Sequence File": "nigelMansell.anyPercent.sol",
"Disable State Blocks": [ "SRAM", "CHRR", "SPRT", "CTRL" ]
}

View File

@ -1,6 +0,0 @@
{
"Rom File": "../roms/Nigel Mansell's World Championship Challenge (U) [!].nes",
"Expected ROM SHA1": "BBE5CF2DFA0B5422776A530D6F1B617238A8569F",
"Initial State File": "",
"Sequence File": "anyPercent.sol"
}

View File

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

View File

@ -0,0 +1,7 @@
{
"Rom File": "roms/Ninja Gaiden (U) [!].nes",
"Expected ROM SHA1": "CA513F841D75EFEB33BB8099FB02BEEB39F6BB9C",
"Initial State File": "",
"Sequence File": "ninjaGaiden.anyPercent.sol",
"Disable State Blocks": [ "SRAM", "CHRR", "NTAB", "SPRT", "CTRL" ]
}

View File

@ -0,0 +1,7 @@
{
"Rom File": "roms/Ninja Gaiden (U) [!].nes",
"Expected ROM SHA1": "CA513F841D75EFEB33BB8099FB02BEEB39F6BB9C",
"Initial State File": "",
"Sequence File": "ninjaGaiden.pacifist.sol",
"Disable State Blocks": [ "SRAM", "CHRR", "NTAB", "SPRT", "CTRL" ]
}

View File

@ -1,6 +0,0 @@
{
"Rom File": "../roms/Ninja Gaiden (U) [!].nes",
"Expected ROM SHA1": "CA513F841D75EFEB33BB8099FB02BEEB39F6BB9C",
"Initial State File": "",
"Sequence File": "anyPercent.sol"
}

View File

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

View File

@ -1,6 +0,0 @@
{
"Rom File": "../roms/Ninja Gaiden (U) [!].nes",
"Expected ROM SHA1": "CA513F841D75EFEB33BB8099FB02BEEB39F6BB9C",
"Initial State File": "",
"Sequence File": "pacifist.sol"
}

View File

@ -0,0 +1,7 @@
{
"Rom File": "roms/Ninja Gaiden II - The Dark Sword of Chaos (U) [!].nes",
"Expected ROM SHA1": "B1796660E4A4CEFC72181D4BF4F97999BC048A77",
"Initial State File": "",
"Sequence File": "ninjaGaiden2.anyPercent.sol",
"Disable State Blocks": [ "SRAM", "CHRR", "NTAB", "SPRT", "CTRL" ]
}

View File

@ -0,0 +1,7 @@
{
"Rom File": "roms/Ninja Gaiden II - The Dark Sword of Chaos (U) [!].nes",
"Expected ROM SHA1": "B1796660E4A4CEFC72181D4BF4F97999BC048A77",
"Initial State File": "",
"Sequence File": "ninjaGaiden2.pacifist.sol",
"Disable State Blocks": [ "SRAM", "CHRR", "NTAB", "SPRT", "CTRL" ]
}

View File

@ -1,6 +0,0 @@
{
"Rom File": "../roms/Ninja Gaiden II - The Dark Sword of Chaos (U) [!].nes",
"Expected ROM SHA1": "B1796660E4A4CEFC72181D4BF4F97999BC048A77",
"Initial State File": "",
"Sequence File": "anyPercent.sol"
}

View File

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

View File

@ -1,6 +0,0 @@
{
"Rom File": "../roms/Ninja Gaiden II - The Dark Sword of Chaos (U) [!].nes",
"Expected ROM SHA1": "B1796660E4A4CEFC72181D4BF4F97999BC048A77",
"Initial State File": "",
"Sequence File": "pacifist.sol"
}

View File

@ -0,0 +1,7 @@
{
"Rom File": "roms/nova.nes",
"Expected ROM SHA1": "B6B07EE76492ED475F39167C89B342353F999231",
"Initial State File": "",
"Sequence File": "novaTheSquirrel.anyPercent.sol",
"Disable State Blocks": [ "CHRR", "NTAB", "SPRT", "CTRL" ]
}

View File

@ -1,7 +0,0 @@
Nova The Squirrel is a open-source free game. This rom was obtained from
https://github.com/NovaSquirrel/NovaTheSquirrel
https://novasquirrel.itch.io/nova-the-squirrel
We use the movie https://tasvideos.org/5246M by Cephla distributed under the CC2.0 license

View File

@ -1,6 +0,0 @@
{
"Rom File": "../roms/nova.nes",
"Expected ROM SHA1": "B6B07EE76492ED475F39167C89B342353F999231",
"Initial State File": "",
"Sequence File": "anyPercent.sol"
}

View File

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

View File

@ -0,0 +1,7 @@
{
"Rom File": "roms/Prince of Persia (U) [!].nes",
"Expected ROM SHA1": "6B58F149F34FA829135619C58700CAAA95B9CDE3",
"Initial State File": "",
"Sequence File": "princeOfPersia.anyPercent.sol",
"Disable State Blocks": [ "SRAM", "CHRR", "NTAB", "SPRT", "CTRL" ]
}

View File

@ -1,6 +0,0 @@
{
"Rom File": "../roms/Prince of Persia (U) [!].nes",
"Expected ROM SHA1": "6B58F149F34FA829135619C58700CAAA95B9CDE3",
"Initial State File": "",
"Sequence File": "anyPercent.sol"
}

View File

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

View File

@ -1,27 +1,19 @@
#!/bin/bash
# Finding all test scripts
testScriptList=`find . -type f -name *.test`
testScriptList=`find . -type f -name "*.test"`
# Iterating over the scripts
for script in ${testScriptList}; do
# Getting base folder
folder=`dirname ${script}`
# Getting filename
fileName=`basename ${script}`
# Going to folder
pushd ${folder}
# Running script on quickerNES
quickerNESTester ${fileName} --cycleType Rerecord
# Running script on quickerNES
quickNESTester ${fileName} --cycleType Rerecord
# Coming back
popd
done

View File

@ -0,0 +1,7 @@
{
"Rom File": "roms/Saint Seiya - Ougon Densetsu Kanketsu Hen (J) [!].nes",
"Expected ROM SHA1": "F871D9B3DAFDDCDAD5F2ACD71044292E5169064E",
"Initial State File": "",
"Sequence File": "saintSeiyaKanketsuHen.anyPercent.sol",
"Disable State Blocks": [ "SRAM", "CHRR", "SPRT", "CTRL" ]
}

View File

@ -1,6 +0,0 @@
{
"Rom File": "../roms/Saint Seiya - Ougon Densetsu Kanketsu Hen (J) [!].nes",
"Expected ROM SHA1": "F871D9B3DAFDDCDAD5F2ACD71044292E5169064E",
"Initial State File": "",
"Sequence File": "anyPercent.sol"
}

View File

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

View File

@ -0,0 +1,7 @@
{
"Rom File": "roms/Saint Seiya - Ougon Densetsu (J) [!].nes",
"Expected ROM SHA1": "3F3B499CF50386084E053BCA096AE8E52330CFAE",
"Initial State File": "",
"Sequence File": "saintSeiyaKanketsuHen.anyPercent.sol",
"Disable State Blocks": [ "SRAM", "CHRR", "NTAB", "SPRT", "CTRL" ]
}

View File

@ -1,6 +0,0 @@
{
"Rom File": "../roms/Saint Seiya - Ougon Densetsu (J) [!].nes",
"Expected ROM SHA1": "3F3B499CF50386084E053BCA096AE8E52330CFAE",
"Initial State File": "",
"Sequence File": "anyPercent.sol"
}

View File

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

View File

@ -0,0 +1,7 @@
{
"Rom File": "roms/Saiyuuki World (J).nes",
"Expected ROM SHA1": "C2F12D915A4D0B1FFDF8A64AE1092CE6A2D08770",
"Initial State File": "",
"Sequence File": "saiyuukiWorld.anyPercent.sol",
"Disable State Blocks": [ "SRAM", "CHRR", "NTAB", "SPRT", "CTRL" ]
}

View File

@ -0,0 +1,7 @@
{
"Rom File": "roms/Saiyuuki World (J).nes",
"Expected ROM SHA1": "C2F12D915A4D0B1FFDF8A64AE1092CE6A2D08770",
"Initial State File": "saiyuukiWorld.lastHalf.state",
"Sequence File": "saiyuukiWorld.lastHalf.sol",
"Disable State Blocks": [ "SRAM", "CHRR", "NTAB", "SPRT", "CTRL" ]
}

View File

@ -1,7 +0,0 @@
{
"Rom File": "../roms/Saiyuuki World (J).nes",
"Expected ROM SHA1": "C2F12D915A4D0B1FFDF8A64AE1092CE6A2D08770",
"Initial State File": "",
"Sequence File": "anyPercent.sol",
"Credits": "https://tasvideos.org/4422M by aiqiyou & J.Y"
}

View File

@ -1,7 +0,0 @@
{
"Rom File": "../roms/Saiyuuki World (J).nes",
"Expected ROM SHA1": "C2F12D915A4D0B1FFDF8A64AE1092CE6A2D08770",
"Initial State File": "lastHalf.state",
"Sequence File": "anyPercent.sol",
"Credits": "https://tasvideos.org/4422M by aiqiyou & J.Y"
}

View File

@ -1,9 +0,0 @@
game = 'saiyuukiWorld'
bash = find_program('bash')
goal = 'anyPercent'
test(goal + '-FullCycle', bash, workdir : meson.current_source_dir(), timeout: testTimeout, args : [ testCommands, goal + '.test', '--cycleType', 'Full'], suite: [ game, goal ] )
goal = 'lastHalf'
test(goal + '-FullCycle', bash, workdir : meson.current_source_dir(), timeout: testTimeout, args : [ testCommands, goal + '.test', '--cycleType', 'Full'], suite: [ game, goal ] )

View File

@ -0,0 +1,7 @@
{
"Rom File": "roms/Solar Jetman - Hunt for the Golden Warpship (U) [!].nes",
"Expected ROM SHA1": "872B91A2F7A2F635061EF43F79E7F7E9F59F5C50",
"Initial State File": "",
"Sequence File": "solarJetman.anyPercent.sol",
"Disable State Blocks": [ "SRAM", "CHRR", "NTAB", "SPRT", "CTRL" ]
}

View File

@ -1,6 +0,0 @@
{
"Rom File": "../roms/Solar Jetman - Hunt for the Golden Warpship (U) [!].nes",
"Expected ROM SHA1": "872B91A2F7A2F635061EF43F79E7F7E9F59F5C50",
"Initial State File": "",
"Sequence File": "anyPercent.sol"
}

View File

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

View File

@ -0,0 +1,7 @@
{
"Rom File": "roms/sprilo.nes",
"Expected ROM SHA1": "6EC09B9B51320A536A786D3D4719432B714C5779",
"Initial State File": "",
"Sequence File": "sprilo.anyPercent.sol",
"Disable State Blocks": [ "SRAM", "CHRR", "NTAB", "SPRT", "CTRL" ]
}

Some files were not shown because too many files have changed in this diff Show More