/* output the last line printed as text. */ static void printer_zxp_output_as_text(void) { static unsigned char charset[256*8]; static unsigned char outbuf[32]; unsigned char *ptr; int x,y,f,c,chars; #define SYSV_CHARS 0x5c36 chars=readbyte_internal(SYSV_CHARS); chars+=256*readbyte_internal(SYSV_CHARS+1); memset(charset,0,sizeof(charset)); ptr=charset+32*8; for(f=32*8;f<128*8;f++) *ptr++=readbyte_internal(chars+f); for(x=0;x<32;x++) { c=-1; /* try each char */ for(f=32;f<128 && c==-1;f++) { ptr=zxplast8+x; c=f; for(y=0;y<8;y++,ptr+=32) if(*ptr!=charset[f*8+y]) { c=-1; break; } } /* can't do UDGs, too unreliable */ if(c==-1) c=32; outbuf[x]=c; } for(f=31;f>=0 && outbuf[f]==32;f--) outbuf[f]=0; for(f=0;f<32 && outbuf[f];f++) printer_text_output_char(outbuf[f]); printer_text_output_char('\n'); }
int widget_memory_draw( void *data ) { int x, y; char pbuf[36]; widget_rectangle( LC(0), LR(0), 40 * 8, 16 * 8 + 4, 1 ); widget_rectangle( LC(0), LR(16) + 2, 320, 1, 7 ); for( y = 0; y < 16; ++y ) { libspectrum_word addr = memaddr + y * 8; sprintf( pbuf, "%04X:", addr ); widget_printstring_right( LC(5) - 4, LR(y), 5, pbuf ); for( x = 0; x < 8; ++x ) { libspectrum_byte b = readbyte_internal( addr + x ); widget_printchar_fixed( LC(x + 29) / 8, LR(y) / 8, 7 - (y & 1), b ); sprintf( pbuf + x * 3, "%02X ", b ); } widget_printstring_fixed( LC(5) / 8, LR(y) / 8, 7 - (y & 1), pbuf ); } widget_display_lines( LR(0) / 8, 17 ); return 0; }
libspectrum_byte readbyte( libspectrum_word address ) { printf( "%5d MC %04x\n", tstates, address ); tstates += 3; return readbyte_internal( address ); }
static void update_display( GtkCList *clist, libspectrum_word base ) { size_t i, j; gchar buffer[ 8 + 64 + 20 ]; gchar *text[] = { &buffer[0], &buffer[ 8 ], &buffer[ 8 + 64 ] }; char buffer2[ 8 ]; gtk_clist_freeze( clist ); gtk_clist_clear( clist ); for( i = 0; i < 20; i++ ) { snprintf( text[0], 8, "%04X", base ); text[1][0] = '\0'; for( j = 0; j < 0x10; j++, base++ ) { libspectrum_byte b = readbyte_internal( base ); snprintf( buffer2, 4, "%02X ", b ); strncat( text[1], buffer2, 4 ); text[2][j] = ( b >= 32 && b < 127 ) ? b : '.'; } text[2][ 0x10 ] = '\0'; gtk_clist_append( clist, text ); } gtk_clist_thaw( clist ); }
/* Exit from the last CALL etc by setting a oneshot breakpoint at (SP) and then starting emulation */ int debugger_breakpoint_exit( void ) { libspectrum_word target; target = readbyte_internal( SP ) + 0x100 * readbyte_internal( SP+1 ); if( debugger_breakpoint_add_address( DEBUGGER_BREAKPOINT_TYPE_EXECUTE, memory_source_any, 0, target, 0, DEBUGGER_BREAKPOINT_LIFE_ONESHOT, NULL ) ) return 1; if( debugger_run() ) return 1; return 0; }
/* Append to the current tape file in memory; returns 0 if a block was saved or non-zero if there was an error at the emulator level, or tape traps are not active */ int tape_save_trap( void ) { libspectrum_tape_block *block; libspectrum_byte parity, *data; size_t length; int i; /* Do nothing if tape traps aren't active */ if( !settings_current.tape_traps || tape_recording ) return 2; /* Check we're in the right ROM */ if( ! trap_check_rom() ) return 3; block = libspectrum_tape_block_alloc( LIBSPECTRUM_TAPE_BLOCK_ROM ); /* The +2 here is for the flag and parity bytes */ length = DE + 2; libspectrum_tape_block_set_data_length( block, length ); data = malloc( length * sizeof(libspectrum_byte) ); if( !data ) { free( block ); return 1; } libspectrum_tape_block_set_data( block, data ); /* First, store the flag byte (and initialise the parity counter) */ data[0] = parity = A; /* then the main body of the data, counting parity along the way */ for( i=0; i<DE; i++) { libspectrum_byte b = readbyte_internal( IX+i ); parity ^= b; data[i+1] = b; } /* And finally the parity byte */ data[ DE+1 ] = parity; /* Give a 1 second pause after this block */ libspectrum_tape_block_set_pause( block, 1000 ); libspectrum_tape_append_block( tape, block ); tape_modified = 1; ui_tape_browser_update( UI_TAPE_BROWSER_NEW_BLOCK, block ); /* And then return via the RET at #053E, except on Timex 2068 at #00E4 */ if ( machine_current->machine == LIBSPECTRUM_MACHINE_TC2068 || machine_current->machine == LIBSPECTRUM_MACHINE_TS2068 ) { PC = 0x00e4; } else { PC = 0x053e; } return 0; }
static void do_acceleration( void ) { if( length_known1 ) { int set_b_high = length_long1; set_b_high ^= ( acceleration_mode == ACCELERATION_MODE_DECREASING ); if( set_b_high ) { z80.bc.b.h = 0xfe; } else { z80.bc.b.h = 0x00; } z80.af.b.l |= 0x01; z80.pc.b.l = readbyte_internal( z80.sp.w ); z80.sp.w++; z80.pc.b.h = readbyte_internal( z80.sp.w ); z80.sp.w++; event_remove_type( tape_edge_event ); tape_next_edge( tstates, 0, NULL ); successive_reads = 0; } length_known1 = length_known2; length_long1 = length_long2; }
static void update_display( HWND hwndDlg, libspectrum_word base ) { size_t i, j; TCHAR buffer[ 8 + 64 + 20 ]; TCHAR *text[] = { &buffer[0], &buffer[ 8 ], &buffer[ 8 + 64 ] }; TCHAR buffer2[ 8 ]; SendDlgItemMessage( hwndDlg, IDC_MEM_LV, LVM_DELETEALLITEMS, 0, 0 ); LV_ITEM lvi; lvi.mask = LVIF_TEXT; for( i = 0; i < 20; i++ ) { _sntprintf( text[0], 8, TEXT( "%04X" ), base ); text[1][0] = '\0'; for( j = 0; j < 0x10; j++, base++ ) { libspectrum_byte b = readbyte_internal( base ); _sntprintf( buffer2, 4, TEXT( "%02X " ), b ); _tcsncat( text[1], buffer2, 4 ); text[2][j] = ( b >= 32 && b < 127 ) ? b : '.'; } text[2][ 0x10 ] = '\0'; /* append the item */ lvi.iItem = SendDlgItemMessage( hwndDlg, IDC_MEM_LV, LVM_GETITEMCOUNT, 0, 0 ); lvi.iSubItem = 0; lvi.pszText = text[0]; SendDlgItemMessage( hwndDlg, IDC_MEM_LV, LVM_INSERTITEM, 0, ( LPARAM ) &lvi ); lvi.iSubItem = 1; lvi.pszText = text[1]; SendDlgItemMessage( hwndDlg, IDC_MEM_LV, LVM_SETITEM, 0, ( LPARAM ) &lvi ); lvi.iSubItem = 2; lvi.pszText = text[2]; SendDlgItemMessage( hwndDlg, IDC_MEM_LV, LVM_SETITEM, 0, ( LPARAM ) &lvi ); } }
static void update_display( GtkTreeModel *model, libspectrum_word base ) { size_t i, j; GtkTreeIter iter; gchar buffer[ 8 + 64 + 20 ]; gchar *text[] = { &buffer[0], &buffer[ 8 ], &buffer[ 8 + 64 ] }; char buffer2[ 8 ]; memaddr = base; gtk_list_store_clear( GTK_LIST_STORE( model ) ); for( i = 0; i < 20; i++ ) { snprintf( text[0], 8, "%04X", base ); text[1][0] = '\0'; for( j = 0; j < 0x10; j++, base++ ) { libspectrum_byte b = readbyte_internal( base ); snprintf( buffer2, 4, "%02X ", b ); strncat( text[1], buffer2, 4 ); text[2][j] = ( b >= 32 && b < 127 ) ? b : '.'; } text[2][ 0x10 ] = '\0'; /* Append a new row and fill data */ gtk_list_store_append( GTK_LIST_STORE( model ), &iter ); gtk_list_store_set( GTK_LIST_STORE( model ), &iter, COL_ADDRESS, text[0], COL_HEX, text[1], COL_DATA, text[2], -1 ); } }
static acceleration_mode_t acceleration_detector( libspectrum_word pc ) { int state = 0, count = 0; while( 1 ) { libspectrum_byte b = readbyte_internal( pc ); pc++; count++; switch( state ) { case 0: switch( b ) { case 0x04: state = 1; break; /* INC B - Many loaders */ default: state = 13; break; /* Possible Digital Integration */ } break; case 1: switch( b ) { case 0xc8: state = 2; break; /* RET Z */ default: return ACCELERATION_MODE_NONE; } break; case 2: switch( b ) { case 0x3e: state = 3; break; /* LD A,nn */ default: return ACCELERATION_MODE_NONE; } break; case 3: switch( b ) { case 0x00: /* Search Loader */ case 0x7f: /* ROM loader and variants */ state = 4; break; /* Data byte */ default: return ACCELERATION_MODE_NONE; } break; case 4: switch( b ) { case 0xdb: state = 5; break; /* IN A,(nn) */ default: return ACCELERATION_MODE_NONE; } break; case 5: switch( b ) { case 0xfe: state = 6; break; /* Data byte */ default: return ACCELERATION_MODE_NONE; } break; case 6: switch( b ) { case 0x1f: state = 7; break; /* RRA */ case 0xa9: state = 24; break; /* XOR C - Search Loader */ default: return ACCELERATION_MODE_NONE; } break; case 7: switch( b ) { case 0x00: /* NOP - Bleepload */ case 0xa7: /* AND A - Microsphere */ case 0xc8: /* RET Z - Paul Owens */ case 0xd0: /* RET NC - ROM loader */ state = 8; break; case 0xa9: state = 9; break; /* XOR C - Speedlock */ default: return ACCELERATION_MODE_NONE; } break; case 8: switch( b ) { case 0xa9: state = 9; break; /* XOR C */ default: return ACCELERATION_MODE_NONE; } break; case 9: switch( b ) { case 0xe6: state = 10; break; /* AND nn */ default: return ACCELERATION_MODE_NONE; } break; case 10: switch( b ) { case 0x20: state = 11; break; /* Data byte */ default: return ACCELERATION_MODE_NONE; } break; case 11: switch( b ) { case 0x28: state = 12; break; /* JR nn */ default: return ACCELERATION_MODE_NONE; } break; case 12: if( b == 0x100 - count ) { return ACCELERATION_MODE_INCREASING; } else { return ACCELERATION_MODE_NONE; } break; /* Digital Integration loader */ case 13: state = 14; break; /* Possible Digital Integration */ case 14: switch( b ) { case 0x05: state = 15; break; /* DEC B - Digital Integration */ default: return ACCELERATION_MODE_NONE; } break; case 15: switch( b ) { case 0xc8: state = 16; break; /* RET Z */ default: return ACCELERATION_MODE_NONE; } break; case 16: switch( b ) { case 0xdb: state = 17; break; /* IN A,(nn) */ default: return ACCELERATION_MODE_NONE; } break; case 17: switch( b ) { case 0xfe: state = 18; break; /* Data byte */ default: return ACCELERATION_MODE_NONE; } break; case 18: switch( b ) { case 0xa9: state = 19; break; /* XOR C */ default: return ACCELERATION_MODE_NONE; } break; case 19: switch( b ) { case 0xe6: state = 20; break; /* AND nn */ default: return ACCELERATION_MODE_NONE; } break; case 20: switch( b ) { case 0x40: state = 21; break; /* Data byte */ default: return ACCELERATION_MODE_NONE; } break; case 21: switch( b ) { case 0xca: state = 22; break; /* JP Z,nnnn */ default: return ACCELERATION_MODE_NONE; } break; case 22: /* LSB of jump target */ if( b == ( z80.pc.w - 4 ) % 0x100 ) { state = 23; } else { return ACCELERATION_MODE_NONE; } break; case 23: /* MSB of jump target */ if( b == ( z80.pc.w - 4 ) / 0x100 ) { return ACCELERATION_MODE_DECREASING; } else { return ACCELERATION_MODE_NONE; } /* Search loader */ case 24: switch( b ) { case 0xe6: state = 25; break; /* AND nn */ default: return ACCELERATION_MODE_NONE; } break; case 25: switch( b ) { case 0x40: state = 26; break; /* Data byte */ default: return ACCELERATION_MODE_NONE; } break; case 26: switch( b ) { case 0xd8: state = 27; break; /* RET C */ default: return ACCELERATION_MODE_NONE; } break; case 27: switch( b ) { case 0x00: state = 11; break; /* NOP */ default: return ACCELERATION_MODE_NONE; } break; default: /* Can't happen */ break; } } }
static int trap_load_block( libspectrum_tape_block *block ) { libspectrum_byte parity, *data; int i = 0, length, read, verify; /* On exit: * A = calculated parity byte if parity checked, else 0 (CHECKME) * F : if parity checked, all flags are modified * else carry only is modified (FIXME) * B = 0xB0 (success) or 0x00 (failure) * C = 0x01 (confirmed), 0x21, 0xFE or 0xDE (CHECKME) * DE : decremented by number of bytes loaded or verified * H = calculated parity byte or undefined * L = last byte read, or 1 if none * IX : incremented by number of bytes loaded or verified * A' = unchanged on error + no flag byte, else 0x01 * F' = 0x01 on error + no flag byte, else 0x45 * R = no point in altering it :-) * Other registers unchanged. */ data = libspectrum_tape_block_data( block ); length = libspectrum_tape_block_data_length( block ); /* Number of bytes to load or verify */ read = length - 1; if( read > DE ) read = DE; /* If there's no data in the block (!), set L then error exit. * We don't need to alter H, IX or DE here */ if( !length ) { L = F_ = 1; F &= ~FLAG_C; return 0; } verify = !(F_ & FLAG_C); i = A_; /* i = A' (flag byte) */ AF_ = 0x0145; A = 0; /* Initialise the parity check and L to the block ID byte */ L = parity = *data++; /* If the block ID byte != the flag byte, clear carry and return */ if( parity != i ) goto error_ret; /* Now set L to the *last* byte in the block */ L = data[read - 1]; /* Loading or verifying determined by the carry flag of F' */ if( verify ) { /* verifying */ for( i = 0; i < read; i++ ) { parity ^= data[i]; if( data[i] != readbyte_internal(IX+i) ) { /* Verification failure */ L = data[i]; goto error_ret; } } } else { for( i = 0; i < read; i++ ) { parity ^= data[i]; writebyte_internal( IX+i, data[i] ); } } /* At this point, i == number of bytes actually read or verified */ /* If |DE| bytes have been read and there's more data, do the parity check */ if( DE == i && read + 1 < length ) { parity ^= data[read]; A = parity; CP( 1 ); /* parity check is successful if A==0 */ B = 0xB0; } else { /* Failure to read first bit of the next byte (ref. 48K ROM, 0x5EC) */ B = 255; L = 1; INC( B ); error_ret: F &= ~FLAG_C; } /* At this point, AF, AF', B and L are already modified */ C = 1; H = parity; DE -= i; IX += i; return 0; }
/* Execute Z80 opcodes until the next event */ void z80_do_opcodes( void ) { #ifdef HAVE_ENOUGH_MEMORY libspectrum_byte opcode = 0x00; #endif int even_m1 = machine_current->capabilities & LIBSPECTRUM_MACHINE_CAPABILITY_EVEN_M1; #ifdef __GNUC__ #undef SETUP_CHECK #define SETUP_CHECK( label, condition ) \ if( condition ) { cgoto[ next ] = &&label; next = pos_##label + 1; } \ check++; #undef SETUP_NEXT #define SETUP_NEXT( label ) \ if( next != check ) { cgoto[ next ] = &&label; } \ next = check; void *cgoto[ numchecks ]; size_t next = 0; size_t check = 0; #include "z80_checks.h" #endif /* #ifdef __GNUC__ */ while( tstates < event_next_event ) { /* Profiler */ CHECK( profile, profile_active ) profile_map( PC ); END_CHECK /* If we're due an end of frame from RZX playback, generate one */ CHECK( rzx, rzx_playback ) if( R + rzx_instructions_offset >= rzx_instruction_count ) { event_add( tstates, spectrum_frame_event ); break; /* And break out of the execution loop to let the interrupt happen */ } END_CHECK /* Check if the debugger should become active at this point */ CHECK( debugger, debugger_mode != DEBUGGER_MODE_INACTIVE ) if( !z80.halted && debugger_check( DEBUGGER_BREAKPOINT_TYPE_EXECUTE, PC ) ) debugger_trap(); END_CHECK CHECK( beta, beta_available ) #define NOT_128_TYPE_OR_IS_48_TYPE ( !( machine_current->capabilities & \ LIBSPECTRUM_MACHINE_CAPABILITY_128_MEMORY ) || \ machine_current->ram.current_rom ) if( beta_active ) { if( NOT_128_TYPE_OR_IS_48_TYPE && PC >= 16384 ) { beta_unpage(); } } else if( ( PC & beta_pc_mask ) == beta_pc_value && NOT_128_TYPE_OR_IS_48_TYPE ) { beta_page(); } END_CHECK CHECK( plusd, plusd_available ) if( PC == 0x0008 || PC == 0x003a || PC == 0x0066 || PC == 0x028e ) { plusd_page(); } END_CHECK CHECK( didaktik80, didaktik80_available ) if( PC == 0x0000 || PC == 0x0008 ) { didaktik80_page(); } else if( PC == 0x1700 ) { didaktik80_unpage(); } END_CHECK CHECK( disciple, disciple_available ) if( PC == 0x0001 || PC == 0x0008 || PC == 0x0066 || PC == 0x028e ) { disciple_page(); } END_CHECK CHECK( usource, usource_available ) if( PC == 0x2bae ) { usource_toggle(); } END_CHECK CHECK( if1p, if1_available ) if( PC == 0x0008 || PC == 0x1708 ) { if1_page(); } END_CHECK CHECK( divide_early, settings_current.divide_enabled ) if( ( PC & 0xff00 ) == 0x3d00 ) { divide_set_automap( 1 ); } END_CHECK CHECK( spectranet_page, spectranet_available && !settings_current.spectranet_disable ) if( PC == 0x0008 || ((PC & 0xfff8) == 0x3ff8) ) spectranet_page( 0 ); if( PC == spectranet_programmable_trap && spectranet_programmable_trap_active ) event_add( 0, z80_nmi_event ); END_CHECK opcode_delay: contend_read( PC, 4 ); /* Check to see if M1 cycles happen on even tstates */ CHECK( evenm1, even_m1 ) if( tstates & 1 ) { if( ++tstates == event_next_event ) { break; } } END_CHECK run_opcode: /* Do the instruction fetch; readbyte_internal used here to avoid triggering read breakpoints */ opcode = readbyte_internal( PC ); CHECK( if1u, if1_available ) if( PC == 0x0700 ) { if1_unpage(); } END_CHECK CHECK( divide_late, settings_current.divide_enabled ) if( ( PC & 0xfff8 ) == 0x1ff8 ) { divide_set_automap( 0 ); } else if( (PC == 0x0000) || (PC == 0x0008) || (PC == 0x0038) || (PC == 0x0066) || (PC == 0x04c6) || (PC == 0x0562) ) { divide_set_automap( 1 ); } END_CHECK CHECK( opus, opus_available ) if( opus_active ) { if( PC == 0x1748 ) { opus_unpage(); } } else if( PC == 0x0008 || PC == 0x0048 || PC == 0x1708 ) { opus_page(); } END_CHECK CHECK( spectranet_unpage, spectranet_available ) if( PC == 0x007c ) spectranet_unpage(); END_CHECK CHECK( z80_halted, z80.halted ) /* Opcode read from memory is ignored and PC is left unchanged */ R++; continue; END_CHECK CHECK( z80_iff2_read, z80.iff2_read ) z80.iff2_read = 0; /* Execute *one* instruction before reevaluating the checks */ event_add( tstates, z80_nmos_iff2_event ); END_CHECK CHECK( didaktik80snap, didaktik80_snap ) if( PC == 0x0066 && !didaktik80_active ) { opcode = 0xc7; /* RST 00 */ didaktik80_snap = 0; /* FIXME: this should be a time-based reset */ } END_CHECK CHECK( svg_capture, svg_capture_active ) svg_capture(); END_CHECK end_opcode: PC++; R++; switch(opcode) { #include "z80/opcodes_base.c" } } }