From ca9fd1d3117f7efc2bdc9a3aa99dbade2ea11560 Mon Sep 17 00:00:00 2001 From: Thomas Sowell Date: Wed, 1 Dec 2021 14:13:30 -0900 Subject: [PATCH] Add COBRA CP/M loader snapshot support --- Makefile.am | 1 + cobra.c | 351 ++++++++++++++++++++++++++++++++++++++++++++++++++ libspectrum.c | 2 + snapshot.c | 8 ++ 4 files changed, 362 insertions(+) create mode 100644 cobra.c diff --git a/Makefile.am b/Makefile.am index 9fb8e77..8312e56 100644 --- a/Makefile.am +++ b/Makefile.am @@ -27,6 +27,7 @@ lib_LTLIBRARIES = libspectrum.la libspectrum_la_SOURCES = bzip2.c \ buffer.c \ + cobra.c \ creator.c \ crypto.c \ csw.c \ diff --git a/cobra.c b/cobra.c new file mode 100644 index 0000000..1d2e424 --- /dev/null +++ b/cobra.c @@ -0,0 +1,351 @@ +#include "config.h" + +#include "internals.h" + +static void +write_loader ( libspectrum_buffer *buffer, libspectrum_snap *snap ); +static void +write_pages ( libspectrum_buffer *buffer, libspectrum_snap *snap ); +static size_t +write_restore ( libspectrum_buffer *buffer, libspectrum_snap *snap ); + +static int +libspectrum_cobra_read_restore ( const libspectrum_byte *buffer, + size_t buffer_length, + libspectrum_snap *snap ); +static int +libspectrum_cobra_read_data ( const libspectrum_byte *buffer, + size_t buffer_length, + libspectrum_snap *snap ); + +static libspectrum_byte restore[] = { + 0x3e, 0xaa, 0xed, 0x4f, 0x3e, 0xaa, 0xed, 0x47, 0x21, 0xaa, 0xff, 0xe5, + 0x21, 0xaa, 0xff, 0xe5, 0xf1, 0x08, 0xf1, 0x08, 0x01, 0xaa, 0xff, 0x11, + 0xaa, 0xff, 0x21, 0xaa, 0xff, 0x31, 0xac, 0xff, 0xdd, 0x21, 0xaa, 0xff, + 0xfd, 0x21, 0xaa, 0xff, 0xd9, 0x01, 0xaa, 0xff, 0x11, 0xaa, 0xff, 0x21, + 0xaa, 0xff, 0xd9, 0xf3, 0xed, 0x5e, 0xc3, 0xaa, 0xff +}; +const size_t restore_len = 57; + +libspectrum_error +libspectrum_cobra_read( libspectrum_snap *snap, + const libspectrum_byte *buffer, size_t buffer_length ) +{ + int error; + + libspectrum_snap_set_machine( snap, LIBSPECTRUM_MACHINE_48 ); + + error = libspectrum_cobra_read_restore( + &buffer[0x200], buffer_length - 0x300, snap); + if( error != LIBSPECTRUM_ERROR_NONE ) return error; + + error = libspectrum_cobra_read_data( + &buffer[0x100], buffer_length - 0x100, snap); + if( error != LIBSPECTRUM_ERROR_NONE ) return error; + + return LIBSPECTRUM_ERROR_NONE; +} + +static int +libspectrum_cobra_read_restore ( const libspectrum_byte *buffer, + size_t buffer_length, + libspectrum_snap *snap ) +{ + if( buffer_length < restore_len ) { + libspectrum_print_error( + LIBSPECTRUM_ERROR_CORRUPT, + "libspectrum_cobra_read_restore: not enough data in buffer" + ); + return LIBSPECTRUM_ERROR_CORRUPT; + } + + libspectrum_snap_set_r ( snap, buffer[0x01] ); + libspectrum_snap_set_i ( snap, buffer[0x05] ); + libspectrum_snap_set_f_ ( snap, buffer[0x09] ); + libspectrum_snap_set_a_ ( snap, buffer[0x0a] ); + libspectrum_snap_set_f ( snap, buffer[0x0d] ); + libspectrum_snap_set_a ( snap, buffer[0x0e] ); + libspectrum_snap_set_bc ( snap, buffer[0x15] + buffer[0x16]*0x100 ); + libspectrum_snap_set_de ( snap, buffer[0x18] + buffer[0x19]*0x100 ); + libspectrum_snap_set_hl ( snap, buffer[0x1b] + buffer[0x1c]*0x100 ); + libspectrum_snap_set_sp ( snap, buffer[0x1e] + buffer[0x1f]*0x100 - 2 ); + libspectrum_snap_set_ix ( snap, buffer[0x22] + buffer[0x23]*0x100 ); + libspectrum_snap_set_iy ( snap, buffer[0x26] + buffer[0x27]*0x100 ); + libspectrum_snap_set_bc_( snap, buffer[0x2a] + buffer[0x2b]*0x100 ); + libspectrum_snap_set_de_( snap, buffer[0x2d] + buffer[0x2e]*0x100 ); + libspectrum_snap_set_hl_( snap, buffer[0x30] + buffer[0x31]*0x100 ); + + if( buffer[0x33] == 0xfb ) { + libspectrum_snap_set_iff1( snap, 1 ); + libspectrum_snap_set_iff2( snap, 1 ); + } else if( buffer[0x33] == 0xf3 ) { + libspectrum_snap_set_iff1( snap, 0 ); + libspectrum_snap_set_iff2( snap, 0 ); + } else { + libspectrum_print_error( + LIBSPECTRUM_ERROR_CORRUPT, + "libspectrum_cobra_read_buffer: invalid ei/di opcode" + ); + return LIBSPECTRUM_ERROR_CORRUPT; + } + + if( buffer[0x35] == 0x46 ) { + libspectrum_snap_set_im( snap, 0 ); + } else if ( buffer[0x35] == 0x56 ) { + libspectrum_snap_set_im( snap, 1 ); + } else if ( buffer[0x35] == 0x5e ) { + libspectrum_snap_set_im( snap, 1 ); + } else { + libspectrum_print_error( + LIBSPECTRUM_ERROR_CORRUPT, + "libspectrum_cobra_read_buffer: invalid im opcode" + ); + return LIBSPECTRUM_ERROR_CORRUPT; + } + + libspectrum_snap_set_pc ( snap, buffer[0x37] + buffer[0x38]*0x100 ); + + return LIBSPECTRUM_ERROR_NONE; +} + +static int +libspectrum_cobra_read_data ( const libspectrum_byte *buffer, + size_t buffer_length, + libspectrum_snap *snap ) +{ + if( buffer_length < 0xc000 ) { + libspectrum_print_error( + LIBSPECTRUM_ERROR_CORRUPT, + "libspectrum_cobra_read_data: not enough data in buffer" + ); + return LIBSPECTRUM_ERROR_CORRUPT; + } + + return libspectrum_split_to_48k_pages( snap, buffer ); +} + +libspectrum_error +libspectrum_cobra_write( libspectrum_buffer *buffer, int *out_flags, + libspectrum_snap *snap, int in_flags GCC_UNUSED ) +{ + libspectrum_buffer *buffer_mem; + libspectrum_error error = LIBSPECTRUM_ERROR_NONE; + + /* Minor info loss already due to things like tstate count, halted state, + etc which are not stored in .sna format */ + *out_flags = LIBSPECTRUM_FLAG_SNAPSHOT_MINOR_INFO_LOSS; + + /* We don't store +D info at all */ + if( libspectrum_snap_plusd_active( snap ) ) + *out_flags |= LIBSPECTRUM_FLAG_SNAPSHOT_MAJOR_INFO_LOSS; + + /* We don't store Beta info at all */ + if( libspectrum_snap_beta_active( snap ) ) + *out_flags |= LIBSPECTRUM_FLAG_SNAPSHOT_MAJOR_INFO_LOSS; + + /* We don't store Opus info at all */ + if( libspectrum_snap_opus_active( snap ) ) + *out_flags |= LIBSPECTRUM_FLAG_SNAPSHOT_MAJOR_INFO_LOSS; + + /* We don't save IDE interface info at all */ + if( libspectrum_snap_zxatasp_active( snap ) ) + *out_flags |= LIBSPECTRUM_FLAG_SNAPSHOT_MAJOR_INFO_LOSS; + if( libspectrum_snap_zxcf_active( snap ) ) + *out_flags |= LIBSPECTRUM_FLAG_SNAPSHOT_MAJOR_INFO_LOSS; + if( libspectrum_snap_simpleide_active( snap ) ) + *out_flags |= LIBSPECTRUM_FLAG_SNAPSHOT_MAJOR_INFO_LOSS; + if( libspectrum_snap_divide_active( snap ) ) + *out_flags |= LIBSPECTRUM_FLAG_SNAPSHOT_MAJOR_INFO_LOSS; + + /* We don't save the Interface 2 ROM at all */ + if( libspectrum_snap_interface2_active( snap ) ) + *out_flags |= LIBSPECTRUM_FLAG_SNAPSHOT_MAJOR_INFO_LOSS; + + /* We don't save the Timex Dock at all */ + if( libspectrum_snap_dock_active( snap ) ) + *out_flags |= LIBSPECTRUM_FLAG_SNAPSHOT_MAJOR_INFO_LOSS; + + /* We don't save custom ROMs at all */ + if( libspectrum_snap_custom_rom( snap ) ) + *out_flags |= LIBSPECTRUM_FLAG_SNAPSHOT_MAJOR_INFO_LOSS; + + /* We don't save AY interfaces at all */ + if( libspectrum_snap_fuller_box_active( snap ) || + libspectrum_snap_melodik_active( snap ) ) + *out_flags |= LIBSPECTRUM_FLAG_SNAPSHOT_MAJOR_INFO_LOSS; + + /* We don't save the Specdrum state at all */ + if( libspectrum_snap_specdrum_active( snap ) ) + *out_flags |= LIBSPECTRUM_FLAG_SNAPSHOT_MAJOR_INFO_LOSS; + + /* We don't save the Spectranet state at all */ + if( libspectrum_snap_spectranet_active( snap ) ) + *out_flags |= LIBSPECTRUM_FLAG_SNAPSHOT_MAJOR_INFO_LOSS; + + /* We don't save the uSource state at all */ + if( libspectrum_snap_usource_active( snap ) ) + *out_flags |= LIBSPECTRUM_FLAG_SNAPSHOT_MAJOR_INFO_LOSS; + + /* We don't save the DISCiPLE state at all */ + if( libspectrum_snap_disciple_active( snap ) ) + *out_flags |= LIBSPECTRUM_FLAG_SNAPSHOT_MAJOR_INFO_LOSS; + + /* We don't save the Didaktik80 state at all */ + if( libspectrum_snap_didaktik80_active( snap ) ) + *out_flags |= LIBSPECTRUM_FLAG_SNAPSHOT_MAJOR_INFO_LOSS; + + /* We don't save the Covox state at all */ + if( libspectrum_snap_covox_active( snap ) ) + *out_flags |= LIBSPECTRUM_FLAG_SNAPSHOT_MAJOR_INFO_LOSS; + + /* We don't save the Multiface state at all */ + if( libspectrum_snap_multiface_active( snap ) ) + *out_flags |= LIBSPECTRUM_FLAG_SNAPSHOT_MAJOR_INFO_LOSS; + + /* We don't save the MMC state at all */ + if( libspectrum_snap_divmmc_active( snap ) ) + *out_flags |= LIBSPECTRUM_FLAG_SNAPSHOT_MAJOR_INFO_LOSS; + if( libspectrum_snap_zxmmc_active( snap ) ) + *out_flags |= LIBSPECTRUM_FLAG_SNAPSHOT_MAJOR_INFO_LOSS; + + /* We don't save the TTX2000S state at all */ + if( libspectrum_snap_ttx2000s_active( snap ) ) + *out_flags |= LIBSPECTRUM_FLAG_SNAPSHOT_MAJOR_INFO_LOSS; + + buffer_mem = libspectrum_buffer_alloc(); + + switch( libspectrum_snap_machine( snap ) ) { + + case LIBSPECTRUM_MACHINE_48_NTSC: + case LIBSPECTRUM_MACHINE_TC2048: + case LIBSPECTRUM_MACHINE_TC2068: + case LIBSPECTRUM_MACHINE_TS2068: + case LIBSPECTRUM_MACHINE_128: + case LIBSPECTRUM_MACHINE_128E: + case LIBSPECTRUM_MACHINE_PENT512: + case LIBSPECTRUM_MACHINE_PENT1024: + case LIBSPECTRUM_MACHINE_PLUS2: + case LIBSPECTRUM_MACHINE_PLUS2A: + case LIBSPECTRUM_MACHINE_PLUS3: + case LIBSPECTRUM_MACHINE_PLUS3E: + case LIBSPECTRUM_MACHINE_SCORP: + case LIBSPECTRUM_MACHINE_SE: + case LIBSPECTRUM_MACHINE_PENT: + *out_flags |= LIBSPECTRUM_FLAG_SNAPSHOT_MAJOR_INFO_LOSS; + break; + + case LIBSPECTRUM_MACHINE_16: + case LIBSPECTRUM_MACHINE_48: + *out_flags |= LIBSPECTRUM_FLAG_SNAPSHOT_MINOR_INFO_LOSS; + break; + + default: + libspectrum_print_error( LIBSPECTRUM_ERROR_LOGIC, + "Emulated machine type is set to 'unknown'!" ); + libspectrum_buffer_free( buffer_mem ); + return LIBSPECTRUM_ERROR_LOGIC; + } + + if( error ) { + libspectrum_buffer_free( buffer_mem ); + return error; + } + + write_loader( buffer, snap ); + write_pages( buffer, snap ); + libspectrum_buffer_write_buffer( buffer, buffer_mem ); + libspectrum_buffer_free( buffer_mem ); + + return LIBSPECTRUM_ERROR_NONE; +} + +static void +write_loader( libspectrum_buffer *buffer, libspectrum_snap *snap ) +{ + const libspectrum_byte loader[] = { + 0x18, 0x02, 0x00, 0x41, 0xf3, 0x21, 0xff, 0xc1, 0x11, 0xff, 0xff, 0x01, + 0x00, 0xc0, 0xed, 0xb8, 0x01, 0x00, 0x1b, 0x21, 0x00, 0x40, 0x56, 0x3e, + 0x40, 0xd3, 0xfe, 0x72, 0xaf, 0xd3, 0xfe, 0x23, 0x0b, 0x78, 0xb1, 0x20, + 0xf1, 0xed, 0x7b, 0x02, 0x01, 0x2a, 0x02, 0x01, 0x3e, 0x03, 0xd3, 0xe3, + 0xd3, 0xeb, 0xd3, 0xf3, 0xd3, 0xfb, 0xaf, 0xd3, 0xfd, 0x3e, 0x00, 0xdb, + 0xfe, 0xe6, 0x3f, 0xfe, 0x3f, 0x20, 0x02, 0x18, 0xf4, 0xd9, 0x21, 0x38, + 0x00, 0x3e, 0xc1, 0xd3, 0xfe, 0xed, 0x4f, 0xe9 + }; + const size_t loader_len = 80; + + libspectrum_buffer_write( buffer, loader, loader_len ); + libspectrum_buffer_set( buffer, 0, 256 - loader_len ); +} + +static int +write_word( libspectrum_byte *buffer, libspectrum_word w) +{ + libspectrum_byte* lsb_data_ptr = buffer; + return libspectrum_write_word( &lsb_data_ptr, w ); +} + +static size_t +write_restore( libspectrum_buffer *buffer, libspectrum_snap *snap ) +{ + const libspectrum_byte im[3] = { 0x46, 0x56, 0x5e }; + + restore[0x01] = libspectrum_snap_r ( snap ); + restore[0x05] = libspectrum_snap_i ( snap ); + restore[0x09] = libspectrum_snap_f_ ( snap ); + restore[0x0a] = libspectrum_snap_a_ ( snap ); + restore[0x0d] = libspectrum_snap_f ( snap ); + restore[0x0e] = libspectrum_snap_a ( snap ); + write_word( &restore[0x15], libspectrum_snap_bc ( snap ) ); + write_word( &restore[0x18], libspectrum_snap_de ( snap ) ); + write_word( &restore[0x1b], libspectrum_snap_hl ( snap ) ); + write_word( &restore[0x1e], libspectrum_snap_sp ( snap ) + 2 ); + write_word( &restore[0x22], libspectrum_snap_ix ( snap ) ); + write_word( &restore[0x26], libspectrum_snap_iy ( snap ) ); + write_word( &restore[0x2a], libspectrum_snap_bc_( snap ) ); + write_word( &restore[0x2d], libspectrum_snap_de_( snap ) ); + write_word( &restore[0x30], libspectrum_snap_hl_( snap ) ); + restore[0x33] = libspectrum_snap_iff1( snap ) ? 0xfb : 0xf3; + restore[0x35] = im[ libspectrum_snap_im ( snap ) ]; + write_word( &restore[0x37], libspectrum_snap_pc ( snap ) ); + + libspectrum_buffer_write( buffer, restore, restore_len ); + + return restore_len; +} + +static void +write_pages( libspectrum_buffer *buffer, libspectrum_snap *snap ) +{ + libspectrum_byte *ram; + size_t size; + + ram = libspectrum_snap_pages( snap, 5 ); + if( ram ) { + /* Restore routine is offset to provide stack space in video RAM */ + /* Write first 256 bytes from snapshot */ + libspectrum_buffer_write( buffer, ram, 0x100 ); + /* Write restore routine */ + size = write_restore( buffer, snap ); + ram += 0x100 + size; + /* Write the rest of the page */ + libspectrum_buffer_write( buffer, ram, 0x4000 - 0x100 - size ); + } else { + libspectrum_buffer_set( buffer, 0xff, 0x100 ); + size = write_restore( buffer, snap ); + libspectrum_buffer_set( buffer, 0xff, 0x4000 - 0x100 - size ); + } + + ram = libspectrum_snap_pages( snap, 2 ); + if( ram ) { + libspectrum_buffer_write( buffer, ram, 0x4000 ); + } else { + libspectrum_buffer_set( buffer, 0xff, 0x4000 ); + } + + ram = libspectrum_snap_pages( snap, 0 ); + if( ram ) { + libspectrum_buffer_write( buffer, ram, 0x4000 ); + } else { + libspectrum_buffer_set( buffer, 0xff, 0x4000 ); + } +} diff --git a/libspectrum.c b/libspectrum.c index 55ba4aa..b02fd36 100644 --- a/libspectrum.c +++ b/libspectrum.c @@ -521,6 +521,7 @@ libspectrum_identify_file_raw( libspectrum_id_t *type, const char *filename, { LIBSPECTRUM_ID_SNAPSHOT_Z80, "slt", 3, "\0\0", 6, 2, 1 }, { LIBSPECTRUM_ID_SNAPSHOT_ZXS, "zxs", 3, "SNAP", 8, 4, 4 }, { LIBSPECTRUM_ID_SNAPSHOT_PLUSD,"mgtsnp", 3, NULL, 0, 0, 0 }, + { LIBSPECTRUM_ID_SNAPSHOT_COBRA,"com", 3, "\x18\x02", 8, 2, 4 }, { LIBSPECTRUM_ID_CARTRIDGE_DCK, "dck", 3, NULL, 0, 0, 0 }, { LIBSPECTRUM_ID_CARTRIDGE_IF2, "rom", 3, NULL, 0, 0, 0 }, @@ -720,6 +721,7 @@ libspectrum_identify_class( libspectrum_class_t *libspectrum_class, case LIBSPECTRUM_ID_SNAPSHOT_SZX: case LIBSPECTRUM_ID_SNAPSHOT_Z80: case LIBSPECTRUM_ID_SNAPSHOT_ZXS: + case LIBSPECTRUM_ID_SNAPSHOT_COBRA: *libspectrum_class = LIBSPECTRUM_CLASS_SNAPSHOT; return 0; case LIBSPECTRUM_ID_TAPE_TAP: diff --git a/snapshot.c b/snapshot.c index e6f47d8..43d6cee 100644 --- a/snapshot.c +++ b/snapshot.c @@ -112,6 +112,9 @@ libspectrum_snap_read( libspectrum_snap *snap, const libspectrum_byte *buffer, case LIBSPECTRUM_ID_SNAPSHOT_ZXS: error = libspectrum_zxs_read( snap, buffer, length ); break; + case LIBSPECTRUM_ID_SNAPSHOT_COBRA: + error = libspectrum_cobra_read( snap, buffer, length ); break; + default: libspectrum_print_error( LIBSPECTRUM_ERROR_LOGIC, "libspectrum_snap_read: unknown snapshot type %d", @@ -171,6 +174,11 @@ libspectrum_snap_write_buffer( libspectrum_buffer *buffer, int *out_flags, error = libspectrum_z80_write2( buffer, out_flags, snap, in_flags ); break; + case LIBSPECTRUM_ID_SNAPSHOT_COBRA: + error = libspectrum_cobra_write( buffer, out_flags, snap, creator, + in_flags ); + break; + default: libspectrum_print_error( LIBSPECTRUM_ERROR_UNKNOWN, "libspectrum_snap_write: format not supported" ); -- 2.34.1