libspectrum: read and write the SZX uSpeech chunk

Groundwork for Currah uSpeech emulation. Fuse gained uSpeech in 1.7.0 and
libspectrum gained the snapshot support for it in 1.6.0; the vendored trees
here are 1.6.0 and 1.5.0 respectively, so neither side has it. Backport the
libspectrum half.

szx.c already had ZXSTBID_USPEECH ("USPE") defined and wired into the chunk
table, but pointed at skip_chunk, so the chunk was parsed and discarded.
Point it at a real reader and add the matching writer, both taken from
libspectrum 1.6.0. The payload is a single byte holding the paged flag;
presence of the chunk is itself the active flag.

The two accessors this needs, uspeech_active and uspeech_paged, are added to
snap_accessors.txt. Getting them into the generated snap_accessors.c meant
fixing that file's own drift first: the TurboSound accessors had been wrapped
in "#ifdef __LIBRETRO__" by hand in the output, which accessor.pl could not
express and would therefore have dropped on the next regeneration. Add an
%ifdef/%endif directive to accessor.pl, mark the TurboSound block with it in
snap_accessors.txt, and regenerate, so the file is reproducible again:

  perl accessor.pl snap_accessors.txt > snap_accessors.c

Nothing sets uspeech_active yet - the peripheral itself does not exist in the
vendored Fuse tree - so no snapshot written by this core changes by a single
byte until it does. That matters here: retro_serialize_size() is sampled once
by frontends to size the rewind ring buffer and never resampled, so when the
peripheral does land, this chunk must be written under a session-constant
condition rather than gated on uspeech_active, the way write_amxm_chunk() and
write_ay2_chunk() already are for exactly this reason.

Verified by round-tripping real SZX data rather than by checking the accessors
against themselves: the chunk id appears in the written buffer, its length
field reads 1, its payload byte matches the paged flag for both values, both
fields survive a read back, an inactive snapshot emits no chunk at all, and an
SZX carrying no USPE chunk still loads with uspeech reported inactive. Clean
under ASan and UBSan with -fno-sanitize-recover=all.
This commit is contained in:
Lib authored and libretroadmin committed 2026-08-05 23:53:07 +02:00
1 parent 60b60fb887
commit 3027d0921c
5 files changed
+108 -9

No files matched your search

