1 /* coretest.c: Test program for Fuse's Z80 core
2    Copyright (c) 2003-2017 Philip Kendall
3 
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 2 of the License, or
7    (at your option) any later version.
8 
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13 
14    You should have received a copy of the GNU General Public License along
15    with this program; if not, write to the Free Software Foundation, Inc.,
16    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 
18    Author contact information:
19 
20    E-mail: philip-fuse@shadowmagic.org.uk
21 
22 */
23 
24 #include <config.h>
25 
26 #include <errno.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 
31 #include "fuse.h"
32 #include "peripherals/disk/beta.h"
33 #include "peripherals/disk/didaktik.h"
34 #include "peripherals/disk/disciple.h"
35 #include "peripherals/disk/opus.h"
36 #include "peripherals/disk/plusd.h"
37 #include "peripherals/ide/divide.h"
38 #include "peripherals/ide/divmmc.h"
39 #include "peripherals/if1.h"
40 #include "peripherals/spectranet.h"
41 #include "peripherals/ula.h"
42 #include "peripherals/usource.h"
43 #include "profile.h"
44 #include "rzx.h"
45 #include "slt.h"
46 #include "tape.h"
47 
48 #include "event.h"
49 #include "infrastructure/startup_manager.h"
50 #include "module.h"
51 #include "spectrum.h"
52 #include "ui/ui.h"
53 #include "z80.h"
54 #include "z80_macros.h"
55 
56 static const char *progname;		/* argv[0] */
57 static const char *testsfile;		/* argv[1] */
58 
59 static int init_dummies( void );
60 
61 libspectrum_dword tstates;
62 libspectrum_dword event_next_event;
63 
64 /* 64Kb of RAM */
65 static libspectrum_byte initial_memory[ 0x10000 ], memory[ 0x10000 ];
66 
67 libspectrum_byte readbyte( libspectrum_word address );
68 libspectrum_byte readbyte_internal( libspectrum_word address );
69 
70 void writebyte( libspectrum_word address, libspectrum_byte b );
71 void writebyte_internal( libspectrum_word address, libspectrum_byte b );
72 
73 static int run_test( FILE *f );
74 static int read_test( FILE *f, libspectrum_dword *end_tstates );
75 
76 static void dump_z80_state( void );
77 static void dump_memory_state( void );
78 
79 int
main(int argc,char ** argv)80 main( int argc, char **argv )
81 {
82   FILE *f;
83 
84   progname = argv[0];
85 
86   if( argc < 2 ) {
87     fprintf( stderr, "Usage: %s <testsfile>\n", progname );
88     return 1;
89   }
90 
91   testsfile = argv[1];
92 
93   if( init_dummies() ) return 1;
94 
95   /* Initialise the tables used by the Z80 core */
96   z80_init( NULL );
97 
98   f = fopen( testsfile, "r" );
99   if( !f ) {
100     fprintf( stderr, "%s: couldn't open tests file `%s': %s\n", progname,
101 	     testsfile, strerror( errno ) );
102     return 1;
103   }
104 
105   while( run_test( f ) ) {
106     /* Do nothing */
107   }
108 
109   if( fclose( f ) ) {
110     fprintf( stderr, "%s: couldn't close `%s': %s\n", progname, testsfile,
111 	     strerror( errno ) );
112     return 1;
113   }
114 
115   return 0;
116 }
117 
118 libspectrum_byte
readbyte(libspectrum_word address)119 readbyte( libspectrum_word address )
120 {
121   printf( "%5d MC %04x\n", tstates, address );
122   tstates += 3;
123   return readbyte_internal( address );
124 }
125 
126 libspectrum_byte
readbyte_internal(libspectrum_word address)127 readbyte_internal( libspectrum_word address )
128 {
129   printf( "%5d MR %04x %02x\n", tstates, address, memory[ address ] );
130   return memory[ address ];
131 }
132 
133 void
writebyte(libspectrum_word address,libspectrum_byte b)134 writebyte( libspectrum_word address, libspectrum_byte b )
135 {
136   printf( "%5d MC %04x\n", tstates, address );
137   tstates += 3;
138   writebyte_internal( address, b );
139 }
140 
141 void
writebyte_internal(libspectrum_word address,libspectrum_byte b)142 writebyte_internal( libspectrum_word address, libspectrum_byte b )
143 {
144   printf( "%5d MW %04x %02x\n", tstates, address, b );
145   memory[ address ] = b;
146 }
147 
148 void
contend_read(libspectrum_word address,libspectrum_dword time)149 contend_read( libspectrum_word address, libspectrum_dword time )
150 {
151   printf( "%5d MC %04x\n", tstates, address );
152   tstates += time;
153 }
154 
155 void
contend_read_no_mreq(libspectrum_word address,libspectrum_dword time)156 contend_read_no_mreq( libspectrum_word address, libspectrum_dword time )
157 {
158   contend_read( address, time );
159 }
160 
161 void
contend_write_no_mreq(libspectrum_word address,libspectrum_dword time)162 contend_write_no_mreq( libspectrum_word address, libspectrum_dword time )
163 {
164   printf( "%5d MC %04x\n", tstates, address );
165   tstates += time;
166 }
167 
168 static void
contend_port_preio(libspectrum_word port)169 contend_port_preio( libspectrum_word port )
170 {
171   if( ( port & 0xc000 ) == 0x4000 ) {
172     printf( "%5d PC %04x\n", tstates, port );
173   }
174 
175   tstates++;
176 }
177 
178 static void
contend_port_postio(libspectrum_word port)179 contend_port_postio( libspectrum_word port )
180 {
181   if( port & 0x0001 ) {
182 
183     if( ( port & 0xc000 ) == 0x4000 ) {
184       printf( "%5d PC %04x\n", tstates, port ); tstates++;
185       printf( "%5d PC %04x\n", tstates, port ); tstates++;
186       printf( "%5d PC %04x\n", tstates, port ); tstates++;
187     } else {
188       tstates += 3;
189     }
190 
191   } else {
192 
193     printf( "%5d PC %04x\n", tstates, port ); tstates += 3;
194 
195   }
196 }
197 
198 libspectrum_byte
readport(libspectrum_word port)199 readport( libspectrum_word port )
200 {
201   libspectrum_byte r = port >> 8;
202 
203   contend_port_preio( port );
204 
205   printf( "%5d PR %04x %02x\n", tstates, port, r );
206 
207   contend_port_postio( port );
208 
209   return r;
210 }
211 
212 void
writeport(libspectrum_word port,libspectrum_byte b)213 writeport( libspectrum_word port, libspectrum_byte b )
214 {
215   contend_port_preio( port );
216 
217   printf( "%5d PW %04x %02x\n", tstates, port, b );
218 
219   contend_port_postio( port );
220 }
221 
222 static int
run_test(FILE * f)223 run_test( FILE *f )
224 {
225   size_t i;
226 
227   /* Get ourselves into a known state */
228   z80_reset( 1 ); tstates = 0;
229   for( i = 0; i < 0x10000; i += 4 ) {
230     memory[ i     ] = 0xde; memory[ i + 1 ] = 0xad;
231     memory[ i + 2 ] = 0xbe; memory[ i + 3 ] = 0xef;
232   }
233 
234   if( read_test( f, &event_next_event ) ) return 0;
235 
236   /* Grab a copy of the memory for comparison at the end */
237   memcpy( initial_memory, memory, 0x10000 );
238 
239   z80_do_opcodes();
240 
241   /* And dump our final state */
242   dump_z80_state();
243   dump_memory_state();
244 
245   printf( "\n" );
246 
247   return 1;
248 }
249 
250 static int
read_test(FILE * f,libspectrum_dword * end_tstates)251 read_test( FILE *f, libspectrum_dword *end_tstates )
252 {
253   unsigned af, bc, de, hl, af_, bc_, de_, hl_, ix, iy, sp, pc, memptr;
254   unsigned i, r, iff1, iff2, im;
255   unsigned end_tstates2;
256   unsigned address;
257   char test_name[ 80 ];
258 
259   do {
260 
261     if( !fgets( test_name, sizeof( test_name ), f ) ) {
262 
263       if( feof( f ) ) return 1;
264 
265       fprintf( stderr, "%s: error reading test description from `%s': %s\n",
266 	       progname, testsfile, strerror( errno ) );
267       return 1;
268     }
269 
270   } while( test_name[0] == '\n' );
271 
272   /* FIXME: how should we read/write our data types? */
273   if( fscanf( f, "%x %x %x %x %x %x %x %x %x %x %x %x %x", &af, &bc,
274 	      &de, &hl, &af_, &bc_, &de_, &hl_, &ix, &iy, &sp, &pc,
275 	      &memptr ) != 13 ) {
276     fprintf( stderr, "%s: first registers line in `%s' corrupt\n", progname,
277 	     testsfile );
278     return 1;
279   }
280 
281   AF  = af;  BC  = bc;  DE  = de;  HL  = hl;
282   AF_ = af_; BC_ = bc_; DE_ = de_; HL_ = hl_;
283   IX  = ix;  IY  = iy;  SP  = sp;  PC  = pc;
284   z80.memptr.w = memptr;
285 
286   if( fscanf( f, "%x %x %u %u %u %d %d", &i, &r, &iff1, &iff2, &im,
287 	      &z80.halted, &end_tstates2 ) != 7 ) {
288     fprintf( stderr, "%s: second registers line in `%s' corrupt\n", progname,
289 	     testsfile );
290     return 1;
291   }
292 
293   I = i; R = R7 = r; IFF1 = iff1; IFF2 = iff2; IM = im;
294   *end_tstates = end_tstates2;
295 
296   while( 1 ) {
297 
298     if( fscanf( f, "%x", &address ) != 1 ) {
299       fprintf( stderr, "%s: no address found in `%s'\n", progname, testsfile );
300       return 1;
301     }
302 
303     if( address >= 0x10000 ) break;
304 
305     while( 1 ) {
306 
307       unsigned byte;
308 
309       if( fscanf( f, "%x", &byte ) != 1 ) {
310 	fprintf( stderr, "%s: no data byte found in `%s'\n", progname,
311 		 testsfile );
312 	return 1;
313       }
314 
315       if( byte >= 0x100 ) break;
316 
317       memory[ address++ ] = byte;
318 
319     }
320   }
321 
322   printf( "%s", test_name );
323 
324   return 0;
325 }
326 
327 static void
dump_z80_state(void)328 dump_z80_state( void )
329 {
330   printf( "%04x %04x %04x %04x %04x %04x %04x %04x %04x %04x %04x %04x %04x\n",
331 	  AF, BC, DE, HL, AF_, BC_, DE_, HL_, IX, IY, SP, PC, z80.memptr.w );
332   printf( "%02x %02x %d %d %d %d %d\n", I, ( R7 & 0x80 ) | ( R & 0x7f ),
333 	  IFF1, IFF2, IM, z80.halted, tstates );
334 }
335 
336 static void
dump_memory_state(void)337 dump_memory_state( void )
338 {
339   size_t i;
340 
341   for( i = 0; i < 0x10000; i++ ) {
342 
343     if( memory[ i ] == initial_memory[ i ] ) continue;
344 
345     printf( "%04x ", (unsigned)i );
346 
347     while( i < 0x10000 && memory[ i ] != initial_memory[ i ] )
348       printf( "%02x ", memory[ i++ ] );
349 
350     printf( "-1\n" );
351   }
352 }
353 
354 /* Error 'handing': dump core as these should never be called */
355 
356 void
fuse_abort(void)357 fuse_abort( void )
358 {
359   abort();
360 }
361 
362 int
ui_error(ui_error_level severity GCC_UNUSED,const char * format,...)363 ui_error( ui_error_level severity GCC_UNUSED, const char *format, ... )
364 {
365   va_list ap;
366 
367   va_start( ap, format );
368   vfprintf( stderr, format, ap );
369   va_end( ap );
370 
371   abort();
372 }
373 
374 /*
375  * Stuff below here not interesting: dummy functions and variables to replace
376  * things used by Fuse, but not by the core test code
377  */
378 
379 #include "debugger/debugger.h"
380 #include "machine.h"
381 #include "peripherals/scld.h"
382 #include "settings.h"
383 
384 libspectrum_byte *slt[256];
385 size_t slt_length[256];
386 
387 int
tape_load_trap(void)388 tape_load_trap( void )
389 {
390   /* Should never be called */
391   abort();
392 }
393 
394 int
tape_save_trap(void)395 tape_save_trap( void )
396 {
397   /* Should never be called */
398   abort();
399 }
400 
401 scld scld_last_dec;
402 
403 size_t rzx_instruction_count;
404 int rzx_playback;
405 int rzx_instructions_offset;
406 
407 enum debugger_mode_t debugger_mode;
408 
409 libspectrum_byte **ROM = NULL;
410 memory_page memory_map[8];
411 memory_page *memory_map_home[MEMORY_PAGES_IN_64K];
412 memory_page memory_map_rom[SPECTRUM_ROM_PAGES * MEMORY_PAGES_IN_16K];
413 int memory_contended[8] = { 1 };
414 libspectrum_byte spectrum_contention[ 80000 ] = { 0 };
415 int profile_active = 0;
416 
417 void
profile_map(libspectrum_word pc GCC_UNUSED)418 profile_map( libspectrum_word pc GCC_UNUSED )
419 {
420   abort();
421 }
422 
423 int
debugger_check(debugger_breakpoint_type type GCC_UNUSED,libspectrum_dword value GCC_UNUSED)424 debugger_check( debugger_breakpoint_type type GCC_UNUSED, libspectrum_dword value GCC_UNUSED )
425 {
426   abort();
427 }
428 
debugger_system_variable_register(const char * type,const char * detail,debugger_get_system_variable_fn_t get,debugger_set_system_variable_fn_t set)429 void debugger_system_variable_register(
430   const char *type, const char *detail,
431   debugger_get_system_variable_fn_t get,
432   debugger_set_system_variable_fn_t set )
433 {
434 }
435 
436 int
debugger_trap(void)437 debugger_trap( void )
438 {
439   abort();
440 }
441 
442 int
slt_trap(libspectrum_word address GCC_UNUSED,libspectrum_byte level GCC_UNUSED)443 slt_trap( libspectrum_word address GCC_UNUSED, libspectrum_byte level GCC_UNUSED )
444 {
445   return 0;
446 }
447 
448 int beta_available = 0;
449 int beta_active = 0;
450 int if1_available = 0;
451 
452 void
beta_page(void)453 beta_page( void )
454 {
455   abort();
456 }
457 
458 void
beta_unpage(void)459 beta_unpage( void )
460 {
461   abort();
462 }
463 
464 int spectrum_frame_event = 0;
465 
466 int
event_register(event_fn_t fn GCC_UNUSED,const char * string GCC_UNUSED)467 event_register( event_fn_t fn GCC_UNUSED, const char *string GCC_UNUSED )
468 {
469   return 0;
470 }
471 
472 int opus_available = 0;
473 int opus_active = 0;
474 
475 void
opus_page(void)476 opus_page( void )
477 {
478   abort();
479 }
480 
481 void
opus_unpage(void)482 opus_unpage( void )
483 {
484   abort();
485 }
486 
487 int plusd_available = 0;
488 int plusd_active = 0;
489 
490 void
plusd_page(void)491 plusd_page( void )
492 {
493   abort();
494 }
495 
496 int disciple_available = 0;
497 int disciple_active = 0;
498 
499 void
disciple_page(void)500 disciple_page( void )
501 {
502   abort();
503 }
504 
505 int didaktik80_available = 0;
506 int didaktik80_active = 0;
507 int didaktik80_snap = 0;
508 
509 void
didaktik80_page(void)510 didaktik80_page( void )
511 {
512   abort();
513 }
514 
515 void
didaktik80_unpage(void)516 didaktik80_unpage( void )
517 {
518   abort();
519 }
520 
521 int usource_available = 0;
522 int usource_active = 0;
523 
524 void
usource_toggle(void)525 usource_toggle( void )
526 {
527   abort();
528 }
529 
530 void
if1_page(void)531 if1_page( void )
532 {
533   abort();
534 }
535 
536 void
if1_unpage(void)537 if1_unpage( void )
538 {
539   abort();
540 }
541 
542 int multiface_activated = 0;
543 
544 void
multiface_setic8(void)545 multiface_setic8( void )
546 {
547   abort();
548 }
549 
550 void
divide_set_automap(int state GCC_UNUSED)551 divide_set_automap( int state GCC_UNUSED )
552 {
553   abort();
554 }
555 
556 void
divmmc_set_automap(int state GCC_UNUSED)557 divmmc_set_automap( int state GCC_UNUSED )
558 {
559   abort();
560 }
561 
562 int spectranet_available = 0;
563 
564 void
spectranet_page(int via_io GCC_UNUSED)565 spectranet_page( int via_io GCC_UNUSED )
566 {
567   abort();
568 }
569 
570 void
spectranet_nmi(void)571 spectranet_nmi( void )
572 {
573   abort();
574 }
575 
576 void
spectranet_unpage(void)577 spectranet_unpage( void )
578 {
579   abort();
580 }
581 
582 void
spectranet_retn(void)583 spectranet_retn( void )
584 {
585 }
586 
587 int
spectranet_nmi_flipflop(void)588 spectranet_nmi_flipflop( void )
589 {
590   return 0;
591 }
592 
593 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)594 startup_manager_register( startup_manager_module module,
595   startup_manager_module *dependencies, size_t dependency_count,
596   startup_manager_init_fn init_fn, void *init_context,
597   startup_manager_end_fn end_fn )
598 {
599 }
600 
601 int svg_capture_active = 0;     /* SVG capture enabled? */
602 
603 void
svg_capture(void)604 svg_capture( void )
605 {
606   abort();
607 }
608 
609 int
rzx_frame(void)610 rzx_frame( void )
611 {
612   abort();
613 }
614 
615 void
writeport_internal(libspectrum_word port GCC_UNUSED,libspectrum_byte b GCC_UNUSED)616 writeport_internal( libspectrum_word port GCC_UNUSED, libspectrum_byte b GCC_UNUSED )
617 {
618   abort();
619 }
620 
621 void
event_add_with_data(libspectrum_dword event_time GCC_UNUSED,int type GCC_UNUSED,void * user_data GCC_UNUSED)622 event_add_with_data( libspectrum_dword event_time GCC_UNUSED,
623 		     int type GCC_UNUSED, void *user_data GCC_UNUSED )
624 {
625   /* Do nothing */
626 }
627 
628 int
module_register(module_info_t * module GCC_UNUSED)629 module_register( module_info_t *module GCC_UNUSED )
630 {
631   return 0;
632 }
633 
634 void
z80_debugger_variables_init(void)635 z80_debugger_variables_init( void )
636 {
637 }
638 
639 fuse_machine_info *machine_current;
640 static fuse_machine_info dummy_machine;
641 
642 settings_info settings_current;
643 
644 libspectrum_word beta_pc_mask;
645 libspectrum_word beta_pc_value;
646 
647 int spectranet_programmable_trap_active;
648 libspectrum_word spectranet_programmable_trap;
649 
650 /* Initialise the dummy variables such that we're running on a clean a
651    machine as possible */
652 static int
init_dummies(void)653 init_dummies( void )
654 {
655   size_t i;
656 
657   for( i = 0; i < 8; i++ ) {
658     memory_map[i].page = &memory[ i * MEMORY_PAGE_SIZE ];
659   }
660 
661   debugger_mode = DEBUGGER_MODE_INACTIVE;
662   dummy_machine.capabilities = 0;
663   dummy_machine.ram.current_rom = 0;
664   machine_current = &dummy_machine;
665   rzx_playback = 0;
666   scld_last_dec.name.intdisable = 0;
667   settings_current.slt_traps = 0;
668   settings_current.divide_enabled = 0;
669   settings_current.divmmc_enabled = 0;
670   settings_current.z80_is_cmos = 0;
671   beta_pc_mask = 0xfe00;
672   beta_pc_value = 0x3c00;
673   spectranet_programmable_trap_active = 0;
674   spectranet_programmable_trap = 0x0000;
675 
676   return 0;
677 }
678