Add save/load state to mockingboard

This commit is contained in:
Aaron Culliney 2016-10-09 14:54:11 -07:00
parent bb93b5c243
commit 37df740fd3
6 changed files with 614 additions and 12 deletions

View File

@ -1025,7 +1025,284 @@ void SetCLK(double CLK)
#define SS_YAML_KEY_CHANGE "Change"
#define SS_YAML_VALUE_CHANGE_FORMAT "%d, %d, 0x%1X, 0x%02X"
#if UNBREAK_SOON
#if 1 // APPLE2IX
static bool _saveState(StateHelper_s *helper, struct CAY8910 *_this) {
int fd = helper->fd;
bool saved = false;
do {
unsigned int data = 0;
data = _this->ay_tone_tick[0];
if (!helper->save(fd, (uint8_t *)&data, sizeof(data))) {
break;
}
data = _this->ay_tone_tick[1];
if (!helper->save(fd, (uint8_t *)&data, sizeof(data))) {
break;
}
data = _this->ay_tone_tick[2];
if (!helper->save(fd, (uint8_t *)&data, sizeof(data))) {
break;
}
data = _this->ay_tone_high[0];
if (!helper->save(fd, (uint8_t *)&data, sizeof(data))) {
break;
}
data = _this->ay_tone_high[1];
if (!helper->save(fd, (uint8_t *)&data, sizeof(data))) {
break;
}
data = _this->ay_tone_high[2];
if (!helper->save(fd, (uint8_t *)&data, sizeof(data))) {
break;
}
data = _this->ay_noise_tick;
if (!helper->save(fd, (uint8_t *)&data, sizeof(data))) {
break;
}
data = _this->ay_tone_subcycles;
if (!helper->save(fd, (uint8_t *)&data, sizeof(data))) {
break;
}
data = _this->ay_env_subcycles;
if (!helper->save(fd, (uint8_t *)&data, sizeof(data))) {
break;
}
data = _this->ay_env_internal_tick;
if (!helper->save(fd, (uint8_t *)&data, sizeof(data))) {
break;
}
data = _this->ay_env_tick;
if (!helper->save(fd, (uint8_t *)&data, sizeof(data))) {
break;
}
data = _this->ay_tick_incr;
if (!helper->save(fd, (uint8_t *)&data, sizeof(data))) {
break;
}
data = _this->ay_tone_period[0];
if (!helper->save(fd, (uint8_t *)&data, sizeof(data))) {
break;
}
data = _this->ay_tone_period[1];
if (!helper->save(fd, (uint8_t *)&data, sizeof(data))) {
break;
}
data = _this->ay_tone_period[2];
if (!helper->save(fd, (uint8_t *)&data, sizeof(data))) {
break;
}
data = _this->ay_noise_period;
if (!helper->save(fd, (uint8_t *)&data, sizeof(data))) {
break;
}
data = _this->ay_env_period;
if (!helper->save(fd, (uint8_t *)&data, sizeof(data))) {
break;
}
data = _this->rng;
if (!helper->save(fd, (uint8_t *)&data, sizeof(data))) {
break;
}
data = _this->noise_toggle;
if (!helper->save(fd, (uint8_t *)&data, sizeof(data))) {
break;
}
data = _this->env_first;
if (!helper->save(fd, (uint8_t *)&data, sizeof(data))) {
break;
}
data = _this->env_rev;
if (!helper->save(fd, (uint8_t *)&data, sizeof(data))) {
break;
}
data = _this->env_counter;
if (!helper->save(fd, (uint8_t *)&data, sizeof(data))) {
break;
}
// Registers
{
bool success = true;
for (unsigned int i=0; i<16; i++) {
uint8_t reg = _this->sound_ay_registers[i];
if (!helper->save(fd, &reg, sizeof(reg))) {
success = false;
break;
}
}
if (!success) {
break;
}
}
// Queued changes
assert(_this->ay_change_count >= 0);
if (!helper->save(fd, (uint8_t *)&(_this->ay_change_count), sizeof(_this->ay_change_count))) {
break;
}
if (_this->ay_change_count)
{
bool success = true;
for (int i=0; i<_this->ay_change_count; i++) {
unsigned long tstates = (_this->ay_change[i]).tstates;
if (!helper->save(fd, (uint8_t *)&tstates, sizeof(tstates))) {
success = false;
break;
}
unsigned short ofs = (_this->ay_change[i]).ofs;
if (!helper->save(fd, (uint8_t *)&ofs, sizeof(ofs))) {
success = false;
break;
}
unsigned char reg = (_this->ay_change[i]).reg;
if (!helper->save(fd, (uint8_t *)&reg, sizeof(reg))) {
success = false;
break;
}
unsigned char val = (_this->ay_change[i]).val;
if (!helper->save(fd, (uint8_t *)&val, sizeof(val))) {
success = false;
break;
}
}
if (!success) {
break;
}
}
saved = true;
} while (0);
return saved;
}
static bool _loadState(StateHelper_s *helper, struct CAY8910 *_this) {
int fd = helper->fd;
bool loaded = false;
do {
if (!helper->load(fd, (uint8_t *)&(_this->ay_tone_tick[0]), sizeof(unsigned int))) {
break;
}
if (!helper->load(fd, (uint8_t *)&(_this->ay_tone_tick[1]), sizeof(unsigned int))) {
break;
}
if (!helper->load(fd, (uint8_t *)&(_this->ay_tone_tick[2]), sizeof(unsigned int))) {
break;
}
if (!helper->load(fd, (uint8_t *)&(_this->ay_tone_high[0]), sizeof(unsigned int))) {
break;
}
if (!helper->load(fd, (uint8_t *)&(_this->ay_tone_high[1]), sizeof(unsigned int))) {
break;
}
if (!helper->load(fd, (uint8_t *)&(_this->ay_tone_high[2]), sizeof(unsigned int))) {
break;
}
if (!helper->load(fd, (uint8_t *)&(_this->ay_noise_tick), sizeof(unsigned int))) {
break;
}
if (!helper->load(fd, (uint8_t *)&(_this->ay_tone_subcycles), sizeof(unsigned int))) {
break;
}
if (!helper->load(fd, (uint8_t *)&(_this->ay_env_subcycles), sizeof(unsigned int))) {
break;
}
if (!helper->load(fd, (uint8_t *)&(_this->ay_env_internal_tick), sizeof(unsigned int))) {
break;
}
if (!helper->load(fd, (uint8_t *)&(_this->ay_env_tick), sizeof(unsigned int))) {
break;
}
if (!helper->load(fd, (uint8_t *)&(_this->ay_tick_incr), sizeof(unsigned int))) {
break;
}
if (!helper->load(fd, (uint8_t *)&(_this->ay_tone_period[0]), sizeof(unsigned int))) {
break;
}
if (!helper->load(fd, (uint8_t *)&(_this->ay_tone_period[1]), sizeof(unsigned int))) {
break;
}
if (!helper->load(fd, (uint8_t *)&(_this->ay_tone_period[2]), sizeof(unsigned int))) {
break;
}
if (!helper->load(fd, (uint8_t *)&(_this->ay_noise_period), sizeof(unsigned int))) {
break;
}
if (!helper->load(fd, (uint8_t *)&(_this->ay_env_period), sizeof(unsigned int))) {
break;
}
if (!helper->load(fd, (uint8_t *)&(_this->rng), sizeof(unsigned int))) {
break;
}
if (!helper->load(fd, (uint8_t *)&(_this->noise_toggle), sizeof(unsigned int))) {
break;
}
if (!helper->load(fd, (uint8_t *)&(_this->env_first), sizeof(unsigned int))) {
break;
}
if (!helper->load(fd, (uint8_t *)&(_this->env_rev), sizeof(unsigned int))) {
break;
}
if (!helper->load(fd, (uint8_t *)&(_this->env_counter), sizeof(unsigned int))) {
break;
}
// Registers
{
bool success = true;
for (unsigned int i=0; i<16; i++) {
if (!helper->load(fd, &(_this->sound_ay_registers[i]), sizeof(_this->sound_ay_registers[i]))) {
success = false;
break;
}
}
if (!success) {
break;
}
}
// Queued changes
if (!helper->load(fd, (uint8_t *)&(_this->ay_change_count), sizeof(_this->ay_change_count))) {
break;
}
assert(_this->ay_change_count >= 0);
if (_this->ay_change_count)
{
bool success = true;
for (int i=0; i<_this->ay_change_count; i++) {
if (!helper->load(fd, (uint8_t *)&(_this->ay_change[i].tstates), sizeof(_this->ay_change[i].tstates))) {
success = false;
break;
}
if (!helper->load(fd, (uint8_t *)&(_this->ay_change[i].ofs), sizeof(_this->ay_change[i].ofs))) {
success = false;
break;
}
if (!helper->load(fd, (uint8_t *)&(_this->ay_change[i].reg), sizeof(_this->ay_change[i].reg))) {
success = false;
break;
}
if (!helper->load(fd, (uint8_t *)&(_this->ay_change[i].val), sizeof(_this->ay_change[i].val))) {
success = false;
break;
}
}
if (!success) {
break;
}
}
loaded = true;
} while (0);
return loaded;
}
#else
void CAY8910::SaveSnapshot(YamlSaveHelper& yamlSaveHelper, std::string& suffix)
{
std::string unit = std::string(SS_YAML_KEY_AY8910) + suffix;
@ -1168,7 +1445,7 @@ bool CAY8910::LoadSnapshot(YamlLoadHelper& yamlLoadHelper, std::string& suffix)
return true;
}
#endif // UNBREAK_SOON
#endif
///////////////////////////////////////////////////////////////////////////////
@ -1277,7 +1554,17 @@ uint8_t* AY8910_GetRegsPtr(unsigned int uChip)
return GetAYRegsPtr(&g_AY8910[uChip]);
}
#if UNBREAK_SOON
#if 1 // APPLE2IX
bool _ay8910_saveState(StateHelper_s *helper, unsigned int chip) {
assert(chip < MAX_8910);
return _saveState(helper, &g_AY8910[chip]);
}
bool _ay8910_loadState(StateHelper_s *helper, unsigned int chip) {
assert(chip < MAX_8910);
return _loadState(helper, &g_AY8910[chip]);
}
#else
UINT AY8910_SaveSnapshot(YamlSaveHelper& yamlSaveHelper, UINT uChip, std::string& suffix)
{
if (uChip >= MAX_8910)
@ -1294,5 +1581,5 @@ UINT AY8910_LoadSnapshot(YamlLoadHelper& yamlLoadHelper, UINT uChip, std::string
return g_AY8910[uChip].LoadSnapshot(yamlLoadHelper, suffix) ? 1 : 0;
}
#endif // UNBREAK_SOON
#endif

View File

@ -32,7 +32,10 @@ uint8_t* AY8910_GetRegsPtr(unsigned int uChip);
void AY8910UpdateSetCycles();
#if UNBREAK_SOON
#if 1 // APPLE2IX
bool _ay8910_saveState(StateHelper_s *helper, unsigned int chip);
bool _ay8910_loadState(StateHelper_s *helper, unsigned int chip);
#else
UINT AY8910_SaveSnapshot(class YamlSaveHelper& yamlSaveHelper, UINT uChip, std::string& suffix);
UINT AY8910_LoadSnapshot(class YamlLoadHelper& yamlLoadHelper, UINT uChip, std::string& suffix);
#endif
@ -69,7 +72,8 @@ public:
void sound_frame(struct CAY8910 *_this);
uint8_t* GetAYRegsPtr(struct CAY8910 *_this);
void SetCLK(double CLK);
#if UNBREAK_SOON
#if 1 // APPLE2IX
#else
void SaveSnapshot(class YamlSaveHelper& yamlSaveHelper, std::string& suffix);
bool LoadSnapshot(class YamlLoadHelper& yamlLoadHelper, std::string& suffix);
#endif

View File

@ -2583,6 +2583,8 @@ int MB_SetSnapshot_v1(const SS_CARD_MOCKINGBOARD_v1* const pSS, const DWORD /*dw
//===========================================================================
#if 1 // APPLE2IX
static void mb_prefsChanged(const char *domain) {
long lVal = 0;
long goesToTen = prefs_parseLongValue(domain, PREF_MOCKINGBOARD_VOLUME, &lVal, /*base:*/10) ? lVal : 5; // expected range 0-10
@ -2599,7 +2601,292 @@ static __attribute__((constructor)) void _init_mockingboard(void) {
prefs_registerListener(PREF_DOMAIN_AUDIO, &mb_prefsChanged);
}
#if 0 // !APPLE2IX
static bool _sy6522_saveState(StateHelper_s *helper, SY6522 *sy6522) {
int fd = helper->fd;
bool saved = false;
do {
uint8_t state8 = 0x0;
state8 = sy6522->ORA;
if (!helper->save(fd, &state8, 1)) {
break;
}
state8 = sy6522->ORB;
if (!helper->save(fd, &state8, 1)) {
break;
}
state8 = sy6522->DDRA;
if (!helper->save(fd, &state8, 1)) {
break;
}
state8 = sy6522->DDRB;
if (!helper->save(fd, &state8, 1)) {
break;
}
uint16_t state16 = 0x0;
state16 = sy6522->TIMER1_COUNTER.w;
if (!helper->save(fd, (uint8_t *)&state16, 2)) {
break;
}
state16 = sy6522->TIMER1_LATCH.w;
if (!helper->save(fd, (uint8_t *)&state16, 2)) {
break;
}
state16 = sy6522->TIMER2_COUNTER.w;
if (!helper->save(fd, (uint8_t *)&state16, 2)) {
break;
}
state16 = sy6522->TIMER2_LATCH.w;
if (!helper->save(fd, (uint8_t *)&state16, 2)) {
break;
}
state8 = sy6522->SERIAL_SHIFT;
if (!helper->save(fd, &state8, 1)) {
break;
}
state8 = sy6522->ACR;
if (!helper->save(fd, &state8, 1)) {
break;
}
state8 = sy6522->PCR;
if (!helper->save(fd, &state8, 1)) {
break;
}
state8 = sy6522->IFR;
if (!helper->save(fd, &state8, 1)) {
break;
}
state8 = sy6522->IER;
if (!helper->save(fd, &state8, 1)) {
break;
}
// NB. No need to write ORA_NO_HS, since same data as ORA, just without handshake
saved = true;
} while (0);
return saved;
}
static bool _sy6522_loadState(StateHelper_s *helper, SY6522 *sy6522) {
int fd = helper->fd;
bool loaded = false;
do {
if (!helper->load(fd, &(sy6522->ORA), 1)) {
break;
}
if (!helper->load(fd, &(sy6522->ORB), 1)) {
break;
}
if (!helper->load(fd, &(sy6522->DDRA), 1)) {
break;
}
if (!helper->load(fd, &(sy6522->DDRB), 1)) {
break;
}
if (!helper->load(fd, (uint8_t *)&(sy6522->TIMER1_COUNTER.w), 2)) {
break;
}
if (!helper->load(fd, (uint8_t *)&(sy6522->TIMER1_LATCH.w), 2)) {
break;
}
if (!helper->load(fd, (uint8_t *)&(sy6522->TIMER2_COUNTER.w), 2)) {
break;
}
if (!helper->load(fd, (uint8_t *)&(sy6522->TIMER2_LATCH.w), 2)) {
break;
}
if (!helper->load(fd, &(sy6522->SERIAL_SHIFT), 1)) {
break;
}
if (!helper->load(fd, &(sy6522->ACR), 1)) {
break;
}
if (!helper->load(fd, &(sy6522->PCR), 1)) {
break;
}
if (!helper->load(fd, &(sy6522->IFR), 1)) {
break;
}
if (!helper->load(fd, &(sy6522->IER), 1)) {
break;
}
// NB. No need to write ORA_NO_HS, since same data as ORA, just without handshake
loaded = true;
} while (0);
return loaded;
}
static bool _ssi263_saveState(StateHelper_s *helper, SSI263A *ssi263) {
int fd = helper->fd;
bool saved = false;
do {
if (!helper->save(fd, &(ssi263->DurationPhoneme), 1)) {
break;
}
if (!helper->save(fd, &(ssi263->Inflection), 1)) {
break;
}
if (!helper->save(fd, &(ssi263->RateInflection), 1)) {
break;
}
if (!helper->save(fd, &(ssi263->CtrlArtAmp), 1)) {
break;
}
if (!helper->save(fd, &(ssi263->FilterFreq), 1)) {
break;
}
if (!helper->save(fd, &(ssi263->CurrentMode), 1)) {
break;
}
saved = true;
} while (0);
return saved;
}
static bool _ssi263_loadState(StateHelper_s *helper, SSI263A *ssi263) {
int fd = helper->fd;
bool loaded = false;
do {
if (!helper->load(fd, &(ssi263->DurationPhoneme), 1)) {
break;
}
if (!helper->load(fd, &(ssi263->Inflection), 1)) {
break;
}
if (!helper->load(fd, &(ssi263->RateInflection), 1)) {
break;
}
if (!helper->load(fd, &(ssi263->CtrlArtAmp), 1)) {
break;
}
if (!helper->load(fd, &(ssi263->FilterFreq), 1)) {
break;
}
if (!helper->load(fd, &(ssi263->CurrentMode), 1)) {
break;
}
loaded = true;
} while (0);
return loaded;
}
bool mb_saveState(StateHelper_s *helper) {
LOG("SAVE mockingboard state ...");
int fd = helper->fd;
bool saved = false;
for (unsigned int i=0; i<NUM_DEVS_PER_MB; i++) {
unsigned int deviceIdx = i<<1;
SY6522_AY8910 *mb = &g_MB[deviceIdx];
for (unsigned int j=0; j<NUM_MB; j++) {
if (!_sy6522_saveState(helper, &(mb->sy6522))) {
goto exit_save;
}
if (!_ay8910_saveState(helper, deviceIdx)) {
goto exit_save;
}
if (!_ssi263_saveState(helper, &(mb->SpeechChip))) {
goto exit_save;
}
if (!helper->save(fd, &(mb->nAYCurrentRegister), 1)) {
goto exit_save;
}
// TIMER1 IRQ
// TIMER2 IRQ
// SPEECH IRQ
deviceIdx++;
mb++;
}
}
saved = true;
exit_save:
return saved;
}
bool mb_loadState(StateHelper_s *helper) {
LOG("LOAD mockingboard state ...");
int fd = helper->fd;
// NOTE : always load state and calculate based on CPU @1.0 scale
double cpuScaleFactor = cpu_scale_factor;
double cpuAltScaleFactor = cpu_altscale_factor;
cpu_scale_factor = 1.;
cpu_altscale_factor = 1.;
timing_initialize();
MB_Reset();
AY8910UpdateSetCycles();
bool loaded = false;
for (unsigned int i=0; i<NUM_DEVS_PER_MB; i++) {
for (unsigned int j=0; j<NUM_MB; j++) {
unsigned int idx = (i<<1) + j;
SY6522_AY8910 *mb = &g_MB[idx];
if (!_sy6522_loadState(helper, &(mb->sy6522))) {
goto exit_load;
}
if (!_ay8910_loadState(helper, idx)) {
goto exit_load;
}
if (!_ssi263_loadState(helper, &(mb->SpeechChip))) {
goto exit_load;
}
if (!helper->load(fd, &(mb->nAYCurrentRegister), 1)) {
goto exit_load;
}
// TIMER1 IRQ
// TIMER2 IRQ
// SPEECH IRQ
StartTimer(mb);
++mb;
}
}
loaded = true;
MB_Reinitialize();
exit_load:
cpu_scale_factor = cpuScaleFactor;
cpu_altscale_factor = cpuAltScaleFactor;
timing_initialize();
return loaded;
}
#else
static UINT DoWriteFile(const HANDLE hFile, const void* const pData, const UINT Length)
{
DWORD dwBytesWritten;

View File

@ -114,14 +114,17 @@ bool MB_IsActive();
unsigned long MB_GetVolume();
void MB_SetVolumeZeroToTen(unsigned long goesToTen);
void MB_SetVolume(unsigned long dwVolume, unsigned long dwVolumeMax);
#if UNBREAK_SOON
#if 1 // APPLE2IX
bool mb_saveState(StateHelper_s *helper);
bool mb_loadState(StateHelper_s *helper);
#else
void MB_GetSnapshot_v1(struct SS_CARD_MOCKINGBOARD_v1* const pSS, const DWORD dwSlot); // For debugger
int MB_SetSnapshot_v1(const struct SS_CARD_MOCKINGBOARD_v1* const pSS, const DWORD dwSlot);
std::string MB_GetSnapshotCardName(void);
void MB_SaveSnapshot(class YamlSaveHelper& yamlSaveHelper, const UINT uSlot);
bool MB_LoadSnapshot(class YamlLoadHelper& yamlLoadHelper, UINT slot, UINT version);
#endif
#ifdef APPLE2IX
#if 1 // APPLE2IX
uint8_t mb_read(uint16_t ea);
void mb_io_initialize(unsigned int slot4, unsigned int slot5);
# if MB_TRACING

View File

@ -15,7 +15,9 @@
#include "common.h"
#define SAVE_MAGICK "A2VM"
#define SAVE_MAGICK "A2VM"
#define SAVE_MAGICK2 "A2V2"
#define SAVE_VERSION 2
#define SAVE_MAGICK_LEN sizeof(SAVE_MAGICK)
typedef struct module_ctor_node_s {
@ -97,12 +99,13 @@ bool emulator_saveState(const char * const path) {
assert(fd != 0 && "crazy platform");
// save header
if (!_save_state(fd, (const uint8_t *)SAVE_MAGICK, SAVE_MAGICK_LEN)) {
if (!_save_state(fd, (const uint8_t *)SAVE_MAGICK2, SAVE_MAGICK_LEN)) {
break;
}
StateHelper_s helper = {
.fd = fd,
.version = SAVE_VERSION,
.save = &_save_state,
.load = &_load_state,
};
@ -123,6 +126,10 @@ bool emulator_saveState(const char * const path) {
break;
}
if (!mb_saveState(&helper)) {
break;
}
TEMP_FAILURE_RETRY(fsync(fd));
saved = true;
} while (0);
@ -149,6 +156,7 @@ bool emulator_loadState(const char * const path) {
video_setDirty(A2_DIRTY_FLAG);
int version=-1;
do {
TEMP_FAILURE_RETRY(fd = open(path, O_RDONLY));
if (fd < 0) {
@ -163,13 +171,19 @@ bool emulator_loadState(const char * const path) {
}
// check header
if (memcmp(magick, SAVE_MAGICK, SAVE_MAGICK_LEN) != 0) {
if (memcmp(magick, SAVE_MAGICK, SAVE_MAGICK_LEN) == 0) {
version = 1;
} else if (memcmp(magick, SAVE_MAGICK2, SAVE_MAGICK_LEN) == 0) {
version = 2;
} else {
ERRLOG("bad header magick in emulator save state file");
break;
}
StateHelper_s helper = {
.fd = fd,
.version = version,
.save = &_save_state,
.load = &_load_state,
};
@ -190,6 +204,12 @@ bool emulator_loadState(const char * const path) {
break;
}
if (version >= 2) {
if (!mb_loadState(&helper)) {
break;
}
}
loaded = true;
} while (0);

View File

@ -59,6 +59,7 @@ bool emulator_isShuttingDown(void);
typedef struct StateHelper_s {
int fd;
int version;
bool (*save)(int fd, const uint8_t * outbuf, ssize_t outmax);
bool (*load)(int fd, uint8_t * inbuf, ssize_t inmax);
} StateHelper_s;