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 ); }
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 ); } }
static void wd_fdc_event( libspectrum_dword last_tstates GCC_UNUSED, int event, void *user_data ) { wd_fdc *f = user_data; fdd_t *d = f->current_drive; if( event == timeout_event ) { if( f->state == WD_FDC_STATE_READ || f->state == WD_FDC_STATE_WRITE || f->state == WD_FDC_STATE_READTRACK || f->state == WD_FDC_STATE_WRITETRACK || f->state == WD_FDC_STATE_READID ) { f->state = WD_FDC_STATE_NONE; f->status_register |= WD_FDC_SR_LOST; f->status_register &= ~WD_FDC_SR_BUSY; wd_fdc_reset_datarq( f ); wd_fdc_set_intrq( f ); } return; } if( event == motor_off_event ) { if( f->type == WD1770 || f->type == WD1772 ) { f->status_register &= ~WD_FDC_SR_MOTORON; fdd_motoron( d, 0 ); } else { /* WD1773/FD1973 */ f->head_load = 0; if( f->flags & WD_FLAG_BETA128 ) fdd_motoron( d, 0 ); else fdd_head_load( d, 0 ); } return; } if( ( f->type == WD1773 || f->type == FD1793 || f->type == WD2797 ) && f->hlt_time > 0 && f->head_load && !f->hlt ) f->hlt = 1; if( ( ( f->type == WD1770 || f->type == WD1772 ) && ( f->status_register & WD_FDC_SR_MOTORON ) && f->status_type == WD_FDC_STATUS_TYPE1 ) || ( ( f->type == WD1773 || f->type == FD1793 || f->type == WD2797 ) && ( f->state == WD_FDC_STATE_SEEK || f->state == WD_FDC_STATE_SEEK_DELAY ) && f->head_load ) ) { f->status_register |= WD_FDC_SR_SPINUP; } if( f->read_id ) { if( f->state == WD_FDC_STATE_VERIFY ) wd_fdc_seek_verify_read_id( f ); else if( ( f->state == WD_FDC_STATE_READ || f->state == WD_FDC_STATE_WRITE ) && f->datarq ) f->datarq = 0, wd_fdc_set_datarq( f ); else if( f->state == WD_FDC_STATE_READ || f->state == WD_FDC_STATE_WRITE ) wd_fdc_type_ii_seek( f ); else if( f->state == WD_FDC_STATE_READID ) wd_fdc_type_iii( f ); } else if( f->state == WD_FDC_STATE_SEEK || f->state == WD_FDC_STATE_SEEK_DELAY ) wd_fdc_type_i( f ); else if( f->state == WD_FDC_STATE_VERIFY ) wd_fdc_seek_verify( f ); else if( ( f->state == WD_FDC_STATE_READ || f->state == WD_FDC_STATE_WRITE ) && f->datarq ) f->datarq = 0, wd_fdc_set_datarq( f ); else if( f->state == WD_FDC_STATE_READ || f->state == WD_FDC_STATE_WRITE ) wd_fdc_type_ii( f ); else if( ( f->state == WD_FDC_STATE_READTRACK || f->state == WD_FDC_STATE_READID || f->state == WD_FDC_STATE_WRITETRACK ) && f->datarq ) f->datarq = 0, wd_fdc_set_datarq( f ); else if( f->state == WD_FDC_STATE_READTRACK || f->state == WD_FDC_STATE_READID || f->state == WD_FDC_STATE_WRITETRACK ) wd_fdc_type_iii( f ); }
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 ); }
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 ); }
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 ); }
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 ) {
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 ); } }