mirror of
https://github.com/libretro/fuse-libretro.git
synced 2026-11-07 10:36:03 -10:00
Let the core options, not the snapshot, decide which peripherals are fitted
Loading snapshot content or restoring a savestate took the optional
peripheral set out of the file and applied it over the top of the core
options. Three ways that goes wrong, all reproduced:
- an SZX carrying the USPE chunk turns the Currah uSpeech on with
fuse_uspeech disabled, and speaks
- a .z80, which has nowhere to record uSpeech at all, turns it off with
fuse_uspeech enabled, so a user who has both ROMs in place and the
option on gets silence the moment they load a game
- with fuse_fuller_box or fuse_melodik enabled, loading any snapshot
hard resets the machine and discards the snapshot that was just
loaded, landing the user back at the BASIC boot screen
The last one is the reason this cannot be fixed where it was being
attempted. snapshot_copy_from() runs periph_disable_optional(), then
module_snapshot_enabled() to read the flags out of the file, then
machine_reset(0), which is what turns settings_current into an actual
peripheral table, and only then module_snapshot_from() to restore the
registers and RAM. sync_periph_from_ports_and_options() was correcting
the settings after all of that had finished, and arming
periph_needs_update so retro_run() would pick the change up. But
periph_posthook() calls machine_reset(1) whenever a peripheral declaring
hard_reset changed, and the Fuller Box, Melodik and uSpeech all declare
it - so the correction was itself destroying the state.
Override the flags in the one place where it costs nothing instead:
between module_snapshot_enabled() and the reset. The peripheral table is
then built correctly the first time, the register and RAM restore that
follows is untouched, and no second reset is needed. Assignment only, so
deliberately no periph_needs_update.
sync_periph_from_ports_and_options() stays as the path for options and
port wiring changed while already running; the port/option combining
rule the two now share is factored into resolve_port_peripherals() so
they cannot drift apart.
Verified against the built core with a headless libretro frontend, using
an SZX generated through libspectrum carrying the USPE chunk and a .z80
without it, both holding a Z80 program that pages uSpeech in and writes
allophones, so "is the peripheral actually fitted" is answerable from
the audio:
SZX with chunk, uspeech off before: RMS 411.6 after: silent
z80 no chunk, uspeech on before: silent after: RMS 411.3
SZX with chunk, uspeech on before: RMS 411.6 after: RMS 411.6
SZX, fuller_box on before: silent, reset to BASIC
after: RMS 411.6, snapshot intact
SZX, melodik on before: silent, reset to BASIC
after: RMS 411.6, snapshot intact
No-content boot framebuffers are unchanged with the option both on and
off, the twelve machines the core can start without external ROMs are
unchanged, and a savestate round trip still comes back with uSpeech
fitted. ASan and UBSan clean over content load, savestate restore and
both option states.
Note for anyone editing fuse/snapshot.c: src/fuse/snapshot.c includes it
as a source file, and this build system has no header dependency
tracking, so make does not rebuild the object when only the included
file changes. Touch src/fuse/snapshot.c, or the build silently keeps the
old code - the same trap settings.h has.
This commit is contained in:
1 parent
010d112156
commit
ad7e52fb53
3 files changed
+63
-18
No files matched your search
@@ -87,6 +87,18 @@ snapshot_copy_from( libspectrum_snap *snap )
|
||||
periph_disable_optional();
|
||||
module_snapshot_enabled( snap );
|
||||
|
||||
#ifdef __LIBRETRO__
|
||||
/* Which optional peripherals are plugged in is frontend configuration
|
||||
here, not emulated machine state, so the core options outrank whatever
|
||||
module_snapshot_enabled() just read out of the file. Do it now rather
|
||||
than after the load: machine_reset() below builds the peripheral table
|
||||
from settings_current, and module_snapshot_from() restores the
|
||||
registers and RAM afterwards. Correcting it later instead would mean
|
||||
a second reset, which for a peripheral declaring hard_reset throws
|
||||
away the snapshot that was just loaded. */
|
||||
libretro_snapshot_periph_override();
|
||||
#endif
|
||||
|
||||
machine = libspectrum_snap_machine( snap );
|
||||
|
||||
settings_current.late_timings = libspectrum_snap_late_timings( snap );
|
||||
|
||||
@@ -36,6 +36,7 @@ int snapshot_copy_from( libspectrum_snap *snap );
|
||||
|
||||
#ifdef __LIBRETRO__
|
||||
int snapshot_update( void );
|
||||
void libretro_snapshot_periph_override( void );
|
||||
#endif
|
||||
int snapshot_write( const char *filename );
|
||||
int snapshot_copy_to( libspectrum_snap *snap );
|
||||
|
||||
+50
-18
@@ -26,6 +26,7 @@
|
||||
#include <pokefinder/pokemem.h>
|
||||
#include <periph.h>
|
||||
#include <sound.h>
|
||||
#include <snapshot.h>
|
||||
#include <timer/timer.h>
|
||||
|
||||
#include "ui/uimedia.h"
|
||||
@@ -2529,37 +2530,68 @@ void retro_deinit(void)
|
||||
}
|
||||
}
|
||||
|
||||
// Re-derive every Fuse peripheral setting whose authoritative value lives
|
||||
// outside the emulated machine: live frontend port wiring, and core options.
|
||||
//
|
||||
// Kempston Mouse is a single peripheral, not per-port; enable it in Fuse
|
||||
// whenever any port is currently configured as a mouse, disable it
|
||||
// otherwise. The Fuller Box is the same shape of problem in reverse - its
|
||||
// joystick port (0x7f) and its AY (0x3f/0x5f) are one periph_t, so wiring a
|
||||
// Fuller joystick to a port has to switch the whole box on regardless of
|
||||
// the fuse_fuller_box option.
|
||||
// the fuse_fuller_box option. Both callers below have to agree on how the
|
||||
// option and the port wiring combine, hence the shared helper.
|
||||
static void resolve_port_peripherals(int *kempston_mouse, int *fuller)
|
||||
{
|
||||
unsigned p;
|
||||
|
||||
*kempston_mouse = 0;
|
||||
*fuller = opt_fuller;
|
||||
|
||||
for (p = 0; p < MAX_PADS; p++)
|
||||
{
|
||||
if (input_devices[p] == RETRO_DEVICE_KEMPSTON_MOUSE)
|
||||
*kempston_mouse = 1;
|
||||
else if (input_devices[p] == RETRO_DEVICE_FULLER_JOYSTICK)
|
||||
*fuller = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Called from snapshot_copy_from(), after module_snapshot_enabled() has
|
||||
// copied the peripheral flags out of the file and before machine_reset()
|
||||
// turns settings_current into an actual peripheral table. Assigns only; the
|
||||
// reset that follows is what applies these, so there is deliberately no
|
||||
// periph_needs_update here.
|
||||
void libretro_snapshot_periph_override(void)
|
||||
{
|
||||
int kempston_mouse;
|
||||
int fuller;
|
||||
|
||||
resolve_port_peripherals(&kempston_mouse, &fuller);
|
||||
|
||||
settings_current.kempston_mouse = kempston_mouse;
|
||||
settings_current.fuller = fuller;
|
||||
settings_current.melodik = opt_melodik;
|
||||
settings_current.uspeech = opt_uspeech;
|
||||
settings_current.issue2 = opt_issue2;
|
||||
}
|
||||
|
||||
// Re-derive every Fuse peripheral setting whose authoritative value lives
|
||||
// outside the emulated machine: live frontend port wiring, and core options.
|
||||
//
|
||||
// None of this is emulated machine state, so it must be re-applied after a
|
||||
// snapshot/rewind restore too: kempmouse_snapshot_enabled(),
|
||||
// fuller_enabled_snapshot(), melodik_enabled_snapshot(),
|
||||
// uspeech_enabled_snapshot() and ula_from_snapshot() all overwrite the
|
||||
// corresponding settings_current fields with whatever was true at the moment that particular state was
|
||||
// captured (e.g. still off, if rewound back to before the mouse was ever
|
||||
// connected), which can otherwise leave a peripheral disabled even though
|
||||
// the frontend has it wired up right now.
|
||||
// corresponding settings_current fields with whatever was true at the
|
||||
// moment that particular state was captured (e.g. still off, if rewound
|
||||
// back to before the mouse was ever connected), which can otherwise leave a
|
||||
// peripheral disabled even though the frontend has it wired up right now.
|
||||
// libretro_snapshot_periph_override() gets there first for anything loaded
|
||||
// through snapshot_copy_from(); this remains the path for option and port
|
||||
// changes made while already running.
|
||||
static void sync_periph_from_ports_and_options(void)
|
||||
{
|
||||
unsigned p;
|
||||
int kempston_mouse = 0;
|
||||
int fuller = opt_fuller;
|
||||
int kempston_mouse;
|
||||
int fuller;
|
||||
|
||||
for (p = 0; p < MAX_PADS; p++)
|
||||
{
|
||||
if (input_devices[p] == RETRO_DEVICE_KEMPSTON_MOUSE)
|
||||
kempston_mouse = 1;
|
||||
else if (input_devices[p] == RETRO_DEVICE_FULLER_JOYSTICK)
|
||||
fuller = 1;
|
||||
}
|
||||
resolve_port_peripherals(&kempston_mouse, &fuller);
|
||||
|
||||
// Issue 2 only changes what ula_write() latches for the unused keyboard
|
||||
// bits; no port (de)registration is involved, so it never needs a
|
||||
|
||||
Reference in new issue
Block a user