From d7117cdcdc0f4826a1245d1374dc8acc54b9e863 Mon Sep 17 00:00:00 2001 From: zanac <21194919+zanac@users.noreply.github.com> Date: Fri, 30 Oct 2026 07:47:53 +0100 Subject: [PATCH] Save states: exact load for Ms. Pac-Man, SNK, Bubble Bobble, OPL, SCSP and Sega Model 2 (#2182) - mspacman: the mapped ROM (decoded set or boot ROM) - snk.c: the sound command/busy register, the sprite blink parity, the tile cache marked dirty after a load - fmopl.c: the whole YM3812/YM3526/Y8950 state (channels, operators, envelopes, LFO, noise, timers, status, Delta-T ADPCM); replaces the register replay of #2045 (3812intf.c back to its earlier version) - scsp.c: chip registers, slots, LFOs, DSP, timers, MIDI FIFOs, DMA - model2: which CPU waits on the i960/copro FIFOs, the SCSP IRQ line - bublbobl: ROM bank, display enable, sound NMI, MCU port latches - m6800.c: the 6801/6803/63701 internal registers (ports, timer, TCSR, RAM control, WAI state) - mb86233.c: the stall flag Claude-Session: https://claude.ai/code/session_01N3fzdsnzkQVtgYtYzqepjX Co-authored-by: Claude --- src/cpu/m6800/m6800.c | 18 ++++ src/cpu/mb86233/mb86233.c | 2 + src/drivers/bublbobl.c | 42 ++++++++- src/drivers/model2.c | 1 + src/drivers/snk.c | 20 ++++ src/machine/bublbobl_machine.c | 41 ++++++++- src/machine/model2_machine.c | 23 +++++ src/machine/mspacman.c | 28 +++++- src/sound/3812intf.c | 51 +---------- src/sound/fmopl.c | 161 +++++++++++++++++++++++++++++++++ src/sound/scsp.c | 131 +++++++++++++++++++++++++++ src/vidhrdw/snk_vidhrdw.c | 9 ++ 12 files changed, 472 insertions(+), 55 deletions(-) diff --git a/src/cpu/m6800/m6800.c b/src/cpu/m6800/m6800.c index 99e8d72e..dc8ee761 100644 --- a/src/cpu/m6800/m6800.c +++ b/src/cpu/m6800/m6800.c @@ -554,6 +554,24 @@ static void state_register(const char *type) state_save_register_UINT8 (type, cpu, "NMI_STATE", &m6800.nmi_state, 1); state_save_register_UINT8 (type, cpu, "IRQ_STATE", &m6800.irq_state[M6800_IRQ_LINE], 1); state_save_register_UINT8 (type, cpu, "TIN_STATE", &m6800.irq_state[M6800_TIN_LINE], 1); + state_save_register_UINT16(type, cpu, "PPC", &m6800.ppc.w.l, 1); + state_save_register_UINT8 (type, cpu, "WAI_STATE", &m6800.wai_state, 1); + state_save_register_UINT8 (type, cpu, "IC_EDDGE", &m6800.ic_eddge, 1); + state_save_register_int (type, cpu, "EXTRA_CYCLES", &m6800.extra_cycles); + /* the internal registers of the 6801/6803/63701: ports, timer, RAM control */ + state_save_register_UINT8 (type, cpu, "PORT1_DDR", &m6800.port1_ddr, 1); + state_save_register_UINT8 (type, cpu, "PORT2_DDR", &m6800.port2_ddr, 1); + state_save_register_UINT8 (type, cpu, "PORT1_DATA", &m6800.port1_data, 1); + state_save_register_UINT8 (type, cpu, "PORT2_DATA", &m6800.port2_data, 1); + state_save_register_UINT8 (type, cpu, "TCSR", &m6800.tcsr, 1); + state_save_register_UINT8 (type, cpu, "PENDING_TCSR", &m6800.pending_tcsr, 1); + state_save_register_UINT8 (type, cpu, "IRQ2", &m6800.irq2, 1); + state_save_register_UINT8 (type, cpu, "RAM_CTRL", &m6800.ram_ctrl, 1); + state_save_register_UINT32(type, cpu, "COUNTER", &m6800.counter.d, 1); + state_save_register_UINT32(type, cpu, "OUTPUT_COMPARE", &m6800.output_compare.d, 1); + state_save_register_UINT16(type, cpu, "INPUT_CAPTURE", &m6800.input_capture, 1); + state_save_register_UINT32(type, cpu, "TIMER_OVER", &m6800.timer_over.d, 1); + state_save_register_UINT32(type, cpu, "TIMER_NEXT", &timer_next, 1); } void m6800_init(void) diff --git a/src/cpu/mb86233/mb86233.c b/src/cpu/mb86233/mb86233.c index 1e4ffc8c..f5513d54 100644 --- a/src/cpu/mb86233/mb86233.c +++ b/src/cpu/mb86233/mb86233.c @@ -1160,6 +1160,8 @@ void mb86233_init( void ) state_save_register_int( "mb86233", cpu, "gpio1", &mb.gpio[ 1 ] ); state_save_register_int( "mb86233", cpu, "gpio2", &mb.gpio[ 2 ] ); state_save_register_int( "mb86233", cpu, "gpio3", &mb.gpio[ 3 ] ); + /* a stalled FIFO access is retried when the CPU runs again */ + state_save_register_int( "mb86233", cpu, "stall", &mb.stall ); } void mb86233_reset( void *param ) diff --git a/src/drivers/bublbobl.c b/src/drivers/bublbobl.c index 346f56c3..d56c258c 100644 --- a/src/drivers/bublbobl.c +++ b/src/drivers/bublbobl.c @@ -138,6 +138,7 @@ CPU 3 #include "driver.h" #include "vidhrdw/generic.h" #include "cpu/m6805/m6805.h" +#include "state.h" static int ddr1, ddr2, ddr3, ddr4; static int port1_in, port2_in, port3_in, port4_in; @@ -170,6 +171,26 @@ WRITE_HANDLER( bublbobl_sh_nmi_disable_w ); WRITE_HANDLER( bublbobl_sh_nmi_enable_w ); +void bublbobl_save_state(void); + +/* save states: the M6801 ports here, the rest in machine/bublbobl_machine.c */ +static void bublbobl_register_state(void) +{ + bublbobl_save_state(); + state_save_register_int("bublbobl", 0, "ddr1", &ddr1); + state_save_register_int("bublbobl", 0, "ddr2", &ddr2); + state_save_register_int("bublbobl", 0, "ddr3", &ddr3); + state_save_register_int("bublbobl", 0, "ddr4", &ddr4); + state_save_register_int("bublbobl", 0, "port1_in", &port1_in); + state_save_register_int("bublbobl", 0, "port2_in", &port2_in); + state_save_register_int("bublbobl", 0, "port3_in", &port3_in); + state_save_register_int("bublbobl", 0, "port4_in", &port4_in); + state_save_register_int("bublbobl", 0, "port1_out", &port1_out); + state_save_register_int("bublbobl", 0, "port2_out", &port2_out); + state_save_register_int("bublbobl", 0, "port3_out", &port3_out); + state_save_register_int("bublbobl", 0, "port4_out", &port4_out); +} + READ_HANDLER( bublbobl_mcu_ddr1_r ) { return ddr1; @@ -302,6 +323,24 @@ static unsigned char portA_in,portA_out,ddrA; static unsigned char portB_in,portB_out,ddrB; static unsigned char portC_in,portC_out,ddrC; +static void tokio_register_state(void) +{ + bublbobl_save_state(); + state_save_register_UINT8("tokio", 0, "from_main", &from_main, 1); + state_save_register_UINT8("tokio", 0, "from_mcu", &from_mcu, 1); + state_save_register_int ("tokio", 0, "mcu_sent", &mcu_sent); + state_save_register_int ("tokio", 0, "main_sent", &main_sent); + state_save_register_UINT8("tokio", 0, "portA_in", &portA_in, 1); + state_save_register_UINT8("tokio", 0, "portA_out", &portA_out, 1); + state_save_register_UINT8("tokio", 0, "ddrA", &ddrA, 1); + state_save_register_UINT8("tokio", 0, "portB_in", &portB_in, 1); + state_save_register_UINT8("tokio", 0, "portB_out", &portB_out, 1); + state_save_register_UINT8("tokio", 0, "ddrB", &ddrB, 1); + state_save_register_UINT8("tokio", 0, "portC_in", &portC_in, 1); + state_save_register_UINT8("tokio", 0, "portC_out", &portC_out, 1); + state_save_register_UINT8("tokio", 0, "ddrC", &ddrC, 1); +} + READ_HANDLER( tokio_68705_portA_r ) { return (portA_out & ddrA) | (portA_in & ~ddrA); @@ -1720,6 +1759,7 @@ static DRIVER_INIT( bublbobl ) memcpy(ROM+0x08000,ROM+0x10000,0x4000); bublbobl_video_enable = 1; + bublbobl_register_state(); } static DRIVER_INIT( boblbobl ) @@ -1745,7 +1785,7 @@ static DRIVER_INIT( tokio ) /* preemptively enable video, the bit is not mapped for this game and */ /* I don't know if it even has it. */ - bublbobl_video_enable = 1; + bublbobl_video_enable = 1; tokio_register_state(); } diff --git a/src/drivers/model2.c b/src/drivers/model2.c index 848734ae..b60b4585 100644 --- a/src/drivers/model2.c +++ b/src/drivers/model2.c @@ -1881,6 +1881,7 @@ static DRIVER_INIT( model2a ) state_save_register_UINT8( "model2comm", 0, "zfg", &m2comm_zfg, 1 ); state_save_register_UINT8( "model2", 0, "io_analog_channel", &io_analog_channel, 1 ); state_save_register_UINT8( "model2", 0, "lightgun_mux", &m_lightgun_mux, 1 ); + state_save_register_int( "model2", 0, "scsp_last_line", &scsp_last_line ); state_save_register_func_postload( model2_postload ); } diff --git a/src/drivers/snk.c b/src/drivers/snk.c index 2bd1d94b..b5072f7f 100644 --- a/src/drivers/snk.c +++ b/src/drivers/snk.c @@ -216,6 +216,7 @@ Credits (in alphabetical order) ***************************************************************************/ #include "driver.h" +#include "state.h" #include "vidhrdw/generic.h" #include "cpu/z80/z80.h" #include "snk.h" @@ -289,6 +290,14 @@ static int hard_flags; */ static int snk_sound_register; +/* the sound handshake register is part of the save state (the sound CPU + went another way after loading a state in a new instance) */ +static MACHINE_INIT( snk ) +{ + snk_sound_register = 0; + state_save_register_int("snk", 0, "sound_register", &snk_sound_register); +} + /*********************************************************************/ static int snk_rot8( int which ){ @@ -1058,6 +1067,7 @@ static MACHINE_DRIVER_START( tnk3 ) MDRV_PALETTE_INIT(aso) MDRV_VIDEO_START(snk) + MDRV_MACHINE_INIT(snk) MDRV_VIDEO_UPDATE(tnk3) /* sound hardware */ @@ -1094,6 +1104,7 @@ static MACHINE_DRIVER_START( athena ) MDRV_PALETTE_INIT(aso) MDRV_VIDEO_START(snk) + MDRV_MACHINE_INIT(snk) MDRV_VIDEO_UPDATE(tnk3) /* sound hardware */ @@ -1130,6 +1141,7 @@ static MACHINE_DRIVER_START( ikari ) MDRV_PALETTE_INIT(snk_3bpp_shadow) MDRV_VIDEO_START(snk) + MDRV_MACHINE_INIT(snk) MDRV_VIDEO_UPDATE(ikari) /* sound hardware */ @@ -1168,6 +1180,7 @@ static MACHINE_DRIVER_START( victroad ) MDRV_PALETTE_INIT(snk_3bpp_shadow) MDRV_VIDEO_START(snk) + MDRV_MACHINE_INIT(snk) MDRV_VIDEO_UPDATE(ikari) /* sound hardware */ @@ -1205,6 +1218,7 @@ static MACHINE_DRIVER_START( gwar ) MDRV_PALETTE_INIT(RRRR_GGGG_BBBB) MDRV_VIDEO_START(snk) + MDRV_MACHINE_INIT(snk) MDRV_VIDEO_UPDATE(gwar) /* sound hardware */ @@ -1243,6 +1257,7 @@ static MACHINE_DRIVER_START( bermudat ) MDRV_PALETTE_INIT(RRRR_GGGG_BBBB) MDRV_VIDEO_START(snk) + MDRV_MACHINE_INIT(snk) MDRV_VIDEO_UPDATE(gwar) /* sound hardware */ @@ -1280,6 +1295,7 @@ static MACHINE_DRIVER_START( psychos ) MDRV_PALETTE_INIT(RRRR_GGGG_BBBB) MDRV_VIDEO_START(snk) + MDRV_MACHINE_INIT(snk) MDRV_VIDEO_UPDATE(gwar) /* sound hardware */ @@ -1317,6 +1333,7 @@ static MACHINE_DRIVER_START( chopper1 ) MDRV_PALETTE_INIT(RRRR_GGGG_BBBB) MDRV_VIDEO_START(snk) + MDRV_MACHINE_INIT(snk) MDRV_VIDEO_UPDATE(gwar) /* sound hardware */ @@ -1354,6 +1371,7 @@ static MACHINE_DRIVER_START( tdfever ) MDRV_PALETTE_INIT(snk_4bpp_shadow) MDRV_VIDEO_START(snk) + MDRV_MACHINE_INIT(snk) MDRV_VIDEO_UPDATE(tdfever) /* sound hardware */ @@ -1391,6 +1409,7 @@ static MACHINE_DRIVER_START( tdfever2 ) MDRV_PALETTE_INIT(snk_4bpp_shadow) MDRV_VIDEO_START(snk) + MDRV_MACHINE_INIT(snk) MDRV_VIDEO_UPDATE(tdfever) /* sound hardware */ @@ -1428,6 +1447,7 @@ static MACHINE_DRIVER_START( ftsoccer ) MDRV_PALETTE_INIT(snk_4bpp_shadow) MDRV_VIDEO_START(snk) + MDRV_MACHINE_INIT(snk) MDRV_VIDEO_UPDATE(tdfever) /* sound hardware */ diff --git a/src/machine/bublbobl_machine.c b/src/machine/bublbobl_machine.c index cbd1f118..ddeee185 100644 --- a/src/machine/bublbobl_machine.c +++ b/src/machine/bublbobl_machine.c @@ -10,12 +10,26 @@ #include "driver.h" #include "cpu/z80/z80.h" #include "vidhrdw/generic.h" +#include "state.h" unsigned char *bublbobl_sharedram1,*bublbobl_sharedram2; extern int bublbobl_video_enable; +/* the ROM bank at 0x8000, -1 until the game selects one */ +static int bublbobl_bank = -1; + +static void bublbobl_set_bank(void) +{ + unsigned char *ROM = memory_region(REGION_CPU1); + + if (bublbobl_bank >= 0) + cpu_setbank(1,&ROM[0x10000 + 0x4000 * bublbobl_bank]); + else + cpu_setbank(1,&ROM[0x8000]); +} + READ_HANDLER( bublbobl_sharedram1_r ) { @@ -41,7 +55,8 @@ WRITE_HANDLER( bublbobl_bankswitch_w ) unsigned char *ROM = memory_region(REGION_CPU1); /* bits 0-2 select ROM bank */ - cpu_setbank(1,&ROM[0x10000 + 0x4000 * ((data ^ 4) & 7)]); + bublbobl_bank = (data ^ 4) & 7; + cpu_setbank(1,&ROM[0x10000 + 0x4000 * bublbobl_bank]); /* bit 3 n.c. */ @@ -65,7 +80,8 @@ WRITE_HANDLER( tokio_bankswitch_w ) unsigned char *ROM = memory_region(REGION_CPU1); /* bits 0-2 select ROM bank */ - cpu_setbank(1,&ROM[0x10000 + 0x4000 * (data & 7)]); + bublbobl_bank = data & 7; + cpu_setbank(1,&ROM[0x10000 + 0x4000 * bublbobl_bank]); /* bits 3-7 unknown */ } @@ -257,3 +273,24 @@ WRITE_HANDLER( bublbobl_68705_ddrB_w ) { ddrB = data; } + + +/* save states: the ROM bank, the display enable, the sound NMI and the + 68705 ports of Bubble Bobble and Tokio */ +void bublbobl_save_state(void) +{ + bublbobl_bank = -1; + state_save_register_int ("bublbobl", 0, "bank", &bublbobl_bank); + state_save_register_int ("bublbobl", 0, "video_enable", &bublbobl_video_enable); + state_save_register_int ("bublbobl", 0, "sound_nmi_enable", &sound_nmi_enable); + state_save_register_int ("bublbobl", 0, "pending_nmi", &pending_nmi); + state_save_register_UINT8("bublbobl", 0, "portA_in", &portA_in, 1); + state_save_register_UINT8("bublbobl", 0, "portA_out", &portA_out, 1); + state_save_register_UINT8("bublbobl", 0, "ddrA", &ddrA, 1); + state_save_register_UINT8("bublbobl", 0, "portB_in", &portB_in, 1); + state_save_register_UINT8("bublbobl", 0, "portB_out", &portB_out, 1); + state_save_register_UINT8("bublbobl", 0, "ddrB", &ddrB, 1); + state_save_register_int ("bublbobl", 0, "address", &address); + state_save_register_int ("bublbobl", 0, "latch", &latch); + state_save_register_func_postload(bublbobl_set_bank); +} diff --git a/src/machine/model2_machine.c b/src/machine/model2_machine.c index f0760279..6e02b0d7 100644 --- a/src/machine/model2_machine.c +++ b/src/machine/model2_machine.c @@ -526,11 +526,27 @@ READ32_HANDLER( model2_copro_status_r ) static UINT32 fifo_in_save[ FIFO_ALLOC ], fifo_out_save[ FIFO_ALLOC ]; static int fifo_in_count, fifo_out_count; +static INT32 fifo_waits_save[ 4 ]; + +static void fifo_wait_load( struct m2_fifo *f, int reader_waits, int writer_waits ) +{ + f->reader_waits = reader_waits ? 1 : 0; + f->writer_waits = writer_waits ? 1 : 0; + if( f->reader_waits ) + cpunum_suspend( f->reader, SUSPEND_REASON_SPIN, 1 ); + if( f->writer_waits ) + cpunum_suspend( f->writer, SUSPEND_REASON_SPIN, 1 ); +} + static void copro_presave( void ) { int i; fifo_in_count = fifo_in.count; fifo_out_count = fifo_out.count; + fifo_waits_save[ 0 ] = fifo_in.reader_waits; + fifo_waits_save[ 1 ] = fifo_in.writer_waits; + fifo_waits_save[ 2 ] = fifo_out.reader_waits; + fifo_waits_save[ 3 ] = fifo_out.writer_waits; for( i = 0; i < fifo_in.count; i++ ) fifo_in_save[ i ] = fifo_in.data[ ( fifo_in.head + i ) % FIFO_ALLOC ]; for( i = 0; i < fifo_out.count; i++ ) @@ -548,6 +564,12 @@ static void copro_postload( void ) fifo_out.data[ i ] = fifo_out_save[ i ]; fifo_in.count = fifo_in_count; fifo_out.count = fifo_out_count; + if( model2_copro_type == MODEL2_COPRO_SHARC ) + sharc_flags_update(); + /* the CPUs waiting on a FIFO at the save wait again, so that the + access that ends the wait resumes them */ + fifo_wait_load( &fifo_in, fifo_waits_save[ 0 ], fifo_waits_save[ 1 ] ); + fifo_wait_load( &fifo_out, fifo_waits_save[ 2 ], fifo_waits_save[ 3 ] ); if( tgp_booted ) cpunum_resume( CPU_TGP, SUSPEND_REASON_HALT ); else @@ -585,6 +607,7 @@ void model2_copro_init( void ) state_save_register_UINT32( "model2tgp", 0, "fifo_out", fifo_out_save, FIFO_ALLOC ); state_save_register_int( "model2tgp", 0, "fifo_in_count", &fifo_in_count ); state_save_register_int( "model2tgp", 0, "fifo_out_count", &fifo_out_count ); + state_save_register_INT32( "model2tgp", 0, "fifo_waits", fifo_waits_save, 4 ); state_save_register_func_presave( copro_presave ); state_save_register_func_postload( copro_postload ); } diff --git a/src/machine/mspacman.c b/src/machine/mspacman.c index 832f7ab4..b58b16e8 100644 --- a/src/machine/mspacman.c +++ b/src/machine/mspacman.c @@ -1,6 +1,7 @@ #include "driver.h" #include "vidhrdw/generic.h" +#include "state.h" /* @@ -180,11 +181,26 @@ void mspacman_decode(void) } -MACHINE_INIT( mspacman ) +/* the board boots from the original Pac-Man ROMs and switches to the + decoded Ms. Pac-Man ROMs on a write to 0x5006; the switch is part of the + save state, so that a state loaded in a new instance runs the same code */ +static UINT8 mspacman_rom_active; + +static void mspacman_set_bank(void) { unsigned char *RAM = memory_region(REGION_CPU1); + + cpu_setbank (1, &RAM[mspacman_rom_active ? 0x10000 : 0x00000]); +} + +MACHINE_INIT( mspacman ) +{ mspacman_decode(); - cpu_setbank (1, &RAM[0x00000]); + mspacman_rom_active = 0; + mspacman_set_bank(); + + state_save_register_UINT8("mspacman", 0, "rom_active", &mspacman_rom_active, 1); + state_save_register_func_postload(mspacman_set_bank); machine_init_pacman(); } @@ -192,7 +208,9 @@ MACHINE_INIT( mspacman ) WRITE_HANDLER( mspacman_activate_rom ) { - unsigned char *RAM = memory_region(REGION_CPU1); - - if(data==1) cpu_setbank (1, &RAM[0x10000]); + if (data == 1) + { + mspacman_rom_active = 1; + mspacman_set_bank(); + } } diff --git a/src/sound/3812intf.c b/src/sound/3812intf.c index 29733ba3..0f5f8d56 100644 --- a/src/sound/3812intf.c +++ b/src/sound/3812intf.c @@ -18,7 +18,6 @@ ******************************************************************************/ #include "driver.h" #include "3812intf.h" -#include "state.h" #include "fm.h" #include "sound/fmopl.h" @@ -28,42 +27,6 @@ static int stream_3812[MAX_3812]; static void *Timer_3812[MAX_3812*2]; static const struct YM3812interface *intf_3812 = NULL; - -/* The OPL core keeps no save state: the interface keeps a copy of every - register written (and the address register), saves it, and writes it all - back on load. That restores the channels and starts the timers again, which - sound CPUs that wait on the timer flag or take its interrupt need. */ -static UINT8 regs_3812[MAX_3812][256]; -static UINT8 addr_3812[MAX_3812]; - -static void YM3812_postload(void) -{ - int i, r; - for (i = 0; i < intf_3812->num; i++) - { - YM3812ResetChip(i); - for (r = 0x01; r < 0x100; r++) - { - if (r == 0x04) - continue; - YM3812Write(i, 0, r); - YM3812Write(i, 1, regs_3812[i][r]); - } - /* the timers last: their load values are in place, start them (bit 7, the IRQ reset, is an action) */ - YM3812Write(i, 0, 0x04); - YM3812Write(i, 1, regs_3812[i][0x04] & 0x63); - YM3812Write(i, 0, addr_3812[i]); - } -} - -static void YM3812_write_shadow(int n, int a, int data) -{ - if (a == 0) - addr_3812[n] = data; - else - regs_3812[n][addr_3812[n]] = data; - YM3812Write(n, a, data); -} static void IRQHandler_3812(int n,int irq) { if (intf_3812->handler[n]) (intf_3812->handler[n])(irq ? ASSERT_LINE : CLEAR_LINE); @@ -123,13 +86,7 @@ int YM3812_sh_start(const struct MachineSound *msound) Timer_3812[i*2+0] = timer_alloc(timer_callback_3812); Timer_3812[i*2+1] = timer_alloc(timer_callback_3812); - - memset(regs_3812[i], 0, sizeof(regs_3812[i])); - addr_3812[i] = 0; - state_save_register_UINT8("YM3812", i, "regs", regs_3812[i], 256); - state_save_register_UINT8("YM3812", i, "address", &addr_3812[i], 1); } - state_save_register_func_postload(YM3812_postload); return 0; } @@ -148,10 +105,10 @@ void YM3812_sh_reset(void) } WRITE_HANDLER( YM3812_control_port_0_w ) { - YM3812_write_shadow(0, 0, data); + YM3812Write(0, 0, data); } WRITE_HANDLER( YM3812_write_port_0_w ) { - YM3812_write_shadow(0, 1, data); + YM3812Write(0, 1, data); } READ_HANDLER( YM3812_status_port_0_r ) { return YM3812Read(0, 0); @@ -162,10 +119,10 @@ READ_HANDLER( YM3812_read_port_0_r ) { WRITE_HANDLER( YM3812_control_port_1_w ) { - YM3812_write_shadow(1, 0, data); + YM3812Write(1, 0, data); } WRITE_HANDLER( YM3812_write_port_1_w ) { - YM3812_write_shadow(1, 1, data); + YM3812Write(1, 1, data); } READ_HANDLER( YM3812_status_port_1_r ) { return YM3812Read(1, 0); diff --git a/src/sound/fmopl.c b/src/sound/fmopl.c index 038442af..9f6cf47e 100644 --- a/src/sound/fmopl.c +++ b/src/sound/fmopl.c @@ -73,6 +73,7 @@ Revision History: #include "driver.h" /* use M.A.M.E. */ +#include "state.h" #include "ymdeltat.h" #include "fmopl.h" @@ -2006,6 +2007,157 @@ static int OPLTimerOver(FM_OPL *OPL,int c) #define MAX_OPL_CHIPS 2 +/* Save states: the whole chip state, so that a state loaded in a new + instance (netplay, RunAhead with a second instance) goes on exactly as + the running one. Only pointers are left out: connect1 is rebuilt from + CON, and the Delta-T pan pointer is kept as its index. */ +static FM_OPL *OPL_saved[3 * MAX_OPL_CHIPS]; +static int OPL_saved_count; +#if BUILD_Y8950 +static UINT8 OPL_deltat_pan[3 * MAX_OPL_CHIPS]; +#endif + +static void OPL_save_presave(void) +{ +#if BUILD_Y8950 + int i; + for (i = 0; i < OPL_saved_count; i++) + { + YM_DELTAT *DELTAT = OPL_saved[i]->deltat; + if (DELTAT && DELTAT->pan && DELTAT->output_pointer) + OPL_deltat_pan[i] = DELTAT->pan - DELTAT->output_pointer; + } +#endif +} + +static void OPL_save_postload(void) +{ + int i, ch; + for (i = 0; i < OPL_saved_count; i++) + { + FM_OPL *OPL = OPL_saved[i]; + for (ch = 0; ch < 9; ch++) + { + OPL_SLOT *SLOT = &OPL->P_CH[ch].SLOT[SLOT1]; + SLOT->connect1 = SLOT->CON ? &output[0] : &phase_modulation; + } +#if BUILD_Y8950 + if (OPL->deltat && OPL->deltat->output_pointer) + { + OPL->deltat->pan = &OPL->deltat->output_pointer[OPL_deltat_pan[i] & 3]; + } +#endif + } +} + +static void OPL_save_state(FM_OPL *OPL, const char *name, int num) +{ + char buf[32]; + int ch, s; + + if (OPL_saved_count >= 3 * MAX_OPL_CHIPS) + return; + OPL_saved[OPL_saved_count++] = OPL; + + for (ch = 0; ch < 9; ch++) + { + OPL_CH *CH = &OPL->P_CH[ch]; + sprintf(buf, "%s.ch%d", name, ch); + state_save_register_UINT32(buf, num, "block_fnum", &CH->block_fnum, 1); + state_save_register_UINT32(buf, num, "fc", &CH->fc, 1); + state_save_register_UINT32(buf, num, "ksl_base", &CH->ksl_base, 1); + state_save_register_UINT8 (buf, num, "kcode", &CH->kcode, 1); + for (s = 0; s < 2; s++) + { + OPL_SLOT *SLOT = &CH->SLOT[s]; + sprintf(buf, "%s.ch%d.slot%d", name, ch, s); + state_save_register_UINT32(buf, num, "ar", &SLOT->ar, 1); + state_save_register_UINT32(buf, num, "dr", &SLOT->dr, 1); + state_save_register_UINT32(buf, num, "rr", &SLOT->rr, 1); + state_save_register_UINT8 (buf, num, "KSR", &SLOT->KSR, 1); + state_save_register_UINT8 (buf, num, "ksl", &SLOT->ksl, 1); + state_save_register_UINT8 (buf, num, "ksr", &SLOT->ksr, 1); + state_save_register_UINT8 (buf, num, "mul", &SLOT->mul, 1); + state_save_register_UINT32(buf, num, "Cnt", &SLOT->Cnt, 1); + state_save_register_UINT32(buf, num, "Incr", &SLOT->Incr, 1); + state_save_register_UINT8 (buf, num, "FB", &SLOT->FB, 1); + state_save_register_INT32 (buf, num, "op1_out", SLOT->op1_out, 2); + state_save_register_UINT8 (buf, num, "CON", &SLOT->CON, 1); + state_save_register_UINT8 (buf, num, "eg_type", &SLOT->eg_type, 1); + state_save_register_UINT8 (buf, num, "state", &SLOT->state, 1); + state_save_register_UINT32(buf, num, "TL", &SLOT->TL, 1); + state_save_register_INT32 (buf, num, "TLL", &SLOT->TLL, 1); + state_save_register_INT32 (buf, num, "volume", &SLOT->volume, 1); + state_save_register_UINT32(buf, num, "sl", &SLOT->sl, 1); + state_save_register_UINT8 (buf, num, "eg_sh_ar", &SLOT->eg_sh_ar, 1); + state_save_register_UINT8 (buf, num, "eg_sel_ar", &SLOT->eg_sel_ar, 1); + state_save_register_UINT8 (buf, num, "eg_sh_dr", &SLOT->eg_sh_dr, 1); + state_save_register_UINT8 (buf, num, "eg_sel_dr", &SLOT->eg_sel_dr, 1); + state_save_register_UINT8 (buf, num, "eg_sh_rr", &SLOT->eg_sh_rr, 1); + state_save_register_UINT8 (buf, num, "eg_sel_rr", &SLOT->eg_sel_rr, 1); + state_save_register_UINT32(buf, num, "key", &SLOT->key, 1); + state_save_register_UINT32(buf, num, "AMmask", &SLOT->AMmask, 1); + state_save_register_UINT8 (buf, num, "vib", &SLOT->vib, 1); + state_save_register_UINT16(buf, num, "wavetable", &SLOT->wavetable, 1); + } + } + state_save_register_UINT32(name, num, "eg_cnt", &OPL->eg_cnt, 1); + state_save_register_UINT32(name, num, "eg_timer", &OPL->eg_timer, 1); + state_save_register_UINT8 (name, num, "rhythm", &OPL->rhythm, 1); + state_save_register_UINT8 (name, num, "lfo_am_depth", &OPL->lfo_am_depth, 1); + state_save_register_UINT8 (name, num, "lfo_pm_depth_range", &OPL->lfo_pm_depth_range, 1); + state_save_register_UINT32(name, num, "lfo_am_cnt", &OPL->lfo_am_cnt, 1); + state_save_register_UINT32(name, num, "lfo_pm_cnt", &OPL->lfo_pm_cnt, 1); + state_save_register_UINT32(name, num, "noise_rng", &OPL->noise_rng, 1); + state_save_register_UINT32(name, num, "noise_p", &OPL->noise_p, 1); + state_save_register_UINT8 (name, num, "wavesel", &OPL->wavesel, 1); + state_save_register_UINT32(name, num, "T", OPL->T, 2); + state_save_register_UINT8 (name, num, "st", OPL->st, 2); + state_save_register_UINT8 (name, num, "address", &OPL->address, 1); + state_save_register_UINT8 (name, num, "status", &OPL->status, 1); + state_save_register_UINT8 (name, num, "statusmask", &OPL->statusmask, 1); + state_save_register_UINT8 (name, num, "mode", &OPL->mode, 1); +#if BUILD_Y8950 + if (OPL->deltat) + { + YM_DELTAT *DELTAT = OPL->deltat; + state_save_register_UINT8 (name, num, "portDirection", &OPL->portDirection, 1); + state_save_register_UINT8 (name, num, "portLatch", &OPL->portLatch, 1); + sprintf(buf, "%s.deltat", name); + state_save_register_UINT32(buf, num, "now_addr", &DELTAT->now_addr, 1); + state_save_register_UINT32(buf, num, "now_step", &DELTAT->now_step, 1); + state_save_register_UINT32(buf, num, "step", &DELTAT->step, 1); + state_save_register_UINT32(buf, num, "start", &DELTAT->start, 1); + state_save_register_UINT32(buf, num, "limit", &DELTAT->limit, 1); + state_save_register_UINT32(buf, num, "end", &DELTAT->end, 1); + state_save_register_UINT32(buf, num, "delta", &DELTAT->delta, 1); + state_save_register_INT32 (buf, num, "volume", &DELTAT->volume, 1); + state_save_register_INT32 (buf, num, "acc", &DELTAT->acc, 1); + state_save_register_INT32 (buf, num, "adpcmd", &DELTAT->adpcmd, 1); + state_save_register_INT32 (buf, num, "adpcml", &DELTAT->adpcml, 1); + state_save_register_INT32 (buf, num, "prev_acc", &DELTAT->prev_acc, 1); + state_save_register_UINT8 (buf, num, "now_data", &DELTAT->now_data, 1); + state_save_register_UINT8 (buf, num, "CPU_data", &DELTAT->CPU_data, 1); + state_save_register_UINT8 (buf, num, "portstate", &DELTAT->portstate, 1); + state_save_register_UINT8 (buf, num, "control2", &DELTAT->control2, 1); + state_save_register_UINT8 (buf, num, "portshift", &DELTAT->portshift, 1); + state_save_register_UINT8 (buf, num, "DRAMportshift", &DELTAT->DRAMportshift, 1); + state_save_register_UINT8 (buf, num, "memread", &DELTAT->memread, 1); + state_save_register_UINT8 (buf, num, "PCM_BSY", &DELTAT->PCM_BSY, 1); + state_save_register_UINT8 (buf, num, "reg", DELTAT->reg, 16); + state_save_register_UINT8 (buf, num, "pan", &OPL_deltat_pan[OPL_saved_count - 1], 1); + } +#endif + state_save_register_func_presave(OPL_save_presave); + state_save_register_func_postload(OPL_save_postload); +} + +static void OPL_save_reset(void) +{ + OPL_saved_count = 0; +} + + #if (BUILD_YM3812) static FM_OPL *OPL_YM3812[MAX_OPL_CHIPS]; /* array of pointers to the YM3812's */ @@ -2032,6 +2184,7 @@ int YM3812Init(int num, int clock, int rate) } /* reset */ YM3812ResetChip(i); + OPL_save_state(OPL_YM3812[i], "YM3812", i); } return 0; @@ -2041,6 +2194,8 @@ void YM3812Shutdown(void) { int i; + OPL_save_reset(); + for (i = 0;i < YM3812NumChips; i++) { /* emulator shutdown */ @@ -2183,6 +2338,7 @@ int YM3526Init(int num, int clock, int rate) } /* reset */ YM3526ResetChip(i); + OPL_save_state(OPL_YM3526[i], "YM3526", i); } return 0; @@ -2192,6 +2348,8 @@ void YM3526Shutdown(void) { int i; + OPL_save_reset(); + for (i = 0;i < YM3526NumChips; i++) { /* emulator shutdown */ @@ -2352,6 +2510,7 @@ int Y8950Init(int num, int clock, int rate) /*OPL_Y8950[i]->deltat->read_time = 8.0 / clock;*/ /* a single byte read takes 8 cycles of main clock */ /* reset */ Y8950ResetChip(i); + OPL_save_state(OPL_Y8950[i], "Y8950", i); } return 0; @@ -2361,6 +2520,8 @@ void Y8950Shutdown(void) { int i; + OPL_save_reset(); + for (i = 0;i < Y8950NumChips; i++) { /* emulator shutdown */ diff --git a/src/sound/scsp.c b/src/sound/scsp.c index a8ee059a..2bf87339 100644 --- a/src/sound/scsp.c +++ b/src/sound/scsp.c @@ -23,6 +23,7 @@ #include #include "driver.h" #include "cpuintrf.h" +#include "state.h" static signed short *bufferl; static signed short *bufferr; @@ -1095,6 +1096,134 @@ static void SCSP_Update(int num, short **buf, int samples) SCSP_DoMasterSamples(num, samples); } +/* Save states: the chip registers, the slots (phase, envelope, LFOs), + the effect DSP, the timers, the interrupts and the MIDI FIFOs, so that + a state loaded in a new instance goes on exactly as the running one. + The pointers (sample base, LFO tables) are kept as offsets. */ +static UINT32 SCSP_ss_base[MAX_SCSP][32]; +static UINT8 SCSP_ss_lfo[MAX_SCSP][32][4]; /* PLFO table, PLFO scale, ALFO table, ALFO scale */ + +static int SCSP_lfo_table_index(int *table, int alfo) +{ + if (alfo) + { + if (table == ALFO_SQR) return 1; + if (table == ALFO_TRI) return 2; + if (table == ALFO_NOI) return 3; + return 0; + } + if (table == PLFO_SQR) return 1; + if (table == PLFO_TRI) return 2; + if (table == PLFO_NOI) return 3; + return 0; +} + +static int *SCSP_lfo_table(int index, int alfo) +{ + static int *atab[4] = { ALFO_SAW, ALFO_SQR, ALFO_TRI, ALFO_NOI }; + static int *ptab[4] = { PLFO_SAW, PLFO_SQR, PLFO_TRI, PLFO_NOI }; + return alfo ? atab[index & 3] : ptab[index & 3]; +} + +static void SCSP_presave(void) +{ + int i, s; + for (i = 0; i < MAX_SCSP; i++) + for (s = 0; s < 32; s++) + { + struct _SLOT *slot = &SCSPs[i].Slots[s]; + SCSP_ss_base[i][s] = (slot->base && SCSPs[i].SCSPRAM) ? (UINT32)(slot->base - SCSPs[i].SCSPRAM) : 0; + SCSP_ss_lfo[i][s][0] = slot->PLFO.table ? SCSP_lfo_table_index(slot->PLFO.table, 0) : 0; + SCSP_ss_lfo[i][s][1] = slot->PLFO.scale ? (UINT8)((slot->PLFO.scale - PSCALES[0]) / 256) : 0; + SCSP_ss_lfo[i][s][2] = slot->ALFO.table ? SCSP_lfo_table_index(slot->ALFO.table, 1) : 0; + SCSP_ss_lfo[i][s][3] = slot->ALFO.scale ? (UINT8)((slot->ALFO.scale - ASCALES[0]) / 256) : 0; + } +} + +static void SCSP_postload(void) +{ + int i, s; + for (i = 0; i < MAX_SCSP; i++) + for (s = 0; s < 32; s++) + { + struct _SLOT *slot = &SCSPs[i].Slots[s]; + slot->base = SCSPs[i].SCSPRAM ? SCSPs[i].SCSPRAM + SCSP_ss_base[i][s] : NULL; + slot->PLFO.table = SCSP_lfo_table(SCSP_ss_lfo[i][s][0], 0); + slot->PLFO.scale = PSCALES[SCSP_ss_lfo[i][s][1] & 7]; + slot->ALFO.table = SCSP_lfo_table(SCSP_ss_lfo[i][s][2], 1); + slot->ALFO.scale = ASCALES[SCSP_ss_lfo[i][s][3] & 7]; + } +} + +static void SCSP_save_state(int num) +{ + char buf[32]; + int i, s; + + for (i = 0; i < num; i++) + { + struct _SCSP *chip = &SCSPs[i]; + struct _SCSPDSP *DSP = &chip->DSP; + + state_save_register_UINT16("SCSP", i, "regs", chip->udata.data, 0x30/2); + state_save_register_int ("SCSP", i, "MvolSet", &chip->MvolSet); + for (s = 0; s < 32; s++) + { + struct _SLOT *slot = &chip->Slots[s]; + sprintf(buf, "SCSP.slot%d", s); + state_save_register_UINT16(buf, i, "regs", slot->udata.data, 0x10); + state_save_register_UINT8 (buf, i, "active", &slot->active, 1); + state_save_register_UINT32(buf, i, "base", &SCSP_ss_base[i][s], 1); + state_save_register_UINT32(buf, i, "cur_addr", &slot->cur_addr, 1); + state_save_register_UINT32(buf, i, "step", &slot->step, 1); + state_save_register_int (buf, i, "EG.volume", &slot->EG.volume); + state_save_register_int (buf, i, "EG.state", (int *)&slot->EG.state); + state_save_register_int (buf, i, "EG.step", &slot->EG.step); + state_save_register_int (buf, i, "EG.AR", &slot->EG.AR); + state_save_register_int (buf, i, "EG.D1R", &slot->EG.D1R); + state_save_register_int (buf, i, "EG.D2R", &slot->EG.D2R); + state_save_register_int (buf, i, "EG.RR", &slot->EG.RR); + state_save_register_int (buf, i, "EG.DL", &slot->EG.DL); + state_save_register_UINT8 (buf, i, "EG.EGHOLD", &slot->EG.EGHOLD, 1); + state_save_register_UINT8 (buf, i, "EG.LPLINK", &slot->EG.LPLINK, 1); + state_save_register_UINT16(buf, i, "PLFO.phase", &slot->PLFO.phase, 1); + state_save_register_UINT32(buf, i, "PLFO.phase_step", &slot->PLFO.phase_step, 1); + state_save_register_UINT16(buf, i, "ALFO.phase", &slot->ALFO.phase, 1); + state_save_register_UINT32(buf, i, "ALFO.phase_step", &slot->ALFO.phase_step, 1); + state_save_register_UINT8 (buf, i, "LFO.tables", SCSP_ss_lfo[i][s], 4); + } + state_save_register_UINT32("SCSP.DSP", i, "RBP", &DSP->RBP, 1); + state_save_register_UINT32("SCSP.DSP", i, "RBL", &DSP->RBL, 1); + state_save_register_INT16 ("SCSP.DSP", i, "COEF", DSP->COEF, 64); + state_save_register_UINT16("SCSP.DSP", i, "MADRS", DSP->MADRS, 32); + state_save_register_UINT16("SCSP.DSP", i, "MPRO", DSP->MPRO, 128*4); + state_save_register_INT32 ("SCSP.DSP", i, "TEMP", DSP->TEMP, 128); + state_save_register_INT32 ("SCSP.DSP", i, "MEMS", DSP->MEMS, 32); + state_save_register_UINT32("SCSP.DSP", i, "DEC", &DSP->DEC, 1); + state_save_register_INT32 ("SCSP.DSP", i, "MIXS", DSP->MIXS, 16); + state_save_register_INT16 ("SCSP.DSP", i, "EXTS", DSP->EXTS, 2); + state_save_register_INT16 ("SCSP.DSP", i, "EFREG", DSP->EFREG, 16); + state_save_register_int ("SCSP.DSP", i, "Stopped", &DSP->Stopped); + state_save_register_int ("SCSP.DSP", i, "LastStep", &DSP->LastStep); + } + state_save_register_UINT32("SCSP", 0, "IrqTimA", &IrqTimA, 1); + state_save_register_UINT32("SCSP", 0, "IrqTimBC", &IrqTimBC, 1); + state_save_register_UINT32("SCSP", 0, "IrqMidi", &IrqMidi, 1); + state_save_register_UINT8 ("SCSP", 0, "MidiOutW", &MidiOutW, 1); + state_save_register_UINT8 ("SCSP", 0, "MidiOutR", &MidiOutR, 1); + state_save_register_UINT8 ("SCSP", 0, "MidiStack", MidiStack, 32); + state_save_register_UINT8 ("SCSP", 0, "MidiOutStack", MidiOutStack, 16); + state_save_register_UINT8 ("SCSP", 0, "MidiW", &MidiW, 1); + state_save_register_UINT8 ("SCSP", 0, "MidiR", &MidiR, 1); + state_save_register_INT32 ("SCSP", 0, "TimPris", (INT32 *)TimPris, 3); + state_save_register_INT32 ("SCSP", 0, "TimCnt", (INT32 *)TimCnt, 3); + state_save_register_UINT32("SCSP", 0, "dmea", &scsp_dmea, 1); + state_save_register_UINT16("SCSP", 0, "drga", &scsp_drga, 1); + state_save_register_UINT16("SCSP", 0, "dtlg", &scsp_dtlg, 1); + state_save_register_func_presave(SCSP_presave); + state_save_register_func_postload(SCSP_postload); +} + int SCSP_sh_start(const struct MachineSound *msound) { char buf[2][40]; @@ -1124,6 +1253,8 @@ int SCSP_sh_start(const struct MachineSound *msound) SCSP = &SCSPs[0]; + SCSP_save_state(intf->num); + return 0; } diff --git a/src/vidhrdw/snk_vidhrdw.c b/src/vidhrdw/snk_vidhrdw.c index 4a3116ec..bb0dc6d8 100644 --- a/src/vidhrdw/snk_vidhrdw.c +++ b/src/vidhrdw/snk_vidhrdw.c @@ -1,4 +1,5 @@ #include "driver.h" +#include "state.h" #include "vidhrdw/generic.h" #include "snk.h" @@ -71,9 +72,17 @@ PALETTE_INIT( snk_4bpp_shadow ) gfx_drawmode_table[15] = DRAWMODE_NONE; } +/* after a state load the tile cache doesn't match the restored video RAM */ +static void snk_postload(void) +{ + memset( dirtybuffer, 0xff, MAX_VRAM_SIZE ); +} + VIDEO_START( snk ) { snk_blink_parity = 0; + state_save_register_int("snk", 0, "blink_parity", &snk_blink_parity); + state_save_register_func_postload(snk_postload); dirtybuffer = auto_malloc( MAX_VRAM_SIZE ); if(!dirtybuffer) return 1;