From 4a5211469b658a976ecf03b034ec87c83ec517b6 Mon Sep 17 00:00:00 2001
From: notaz <notasas@gmail.com>
Date: Tue, 11 Mar 2025 01:13:32 +0200
Subject: [PATCH] core, new io save states

irixxxx/picodrive#166
---
 pico/memory.c   | 37 ++++++++++++++++++++++++++++++++++---
 pico/pico_int.h |  2 ++
 pico/state.c    | 18 ++++++++----------
 3 files changed, 44 insertions(+), 13 deletions(-)

diff --git a/pico/memory.c b/pico/memory.c
index f1724937..cfdd48c8 100644
--- a/pico/memory.c
+++ b/pico/memory.c
@@ -470,8 +470,8 @@ static port_read_func *port_readers[3] = {
   read_nothing
 };
 
-static int padTHLatency[3]; // TODO this should be in the save file structures
-static int padTLLatency[3]; // TODO this should be in the save file structures
+static int padTHLatency[3];
+static int padTLLatency[3];
 
 static NOINLINE u32 port_read(int i)
 {
@@ -483,7 +483,7 @@ static NOINLINE u32 port_read(int i)
 
   // pull-ups: should be 0x7f, but Decap Attack has a bug where it temp.
   // disables output before doing TH-low read, so emulate RC filter for TH.
-  // Decap Attack reportedly doesn't work on Nomad but works on must
+  // Decap Attack reportedly doesn't work on Nomad but works on most
   // other MD revisions (different pull-up strength?).
   u32 mask = 0x3f;
   if (CYCLES_GE(SekCyclesDone(), padTHLatency[i])) {
@@ -609,6 +609,37 @@ NOINLINE void io_ports_write(u32 a, u32 d)
   PicoMem.ioports[a] = d;
 }
 
+int io_ports_pack(void *buf, size_t size)
+{
+  size_t b, i;
+  memcpy(buf, PicoMem.ioports, (b = sizeof(PicoMem.ioports)));
+  for (i = 0; i < ARRAY_SIZE(Pico.m.padTHPhase); i++)
+    save_u8_(buf, &b, Pico.m.padTHPhase[i]);
+  for (i = 0; i < ARRAY_SIZE(Pico.m.padDelay); i++)
+    save_u8_(buf, &b, Pico.m.padDelay[i]);
+  for (i = 0; i < ARRAY_SIZE(padTHLatency); i++) {
+    save_u32(buf, &b, padTHLatency[i]);
+    save_u32(buf, &b, padTLLatency[i]);
+  }
+  assert(b <= size);
+  return b;
+}
+
+void io_ports_unpack(const void *buf, size_t size)
+{
+  size_t b, i;
+  memcpy(PicoMem.ioports, buf, (b = sizeof(PicoMem.ioports)));
+  for (i = 0; i < ARRAY_SIZE(Pico.m.padTHPhase); i++)
+    Pico.m.padTHPhase[i] = load_u8_(buf, &b);
+  for (i = 0; i < ARRAY_SIZE(Pico.m.padDelay); i++)
+    Pico.m.padDelay[i] = load_u8_(buf, &b);
+  for (i = 0; i < ARRAY_SIZE(padTHLatency); i++) {
+    padTHLatency[i] = load_u32(buf, &b);
+    padTLLatency[i] = load_u32(buf, &b);
+  }
+  assert(b <= size);
+}
+
 static int z80_cycles_from_68k(void)
 {
   int m68k_cnt = SekCyclesDone() - Pico.t.m68c_frame_start;
diff --git a/pico/pico_int.h b/pico/pico_int.h
index 585cb089..aa5fcf3c 100644
--- a/pico/pico_int.h
+++ b/pico/pico_int.h
@@ -756,6 +756,8 @@ u32 PicoRead16_io(u32 a);
 void PicoWrite8_io(u32 a, u32 d);
 void PicoWrite16_io(u32 a, u32 d);
 u32 PicoReadPad(int i, u32 mask);
+int io_ports_pack(void *buf, size_t size);
+void io_ports_unpack(const void *buf, size_t size);
 
 // pico/memory.c
 PICO_INTERNAL void PicoMemSetupPico(void);
diff --git a/pico/state.c b/pico/state.c
index 914fc41b..72129139 100644
--- a/pico/state.c
+++ b/pico/state.c
@@ -108,7 +108,7 @@ typedef enum {
   CHUNK_RC,      // old
   CHUNK_MISC_CD,
   //
-  CHUNK_IOPORTS, // versions < 1.70 did not save that..
+  CHUNK_IOPORTS, // old
   CHUNK_SMS,     // 25
   // 32x
   CHUNK_MSH2,
@@ -139,7 +139,8 @@ typedef enum {
   CHUNK_CD_MSD,
   CHUNK_VDP,
   CHUNK_FM_TIMERS,
-  CHUNK_FMv3,
+  CHUNK_FMv3 = 60,
+  CHUNK_IOPORTSv2,
   //
   CHUNK_DEFAULT_COUNT,
   CHUNK_CARTHW_ = CHUNK_CARTHW,  // 64 (defined in PicoInt)
@@ -253,6 +254,8 @@ static int state_save(void *file)
     CHECKED_WRITE_BUFF(CHUNK_RAM,   PicoMem.ram);
     CHECKED_WRITE_BUFF(CHUNK_VSRAM, PicoMem.vsram);
     CHECKED_WRITE_BUFF(CHUNK_IOPORTS, PicoMem.ioports);
+    len = io_ports_pack(buf2, CHUNK_LIMIT_W);
+    CHECKED_WRITE(CHUNK_IOPORTSv2, len, buf2);
     if (PicoIn.AHW & PAHW_PICO) {
       len = PicoPicoPCMSave(buf2, CHUNK_LIMIT_W);
       CHECKED_WRITE(CHUNK_PICO_PCM, len, buf2);
@@ -491,14 +494,9 @@ static int state_load(void *file)
         CHECKED_READ2(0x200+4, ym_regs);
         ym2612_unpack_state_old();
         break;
-      case CHUNK_FM_TIMERS:
-        CHECKED_READ(len, buf);
-        ym2612_unpack_timers(buf, len);
-        break;
-      case CHUNK_FMv3:
-        CHECKED_READ(len, buf);
-        YM2612PicoStateLoad3(buf, len);
-        break;
+      case CHUNK_FM_TIMERS: CHECKED_READ(len, buf); ym2612_unpack_timers(buf, len); break;
+      case CHUNK_FMv3:      CHECKED_READ(len, buf); YM2612PicoStateLoad3(buf, len); break;
+      case CHUNK_IOPORTSv2: CHECKED_READ(len, buf); io_ports_unpack(buf, len); break;
 
       case CHUNK_PICO_PCM:
         CHECKED_READ(len, buf);
-- 
2.39.5