+18 -1
View File
@@ -41,8 +41,17 @@ while(<>) {
my $item = { };
# Preprocessor guard around a run of fields, written as
# %ifdef SYMBOL
# ...
# %endif
# in snap_accessors.txt. Emitted verbatim into every generated section,
# so a field set that only exists in some builds stays expressible in the
# input file instead of being hand-patched into the output.
if( /^\s*%(ifdef|endif)\s*(\S*)/ ) {
$item->{guard} = ( $1 eq "ifdef" ) ? "#ifdef $2\n" : "#endif\n";
# Leading C comment
if( /^\s*\/\*/ ) {
} elsif( /^\s*\/\*/ ) {
$item->{section_comment} = $_;
} else {
# Trailing C comment
@@ -140,6 +149,8 @@ CODE
foreach my $item ( @accessors ) {
next if $item->{section_comment};
if( $item->{guard} ) { print $item->{guard}; next; }
if( $item->{indexed} ) {
dump_accessor_indexed( $item->{type}, $item->{name} );
} else {
@@ -151,6 +162,8 @@ sub dump_accessor_declaration ($) {
my( $item ) = @_;
if( $item->{guard} ) { print $item->{guard}; return; }
if( $item->{section_comment} ) {
print "\n ", $item->{section_comment};
return;
@@ -175,6 +188,8 @@ sub dump_accessor_initialisation ($) {
my( $item ) = @_;
if( $item->{guard} ) { print $item->{guard}; return; }
if( $item->{section_comment} ) {
print "\n ", $item->{section_comment};
return;
@@ -206,6 +221,8 @@ sub dump_accessor_free ($) {
my( $item ) = @_;
if( $item->{guard} ) { print $item->{guard}; return; }
if( $item->{section_comment} ) {
$new_section = 1;
return;
+39 -7
View File
@@ -79,9 +79,9 @@ struct libspectrum_snap {
libspectrum_byte out_plus3_memoryport; /* Used for both the +3's and the Scorpion's 0x1ffd port */
libspectrum_byte out_ay_registerport;
libspectrum_byte ay_registers[ 16 ];
#ifdef __LIBRETRO__
/* TurboSound: second AY-3-8912 chip */
/* TurboSound: second AY-3-8912 chip (fuse-libretro only) */
int turbosound_active;
libspectrum_byte out_ay2_registerport;
libspectrum_byte ay2_registers[ 16 ];
@@ -249,6 +249,10 @@ struct libspectrum_snap {
libspectrum_byte* usource_rom[1];
size_t usource_rom_length[1]; /* Length of the ROM */
/* uSpeech emulation */
int uspeech_active;
int uspeech_paged;
/* DISCiPLE emulation */
int disciple_active;
int disciple_paged;
@@ -375,9 +379,9 @@ libspectrum_snap_alloc( void )
libspectrum_snap_set_out_ay_registerport( snap, 0x0e );
for( i = 0; i < 16; i++ )
libspectrum_snap_set_ay_registers( snap, i, 0 );
#ifdef __LIBRETRO__
/* TurboSound: second AY-3-8912 chip */
/* TurboSound: second AY-3-8912 chip (fuse-libretro only) */
libspectrum_snap_set_turbosound_active( snap, 0 );
libspectrum_snap_set_out_ay2_registerport( snap, 0 );
for( i = 0; i < 16; i++ )
@@ -556,6 +560,10 @@ libspectrum_snap_alloc( void )
libspectrum_snap_set_usource_rom( snap, 0, NULL );
libspectrum_snap_set_usource_rom_length( snap, 0, 0 );
/* uSpeech emulation */
libspectrum_snap_set_uspeech_active( snap, 0 );
libspectrum_snap_set_uspeech_paged( snap, 0 );
/* DISCiPLE emulation */
libspectrum_snap_set_disciple_active( snap, 0 );
libspectrum_snap_set_disciple_paged( snap, 0 );
@@ -634,6 +642,8 @@ libspectrum_snap_free( libspectrum_snap *snap )
for( i = 0; i < SNAPSHOT_SLT_PAGES; i++ )
libspectrum_free( libspectrum_snap_slt( snap, i ) );
libspectrum_free( libspectrum_snap_slt_screen( snap ) );
#ifdef __LIBRETRO__
#endif
libspectrum_free( libspectrum_snap_interface1_rom( snap, 0 ) );
@@ -1155,7 +1165,6 @@ libspectrum_snap_set_ay_registers( libspectrum_snap *snap, int idx, libspectrum_
{
snap->ay_registers[idx] = ay_registers;
}
#ifdef __LIBRETRO__
int
@@ -1205,8 +1214,7 @@ libspectrum_snap_set_out_ay2_active_chip( libspectrum_snap *snap, libspectrum_by
{
snap->out_ay2_active_chip = out_ay2_active_chip;
}
#endif /* #ifdef __LIBRETRO__ */
#endif
libspectrum_byte
libspectrum_snap_out_scld_hsr( libspectrum_snap *snap )
@@ -2612,6 +2620,30 @@ libspectrum_snap_set_usource_rom_length( libspectrum_snap *snap, int idx, size_t
snap->usource_rom_length[idx] = usource_rom_length;
}
int
libspectrum_snap_uspeech_active( libspectrum_snap *snap )
{
return snap->uspeech_active;
}
void
libspectrum_snap_set_uspeech_active( libspectrum_snap *snap, int uspeech_active )
{
snap->uspeech_active = uspeech_active;
}
int
libspectrum_snap_uspeech_paged( libspectrum_snap *snap )
{
return snap->uspeech_paged;
}
void
libspectrum_snap_set_uspeech_paged( libspectrum_snap *snap, int uspeech_paged )
{
snap->uspeech_paged = uspeech_paged;
}
int
libspectrum_snap_disciple_active( libspectrum_snap *snap )
{
+6
View File
@@ -80,11 +80,13 @@ libspectrum_byte out_plus3_memoryport 0 0x08 /* Used for both the +3's and the S
libspectrum_byte out_ay_registerport 0 0x0e
libspectrum_byte ay_registers 16
%ifdef __LIBRETRO__
/* TurboSound: second AY-3-8912 chip (fuse-libretro only) */
int turbosound_active
libspectrum_byte out_ay2_registerport
libspectrum_byte ay2_registers 16
libspectrum_byte out_ay2_active_chip
%endif
/* Timex-specific bits */
libspectrum_byte out_scld_hsr
@@ -247,6 +249,10 @@ int usource_custom_rom
libspectrum_byte* usource_rom 1
size_t usource_rom_length 1 /* Length of the ROM */
/* uSpeech emulation */
int uspeech_active
int uspeech_paged
/* DISCiPLE emulation */
int disciple_active
int disciple_paged
+41 -1
View File
@@ -350,6 +350,9 @@ write_mfce_chunk( libspectrum_buffer *buffer, libspectrum_buffer *data,
static void
write_zmmc_chunk( libspectrum_buffer *buffer, libspectrum_buffer *data,
libspectrum_snap *snap );
static void
write_uspe_chunk( libspectrum_buffer *buffer, libspectrum_buffer *data,
libspectrum_snap *snap );
#ifdef HAVE_ZLIB_H
@@ -2523,6 +2526,29 @@ read_zmmc_chunk( libspectrum_snap *snap, libspectrum_word version GCC_UNUSED,
return LIBSPECTRUM_ERROR_NONE;
}
static libspectrum_error
read_uspe_chunk( libspectrum_snap *snap, libspectrum_word version GCC_UNUSED,
const libspectrum_byte **buffer,
const libspectrum_byte *end GCC_UNUSED, size_t data_length,
szx_context *ctx GCC_UNUSED )
{
libspectrum_byte paged;
if( data_length != 1 ) {
libspectrum_print_error( LIBSPECTRUM_ERROR_UNKNOWN,
"%s:read_uspe_chunk: unknown length %lu",
__FILE__, (unsigned long)data_length );
return LIBSPECTRUM_ERROR_UNKNOWN;
}
paged = *(*buffer)++;
libspectrum_snap_set_uspeech_paged( snap, paged );
libspectrum_snap_set_uspeech_active( snap, 1 );
return LIBSPECTRUM_ERROR_NONE;
}
static libspectrum_error
skip_chunk( libspectrum_snap *snap GCC_UNUSED,
libspectrum_word version GCC_UNUSED,
@@ -2584,7 +2610,7 @@ static struct read_chunk_t read_chunks[] = {
{ ZXSTBID_SPECTRANETFLASHPAGE, read_snef_chunk },
{ ZXSTBID_SPECTRANETRAMPAGE, read_sner_chunk },
{ ZXSTBID_TIMEXREGS, read_scld_chunk },
{ ZXSTBID_USPEECH, skip_chunk },
{ ZXSTBID_USPEECH, read_uspe_chunk },
{ ZXSTBID_Z80REGS, read_z80r_chunk },
{ ZXSTBID_ZXATASPRAMPAGE, read_atrp_chunk },
{ ZXSTBID_ZXATASP, read_zxat_chunk },
@@ -3052,6 +3078,10 @@ libspectrum_szx_write( libspectrum_buffer *buffer, int *out_flags,
write_zmmc_chunk( buffer, block_data, snap );
}
if( libspectrum_snap_uspeech_active( snap ) ) {
write_uspe_chunk( buffer, block_data, snap );
}
libspectrum_buffer_free( block_data );
return LIBSPECTRUM_ERROR_NONE;
@@ -4311,6 +4341,16 @@ write_zmmc_chunk( libspectrum_buffer *buffer, libspectrum_buffer *data,
write_chunk( buffer, ZXSTBID_ZXMMC, NULL );
}
static void
write_uspe_chunk( libspectrum_buffer *buffer, libspectrum_buffer *data,
libspectrum_snap *snap )
{
libspectrum_buffer_write_byte( data,
libspectrum_snap_uspeech_paged( snap ) );
write_chunk( buffer, ZXSTBID_USPEECH, data );
}
static void
write_chunk( libspectrum_buffer *buffer, const char *id,
libspectrum_buffer *block_data )
+4
View File
@@ -1110,6 +1110,10 @@ LIBSPECTRUM_API int libspectrum_snap_usource_active( libspectrum_snap *snap );
LIBSPECTRUM_API void libspectrum_snap_set_usource_active( libspectrum_snap *snap, int usource_active );
LIBSPECTRUM_API int libspectrum_snap_usource_paged( libspectrum_snap *snap );
LIBSPECTRUM_API void libspectrum_snap_set_usource_paged( libspectrum_snap *snap, int usource_paged );
LIBSPECTRUM_API int libspectrum_snap_uspeech_active( libspectrum_snap *snap );
LIBSPECTRUM_API void libspectrum_snap_set_uspeech_active( libspectrum_snap *snap, int uspeech_active );
LIBSPECTRUM_API int libspectrum_snap_uspeech_paged( libspectrum_snap *snap );
LIBSPECTRUM_API void libspectrum_snap_set_uspeech_paged( libspectrum_snap *snap, int uspeech_paged );
LIBSPECTRUM_API int libspectrum_snap_usource_custom_rom( libspectrum_snap *snap );
LIBSPECTRUM_API void libspectrum_snap_set_usource_custom_rom( libspectrum_snap *snap, int usource_custom_rom );
LIBSPECTRUM_API libspectrum_byte * libspectrum_snap_usource_rom( libspectrum_snap *snap, int idx );