From ee1f84bb4a709bc376ad279444cc2e48f437ea9c Mon Sep 17 00:00:00 2001 From: goyuken Date: Mon, 29 Sep 2014 20:13:26 +0000 Subject: [PATCH] lynx: savestates. should work pretty well, but format is subject to change --- .../Consoles/Atari/lynx/LibLynx.cs | 13 ++ .../Consoles/Atari/lynx/Lynx.cs | 65 ++++++- lynx/bizlynx/bizlynx.vcxproj | 2 + lynx/bizlynx/bizlynx.vcxproj.filters | 6 + lynx/c65c02.cpp | 25 +++ lynx/c65c02.h | 2 + lynx/cart.cpp | 33 ++++ lynx/cart.h | 2 + lynx/cinterface.cpp | 35 ++++ lynx/machine.h | 1 + lynx/memmap.cpp | 16 +- lynx/memmap.h | 2 + lynx/mikie.cpp | 182 ++++++++++++++++++ lynx/mikie.h | 2 + lynx/newstate.cpp | 64 ++++++ lynx/newstate.h | 98 ++++++++++ lynx/ram.cpp | 5 + lynx/ram.h | 2 + lynx/rom.cpp | 8 +- lynx/rom.h | 14 +- lynx/susie.cpp | 103 ++++++++++ lynx/susie.h | 2 + lynx/system.cpp | 22 +++ lynx/system.h | 2 + output/dll/bizlynx.dll | Bin 61440 -> 80384 bytes 25 files changed, 697 insertions(+), 9 deletions(-) create mode 100644 lynx/newstate.cpp create mode 100644 lynx/newstate.h diff --git a/BizHawk.Emulation.Cores/Consoles/Atari/lynx/LibLynx.cs b/BizHawk.Emulation.Cores/Consoles/Atari/lynx/LibLynx.cs index f796d5272f..ca901f3b89 100644 --- a/BizHawk.Emulation.Cores/Consoles/Atari/lynx/LibLynx.cs +++ b/BizHawk.Emulation.Cores/Consoles/Atari/lynx/LibLynx.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using BizHawk.Emulation.Common; using System.Runtime.InteropServices; namespace BizHawk.Emulation.Cores.Atari.Lynx @@ -27,6 +28,18 @@ namespace BizHawk.Emulation.Cores.Atari.Lynx [DllImport(dllname, CallingConvention = cc)] public static extern bool GetSaveRamPtr(IntPtr s, out int size, out IntPtr data); + [DllImport(dllname, CallingConvention = cc)] + public static extern int BinStateSize(IntPtr s); + [DllImport(dllname, CallingConvention = cc)] + public static extern bool BinStateSave(IntPtr s, byte[] data, int length); + [DllImport(dllname, CallingConvention = cc)] + public static extern bool BinStateLoad(IntPtr s, byte[] data, int length); + [DllImport(dllname, CallingConvention = cc)] + public static extern void TxtStateSave(IntPtr s, [In]ref TextStateFPtrs ff); + [DllImport(dllname, CallingConvention = cc)] + public static extern void TxtStateLoad(IntPtr s, [In]ref TextStateFPtrs ff); + + [Flags] public enum Buttons : ushort { diff --git a/BizHawk.Emulation.Cores/Consoles/Atari/lynx/Lynx.cs b/BizHawk.Emulation.Cores/Consoles/Atari/lynx/Lynx.cs index 8ea08b84d5..d4efcb9f8f 100644 --- a/BizHawk.Emulation.Cores/Consoles/Atari/lynx/Lynx.cs +++ b/BizHawk.Emulation.Cores/Consoles/Atari/lynx/Lynx.cs @@ -7,6 +7,7 @@ using System.Runtime.InteropServices; using BizHawk.Common; using BizHawk.Emulation.Common; +using Newtonsoft.Json; namespace BizHawk.Emulation.Cores.Atari.Lynx { @@ -96,6 +97,9 @@ namespace BizHawk.Emulation.Cores.Atari.Lynx { CoreComm.VsyncNum = 16000000; // 16.00 mhz refclock CoreComm.VsyncDen = 16 * 105 * 159; + + savebuff = new byte[LibLynx.BinStateSize(Core)]; + savebuff2 = new byte[savebuff.Length + 13]; } catch { @@ -189,25 +193,84 @@ namespace BizHawk.Emulation.Cores.Atari.Lynx #region savestates + JsonSerializer ser = new JsonSerializer() { Formatting = Formatting.Indented }; + byte[] savebuff; + byte[] savebuff2; + + class TextStateData + { + public int Frame; + public int LagCount; + public bool IsLagFrame; + } + public void SaveStateText(TextWriter writer) { + var s = new TextState(); + s.Prepare(); + var ff = s.GetFunctionPointersSave(); + LibLynx.TxtStateSave(Core, ref ff); + s.ExtraData.IsLagFrame = IsLagFrame; + s.ExtraData.LagCount = LagCount; + s.ExtraData.Frame = Frame; + + ser.Serialize(writer, s); + // write extra copy of stuff we don't use + writer.WriteLine(); + writer.WriteLine("Frame {0}", Frame); + + Console.WriteLine(BizHawk.Common.BufferExtensions.BufferExtensions.HashSHA1(SaveStateBinary())); } public void LoadStateText(TextReader reader) { + var s = (TextState)ser.Deserialize(reader, typeof(TextState)); + s.Prepare(); + var ff = s.GetFunctionPointersLoad(); + LibLynx.TxtStateLoad(Core, ref ff); + IsLagFrame = s.ExtraData.IsLagFrame; + LagCount = s.ExtraData.LagCount; + Frame = s.ExtraData.Frame; } public void SaveStateBinary(BinaryWriter writer) { + if (!LibLynx.BinStateSave(Core, savebuff, savebuff.Length)) + throw new InvalidOperationException("Core's BinStateSave() returned false!"); + writer.Write(savebuff.Length); + writer.Write(savebuff); + + // other variables + writer.Write(IsLagFrame); + writer.Write(LagCount); + writer.Write(Frame); } public void LoadStateBinary(BinaryReader reader) { + int length = reader.ReadInt32(); + if (length != savebuff.Length) + throw new InvalidOperationException("Save buffer size mismatch!"); + reader.Read(savebuff, 0, length); + if (!LibLynx.BinStateLoad(Core, savebuff, savebuff.Length)) + throw new InvalidOperationException("Core's BinStateLoad() returned false!"); + + // other variables + IsLagFrame = reader.ReadBoolean(); + LagCount = reader.ReadInt32(); + Frame = reader.ReadInt32(); } public byte[] SaveStateBinary() { - return new byte[0]; + var ms = new MemoryStream(savebuff2, true); + var bw = new BinaryWriter(ms); + SaveStateBinary(bw); + bw.Flush(); + if (ms.Position != savebuff2.Length) + throw new InvalidOperationException(); + ms.Close(); + return savebuff2; } public bool BinarySaveStatesPreferred diff --git a/lynx/bizlynx/bizlynx.vcxproj b/lynx/bizlynx/bizlynx.vcxproj index d28b372eed..9953a1480d 100644 --- a/lynx/bizlynx/bizlynx.vcxproj +++ b/lynx/bizlynx/bizlynx.vcxproj @@ -73,6 +73,7 @@ + @@ -90,6 +91,7 @@ + diff --git a/lynx/bizlynx/bizlynx.vcxproj.filters b/lynx/bizlynx/bizlynx.vcxproj.filters index 5ce0f99bbb..0e61f577b7 100644 --- a/lynx/bizlynx/bizlynx.vcxproj.filters +++ b/lynx/bizlynx/bizlynx.vcxproj.filters @@ -54,6 +54,9 @@ Source Files + + Source Files + @@ -104,5 +107,8 @@ Header Files\sound + + Header Files + \ No newline at end of file diff --git a/lynx/c65c02.cpp b/lynx/c65c02.cpp index f63e088e26..761085ed5a 100644 --- a/lynx/c65c02.cpp +++ b/lynx/c65c02.cpp @@ -57,6 +57,31 @@ void C65C02::GetRegs(C6502_REGS ®s) regs.IRQ=mSystem.gSystemIRQ; } +SYNCFUNC(C65C02) +{ + NSS(mA); + NSS(mX); + NSS(mY); + NSS(mSP); + NSS(mOpcode); + NSS(mOperand); + NSS(mPC); + + NSS(mN); + NSS(mV); + NSS(mB); + NSS(mD); + NSS(mI); + NSS(mZ); + NSS(mC); + + NSS(mIRQActive); + + // don't need to save these: + // mRamPointer + // mBCDTable +} + void C65C02::Update(void) { diff --git a/lynx/c65c02.h b/lynx/c65c02.h index 6eed1dff44..1c19bf3370 100644 --- a/lynx/c65c02.h +++ b/lynx/c65c02.h @@ -161,6 +161,8 @@ public: inline int GetPC() { return mPC; } + templatevoid SyncState(NewState *ns); + private: CSystem &mSystem; diff --git a/lynx/cart.cpp b/lynx/cart.cpp index 430ec5db90..a4840465ef 100644 --- a/lynx/cart.cpp +++ b/lynx/cart.cpp @@ -330,3 +330,36 @@ bool CCart::GetSaveRamPtr(int &size, uint8 *&data) return false; } } + +SYNCFUNC(CCart) +{ + NSS(mWriteEnableBank0); + NSS(mWriteEnableBank1); + NSS(mCartRAM); + + EBS(mBank, 0); + EVS(mBank, bank0, 0); + EVS(mBank, bank1, 1); + EVS(mBank, ram, 2); + EVS(mBank, cpu, 3); + EES(mBank, bank0); + + NSS(mMaskBank0); + NSS(mMaskBank1); + if (false) + PSS(mCartBank0, mMaskBank0 + 1); + if (mCartRAM) + PSS(mCartBank1, mMaskBank1 + 1); + + NSS(mCounter); + NSS(mShifter); + NSS(mAddrData); + NSS(mStrobe); + + NSS(mShiftCount0); + NSS(mCountMask0); + NSS(mShiftCount1); + NSS(mCountMask1); + + NSS(last_strobe); +} diff --git a/lynx/cart.h b/lynx/cart.h index 558be11c6e..278799fea2 100644 --- a/lynx/cart.h +++ b/lynx/cart.h @@ -109,6 +109,8 @@ public: bool GetSaveRamPtr(int &size, uint8 *&data); + templatevoid SyncState(NewState *ns); + private: EMMODE mBank; uint32 mMaskBank0; diff --git a/lynx/cinterface.cpp b/lynx/cinterface.cpp index 9edf3d7699..cf589a73aa 100644 --- a/lynx/cinterface.cpp +++ b/lynx/cinterface.cpp @@ -41,3 +41,38 @@ EXPORT int GetSaveRamPtr(CSystem *s, int *size, uint8 **data) { return s->GetSaveRamPtr(*size, *data); } + + +EXPORT int BinStateSize(CSystem *s) +{ + NewStateDummy dummy; + s->SyncState(&dummy); + return dummy.GetLength(); +} + +EXPORT int BinStateSave(CSystem *s, char *data, int length) +{ + NewStateExternalBuffer saver(data, length); + s->SyncState(&saver); + return !saver.Overflow() && saver.GetLength() == length; +} + +EXPORT int BinStateLoad(CSystem *s, const char *data, int length) +{ + NewStateExternalBuffer loader(const_cast(data), length); + s->SyncState(&loader); + return !loader.Overflow() && loader.GetLength() == length; +} + +EXPORT void TxtStateSave(CSystem *s, FPtrs *ff) +{ + NewStateExternalFunctions saver(ff); + s->SyncState(&saver); +} + +EXPORT void TxtStateLoad(CSystem *s, FPtrs *ff) +{ + NewStateExternalFunctions loader(ff); + s->SyncState(&loader); +} + diff --git a/lynx/machine.h b/lynx/machine.h index fd5c811629..3b464fb87a 100644 --- a/lynx/machine.h +++ b/lynx/machine.h @@ -59,6 +59,7 @@ // #include "lynxbase.h" +#include "newstate.h" #endif diff --git a/lynx/memmap.cpp b/lynx/memmap.cpp index 738e3e3eec..39d681614e 100644 --- a/lynx/memmap.cpp +++ b/lynx/memmap.cpp @@ -186,4 +186,18 @@ INLINE uint8 CMemMap::Peek(uint32 addr) return retval; } -//END OF FILE +SYNCFUNC(CMemMap) +{ + NSS(mMikieEnabled); + NSS(mSusieEnabled); + NSS(mRomEnabled); + NSS(mVectorsEnabled); + + if (isReader) + { + // force regenerate memory handlers + uint8 current = Peek(0); + Poke(0, ~current); + Poke(0, current); + } +} diff --git a/lynx/memmap.h b/lynx/memmap.h index 9ae36ed3a1..7436757b32 100644 --- a/lynx/memmap.h +++ b/lynx/memmap.h @@ -80,6 +80,8 @@ class CMemMap : public CLynxBase uint32 WriteCycle(void) {return 5;}; uint32 ObjectSize(void) {return MEMMAP_SIZE;}; + templatevoid SyncState(NewState *ns); + // Data members private: diff --git a/lynx/mikie.cpp b/lynx/mikie.cpp index 148b6c0a2c..676940efc5 100644 --- a/lynx/mikie.cpp +++ b/lynx/mikie.cpp @@ -2564,3 +2564,185 @@ void CMikie::Update() // cannot be updated until this point otherwise it screws up the counters. mSystem.gSystemCycleCount+=mikie_work_done; } + +SYNCFUNC(CMikie) +{ + NSS(startTS); + // SSS(miksynth); + // SSS(mikbuf); + + // mpDisplayCurrent; + NSS(mpDisplayCurrentLine); + NSS(framebuffer); + + NSS(last_lsample); + NSS(last_rsample); + + NSS(mDisplayAddress); + NSS(mAudioInputComparator); + NSS(mTimerStatusFlags); + NSS(mTimerInterruptMask); + + NSS(mPalette); + NSS(mColourMap); + + NSS(mIODAT); + NSS(mIODIR); + NSS(mIODAT_REST_SIGNAL); + + NSS(mDISPCTL_DMAEnable); + NSS(mDISPCTL_Flip); + NSS(mDISPCTL_FourColour); + NSS(mDISPCTL_Colour); + + + + NSS(mTIM_0_BKUP); + NSS(mTIM_0_ENABLE_RELOAD); + NSS(mTIM_0_ENABLE_COUNT); + NSS(mTIM_0_LINKING); + NSS(mTIM_0_CURRENT); + NSS(mTIM_0_TIMER_DONE); + NSS(mTIM_0_LAST_CLOCK); + NSS(mTIM_0_BORROW_IN); + NSS(mTIM_0_BORROW_OUT); + NSS(mTIM_0_LAST_LINK_CARRY); + NSS(mTIM_0_LAST_COUNT); + + NSS(mTIM_1_BKUP); + NSS(mTIM_1_ENABLE_RELOAD); + NSS(mTIM_1_ENABLE_COUNT); + NSS(mTIM_1_LINKING); + NSS(mTIM_1_CURRENT); + NSS(mTIM_1_TIMER_DONE); + NSS(mTIM_1_LAST_CLOCK); + NSS(mTIM_1_BORROW_IN); + NSS(mTIM_1_BORROW_OUT); + NSS(mTIM_1_LAST_LINK_CARRY); + NSS(mTIM_1_LAST_COUNT); + + NSS(mTIM_2_BKUP); + NSS(mTIM_2_ENABLE_RELOAD); + NSS(mTIM_2_ENABLE_COUNT); + NSS(mTIM_2_LINKING); + NSS(mTIM_2_CURRENT); + NSS(mTIM_2_TIMER_DONE); + NSS(mTIM_2_LAST_CLOCK); + NSS(mTIM_2_BORROW_IN); + NSS(mTIM_2_BORROW_OUT); + NSS(mTIM_2_LAST_LINK_CARRY); + NSS(mTIM_2_LAST_COUNT); + + NSS(mTIM_3_BKUP); + NSS(mTIM_3_ENABLE_RELOAD); + NSS(mTIM_3_ENABLE_COUNT); + NSS(mTIM_3_LINKING); + NSS(mTIM_3_CURRENT); + NSS(mTIM_3_TIMER_DONE); + NSS(mTIM_3_LAST_CLOCK); + NSS(mTIM_3_BORROW_IN); + NSS(mTIM_3_BORROW_OUT); + NSS(mTIM_3_LAST_LINK_CARRY); + NSS(mTIM_3_LAST_COUNT); + + NSS(mTIM_4_BKUP); + NSS(mTIM_4_ENABLE_RELOAD); + NSS(mTIM_4_ENABLE_COUNT); + NSS(mTIM_4_LINKING); + NSS(mTIM_4_CURRENT); + NSS(mTIM_4_TIMER_DONE); + NSS(mTIM_4_LAST_CLOCK); + NSS(mTIM_4_BORROW_IN); + NSS(mTIM_4_BORROW_OUT); + NSS(mTIM_4_LAST_LINK_CARRY); + NSS(mTIM_4_LAST_COUNT); + + NSS(mTIM_5_BKUP); + NSS(mTIM_5_ENABLE_RELOAD); + NSS(mTIM_5_ENABLE_COUNT); + NSS(mTIM_5_LINKING); + NSS(mTIM_5_CURRENT); + NSS(mTIM_5_TIMER_DONE); + NSS(mTIM_5_LAST_CLOCK); + NSS(mTIM_5_BORROW_IN); + NSS(mTIM_5_BORROW_OUT); + NSS(mTIM_5_LAST_LINK_CARRY); + NSS(mTIM_5_LAST_COUNT); + + NSS(mTIM_6_BKUP); + NSS(mTIM_6_ENABLE_RELOAD); + NSS(mTIM_6_ENABLE_COUNT); + NSS(mTIM_6_LINKING); + NSS(mTIM_6_CURRENT); + NSS(mTIM_6_TIMER_DONE); + NSS(mTIM_6_LAST_CLOCK); + NSS(mTIM_6_BORROW_IN); + NSS(mTIM_6_BORROW_OUT); + NSS(mTIM_6_LAST_LINK_CARRY); + NSS(mTIM_6_LAST_COUNT); + + NSS(mTIM_7_BKUP); + NSS(mTIM_7_ENABLE_RELOAD); + NSS(mTIM_7_ENABLE_COUNT); + NSS(mTIM_7_LINKING); + NSS(mTIM_7_CURRENT); + NSS(mTIM_7_TIMER_DONE); + NSS(mTIM_7_LAST_CLOCK); + NSS(mTIM_7_BORROW_IN); + NSS(mTIM_7_BORROW_OUT); + NSS(mTIM_7_LAST_LINK_CARRY); + NSS(mTIM_7_LAST_COUNT); + + NSS(mAUDIO_BKUP); + NSS(mAUDIO_ENABLE_RELOAD); + NSS(mAUDIO_ENABLE_COUNT); + NSS(mAUDIO_LINKING); + NSS(mAUDIO_CURRENT); + NSS(mAUDIO_TIMER_DONE); + NSS(mAUDIO_LAST_CLOCK); + NSS(mAUDIO_BORROW_IN); + NSS(mAUDIO_BORROW_OUT); + NSS(mAUDIO_LAST_LINK_CARRY); + NSS(mAUDIO_LAST_COUNT); + NSS(mAUDIO_VOLUME); + NSS(mAUDIO_INTEGRATE_ENABLE); + NSS(mAUDIO_WAVESHAPER); + + NSS(mAUDIO_OUTPUT); + NSS(mAUDIO_ATTEN); + NSS(mSTEREO); + NSS(mPAN); + + + + NSS(mUART_RX_IRQ_ENABLE); + NSS(mUART_TX_IRQ_ENABLE); + + NSS(mUART_RX_COUNTDOWN); + NSS(mUART_TX_COUNTDOWN); + + NSS(mUART_SENDBREAK); + NSS(mUART_TX_DATA); + NSS(mUART_RX_DATA); + NSS(mUART_RX_READY); + + NSS(mUART_PARITY_ENABLE); + NSS(mUART_PARITY_EVEN); + + NSS(mUART_CABLE_PRESENT); + // mpUART_TX_CALLBACK; + // mUART_TX_CALLBACK_OBJECT; + + NSS(mUART_Rx_input_queue); + NSS(mUART_Rx_input_ptr); + NSS(mUART_Rx_output_ptr); + NSS(mUART_Rx_waiting); + NSS(mUART_Rx_framing_error); + NSS(mUART_Rx_overun_error); + + + // mpRamPointer; + NSS(mLynxLine); + NSS(mLynxLineDMACounter); + NSS(mLynxAddr); +} diff --git a/lynx/mikie.h b/lynx/mikie.h index ba3cfd0038..fa991e4e74 100644 --- a/lynx/mikie.h +++ b/lynx/mikie.h @@ -221,6 +221,8 @@ public: uint32 mpDisplayCurrentLine; uint32 framebuffer[SCREEN_WIDTH * SCREEN_HEIGHT]; + templatevoid SyncState(NewState *ns); + private: CSystem &mSystem; diff --git a/lynx/newstate.cpp b/lynx/newstate.cpp new file mode 100644 index 0000000000..efc0d554d4 --- /dev/null +++ b/lynx/newstate.cpp @@ -0,0 +1,64 @@ +#include "newstate.h" +#include +#include + +NewStateDummy::NewStateDummy() + :length(0) +{ +} +void NewStateDummy::Save(const void *ptr, size_t size, const char *name) +{ + length += size; +} +void NewStateDummy::Load(void *ptr, size_t size, const char *name) +{ +} + +NewStateExternalBuffer::NewStateExternalBuffer(char *buffer, long maxlength) + :buffer(buffer), length(0), maxlength(maxlength) +{ +} + +void NewStateExternalBuffer::Save(const void *ptr, size_t size, const char *name) +{ + if (maxlength - length >= (long)size) + { + std::memcpy(buffer + length, ptr, size); + } + length += size; +} + +void NewStateExternalBuffer::Load(void *ptr, size_t size, const char *name) +{ + char *dst = static_cast(ptr); + if (maxlength - length >= (long)size) + { + std::memcpy(dst, buffer + length, size); + } + length += size; +} + +NewStateExternalFunctions::NewStateExternalFunctions(const FPtrs *ff) + :Save_(ff->Save_), + Load_(ff->Load_), + EnterSection_(ff->EnterSection_), + ExitSection_(ff->ExitSection_) +{ +} + +void NewStateExternalFunctions::Save(const void *ptr, size_t size, const char *name) +{ + Save_(ptr, size, name); +} +void NewStateExternalFunctions::Load(void *ptr, size_t size, const char *name) +{ + Load_(ptr, size, name); +} +void NewStateExternalFunctions::EnterSection(const char *name) +{ + EnterSection_(name); +} +void NewStateExternalFunctions::ExitSection(const char *name) +{ + ExitSection_(name); +} diff --git a/lynx/newstate.h b/lynx/newstate.h new file mode 100644 index 0000000000..0c9230a373 --- /dev/null +++ b/lynx/newstate.h @@ -0,0 +1,98 @@ +#ifndef NEWSTATE_H +#define NEWSTATE_H + +#include +#include + +class NewState +{ +public: + virtual void Save(const void *ptr, size_t size, const char *name) = 0; + virtual void Load(void *ptr, size_t size, const char *name) = 0; + virtual void EnterSection(const char *name) { } + virtual void ExitSection(const char *name) { } +}; + +class NewStateDummy : public NewState +{ +private: + long length; +public: + NewStateDummy(); + long GetLength() { return length; } + void Rewind() { length = 0; } + virtual void Save(const void *ptr, size_t size, const char *name); + virtual void Load(void *ptr, size_t size, const char *name); +}; + +class NewStateExternalBuffer : public NewState +{ +private: + char *const buffer; + long length; + const long maxlength; +public: + NewStateExternalBuffer(char *buffer, long maxlength); + long GetLength() { return length; } + void Rewind() { length = 0; } + bool Overflow() { return length > maxlength; } + virtual void Save(const void *ptr, size_t size, const char *name); + virtual void Load(void *ptr, size_t size, const char *name); +}; + +struct FPtrs +{ + void (*Save_)(const void *ptr, size_t size, const char *name); + void (*Load_)(void *ptr, size_t size, const char *name); + void (*EnterSection_)(const char *name); + void (*ExitSection_)(const char *name); +}; + +class NewStateExternalFunctions : public NewState +{ +private: + void (*Save_)(const void *ptr, size_t size, const char *name); + void (*Load_)(void *ptr, size_t size, const char *name); + void (*EnterSection_)(const char *name); + void (*ExitSection_)(const char *name); +public: + NewStateExternalFunctions(const FPtrs *ff); + virtual void Save(const void *ptr, size_t size, const char *name); + virtual void Load(void *ptr, size_t size, const char *name); + virtual void EnterSection(const char *name); + virtual void ExitSection(const char *name); +}; + +// defines and explicitly instantiates +#define SYNCFUNC(x)\ + template void x::SyncState(NewState *ns);\ + template void x::SyncState(NewState *ns);\ + templatevoid x::SyncState(NewState *ns) + +// N = normal variable +// P = pointer to fixed size data +// S = "sub object" +// T = "ptr to sub object" +// R = pointer, store its offset from some other pointer +// E = general purpose cased value "enum" + + +// first line is default value in converted enum; last line is default value in argument x +#define EBS(x,d) do { int _ttmp = (d); if (isReader) ns->Load(&_ttmp, sizeof(_ttmp), #x); if (0) +#define EVS(x,v,n) else if (!isReader && (x) == (v)) _ttmp = (n); else if (isReader && _ttmp == (n)) (x) = (v) +#define EES(x,d) else if (isReader) (x) = (d); if (!isReader) ns->Save(&_ttmp, sizeof(_ttmp), #x); } while (0) + +#define RSS(x,b) do { if (isReader)\ +{ ptrdiff_t _ttmp; ns->Load(&_ttmp, sizeof(_ttmp), #x); (x) = (_ttmp == (ptrdiff_t)0xdeadbeef ? 0 : (b) + _ttmp); }\ + else\ +{ ptrdiff_t _ttmp = (x) == 0 ? 0xdeadbeef : (x) - (b); ns->Save(&_ttmp, sizeof(_ttmp), #x); } } while (0) + +#define PSS(x,s) do { if (isReader) ns->Load((x), (s), #x); else ns->Save((x), (s), #x); } while (0) + +#define NSS(x) do { if (isReader) ns->Load(&(x), sizeof(x), #x); else ns->Save(&(x), sizeof(x), #x); } while (0) + +#define SSS(x) do { ns->EnterSection(#x); (x).SyncState(ns); ns->ExitSection(#x); } while (0) + +#define TSS(x) do { ns->EnterSection(#x); (x)->SyncState(ns); ns->ExitSection(#x); } while (0) + +#endif diff --git a/lynx/ram.cpp b/lynx/ram.cpp index 1ba8c77ff1..e782c0e16f 100644 --- a/lynx/ram.cpp +++ b/lynx/ram.cpp @@ -62,3 +62,8 @@ void CRam::Reset(void) //MDFNMP_AddRAM(65536, 0x0000, mRamData); std::memset(mRamData, DEFAULT_RAM_CONTENTS, RAM_SIZE); } + +SYNCFUNC(CRam) +{ + NSS(mRamData); +} diff --git a/lynx/ram.h b/lynx/ram.h index 007c7ef8ea..0ce6b04d8f 100644 --- a/lynx/ram.h +++ b/lynx/ram.h @@ -79,6 +79,8 @@ public: uint32 ObjectSize(void) {return RAM_SIZE;}; uint8* GetRamPointer(void) { return mRamData; }; + templatevoid SyncState(NewState *ns); + // Data members private: diff --git a/lynx/rom.cpp b/lynx/rom.cpp index 67e50a4ff6..56d93b499e 100644 --- a/lynx/rom.cpp +++ b/lynx/rom.cpp @@ -47,7 +47,7 @@ CRom::CRom(const uint8 *romfile, uint32 length) { - mWriteEnable=FALSE; + //mWriteEnable = false; Reset(); std::memset(mRomData, DEFAULT_ROM_CONTENTS, ROM_SIZE); @@ -58,3 +58,9 @@ CRom::CRom(const uint8 *romfile, uint32 length) void CRom::Reset(void) { } + +SYNCFUNC(CRom) +{ + //NSS(mWriteEnable); + //NSS(mRomData); +} diff --git a/lynx/rom.h b/lynx/rom.h index 5c4109c760..6f27be7529 100644 --- a/lynx/rom.h +++ b/lynx/rom.h @@ -61,16 +61,18 @@ public: public: void Reset(void) MDFN_COLD; - void Poke(uint32 addr,uint8 data) { if(mWriteEnable) mRomData[addr&ROM_ADDR_MASK]=data;}; - uint8 Peek(uint32 addr) { return(mRomData[addr&ROM_ADDR_MASK]);}; - uint32 ReadCycle(void) {return 5;}; - uint32 WriteCycle(void) {return 5;}; - uint32 ObjectSize(void) {return ROM_SIZE;}; + void Poke(uint32 addr,uint8 data) { /*if(mWriteEnable) mRomData[addr&ROM_ADDR_MASK]=data;*/ } + uint8 Peek(uint32 addr) { return(mRomData[addr&ROM_ADDR_MASK]); } + uint32 ReadCycle(void) {return 5;} + uint32 WriteCycle(void) {return 5;} + uint32 ObjectSize(void) {return ROM_SIZE;} + + templatevoid SyncState(NewState *ns); // Data members public: - bool mWriteEnable; + //bool mWriteEnable; // always false private: uint8 mRomData[ROM_SIZE]; }; diff --git a/lynx/susie.cpp b/lynx/susie.cpp index 22ba6872da..2a03e99299 100644 --- a/lynx/susie.cpp +++ b/lynx/susie.cpp @@ -2129,3 +2129,106 @@ uint8 CSusie::Peek(uint32 addr) return 0xff; } + +SYNCFUNC(CSusie) +{ + NSS(lagged); + + NSS(cycles_used); + + NSS(mTMPADR); + NSS(mTILTACUM); + NSS(mHOFF); + NSS(mVOFF); + NSS(mVIDBAS); + NSS(mCOLLBAS); + NSS(mVIDADR); + NSS(mCOLLADR); + NSS(mSCBNEXT); + NSS(mSPRDLINE); + NSS(mHPOSSTRT); + NSS(mVPOSSTRT); + NSS(mSPRHSIZ); + NSS(mSPRVSIZ); + NSS(mSTRETCH); + NSS(mTILT); + NSS(mSPRDOFF); + NSS(mSPRVPOS); + NSS(mCOLLOFF); + NSS(mVSIZACUM); + NSS(mHSIZACUM); + NSS(mHSIZOFF); + NSS(mVSIZOFF); + NSS(mSCBADR); + NSS(mPROCADR); + + + NSS(mMATHABCD); + NSS(mMATHEFGH); + NSS(mMATHJKLM); + NSS(mMATHNP); + NSS(mMATHAB_sign); + NSS(mMATHCD_sign); + NSS(mMATHEFGH_sign); + + NSS(mSPRCTL0_Type); + NSS(mSPRCTL0_Vflip); + NSS(mSPRCTL0_Hflip); + NSS(mSPRCTL0_PixelBits); + + NSS(mSPRCTL1_StartLeft); + NSS(mSPRCTL1_StartUp); + NSS(mSPRCTL1_SkipSprite); + NSS(mSPRCTL1_ReloadPalette); + NSS(mSPRCTL1_ReloadDepth); + NSS(mSPRCTL1_Sizing); + NSS(mSPRCTL1_Literal); + + NSS(mSPRCOLL_Number); + NSS(mSPRCOLL_Collide); + + NSS(mSPRSYS_StopOnCurrent); + NSS(mSPRSYS_LeftHand); + NSS(mSPRSYS_VStretch); + NSS(mSPRSYS_NoCollide); + NSS(mSPRSYS_Accumulate); + NSS(mSPRSYS_SignedMath); + NSS(mSPRSYS_Status); + NSS(mSPRSYS_UnsafeAccess); + NSS(mSPRSYS_LastCarry); + NSS(mSPRSYS_Mathbit); + NSS(mSPRSYS_MathInProgress); + + NSS(mSUZYBUSEN); + + NSS(mSPRINIT); + + NSS(mSPRGO); + NSS(mEVERON); + + NSS(mPenIndex); + + + + NSS(mLineType); + NSS(mLineShiftRegCount); + NSS(mLineShiftReg); + NSS(mLineRepeatCount); + NSS(mLinePixel); + NSS(mLinePacketBitsLeft); + + NSS(mCollision); + + // mRamPointer; + + NSS(mLineBaseAddress); + NSS(mLineCollisionAddress); + + NSS(hquadoff); + NSS(vquadoff); + + + + NSS(mJOYSTICK); + NSS(mSWITCHES); +} diff --git a/lynx/susie.h b/lynx/susie.h index 470b56e904..e8e9de327c 100644 --- a/lynx/susie.h +++ b/lynx/susie.h @@ -329,6 +329,8 @@ class CSusie : public CLynxBase uint32 PaintSprites(void); bool lagged; // set to false whenever joystick/switches are read + templatevoid SyncState(NewState *ns); + private: void DoMathDivide(void); void DoMathMultiply(void); diff --git a/lynx/system.cpp b/lynx/system.cpp index 9114fd29ec..2635c957fe 100644 --- a/lynx/system.cpp +++ b/lynx/system.cpp @@ -188,6 +188,28 @@ bool CSystem::Advance(int buttons, uint32 *vbuff, int16 *sbuff, int &sbuffsize) return mSusie->lagged; } +SYNCFUNC(CSystem) +{ + // mMemMap regenerates the mMemoryHandlers directly on load + + TSS(mCart); + TSS(mRom); + TSS(mMemMap); + TSS(mRam); + TSS(mCpu); + TSS(mMikie); + TSS(mSusie); + + NSS(gSuzieDoneTime); + NSS(gSystemCycleCount); + NSS(gNextTimerEvent); + NSS(gSystemIRQ); + NSS(gSystemNMI); + NSS(gSystemCPUSleep); + NSS(gSystemHalt); + NSS(frameoverflow); +} + /* static MDFNSetting LynxSettings[] = diff --git a/lynx/system.h b/lynx/system.h index bcaf6b8f36..9502baa5ad 100644 --- a/lynx/system.h +++ b/lynx/system.h @@ -216,6 +216,8 @@ public: // frame overflow detection int frameoverflow; + + templatevoid SyncState(NewState *ns); }; #endif diff --git a/output/dll/bizlynx.dll b/output/dll/bizlynx.dll index d60ab6abceaf1886b185f209e546a3607fcbfd13..08214e2095ee9e9b61024eb85c98a16248202211 100644 GIT binary patch delta 28518 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 delta 9252 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 c&d}y)3$