Save states: the missing YM2203/YM2610/YM2612, MultiPCM and 6809 state (RunAhead, netplay) (#2176)

Loading a state, and so every RunAhead or netplay rollback, put back an
incomplete sound chip or CPU, and the game went on differently from the
run that never loaded: the audio of the Mega Drive based boards, Neo Geo
and the YM2203 games diverged, and on the 6809 games the game itself.

OPN chips (sound/fm.c):
- The reset sets the L/R pan of all the channels (0xb4-0xb6) and clears
  0x30-0xb2 without storing them in REGS; the postload rewrites REGS, so
  a channel the game never panned itself was muted after a load (the DAC
  of the Bare Knuckle games). The reset values are now in REGS (YM2203,
  YM2608, YM2610, YM2612), and the YM2612 DAC select too.
- The postloads of the YM2608 and YM2610 skipped 0xb6 (pan, AMS, PMS of
  the 3rd channel of each bank), as the YM2612 one did before.
- The chip recomputes its phase steps and envelope rates only when a
  register asks for it, so they can lag behind the registers (e.g. after
  a 3 slot mode change). They are now saved as they are, with the key
  codes and the LFO step, instead of being remade from the registers. The
  YM2612 and YM2610 extra state is one shared block (OPN_EXT), now also
  used by the YM2203, which had none.
- The YM2612 DAC value is saved as it is: before the first write it is 0,
  which is not what the register gives (aladmdb).
- YM2610: the extra FM and SSG state was opt-in (only zn.c asked for it);
  it is now always saved (Neo Geo). The opt-in FM timers stay as they were.

SSG (sound/ay8910.c): the periods of the SSG of a YM chip are saved as they
were made; the register rewrite of the postload remade them with the
current clock. The YM2203 now saves the SSG extra state too.

MultiPCM (sound/multipcm.c): the sample pointer of a voice was remade from
the registers at load, without the bank and with the sample number of now,
not of the key on; it is saved as its offset in the ROM. The release
counter is saved too (Star Wars Arcade).

6809 family: the M6809 did not save the accumulators (D), the HD6309 D and
W; the M6809, HD6309 and Konami did not save the cycles of an interrupt
taken outside the execute loop.


Claude-Session: https://claude.ai/code/session_01N3fzdsnzkQVtgYtYzqepjX

Co-authored-by: Claude <[email protected]>
This commit is contained in:
zanacandClaude authored and GitHub committed 2026-10-28 23:13:45 -04:00
1 parent 9649c81834
commit 6266128ce8
8 files changed
+190 -156

No files matched your search

+3
View File
@@ -587,6 +587,8 @@ void hd6309_init(void)
state_save_register_UINT16("hd6309", cpu, "X", &X, 1);
state_save_register_UINT16("hd6309", cpu, "Y", &Y, 1);
state_save_register_UINT16("hd6309", cpu, "V", &V, 1);
state_save_register_UINT16("hd6309", cpu, "D", &D, 1); /* the accumulators A and B */
state_save_register_UINT16("hd6309", cpu, "W", &W, 1); /* the accumulators E and F */
state_save_register_UINT8("hd6309", cpu, "DP", &DP, 1);
state_save_register_UINT8("hd6309", cpu, "CC", &CC, 1);
state_save_register_UINT8("hd6309", cpu, "MD", &MD, 1);
@@ -595,6 +597,7 @@ void hd6309_init(void)
state_save_register_UINT8("hd6309", cpu, "NMI", &hd6309.nmi_state, 1);
state_save_register_UINT8("hd6309", cpu, "IRQ", &hd6309.irq_state[0], 1);
state_save_register_UINT8("hd6309", cpu, "FIRQ", &hd6309.irq_state[1], 1);
state_save_register_int("hd6309", cpu, "extra_cycles", &hd6309.extra_cycles); /* the cycles of an interrupt taken outside the execute loop */
}
/****************************************************************************/
+1
View File
@@ -483,6 +483,7 @@ void konami_init(void)
state_save_register_UINT8("konami", cpu, "NMI", &konami.nmi_state, 1);
state_save_register_UINT8("konami", cpu, "IRQ", &konami.irq_state[0], 1);
state_save_register_UINT8("konami", cpu, "FIRQ", &konami.irq_state[1], 1);
state_save_register_int("konami", cpu, "extra_cycles", &konami.extra_cycles); /* the cycles of an interrupt taken outside the execute loop */
}
void konami_reset(void *param)
+2
View File
@@ -512,12 +512,14 @@ void m6809_init(void)
state_save_register_UINT16("m6809", cpu, "S", &S, 1);
state_save_register_UINT16("m6809", cpu, "X", &X, 1);
state_save_register_UINT16("m6809", cpu, "Y", &Y, 1);
state_save_register_UINT16("m6809", cpu, "D", &D, 1); /* the accumulators A and B */
state_save_register_UINT8("m6809", cpu, "DP", &DP, 1);
state_save_register_UINT8("m6809", cpu, "CC", &CC, 1);
state_save_register_UINT8("m6809", cpu, "INT", &m6809.int_state, 1);
state_save_register_UINT8("m6809", cpu, "NMI", &m6809.nmi_state, 1);
state_save_register_UINT8("m6809", cpu, "IRQ", &m6809.irq_state[0], 1);
state_save_register_UINT8("m6809", cpu, "FIRQ", &m6809.irq_state[1], 1);
state_save_register_int("m6809", cpu, "extra_cycles", &m6809.extra_cycles); /* the cycles of an interrupt taken outside the execute loop */
}
void m6809_reset(void *param)
+4
View File
@@ -73,6 +73,10 @@ int YM2203_sh_start(const struct MachineSound *msound)
intf = msound->sound_interface;
/* the SSG state the YM2203 save state leaves out (before YM2203Init:
the postloads run last registered first) */
AY8910_ym_ext_state_register(intf->num);
/* Timer Handler set */
FMTimerInit();
/* stream system initialize */
+12 -12
View File
@@ -76,10 +76,8 @@ static void FMTimerInit( void )
}
}
/* optional: keep the two FM timers of each chip in save states (the core
does not save its timers), with the parts of the FM and SSG state the
YM2610 save state leaves out. Opt-in, so the save states of the other
YM2610 drivers keep their layout. */
/* optional: keep the two FM timers of each chip in save states (from before
the core saved its timers) */
static void YM2610_timer_presave( void )
{
int n, c;
@@ -116,16 +114,18 @@ static void YM2610_timer_state_start( void )
{
int n;
memset( timer_on, 0, sizeof( timer_on ) );
if( !timer_state_wanted )
return;
for( n = 0; n < intf->num; n++ )
if( timer_state_wanted )
{
state_save_register_UINT8( "ym2610timer", n, "on", timer_on[n], 2 );
state_save_register_double( "ym2610timer", n, "left", timer_left[n], 2 );
for( n = 0; n < intf->num; n++ )
{
state_save_register_UINT8( "ym2610timer", n, "on", timer_on[n], 2 );
state_save_register_double( "ym2610timer", n, "left", timer_left[n], 2 );
}
state_save_register_func_presave( YM2610_timer_presave );
state_save_register_func_postload( YM2610_timer_postload );
}
state_save_register_func_presave( YM2610_timer_presave );
state_save_register_func_postload( YM2610_timer_postload );
/* the chip and SSG state the YM2610 save state leaves out */
/* the chip and SSG state the YM2610 save state leaves out: for all the
games (Neo Geo too), the save states and RunAhead lost it */
YM2610_ext_state_register( intf->num );
AY8910_ym_ext_state_register( intf->num );
}
+7 -1
View File
@@ -832,7 +832,7 @@ int AY8910_sh_start_ym(const struct MachineSound *msound)
/* optional: the internal counters of the SSG of a YM chip in save states.
The YM postload rewrites the SSG registers, which restarts the envelope,
so the counters are kept in a copy and put back after it. */
#define AY_EXT_INT 12
#define AY_EXT_INT 17
#define AY_EXT_U8 13
static INT32 ay_ext_int[MAX_8910][AY_EXT_INT];
static UINT8 ay_ext_u8[MAX_8910][AY_EXT_U8];
@@ -850,6 +850,10 @@ static void AY8910_ext_presave(void)
i[3] = PSG->CountN; i[4] = PSG->CountE; i[5] = PSG->RNG;
i[6] = PSG->VolA; i[7] = PSG->VolB; i[8] = PSG->VolC; i[9] = PSG->VolE;
i[10] = PSG->lastEnable; i[11] = PSG->register_latch;
/* the periods as they were made: a clock change (the YM prescaler)
does not remake them, the register rewrite of the postload does */
i[12] = PSG->PeriodA; i[13] = PSG->PeriodB; i[14] = PSG->PeriodC;
i[15] = PSG->PeriodN; i[16] = PSG->PeriodE;
u[0] = PSG->EnvelopeA; u[1] = PSG->EnvelopeB; u[2] = PSG->EnvelopeC;
u[3] = PSG->OutputA; u[4] = PSG->OutputB; u[5] = PSG->OutputC; u[6] = PSG->OutputN;
u[7] = (UINT8)PSG->CountEnv; u[8] = PSG->Hold; u[9] = PSG->Alternate;
@@ -869,6 +873,8 @@ static void AY8910_ext_postload(void)
PSG->CountN = i[3]; PSG->CountE = i[4]; PSG->RNG = i[5];
PSG->VolA = i[6]; PSG->VolB = i[7]; PSG->VolC = i[8]; PSG->VolE = i[9];
PSG->lastEnable = i[10]; PSG->register_latch = i[11];
PSG->PeriodA = i[12]; PSG->PeriodB = i[13]; PSG->PeriodC = i[14];
PSG->PeriodN = i[15]; PSG->PeriodE = i[16];
PSG->EnvelopeA = u[0]; PSG->EnvelopeB = u[1]; PSG->EnvelopeC = u[2];
PSG->OutputA = u[3]; PSG->OutputB = u[4]; PSG->OutputC = u[5]; PSG->OutputN = u[6];
PSG->CountEnv = (signed char)u[7]; PSG->Hold = u[8]; PSG->Alternate = u[9];
+140 -134
View File
@@ -1820,6 +1820,86 @@ static void FMsave_state_st(const char *state_name,int num,FM_ST *ST)
state_save_register_UINT8 (state_name, num, "TIMER B" , &ST->TB , 1);
state_save_register_int (state_name, num, "TIMER Bcnt", &ST->TBC );
}
#if (BUILD_YM2203||BUILD_YM2610||BUILD_YM2610B||BUILD_YM2612||BUILD_YM3438)
/* the parts of an OPN chip state that the registers and FMsave_state_channel
leave out: global envelope and LFO counters, key state, SSG-EG negation,
envelope output, algorithm memory, key codes and the phase steps and
envelope rates. The chip recomputes the steps and rates only when a
register write asks for it, so they can lag behind the registers (e.g.
after a 3 slot mode change) and are saved as they are. The postload of
the chip rewrites the registers, which changes some of them, so they are
kept in a copy and put back after it. */
typedef struct
{
UINT32 eg_cnt, eg_timer, lfo_cnt, lfo_inc;
UINT32 key[6*4], vol_out[6*4];
UINT8 ssgn[6*4];
INT32 mem_value[6];
UINT8 kcode[6];
UINT32 block_fnum[6];
UINT32 sl3_block_fnum[3];
INT32 incr[6*4];
UINT8 ksr[6*4];
UINT8 eg_rates[6*4*8];
} OPN_EXT;
static void opn_ext_copy(FM_OPN *OPN, FM_CH *CH, int channels, OPN_EXT *e, int restore)
{
int c, s, i;
#define OPN_EXT_COPY(chip, saved) if (restore) (chip) = (saved); else (saved) = (chip)
OPN_EXT_COPY(OPN->eg_cnt, e->eg_cnt);
OPN_EXT_COPY(OPN->eg_timer, e->eg_timer);
OPN_EXT_COPY(OPN->lfo_cnt, e->lfo_cnt);
OPN_EXT_COPY(OPN->lfo_inc, e->lfo_inc);
for (c = 0; c < 3; c++)
OPN_EXT_COPY(OPN->SL3.block_fnum[c], e->sl3_block_fnum[c]);
for (c = 0; c < channels; c++)
{
OPN_EXT_COPY(CH[c].mem_value, e->mem_value[c]);
OPN_EXT_COPY(CH[c].kcode, e->kcode[c]);
OPN_EXT_COPY(CH[c].block_fnum, e->block_fnum[c]);
for (s = 0; s < 4; s++)
{
FM_SLOT *SLOT = &CH[c].SLOT[s];
UINT8 *rates[8];
UINT8 *r = &e->eg_rates[(c*4+s)*8];
OPN_EXT_COPY(SLOT->key, e->key[c*4+s]);
OPN_EXT_COPY(SLOT->vol_out, e->vol_out[c*4+s]);
OPN_EXT_COPY(SLOT->ssgn, e->ssgn[c*4+s]);
OPN_EXT_COPY(SLOT->Incr, e->incr[c*4+s]);
OPN_EXT_COPY(SLOT->ksr, e->ksr[c*4+s]);
rates[0] = &SLOT->eg_sh_ar; rates[1] = &SLOT->eg_sel_ar;
rates[2] = &SLOT->eg_sh_d1r; rates[3] = &SLOT->eg_sel_d1r;
rates[4] = &SLOT->eg_sh_d2r; rates[5] = &SLOT->eg_sel_d2r;
rates[6] = &SLOT->eg_sh_rr; rates[7] = &SLOT->eg_sel_rr;
for (i = 0; i < 8; i++)
OPN_EXT_COPY(*rates[i], r[i]);
}
}
#undef OPN_EXT_COPY
}
static void opn_ext_register(const char *name, int num, OPN_EXT *e)
{
state_save_register_UINT32(name, num, "eg_cnt", &e->eg_cnt, 1);
state_save_register_UINT32(name, num, "eg_timer", &e->eg_timer, 1);
state_save_register_UINT32(name, num, "lfo_cnt", &e->lfo_cnt, 1);
state_save_register_UINT32(name, num, "key", e->key, 6*4);
state_save_register_UINT32(name, num, "vol_out", e->vol_out, 6*4);
state_save_register_UINT8(name, num, "ssgn", e->ssgn, 6*4);
state_save_register_INT32(name, num, "mem_value", e->mem_value, 6);
state_save_register_UINT32(name, num, "lfo_inc", &e->lfo_inc, 1);
state_save_register_UINT8(name, num, "kcode", e->kcode, 6);
state_save_register_UINT32(name, num, "block_fnum", e->block_fnum, 6);
state_save_register_UINT32(name, num, "sl3_block_fnum", e->sl3_block_fnum, 3);
state_save_register_INT32(name, num, "incr", e->incr, 6*4);
state_save_register_UINT8(name, num, "ksr", e->ksr, 6*4);
state_save_register_UINT8(name, num, "eg_rates", e->eg_rates, 6*4*8);
}
#endif
#endif /* _STATE_H */
#if BUILD_OPN
@@ -2305,7 +2385,11 @@ void YM2203ResetChip(int num)
reset_channels( &OPN->ST , FM2203[num].CH , 3 );
/* reset OPerator paramater */
for(i = 0xb2 ; i >= 0x30 ; i-- ) OPNWriteReg(OPN,i,0);
for(i = 0xb2 ; i >= 0x30 ; i-- )
{
OPNWriteReg(OPN,i,0);
FM2203[num].REGS[i] = 0; /* the reset values, for the save states */
}
for(i = 0x26 ; i >= 0x20 ; i-- ) OPNWriteReg(OPN,i,0);
}
@@ -2342,11 +2426,40 @@ static void YM2203_postload(void)
cur_chip = NULL;
}
/* the parts of the chip state that the registers leave out (see OPN_EXT) */
static OPN_EXT *ym2203_ext;
static void YM2203_ext_presave(void)
{
int num;
for (num = 0; num < YM2203NumChips; num++)
opn_ext_copy(&FM2203[num].OPN, FM2203[num].CH, 3, &ym2203_ext[num], 0);
}
static void YM2203_ext_postload(void)
{
int num;
for (num = 0; num < YM2203NumChips; num++)
opn_ext_copy(&FM2203[num].OPN, FM2203[num].CH, 3, &ym2203_ext[num], 1);
}
static void YM2203_save_state(void)
{
int num;
const char statename[] = "YM2203";
/* registered before YM2203_postload (the postloads run last registered
first), so it puts its values back after the registers are rewritten */
ym2203_ext = auto_malloc(sizeof(*ym2203_ext) * YM2203NumChips);
if (ym2203_ext != NULL)
{
memset(ym2203_ext, 0, sizeof(*ym2203_ext) * YM2203NumChips);
for (num = 0; num < YM2203NumChips; num++)
opn_ext_register("YM2203ext", num, &ym2203_ext[num]);
state_save_register_func_presave(YM2203_ext_presave);
state_save_register_func_postload(YM2203_ext_postload);
}
for(num=0;num<YM2203NumChips;num++)
{
state_save_register_UINT8 (statename, num, "regs" , FM2203[num].REGS , 256);
@@ -3565,7 +3678,7 @@ static void YM2608_postload(void)
OPNWriteReg(&F2608->OPN,r|0x100,F2608->REGS[r|0x100]);
}
/* FB / CONNECT , L / R / AMS / PMS */
for(r=0xb0;r<0xb6;r++)
for(r=0xb0;r<0xb7;r++) /* b4-b6: L/R, AMS, PMS of channels 1-3 */
if((r&3) != 3)
{
OPNWriteReg(&F2608->OPN,r,F2608->REGS[r]);
@@ -3729,11 +3842,13 @@ void YM2608ResetChip(int num)
{
OPNWriteReg(OPN,i ,0xc0);
OPNWriteReg(OPN,i|0x100,0xc0);
FM2608[num].REGS[i] = FM2608[num].REGS[i|0x100] = 0xc0; /* the reset values, for the save states (the postload writes them back) */
}
for(i = 0xb2 ; i >= 0x30 ; i-- )
{
OPNWriteReg(OPN,i ,0);
OPNWriteReg(OPN,i|0x100,0);
FM2608[num].REGS[i] = FM2608[num].REGS[i|0x100] = 0;
}
for(i = 0x26 ; i >= 0x20 ; i-- ) OPNWriteReg(OPN,i,0);
@@ -4269,7 +4384,7 @@ static void YM2610_postload(void)
OPNWriteReg(&F2610->OPN,r|0x100,F2610->REGS[r|0x100]);
}
/* FB / CONNECT , L / R / AMS / PMS */
for(r=0xb0;r<0xb6;r++)
for(r=0xb0;r<0xb7;r++) /* b4-b6: L/R, AMS, PMS of channels 1-3 */
if((r&3) != 3)
{
OPNWriteReg(&F2610->OPN,r,F2610->REGS[r]);
@@ -4295,62 +4410,23 @@ static void YM2610_postload(void)
}
/* optional: the parts of the chip state that YM2610_save_state leaves out
(global envelope and LFO counters, key state, SSG-EG negation, the
envelope output and the algorithm memory). The postload of the chip
rewrites the registers, which changes some of them, so they are kept in
a copy and put back after it. */
(see OPN_EXT) */
#define YM2610_EXT_CHIPS 2
static struct
{
UINT32 eg_cnt, eg_timer, lfo_cnt;
UINT32 key[6*4], vol_out[6*4];
UINT8 ssgn[6*4];
INT32 mem_value[6];
} ym2610_ext[YM2610_EXT_CHIPS];
static OPN_EXT ym2610_ext[YM2610_EXT_CHIPS];
static int ym2610_ext_chips;
static void YM2610_ext_presave(void)
{
int num, c, s;
int num;
for (num = 0; num < ym2610_ext_chips && num < YM2610NumChips; num++)
{
YM2610 *F2610 = &(FM2610[num]);
ym2610_ext[num].eg_cnt = F2610->OPN.eg_cnt;
ym2610_ext[num].eg_timer = F2610->OPN.eg_timer;
ym2610_ext[num].lfo_cnt = F2610->OPN.lfo_cnt;
for (c = 0; c < 6; c++)
{
ym2610_ext[num].mem_value[c] = F2610->CH[c].mem_value;
for (s = 0; s < 4; s++)
{
ym2610_ext[num].key[c*4+s] = F2610->CH[c].SLOT[s].key;
ym2610_ext[num].vol_out[c*4+s] = F2610->CH[c].SLOT[s].vol_out;
ym2610_ext[num].ssgn[c*4+s] = F2610->CH[c].SLOT[s].ssgn;
}
}
}
opn_ext_copy(&FM2610[num].OPN, FM2610[num].CH, 6, &ym2610_ext[num], 0);
}
static void YM2610_ext_postload(void)
{
int num, c, s;
int num;
for (num = 0; num < ym2610_ext_chips && num < YM2610NumChips; num++)
{
YM2610 *F2610 = &(FM2610[num]);
F2610->OPN.eg_cnt = ym2610_ext[num].eg_cnt;
F2610->OPN.eg_timer = ym2610_ext[num].eg_timer;
F2610->OPN.lfo_cnt = ym2610_ext[num].lfo_cnt;
for (c = 0; c < 6; c++)
{
F2610->CH[c].mem_value = ym2610_ext[num].mem_value[c];
for (s = 0; s < 4; s++)
{
F2610->CH[c].SLOT[s].key = ym2610_ext[num].key[c*4+s];
F2610->CH[c].SLOT[s].vol_out = ym2610_ext[num].vol_out[c*4+s];
F2610->CH[c].SLOT[s].ssgn = ym2610_ext[num].ssgn[c*4+s];
}
}
}
opn_ext_copy(&FM2610[num].OPN, FM2610[num].CH, 6, &ym2610_ext[num], 1);
}
/* call before YM2610Init (the postload functions run last registered first) */
@@ -4360,15 +4436,7 @@ void YM2610_ext_state_register(int chips)
if (chips > YM2610_EXT_CHIPS) chips = YM2610_EXT_CHIPS;
ym2610_ext_chips = chips;
for (num = 0; num < chips; num++)
{
state_save_register_UINT32("YM2610ext", num, "eg_cnt", &ym2610_ext[num].eg_cnt, 1);
state_save_register_UINT32("YM2610ext", num, "eg_timer", &ym2610_ext[num].eg_timer, 1);
state_save_register_UINT32("YM2610ext", num, "lfo_cnt", &ym2610_ext[num].lfo_cnt, 1);
state_save_register_UINT32("YM2610ext", num, "key", ym2610_ext[num].key, 6*4);
state_save_register_UINT32("YM2610ext", num, "vol_out", ym2610_ext[num].vol_out, 6*4);
state_save_register_UINT8("YM2610ext", num, "ssgn", ym2610_ext[num].ssgn, 6*4);
state_save_register_INT32("YM2610ext", num, "mem_value", ym2610_ext[num].mem_value, 6);
}
opn_ext_register("YM2610ext", num, &ym2610_ext[num]);
state_save_register_func_presave(YM2610_ext_presave);
state_save_register_func_postload(YM2610_ext_postload);
}
@@ -4505,11 +4573,13 @@ void YM2610ResetChip(int num)
{
OPNWriteReg(OPN,i ,0xc0);
OPNWriteReg(OPN,i|0x100,0xc0);
F2610->REGS[i] = F2610->REGS[i|0x100] = 0xc0; /* the reset values, for the save states (the postload writes them back) */
}
for(i = 0xb2 ; i >= 0x30 ; i-- )
{
OPNWriteReg(OPN,i ,0);
OPNWriteReg(OPN,i|0x100,0);
F2610->REGS[i] = F2610->REGS[i|0x100] = 0;
}
for(i = 0x26 ; i >= 0x20 ; i-- ) OPNWriteReg(OPN,i,0);
/**** ADPCM work initial ****/
@@ -4853,8 +4923,8 @@ static void YM2612_postload(void)
for(num=0;num<YM2612NumChips;num++)
{
/* DAC data & port */
FM2612[num].dacout = ((int)FM2612[num].REGS[0x2a] - 0x80) << 6; /* level unknown */
/* DAC port (the DAC data is saved as it is: before the first write
it is 0, which is not what the register would give) */
FM2612[num].dacen = FM2612[num].REGS[0x2b] & 0x80; /* DAC Sel (0x2b), as YM2612Write */
/* OPN registers */
/* DT / MULTI , TL , KS / AR , AMON / DR , SR , SL / RR , SSG-EG */
@@ -4877,77 +4947,21 @@ static void YM2612_postload(void)
cur_chip = NULL;
}
/* the parts of the chip state that the registers and FMsave_state_channel
leave out (global envelope and LFO counters, key state, SSG-EG negation,
envelope output, algorithm memory), as for the YM2610: kept in a copy
because the postload rewrites the registers, which changes some of them */
static struct
{
UINT32 eg_cnt, eg_timer, lfo_cnt, lfo_inc;
UINT32 key[6*4], vol_out[6*4];
UINT8 ssgn[6*4];
INT32 mem_value[6];
UINT8 kcode[6];
UINT32 block_fnum[6];
UINT32 sl3_block_fnum[3];
} *ym2612_ext;
/* the parts of the chip state that the registers leave out (see OPN_EXT) */
static OPN_EXT *ym2612_ext;
static void YM2612_ext_presave(void)
{
int num, c, s;
int num;
for (num = 0; num < YM2612NumChips; num++)
{
YM2612 *F2612 = &(FM2612[num]);
ym2612_ext[num].eg_cnt = F2612->OPN.eg_cnt;
ym2612_ext[num].eg_timer = F2612->OPN.eg_timer;
ym2612_ext[num].lfo_cnt = F2612->OPN.lfo_cnt;
ym2612_ext[num].lfo_inc = F2612->OPN.lfo_inc;
for (c = 0; c < 3; c++)
ym2612_ext[num].sl3_block_fnum[c] = F2612->OPN.SL3.block_fnum[c];
for (c = 0; c < 6; c++)
{
ym2612_ext[num].mem_value[c] = F2612->CH[c].mem_value;
ym2612_ext[num].kcode[c] = F2612->CH[c].kcode;
ym2612_ext[num].block_fnum[c] = F2612->CH[c].block_fnum;
for (s = 0; s < 4; s++)
{
ym2612_ext[num].key[c*4+s] = F2612->CH[c].SLOT[s].key;
ym2612_ext[num].vol_out[c*4+s] = F2612->CH[c].SLOT[s].vol_out;
ym2612_ext[num].ssgn[c*4+s] = F2612->CH[c].SLOT[s].ssgn;
}
}
}
opn_ext_copy(&FM2612[num].OPN, FM2612[num].CH, 6, &ym2612_ext[num], 0);
}
static void YM2612_ext_postload(void)
{
int num, c, s;
int num;
for (num = 0; num < YM2612NumChips; num++)
{
YM2612 *F2612 = &(FM2612[num]);
F2612->OPN.eg_cnt = ym2612_ext[num].eg_cnt;
F2612->OPN.eg_timer = ym2612_ext[num].eg_timer;
F2612->OPN.lfo_cnt = ym2612_ext[num].lfo_cnt;
F2612->OPN.lfo_inc = ym2612_ext[num].lfo_inc;
for (c = 0; c < 3; c++)
F2612->OPN.SL3.block_fnum[c] = ym2612_ext[num].sl3_block_fnum[c];
for (c = 0; c < 6; c++)
{
F2612->CH[c].mem_value = ym2612_ext[num].mem_value[c];
F2612->CH[c].kcode = ym2612_ext[num].kcode[c];
F2612->CH[c].block_fnum = ym2612_ext[num].block_fnum[c];
/* recompute the phase steps and the envelope rates */
F2612->CH[c].SLOT[SLOT1].Incr = -1;
for (s = 0; s < 4; s++)
F2612->CH[c].SLOT[s].ksr = 0xff;
for (s = 0; s < 4; s++)
{
F2612->CH[c].SLOT[s].key = ym2612_ext[num].key[c*4+s];
F2612->CH[c].SLOT[s].vol_out = ym2612_ext[num].vol_out[c*4+s];
F2612->CH[c].SLOT[s].ssgn = ym2612_ext[num].ssgn[c*4+s];
}
}
}
opn_ext_copy(&FM2612[num].OPN, FM2612[num].CH, 6, &ym2612_ext[num], 1);
}
static void YM2612_save_state(void)
@@ -4962,19 +4976,7 @@ static void YM2612_save_state(void)
{
memset(ym2612_ext, 0, sizeof(*ym2612_ext) * YM2612NumChips);
for (num = 0; num < YM2612NumChips; num++)
{
state_save_register_UINT32("YM2612ext", num, "eg_cnt", &ym2612_ext[num].eg_cnt, 1);
state_save_register_UINT32("YM2612ext", num, "eg_timer", &ym2612_ext[num].eg_timer, 1);
state_save_register_UINT32("YM2612ext", num, "lfo_cnt", &ym2612_ext[num].lfo_cnt, 1);
state_save_register_UINT32("YM2612ext", num, "key", ym2612_ext[num].key, 6*4);
state_save_register_UINT32("YM2612ext", num, "vol_out", ym2612_ext[num].vol_out, 6*4);
state_save_register_UINT8("YM2612ext", num, "ssgn", ym2612_ext[num].ssgn, 6*4);
state_save_register_INT32("YM2612ext", num, "mem_value", ym2612_ext[num].mem_value, 6);
state_save_register_UINT32("YM2612ext", num, "lfo_inc", &ym2612_ext[num].lfo_inc, 1);
state_save_register_UINT8("YM2612ext", num, "kcode", ym2612_ext[num].kcode, 6);
state_save_register_UINT32("YM2612ext", num, "block_fnum", ym2612_ext[num].block_fnum, 6);
state_save_register_UINT32("YM2612ext", num, "sl3_block_fnum", ym2612_ext[num].sl3_block_fnum, 3);
}
opn_ext_register("YM2612ext", num, &ym2612_ext[num]);
state_save_register_func_presave(YM2612_ext_presave);
state_save_register_func_postload(YM2612_ext_postload);
}
@@ -4982,6 +4984,7 @@ static void YM2612_save_state(void)
for(num=0;num<YM2612NumChips;num++)
{
state_save_register_UINT8 (statename, num, "regs" , FM2612[num].REGS , 512);
state_save_register_INT32 (statename, num, "dacout" , &FM2612[num].dacout, 1);
FMsave_state_st(statename,num,&FM2612[num].OPN.ST);
FMsave_state_channel(statename,num,FM2612[num].CH,6);
/* 3slots */
@@ -5070,15 +5073,18 @@ void YM2612ResetChip(int num)
{
OPNWriteReg(OPN,i ,0xc0);
OPNWriteReg(OPN,i|0x100,0xc0);
F2612->REGS[i] = F2612->REGS[i|0x100] = 0xc0; /* the reset values, for the save states (the postload writes them back) */
}
for(i = 0xb2 ; i >= 0x30 ; i-- )
{
OPNWriteReg(OPN,i ,0);
OPNWriteReg(OPN,i|0x100,0);
F2612->REGS[i] = F2612->REGS[i|0x100] = 0;
}
for(i = 0x26 ; i >= 0x20 ; i-- ) OPNWriteReg(OPN,i,0);
/* DAC mode clear */
F2612->dacen = 0;
F2612->REGS[0x2b] = 0;
}
/* YM2612 write */
+21 -9
View File
@@ -83,6 +83,7 @@ typedef struct Voice_t
int relamt; /* release amount*/
int relcount; /* release counter*/
INT8 relstage; /* release stage*/
INT32 samp_offset; /* pSamp in the ROM, for the save states */
} VoiceT;
/* chip structure*/
@@ -104,19 +105,27 @@ typedef struct MultiPCM_t
static MultiPCMT mpcm[MAX_MULTIPCM];
/* the sample pointer of each voice as its offset in the ROM: it was made at
the key on, with the bank and the sample number of then (the registers
can have changed since) */
static void MultiPCM_presave(void)
{
int i, j;
for (i = 0; i < MAX_MULTIPCM; i++)
for (j = 0; j < 28; j++)
mpcm[i].Voices[j].samp_offset = (mpcm[i].Voices[j].pSamp && mpcm[i].romptr) ?
(INT32)(mpcm[i].Voices[j].pSamp - (INT8 *)mpcm[i].romptr) : -1;
}
static void MultiPCM_postload(void)
{
int i, j, inum;
int i, j;
/* recalculate all the proper voice pointers*/
for (i = 0; i < 2; i++)
{
for (i = 0; i < MAX_MULTIPCM; i++)
for (j = 0; j < 28; j++)
{
inum = mpcm[i].registers[j][1] | ((mpcm[i].registers[j][2]&0x1)<<8);
mpcm[i].Voices[j].pSamp = &mpcm[i].romptr[mpcm[i].samples[inum].st];
}
}
mpcm[i].Voices[j].pSamp = (mpcm[i].Voices[j].samp_offset >= 0 && mpcm[i].romptr) ?
(INT8 *)&mpcm[i].romptr[mpcm[i].Voices[j].samp_offset] : NULL;
}
static void MultiPCM_update(int chip, INT16 **buffer, int length )
@@ -340,12 +349,15 @@ int MultiPCM_sh_start(const struct MachineSound *msound)
state_save_register_INT32(mname2, 1, "ptsum", &mpcm[i].Voices[v].ptsum, 1);
state_save_register_int(mname2, 1, "relamt", &mpcm[i].Voices[v].relamt);
state_save_register_INT8(mname2, 1, "relstage", &mpcm[i].Voices[v].relstage, 1);
state_save_register_int(mname2, 1, "relcount", &mpcm[i].Voices[v].relcount);
state_save_register_INT32(mname2, 1, "sample offset", &mpcm[i].Voices[v].samp_offset, 1);
}
state_save_register_int(mname, i, "curreg", &mpcm[i].curreg);
state_save_register_int(mname, i, "curvoice", &mpcm[i].curvoice);
}
state_save_register_func_presave(MultiPCM_presave);
state_save_register_func_postload(MultiPCM_postload);
return 0;