ST-V and Model 2: save states, run-ahead and netplay; SCSP slot allocation (#2202)

Save states on ST-V were broken on master: a loaded state, a run-ahead
rewind or a netplay resync did not continue the emulation the same way.
Model 2 was almost right (hpyagu98 differed after a rewind).

Core:
- SH-2: each CPU registers its own state (both used to register the
  boot buffer), and the rest of the CPU is saved: delay slot, divider,
  interrupt queue and lines, on-chip registers, FRT, DMA timers.
- Memory banks are registered after the driver init, where the drivers
  point them (ST-V work RAM was saved from the wrong buffer).
- cpu_spinuntil_time / cpu_yielduntil_time trigger counters are saved.
- The crosshair idle timer is saved (the crosshair hid at another frame
  after a load or a rewind).

ST-V:
- SMPC, SCU registers and interrupts, SCU DSP, I/O, sound CPU control,
  CD block scalars, VDP1 clipping, CRTC line counter are saved.
- The RTC starts from the host date and time at boot and then follows the
  emulated time (it read the host clock at every query, so two runs from
  the same state could read different times).

SH-2 recompiler: the polling loop skip counted a whole chain of blocks
that came back to the loop start as one loop iteration, skipping the
writes and calls in between. It now only skips when the block ran one
turn of the loop. Results with a warm or a cold block cache are now the
same.

Model 2: the polygon count of the last display list is saved.

SCSP (issue #2194, regression of #2198): a key off on a slot that had
already stopped (a one-shot sample past its end) left its envelope at the
level it stopped at, because a stopped slot no longer updates it. The
monitor register (0x408) then showed the slot as still sounding, the
sound drivers took it for busy and, after a while, put every new note on
the one slot left. Cotton Boomerang lost all but the drums at the first
boss, Winter Heat's voices cut each other from the first attract demo on.
Such a slot now ends its release at once.


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

Co-authored-by: Claude Opus 5.5 <[email protected]>
This commit is contained in:
zanacandClaude Opus 5.5 authored and GitHub committed 2026-11-03 12:04:40 -05:00
1 parent 7ed1af5370
commit ab52620ee4
18 files changed
+359 -37

No files matched your search

+25 -1
View File
@@ -3154,7 +3154,7 @@ void sh2_init(void)
int cpu = cpu_getactivecpu();
/* this CPU's buffer from now on (the core allocated it before the init) */
sh2p = (SH2 *)cpunum_get_context_ptr(cpu);
sh2p = (SH2 *)cpunum_get_context_buffer(cpu);
if (!sh2p)
sh2p = &sh2_boot;
memset(sh2p, 0, sizeof(SH2));
@@ -3199,5 +3199,29 @@ void sh2_init(void)
state_save_register_UINT32("sh2", cpu, "R13", &sh2.r[13], 1);
state_save_register_UINT32("sh2", cpu, "R14", &sh2.r[14], 1);
state_save_register_UINT32("sh2", cpu, "EA", &sh2.ea, 1);
/* the rest of the CPU: the delay slot, the divider, the interrupts and
the on-chip modules (their registers, the free running timer, the DMA) */
state_save_register_UINT32("sh2", cpu, "PPC", &sh2.ppc, 1);
state_save_register_UINT32("sh2", cpu, "delay", &sh2.delay, 1);
state_save_register_UINT32("sh2", cpu, "cpu_off", &sh2.cpu_off, 1);
state_save_register_UINT32("sh2", cpu, "dvsr", &sh2.dvsr, 1);
state_save_register_UINT32("sh2", cpu, "dvdnth", &sh2.dvdnth, 1);
state_save_register_UINT32("sh2", cpu, "dvdntl", &sh2.dvdntl, 1);
state_save_register_UINT32("sh2", cpu, "dvcr", &sh2.dvcr, 1);
state_save_register_UINT32("sh2", cpu, "pending_irq", &sh2.pending_irq, 1);
state_save_register_UINT32("sh2", cpu, "test_irq", &sh2.test_irq, 1);
state_save_register_INT32("sh2", cpu, "irq_queue", (INT32 *)sh2.irq_queue, 16 * 2);
state_save_register_INT8("sh2", cpu, "irq_line_state", sh2.irq_line_state, 16);
state_save_register_INT8("sh2", cpu, "nmi_line_state", &sh2.nmi_line_state, 1);
state_save_register_UINT32("sh2", cpu, "m", sh2.m, 0x200 / 4);
state_save_register_UINT16("sh2", cpu, "frc", &sh2.frc, 1);
state_save_register_UINT16("sh2", cpu, "ocra", &sh2.ocra, 1);
state_save_register_UINT16("sh2", cpu, "ocrb", &sh2.ocrb, 1);
state_save_register_UINT16("sh2", cpu, "icr", &sh2.icr, 1);
state_save_register_UINT32("sh2", cpu, "frc_base", &sh2.frc_base, 1);
state_save_register_int("sh2", cpu, "frt_input", &sh2.frt_input);
state_save_register_int("sh2", cpu, "internal_irq_level", &sh2.internal_irq_level);
state_save_register_int("sh2", cpu, "internal_irq_vector", &sh2.internal_irq_vector);
state_save_register_INT32("sh2", cpu, "dma_timer_active", (INT32 *)sh2.dma_timer_active, 2);
return;
}
+10 -3
View File
@@ -85,6 +85,7 @@ struct jit_block
UINT8 len; /* instructions */
UINT8 idle; /* the SGL idle counting loop starts here (see jit_idle_skip) */
UINT8 spin; /* a polling loop that only reads: see jit_spin_skip */
UINT16 spincost; /* cycles of one iteration of that loop */
UINT16 op0; /* first opcode, to retry an untranslatable block once it changes */
};
@@ -1942,12 +1943,13 @@ static int jit_spin_block(const UINT16 *ops, UINT32 count, UINT32 pc)
{
UINT32 i;
/* the loop: from the start to a BT or BF back to it; the rest of the
block only runs once the loop is left */
block only runs once the loop is left. Returns the instructions of
the loop, 0 if the block does not start with one */
for (i = 0; i < count && i < JIT_SPIN_MAX; i++)
{
UINT16 op = ops[i];
if ((op & 0xfd00) == 0x8900) /* BT or BF, no delay slot */
return i > 0 && pc + i * 2 + 4 + (INT8)(op & 0xff) * 2 == pc;
return (i > 0 && pc + i * 2 + 4 + (INT8)(op & 0xff) * 2 == pc) ? (int)i + 1 : 0;
if (!jit_spin_op(op))
return 0;
}
@@ -1971,7 +1973,9 @@ static void jit_spin_save(void)
static void jit_spin_skip(const struct jit_block *block, int before)
{
int cost = before - sh2_icount, k;
if (cost <= 0 || jit_spin_io || sh2.pc != block->pc || sh2.delay || sh2.test_irq
/* only one turn of the loop itself: a block chained after it may come
back to the start with the same registers, having done anything */
if (cost != block->spincost || jit_spin_io || sh2.pc != block->pc || sh2.delay || sh2.test_irq
|| memcmp(jit_spin_regs.r, sh2.r, sizeof(jit_spin_regs.r)) || jit_spin_regs.sr != sh2.sr
|| jit_spin_regs.gbr != sh2.gbr || jit_spin_regs.mach != sh2.mach || jit_spin_regs.macl != sh2.macl
|| jit_spin_regs.pr != sh2.pr)
@@ -2000,6 +2004,7 @@ static void jit_compile(struct jit_block *block, UINT32 pc)
block->entry = NULL;
block->idle = 0;
block->spin = 0;
block->spincost = 0;
block->op0 = jit_fetch(pc);
/* code must sit in one fast RAM region, below the special areas */
@@ -2049,6 +2054,8 @@ static void jit_compile(struct jit_block *block, UINT32 pc)
ctx.pre[i + 1] = ctx.pre[i] + dec[i].acct;
cost += dec[i].cost;
}
/* the loop branch taken: 3 cycles */
block->spincost = block->spin ? ctx.pre[block->spin - 1] + 3 : 0;
C = sljit_create_compiler(NULL);
if (!C)
+12 -10
View File
@@ -189,6 +189,9 @@ static UINT32 saved_cycles[MAX_CPU * 2];
#define EXACT_PER_CPU 7
static INT32 saved_exact[MAX_CPU * EXACT_PER_CPU];
static INT32 saved_exact_globals[3];
/* the triggers of cpu_spinuntil_time() and cpu_yielduntil_time(): numbered in
turn, the number is in the state (the CPU waits on it, a timer pulls it) */
static INT32 spin_timetrig, yield_timetrig;
static int vblank;
static int current_frame;
static int vblank_countdown;
@@ -363,6 +366,9 @@ int cpu_init(void)
state_save_register_UINT32("cpu", 0, "totalcycles", saved_cycles, cpu_gettotalcpu() * 2);
state_save_register_INT32("cpu", 0, "exact", saved_exact, cpu_gettotalcpu() * EXACT_PER_CPU);
state_save_register_INT32("cpu", 0, "exact globals", saved_exact_globals, 3);
spin_timetrig = yield_timetrig = 0;
state_save_register_INT32("cpu", 0, "spin timetrig", &spin_timetrig, 1);
state_save_register_INT32("cpu", 0, "yield timetrig", &yield_timetrig, 1);
state_save_register_func_presave(cpu_suspend_presave);
state_save_register_func_postload(cpu_suspend_postload);
@@ -1470,21 +1476,17 @@ void cpu_yield(void)
void cpu_spinuntil_time(double duration)
{
static int timetrig = 0;
cpu_spinuntil_trigger(TRIGGER_SUSPENDTIME + timetrig);
cpu_triggertime(duration, TRIGGER_SUSPENDTIME + timetrig);
timetrig = (timetrig + 1) & 255;
cpu_spinuntil_trigger(TRIGGER_SUSPENDTIME + spin_timetrig);
cpu_triggertime(duration, TRIGGER_SUSPENDTIME + spin_timetrig);
spin_timetrig = (spin_timetrig + 1) & 255;
}
void cpu_yielduntil_time(double duration)
{
static int timetrig = 0;
cpu_yielduntil_trigger(TRIGGER_YIELDTIME + timetrig);
cpu_triggertime(duration, TRIGGER_YIELDTIME + timetrig);
timetrig = (timetrig + 1) & 255;
cpu_yielduntil_trigger(TRIGGER_YIELDTIME + yield_timetrig);
cpu_triggertime(duration, TRIGGER_YIELDTIME + yield_timetrig);
yield_timetrig = (yield_timetrig + 1) & 255;
}
+9
View File
@@ -1286,6 +1286,15 @@ void *cpunum_get_context_ptr(int cpunum)
return (cpu_active_context[cpu[cpunum].family] == cpunum) ? NULL : cpu[cpunum].context;
}
/* the context buffer of a CPU, also from its init function (when the CPU
does not count in totalcpu yet), whether it is active or not */
void *cpunum_get_context_buffer(int cpunum)
{
if (cpunum < 0 || cpunum >= MAX_CPU)
return NULL;
return cpu[cpunum].context;
}
/*--------------------------
Get/set cycle table
+1
View File
@@ -636,6 +636,7 @@ void cpunum_write_bytes(int cpunum, offs_t address, const UINT8 *data, UINT32 co
/* return a pointer to the saved context of a given CPU, or NULL if the
context is active (and contained within the CPU core */
void *cpunum_get_context_ptr(int cpunum);
void *cpunum_get_context_buffer(int cpunum);
/* return a pointer to the active cycle count table for a given CPU */
const void *cpunum_get_cycle_table(int cpunum, int which);
+11 -1
View File
@@ -1,6 +1,7 @@
#ifndef DECLARE
#include "driver.h"
#include "state.h"
#ifdef MSB_FIRST
@@ -3516,11 +3517,20 @@ static INLINE void plotclip(struct mame_bitmap *bitmap,int x,int y,int pen,const
plot_pixel(bitmap,x,y,pen);
}
/* where each crosshair was and since which frame: in the save states, so a
crosshair hides at the same frame after a load or a run-ahead rewind */
static INT32 inactive_xy [MAX_PLAYER_COUNT][3];
void crosshair_state_save_register(void)
{
memset(inactive_xy, 0, sizeof(inactive_xy));
state_save_register_INT32("crosshair", 0, "inactive", &inactive_xy[0][0], MAX_PLAYER_COUNT * 3);
}
void draw_crosshair(int player_number, struct mame_bitmap *bitmap,int x,int y,const struct rectangle *clip)
{
unsigned long color,black,white;
int i;
static int inactive_xy [MAX_PLAYER_COUNT][3];
if (!options.crosshair_enable)
{
+1
View File
@@ -272,6 +272,7 @@ void mdrawgfxzoom( struct mame_bitmap *dest_bmp,const struct GfxElement *gfx,
void drawgfx_toggle_crosshair(void);
void draw_crosshair(int player_number, struct mame_bitmap *bitmap,int x,int y,const struct rectangle *clip);
void crosshair_state_save_register(void);
static INLINE void sect_rect(struct rectangle *dst, const struct rectangle *src)
{
+97 -21
View File
@@ -156,6 +156,7 @@ int minit_boost,sinit_boost;
static int scanline;
static int scsp_last_line = 0; /* the SCSP interrupt level last raised on the 68000 */
/**************************************************************************************/
@@ -1863,23 +1864,70 @@ static void smpc_intback_continue(int param)
smpc_ram[0x63] = 0x00;
}
static void smpc_intback_resolve(int param)
/*
The RTC: the local date and time of the host when the machine started,
moved on by the emulated time (not the host clock: a save state, the
run-ahead and the netplay peers must read the same time)
*/
static INT32 smpc_rtc_days; /* days from 1970-01-01 */
static INT32 smpc_rtc_secs; /* seconds of that day */
static INT32 smpc_days_from_civil(int y, int m, int d)
{
int era, yoe, doy, doe;
y -= m <= 2;
era = (y >= 0 ? y : y - 399) / 400;
yoe = y - era * 400;
doy = (153 * (m + (m > 2 ? -3 : 9)) + 2) / 5 + d - 1;
doe = yoe * 365 + yoe / 4 - yoe / 100 + doy;
return era * 146097 + doe - 719468;
}
static void smpc_rtc_start(void)
{
time_t ltime;
struct tm *today;
time(&ltime);
today = localtime(&ltime);
smpc_rtc_days = smpc_days_from_civil(today->tm_year + 1900, today->tm_mon + 1, today->tm_mday);
smpc_rtc_secs = today->tm_hour * 3600 + today->tm_min * 60 + today->tm_sec;
}
static void smpc_rtc_fill(void)
{
double now = timer_get_time();
INT32 total = smpc_rtc_secs + (INT32)now;
INT32 days = smpc_rtc_days + total / 86400;
INT32 secs = total % 86400;
int era, doe, yoe, y, doy, mp, d, m;
/* civil from days (H. Hinnant) */
days += 719468;
era = (days >= 0 ? days : days - 146096) / 146097;
doe = days - era * 146097;
yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365;
y = yoe + era * 400;
doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
mp = (5 * doy + 2) / 153;
d = doy - (153 * mp + 2) / 5 + 1;
m = mp + (mp < 10 ? 3 : -9);
y += m <= 2;
smpc_ram[0x23] = DectoBCD(y / 100);
smpc_ram[0x25] = DectoBCD(y % 100);
smpc_ram[0x27] = (((days - 719468 + 4) % 7) << 4) | m; /* 1970-01-01 was a Thursday */
smpc_ram[0x29] = DectoBCD(d);
smpc_ram[0x2b] = DectoBCD(secs / 3600);
smpc_ram[0x2d] = DectoBCD((secs / 60) % 60);
smpc_ram[0x2f] = DectoBCD(secs % 60);
}
static void smpc_intback_resolve(int param)
{
if (smpc_intback_buf[0] != 0)
{
smpc_ram[0x21]=0x80;
smpc_ram[0x23] = DectoBCD((today->tm_year + 1900)/100);
smpc_ram[0x25] = DectoBCD((today->tm_year + 1900)%100);
smpc_ram[0x27] = (today->tm_wday << 4) | (today->tm_mon+1);
smpc_ram[0x29] = DectoBCD(today->tm_mday);
smpc_ram[0x2b] = DectoBCD(today->tm_hour);
smpc_ram[0x2d] = DectoBCD(today->tm_min);
smpc_ram[0x2f] = DectoBCD(today->tm_sec);
smpc_rtc_fill();
smpc_ram[0x31]=0x00;
smpc_ram[0x35]=0x00;
smpc_ram[0x37]=0x00;
@@ -3850,15 +3898,47 @@ static READ32_HANDLER( stv_cart_unmapped_r )
return 0xffffffff;
}
/* the state of the board around the CPUs: SMPC, SCU (its registers, the
interrupts, the DSP), the I/O chip and the sound CPU control */
static void stv_state_save_register(void)
{
state_save_register_UINT8 ("stv", 0, "smpc_ram", smpc_ram, 0x80);
state_save_register_UINT8 ("stv", 0, "smpc_sr", &smpc_sr, 1);
state_save_register_UINT8 ("stv", 0, "smpc_intback_stage", &smpc_intback_stage, 1);
state_save_register_UINT8 ("stv", 0, "smpc_intback_buf", smpc_intback_buf, 3);
state_save_register_UINT8 ("stv", 0, "smpc_pmode", &smpc_pmode, 1);
state_save_register_INT32 ("stv", 0, "smpc_rtc_days", &smpc_rtc_days, 1);
state_save_register_INT32 ("stv", 0, "smpc_rtc_secs", &smpc_rtc_secs, 1);
state_save_register_UINT8 ("stv", 0, "IOSEL1", &IOSEL1, 1);
state_save_register_UINT8 ("stv", 0, "IOSEL2", &IOSEL2, 1);
state_save_register_UINT8 ("stv", 0, "EXLE1", &EXLE1, 1);
state_save_register_UINT8 ("stv", 0, "EXLE2", &EXLE2, 1);
state_save_register_UINT8 ("stv", 0, "PDR1", &PDR1, 1);
state_save_register_UINT8 ("stv", 0, "PDR2", &PDR2, 1);
state_save_register_UINT32("stv", 0, "scu", stv_scu, 0x100 / 4);
state_save_register_UINT32("stv", 0, "scu_ist", &scu_ist, 1);
state_save_register_int ("stv", 0, "scu_irq_level", &scu_irq_level);
state_save_register_int ("stv", 0, "scu_irq_vector", &scu_irq_vector);
state_save_register_int ("stv", 0, "scu_timer0_count", &scu_timer0_count);
state_save_register_int ("stv", 0, "slave_irl", &slave_irl);
state_save_register_UINT8 ("stv", 0, "port_sel", &port_sel, 1);
state_save_register_UINT8 ("stv", 0, "mux_data", &mux_data, 1);
state_save_register_UINT8 ("stv", 0, "ioga_mode", &ioga_mode, 1);
state_save_register_UINT8 ("stv", 0, "ioga_portg", &ioga_portg, 1);
state_save_register_UINT16("stv", 0, "ioga_count", ioga_count, 2);
state_save_register_int ("stv", 0, "hopper_power", &hopper_power);
state_save_register_int ("stv", 0, "hopper_status", &hopper_status);
state_save_register_UINT8 ("stv", 0, "SCSP_reset", &SCSP_reset, 1);
state_save_register_UINT8 ("stv", 0, "en_68k", &en_68k, 1);
state_save_register_int ("stv", 0, "scsp_last_line", &scsp_last_line);
scudsp_state_save_register();
stvcd_state_save_register();
}
DRIVER_INIT ( stv )
{
unsigned char *ROM = memory_region(REGION_USER1);
time_t ltime;
struct tm *today;
time(&ltime);
today = localtime(&ltime);
cpu_setbank(1,&ROM[0x000000]);
stv_hopper = 0;
stv_critcrsh = 0;
@@ -3880,6 +3960,8 @@ DRIVER_INIT ( stv )
cpu_setbank(4,&stv_workram_l[0x000000]);
cpu_setbank(5,&stv_backupram[0x000000]);
stv_state_save_register();
/* the SH-2 recompiler runs code from, and accesses directly, the BIOS */
/* and the work RAMs (the handlers a game installs there stay in use) */
sh2_jit_set(0);
@@ -3915,13 +3997,8 @@ DRIVER_INIT ( stv )
install_mem_write32_handler(1, 0x60ffc44, 0x60ffc47, w60ffc44_write );
install_mem_write32_handler(1, 0x60ffc48, 0x60ffc4b, w60ffc48_write );
smpc_ram[0x23] = DectoBCD((today->tm_year + 1900)/100);
smpc_ram[0x25] = DectoBCD((today->tm_year + 1900)%100);
smpc_ram[0x27] = (today->tm_wday << 4) | (today->tm_mon+1);
smpc_ram[0x29] = DectoBCD(today->tm_mday);
smpc_ram[0x2b] = DectoBCD(today->tm_hour);
smpc_ram[0x2d] = DectoBCD(today->tm_min);
smpc_ram[0x2f] = DectoBCD(today->tm_sec);
smpc_rtc_start();
smpc_rtc_fill();
smpc_ram[0x31] = 0x00; /*CTG1=0 CTG0=0 (correct??)*/
/* smpc_ram[0x33] = readinputport(7);*/
smpc_ram[0x5f] = 0x10;
@@ -4009,7 +4086,6 @@ static data16_t stv_opcode_read(offs_t pc)
struct sh2_config sh2_conf_master = { 0, stv_opcode_read };
struct sh2_config sh2_conf_slave = { 1, stv_opcode_read };
static int scsp_last_line = 0;
/* the SCSP raises and drops each level on its own (a negative level drops it);
0 drops the last raised level, as in current MAME */
+50
View File
@@ -1,4 +1,5 @@
#include "driver.h"
#include "state.h"
#include "machine/scudsp.h"
#include "cpu/sh2/sh2.h"
@@ -270,6 +271,55 @@ static UINT32 dsp_get_mem_source_dma( UINT32 memcode, UINT32 counter )
return 0;
}
/* the save state: the 48-bit MUL and ALU go as two 32-bit halves */
static UINT32 dsp_ss_mul[2], dsp_ss_alu[2];
static void scudsp_presave(void)
{
dsp_ss_mul[0] = (UINT32)(dsp_reg.mul >> 32);
dsp_ss_mul[1] = (UINT32)dsp_reg.mul;
dsp_ss_alu[0] = (UINT32)(dsp_reg.alu >> 32);
dsp_ss_alu[1] = (UINT32)dsp_reg.alu;
}
static void scudsp_postload(void)
{
dsp_reg.mul = ((UINT64)dsp_ss_mul[0] << 32) | dsp_ss_mul[1];
dsp_reg.alu = ((UINT64)dsp_ss_alu[0] << 32) | dsp_ss_alu[1];
}
void scudsp_state_save_register(void)
{
state_save_register_UINT8 ("scudsp", 0, "pc", &dsp_reg.pc, 1);
state_save_register_UINT8 ("scudsp", 0, "delay", &dsp_reg.delay, 1);
state_save_register_UINT8 ("scudsp", 0, "top", &dsp_reg.top, 1);
state_save_register_UINT16("scudsp", 0, "lop", &dsp_reg.lop, 1);
state_save_register_UINT8 ("scudsp", 0, "ct0", &dsp_reg.ct0, 1);
state_save_register_UINT8 ("scudsp", 0, "ct1", &dsp_reg.ct1, 1);
state_save_register_UINT8 ("scudsp", 0, "ct2", &dsp_reg.ct2, 1);
state_save_register_UINT8 ("scudsp", 0, "ct3", &dsp_reg.ct3, 1);
state_save_register_UINT32("scudsp", 0, "md0", dsp_reg.md0, 0x40);
state_save_register_UINT32("scudsp", 0, "md1", dsp_reg.md1, 0x40);
state_save_register_UINT32("scudsp", 0, "md2", dsp_reg.md2, 0x40);
state_save_register_UINT32("scudsp", 0, "md3", dsp_reg.md3, 0x40);
state_save_register_UINT8 ("scudsp", 0, "ra", &dsp_reg.ra, 1);
state_save_register_UINT32("scudsp", 0, "rx", &dsp_reg.rx, 1);
state_save_register_UINT32("scudsp", 0, "mul", dsp_ss_mul, 2);
state_save_register_UINT32("scudsp", 0, "ry", &dsp_reg.ry, 1);
state_save_register_UINT32("scudsp", 0, "alu", dsp_ss_alu, 2);
state_save_register_UINT16("scudsp", 0, "ph", &dsp_reg.ph, 1);
state_save_register_UINT32("scudsp", 0, "pl", &dsp_reg.pl, 1);
state_save_register_UINT16("scudsp", 0, "ach", &dsp_reg.ach, 1);
state_save_register_UINT32("scudsp", 0, "acl", &dsp_reg.acl, 1);
state_save_register_UINT32("scudsp", 0, "ra0", &dsp_reg.ra0, 1);
state_save_register_UINT32("scudsp", 0, "wa0", &dsp_reg.wa0, 1);
state_save_register_UINT32("scudsp", 0, "internal_prg", dsp_reg.internal_prg, 0x100);
state_save_register_UINT32("scudsp", 0, "opcode", &opcode, 1);
state_save_register_UINT8 ("scudsp", 0, "update_mul", &update_mul, 1);
state_save_register_func_presave(scudsp_presave);
state_save_register_func_postload(scudsp_postload);
}
void dsp_prg_ctrl(UINT32 data)
{
if(LEF) dsp_reg.pc = (data & 0xff);
+1
View File
@@ -6,6 +6,7 @@ extern void dsp_ram_addr_ctrl(UINT32 data);
extern void dsp_ram_addr_w(UINT32 data);
extern UINT32 dsp_ram_addr_r(void);
extern void dsp_execute_program(void);
extern void scudsp_state_save_register(void);
/* raise an SCU interrupt source (bit of the status register), drivers/stv.c */
extern void stv_scu_raise_irq(int bit);
+94
View File
@@ -12,6 +12,7 @@
***************************************************************************/
#include "driver.h"
#include "state.h"
#include "machine/stvcd.h"
#include <stdio.h>
@@ -1065,3 +1066,96 @@ void CD_com_update(UINT32 count){
}
log_cb(RETRO_LOG_DEBUG, LOGPRE "CD block update\n");
}
/*
The save state of the CD block: its registers and the state of the
commands. ST-V has no CD drive (Sports Fishing 2 needs one and isn't
dumped completely): the games only reach the registers, so the sector
buffer and the disc are left out. The info transfer pointer goes as
the buffer it points into.
*/
static INT32 cd_ss_info_buf;
static void stvcd_presave(void)
{
if (CD_info_ptr == CD_sat_toc) cd_ss_info_buf = 1;
else if (CD_info_ptr == CD_sat_subq) cd_ss_info_buf = 2;
else if (CD_info_ptr == CD_sat_subrw) cd_ss_info_buf = 3;
else if (CD_info_ptr == cdb_sat_file_info) cd_ss_info_buf = 4;
else cd_ss_info_buf = 0;
}
static void stvcd_postload(void)
{
switch (cd_ss_info_buf)
{
case 1: CD_info_ptr = CD_sat_toc; break;
case 2: CD_info_ptr = CD_sat_subq; break;
case 3: CD_info_ptr = CD_sat_subrw; break;
case 4: CD_info_ptr = cdb_sat_file_info; break;
default: CD_info_ptr = NULL; break;
}
}
void stvcd_state_save_register(void)
{
state_save_register_UINT8 ("stvcd", 0, "cr_first", &CD_cr_first, 1);
state_save_register_UINT8 ("stvcd", 0, "cr_writing", &CD_cr_writing, 1);
state_save_register_UINT16("stvcd", 0, "CR1", &CR1, 1);
state_save_register_UINT16("stvcd", 0, "CR2", &CR2, 1);
state_save_register_UINT16("stvcd", 0, "CR3", &CR3, 1);
state_save_register_UINT16("stvcd", 0, "CR4", &CR4, 1);
state_save_register_UINT16("stvcd", 0, "hirq", &CD_hirq, 1);
state_save_register_UINT16("stvcd", 0, "hirq_i", &CD_hirq_i, 1);
state_save_register_UINT16("stvcd", 0, "mask", &CD_mask, 1);
state_save_register_UINT8 ("stvcd", 0, "status", &CD_status, 1);
state_save_register_UINT32("stvcd", 0, "fad", &CD_fad, 1);
state_save_register_UINT8 ("stvcd", 0, "track", &CD_track, 1);
state_save_register_UINT8 ("stvcd", 0, "control", &CD_control, 1);
state_save_register_UINT8 ("stvcd", 0, "index", &CD_index, 1);
state_save_register_UINT32("stvcd", 0, "fn", &fn, 1);
state_save_register_INT32 ("stvcd", 0, "com", &CD_com, 1);
state_save_register_INT32 ("stvcd", 0, "com_play", &CD_com_play, 1);
state_save_register_UINT8 ("stvcd", 0, "stat", &CD_stat, 1);
state_save_register_UINT8 ("stvcd", 0, "flag", &CD_flag, 1);
state_save_register_UINT32("stvcd", 0, "last_part", &CD_last_part, 1);
state_save_register_UINT32("stvcd", 0, "play_fad", &CD_play_fad, 1);
state_save_register_UINT32("stvcd", 0, "play_range", &CD_play_range, 1);
state_save_register_UINT32("stvcd", 0, "seek_target", &CD_seek_target, 1);
state_save_register_UINT8 ("stvcd", 0, "scan_dir", &CD_scan_dir, 1);
state_save_register_UINT32("stvcd", 0, "search_pn", &CD_search_pn, 1);
state_save_register_UINT32("stvcd", 0, "search_sp", &CD_search_sp, 1);
state_save_register_UINT32("stvcd", 0, "search_fad", &CD_search_fad, 1);
state_save_register_UINT32("stvcd", 0, "file_scope_first", &CD_file_scope_first, 1);
state_save_register_UINT32("stvcd", 0, "file_scope_last", &CD_file_scope_last, 1);
state_save_register_UINT32("stvcd", 0, "data_pn", &CD_data_pn, 1);
state_save_register_UINT32("stvcd", 0, "data_sp", &CD_data_sp, 1);
state_save_register_UINT32("stvcd", 0, "data_sn", &CD_data_sn, 1);
state_save_register_UINT32("stvcd", 0, "data_count", &CD_data_count, 1);
state_save_register_UINT32("stvcd", 0, "data_delete", &CD_data_delete, 1);
state_save_register_UINT32("stvcd", 0, "data_size", &CD_data_size, 1);
state_save_register_INT32 ("stvcd", 0, "info_buf", &cd_ss_info_buf, 1);
state_save_register_UINT32("stvcd", 0, "info_count", &CD_info_count, 1);
state_save_register_UINT32("stvcd", 0, "info_size", &CD_info_size, 1);
state_save_register_UINT32("stvcd", 0, "trans_type", &CD_trans_type, 1);
state_save_register_UINT32("stvcd", 0, "actual_size", &CD_actual_size, 1);
state_save_register_UINT32("stvcd", 0, "free_space", &CD_free_space, 1);
state_save_register_UINT8 ("stvcd", 0, "filt_num", &CD_filt_num, 1);
state_save_register_UINT8 ("stvcd", 0, "mpeg_filt_num", &CD_mpeg_filt_num, 1);
state_save_register_UINT32("stvcd", 0, "cur_fad", &CD_cur_fad, 1);
state_save_register_UINT32("stvcd", 0, "cur_track", &CD_cur_track, 1);
state_save_register_UINT32("stvcd", 0, "cur_ctrl", &CD_cur_ctrl, 1);
state_save_register_UINT32("stvcd", 0, "cur_idx", &CD_cur_idx, 1);
state_save_register_UINT32("stvcd", 0, "cur_fid", &CD_cur_fid, 1);
state_save_register_UINT8 ("stvcd", 0, "init_flag", &CD_init_flag, 1);
state_save_register_UINT32("stvcd", 0, "repeat", &CD_repeat, 1);
state_save_register_UINT32("stvcd", 0, "standby", &CD_standby, 1);
state_save_register_UINT32("stvcd", 0, "repeat_max", &CD_repeat_max, 1);
state_save_register_UINT8 ("stvcd", 0, "ecc", &CD_ecc, 1);
state_save_register_UINT32("stvcd", 0, "drive_speed", &CD_drive_speed, 1);
state_save_register_UINT32("stvcd", 0, "get_sect_size", &cdb_get_sect_size, 1);
state_save_register_UINT32("stvcd", 0, "put_sect_size", &cdb_put_sect_size, 1);
state_save_register_func_presave(stvcd_presave);
state_save_register_func_postload(stvcd_postload);
}
+2
View File
@@ -245,3 +245,5 @@ extern UINT32 CD_drive_speed; /* 0 = noop, 1 = 1x, 2 = 2x*/
extern UINT8 cdda_buff[8192]; /* CD-DA buffer for SCSP communication*/
extern UINT32 cdda_pos;
void stvcd_state_save_register(void);
+10 -1
View File
@@ -259,7 +259,8 @@ int memory_init(void)
if (!populate_ports())
return 0;
register_banks();
/* the banks are registered for the save states by memory_register_banks(),
after the driver init: a driver often points them at its own RAM there */
#ifdef MEM_DUMP
/* dump the final memory configuration */
@@ -3136,3 +3137,11 @@ static void verify_masks(void)
log_cb(RETRO_LOG_INFO, LOGPRE "mame-memory: Busted entries that are static = %d\n", static_count);
}
#endif
/* the save state entries of the memory banks, once the driver init has
pointed them where it wants them (see memory_init) */
void memory_register_banks(void)
{
register_banks();
}
+4
View File
@@ -370,6 +370,10 @@ static int init_machine(void)
if (gamedrv->driver_init)
(*gamedrv->driver_init)();
/* the memory banks in the save states, where the driver init left them */
memory_register_banks();
crosshair_state_save_register();
return 0;
cant_init_memory:
+1
View File
@@ -808,6 +808,7 @@ DECLARE_PORT_HANDLERS_32BIT_LE(32)
int memory_init(void);
void memory_shutdown(void);
void memory_set_context(int activecpu);
void memory_register_banks(void);
void memory_set_unmap_value(data32_t value);
/* ----- dynamic bank handlers ----- */
+10
View File
@@ -404,7 +404,17 @@ static void SCSP_StartSlot(struct _SLOT *slot)
static void SCSP_StopSlot(struct _SLOT *slot,int keyoff)
{
if(keyoff)
{
slot->EG.state=RELEASE;
/* a slot that already stopped (a one-shot sample past its end) no longer
updates its envelope: its release is over at once. Left at the level
it stopped at, the monitor (0x408) showed it as still sounding, the
sound drivers took it for busy and, after a while, put every new note
on the one slot left (the Cotton Boomerang boss music lost all but the
drums, the Winter Heat voices cut each other from the first demo on) */
if(!slot->active)
slot->EG.volume=0;
}
else
slot->active=0;
slot->udata.data[0]&=~0x800;
+5
View File
@@ -3093,6 +3093,11 @@ VIDEO_START( model2 )
state_save_register_float( "model2video", 0, "texture_parameters", &m_geo->texture_parameters[ 0 ].diffuse, sizeof( m_geo->texture_parameters ) / ( sizeof( float ) ) ); /* all 32-bit fields */
state_save_register_UINT32( "model2video", 0, "polygon_ram0", m_geo->polygon_ram0, 0x8000 );
state_save_register_UINT32( "model2video", 0, "polygon_ram1", m_geo->polygon_ram1, 0x8000 );
/* the polygons of the last display list: their count is read by the games
(model2_polygon_count_r); the list itself has been drawn when the frame
ends, the next display list starts a new one */
state_save_register_UINT32( "model2video", 0, "poly_list_index", &m_raster->poly_list_index, 1 );
state_save_register_int( "model2video", 0, "render_done", &m_render_done );
return 0;
}
+16
View File
@@ -2909,6 +2909,16 @@ static int vdp1_start(void)
state_save_register_UINT16("stv_vdp1", 0, "lopr", &vdp1.lopr, 1);
state_save_register_UINT16("stv_vdp1", 0, "copr", &vdp1.copr, 1);
state_save_register_UINT16("stv_vdp1", 0, "ewdr", &vdp1.ewdr, 1);
/* the clipping set by the command lists: a list without clipping
commands keeps the last one */
state_save_register_int("stv_vdp1", 0, "system_clip_min_x", &vdp1.system_cliprect.min_x);
state_save_register_int("stv_vdp1", 0, "system_clip_max_x", &vdp1.system_cliprect.max_x);
state_save_register_int("stv_vdp1", 0, "system_clip_min_y", &vdp1.system_cliprect.min_y);
state_save_register_int("stv_vdp1", 0, "system_clip_max_y", &vdp1.system_cliprect.max_y);
state_save_register_int("stv_vdp1", 0, "user_clip_min_x", &vdp1.user_cliprect.min_x);
state_save_register_int("stv_vdp1", 0, "user_clip_max_x", &vdp1.user_cliprect.max_x);
state_save_register_int("stv_vdp1", 0, "user_clip_min_y", &vdp1.user_cliprect.min_y);
state_save_register_int("stv_vdp1", 0, "user_clip_max_y", &vdp1.user_cliprect.max_y);
state_save_register_func_postload(vdp1_state_save_postload);
return 0;
}
@@ -10695,7 +10705,10 @@ static void crtc_regs_w(int offset, UINT16 data, UINT16 mem_mask)
static void crtc_state_save_postload(void)
{
/* the resolution again, not a resolution change: the field is the saved one */
int odd_bit = crtc.odd_bit;
crtc_reconfigure();
crtc.odd_bit = odd_bit;
}
static void crtc_start(void)
@@ -10727,6 +10740,9 @@ static void crtc_start(void)
state_save_register_UINT16("stv_crtc", 0, "hcnt", &crtc.hcounter_latch, 1);
state_save_register_UINT16("stv_crtc", 0, "vcnt", &crtc.vcounter_latch, 1);
state_save_register_int("stv_crtc", 0, "vramsz", &crtc.vramsz);
/* the current line and when it started: the H and V counters */
state_save_register_int("stv_crtc", 0, "line", &crtc_line);
state_save_register_double("stv_crtc", 0, "line_time", &crtc_line_time, 1);
state_save_register_func_postload(crtc_state_save_postload);
}