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