コード例 #1
0
ファイル: upd_fdc.c プロジェクト: jacadym/fuse-emulator
libspectrum_byte
upd_fdc_read_data( upd_fdc *f )
{
  libspectrum_byte r;

  fdd_t *d = f->current_drive;

  if( !( f->main_status & UPD_FDC_MAIN_DATAREQ ) || 
      !( f->main_status & UPD_FDC_MAIN_DATA_READ ) )
    return 0xff;

  if( f->state == UPD_FDC_STATE_EXE ) {		/* READ_DATA/READ_DIAG */
    f->data_offset++;				/* count read bytes */
    fdd_read_data( d ); crc_add( f, d );	/* read a byte */

    /* Speedlock hack */
    if( f->speedlock > 0 && !d->do_read_weak ) {	/* do not conflict with fdd weak reads */
      if( f->data_offset < 64 && d->data != 0xe5 )
        f->speedlock = 2;		/* W.E.C Le Mans type ... */
      else if( ( f->speedlock > 1 || f->data_offset < 64 ) &&
	       !( f->data_offset % 29 ) ) {
	d->data ^= f->data_offset;	/* mess up data */
	crc_add( f, d );			/* mess up crc */
      }
    }
    /* EOSpeedlock hack */

    r = d->data & 0xff;
    if( f->data_offset == f->rlen ) {	/* send only rlen byte to host */
      while( f->data_offset < f->sector_length ) {
	fdd_read_data( d ); crc_add( f, d );
	f->data_offset++;
      }
    }
    if( ( f->cmd->id == UPD_CMD_READ_DIAG || f->cmd->id == UPD_CMD_READ_DATA )
        && 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 );
      if( f->crc != 0x000 ) {
	f->status_register[2] |= UPD_FDC_ST2_DATA_ERROR;
	f->status_register[1] |= UPD_FDC_ST1_CRC_ERROR;
	if( f->cmd->id == UPD_CMD_READ_DATA ) {		/* READ DIAG not aborted! */
	  f->status_register[0] |= UPD_FDC_ST0_INT_ABNORM;
	  cmd_result( f );			/* set up result phase */
	  return r;
        }
      }
	
      if( f->cmd->id == UPD_CMD_READ_DATA ) {
	if( f->ddam != f->del_data ) {	/* we read a not 'wanted' sector... so */
	  if( f->data_register[5] > f->data_register[3] )	/* if we want to read more... */
            f->status_register[0] |= UPD_FDC_ST0_INT_ABNORM;
          cmd_result( f );			/* set up result phase */
    	  return r;
	}
	f->rev = 2;
        f->main_status &= ~UPD_FDC_MAIN_DATAREQ;
	start_read_data( f );
      } else {				/* READ DIAG */
	f->data_register[3]++;		/*FIXME ??? */
	f->data_register[5]--;
        if( f->data_register[5] == 0 ) {	/* no more */
          cmd_result( f );			/* set up result phase */
          return r;
        }
        f->main_status &= ~UPD_FDC_MAIN_DATAREQ;
        start_read_diag( f );
      }
    }
    return r;
  }

  if( f->state != UPD_FDC_STATE_RES )
    return 0xff;

  if( f->cmd->id == UPD_CMD_SENSE_DRIVE ) {	/* 1 */
    r = f->status_register[3];
  } else if( f->cmd->id == UPD_CMD_SENSE_INT ) { /* 2 */
    r = f->sense_int_res[f->cmd->res_length - f->cycle];
  } else if( f->cmd->res_length - f->cycle < 3 ) {
    r = f->status_register[f->cmd->res_length - f->cycle];
  } else {
    r = f->data_register[f->cmd->res_length - f->cycle - 2];
  }
  f->cycle--;
  if( f->cycle == 0 ) {
    f->state = UPD_FDC_STATE_CMD;
    f->main_status |= UPD_FDC_MAIN_DATAREQ;
    f->main_status &= ~UPD_FDC_MAIN_DATADIR;
    f->main_status &= ~UPD_FDC_MAIN_BUSY;
    if( f->intrq < UPD_INTRQ_READY )
      f->intrq = UPD_INTRQ_NONE;
  }
  return r;
}
コード例 #2
0
ファイル: upd_fdc.c プロジェクト: jacadym/fuse-emulator
static int
read_datamark( upd_fdc *f )
{
  fdd_t *d = f->current_drive;
  int i;

  if( f->mf ) {	/* double density (MFM) */
    for( i = 40; i > 0; i-- ) {
      fdd_read_data( d );
      if( d->data == 0x4e )		/* read next */
	continue;

      if( d->data == 0x00 )		/* go to PLL sync */
	break;

      f->status_register[2] |= UPD_FDC_ST2_MISSING_DM;
      return 1;				/* something wrong... */
    } 
    for( ; i > 0; i-- ) {
      crc_preset( f );
      fdd_read_data( d ); crc_add( f, d );
      if( d->data == 0x00 )
	continue;

      if( d->data == 0xffa1 )	/* got to a1 mark */
	break;

      f->status_register[2] |= UPD_FDC_ST2_MISSING_DM;
      return 1;
    }
    for( i = d->data == 0xffa1 ? 2 : 3; i > 0; i-- ) {
      fdd_read_data( d ); crc_add( f, d );
      if( d->data != 0xffa1 ) {
        f->status_register[2] |= UPD_FDC_ST2_MISSING_DM;
	return 1;
      }
    } 
    fdd_read_data( d ); crc_add( f, d );
    if( d->data < 0x00f8 || d->data > 0x00fb ) { /* !fb deleted mark */
      f->status_register[2] |= UPD_FDC_ST2_MISSING_DM;
      return 1;
    }
    if( d->data != 0x00fb )
      f->ddam = 1;
    else
      f->ddam = 0;
    return 0;
  } else {		/* SD -> FM */
    for( i = 30; i > 0; i-- ) {
      fdd_read_data( d );
      if( d->data == 0xff )		/* read next */
	continue;

      if( d->data == 0x00 )		/* go to PLL sync */
	break;

      f->status_register[2] |= UPD_FDC_ST2_MISSING_DM;
      return 1;				/* something wrong... */
    } 
    for( ; i > 0; i-- ) {
      crc_preset( f );
      fdd_read_data( d ); crc_add( f, d );
      if( d->data == 0x00 )
	continue;

      if( d->data >= 0xfff8 && d->data <= 0xfffb )	/* !fb deleted mark */
	break;

      f->status_register[2] |= UPD_FDC_ST2_MISSING_DM;
      return 1;
    }
    if( i == 0 ) {
      fdd_read_data( d ); crc_add( f, d );
      if( d->data < 0xfff8 || d->data > 0xfffb ) {	/* !fb deleted mark */
        f->status_register[2] |= UPD_FDC_ST2_MISSING_DM;
	return 1;
      }
    }
    if( d->data != 0x00fb )
      f->ddam = 1;
    else
      f->ddam = 0;
    return 0;
  }
  f->status_register[2] |= UPD_FDC_ST2_MISSING_DM;
  return 1;
}
コード例 #3
0
ファイル: upd_fdc.c プロジェクト: jacadym/fuse-emulator
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 );
}
コード例 #4
0
ファイル: upd_fdc.c プロジェクト: jacadym/fuse-emulator
/* 
   Read next ID into f->id_*
   return 0 if found an ID 
   return 1 if found but with CRC error
   return 2 if not found ID
*/
static int
read_id( upd_fdc *f )
{
  int i;
  fdd_t *d = f->current_drive;

  f->status_register[1] &= ~( UPD_FDC_ST1_CRC_ERROR | 
    			      UPD_FDC_ST1_MISSING_AM |
    			      UPD_FDC_ST1_NO_DATA );
  f->id_mark = UPD_FDC_AM_NONE;
  i = f->rev;
  while( i == f->rev && d->ready ) {
    fdd_read_data( d ); if( d->index ) f->rev--;
    crc_preset( f );
    if( f->mf ) {	/* double density (MFM) */
      if( d->data == 0xffa1 ) {
        crc_add( f, d );
	fdd_read_data( d ); crc_add( f, d );
	if( d->index ) f->rev--;
	if( d->data != 0xffa1 )
	  continue;
	fdd_read_data( d ); crc_add( f, d );
	if( d->index ) f->rev--;
	if( d->data != 0xffa1 )
	  continue;
      } else {		/* no 0xa1 with missing clock... */
	continue;
      }
    }
    fdd_read_data( d );
    if( d->index ) f->rev--;
    if( f->mf ) {	/* double density (MFM) */
      if( d->data != 0x00fe )
	continue;
    } else {		/* single density (FM) */
      if( d->data != 0xfffe )
	continue;
    }
    crc_add( f, d );
    fdd_read_data( d ); crc_add( f, d );
    if( d->index ) f->rev--;
    f->id_track = d->data;
    fdd_read_data( d ); crc_add( f, d );
    if( d->index ) f->rev--;
    f->id_head = d->data;
    fdd_read_data( d ); crc_add( f, d );
    if( d->index ) f->rev--;
    f->id_sector = d->data;
    fdd_read_data( d ); crc_add( f, d );
    if( d->index ) f->rev--;
    f->id_length = d->data > MAX_SIZE_CODE ? MAX_SIZE_CODE : d->data;
    f->sector_length = 0x80 << f->id_length; 
    fdd_read_data( d ); crc_add( f, d );
    if( d->index ) f->rev--;
    fdd_read_data( d ); crc_add( f, d );
    if( d->index ) f->rev--;

    if( f->crc != 0x0000 ) {
      f->status_register[1] |= UPD_FDC_ST1_CRC_ERROR | UPD_FDC_ST1_NO_DATA;
      f->id_mark = UPD_FDC_AM_ID;
      return 1;		/* found but CRC error */
    } else {
      f->id_mark = UPD_FDC_AM_ID;
      return 0;		/* found and OK */
    }
  }
  if(!d->ready) f->rev = 0;
  f->status_register[1] |= UPD_FDC_ST1_MISSING_AM | UPD_FDC_ST1_NO_DATA;	/*FIXME _NO_DATA? */
  return 2;		/* not found */
}
コード例 #5
0
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 );
}
コード例 #6
0
static int
read_datamark( wd_fdc *f )
{
  fdd_t *d = f->current_drive;
  int i;

  f->id_mark = WD_FDC_AM_NONE;

  if( f->dden ) {	/* double density (MFM) */
    for( i = 40; i > 0; i-- ) {
      fdd_read_data( d );
      if( d->data == 0x4e )		/* read next */
	continue;

      if( d->data == 0x00 )		/* go to PLL sync */
	break;

      return 1;				/* something wrong... */
    }
    for( ; i > 0; i-- ) {
      crc_preset( f );
      fdd_read_data( d ); crc_add(f, d);
      if( d->data == 0x00 )
	continue;

      if( d->data == 0xffa1 )	/* got to a1 mark */
	break;

      return 1;
    }
    for( i = d->data == 0xffa1 ? 2 : 3; i > 0; i-- ) {
      fdd_read_data( d ); crc_add(f, d);
      if( d->data != 0xffa1 )
	return 1;
    }
    fdd_read_data( d ); crc_add(f, d);
    if( d->data < 0x00f8 || d->data > 0x00fb )	/* !fb deleted mark */
      return 1;
    if( d->data != 0x00fb )
      f->ddam = 1;
    else
      f->ddam = 0;
    f->id_mark = WD_FDC_AM_DATA;
    return 0;
  } else {		/* SD -> FM */
    for( i = 30; i > 0; i-- ) {
      fdd_read_data( d );
      if( d->data == 0xff )		/* read next */
	continue;

      if( d->data == 0x00 )		/* go to PLL sync */
	break;

      return 1;				/* something wrong... */
    }
    for( ; i > 0; i-- ) {
      crc_preset( f );
      fdd_read_data( d ); crc_add(f, d);
      if( d->data == 0x00 )
	continue;

      if( d->data >= 0xfff8 && d->data <= 0xfffb )	/* !fb deleted mark */
	break;

      return 1;
    }
    if( i == 0 ) {
      fdd_read_data( d ); crc_add(f, d);
      if( d->data < 0xfff8 || d->data > 0xfffb )	/* !fb deleted mark */
	return 1;
    }
    if( d->data != 0x00fb )
      f->ddam = 1;
    else
      f->ddam = 0;
    f->id_mark = WD_FDC_AM_DATA;
    return 0;
  }
  return 1;
}
コード例 #7
0
/* return 0 if found an ID
   return 1 if not found ID
   return 2 if found but with CRC error (READ ADDRESS command)

   what we can do, if disk not rotating, or head not loaded?
*/
static int
read_id( wd_fdc *f )
{
  int i = f->rev;
  fdd_t *d = f->current_drive;

  f->id_mark = WD_FDC_AM_NONE;

  if( f->rev <= 0 )
    return 1;

  while( i == f->rev ) { /* **FIXME d->motoron? */
    crc_preset( f );
    if( f->dden ) {	/* double density (MFM) */
      fdd_read_data( d );
      if( d->index ) f->rev--;
      crc_add(f, d);
      if( d->data == 0xffa1 ) {
        fdd_read_data( d ); crc_add(f, d);
        if( d->index ) f->rev--;
        if( d->data != 0xffa1 )
          continue;
        fdd_read_data( d ); crc_add(f, d);
        if( d->index ) f->rev--;
        if( d->data != 0xffa1 )
          continue;
      } else {		/* no 0xa1 with missing clock... */
        continue;
      }
    }
    fdd_read_data( d ); crc_add(f, d);
    if( d->index ) f->rev--;
    if( f->dden ) {	/* double density (MFM) */
      if( d->data != 0x00fe )
        continue;
    } else {		/* single density (FM) */
      if( d->data != 0xfffe )
        continue;
    }
    fdd_read_data( d ); crc_add(f, d);
    if( d->index ) f->rev--;
    f->id_track = d->data;
    fdd_read_data( d ); crc_add(f, d);
    if( d->index ) f->rev--;
    f->id_head = d->data;
    fdd_read_data( d ); crc_add(f, d);
    if( d->index ) f->rev--;
    f->id_sector = d->data;
    fdd_read_data( d ); crc_add(f, d);
    if( d->index ) f->rev--;
    f->id_length = d->data;
    if( f->non_ibm_len_code ) {		/* 00->256 01->512 10->1024 11->128 */
      f->sector_length = 0x80 << ( ( d->data + 1 ) & 0x03 );
    } else {				/* 00->128 01->256 10->512 11->1024 */
      f->sector_length = 0x80 << ( d->data & 0x03 );
    }
    fdd_read_data( d ); crc_add(f, d);
    if( d->index ) f->rev--;
    fdd_read_data( d ); crc_add(f, d);
    if( d->index ) f->rev--;
    if( f->crc != 0x0000 ) {
      f->status_register |= WD_FDC_SR_CRCERR;
      f->id_mark = WD_FDC_AM_ID;
      return 2;
    } else {
      f->status_register &= ~WD_FDC_SR_CRCERR;
      f->id_mark = WD_FDC_AM_ID;
      return 0;
    }
  }
  return 1;
}
コード例 #8
0
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 ) {