Add savestate support to fmopl.c and the YM3812, YM3526 and Y8950 interfaces

This commit is contained in:
Barry Harris 2012-02-04 20:55:41 +00:00
parent 7e1ceedc78
commit 4c3ddb77cd
6 changed files with 209 additions and 68 deletions

View File

@ -654,7 +654,7 @@ static INT32 DrvScan(INT32 nAction,INT32 *pnMin)
struct BurnArea ba;
if (pnMin) {
*pnMin = 0x029707;
*pnMin = 0x029722;
}
if (nAction & ACB_VOLATILE) {
@ -666,9 +666,10 @@ static INT32 DrvScan(INT32 nAction,INT32 *pnMin)
BurnAcb(&ba);
SekScan(nAction);
ZetScan(nAction);
M6809Scan(nAction);
AY8910Scan(nAction, pnMin);
BurnY8950Scan(nAction, pnMin);
SCAN_VAR(MC6840_idx0);
SCAN_VAR(MC6840_idx1);

View File

@ -568,6 +568,7 @@ void BurnY8950Scan(INT32 nAction, INT32* pnMin)
#endif
BurnTimerScanY8950(nAction, pnMin);
FMOPLScan(FM_OPL_SAVESTATE_Y8950, 0, nAction, pnMin);
if (nAction & ACB_DRIVER_DATA) {
SCAN_VAR(nY8950Position);

View File

@ -567,6 +567,7 @@ void BurnYM3526Scan(INT32 nAction, INT32* pnMin)
#endif
BurnTimerScanYM3526(nAction, pnMin);
FMOPLScan(FM_OPL_SAVESTATE_YM3526, 0, nAction, pnMin);
if (nAction & ACB_DRIVER_DATA) {
SCAN_VAR(nYM3526Position);

View File

@ -582,6 +582,7 @@ void BurnYM3812Scan(INT32 nAction, INT32* pnMin)
#endif
BurnTimerScanYM3812(nAction, pnMin);
FMOPLScan(FM_OPL_SAVESTATE_YM3812, 0, nAction, pnMin);
if (nAction & ACB_DRIVER_DATA) {
SCAN_VAR(nYM3812Position);

View File

@ -72,6 +72,7 @@ Revision History:
#include <math.h>
#include "driver.h" /* use M.A.M.E. */
#include "state.h"
#include "ymdeltat.h"
@ -274,6 +275,9 @@ typedef struct fm_opl_f {
UINT32 fn_tab[1024]; /* fnumber->increment counter */
/* LFO */
UINT32 LFO_AM;
INT32 LFO_PM;
UINT8 lfo_am_depth;
UINT8 lfo_pm_depth_range;
UINT32 lfo_am_cnt;
@ -324,6 +328,13 @@ typedef struct fm_opl_f {
int rate; /* sampling rate (Hz) */
double freqbase; /* frequency base */
double TimerBase; /* Timer base time (==sampling time)*/
signed int phase_modulation; /* phase modulation input (SLOT 2) */
signed int output[1];
#if BUILD_Y8950
INT32 output_deltat[4]; /* for Y8950 DELTA-T, chip is mono, that 4 here is just for safety */
#endif
} FM_OPL;
@ -641,17 +652,6 @@ static int num_lock = 0;
static void *cur_chip = NULL; /* current chip pointer */
static OPL_SLOT *SLOT7_1, *SLOT7_2, *SLOT8_1, *SLOT8_2;
static signed int phase_modulation; /* phase modulation input (SLOT 2) */
static signed int output[1];
#if BUILD_Y8950
static INT32 output_deltat[4]; /* for Y8950 DELTA-T, chip is mono, that 4 here is just for safety */
#endif
static UINT32 LFO_AM;
static INT32 LFO_PM;
INLINE int limit( int val, int max, int min ) {
if ( val > max )
@ -718,12 +718,12 @@ INLINE void advance_lfo(FM_OPL *OPL)
tmp = lfo_am_table[ OPL->lfo_am_cnt >> LFO_SH ];
if (OPL->lfo_am_depth)
LFO_AM = tmp;
OPL->LFO_AM = tmp;
else
LFO_AM = tmp>>2;
OPL->LFO_AM = tmp>>2;
OPL->lfo_pm_cnt += OPL->lfo_pm_inc;
LFO_PM = ((OPL->lfo_pm_cnt>>LFO_SH) & 7) | OPL->lfo_pm_depth_range;
OPL->LFO_PM = ((OPL->lfo_pm_cnt>>LFO_SH) & 7) | OPL->lfo_pm_depth_range;
}
/* advance to next sample */
@ -833,7 +833,7 @@ INLINE void advance(FM_OPL *OPL)
unsigned int fnum_lfo = (block_fnum&0x0380) >> 7;
signed int lfo_fn_table_index_offset = lfo_pm_table[LFO_PM + 16*fnum_lfo ];
signed int lfo_fn_table_index_offset = lfo_pm_table[OPL->LFO_PM + 16*fnum_lfo ];
if (lfo_fn_table_index_offset) /* LFO phase modulation active */
{
@ -913,16 +913,16 @@ INLINE signed int op_calc1(UINT32 phase, unsigned int env, signed int pm, unsign
}
#define volume_calc(OP) ((OP)->TLL + ((UINT32)(OP)->volume) + (LFO_AM & (OP)->AMmask))
#define volume_calc(OP) ((OP)->TLL + ((UINT32)(OP)->volume) + (OPL->LFO_AM & (OP)->AMmask))
/* calculate output */
INLINE void OPL_CALC_CH( OPL_CH *CH )
INLINE void OPL_CALC_CH( FM_OPL *OPL, OPL_CH *CH )
{
OPL_SLOT *SLOT;
unsigned int env;
signed int out;
phase_modulation = 0;
OPL->phase_modulation = 0;
/* SLOT 1 */
SLOT = &CH->SLOT[SLOT1];
@ -942,7 +942,7 @@ INLINE void OPL_CALC_CH( OPL_CH *CH )
SLOT++;
env = volume_calc(SLOT);
if( env < ENV_QUIET )
output[0] += op_calc(SLOT->Cnt, env, phase_modulation, SLOT->wavetable);
OPL->output[0] += op_calc(SLOT->Cnt, env, OPL->phase_modulation, SLOT->wavetable);
}
/*
@ -982,7 +982,7 @@ number number BLK/FNUM2 FNUM Drum Hat Drum Tom Cymbal
/* calculate rhythm */
INLINE void OPL_CALC_RH( OPL_CH *CH, unsigned int noise )
INLINE void OPL_CALC_RH( FM_OPL *OPL, OPL_CH *CH, unsigned int noise )
{
OPL_SLOT *SLOT;
signed int out;
@ -996,7 +996,7 @@ INLINE void OPL_CALC_RH( OPL_CH *CH, unsigned int noise )
- output sample always is multiplied by 2
*/
phase_modulation = 0;
OPL->phase_modulation = 0;
/* SLOT 1 */
SLOT = &CH[6].SLOT[SLOT1];
env = volume_calc(SLOT);
@ -1005,7 +1005,7 @@ INLINE void OPL_CALC_RH( OPL_CH *CH, unsigned int noise )
SLOT->op1_out[0] = SLOT->op1_out[1];
if (!SLOT->CON)
phase_modulation = SLOT->op1_out[0];
OPL->phase_modulation = SLOT->op1_out[0];
/* else ignore output of operator 1 */
SLOT->op1_out[1] = 0;
@ -1020,7 +1020,7 @@ INLINE void OPL_CALC_RH( OPL_CH *CH, unsigned int noise )
SLOT++;
env = volume_calc(SLOT);
if( env < ENV_QUIET )
output[0] += op_calc(SLOT->Cnt, env, phase_modulation, SLOT->wavetable) * 2;
OPL->output[0] += op_calc(SLOT->Cnt, env, OPL->phase_modulation, SLOT->wavetable) * 2;
/* Phase generation is based on: */
@ -1088,7 +1088,7 @@ INLINE void OPL_CALC_RH( OPL_CH *CH, unsigned int noise )
phase = 0xd0>>2;
}
output[0] += op_calc(phase<<FREQ_SH, env, 0, SLOT7_1->wavetable) * 2;
OPL->output[0] += op_calc(phase<<FREQ_SH, env, 0, SLOT7_1->wavetable) * 2;
}
/* Snare Drum (verified on real YM3812) */
@ -1109,13 +1109,13 @@ INLINE void OPL_CALC_RH( OPL_CH *CH, unsigned int noise )
if (noise)
phase ^= 0x100;
output[0] += op_calc(phase<<FREQ_SH, env, 0, SLOT7_2->wavetable) * 2;
OPL->output[0] += op_calc(phase<<FREQ_SH, env, 0, SLOT7_2->wavetable) * 2;
}
/* Tom Tom (verified on real YM3812) */
env = volume_calc(SLOT8_1);
if( env < ENV_QUIET )
output[0] += op_calc(SLOT8_1->Cnt, env, 0, SLOT8_1->wavetable) * 2;
OPL->output[0] += op_calc(SLOT8_1->Cnt, env, 0, SLOT8_1->wavetable) * 2;
/* Top Cymbal (verified on real YM3812) */
env = volume_calc(SLOT8_2);
@ -1142,7 +1142,7 @@ INLINE void OPL_CALC_RH( OPL_CH *CH, unsigned int noise )
if (res2)
phase = 0x300;
output[0] += op_calc(phase<<FREQ_SH, env, 0, SLOT8_2->wavetable) * 2;
OPL->output[0] += op_calc(phase<<FREQ_SH, env, 0, SLOT8_2->wavetable) * 2;
}
}
@ -1710,7 +1710,7 @@ static void OPLWriteReg(FM_OPL *OPL, int r, int v)
CH = &OPL->P_CH[r&0x0f];
CH->SLOT[SLOT1].FB = (v>>1)&7 ? ((v>>1)&7) + 7 : 0;
CH->SLOT[SLOT1].CON = v&1;
CH->SLOT[SLOT1].connect1 = CH->SLOT[SLOT1].CON ? &output[0] : &phase_modulation;
CH->SLOT[SLOT1].connect1 = CH->SLOT[SLOT1].CON ? &OPL->output[0] : &OPL->phase_modulation;
break;
case 0xe0: /* waveform select */
/* simply ignore write to the waveform select register if selecting not enabled in test register */
@ -1817,7 +1817,7 @@ static void OPLResetChip(FM_OPL *OPL)
YM_DELTAT *DELTAT = OPL->deltat;
DELTAT->freqbase = OPL->freqbase;
DELTAT->output_pointer = &output_deltat[0];
DELTAT->output_pointer = &OPL->output_deltat[0];
DELTAT->portshift = 5;
DELTAT->output_range = 1<<23;
YM_DELTAT_ADPCM_Reset(DELTAT,0,YM_DELTAT_EMULATION_MODE_NORMAL);
@ -2125,30 +2125,30 @@ void YM3812UpdateOne(int which, INT16 *buffer, int length)
{
int lt;
output[0] = 0;
OPL->output[0] = 0;
advance_lfo(OPL);
/* FM part */
OPL_CALC_CH(&OPL->P_CH[0]);
OPL_CALC_CH(&OPL->P_CH[1]);
OPL_CALC_CH(&OPL->P_CH[2]);
OPL_CALC_CH(&OPL->P_CH[3]);
OPL_CALC_CH(&OPL->P_CH[4]);
OPL_CALC_CH(&OPL->P_CH[5]);
OPL_CALC_CH(OPL, &OPL->P_CH[0]);
OPL_CALC_CH(OPL, &OPL->P_CH[1]);
OPL_CALC_CH(OPL, &OPL->P_CH[2]);
OPL_CALC_CH(OPL, &OPL->P_CH[3]);
OPL_CALC_CH(OPL, &OPL->P_CH[4]);
OPL_CALC_CH(OPL, &OPL->P_CH[5]);
if(!rhythm)
{
OPL_CALC_CH(&OPL->P_CH[6]);
OPL_CALC_CH(&OPL->P_CH[7]);
OPL_CALC_CH(&OPL->P_CH[8]);
OPL_CALC_CH(OPL, &OPL->P_CH[6]);
OPL_CALC_CH(OPL, &OPL->P_CH[7]);
OPL_CALC_CH(OPL, &OPL->P_CH[8]);
}
else /* Rhythm part */
{
OPL_CALC_RH(&OPL->P_CH[0], (OPL->noise_rng>>0)&1 );
OPL_CALC_RH(OPL, &OPL->P_CH[0], (OPL->noise_rng>>0)&1 );
}
lt = output[0];
lt = OPL->output[0];
lt >>= FINAL_SH;
@ -2276,30 +2276,30 @@ void YM3526UpdateOne(int which, INT16 *buffer, int length)
{
int lt;
output[0] = 0;
OPL->output[0] = 0;
advance_lfo(OPL);
/* FM part */
OPL_CALC_CH(&OPL->P_CH[0]);
OPL_CALC_CH(&OPL->P_CH[1]);
OPL_CALC_CH(&OPL->P_CH[2]);
OPL_CALC_CH(&OPL->P_CH[3]);
OPL_CALC_CH(&OPL->P_CH[4]);
OPL_CALC_CH(&OPL->P_CH[5]);
OPL_CALC_CH(OPL, &OPL->P_CH[0]);
OPL_CALC_CH(OPL, &OPL->P_CH[1]);
OPL_CALC_CH(OPL, &OPL->P_CH[2]);
OPL_CALC_CH(OPL, &OPL->P_CH[3]);
OPL_CALC_CH(OPL, &OPL->P_CH[4]);
OPL_CALC_CH(OPL, &OPL->P_CH[5]);
if(!rhythm)
{
OPL_CALC_CH(&OPL->P_CH[6]);
OPL_CALC_CH(&OPL->P_CH[7]);
OPL_CALC_CH(&OPL->P_CH[8]);
OPL_CALC_CH(OPL, &OPL->P_CH[6]);
OPL_CALC_CH(OPL, &OPL->P_CH[7]);
OPL_CALC_CH(OPL, &OPL->P_CH[8]);
}
else /* Rhythm part */
{
OPL_CALC_RH(&OPL->P_CH[0], (OPL->noise_rng>>0)&1 );
OPL_CALC_RH(OPL, &OPL->P_CH[0], (OPL->noise_rng>>0)&1 );
}
lt = output[0];
lt = OPL->output[0];
lt >>= FINAL_SH;
@ -2452,8 +2452,8 @@ void Y8950UpdateOne(int which, INT16 *buffer, int length)
{
int lt;
output[0] = 0;
output_deltat[0] = 0;
OPL->output[0] = 0;
OPL->output_deltat[0] = 0;
advance_lfo(OPL);
@ -2462,25 +2462,25 @@ void Y8950UpdateOne(int which, INT16 *buffer, int length)
YM_DELTAT_ADPCM_CALC(DELTAT);
/* FM part */
OPL_CALC_CH(&OPL->P_CH[0]);
OPL_CALC_CH(&OPL->P_CH[1]);
OPL_CALC_CH(&OPL->P_CH[2]);
OPL_CALC_CH(&OPL->P_CH[3]);
OPL_CALC_CH(&OPL->P_CH[4]);
OPL_CALC_CH(&OPL->P_CH[5]);
OPL_CALC_CH(OPL, &OPL->P_CH[0]);
OPL_CALC_CH(OPL, &OPL->P_CH[1]);
OPL_CALC_CH(OPL, &OPL->P_CH[2]);
OPL_CALC_CH(OPL, &OPL->P_CH[3]);
OPL_CALC_CH(OPL, &OPL->P_CH[4]);
OPL_CALC_CH(OPL, &OPL->P_CH[5]);
if(!rhythm)
{
OPL_CALC_CH(&OPL->P_CH[6]);
OPL_CALC_CH(&OPL->P_CH[7]);
OPL_CALC_CH(&OPL->P_CH[8]);
OPL_CALC_CH(OPL, &OPL->P_CH[6]);
OPL_CALC_CH(OPL, &OPL->P_CH[7]);
OPL_CALC_CH(OPL, &OPL->P_CH[8]);
}
else /* Rhythm part */
{
OPL_CALC_RH(&OPL->P_CH[0], (OPL->noise_rng>>0)&1 );
OPL_CALC_RH(OPL, &OPL->P_CH[0], (OPL->noise_rng>>0)&1 );
}
lt = output[0] + (output_deltat[0]>>11);
lt = OPL->output[0] + (OPL->output_deltat[0]>>11);
lt >>= FINAL_SH;
@ -2520,3 +2520,135 @@ void Y8950SetKeyboardHandler(int which,OPL_PORTHANDLER_W KeyboardHandler_w,OPL_P
#endif
void FMOPLScan(INT32 nType, INT32 which, INT32 nAction, INT32 *pnMin)
{
FM_OPL *OPL = OPL_YM3812[which];
if (nType == FM_OPL_SAVESTATE_YM3526) {
OPL = OPL_YM3526[which];
}
if (nType == FM_OPL_SAVESTATE_Y8950) {
OPL = OPL_Y8950[which];
}
if (nAction & ACB_DRIVER_DATA) {
for (INT32 ch = 0; ch < 9; ch++) {
OPL_CH *CH = &OPL->P_CH[ch];
SCAN_VAR(CH->block_fnum);
SCAN_VAR(CH->kcode);
for (INT32 slot = 0; slot < 2; slot++) {
OPL_SLOT *SLOT = &CH->SLOT[slot];
SCAN_VAR(SLOT->ar);
SCAN_VAR(SLOT->dr);
SCAN_VAR(SLOT->rr);
SCAN_VAR(SLOT->KSR);
SCAN_VAR(SLOT->ksl);
SCAN_VAR(SLOT->mul);
SCAN_VAR(SLOT->Cnt);
SCAN_VAR(SLOT->FB);
SCAN_VAR(SLOT->op1_out);
SCAN_VAR(SLOT->CON);
SCAN_VAR(SLOT->eg_type);
SCAN_VAR(SLOT->state);
SCAN_VAR(SLOT->TL);
SCAN_VAR(SLOT->volume);
SCAN_VAR(SLOT->sl);
SCAN_VAR(SLOT->key);
SCAN_VAR(SLOT->AMmask);
SCAN_VAR(SLOT->vib);
SCAN_VAR(SLOT->wavetable);
}
}
SCAN_VAR(OPL->eg_cnt);
SCAN_VAR(OPL->eg_timer);
SCAN_VAR(OPL->rhythm);
SCAN_VAR(OPL->lfo_am_depth);
SCAN_VAR(OPL->lfo_pm_depth_range);
SCAN_VAR(OPL->lfo_am_cnt);
SCAN_VAR(OPL->lfo_pm_cnt);
SCAN_VAR(OPL->noise_rng);
SCAN_VAR(OPL->noise_p);
if (OPL->type & OPL_TYPE_WAVESEL) {
SCAN_VAR(OPL->wavesel);
}
SCAN_VAR(OPL->T);
SCAN_VAR(OPL->st);
#if BUILD_Y8950
if ((OPL->type & OPL_TYPE_ADPCM) && (OPL->deltat)) {
SCAN_VAR(OPL->deltat->portstate);
SCAN_VAR(OPL->deltat->now_addr);
SCAN_VAR(OPL->deltat->now_step);
SCAN_VAR(OPL->deltat->acc);
SCAN_VAR(OPL->deltat->prev_acc);
SCAN_VAR(OPL->deltat->adpcmd);
SCAN_VAR(OPL->deltat->adpcml);
}
if (OPL->type & OPL_TYPE_IO) {
SCAN_VAR(OPL->portDirection);
SCAN_VAR(OPL->portLatch);
}
#endif
SCAN_VAR(OPL->address);
SCAN_VAR(OPL->status);
SCAN_VAR(OPL->statusmask);
SCAN_VAR(OPL->mode);
if (nAction & ACB_WRITE) {
for (INT32 ch = 0; ch < 9; ch++) {
OPL_CH *CH = &OPL->P_CH[ch];
UINT32 block_fnum = CH->block_fnum;
CH->ksl_base = ksl_tab[block_fnum >> 6];
CH->fc = OPL->fn_tab[block_fnum & 0x03ff] >> (7 - (block_fnum >> 10));
for (INT32 slot = 0; slot < 2; slot++) {
OPL_SLOT *SLOT = &CH->SLOT[slot];
SLOT->ksr = CH->kcode >> SLOT->KSR;
if ((SLOT->ar + SLOT->ksr) < 16 + 62) {
SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ];
SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + SLOT->ksr ];
} else {
SLOT->eg_sh_ar = 0;
SLOT->eg_sel_ar = 13*RATE_STEPS;
}
SLOT->eg_sh_dr = eg_rate_shift [SLOT->dr + SLOT->ksr ];
SLOT->eg_sel_dr = eg_rate_select[SLOT->dr + SLOT->ksr ];
SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr ];
SLOT->eg_sel_rr = eg_rate_select[SLOT->rr + SLOT->ksr ];
SLOT->Incr = CH->fc * SLOT->mul;
SLOT->TLL = SLOT->TL + (CH->ksl_base >> SLOT->ksl);
SLOT->connect1 = SLOT->CON ? &OPL->output[0] : &OPL->phase_modulation;
}
}
#if BUILD_Y8950
if ((OPL->type & OPL_TYPE_ADPCM) && (OPL->deltat)) {
// We really should call the postlod function for the YM_DELTAT, but it's hard without registers
// (see the way the YM2610 does it)
//YM_DELTAT_postload(OPL->deltat, REGS);
}
#endif
}
}
}

View File

@ -105,5 +105,10 @@ void Y8950SetUpdateHandler (int which, OPL_UPDATEHANDLER UpdateHandler, int para
#endif
#define FM_OPL_SAVESTATE_YM3812 1
#define FM_OPL_SAVESTATE_YM3526 2
#define FM_OPL_SAVESTATE_Y8950 3
void FMOPLScan(INT32 nType, INT32 which, INT32 nAction, INT32 *pnMin);
#endif /* __FMOPL_H_ */