sound: new ym2612 save states
authornotaz <notasas@gmail.com>
Fri, 7 Mar 2025 00:28:47 +0000 (02:28 +0200)
committerkub <derkub@gmail.com>
Sun, 9 Mar 2025 22:34:50 +0000 (23:34 +0100)
irixxxx/picodrive#166

pico/memory.c
pico/pico_int.h
pico/sound/sound.c
pico/sound/ym2612.c
pico/sound/ym2612.h
pico/state.c
pico/state.h

index 4ed955c..87b847f 100644 (file)
@@ -8,8 +8,10 @@
  * See COPYING file in the top-level directory.\r
  */\r
 \r
+#include <assert.h>\r
 #include "pico_int.h"\r
 #include "memory.h"\r
+#include "state.h"\r
 \r
 #include "sound/ym2612.h"\r
 #include "sound/sn76496.h"\r
@@ -1327,7 +1329,7 @@ static int ym2612_write_local(u32 a, u32 d, int is_from_z80)
           //elprintf(EL_STATUS, "%03i dac w %08x z80 %i", cycles, d, is_from_z80);\r
           if (ym2612.dacen)\r
             PsndDoDAC(cycles);\r
-          ym2612.dacout = ((int)d - 0x80) << 6;\r
+          ym2612.dacout = ((int)d - 0x80) << DAC_SHIFT;\r
           return 0;\r
         }\r
         case 0x2b: { /* DAC Sel  (YM2612) */\r
@@ -1374,10 +1376,11 @@ static u32 ym2612_read_local_68k(void)
   return ym2612.OPN.ST.status;\r
 }\r
 \r
-void ym2612_pack_state(void)\r
+int ym2612_pack_timers(void *buf, size_t size)\r
 {\r
   // timers are saved as tick counts, in 16.16 int format\r
   int tac, tat = 0, tbc, tbt = 0, busy = 0;\r
+  size_t b = 0;\r
   tac = 1024 - ym2612.OPN.ST.TA;\r
   tbc = 256  - ym2612.OPN.ST.TB;\r
   if (Pico.t.ym2612_busy > 0)\r
@@ -1388,18 +1391,26 @@ void ym2612_pack_state(void)
     tbt = ((Pico.t.timer_b_step - Pico.t.timer_b_next_oflow) * ((1LL<<32)/TIMER_B_TICK_ZCYCLES+1))>>16;\r
   elprintf(EL_YMTIMER, "save: timer a %i/%i", tat >> 16, tac);\r
   elprintf(EL_YMTIMER, "save: timer b %i/%i", tbt >> 16, tbc);\r
-\r
 #ifdef __GP2X__\r
   if (PicoIn.opt & POPT_EXT_FM)\r
     YM2612PicoStateSave2_940(tat, tbt);\r
   else\r
 #endif\r
-    YM2612PicoStateSave2(tat, tbt, busy);\r
+  {\r
+    //YM2612PicoStateSave2(tat, tbt, busy);\r
+    assert(size >= 16);\r
+    save_u16(buf, &b, ym2612.OPN.ST.TA);\r
+    save_u16(buf, &b, ym2612.OPN.ST.TB);\r
+    save_u32(buf, &b, tat);\r
+    save_u32(buf, &b, tbt);\r
+    save_u32(buf, &b, busy);\r
+  }\r
+  return b;\r
 }\r
 \r
-void ym2612_unpack_state(void)\r
+void ym2612_unpack_state_old(void)\r
 {\r
-  int i, ret, tac, tat, tbc, tbt, busy = 0;\r
+  int i, ret, tat, tbt, busy = 0;\r
   YM2612PicoStateLoad();\r
   Pico.t.m68c_frame_start = SekCyclesDone();\r
 \r
@@ -1435,7 +1446,30 @@ void ym2612_unpack_state(void)
     elprintf(EL_STATUS, "old ym2612 state");\r
     return; // no saved timers\r
   }\r
+  {\r
+    u8 tmp[16];\r
+    size_t b = 0;\r
+    save_u16(tmp, &b, ym2612.OPN.ST.TA);\r
+    save_u16(tmp, &b, ym2612.OPN.ST.TB);\r
+    save_u32(tmp, &b, tat);\r
+    save_u32(tmp, &b, tbt);\r
+    save_u32(tmp, &b, busy);\r
+    ym2612_unpack_timers(tmp, b);\r
+  }\r
+}\r
 \r
