static int wd_fdc_spinup( wd_fdc *f, libspectrum_byte b ) { libspectrum_dword delay = 0; fdd_t *d = f->current_drive; if( f->state != WD_FDC_STATE_SEEK && ( b & 0x04 ) ) delay = 30; if( f->type == WD1770 || f->type == WD1772 ) { if( !( f->status_register & WD_FDC_SR_MOTORON ) ) { f->status_register |= WD_FDC_SR_MOTORON; fdd_motoron( d, 1 ); if( !( b & 0x08 ) ) delay += 6 * 200; } } else { /* WD1773/FD1793/WD2797 */ event_remove_type( motor_off_event ); if( f->state == WD_FDC_STATE_SEEK ) { if( b & 0x08 ) { f->head_load = 1; if( f->flags & WD_FLAG_BETA128 ) fdd_motoron( d, 1 ); else fdd_head_load( d, 1 ); } else if( !( b & 0x04 ) ) { /* HLD reset only if V flag == 0 too */ f->head_load = 0; if( !( f->flags & WD_FLAG_NOHLT ) && f->hlt_time > 0 ) f->hlt = 0; /* reset the trigger */ if( f->flags & WD_FLAG_BETA128 ) fdd_motoron( d, 0 ); else fdd_head_load( d, 0 ); } return 0; } else { f->head_load = 1; if( f->flags & WD_FLAG_BETA128 ) fdd_motoron( d, 1 ); else fdd_head_load( d, 1 ); if( f->hlt_time > 0 ) delay += f->hlt_time; } } /* For Type III commands on WD2797 */ if( f->type == WD2797 && ( b & 0xc0 ) == 0xc0 && ( b & 0x30 ) != 0x10 ) fdd_set_head( d, b & 0x02 ? 1 : 0 ); if( delay ) { event_remove_type( fdc_event ); event_add_with_data( tstates + delay * machine_current->timings.processor_speed / 1000, fdc_event, f ); return 1; } return 0; }
static int wd_fdc_spinup( wd_fdc *f, libspectrum_byte b ) { libspectrum_dword delay = 0; if( f->state != WD_FDC_STATE_SEEK && ( b & 0x04 ) ) delay = 30; if( f->type == WD1770 || f->type == WD1772 ) { if( !( b & 0x08 ) && !( f->status_register & WD_FDC_SR_MOTORON ) ) { f->status_register |= WD_FDC_SR_MOTORON; fdd_motoron( &f->current_drive->fdd, 1 ); statusbar_update( 1 ); delay += 6 * 200; } } else { /* WD1773/FD1793 */ event_remove_type( motor_off_event ); if( f->state == WD_FDC_STATE_SEEK ) { if( b & 0x08 ) { f->head_load = 1; if( f->flags & WD_FLAG_BETA128 ) fdd_motoron( &f->current_drive->fdd, 1 ); else fdd_head_load( &f->current_drive->fdd, 1 ); statusbar_update( 1 ); } else { f->head_load = 0; if( f->hlt_time > 0 ) f->hlt = 0; /* reset the trigger */ if( f->flags & WD_FLAG_BETA128 ) fdd_motoron( &f->current_drive->fdd, 0 ); else fdd_head_load( &f->current_drive->fdd, 0 ); statusbar_update( 0 ); } return 0; } else { f->head_load = 1; if( f->flags & WD_FLAG_BETA128 ) fdd_motoron( &f->current_drive->fdd, 1 ); else fdd_head_load( &f->current_drive->fdd, 1 ); statusbar_update( 1 ); if( f->hlt_time > 0 ) delay += f->hlt_time; } } if( delay ) { event_remove_type( fdc_event ); event_add_with_data( tstates + delay * machine_current->timings.processor_speed / 1000, fdc_event, f ); return 1; } return 0; }
static int tape_play( int autoplay ) { if( !libspectrum_tape_present( tape ) ) return 1; /* Otherwise, start the tape going */ tape_playing = 1; tape_autoplay = autoplay; tape_microphone = 0; event_remove_type( tape_mic_off_event ); /* Update the status bar */ ui_statusbar_update( UI_STATUSBAR_ITEM_TAPE, UI_STATUSBAR_STATE_ACTIVE ); /* If we're fastloading, turn sound off */ if( settings_current.fastload ) sound_pause(); loader_tape_play(); event_add( tstates + next_tape_edge_tstates, tape_edge_event ); next_tape_edge_tstates = 0; debugger_event( play_event ); return 0; }
static void head_load( upd_fdc *f ) { event_remove_type( head_event ); if( f->head_load ) { /* head already loaded */ if( f->cmd->id == UPD_CMD_READ_DATA || f->cmd->id == UPD_CMD_SCAN ) start_read_data( f ); else if( f->cmd->id == UPD_CMD_READ_ID ) start_read_id( f ); else if( f->cmd->id == UPD_CMD_READ_DIAG ) { fdd_wait_index_hole( f->current_drive ); /* start reading from index hole */ start_read_diag( f ); } else if( f->cmd->id == UPD_CMD_WRITE_DATA ) start_write_data( f ); else if( f->cmd->id == UPD_CMD_WRITE_ID ) { fdd_wait_index_hole( f->current_drive ); /* start writing from index hole */ start_write_id( f ); } } else { fdd_head_load( f->current_drive, 1 ); f->head_load = 1; event_add_with_data( tstates + f->hld_time * machine_current->timings.processor_speed / 1000, fdc_event, f ); } }
int tape_stop( void ) { if( tape_playing ) { tape_playing = 0; ui_statusbar_update( UI_STATUSBAR_ITEM_TAPE, UI_STATUSBAR_STATE_INACTIVE ); loader_tape_stop(); /* If we were fastloading, sound was off, so turn it back on, and reset the speed counter */ if( settings_current.fastload ) { sound_unpause(); timer_estimate_reset(); } tape_save_next_edge(); event_remove_type( tape_edge_event ); /* Turn off any lingering MIC level in a second (some loaders like Alkatraz seem to check the MIC level soon after loading is finished, presumably as a copy protection check */ event_add( tstates + machine_current->timings.tstates_per_frame, tape_mic_off_event ); } if( stop_event != -1 ) debugger_event( stop_event ); return 0; }
static int playback_frame( void ) { int error, finished; libspectrum_snap *snap; error = libspectrum_rzx_playback_frame( rzx, &finished, &snap ); if( error ) return rzx_stop_playback( 0 ); if( finished ) { ui_error( UI_ERROR_INFO, "Finished RZX playback" ); return rzx_stop_playback( 0 ); } /* Move the RZX sentinel back out to 79000 tstates; the addition of the frame length is because everything is reduced by that in event_frame() */ event_remove_type( sentinel_event ); event_add( RZX_SENTINEL_TIME + tstates, sentinel_event ); if( snap ) { error = snapshot_copy_from( snap ); if( error ) return rzx_stop_playback( 0 ); } /* If we've got another frame to do, fetch the new instruction count and continue */ rzx_instruction_count = libspectrum_rzx_instructions( rzx ); counter_reset(); return 0; }
static int start_playback( libspectrum_rzx *rzx ) { int error; libspectrum_snap *snap; error = libspectrum_rzx_start_playback( rzx, 0, &snap ); if( error ) return error; if( snap ) { error = snapshot_copy_from( snap ); if( error ) return error; } /* End of frame will now be generated by the RZX code */ event_remove_type( spectrum_frame_event ); /* Add a sentinel event to prevent tstates overrun (bug #1057471) */ event_add( RZX_SENTINEL_TIME, sentinel_event ); tstates = libspectrum_rzx_tstates( rzx ); rzx_instruction_count = libspectrum_rzx_instructions( rzx ); rzx_playback = 1; counter_reset(); ui_menu_activate( UI_MENU_ITEM_RECORDING, 1 ); ui_menu_activate( UI_MENU_ITEM_RECORDING_ROLLBACK, 0 ); return 0; }
static void start_read_diag( upd_fdc *f ) { int i; if( !f->read_id ) { f->rev = 2; f->read_id = 1; } if( f->rev ) { i = f->current_drive->disk.i >= f->current_drive->disk.bpt ? 0 : f->current_drive->disk.i; /* start position */ if( read_id( f ) != 2 ) f->rev = 0; i = f->current_drive->disk.bpt ? ( f->current_drive->disk.i - i ) * 200 / f->current_drive->disk.bpt : 200; if( i > 0 ) { event_add_with_data( tstates + i * /* i * 1/20 revolution */ machine_current->timings.processor_speed / 1000, fdc_event, f ); return; } } f->read_id = 0; if( f->id_mark == UPD_FDC_AM_NONE ) { f->status_register[0] |= UPD_FDC_ST0_INT_ABNORM; f->status_register[1] |= UPD_FDC_ST1_EOF_CYLINDER; goto abort_read_diag; } if( f->id_track != f->data_register[1] || f->id_sector != f->data_register[3] || f->data_register[2] != f->id_head ) { f->status_register[1] |= UPD_FDC_ST1_NO_DATA; } if( f->id_track != f->data_register[1] ) { /*FIXME UPD765 set it always? */ f->status_register[2] |= UPD_FDC_ST2_WRONG_CYLINDER; if( f->id_track == 0xff ) f->status_register[2] |= UPD_FDC_ST2_BAD_CYLINDER; } if( read_datamark( f ) > 0 ) { /* not found */ f->status_register[0] |= UPD_FDC_ST0_INT_ABNORM; goto abort_read_diag; } f->main_status |= UPD_FDC_MAIN_DATAREQ | UPD_FDC_MAIN_DATA_READ; f->data_offset = 0; event_remove_type( timeout_event ); event_add_with_data( tstates + 4 * /* 2 revolution: 2 * 200 / 1000 */ machine_current->timings.processor_speed / 10, timeout_event, f ); return; abort_read_diag: f->state = UPD_FDC_STATE_RES; /* end of execution phase */ f->cycle = f->cmd->res_length; f->main_status &= ~UPD_FDC_MAIN_EXECUTION; f->intrq = UPD_INTRQ_RESULT; cmd_result( f ); }
static void wd_fdc_seek_verify( wd_fdc *f ) { fdd_t *d = f->current_drive; event_remove_type( fdc_event ); if( f->type == WD1773 || f->type == FD1793 || f->type == WD2797 ) { if( !f->hlt ) { event_add_with_data( tstates + 5 * /* sample every 5 ms */ machine_current->timings.processor_speed / 1000, fdc_event, f ); return; } if( f->head_load ) f->status_register |= WD_FDC_SR_SPINUP; /* when set, it indicates head is loaded and enganged. This bit is logical "and" of HLD and "HLT" signals. */ } if( d->tr00 ) f->status_register |= WD_FDC_SR_LOST; else f->status_register &= ~WD_FDC_SR_LOST; f->rev = 5; f->id_mark = WD_FDC_AM_NONE; wd_fdc_seek_verify_read_id( f ); }
static void wd_fdc_seek_verify_read_id( wd_fdc *f ) { fdd_t *d = f->current_drive; int i; f->read_id = 1; event_remove_type( fdc_event ); if( f->id_mark == WD_FDC_AM_NONE ) { while( f->rev ) { i = d->disk.i >= d->disk.bpt ? 0 : d->disk.i; /* start position */ if( !read_id( f ) ) { if( f->id_track != f->track_register ) { f->status_register |= WD_FDC_SR_RNF; } } else f->id_mark = WD_FDC_AM_NONE; i = d->disk.bpt ? ( d->disk.i - i ) * 200 / d->disk.bpt : 200; if( i > 0 ) { event_add_with_data( tstates + i * /* i * 1/20 revolution */ machine_current->timings.processor_speed / 1000, fdc_event, f ); return; } else if( f->id_mark != WD_FDC_AM_NONE ) break; } if( f->id_mark == WD_FDC_AM_NONE ) f->status_register |= WD_FDC_SR_RNF; } f->state = WD_FDC_STATE_NONE; f->status_register &= ~WD_FDC_SR_BUSY; wd_fdc_set_intrq( f ); f->read_id = 0; }
static void wd_fdc_type_ii( wd_fdc *f ) { libspectrum_byte b = f->command_register; wd_fdc_drive *d = f->current_drive; event_remove_type( fdc_event ); if( f->type == WD1773 || f->type == FD1793 ) { if( !f->hlt ) { event_add_with_data( tstates + 5 * machine_current->timings.processor_speed / 1000, fdc_event, f ); return; } } if( f->state == WD_FDC_STATE_WRITE ) { if( d->fdd.wrprot ) { f->status_register |= WD_FDC_SR_WRPROT; f->status_register &= ~WD_FDC_SR_BUSY; f->state = WD_FDC_STATE_NONE; wd_fdc_set_intrq( f ); return; } f->status_register &= ~WD_FDC_SR_WRPROT; } f->data_multisector = b & 0x10 ? 1 : 0; f->rev = 5; f->id_mark = WD_FDC_AM_NONE; wd_fdc_type_ii_seek( f ); }
int rzx_stop_playback( int add_interrupt ) { libspectrum_error libspec_error; rzx_playback = 0; if( settings_current.movie_stop_after_rzx ) movie_stop(); ui_menu_activate( UI_MENU_ITEM_RECORDING, 0 ); ui_menu_activate( UI_MENU_ITEM_RECORDING_ROLLBACK, 0 ); event_remove_type( sentinel_event ); /* We've now finished with the RZX file, so add an end of frame event if we've been requested to do so; we don't if we just run out of frames, as this occurs just before a normal end of frame and everything works normally as rzx_playback is now zero again */ if( add_interrupt ) { event_add( machine_current->timings.tstates_per_frame, spectrum_frame_event ); /* We're no longer doing RZX playback, so tstates now be <= the normal frame count */ if( tstates > machine_current->timings.tstates_per_frame ) tstates = machine_current->timings.tstates_per_frame; } else { /* Ensure that tstates will be zero after it is reduced in spectrum_frame() */ tstates = machine_current->timings.tstates_per_frame; } libspec_error = libspectrum_rzx_free( rzx ); if( libspec_error != LIBSPECTRUM_ERROR_NONE ) return libspec_error; debugger_event( end_event ); return 0; }
int tape_stop( void ) { if( tape_playing ) { tape_playing = 0; ui_statusbar_update( UI_STATUSBAR_ITEM_TAPE, UI_STATUSBAR_STATE_INACTIVE ); loader_tape_stop(); /* If we were fastloading, sound was off, so turn it back on, and reset the speed counter */ if( settings_current.fastload ) { sound_unpause(); timer_estimate_reset(); } event_remove_type( tape_edge_event ); } if( stop_event != -1 ) debugger_event( stop_event ); return 0; }
static void cmd_result( upd_fdc *f ) { f->cycle = f->cmd->res_length; f->main_status &= ~UPD_FDC_MAIN_EXECUTION; f->main_status |= UPD_FDC_MAIN_DATAREQ; if( f->cycle > 0 ) { /* result state */ f->state = UPD_FDC_STATE_RES; f->intrq = UPD_INTRQ_RESULT; f->main_status |= UPD_FDC_MAIN_DATA_READ; } else { /* NO result state */ f->state = UPD_FDC_STATE_CMD; f->main_status &= ~UPD_FDC_MAIN_DATADIR; f->main_status &= ~UPD_FDC_MAIN_BUSY; } event_remove_type( timeout_event ); /* remove timeouts... */ if( f->head_load && f->cmd->id <= UPD_CMD_READ_ID ) { event_add_with_data( tstates + f->hut_time * machine_current->timings.processor_speed / 1000, head_event, f ); } }
int tape_record_stop( void ) { libspectrum_tape_block* block; /* put last sample into the recording buffer */ rec_state.tape_buffer_used = write_rec_buffer( rec_state.tape_buffer, rec_state.tape_buffer_used, rec_state.last_level_count ); /* stop scheduling events and turn buffer into a block and pop into the current tape */ event_remove_type( record_event ); block = libspectrum_tape_block_alloc( LIBSPECTRUM_TAPE_BLOCK_RLE_PULSE ); libspectrum_tape_block_set_scale( block, rec_state.tstates_per_sample ); libspectrum_tape_block_set_data_length( block, rec_state.tape_buffer_used ); libspectrum_tape_block_set_data( block, rec_state.tape_buffer ); libspectrum_tape_append_block( tape, block ); rec_state.tape_buffer = NULL; rec_state.tape_buffer_size = 0; rec_state.tape_buffer_used = 0; tape_modified = 1; ui_tape_browser_update( UI_TAPE_BROWSER_NEW_BLOCK, block ); tape_recording = 0; /* Also want to reenable other tape actions */ ui_menu_activate( UI_MENU_ITEM_TAPE_RECORDING, 0 ); 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; }
libspectrum_byte wd_fdc_dr_read( wd_fdc *f ) { fdd_t *d = f->current_drive; if( f->flags & WD_FLAG_DRQ && f->status_register & WD_FDC_SR_BUSY ) event_remove_type( fdc_event ); if( f->state == WD_FDC_STATE_READ ) { f->data_offset++; /* count read bytes */ fdd_read_data( d ); crc_add(f, d); /* read a byte */ if( d->data > 0xff ) { /* no data */ f->status_register |= WD_FDC_SR_RNF; f->status_register &= ~WD_FDC_SR_BUSY; f->status_type = WD_FDC_STATUS_TYPE2; f->state = WD_FDC_STATE_NONE; wd_fdc_set_intrq( f ); wd_fdc_reset_datarq( f ); } else { f->data_register = d->data; if( f->data_offset == f->sector_length ) { /* read the CRC */ fdd_read_data( d ); crc_add(f, d); fdd_read_data( d ); crc_add(f, d); /* FIXME: make this per-FDC */ event_remove_type( timeout_event ); /* clear the timeout */ if( f->crc == 0x0000 && f->data_multisector ) { f->sector_register++; f->rev = 5; wd_fdc_reset_datarq( f ); event_add_with_data( tstates + /* 5 revolutions: 5 * 200 / 1000 */ machine_current->timings.processor_speed, timeout_event, f ); event_add_with_data( tstates + 2 * /* 20 ms delay */ machine_current->timings.processor_speed / 100, fdc_event, f ); } else { f->status_register &= ~WD_FDC_SR_BUSY; if( f->crc == 0x0000 ) f->status_register &= ~WD_FDC_SR_CRCERR; else f->status_register |= WD_FDC_SR_CRCERR; f->status_type = WD_FDC_STATUS_TYPE2; f->state = WD_FDC_STATE_NONE; wd_fdc_set_intrq( f ); wd_fdc_reset_datarq( f ); } } } } else if( f->state == WD_FDC_STATE_READID ) { switch( f->data_offset ) { case 0: /* track */ f->data_register = f->id_track; break; case 1: /* head */ f->data_register = f->id_head; break; case 2: /* sector */ f->data_register = f->id_sector; break; case 3: /* length */ f->data_register = f->id_length; break; case 4: /* crc1 */ f->data_register = f->crc >> 8; break; case 5: /* crc2 */ f->sector_register = f->id_track; f->data_register = f->crc & 0xff; f->status_register &= ~WD_FDC_SR_BUSY; f->status_type = WD_FDC_STATUS_TYPE2; f->state = WD_FDC_STATE_NONE; event_remove_type( timeout_event ); /* clear the timeout */ wd_fdc_set_intrq( f ); wd_fdc_reset_datarq( f ); break; default: break; } f->data_offset++; } else if( f->state == WD_FDC_STATE_READTRACK ) {
static void start_write_data( upd_fdc *f ) { int i; fdd_t *d = f->current_drive; multi_track_next: if( f->first_rw || f->read_id || f->data_register[5] > f->data_register[3] ) { if( !f->read_id ) { if( !f->first_rw ) f->data_register[3]++; f->first_rw = 0; f->rev = 2; f->read_id = 1; } while( f->rev ) { i = f->current_drive->disk.i >= f->current_drive->disk.bpt ? 0 : f->current_drive->disk.i; /* start position */ if( seek_id( f ) == 0 ) f->rev = 0; else f->id_mark = UPD_FDC_AM_NONE; i = f->current_drive->disk.bpt ? ( f->current_drive->disk.i - i ) * 200 / f->current_drive->disk.bpt : 200; if( i > 0 ) { event_add_with_data( tstates + i * /* i * 1/20 revolution */ machine_current->timings.processor_speed / 1000, fdc_event, f ); return; } } f->read_id = 0; if( f->id_mark == UPD_FDC_AM_NONE ) { /* not found/crc error */ f->status_register[0] |= UPD_FDC_ST0_INT_ABNORM; goto abort_write_data; } for( i = 11; i > 0; i-- ) /* "delay" 11 GAP byte */ fdd_read_data( d ); if( f->mf ) /* MFM */ for( i = 11; i > 0; i-- ) /* "delay" another 11 GAP byte */ fdd_read_data( d ); d->data = 0x00; for( i = f->mf ? 12 : 6; i > 0; i-- ) /* write 6/12 zero */ fdd_write_data( d ); crc_preset( f ); if( f->mf ) { /* MFM */ d->data = 0xffa1; for( i = 3; i > 0; i-- ) { /* write 3 0xa1 with clock mark */ fdd_write_data( d ); crc_add( f, d ); } } d->data = ( f->del_data ? 0x00f8 : 0x00fb ) | ( f->mf ? 0x0000 : 0xff00 ); /* write data mark */ fdd_write_data( d ); crc_add( f, d ); } else { f->data_register[1]++; /* next track */ f->data_register[3] = 1; /* first sector */ if( f->mt ) { goto multi_track_next; } abort_write_data: f->state = UPD_FDC_STATE_RES; /* end of execution phase */ f->cycle = f->cmd->res_length; /* end of cylinder is set if: * 1. sector data is read completely * (i.e. no other errors occur like no data. * 2. sector being read is same specified by EOT * 3. terminal count is not received * note: in +3 uPD765 never got TC */ f->status_register[0] |= UPD_FDC_ST0_INT_ABNORM; f->status_register[1] |= UPD_FDC_ST1_EOF_CYLINDER; f->main_status &= ~UPD_FDC_MAIN_EXECUTION; f->intrq = UPD_INTRQ_RESULT; cmd_result( f ); return; } f->main_status |= UPD_FDC_MAIN_DATAREQ | UPD_FDC_MAIN_DATA_WRITE; f->data_offset = 0; event_remove_type( timeout_event ); event_add_with_data( tstates + 4 * /* 2 revolution: 2 * 200 / 1000 */ machine_current->timings.processor_speed / 10, timeout_event, f ); }
static void start_read_data( upd_fdc *f ) { int i; skip_deleted_sector: multi_track_next: if( f->first_rw || f->read_id || f->data_register[5] > f->data_register[3] ) { if( !f->read_id ) { if( !f->first_rw ) f->data_register[3]++; f->first_rw = 0; f->rev = 2; f->read_id = 1; } while( f->rev ) { i = f->current_drive->disk.i >= f->current_drive->disk.bpt ? 0 : f->current_drive->disk.i; /* start position */ if( seek_id( f ) == 0 ) f->rev = 0; else f->id_mark = UPD_FDC_AM_NONE; i = f->current_drive->disk.bpt ? ( f->current_drive->disk.i - i ) * 200 / f->current_drive->disk.bpt : 200; if( i > 0 ) { event_add_with_data( tstates + i * /* i * 1/20 revolution */ machine_current->timings.processor_speed / 1000, fdc_event, f ); return; } } f->read_id = 0; if( f->id_mark == UPD_FDC_AM_NONE ) { /* not found/crc error */ f->status_register[0] |= UPD_FDC_ST0_INT_ABNORM; goto abort_read_data; } if( read_datamark( f ) > 0 ) { /* not found */ f->status_register[0] |= UPD_FDC_ST0_INT_ABNORM; goto abort_read_data; } if( f->ddam != f->del_data ) { f->status_register[2] |= UPD_FDC_ST2_CONTROL_MARK; if( f->sk ) { f->data_register[3]++; goto skip_deleted_sector; /* or not deleted but we want to read deleted */ } } } else { if( f->mt ) { f->data_register[1]++; /* next track */ f->data_register[3] = 1; /* first sector */ goto multi_track_next; } abort_read_data: f->state = UPD_FDC_STATE_RES; /* end of execution phase */ f->cycle = f->cmd->res_length; /* end of cylinder is set if: * 1. sector data is read completely * (i.e. no other errors occur like no data. * 2. sector being read is same specified by EOT * 3. terminal count is not received * note: in +3 uPD765 never got TC */ if( !f->status_register[0] && !f->status_register[1] ) { f->status_register[0] |= UPD_FDC_ST0_INT_ABNORM; f->status_register[1] |= UPD_FDC_ST1_EOF_CYLINDER; } if( !( f->status_register[0] & ( UPD_FDC_ST0_INT_ABNORM | UPD_FDC_ST0_INT_READY ) ) ) { f->data_register[1]++; /* next track */ f->data_register[3] = 1; /* first sector */ } f->main_status &= ~UPD_FDC_MAIN_EXECUTION; f->intrq = UPD_INTRQ_RESULT; cmd_result( f ); return; } f->main_status |= UPD_FDC_MAIN_DATAREQ; if( f->cmd->id != UPD_CMD_SCAN ) f->main_status |= UPD_FDC_MAIN_DATA_READ; f->data_offset = 0; event_remove_type( timeout_event ); event_add_with_data( tstates + 4 * /* 2 revolution: 2 * 200 / 1000 */ machine_current->timings.processor_speed / 10, timeout_event, f ); }
static void wd_fdc_type_i( wd_fdc *f ) { libspectrum_byte b = f->command_register; fdd_t *d = f->current_drive; if( f->state == WD_FDC_STATE_SEEK_DELAY ) { /* after delay */ if( ( b & 0x60 ) != 0x00 ) /* STEP/STEP-IN/STEP-OUT */ goto type_i_verify; goto type_i_loop; } else { /* WD_FDC_STATE_SEEK */ f->status_register |= WD_FDC_SR_SPINUP; } if( ( b & 0x60 ) != 0x00 ) { /* STEP/STEP-IN/STEP-OUT */ if( b & 0x40 ) f->direction = b & 0x20 ? FDD_STEP_OUT : FDD_STEP_IN; if( b & 0x10 ) /* update? */ goto type_i_update; goto type_i_noupdate; } /* SEEK or RESTORE */ if ( !( b & 0x10 ) ) { /* RESTORE */ f->track_register = 0xff; f->data_register = 0; } type_i_loop: if( f->track_register != f->data_register ) { f->direction = f->track_register < f->data_register ? FDD_STEP_IN : FDD_STEP_OUT; type_i_update: f->track_register += f->direction == FDD_STEP_IN ? 1 : -1; type_i_noupdate: if( d->tr00 && f->direction == FDD_STEP_OUT ) { f->track_register = 0; } else { fdd_step( d, f->direction ); f->state = WD_FDC_STATE_SEEK_DELAY; event_remove_type( fdc_event ); event_add_with_data( tstates + f->rates[ b & 0x03 ] * machine_current->timings.processor_speed / 1000, fdc_event, f ); return; } } type_i_verify: if( b & 0x04 ) { if( f->type == WD1773 || f->type == FD1793 || f->type == WD2797 ) { f->head_load = 1; event_remove_type( motor_off_event ); if( f->flags & WD_FLAG_BETA128 ) fdd_motoron( d, 1 ); else fdd_head_load( d, 1 ); event_remove_type( fdc_event ); event_add_with_data( tstates + 15 * /* 15ms */ machine_current->timings.processor_speed / 1000, fdc_event, f ); } f->state = WD_FDC_STATE_VERIFY; if( ( f->type == WD1770 || f->type == WD1772 ) && !( f->status_register & WD_FDC_SR_MOTORON ) ) { f->status_register |= WD_FDC_SR_MOTORON; fdd_motoron( f->current_drive, 1 ); event_remove_type( fdc_event ); event_add_with_data( tstates + 12 * /* 6 revolution 6 * 200 / 1000 */ machine_current->timings.processor_speed / 10, fdc_event, f ); return; } wd_fdc_seek_verify( f ); return; } if( d->tr00 ) f->status_register |= WD_FDC_SR_LOST; else f->status_register &= ~WD_FDC_SR_LOST; f->state = WD_FDC_STATE_NONE; f->status_register &= ~WD_FDC_SR_BUSY; wd_fdc_set_intrq( f ); }
static void wd_fdc_type_ii_seek( wd_fdc *f ) { libspectrum_byte b = f->command_register; fdd_t *d = f->current_drive; int i; event_remove_type( fdc_event ); if( f->id_mark == WD_FDC_AM_NONE ) { f->read_id = 1; while( f->rev ) { i = d->disk.i >= d->disk.bpt ? 0 : d->disk.i; /* start position */ if( !read_id( f ) ) { if( ( f->data_check_head != -1 && f->data_check_head != !!( f->id_head ) ) || ( f->id_track != f->track_register || f->id_sector != f->sector_register ) ) { f->id_mark = WD_FDC_AM_NONE; } } else { f->id_mark = WD_FDC_AM_NONE; } i = d->disk.bpt ? ( d->disk.i - i ) * 200 / d->disk.bpt : 200; if( i > 0 ) { event_add_with_data( tstates + i * /* i * 1/20 revolution */ machine_current->timings.processor_speed / 1000, fdc_event, f ); return; } else if( f->id_mark != WD_FDC_AM_NONE ) { break; } } } f->read_id = 0; if( f->id_mark == WD_FDC_AM_NONE ) { f->status_register |= WD_FDC_SR_RNF; f->status_register &= ~WD_FDC_SR_BUSY; f->state = WD_FDC_STATE_NONE; wd_fdc_set_intrq( f ); return; } if( f->state == WD_FDC_STATE_READ ) { if( f->id_mark == WD_FDC_AM_ID ) read_datamark( f ); if( f->id_mark == WD_FDC_AM_NONE ) { /* not found */ f->status_register |= WD_FDC_SR_RNF; f->status_register &= ~WD_FDC_SR_BUSY; f->state = WD_FDC_STATE_NONE; wd_fdc_set_intrq( f ); return; } if( f->ddam ) f->status_register |= WD_FDC_SR_SPINUP; /* set deleted data mark */ f->data_offset = 0; wd_fdc_set_datarq( f ); } else { f->ddam = b & 0x01; for( i = 11; i > 0; i-- ) /* "delay" 11 GAP byte */ fdd_read_data( d ); wd_fdc_set_datarq( f ); f->data_offset = 0; if( f->dden ) for( i = 11; i > 0; i-- ) /* "delay" another 11 GAP byte */ fdd_read_data( d ); d->data = 0x00; for( i = f->dden ? 12 : 6; i > 0; i-- ) /* write 6/12 zero */ fdd_write_data( d ); crc_preset( f ); if( f->dden ) { /* MFM */ d->data = 0xffa1; for( i = 3; i > 0; i-- ) { /* write 3 0xa1 with clock mark */ fdd_write_data( d ); crc_add(f, d); } } d->data = ( f->ddam ? 0x00f8 : 0x00fb ) | ( f->dden ? 0x0000 : 0xff00 ); /* write data mark */ fdd_write_data( d ); crc_add(f, d); } event_remove_type( timeout_event ); event_add_with_data( tstates + /* 5 revolutions: 5 * 200 / 1000 */ machine_current->timings.processor_speed, timeout_event, f ); }
void upd_fdc_write_data( upd_fdc *f, libspectrum_byte data ) { int i, terminated = 0; unsigned int u; fdd_t *d; if( !( f->main_status & UPD_FDC_MAIN_DATAREQ ) || ( f->main_status & UPD_FDC_MAIN_DATA_READ ) ) return; if( f->main_status & UPD_FDC_MAIN_BUSY && f->state == UPD_FDC_STATE_EXE ) { /* execution phase WRITE/FORMAT */ d = f->current_drive; if( f->cmd->id == UPD_CMD_WRITE_ID ) { /* FORMAT */ /* at the index hole... */ f->data_register[f->data_offset + 5] = data; /* read id fields */ f->data_offset++; if( f->data_offset == 4 ) { /* C, H, R, N done => format track */ event_remove_type( timeout_event ); d->data = 0x00; for( i = f->mf ? 12 : 6; i > 0; i-- ) /* write 6/12 zero */ fdd_write_data( d ); crc_preset( f ); if( f->mf ) { /* MFM */ d->data = 0xffa1; for( i = 3; i > 0; i-- ) { /* write 3 0xa1 with clock mark */ fdd_write_data( d ); crc_add( f, d ); } } d->data = 0x00fe | ( f->mf ? 0x0000 : 0xff00 ); /* write id mark */ fdd_write_data( d ); crc_add( f, d ); for( i = 0; i < 4; i++ ) { d->data = f->data_register[i + 5]; /* write id fields */ fdd_write_data( d ); crc_add( f, d ); } d->data = f->crc >> 8; fdd_write_data( d ); /* write crc1 */ d->data = f->crc & 0xff; fdd_write_data( d ); /* write crc2 */ d->data = f->mf ? 0x4e : 0xff; for( i = 11; i > 0; i-- ) /* write 11 GAP byte */ fdd_write_data( d ); if( f->mf ) /* MFM */ for( i = 11; i > 0; i-- ) /* write another 11 GAP byte */ fdd_write_data( d ); d->data = 0x00; for( i = f->mf ? 12 : 6; i > 0; i-- ) /* write 6/12 zero */ fdd_write_data( d ); crc_preset( f ); if( f->mf ) { /* MFM */ d->data = 0xffa1; for( i = 3; i > 0; i-- ) { /* write 3 0xa1 with clock mark */ fdd_write_data( d ); crc_add( f, d ); } } d->data = 0x00fb | ( f->mf ? 0x0000 : 0xff00 ); /* write data mark */ fdd_write_data( d ); crc_add( f, d ); d->data = f->data_register[4]; /* write filler byte */ for( i = f->rlen; i > 0; i-- ) { fdd_write_data( d ); crc_add( f, d ); } d->data = f->crc >> 8; fdd_write_data( d ); /* write crc1 */ d->data = f->crc & 0xff; fdd_write_data( d ); /* write crc2 */ d->data = f->mf ? 0x4e : 0xff; for( i = f->data_register[3]; i > 0; i-- ) { /* GAP */ fdd_write_data( d ); } f->data_offset = 0; f->data_register[2]--; /* prepare next sector */ }
static void wd_fdc_type_iii( wd_fdc *f ) { int i; fdd_t *d = f->current_drive; event_remove_type( fdc_event ); if( !f->read_id && ( f->type == WD1773 || f->type == FD1793 || f->type == WD2797) ) { if( !disk_ready( f ) ) { f->status_register &= ~WD_FDC_SR_BUSY; f->state = WD_FDC_STATE_NONE; wd_fdc_set_intrq( f ); return; } if( !f->hlt ) { event_add_with_data( tstates + 5 * machine_current->timings.processor_speed / 1000, fdc_event, f ); return; } } if( f->state == WD_FDC_STATE_WRITETRACK ) { /* ----WRITE TRACK---- */ if( d->wrprot ) { f->status_register |= WD_FDC_SR_WRPROT; f->status_register &= ~WD_FDC_SR_BUSY; f->state = WD_FDC_STATE_NONE; wd_fdc_set_intrq( f ); return; } f->status_register &= ~WD_FDC_SR_WRPROT; f->data_offset = 0; fdd_wait_index_hole( d ); wd_fdc_set_datarq( f ); } else if( f->state == WD_FDC_STATE_READTRACK ) { /* ----READ TRACK---- */ fdd_wait_index_hole( d ); wd_fdc_set_datarq( f ); } else { /* ----READID---- */ if( !f->read_id ) { f->read_id = 1; f->rev = 5; f->id_mark = WD_FDC_AM_NONE; } if( f->id_mark == WD_FDC_AM_NONE ) { while( f->rev ) { i = d->disk.i >= d->disk.bpt ? 0 : d->disk.i; /* start position */ read_id( f ); i = d->disk.bpt ? ( d->disk.i - i ) * 200 / d->disk.bpt : 200; if( i > 0 ) { event_add_with_data( tstates + i * /* i * 1/20 revolution */ machine_current->timings.processor_speed / 1000, fdc_event, f ); return; } else if( f->id_mark != WD_FDC_AM_NONE ) break; } if( f->id_mark == WD_FDC_AM_NONE ) { f->state = WD_FDC_STATE_NONE; f->status_register |= WD_FDC_SR_RNF; f->status_register &= ~WD_FDC_SR_BUSY; wd_fdc_set_intrq( f ); f->read_id = 0; return; } } f->read_id = 0; f->data_offset = 0; wd_fdc_set_datarq( f ); } event_remove_type( timeout_event ); event_add_with_data( tstates + 2 * 20 * /* 2 revolutions: 2 * 200 / 1000 */ machine_current->timings.processor_speed / 100, timeout_event, f ); }
void wd_fdc_cr_write( wd_fdc *f, libspectrum_byte b ) { fdd_t *d = f->current_drive; wd_fdc_reset_intrq( f ); if( ( b & 0xf0 ) == 0xd0 ) { /* Type IV - Force Interrupt */ event_remove_type( fdc_event ); f->status_register &= ~( WD_FDC_SR_BUSY | WD_FDC_SR_WRPROT | WD_FDC_SR_CRCERR | WD_FDC_SR_IDX_DRQ ); f->state = WD_FDC_STATE_NONE; f->status_type = WD_FDC_STATUS_TYPE1; wd_fdc_reset_datarq( f ); if( b & 0x08 ) wd_fdc_set_intrq( f ); else if( b & 0x04 ) { d->fdc_index = wd_fdc_wait_index; d->fdc = f; } if( d->tr00 ) f->status_register |= WD_FDC_SR_LOST; else f->status_register &= ~WD_FDC_SR_LOST; wd_fdc_spinup( f, b & 0xf7 ); /* spinup motor, but we do not have a 'h' bit! */ return; } if( f->status_register & WD_FDC_SR_BUSY ) return; f->command_register = b; f->status_register |= WD_FDC_SR_BUSY; /* keep spindle motor on: */ event_remove_type( motor_off_event ); if( !( b & 0x80 ) ) { /* Type I */ f->state = WD_FDC_STATE_SEEK; f->status_type = WD_FDC_STATUS_TYPE1; f->status_register &= ~( WD_FDC_SR_CRCERR | WD_FDC_SR_RNF | WD_FDC_SR_IDX_DRQ ); wd_fdc_reset_datarq( f ); f->rev = 5; if( wd_fdc_spinup( f, b ) ) return; wd_fdc_type_i( f ); } else if( !( b & 0x40 ) ) { /* Type II */ if( f->type == WD1773 || f->type == FD1793 ) { if( !disk_ready( f ) ) { f->status_register &= ~WD_FDC_SR_BUSY; f->state = WD_FDC_STATE_NONE; wd_fdc_set_intrq( f ); return; } } if( f->type == WD1773 && b & 0x02 ) f->data_check_head = b & 0x08 ? 1 : 0; else if( f->type == WD2797 ) f->data_check_head = b & 0x02 ? 1 : 0; else f->data_check_head = -1; /* WD2797 (and FD1797) can read sectors with non-IBM-compatible sector length codes */ f->non_ibm_len_code = ( f->type == WD2797 && !( b & 0x08 ) ) ? 1 : 0; f->state = b & 0x20 ? WD_FDC_STATE_WRITE : WD_FDC_STATE_READ; f->status_type = WD_FDC_STATUS_TYPE2; f->status_register &= ~( WD_FDC_SR_WRPROT | WD_FDC_SR_RNF | WD_FDC_SR_IDX_DRQ| WD_FDC_SR_LOST| WD_FDC_SR_SPINUP ); if( f->type == WD2797 ) fdd_set_head( d, b & 0x02 ? 1 : 0 ); f->rev = 5; if( wd_fdc_spinup( f, b ) ) return; wd_fdc_type_ii( f ); } else if( ( b & 0x30 ) != 0x10 ) { /* Type III */ if( f->type == WD1773 || f->type == FD1793 || f->type == WD2797 ) { if( !disk_ready( f ) ) { f->status_register &= ~WD_FDC_SR_BUSY; f->state = WD_FDC_STATE_NONE; wd_fdc_set_intrq( f ); return; } } f->state = b & 0x20 ? ( b & 0x10 ? WD_FDC_STATE_WRITETRACK : WD_FDC_STATE_READTRACK ) : WD_FDC_STATE_READID; f->status_type = WD_FDC_STATUS_TYPE2; f->status_register &= ~( WD_FDC_SR_SPINUP | WD_FDC_SR_RNF | WD_FDC_SR_IDX_DRQ| WD_FDC_SR_LOST ); f->rev = 5; if( wd_fdc_spinup( f, b ) ) return; wd_fdc_type_iii( f ); } }
void wd_fdc_cr_write( wd_fdc *f, libspectrum_byte b ) { wd_fdc_drive *d = f->current_drive; wd_fdc_reset_intrq( f ); if( ( b & 0xf0 ) == 0xd0 ) { /* Type IV - Force Interrupt */ event_remove_type( fdc_event ); f->status_register &= ~( WD_FDC_SR_BUSY | WD_FDC_SR_WRPROT | WD_FDC_SR_CRCERR | WD_FDC_SR_IDX_DRQ ); f->state = WD_FDC_STATE_NONE; f->status_type = WD_FDC_STATUS_TYPE1; wd_fdc_reset_datarq( f ); if( b & 0x08 ) wd_fdc_set_intrq( f ); else if( b & 0x04 ) d->index_interrupt = 1; if( d->fdd.tr00 ) f->status_register |= WD_FDC_SR_LOST; else f->status_register &= ~WD_FDC_SR_LOST; return; } if( f->status_register & WD_FDC_SR_BUSY ) return; f->command_register = b; f->status_register |= WD_FDC_SR_BUSY; /* keep spindle motor on: */ event_remove_type( motor_off_event ); if( !( b & 0x80 ) ) { /* Type I */ f->state = WD_FDC_STATE_SEEK; f->status_type = WD_FDC_STATUS_TYPE1; f->status_register &= ~( WD_FDC_SR_CRCERR | WD_FDC_SR_RNF | WD_FDC_SR_IDX_DRQ ); wd_fdc_reset_datarq( f ); f->rev = 5; if( wd_fdc_spinup( f, b ) ) return; wd_fdc_type_i( f ); } else if( !( b & 0x40 ) ) { /* Type II */ if( f->type == WD1773 || f->type == FD1793 ) { if( ( ( f->flags & WD_FLAG_BETA128 ) && !f->head_load ) || ( !( f->flags & WD_FLAG_BETA128 ) && !d->fdd.ready ) ) { f->status_register &= ~WD_FDC_SR_BUSY; f->state = WD_FDC_STATE_NONE; wd_fdc_set_intrq( f ); return; } } if( f->type == WD1773 && b & 0x02 ) f->data_check_head = b & 0x08 ? 1 : 0; else f->data_check_head = -1; f->state = b & 0x20 ? WD_FDC_STATE_WRITE : WD_FDC_STATE_READ; f->status_type = WD_FDC_STATUS_TYPE2; f->status_register &= ~( WD_FDC_SR_WRPROT | WD_FDC_SR_RNF | WD_FDC_SR_IDX_DRQ| WD_FDC_SR_LOST| WD_FDC_SR_SPINUP ); f->rev = 5; if( wd_fdc_spinup( f, b ) ) return; wd_fdc_type_ii( f ); } else if( ( b & 0x30 ) != 0x10 ) { /* Type III */ if( f->type == WD1773 || f->type == FD1793 ) { if( ( ( f->flags & WD_FLAG_BETA128 ) && !f->head_load ) || ( !( f->flags & WD_FLAG_BETA128 ) && !d->fdd.ready ) ) { f->status_register &= ~WD_FDC_SR_BUSY; f->state = WD_FDC_STATE_NONE; wd_fdc_set_intrq( f ); return; } } f->state = b & 0x20 ? ( b & 0x10 ? WD_FDC_STATE_WRITETRACK : WD_FDC_STATE_READTRACK ) : WD_FDC_STATE_READID; f->status_type = WD_FDC_STATUS_TYPE2; f->status_register &= ~( WD_FDC_SR_SPINUP | WD_FDC_SR_RNF | WD_FDC_SR_IDX_DRQ| WD_FDC_SR_LOST ); f->rev = 5; if( wd_fdc_spinup( f, b ) ) return; wd_fdc_type_iii( f ); } }