From 37df740fd35401120b5cee926e0a0ec231085859 Mon Sep 17 00:00:00 2001 From: Aaron Culliney Date: Sun, 9 Oct 2016 14:54:11 -0700 Subject: [PATCH] Add save/load state to mockingboard --- src/audio/AY8910.c | 295 ++++++++++++++++++++++++++++++++++++++- src/audio/AY8910.h | 8 +- src/audio/mockingboard.c | 289 +++++++++++++++++++++++++++++++++++++- src/audio/mockingboard.h | 7 +- src/misc.c | 26 +++- src/misc.h | 1 + 6 files changed, 614 insertions(+), 12 deletions(-) diff --git a/src/audio/AY8910.c b/src/audio/AY8910.c index 30a7b6ec..57cd924a 100644 --- a/src/audio/AY8910.c +++ b/src/audio/AY8910.c @@ -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, ®, 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 *)®, 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 diff --git a/src/audio/AY8910.h b/src/audio/AY8910.h index 037cd141..a469cf0e 100644 --- a/src/audio/AY8910.h +++ b/src/audio/AY8910.h @@ -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 diff --git a/src/audio/mockingboard.c b/src/audio/mockingboard.c index 8b6ba4ec..5481c9ac 100644 --- a/src/audio/mockingboard.c +++ b/src/audio/mockingboard.c @@ -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; isy6522))) { + 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; isy6522))) { + 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; diff --git a/src/audio/mockingboard.h b/src/audio/mockingboard.h index b332cba7..05573e9b 100644 --- a/src/audio/mockingboard.h +++ b/src/audio/mockingboard.h @@ -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 diff --git a/src/misc.c b/src/misc.c index 7af9e0b6..a585bd97 100644 --- a/src/misc.c +++ b/src/misc.c @@ -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); diff --git a/src/misc.h b/src/misc.h index bf743420..f6757c92 100644 --- a/src/misc.h +++ b/src/misc.h @@ -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;