+void ym2612_unpack_timers(const void *buf, size_t size)\r
+{\r
+  int tac, tat, tbc, tbt, busy;\r
+  size_t b = 0;\r
+  assert(size >= 16);\r
+  if (size < 16)\r
+    return;\r
+  ym2612.OPN.ST.TA = load_u16(buf, &b);\r
+  ym2612.OPN.ST.TB = load_u16(buf, &b);\r
+  tat = load_u32(buf, &b);\r
+  tbt = load_u32(buf, &b);\r
+  busy = load_u32(buf, &b);\r
   Pico.t.ym2612_busy = cycles_68k_to_z80(busy);\r
   tac = (1024 - ym2612.OPN.ST.TA) << 16;\r
   tbc = (256  - ym2612.OPN.ST.TB) << 16;\r
index 02aa468..612783a 100644 (file)
@@ -904,8 +904,9 @@ void cdda_start_play(int lba_base, int lba_offset, int lb_len);
 #define YM2612_NATIVE_RATE() (((Pico.m.pal?OSC_PAL:OSC_NTSC)/7 + 3*24) / (6*24))\r
 \r
 void ym2612_sync_timers(int z80_cycles, int mode_old, int mode_new);\r
-void ym2612_pack_state(void);\r
-void ym2612_unpack_state(void);\r
+int  ym2612_pack_timers(void *buf_, size_t size);\r
+void ym2612_unpack_timers(const void *buf_, size_t size);\r
+void ym2612_unpack_state_old(void);\r
 \r
 #define TIMER_NO_OFLOW 0x70000000\r
 \r
index d7de6e9..6a62740 100644 (file)
@@ -170,6 +170,7 @@ void PsndRerate(int preserve_state)
   int ym2612_clock = Pico.m.pal ? OSC_PAL/7 : OSC_NTSC/7;\r
   int ym2612_rate = YM2612_NATIVE_RATE();\r
   int ym2612_init = !preserve_state;\r
+  int state_size = 4096;\r
 \r
   // don't init YM2612 if preserve_state and no parameter changes\r
   ym2612_init |= ymclock != ym2612_clock || ymopts != (PicoIn.opt & (POPT_DIS_FM_SSGEG|POPT_EN_FM_DAC));\r
@@ -179,10 +180,9 @@ void PsndRerate(int preserve_state)
   ymopts = PicoIn.opt & (POPT_DIS_FM_SSGEG|POPT_EN_FM_DAC);\r
 \r
   if (preserve_state && ym2612_init) {\r
-    state = malloc(0x204);\r
-    if (state == NULL) return;\r
-    ym2612_pack_state();\r
-    memcpy(state, YM2612GetRegs(), 0x204);\r
+    state = malloc(state_size);\r
+    if (state)\r
+      state_size = YM2612PicoStateSave3(state, state_size);\r
   }\r
 \r
   if (PicoIn.AHW & PAHW_SMS) {\r
@@ -207,10 +207,8 @@ void PsndRerate(int preserve_state)
     PsndFMUpdate = YM2612UpdateONE;\r
   }\r
 \r
-  if (preserve_state && ym2612_init) {\r
-    // feed it back it's own registers, just like after loading state\r
-    memcpy(YM2612GetRegs(), state, 0x204);\r
-    ym2612_unpack_state();\r
+  if (state) {\r
+    YM2612PicoStateLoad3(state, state_size);\r
     free(state);\r
   }\r
 \r
index efa2c10..3639b01 100644 (file)
 \r
 //#include <stdio.h>\r
 \r
+#include <assert.h>\r
 #include <string.h>\r
 #include <math.h>\r
 \r
@@ -153,7 +154,6 @@ void memset32(void *dest, int c, int count);
 #define FREQ_SH                        16  /* 16.16 fixed point (frequency calculations) */\r
 #define EG_SH                  16  /* 16.16 fixed point (envelope generator timing) */\r
 #define LFO_SH                 24  /*  8.24 fixed point (LFO calculations)       */\r
-#define TIMER_SH               16  /* 16.16 fixed point (timers calculations)    */\r
 \r
 #define ENV_BITS               10\r
 #define ENV_LEN                        (1<<ENV_BITS)\r
@@ -513,8 +513,6 @@ static INT32 lfo_pm_table[128*8*32]; /* 128 combinations of 7 bits meaningful (o
        but LFO works with one more bit of a precision so we really need 4096 elements */\r
 static UINT32 fn_table[4096];  /* fnumber->increment counter */\r
 \r
-static int g_lfo_ampm;\r
-\r
 /* register number to channel number , slot offset */\r
 #define OPN_CHAN(N) (N&3)\r
 #define OPN_SLOT(N) ((N>>2)&3)\r
@@ -1228,7 +1226,7 @@ static chan_rend_context crct;
 static void chan_render_prep(void)\r
 {\r
        crct.eg_timer_add = ym2612.OPN.eg_timer_add;\r
-       crct.lfo_init_sft16 = g_lfo_ampm << 16;\r
+       crct.lfo_init_sft16 = ym2612.OPN.lfo_ampm << 16;\r
        crct.lfo_inc = ym2612.OPN.lfo_inc;\r
 }\r
 \r
@@ -1437,9 +1435,9 @@ static void reset_channels(FM_CH *CH)
 \r
        ym2612.OPN.ST.mode   = 0;       /* normal mode */\r
        ym2612.OPN.ST.TA     = 0;\r
-       ym2612.OPN.ST.TAC    = 0;\r
+       //ym2612.OPN.ST.TAC    = 0;\r
        ym2612.OPN.ST.TB     = 0;\r
-       ym2612.OPN.ST.TBC    = 0;\r
+       //ym2612.OPN.ST.TBC    = 0;\r
 \r
        for( c = 0 ; c < 6 ; c++ )\r
        {\r
@@ -1815,7 +1813,7 @@ int YM2612UpdateOne_(s32 *buffer, int length, int stereo, int is_buf_empty)
        if (ym2612.slot_mask & 0x00f000) active_chs |= chan_render(buffer, length, 3, flags|((pan&0x0c0)>>2)) << 3;\r
        BIT_IF(flags, 1, (ym2612.ssg_mask & 0x0f0000) && (ym2612.OPN.ST.flags & 1));\r
        if (ym2612.slot_mask & 0x0f0000) active_chs |= chan_render(buffer, length, 4, flags|((pan&0x300)>>4)) << 4;\r
-       g_lfo_ampm = crct.pack >> 16; // need_save; now because ch5 might skip updating it\r
+       ym2612.OPN.lfo_ampm = crct.pack >> 16; // need_save; now because ch5 might skip updating it\r
        BIT_IF(flags, 1, (ym2612.ssg_mask & 0xf00000) && (ym2612.OPN.ST.flags & 1));\r
        if (ym2612.slot_mask & 0xf00000) active_chs |= chan_render(buffer, length, 5, flags|((pan&0xc00)>>6)|(!!ym2612.dacen<<2)) << 5;\r
 #undef BIT_IF\r
@@ -1856,7 +1854,7 @@ void YM2612ResetChip_(void)
        ym2612.OPN.eg_cnt   = 0;\r
        ym2612.OPN.lfo_inc = 0;\r
        ym2612.OPN.lfo_cnt = 0;\r
-       g_lfo_ampm = 126 << 8;\r
+       ym2612.OPN.lfo_ampm = 126 << 8;\r
        ym2612.OPN.ST.status = 0;\r
 \r
        reset_channels( &ym2612.CH[0] );\r
@@ -1917,7 +1915,7 @@ int YM2612Write_(unsigned int a, unsigned int v)
                                {\r
                                        ym2612.OPN.lfo_inc = 0;\r
                                        ym2612.OPN.lfo_cnt = 0;\r
-                                       g_lfo_ampm = 126 << 8;\r
+                                       ym2612.OPN.lfo_ampm = 126 << 8;\r
                                }\r
                                break;\r
 #if 0 // handled elsewhere\r
@@ -1970,7 +1968,7 @@ int YM2612Write_(unsigned int a, unsigned int v)
                                        break;\r
                                }\r
                        case 0x2a:      /* DAC data (YM2612) */\r
-                               ym2612.dacout = ((int)v - 0x80) << 6;   /* level unknown (notaz: 8 seems to be too much) */\r
+                               ym2612.dacout = ((int)v - 0x80) << DAC_SHIFT;\r
                                ret=0;\r
                                break;\r
                        case 0x2b:      /* DAC Sel  (YM2612) */\r
@@ -2030,6 +2028,7 @@ void YM2612PicoStateLoad_(void)
 }\r
 \r
 /* rather stupid design because I wanted to fit in unused register "space" */\r
+// TODO remove all this along with ym2612.REGS\r
 typedef struct\r
 {\r
        UINT32  state_phase;\r
@@ -2064,7 +2063,6 @@ typedef struct
 #define _block_fnum op1_out_l\r
 #define _block_fnum_sl3 unused_sl3\r
 \r
-\r
 void YM2612PicoStateSave2(int tat, int tbt, int busy)\r
 {\r
        ym_save_addon_slot ss;\r
@@ -2140,7 +2138,7 @@ void YM2612PicoStateSave2(int tat, int tbt, int busy)
        sa.eg_cnt  = ym2612.OPN.eg_cnt;\r
        sa.eg_timer = ym2612.OPN.eg_timer;\r
        sa.lfo_cnt  = ym2612.OPN.lfo_cnt;\r
-       sa.lfo_ampm = g_lfo_ampm;\r
+       sa.lfo_ampm = ym2612.OPN.lfo_ampm;\r
        sa.busy_timer = busy;\r
        //sa.keyon_field = ym2612.slot_mask;\r
        memcpy(ptr, &sa, sizeof(sa)); // 0x30 max\r
@@ -2168,7 +2166,7 @@ int YM2612PicoStateLoad2(int *tat, int *tbt, int *busy)
        ym2612.OPN.eg_cnt = sa.eg_cnt;\r
        ym2612.OPN.eg_timer = sa.eg_timer;\r
        ym2612.OPN.lfo_cnt = sa.lfo_cnt;\r
-       g_lfo_ampm = sa.lfo_ampm;\r
+       ym2612.OPN.lfo_ampm = sa.lfo_ampm;\r
        ym2612.slot_mask = sa.keyon_field;\r
        if (tat != NULL) *tat = sa.TAT;\r
        if (tbt != NULL) *tbt = sa.TBT;\r
@@ -2247,6 +2245,240 @@ int YM2612PicoStateLoad2(int *tat, int *tbt, int *busy)
        return 0;\r
 }\r
 \r
+#include "../state.h"\r
+\r
+#define SLOT_SIZE_MIN 46\r
+#define CH_SIZE_MIN 16\r
+#define OTHER_SIZE_MIN 35\r
+\r
+static size_t save_slot(u8 *buf, const FM_SLOT *slot)\r
+{\r
+       size_t tmp, b = 0;\r
+\r
+       b++; // length, assumes slot state won't grow beyond 255\r
+       tmp = (slot->DT - ym2612.OPN.ST.dt_tab[0]) / sizeof(ym2612.OPN.ST.dt_tab[0]);\r
+       save_u8_(buf, &b, tmp);\r
+       save_u8_(buf, &b, slot->ar);\r
+       save_u8_(buf, &b, slot->d1r);\r
+       save_u8_(buf, &b, slot->d2r);\r
+       save_u8_(buf, &b, slot->rr);\r
+       save_u8_(buf, &b, slot->mul);\r
+       save_u32(buf, &b, slot->phase);\r
+       save_u32(buf, &b, slot->Incr);\r
+       save_u8_(buf, &b, slot->KSR);\r
+       save_u8_(buf, &b, slot->ksr);\r
+       save_u8_(buf, &b, slot->key);\r
+       save_u8_(buf, &b, slot->state);\r
+       save_u8_(buf, &b, slot->tl >> (ENV_BITS-7));\r
+       save_u16(buf, &b, slot->volume);\r
+       save_u32(buf, &b, slot->sl);\r
+       save_u32(buf, &b, slot->eg_pack_rr);\r
+       save_u32(buf, &b, slot->eg_pack_d2r);\r
+       save_u32(buf, &b, slot->eg_pack_d1r);\r
+       save_u32(buf, &b, slot->eg_pack_ar);\r
+       save_u8_(buf, &b, slot->ssg);\r
+       save_u8_(buf, &b, slot->ssgn);\r
+       save_u8_(buf, &b, slot->ar_ksr);\r
+       save_u16(buf, &b, slot->vol_out);\r
+\r
+       //printf("slot size: %zd\n", b);\r
+       assert(b >= SLOT_SIZE_MIN);\r
+       assert(b < 256u);\r
+       buf[0] = b - 1;\r
+       return b;\r
+}\r
+\r
+static void load_slot(const u8 *buf, FM_SLOT *slot)\r
+{\r
+       size_t b = 0;\r
+       u8 dt_reg;\r
+\r
+       dt_reg       = load_u8_(buf, &b);\r
+       slot->ar     = load_u8_(buf, &b);\r
+       slot->d1r    = load_u8_(buf, &b);\r
+       slot->d2r    = load_u8_(buf, &b);\r
+       slot->rr     = load_u8_(buf, &b);\r
+       slot->mul    = load_u8_(buf, &b);\r
+       slot->phase  = load_u32(buf, &b);\r
+       slot->Incr   = load_u32(buf, &b);\r
+       slot->KSR    = load_u8_(buf, &b);\r
+       slot->ksr    = load_u8_(buf, &b);\r
+       slot->key    = load_u8_(buf, &b);\r
+       slot->state  = load_u8_(buf, &b);\r
+       slot->tl     = load_u8_(buf, &b) << (ENV_BITS-7);\r
+       slot->volume = load_s16(buf, &b);\r
+       slot->sl     = load_u32(buf, &b);\r
+       slot->eg_pack_rr  = load_u32(buf, &b);\r
+       slot->eg_pack_d2r = load_u32(buf, &b);\r
+       slot->eg_pack_d1r = load_u32(buf, &b);\r
+       slot->eg_pack_ar  = load_u32(buf, &b);\r
+       slot->ssg     = load_u8_(buf, &b);\r
+       slot->ssgn    = load_u8_(buf, &b);\r
+       slot->ar_ksr  = load_u8_(buf, &b);\r
+       slot->vol_out = load_u16(buf, &b);\r
+\r
+       assert(dt_reg < 8);\r
+       slot->DT = ym2612.OPN.ST.dt_tab[dt_reg & 7];\r
+}\r
+\r
+static size_t save_channel(u8 *buf, const FM_CH *ch)\r
+{\r
+       int i, size_pos;\r
+       size_t b = 0;\r
+\r
+       for (i = 0; i < 4; i++)\r
+               b += save_slot(&buf[b], &ch->SLOT[i]);\r
+       size_pos = b++;\r
+       save_u8_(buf, &b, ch->ALGO);\r
+       save_u8_(buf, &b, ch->FB);\r
+       save_u32(buf, &b, ch->op1_out);\r
+       save_s16(buf, &b, ch->mem_value); // fits in 16bit\r
+       save_u8_(buf, &b, ch->pms); // max 7*32\r
+       save_u8_(buf, &b, ch->ams);\r
+       save_u8_(buf, &b, ch->kcode);\r
+       save_u8_(buf, &b, ch->upd_cnt);\r
+       // ch->fc is derived from .block_fnum\r
+       save_u16(buf, &b, ch->block_fnum);\r
+       save_u8_(buf, &b, ch->AMmasks);\r
+\r
+       assert(b - size_pos - 1 < 256u);\r
+       buf[size_pos] = b - size_pos - 1;\r
+       return b;\r
+}\r
+\r
+static size_t load_channel(const u8 *buf, size_t size, FM_CH *ch)\r
+{\r
+       size_t i, b = 0, slot_size = 0, ch_size;\r
+       u32 fn, blk;\r
+\r
+       for (i = 0; i < 4; i++) {\r
+               u8 size_next = load_u8_(buf, &slot_size);\r
+               if (size_next < SLOT_SIZE_MIN)\r
+                       return 0;\r
+               if (slot_size + size_next > size)\r
+                       return 0;\r
+               load_slot(&buf[slot_size], &ch->SLOT[i]);\r
+               slot_size += size_next;\r
+       }\r
+       if (slot_size + CH_SIZE_MIN > size)\r
+               return 0;\r
+       b = slot_size;\r
+       ch_size        = load_u8_(buf, &b);\r
+       ch->ALGO       = load_u8_(buf, &b);\r
+       ch->FB         = load_u8_(buf, &b);\r
+       ch->op1_out    = load_u32(buf, &b);\r
+       ch->mem_value  = load_s16(buf, &b);\r
+       ch->pms        = load_u8_(buf, &b);\r
+       ch->ams        = load_u8_(buf, &b);\r
+       ch->kcode      = load_u8_(buf, &b);\r
+       ch->upd_cnt    = load_u8_(buf, &b);\r
+       ch->block_fnum = load_u16(buf, &b) & 0x3fff;\r
+       ch->AMmasks    = load_u8_(buf, &b);\r
+\r
+       fn = ch->block_fnum & 0x7ff;\r
+       blk = ch->block_fnum >> 11;\r
+       ch->fc = fn_table[fn*2] >> (7 - blk);\r
+\r
+       assert(ch_size >= b - slot_size - 1);\r
+       return slot_size + 1 + ch_size;\r
+}\r
+\r
+size_t YM2612PicoStateSave3(void *buf_, size_t size)\r
+{\r
+       size_t i, b = 0;\r
+       u8 *buf = buf_;\r
+       u8 lfo_inc_reg = 0;\r
+\r
+       for (i = 0; i < 8; i++) {\r
+               if (ym2612.OPN.lfo_inc == ym2612.OPN.lfo_freq[i]) {\r
+                       lfo_inc_reg = i + 1;\r
+                       break;\r
+               }\r
+       }\r
+       assert(ym2612.OPN.lfo_inc == 0 || i < 8);\r
+\r
+       for (i = 0; i < 6; i++)\r
+               b += save_channel(&buf[b], &ym2612.CH[i]);\r
+       save_u8_(buf, &b, ym2612.OPN.ST.address);\r
+       save_u8_(buf, &b, ym2612.OPN.ST.status);\r
+       save_u8_(buf, &b, ym2612.OPN.ST.mode);\r
+       save_u8_(buf, &b, ym2612.OPN.ST.flags);\r
+       // (timers are saved in CHUNK_FM_TIMERS)\r
+       save_u8_(buf, &b, ym2612.OPN.ST.fn_h);\r
+       save_u8_(buf, &b, ym2612.OPN.SL3.fn_h);\r
+       for (i = 0; i < 3; i++) {\r
+               // ym2612.OPN.SL3.fc is derived from .block_fnum\r
+               save_u8_(buf, &b, ym2612.OPN.SL3.kcode[i]);\r
+               save_u16(buf, &b, ym2612.OPN.SL3.block_fnum[i]);\r
+       }\r
+       save_u16(buf, &b, ym2612.OPN.pan);\r
+       save_u16(buf, &b, ym2612.OPN.eg_cnt);\r
+       save_u16(buf, &b, ym2612.OPN.eg_timer);\r
+       save_u32(buf, &b, ym2612.OPN.lfo_cnt);\r
+       save_u16(buf, &b, ym2612.OPN.lfo_ampm);\r
+       save_u8_(buf, &b, lfo_inc_reg);\r
+       save_u8_(buf, &b, ym2612.addr_A1);\r
+       save_u8_(buf, &b, ym2612.dacen);\r
+       save_s8_(buf, &b, ym2612.dacout >> DAC_SHIFT);\r
+       save_u32(buf, &b, ym2612.ssg_mask);\r
+\r
+       //printf("ym2612 state size: %zu\n", b);\r
+       assert(b <= size);\r
+       return b;\r
+}\r
+\r
+void YM2612PicoStateLoad3(const void *buf_, size_t size)\r
+{\r
+       const u8 *buf = buf_;\r
+       size_t i, b = 0;\r
+       u8 lfo_inc_reg = 0;\r
+\r
+       for (i = 0; i < 6; i++) {\r
+               size_t r = load_channel(&buf[b], size - b, &ym2612.CH[i]);\r
+               if (!r)\r
+                       goto broken;\r
+               b += r;\r
+       }\r
+       if (b + OTHER_SIZE_MIN > size)\r
+               goto broken;\r
+       ym2612.OPN.ST.address = load_u8_(buf, &b);\r
+       ym2612.OPN.ST.status  = load_u8_(buf, &b);\r
+       ym2612.OPN.ST.mode    = load_u8_(buf, &b);\r
+       ym2612.OPN.ST.flags   = load_u8_(buf, &b);\r
+       ym2612.OPN.ST.fn_h    = load_u8_(buf, &b);\r
+       ym2612.OPN.SL3.fn_h   = load_u8_(buf, &b);\r
+       for (i = 0; i < 3; i++) {\r
+               u32 fn, blk;\r
+               ym2612.OPN.SL3.kcode[i] = load_u8_(buf, &b);\r
+               ym2612.OPN.SL3.block_fnum[i] = load_u16(buf, &b) & 0x3fff;\r
+\r
+               fn = ym2612.OPN.SL3.block_fnum[i] & 0x7ff;\r
+               blk = ym2612.OPN.SL3.block_fnum[i] >> 11;\r
+               ym2612.OPN.SL3.fc[i] = fn_table[fn*2] >> (7 - blk);\r
+       }\r
+       ym2612.OPN.pan      = load_u16(buf, &b);\r
+       ym2612.OPN.eg_cnt   = load_u16(buf, &b);\r
+       ym2612.OPN.eg_timer = load_u16(buf, &b);\r
+       ym2612.OPN.lfo_cnt  = load_u32(buf, &b);\r
+       ym2612.OPN.lfo_ampm = load_u16(buf, &b);\r
+       lfo_inc_reg         = load_u8_(buf, &b);\r
+       ym2612.addr_A1      = load_u8_(buf, &b);\r
+       ym2612.dacen        = load_u8_(buf, &b);\r
+       ym2612.dacout       = load_s8_(buf, &b);\r
+       ym2612.ssg_mask     = load_u32(buf, &b);\r
+\r
+       assert(lfo_inc_reg < 9u);\r
+       ym2612.OPN.lfo_inc = 0;\r
+       if (lfo_inc_reg)\r
+               ym2612.OPN.lfo_inc = ym2612.OPN.lfo_freq[--lfo_inc_reg & 7];\r
+       ym2612.dacout = (u32)ym2612.dacout << DAC_SHIFT;\r
+       ym2612.slot_mask = 0xffffff;\r
+       //printf("ym2612 state size: %zu\n", b);\r
+       return;\r
+broken:\r
+       elprintf(EL_STATUS, "broken ym2612 state");\r
+}\r
+\r
 void *YM2612GetRegs(void)\r
 {\r
        return ym2612.REGS;\r
index 56ec5ef..bdf53be 100644 (file)
@@ -5,6 +5,10 @@
 #ifndef _H_FM_FM_\r
 #define _H_FM_FM_\r
 \r
+#include <stdlib.h>\r
+\r
+#define DAC_SHIFT 6\r
+\r
 /* compiler dependence */\r
 #include "../pico_types.h"\r
 #ifndef UINT8\r
@@ -98,13 +102,13 @@ typedef struct
        UINT8   mode;           /* mode  CSM / 3SLOT    */\r
        UINT8   flags;          /* operational flags    */\r
        int             TA;                     /* timer a              */\r
-       int             TAC;            /* timer a maxval       */\r
-       int             TAT;            /* timer a ticker | need_save */\r
+       //int           TAC;            /* timer a maxval       */\r
+       //int           TAT;            /* timer a ticker | need_save */\r
        UINT8   TB;                     /* timer b              */\r
        UINT8   fn_h;           /* freq latch           */\r
        UINT8   pad2[2];\r
-       int             TBC;            /* timer b maxval       */\r
-       int             TBT;            /* timer b ticker | need_save */\r
+       //int           TBC;            /* timer b maxval       */\r
+       //int           TBT;            /* timer b ticker | need_save */\r
        /* local time tables */\r
        INT32   dt_tab[8][32];/* DeTune table       */\r
 } FM_ST;\r
@@ -139,6 +143,7 @@ typedef struct
        /* LFO */\r
        UINT32  lfo_cnt;                /* need_save */\r
        UINT32  lfo_inc;\r
+       UINT32  lfo_ampm;\r
 \r
        UINT32  lfo_freq[8];    /* LFO FREQ table */\r
 } FM_OPN;\r
@@ -146,7 +151,7 @@ typedef struct
 /* here's the virtual YM2612 */\r
 typedef struct\r
 {\r
-       UINT8           REGS[0x200];                    /* registers (for save states)       */\r
+       UINT8           REGS[0x200];                    /* registers (for old save states) */\r
        INT32           addr_A1;                        /* address line A1 | need_save       */\r
 \r
        FM_CH           CH[6];                          /* channel state */\r
@@ -177,8 +182,9 @@ int  YM2612PicoTick_(int n);
 void YM2612PicoStateLoad_(void);\r
 \r
 void *YM2612GetRegs(void);\r
-void YM2612PicoStateSave2(int tat, int tbt, int busy);\r
 int  YM2612PicoStateLoad2(int *tat, int *tbt, int *busy);\r
+size_t YM2612PicoStateSave3(void *buf_, size_t size);\r
+void   YM2612PicoStateLoad3(const void *buf_, size_t size);\r
 \r
 /* NB must be macros for compiling GP2X 940 code */\r
 #ifndef __GP2X__\r
index 9625621..e1d7fe1 100644 (file)
@@ -138,6 +138,8 @@ typedef enum {
   CHUNK_PICO,\r
   CHUNK_CD_MSD,\r
   CHUNK_VDP,\r
+  CHUNK_FM_TIMERS,\r
+  CHUNK_FMv3,\r
   //\r
   CHUNK_DEFAULT_COUNT,\r
   CHUNK_CARTHW_ = CHUNK_CARTHW,  // 64 (defined in PicoInt)\r
@@ -256,9 +258,11 @@ static int state_save(void *file)
       CHECKED_WRITE(CHUNK_PICO_PCM, len, buf2);\r
       CHECKED_WRITE(CHUNK_PICO, sizeof(PicoPicohw), &PicoPicohw);\r
     } else {\r
-      ym2612_pack_state();\r
-      ym_regs = YM2612GetRegs();\r
-      CHECKED_WRITE(CHUNK_FM, 0x200+4, ym_regs);\r
+      // write fm state first since timer load needs OPN.ST.mode\r
+      len = YM2612PicoStateSave3(buf2, CHUNK_LIMIT_W);\r
+      CHECKED_WRITE(CHUNK_FMv3, len, buf2);\r
+      len = ym2612_pack_timers(buf2, CHUNK_LIMIT_W);\r
+      CHECKED_WRITE(CHUNK_FM_TIMERS, len, buf2);\r
     }\r
 \r
     if (!(PicoIn.opt & POPT_DIS_IDLE_DET))\r
@@ -479,7 +483,15 @@ static int state_load(void *file)
       case CHUNK_FM:\r
         ym_regs = YM2612GetRegs();\r
         CHECKED_READ2(0x200+4, ym_regs);\r
-        ym2612_unpack_state();\r
+        ym2612_unpack_state_old();\r
+        break;\r
+      case CHUNK_FM_TIMERS:\r
+        CHECKED_READ(len, buf);\r
+        ym2612_unpack_timers(buf, len);\r
+        break;\r
+      case CHUNK_FMv3:\r
+        CHECKED_READ(len, buf);\r
+        YM2612PicoStateLoad3(buf, len);\r
         break;\r
 \r
       case CHUNK_PICO_PCM:\r
index 1d38022..4f49590 100644 (file)
@@ -1,4 +1,6 @@
 #include <stdlib.h>
+#include <assert.h>
+#include "pico_types.h"
 
 typedef size_t (arearw)(void *p, size_t _size, size_t _n, void *file);
 typedef size_t (areaeof)(void *file);
@@ -7,3 +9,70 @@ typedef int    (areaclose)(void *file);
 
 int PicoStateFP(void *afile, int is_save,
   arearw *read, arearw *write, areaeof *eof, areaseek *seek);
+
+static inline void save_u8_(u8 *buf, size_t *b, u32 u)
+{
+       assert(!(u & ~0xff));
+       buf[(*b)++] = u;
+}
+
+static inline void save_s8_(u8 *buf, size_t *b, s32 s)
+{
+       s32 s_sext = (s32)((u32)s << 24) >> 24;
+       assert(s == s_sext); (void)s_sext;
+       buf[(*b)++] = s;
+}
+
+static inline void save_u16(u8 *buf, size_t *b, u32 u)
+{
+       assert(!(u & ~0xffff));
+       buf[(*b)++] = u;
+       buf[(*b)++] = u >> 8;
+}
+
+static inline void save_s16(u8 *buf, size_t *b, s32 s)
+{
+       s32 s_sext = (s32)((u32)s << 16) >> 16;
+       assert(s == s_sext); (void)s_sext;
+       buf[(*b)++] = s;
+       buf[(*b)++] = s >> 8;
+}
+
+static inline void save_u32(u8 *buf, size_t *b, u32 u)
+{
+       buf[(*b)++] = u;
+       buf[(*b)++] = u >> 8;
+       buf[(*b)++] = u >> 16;
+       buf[(*b)++] = u >> 24;
+}
+
+static inline u8 load_u8_(const u8 *buf, size_t *b)
+{
+       return buf[(*b)++];
+}
+
+static inline s8 load_s8_(const u8 *buf, size_t *b)
+{
+       return buf[(*b)++];
+}
+
+static inline u16 load_u16(const u8 *buf, size_t *b)
+{
+       u16 r = (buf[*b + 1] << 8) | buf[*b];
+       (*b) += 2;
+       return r;
+}
+
+static inline s16 load_s16(const u8 *buf, size_t *b)
+{
+       s16 r = (buf[*b + 1] << 8) | buf[*b];
+       (*b) += 2;
+       return r;
+}
+
+static inline u32 load_u32(const u8 *buf, size_t *b)
+{
+       u32 r = (buf[*b + 3] << 24) | (buf[*b + 2] << 16) | (buf[*b + 1] << 8) | buf[*b];
+       (*b) += 4;
+       return r;
+}