1
0
mirror of https://git.code.sf.net/p/fuse-emulator/fuse synced 2026-01-27 01:41:34 +03:00
Files
fuse/z80/coretest.c
Philip Kendall 4e6984ef2f Baby steps: copy DivIDE code to DivMMC.
Will need to be refactored later to remove duplicated code.
2017-07-21 23:08:52 +01:00

678 lines
13 KiB
C

/* coretest.c: Test program for Fuse's Z80 core
Copyright (c) 2003-2017 Philip Kendall
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Author contact information:
E-mail: philip-fuse@shadowmagic.org.uk
*/
#include <config.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "fuse.h"
#include "peripherals/disk/beta.h"
#include "peripherals/disk/didaktik.h"
#include "peripherals/disk/disciple.h"
#include "peripherals/disk/opus.h"
#include "peripherals/disk/plusd.h"
#include "peripherals/ide/divide.h"
#include "peripherals/ide/divmmc.h"
#include "peripherals/if1.h"
#include "peripherals/spectranet.h"
#include "peripherals/ula.h"
#include "peripherals/usource.h"
#include "profile.h"
#include "rzx.h"
#include "slt.h"
#include "tape.h"
#include "event.h"
#include "infrastructure/startup_manager.h"
#include "module.h"
#include "spectrum.h"
#include "ui/ui.h"
#include "z80.h"
#include "z80_macros.h"
static const char *progname; /* argv[0] */
static const char *testsfile; /* argv[1] */
static int init_dummies( void );
libspectrum_dword tstates;
libspectrum_dword event_next_event;
/* 64Kb of RAM */
static libspectrum_byte initial_memory[ 0x10000 ], memory[ 0x10000 ];
libspectrum_byte readbyte( libspectrum_word address );
libspectrum_byte readbyte_internal( libspectrum_word address );
void writebyte( libspectrum_word address, libspectrum_byte b );
void writebyte_internal( libspectrum_word address, libspectrum_byte b );
static int run_test( FILE *f );
static int read_test( FILE *f, libspectrum_dword *end_tstates );
static void dump_z80_state( void );
static void dump_memory_state( void );
int
main( int argc, char **argv )
{
FILE *f;
progname = argv[0];
if( argc < 2 ) {
fprintf( stderr, "Usage: %s <testsfile>\n", progname );
return 1;
}
testsfile = argv[1];
if( init_dummies() ) return 1;
/* Initialise the tables used by the Z80 core */
z80_init( NULL );
f = fopen( testsfile, "r" );
if( !f ) {
fprintf( stderr, "%s: couldn't open tests file `%s': %s\n", progname,
testsfile, strerror( errno ) );
return 1;
}
while( run_test( f ) ) {
/* Do nothing */
}
if( fclose( f ) ) {
fprintf( stderr, "%s: couldn't close `%s': %s\n", progname, testsfile,
strerror( errno ) );
return 1;
}
return 0;
}
libspectrum_byte
readbyte( libspectrum_word address )
{
printf( "%5d MC %04x\n", tstates, address );
tstates += 3;
return readbyte_internal( address );
}
libspectrum_byte
readbyte_internal( libspectrum_word address )
{
printf( "%5d MR %04x %02x\n", tstates, address, memory[ address ] );
return memory[ address ];
}
void
writebyte( libspectrum_word address, libspectrum_byte b )
{
printf( "%5d MC %04x\n", tstates, address );
tstates += 3;
writebyte_internal( address, b );
}
void
writebyte_internal( libspectrum_word address, libspectrum_byte b )
{
printf( "%5d MW %04x %02x\n", tstates, address, b );
memory[ address ] = b;
}
void
contend_read( libspectrum_word address, libspectrum_dword time )
{
printf( "%5d MC %04x\n", tstates, address );
tstates += time;
}
void
contend_read_no_mreq( libspectrum_word address, libspectrum_dword time )
{
contend_read( address, time );
}
void
contend_write_no_mreq( libspectrum_word address, libspectrum_dword time )
{
printf( "%5d MC %04x\n", tstates, address );
tstates += time;
}
static void
contend_port_preio( libspectrum_word port )
{
if( ( port & 0xc000 ) == 0x4000 ) {
printf( "%5d PC %04x\n", tstates, port );
}
tstates++;
}
static void
contend_port_postio( libspectrum_word port )
{
if( port & 0x0001 ) {
if( ( port & 0xc000 ) == 0x4000 ) {
printf( "%5d PC %04x\n", tstates, port ); tstates++;
printf( "%5d PC %04x\n", tstates, port ); tstates++;
printf( "%5d PC %04x\n", tstates, port ); tstates++;
} else {
tstates += 3;
}
} else {
printf( "%5d PC %04x\n", tstates, port ); tstates += 3;
}
}
libspectrum_byte
readport( libspectrum_word port )
{
libspectrum_byte r = port >> 8;
contend_port_preio( port );
printf( "%5d PR %04x %02x\n", tstates, port, r );
contend_port_postio( port );
return r;
}
void
writeport( libspectrum_word port, libspectrum_byte b )
{
contend_port_preio( port );
printf( "%5d PW %04x %02x\n", tstates, port, b );
contend_port_postio( port );
}
static int
run_test( FILE *f )
{
size_t i;
/* Get ourselves into a known state */
z80_reset( 1 ); tstates = 0;
for( i = 0; i < 0x10000; i += 4 ) {
memory[ i ] = 0xde; memory[ i + 1 ] = 0xad;
memory[ i + 2 ] = 0xbe; memory[ i + 3 ] = 0xef;
}
if( read_test( f, &event_next_event ) ) return 0;
/* Grab a copy of the memory for comparison at the end */
memcpy( initial_memory, memory, 0x10000 );
z80_do_opcodes();
/* And dump our final state */
dump_z80_state();
dump_memory_state();
printf( "\n" );
return 1;
}
static int
read_test( FILE *f, libspectrum_dword *end_tstates )
{
unsigned af, bc, de, hl, af_, bc_, de_, hl_, ix, iy, sp, pc, memptr;
unsigned i, r, iff1, iff2, im;
unsigned end_tstates2;
unsigned address;
char test_name[ 80 ];
do {
if( !fgets( test_name, sizeof( test_name ), f ) ) {
if( feof( f ) ) return 1;
fprintf( stderr, "%s: error reading test description from `%s': %s\n",
progname, testsfile, strerror( errno ) );
return 1;
}
} while( test_name[0] == '\n' );
/* FIXME: how should we read/write our data types? */
if( fscanf( f, "%x %x %x %x %x %x %x %x %x %x %x %x %x", &af, &bc,
&de, &hl, &af_, &bc_, &de_, &hl_, &ix, &iy, &sp, &pc,
&memptr ) != 13 ) {
fprintf( stderr, "%s: first registers line in `%s' corrupt\n", progname,
testsfile );
return 1;
}
AF = af; BC = bc; DE = de; HL = hl;
AF_ = af_; BC_ = bc_; DE_ = de_; HL_ = hl_;
IX = ix; IY = iy; SP = sp; PC = pc;
z80.memptr.w = memptr;
if( fscanf( f, "%x %x %u %u %u %d %d", &i, &r, &iff1, &iff2, &im,
&z80.halted, &end_tstates2 ) != 7 ) {
fprintf( stderr, "%s: second registers line in `%s' corrupt\n", progname,
testsfile );
return 1;
}
I = i; R = R7 = r; IFF1 = iff1; IFF2 = iff2; IM = im;
*end_tstates = end_tstates2;
while( 1 ) {
if( fscanf( f, "%x", &address ) != 1 ) {
fprintf( stderr, "%s: no address found in `%s'\n", progname, testsfile );
return 1;
}
if( address >= 0x10000 ) break;
while( 1 ) {
unsigned byte;
if( fscanf( f, "%x", &byte ) != 1 ) {
fprintf( stderr, "%s: no data byte found in `%s'\n", progname,
testsfile );
return 1;
}
if( byte >= 0x100 ) break;
memory[ address++ ] = byte;
}
}
printf( "%s", test_name );
return 0;
}
static void
dump_z80_state( void )
{
printf( "%04x %04x %04x %04x %04x %04x %04x %04x %04x %04x %04x %04x %04x\n",
AF, BC, DE, HL, AF_, BC_, DE_, HL_, IX, IY, SP, PC, z80.memptr.w );
printf( "%02x %02x %d %d %d %d %d\n", I, ( R7 & 0x80 ) | ( R & 0x7f ),
IFF1, IFF2, IM, z80.halted, tstates );
}
static void
dump_memory_state( void )
{
size_t i;
for( i = 0; i < 0x10000; i++ ) {
if( memory[ i ] == initial_memory[ i ] ) continue;
printf( "%04x ", (unsigned)i );
while( i < 0x10000 && memory[ i ] != initial_memory[ i ] )
printf( "%02x ", memory[ i++ ] );
printf( "-1\n" );
}
}
/* Error 'handing': dump core as these should never be called */
void
fuse_abort( void )
{
abort();
}
int
ui_error( ui_error_level severity GCC_UNUSED, const char *format, ... )
{
va_list ap;
va_start( ap, format );
vfprintf( stderr, format, ap );
va_end( ap );
abort();
}
/*
* Stuff below here not interesting: dummy functions and variables to replace
* things used by Fuse, but not by the core test code
*/
#include "debugger/debugger.h"
#include "machine.h"
#include "peripherals/scld.h"
#include "settings.h"
libspectrum_byte *slt[256];
size_t slt_length[256];
int
tape_load_trap( void )
{
/* Should never be called */
abort();
}
int
tape_save_trap( void )
{
/* Should never be called */
abort();
}
scld scld_last_dec;
size_t rzx_instruction_count;
int rzx_playback;
int rzx_instructions_offset;
enum debugger_mode_t debugger_mode;
libspectrum_byte **ROM = NULL;
memory_page memory_map[8];
memory_page *memory_map_home[MEMORY_PAGES_IN_64K];
memory_page memory_map_rom[SPECTRUM_ROM_PAGES * MEMORY_PAGES_IN_16K];
int memory_contended[8] = { 1 };
libspectrum_byte spectrum_contention[ 80000 ] = { 0 };
int profile_active = 0;
void
profile_map( libspectrum_word pc GCC_UNUSED )
{
abort();
}
int
debugger_check( debugger_breakpoint_type type GCC_UNUSED, libspectrum_dword value GCC_UNUSED )
{
abort();
}
void debugger_system_variable_register(
const char *type, const char *detail,
debugger_get_system_variable_fn_t get,
debugger_set_system_variable_fn_t set )
{
}
int
debugger_trap( void )
{
abort();
}
int
slt_trap( libspectrum_word address GCC_UNUSED, libspectrum_byte level GCC_UNUSED )
{
return 0;
}
int beta_available = 0;
int beta_active = 0;
int if1_available = 0;
void
beta_page( void )
{
abort();
}
void
beta_unpage( void )
{
abort();
}
int spectrum_frame_event = 0;
int
event_register( event_fn_t fn GCC_UNUSED, const char *string GCC_UNUSED )
{
return 0;
}
int opus_available = 0;
int opus_active = 0;
void
opus_page( void )
{
abort();
}
void
opus_unpage( void )
{
abort();
}
int plusd_available = 0;
int plusd_active = 0;
void
plusd_page( void )
{
abort();
}
int disciple_available = 0;
int disciple_active = 0;
void
disciple_page( void )
{
abort();
}
int didaktik80_available = 0;
int didaktik80_active = 0;
int didaktik80_snap = 0;
void
didaktik80_page( void )
{
abort();
}
void
didaktik80_unpage( void )
{
abort();
}
int usource_available = 0;
int usource_active = 0;
void
usource_toggle( void )
{
abort();
}
void
if1_page( void )
{
abort();
}
void
if1_unpage( void )
{
abort();
}
int multiface_activated = 0;
void
multiface_setic8( void )
{
abort();
}
void
divide_set_automap( int state GCC_UNUSED )
{
abort();
}
void
divmmc_set_automap( int state GCC_UNUSED )
{
abort();
}
int spectranet_available = 0;
void
spectranet_page( int via_io GCC_UNUSED )
{
abort();
}
void
spectranet_nmi( void )
{
abort();
}
void
spectranet_unpage( void )
{
abort();
}
void
spectranet_retn( void )
{
}
int
spectranet_nmi_flipflop( void )
{
return 0;
}
void
startup_manager_register( startup_manager_module module,
startup_manager_module *dependencies, size_t dependency_count,
startup_manager_init_fn init_fn, void *init_context,
startup_manager_end_fn end_fn )
{
}
int svg_capture_active = 0; /* SVG capture enabled? */
void
svg_capture( void )
{
abort();
}
int
rzx_frame( void )
{
abort();
}
void
writeport_internal( libspectrum_word port GCC_UNUSED, libspectrum_byte b GCC_UNUSED )
{
abort();
}
void
event_add_with_data( libspectrum_dword event_time GCC_UNUSED,
int type GCC_UNUSED, void *user_data GCC_UNUSED )
{
/* Do nothing */
}
int
module_register( module_info_t *module GCC_UNUSED )
{
return 0;
}
void
z80_debugger_variables_init( void )
{
}
fuse_machine_info *machine_current;
static fuse_machine_info dummy_machine;
settings_info settings_current;
libspectrum_word beta_pc_mask;
libspectrum_word beta_pc_value;
int spectranet_programmable_trap_active;
libspectrum_word spectranet_programmable_trap;
/* Initialise the dummy variables such that we're running on a clean a
machine as possible */
static int
init_dummies( void )
{
size_t i;
for( i = 0; i < 8; i++ ) {
memory_map[i].page = &memory[ i * MEMORY_PAGE_SIZE ];
}
debugger_mode = DEBUGGER_MODE_INACTIVE;
dummy_machine.capabilities = 0;
dummy_machine.ram.current_rom = 0;
machine_current = &dummy_machine;
rzx_playback = 0;
scld_last_dec.name.intdisable = 0;
settings_current.slt_traps = 0;
settings_current.divide_enabled = 0;
settings_current.divmmc_enabled = 0;
settings_current.z80_is_cmos = 0;
beta_pc_mask = 0xfe00;
beta_pc_value = 0x3c00;
spectranet_programmable_trap_active = 0;
spectranet_programmable_trap = 0x0000;
return 0;
}