[comp.os.minix] floppy.c.cdif - allows 1.44M & fixes bugs

ast@cs.vu.nl (Andy Tanenbaum) (01/23/89)

It is not my intention to post a 1.4a kernel now.  It's time for me to get
back to SCO-3.  However, several fixes have been posted recently concerning
floppy.c, and these are important for the PS/2 version currently in progress.
One of these, in particular, is Al Crew's change that makes it possible to
read 1.44 MB PS/2 diskettes.  Thus I am including here the file floppy.c.cdif
(against 1.3).  Would the people working on the PS/2 floppy disk driver please
use this version as the base, since it handles the 1.44 MB diskettes and the
V1.3 version does not.  

People not working on the PS/2 can also install this now.
It is self-contained.
It should work better on 386s and give fewer problems in general.  
I have tested it with 360K and 1.2M diskettes, but not with the other sizes
since I don't have them.  I would appreciate feedback about this driver.
This is especially important since Al says that the algorithm used for
automatically determining the drive type is not really reliable.  He has
introduced default drive types to get around this.  I would like to know
how well this works and if it is really necessary.
As usual, please post reports to the newsgroup.

Andy Tanenbaum (ast@cs.vu.nl)

--------------------------------- floppy.c.cdif ------------------------------
/* This file contains a driver for a Floppy Disk Controller (FDC) using the
 * NEC PD765 chip.  The driver supports two operations: read a block and
 * write a block.  It accepts two messages, one for reading and one for
 * writing, both using message format m2 and with the same parameters:
 *
 *    m_type      DEVICE    PROC_NR     COUNT    POSITION  ADDRESS
 * ----------------------------------------------------------------
 * |  DISK_READ | device  | proc nr |  bytes  |  offset | buf ptr |
 * |------------+---------+---------+---------+---------+---------|
 * | DISK_WRITE | device  | proc nr |  bytes  |  offset | buf ptr |
 * ----------------------------------------------------------------
 *
 * The file contains one entry point:
 *
 *   floppy_task:	main entry when system is brought up
 *
 *  Changes:
 *	27 Oct. 1986 by Jakob Schripsema: fdc_results fixed for 8 MHz
 *	28 Nov. 1986 by Peter Kay: better resetting for 386
 *	06 Jan. 1988 by Al Crew: allow 1.44 MB diskettes
 */

#include "../h/const.h"
#include "../h/type.h"
#include "../h/callnr.h"
#include "../h/com.h"
#include "../h/error.h"
#include "const.h"
#include "type.h"
#include "glo.h"
#include "proc.h"

/* I/O Ports used by floppy disk task. */
#define DOR            0x3F2	/* motor drive control bits */
#define FDC_STATUS     0x3F4	/* floppy disk controller status register */
#define FDC_DATA       0x3F5	/* floppy disk controller data register */
#define FDC_RATE       0x3F7	/* transfer rate register */
#define DMA_ADDR       0x004	/* port for low 16 bits of DMA address */
#define DMA_TOP        0x081	/* port for top 4 bits of 20-bit DMA addr */
#define DMA_COUNT      0x005	/* port for DMA count (count =  bytes - 1) */
#define DMA_M2         0x00C	/* DMA status port */
#define DMA_M1         0x00B	/* DMA status port */
#define DMA_INIT       0x00A	/* DMA init port */
#define DMA_RESET_VAL   0x06

/* Status registers returned as result of operation. */
#define ST0             0x00	/* status register 0 */
#define ST1             0x01	/* status register 1 */
#define ST2             0x02	/* status register 2 */
#define ST3             0x00	/* status register 3 (return by DRIVE_SENSE) */
#define ST_CYL          0x03	/* slot where controller reports cylinder */
#define ST_HEAD         0x04	/* slot where controller reports head */
#define ST_SEC          0x05	/* slot where controller reports sector */
#define ST_PCN          0x01	/* slot where controller reports present cyl */

/* Fields within the I/O ports. */
#define MASTER          0x80	/* used to see who is master */
#define DIRECTION       0x40	/* is FDC trying to read or write? */
#define CTL_BUSY        0x10	/* used to see when controller is busy */
#define CTL_ACCEPTING   0x80	/* bit pattern FDC gives when idle */
#define MOTOR_MASK      0xF0	/* these bits control the motors in DOR */
#define ENABLE_INT      0x0C	/* used for setting DOR port */
#define ST0_BITS        0xF8	/* check top 5 bits of seek status */
#define ST3_FAULT       0x80	/* if this bit is set, drive is sick */
#define ST3_WR_PROTECT  0x40	/* set when diskette is write protected */
#define ST3_READY       0x20	/* set when drive is ready */
#define TRANS_ST0       0x00	/* top 5 bits of ST0 for READ/WRITE */
#define SEEK_ST0        0x20	/* top 5 bits of ST0 for SEEK */
#define BAD_SECTOR      0x05	/* if these bits are set in ST1, recalibrate */
#define BAD_CYL         0x1F	/* if any of these bits are set, recalibrate */
#define WRITE_PROTECT   0x02	/* bit is set if diskette is write protected */
#define CHANGE          0xC0	/* value returned by FDC after reset */

/* Floppy disk controller command bytes. */
#define FDC_SEEK        0x0F	/* command the drive to seek */
#define FDC_READ        0xE6	/* command the drive to read */
#define FDC_WRITE       0xC5	/* command the drive to write */
#define FDC_SENSE       0x08	/* command the controller to tell its status */
#define FDC_RECALIBRATE 0x07	/* command the drive to go to cyl 0 */
#define FDC_SPECIFY     0x03	/* command the drive to accept params */

/* DMA channel commands. */
#define DMA_READ        0x46	/* DMA read opcode */
#define DMA_WRITE       0x4A	/* DMA write opcode */

/* Parameters for the disk drive. */
#define SECTOR_SIZE      512	/* physical sector size in bytes */
#define HC_SIZE         2880	/* # sectors on largest legal disk (1.44MB) */
#define NR_HEADS        0x02	/* two heads (i.e., two tracks/cylinder) */
#define DTL             0xFF	/* determines data length (sector size) */
#define SPEC2           0x02	/* second parameter to SPECIFY */

#define MOTOR_OFF       3*HZ	/* how long to wait before stopping motor */

/* Error codes */
#define ERR_SEEK          -1	/* bad seek */
#define ERR_TRANSFER      -2	/* bad transfer */
#define ERR_STATUS        -3	/* something wrong when getting status */
#define ERR_RECALIBRATE   -4	/* recalibrate didn't work properly */
#define ERR_WR_PROTECT    -5	/* diskette is write protected */
#define ERR_DRIVE         -6	/* something wrong with a drive */

/* Miscellaneous. */
#define MOTOR_RUNNING   0xFF	/* message type for clock interrupt */
#define MAX_ERRORS        20	/* how often to try rd/wt before quitting */
#define MAX_RESULTS        8	/* max number of bytes controller returns */
#define NR_DRIVES          2	/* maximum number of drives */
#define DIVISOR          128	/* used for sector size encoding */
#define MAX_FDC_RETRY    100	/* max # times to try to output to FDC */
#define NT                 7	/* number of diskette/drive combinations */
#define AUTOMATIC	0x3F	/* bit map allowing both 3.5 and 5.25 disks */
#define THREE_INCH	0x48	/* bit map allowing only 3.5 inch diskettes */
#define FIVE_INCH	0x37	/* bit map allowing only 5.25 inch diskettes */

/* Variables. */
PRIVATE struct floppy {		/* main drive struct, one entry per drive */
  int fl_opcode;		/* DISK_READ or DISK_WRITE */
  int fl_curcyl;		/* current cylinder */
  int fl_procnr;		/* which proc wanted this operation? */
  int fl_drive;			/* drive number addressed */
  int fl_cylinder;		/* cylinder number addressed */
  int fl_sector;		/* sector addressed */
  int fl_head;			/* head number addressed */
  int fl_count;			/* byte count */
  vir_bytes fl_address;		/* user virtual address */
  char fl_results[MAX_RESULTS];	/* the controller can give lots of output */
  char fl_calibration;		/* CALIBRATED or UNCALIBRATED */
  char fl_density;		/* 0 = 360K/360K; 1 = 360K/1.2M; etc. */
} floppy[NR_DRIVES];

#define UNCALIBRATED       0	/* drive needs to be calibrated at next use */
#define CALIBRATED         1	/* no calibration needed */

PRIVATE int motor_status;	/* current motor status is in 4 high bits */
PRIVATE int motor_goal;		/* desired motor status is in 4 high bits */
PRIVATE int prev_motor;		/* which motor was started last */
PRIVATE int need_reset;		/* set to 1 when controller must be reset */
PRIVATE int initialized;	/* set to 1 after first successful transfer */
PRIVATE int d;			/* diskette/drive combination */
PRIVATE int current_spec1;	/* latest spec1 sent to the controller */

PRIVATE message mess;		/* message buffer for in and out */

PRIVATE char len[] = {-1,0,1,-1,2,-1,-1,3,-1,-1,-1,-1,-1,-1,-1,4};
PRIVATE char interleave[] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18};

/* Seven combinations of diskette/drive are supported. 
 *
 * # Drive  diskette  Sectors  Tracks  Rotation Data-rate  Comment
 * 0  360K    360K      9       40     300 RPM  250 kbps   Standard PC DSDD
 * 1  1.2M    1.2M     15       80     360 RPM  500 kbps   AT disk in AT drive
 * 2  720K    360K      9       40     300 RPM  250 kbps   Quad density PC
 * 3  720K    720K      9       80     300 RPM  250 kbps   Toshiba, et al.
 * 4  1.2M    360K      9       40     360 RPM  300 kbps   PC disk in AT drive
 * 5  1.2M    720K      9       80     360 RPM  300 kbps   Toshiba in AT drive
 * 6  1.44M   1.44M    18	80     300 RPM  500 kbps   PS/2, et al.
 *
 * In addition, 720K diskettes can be read in 1.44MB drives, but that does 
 * not need a different set of parameters.  This combination uses
 *
 * X  1.44M   720K	9	80     300 RPM  250 kbps   PS/2, et al.
 */
PRIVATE int gap[NT] =
	{0x2A, 0x1B, 0x2A, 0x2A, 0x23, 0x23, 0x1B}; /* gap size */
PRIVATE int rate[NT] = 
	{0x02, 0x00, 0x02, 0x02, 0x01, 0x01, 0x00}; /* 2=250,1=300,0=500 kbps*/
PRIVATE int nr_sectors[NT] = 
	{9,    15,   9,    9,    9,    9,    18};   /* sectors/track */
PRIVATE int nr_blocks[NT] = 
	{720,  2400, 720,  1440, 720,  1440, 2880}; /* sectors/diskette*/
PRIVATE int steps_per_cyl[NT] = 
	{1,    1,    2,    1,    2,    1,     1};   /* 2 = dbl step */
PRIVATE int mtr_setup[NT] = 
	{HZ/4,3*HZ/4,HZ/4,HZ,3*HZ/4,3*HZ/4,HZ};     /* in ticks */
PRIVATE char spec1[NT] =
	{0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xAF}; /* step rate, etc. */

/* This driver hunts around for the proper density by simply trying them all
 * until it finds one that works.  By defining DEFAULT_CLASS, one can reduce 
 * the searching to only 5.25 inch or only 3.5 inch types.  The array
 * drive_class contains a bit map for each drive, telling which of the NT
 * combinations defined above should be tried.
 */
PRIVATE char drive_class[NR_DRIVES] = {
#ifndef DEFAULT_CLASS
	AUTOMATIC, AUTOMATIC		/* both drives can handle both types */
#else
#	if	(DEFAULT_CLASS==3)
		THREE_INCH, THREE_INCH	/* drive 0 = 3.5 inch, drive 1 also */
#	endif

#	if	(DEFAULT_CLASS==5)
		FIVE_INCH, FIVE_INCH	/* drive 0 = 5.25 inch, drive 1 also */
#	endif
#endif
};

/*===========================================================================*
 *				floppy_task				     * 
 *===========================================================================*/
PUBLIC floppy_task()
{
/* Main program of the floppy disk driver task. */

  int r, caller, proc_nr;

  /* Here is the main loop of the disk task.  It waits for a message, carries
   * it out, and sends a reply.
   */
  while (TRUE) {
	/* First wait for a request to read or write a disk block. */
	receive(ANY, &mess);	/* get a request to do some work */
	if (mess.m_source < 0)
		panic("disk task got message from ", mess.m_source);
	caller = mess.m_source;
	proc_nr = mess.PROC_NR;

	/* Now carry out the work. */
	switch(mess.m_type) {
	    case DISK_READ:	r = do_rdwt(&mess);	break;
	    case DISK_WRITE:	r = do_rdwt(&mess);	break;
	    default:		r = EINVAL;		break;
	}

	/* Finally, prepare and send the reply message. */
	mess.m_type = TASK_REPLY;	
	mess.REP_PROC_NR = proc_nr;
	mess.REP_STATUS = r;	/* # of bytes transferred or error code */
	send(caller, &mess);	/* send reply to caller */
  }
}


/*===========================================================================*
 *				do_rdwt					     * 
 *===========================================================================*/
PRIVATE int do_rdwt(m_ptr)
message *m_ptr;			/* pointer to read or write message */
{
/* Carry out a read or write request from the disk. */
  register struct floppy *fp;
  int r, sectors, drive, errors, stop_motor();
  long block;

  /* Decode the message parameters. */
  drive = m_ptr->DEVICE;
  if (drive < 0 || drive >= NR_DRIVES) return(EIO);
  fp = &floppy[drive];		/* 'fp' points to entry for this drive */  
  fp->fl_drive = drive;		/* save drive number explicitly */
  fp->fl_opcode = m_ptr->m_type;	/* DISK_READ or DISK_WRITE */
  if (m_ptr->POSITION % BLOCK_SIZE != 0) return(EINVAL);
  block = m_ptr->POSITION/SECTOR_SIZE;
  if (block >= HC_SIZE) return(EOF); /* sector is beyond end of 1.44M disk */
  d = fp->fl_density;		/* diskette/drive combination */

  /* Check the bit map to see if this density is allowed.  If not, skip it. */
  while ((drive_class[drive] & (1 << d)) == 0) d = (d + 1) % NT;

  /* Store the message parameters in the fp->fl array. */
  fp->fl_density=d;
  fp->fl_cylinder = (int) (block / (NR_HEADS * nr_sectors[d]));
  fp->fl_sector = (int) interleave[(int)(block % nr_sectors[d])];
  fp->fl_head = (int) (block % (NR_HEADS*nr_sectors[d]) )/nr_sectors[d];
  fp->fl_count = m_ptr->COUNT;
  fp->fl_address = (vir_bytes) m_ptr->ADDRESS;
  fp->fl_procnr = m_ptr->PROC_NR;
  if (fp->fl_count != BLOCK_SIZE) return(EINVAL);

  errors = 0;

  /* This loop allows a failed operation to be repeated. */
  while (errors <= MAX_ERRORS) {

	/* If a lot of errors occur when 'initialized' is 0, it probably
	 * means that we are trying at the wrong density.  Try another one.
	 * Increment 'errors' here since loop is aborted on error.
	 */
	port_out(DMA_INIT, DMA_RESET_VAL);  /* reset the dma controller */
	errors++;		/* increment count once per loop cycle */
 	if (errors % (MAX_ERRORS/NT) == 0) {
                d++;

		/* Check bit map to skip illegal densities. */
		while ((drive_class[drive] & (1 << d)) == 0) d = (d + 1) % NT;

 		fp->fl_density = d;
		sectors = nr_sectors[d];
		fp->fl_cylinder = (int) (block / (NR_HEADS * sectors));
		fp->fl_sector = (int) interleave[(int)(block % sectors)];
		fp->fl_head = (int)(block%(NR_HEADS*sectors)) / sectors;
 		need_reset = 1;
	}
  	if (block >= nr_blocks[d]) continue;

	/* First check to see if a reset is needed. */
	if (need_reset) reset();

	/* Set the stepping rate */
	if (current_spec1 != spec1[d]) {
		fdc_out(FDC_SPECIFY);
		fdc_out(current_spec1=spec1[d]);
		fdc_out(SPEC2);
	}

 	/* Set the data rate */
	if (pc_at) port_out(FDC_RATE, rate[d]);

	/* Now set up the DMA chip. */
	dma_setup(fp);

	/* See if motor is running; if not, turn it on and wait */
	start_motor(fp);

	/* If we are going to a new cylinder, perform a seek. */
	r = seek(fp);
	if (r != OK) continue;	/* if error, try again */

	/* Perform the transfer. */
	r = transfer(fp);
	if (r == OK) break;	/* if successful, exit loop */
	if (r == ERR_WR_PROTECT) break;	/* retries won't help */
  }

  /* Start watch_dog timer to turn motor off in a few seconds */
  motor_goal = ENABLE_INT;	/* when timer goes off, kill all motors */
  clock_mess(MOTOR_OFF, stop_motor);
  if (r == OK && fp->fl_cylinder > 0) initialized = 1;	/* seek works */
  return(r == OK ? BLOCK_SIZE : EIO);
}

/*===========================================================================*
 *				dma_setup				     * 
 *===========================================================================*/
PRIVATE dma_setup(fp)
struct floppy *fp;		/* pointer to the drive struct */
{
/* The IBM PC can perform DMA operations by using the DMA chip.  To use it,
 * the DMA (Direct Memory Access) chip is loaded with the 20-bit memory address
 * to be read from or written to, the byte count minus 1, and a read or write
 * opcode.  This routine sets up the DMA chip.  Note that the chip is not
 * capable of doing a DMA across a 64K boundary (e.g., you can't read a 
 * 512-byte block starting at physical address 65520).
 */

  int mode, low_addr, high_addr, top_addr, low_ct, high_ct, top_end, s;
  vir_bytes vir, ct;
  phys_bytes user_phys;
  extern phys_bytes umap();

  mode = (fp->fl_opcode == DISK_READ ? DMA_READ : DMA_WRITE);
  vir = (vir_bytes) fp->fl_address;
  ct = (vir_bytes) fp->fl_count;
  user_phys = umap(proc_addr(fp->fl_procnr), D, vir, ct);
  low_addr  = (int) (user_phys >>  0) & BYTE;
  high_addr = (int) (user_phys >>  8) & BYTE;
  top_addr  = (int) (user_phys >> 16) & BYTE;
  low_ct  = (int) ( (ct - 1) >> 0) & BYTE;
  high_ct = (int) ( (ct - 1) >> 8) & BYTE;

  /* Check to see if the transfer will require the DMA address counter to
   * go from one 64K segment to another.  If so, do not even start it, since
   * the hardware does not carry from bit 15 to bit 16 of the DMA address.
   * Also check for bad buffer address.  These errors mean FS contains a bug.
   */
  if (user_phys == 0) panic("FS gave floppy disk driver bad addr", (int) vir);
  top_end = (int) (((user_phys + ct - 1) >> 16) & BYTE);
  if (top_end != top_addr)panic("Trying to DMA across 64K boundary", top_addr);

  /* Now set up the DMA registers. */
  port_out(DMA_INIT, DMA_RESET_VAL);        /* reset the dma controller */
  s = lock();
  port_out(DMA_M2, mode);	/* set the DMA mode */
  port_out(DMA_M1, mode);	/* set it again */
  port_out(DMA_ADDR, low_addr);	/* output low-order 8 bits */
  port_out(DMA_ADDR, high_addr);/* output next 8 bits */
  port_out(DMA_TOP, top_addr);	/* output highest 4 bits */
  port_out(DMA_COUNT, low_ct);	/* output low 8 bits of count - 1 */
  port_out(DMA_COUNT, high_ct);	/* output high 8 bits of count - 1 */
  restore(s);
  port_out(DMA_INIT, 2);	/* initialize DMA */
}


/*===========================================================================*
 *				start_motor				     * 
 *===========================================================================*/
PRIVATE start_motor(fp)
struct floppy *fp;		/* pointer to the drive struct */
{
/* Control of the floppy disk motors is a big pain.  If a motor is off, you
 * have to turn it on first, which takes 1/2 second.  You can't leave it on
 * all the time, since that would wear out the diskette.  However, if you turn
 * the motor off after each operation, the system performance will be awful.
 * The compromise used here is to leave it on for a few seconds after each
 * operation.  If a new operation is started in that interval, it need not be
 * turned on again.  If no new operation is started, a timer goes off and the
 * motor is turned off.  I/O port DOR has bits to control each of 4 drives.
 * Interrupts must be disabled temporarily to prevent clock interrupts from
 * turning off motors while we are testing the bits.
 */

  int motor_bit, running, send_mess(), old_state;

  old_state = lock();		/* no interrupts while checking out motor */
  motor_bit = 1 << (fp->fl_drive + 4);	/* bit mask for this drive */
  motor_goal = motor_bit | ENABLE_INT | fp->fl_drive;
  if (motor_status & prev_motor) motor_goal |= prev_motor;
  running = motor_status & motor_bit;	/* nonzero if this motor is running */
  port_out(DOR, motor_goal);
  motor_status = motor_goal;
  prev_motor = motor_bit;	/* record motor started for next time */
  restore(old_state);

  /* If the motor was already running, we don't have to wait for it. */
  if (running) return;			/* motor was already running */
  clock_mess(mtr_setup[d], send_mess);	/* motor was not running */
  receive(CLOCK, &mess);		/* wait for clock interrupt */
}


/*===========================================================================*
 *				stop_motor				     * 
 *===========================================================================*/
PRIVATE stop_motor()
{
/* This routine is called by the clock interrupt after several seconds have
 * elapsed with no floppy disk activity.  It checks to see if any drives are
 * supposed to be turned off, and if so, turns them off.
 */

  if ( (motor_goal & MOTOR_MASK) != (motor_status & MOTOR_MASK) ) {
	port_out(DOR, motor_goal);
	motor_status = motor_goal;
  }
}


/*===========================================================================*
 *				seek					     * 
 *===========================================================================*/
PRIVATE int seek(fp)
struct floppy *fp;		/* pointer to the drive struct */
{
/* Issue a SEEK command on the indicated drive unless the arm is already 
 * positioned on the correct cylinder.
 */

  int r, send_mess();

  /* Are we already on the correct cylinder? */
  if (fp->fl_calibration == UNCALIBRATED)
	if (recalibrate(fp) != OK) return(ERR_SEEK);
  if (fp->fl_curcyl == fp->fl_cylinder) return(OK);

  /* No.  Wrong cylinder.  Issue a SEEK and wait for interrupt. */
  fdc_out(FDC_SEEK);		/* start issuing the SEEK command */
  fdc_out( (fp->fl_head << 2) | fp->fl_drive);
  fdc_out(fp->fl_cylinder * steps_per_cyl[d]);
  if (need_reset) return(ERR_SEEK);	/* if controller is sick, abort seek */
  receive(HARDWARE, &mess);

  /* Interrupt has been received.  Check drive status. */
  fdc_out(FDC_SENSE);		/* probe FDC to make it return status */
  r = fdc_results(fp);		/* get controller status bytes */
  if ( (fp->fl_results[ST0] & ST0_BITS) != SEEK_ST0) r = ERR_SEEK;
  if (fp->fl_results[ST1] != fp->fl_cylinder * steps_per_cyl[d]) r = ERR_SEEK;
  if (r != OK) 
	if (recalibrate(fp) != OK) return(ERR_SEEK);
  fp->fl_curcyl = (r == OK ? fp->fl_cylinder : -1);
  if (r == OK && ((d == 6) || (d == 3))) {/* give head time to settle on 3.5 */
	clock_mess(2, send_mess);
	receive(CLOCK, &mess);
  }
  return(r);
}


/*===========================================================================*
 *				transfer				     * 
 *===========================================================================*/
PRIVATE int transfer(fp)
register struct floppy *fp;	/* pointer to the drive struct */
{
/* The drive is now on the proper cylinder.  Read or write 1 block. */

  int r, s, op;
  extern int olivetti;

  /* Never attempt a transfer if the drive is uncalibrated or motor is off. */
  if (fp->fl_calibration == UNCALIBRATED) return(ERR_TRANSFER);
  if ( ( (motor_status>>(fp->fl_drive+4)) & 1) == 0) return(ERR_TRANSFER);

  /* The PC-AT requires the date rate to be set to 250 or 500 kbps */
  if (pc_at) port_out(FDC_RATE, rate[d]);

  /* The command is issued by outputting 9 bytes to the controller chip. */
  op = (fp->fl_opcode == DISK_READ ? FDC_READ : FDC_WRITE);
  fdc_out(op);			/* issue the read or write command */
  fdc_out( (fp->fl_head << 2) | fp->fl_drive);
  fdc_out(fp->fl_cylinder);	/* tell controller which cylinder */
  fdc_out(fp->fl_head);		/* tell controller which head */
  fdc_out(fp->fl_sector);	/* tell controller which sector */
  fdc_out( (int) len[SECTOR_SIZE/DIVISOR]);	/* sector size */
  fdc_out(nr_sectors[d]);	/* tell controller how big a track is */
  fdc_out(gap[d]);		/* tell controller how big sector gap is */
  fdc_out(DTL);			/* tell controller about data length */

  /* Block, waiting for disk interrupt. */
  if (need_reset) return(ERR_TRANSFER);	/* if controller is sick, abort op */
  receive(HARDWARE, &mess);

  /* Get controller status and check for errors. */
  r = fdc_results(fp);
  if (r != OK) return(r);
  if ( (fp->fl_results[ST1] & BAD_SECTOR) || (fp->fl_results[ST2] & BAD_CYL) )
	fp->fl_calibration = UNCALIBRATED;
  if (fp->fl_results[ST1] & WRITE_PROTECT) {
	printf("Diskette in drive %d is write protected.\n", fp->fl_drive);
	return(ERR_WR_PROTECT);
  }
  if ((fp->fl_results[ST0] & ST0_BITS) != TRANS_ST0) return(ERR_TRANSFER);
  if (fp->fl_results[ST1] | fp->fl_results[ST2]) return(ERR_TRANSFER);

  /* Compare actual numbers of sectors transferred with expected number. */
  s =  (fp->fl_results[ST_CYL] - fp->fl_cylinder) * NR_HEADS * nr_sectors[d];
  s += (fp->fl_results[ST_HEAD] - fp->fl_head) * nr_sectors[d];
  s += (fp->fl_results[ST_SEC] - fp->fl_sector);
  if (s * SECTOR_SIZE != fp->fl_count) return(ERR_TRANSFER);
  return(OK);
}


/*===========================================================================*
 *				fdc_results				     * 
 *===========================================================================*/
PRIVATE int fdc_results(fp)
register struct floppy *fp;	/* pointer to the drive struct */
{
/* Extract results from the controller after an operation. */

  int i, j, k, status, ready;

  /* Loop, extracting bytes from FDC until it says it has no more. */
  for (i = 0; i < MAX_RESULTS; i++) {
	ready = FALSE;
	for (j = 0; j < MAX_FDC_RETRY; j++) {
		for (k = 0; k < 32; k++) ;	/* delay loop for 386 */
		port_in(FDC_STATUS, &status);
		if (status & MASTER) {
			ready = TRUE;
			break;
		}
	}
	if (ready == FALSE) return(ERR_STATUS);	/* time out */

	if ((status & CTL_BUSY) == 0) return(OK);
	if ((status & DIRECTION) == 0) return(ERR_STATUS);
	port_in(FDC_DATA, &status);
	fp->fl_results[i] = status & BYTE;
  }

  /* FDC is giving back too many results. */
  need_reset = TRUE;		/* controller chip must be reset */
  return(ERR_STATUS);
}


/*===========================================================================*
 *				fdc_out					     * 
 *===========================================================================*/
PRIVATE fdc_out(val)
int val;			/* write this byte to floppy disk controller */
{
/* Output a byte to the controller.  This is not entirely trivial, since you
 * can only write to it when it is listening, and it decides when to listen.
 * If the controller refuses to listen, the FDC chip is given a hard reset.
 */

  int retries, r;

  if (need_reset) return;	/* if controller is not listening, return */
  retries = MAX_FDC_RETRY;

  /* It may take several tries to get the FDC to accept a command. */
  while (retries-- > 0) {
	port_in(FDC_STATUS, &r);
	r &= (MASTER | DIRECTION);	/* just look at bits 2 and 3 */
	if (r != CTL_ACCEPTING) continue;	/* FDC is not listening */
	port_out(FDC_DATA, val);
	return;
  }

  /* Controller is not listening.  Hit it over the head with a hammer. */
  need_reset = TRUE;
}


/*===========================================================================*
 *				recalibrate				     * 
 *===========================================================================*/
PRIVATE int recalibrate(fp)
register struct floppy *fp;	/* pointer tot he drive struct */
{
/* The floppy disk controller has no way of determining its absolute arm
 * position (cylinder).  Instead, it steps the arm a cylinder at a time and
 * keeps track of where it thinks it is (in software).  However, after a
 * SEEK, the hardware reads information from the diskette telling where the
 * arm actually is.  If the arm is in the wrong place, a recalibration is done,
 * which forces the arm to cylinder 0.  This way the controller can get back
 * into sync with reality.
 */

  int r;
  /* Issue the RECALIBRATE command and wait for the interrupt. */
  start_motor(fp);		/* can't recalibrate with motor off */
  fdc_out(FDC_RECALIBRATE);	/* tell drive to recalibrate itself */
  fdc_out(fp->fl_drive);	/* specify drive */
  if (need_reset) return(ERR_SEEK);	/* don't wait if controller is sick */
  receive(HARDWARE, &mess);	/* wait for interrupt message */

  /* Determine if the recalibration succeeded. */
  fdc_out(FDC_SENSE);		/* issue SENSE command to see where we are */
  r = fdc_results(fp);		/* get results of the SENSE command */
  fp->fl_curcyl = -1;		/* force a SEEK next time */
  if (r != OK ||		/* controller would not respond */
     (fp->fl_results[ST0]&ST0_BITS) != SEEK_ST0 || fp->fl_results[ST_PCN] !=0){
	/* Recalibration failed.  FDC must be reset. */
	need_reset = TRUE;
	fp->fl_calibration = UNCALIBRATED;
	return(ERR_RECALIBRATE);
  } else {
	/* Recalibration succeeded. */
	fp->fl_calibration = CALIBRATED;
	if (ps||((d == 6) || (d == 3))) {/* give head time to settle on 3.5 */
		clock_mess(2, send_mess);
		receive(CLOCK, &mess);
	}
	return(OK);
  }
}


/*===========================================================================*
 *				reset					     * 
 *===========================================================================*/
PRIVATE reset()
{
/* Issue a reset to the controller.  This is done after any catastrophe,
 * like the controller refusing to respond.
 */

  int i, r, status, old_state;
  register struct floppy *fp;

  /* Disable interrupts and strobe reset bit low. */
  need_reset = FALSE;
  old_state = lock();
  motor_status = 0;
  motor_goal = 0;
  port_out(DOR, 0);		/* strobe reset bit low */
  port_out(DOR, ENABLE_INT);	/* strobe it high again */
  restore(old_state);		/* interrupts allowed again */
  receive(HARDWARE, &mess);	/* collect the RESET interrupt */

  /* Interrupt from the reset has been received.  Clear each drive. */
  for (i=0; i<NR_DRIVES; i++) {
	fdc_out(FDC_SENSE);	/* did it work? */
	fdc_results(&floppy[i]);/* get results (using each floppy[i]) */
	floppy[i].fl_calibration = UNCALIBRATED;
  }

  /* Tell FDC drive parameters. */
  fdc_out(FDC_SPECIFY);		/* specify some timing parameters */
  fdc_out(current_spec1=spec1[d]); /* step-rate and head-unload-time */
  fdc_out(SPEC2);		/* head-load-time and non-dma */
}


/*===========================================================================*
 *				clock_mess				     * 
 *===========================================================================*/
PRIVATE clock_mess(ticks, func)
int ticks;			/* how many clock ticks to wait */
int (*func)();			/* function to call upon time out */
{
/* Send the clock task a message. */

  mess.m_type = SET_ALARM;
  mess.CLOCK_PROC_NR = FLOPPY;
  mess.DELTA_TICKS = (long) ticks;
  mess.FUNC_TO_CALL = func;
  sendrec(CLOCK, &mess);
}


/*===========================================================================*
 *				send_mess				     * 
 *===========================================================================*/
PRIVATE send_mess()
{
/* This routine is called when the clock task has timed out on motor startup.*/

  send(FLOPPY, &mess);
}
*** floppy.old	Mon Jan 23 15:50:31 1989
--- floppy.c	Mon Jan 23 15:41:59 1989
***************
*** 15,21 ****
   *   floppy_task:	main entry when system is brought up
   *
   *  Changes:
!  *	27 october 1986 by Jakob Schripsema: fdc_results fixed for 8 MHz
   */
  
  #include "../h/const.h"
--- 15,23 ----
   *   floppy_task:	main entry when system is brought up
   *
   *  Changes:
!  *	27 Oct. 1986 by Jakob Schripsema: fdc_results fixed for 8 MHz
!  *	28 Nov. 1986 by Peter Kay: better resetting for 386
!  *	06 Jan. 1988 by Al Crew: allow 1.44 MB diskettes
   */
  
  #include "../h/const.h"
***************
*** 39,44 ****
--- 41,47 ----
  #define DMA_M2         0x00C	/* DMA status port */
  #define DMA_M1         0x00B	/* DMA status port */
  #define DMA_INIT       0x00A	/* DMA init port */
+ #define DMA_RESET_VAL   0x06
  
  /* Status registers returned as result of operation. */
  #define ST0             0x00	/* status register 0 */
***************
*** 82,91 ****
  
  /* Parameters for the disk drive. */
  #define SECTOR_SIZE      512	/* physical sector size in bytes */
! #define HC_SIZE         2400	/* # sectors on a high-capacity (1.2M) disk */
  #define NR_HEADS        0x02	/* two heads (i.e., two tracks/cylinder) */
  #define DTL             0xFF	/* determines data length (sector size) */
- #define SPEC1           0xDF	/* first parameter to SPECIFY */
  #define SPEC2           0x02	/* second parameter to SPECIFY */
  
  #define MOTOR_OFF       3*HZ	/* how long to wait before stopping motor */
--- 85,93 ----
  
  /* Parameters for the disk drive. */
  #define SECTOR_SIZE      512	/* physical sector size in bytes */
! #define HC_SIZE         2880	/* # sectors on largest legal disk (1.44MB) */
  #define NR_HEADS        0x02	/* two heads (i.e., two tracks/cylinder) */
  #define DTL             0xFF	/* determines data length (sector size) */
  #define SPEC2           0x02	/* second parameter to SPECIFY */
  
  #define MOTOR_OFF       3*HZ	/* how long to wait before stopping motor */
***************
*** 105,111 ****
  #define NR_DRIVES          2	/* maximum number of drives */
  #define DIVISOR          128	/* used for sector size encoding */
  #define MAX_FDC_RETRY    100	/* max # times to try to output to FDC */
! #define NT                 6	/* number of diskette/drive combinations */
  
  /* Variables. */
  PRIVATE struct floppy {		/* main drive struct, one entry per drive */
--- 107,116 ----
  #define NR_DRIVES          2	/* maximum number of drives */
  #define DIVISOR          128	/* used for sector size encoding */
  #define MAX_FDC_RETRY    100	/* max # times to try to output to FDC */
! #define NT                 7	/* number of diskette/drive combinations */
! #define AUTOMATIC	0x3F	/* bit map allowing both 3.5 and 5.25 disks */
! #define THREE_INCH	0x48	/* bit map allowing only 3.5 inch diskettes */
! #define FIVE_INCH	0x37	/* bit map allowing only 5.25 inch diskettes */
  
  /* Variables. */
  PRIVATE struct floppy {		/* main drive struct, one entry per drive */
***************
*** 120,126 ****
    vir_bytes fl_address;		/* user virtual address */
    char fl_results[MAX_RESULTS];	/* the controller can give lots of output */
    char fl_calibration;		/* CALIBRATED or UNCALIBRATED */
!   char fl_density;		/* 0 = 360K/360K; 1 = 360K/1.2M; 2= 1.2M/1.2M*/
  } floppy[NR_DRIVES];
  
  #define UNCALIBRATED       0	/* drive needs to be calibrated at next use */
--- 125,131 ----
    vir_bytes fl_address;		/* user virtual address */
    char fl_results[MAX_RESULTS];	/* the controller can give lots of output */
    char fl_calibration;		/* CALIBRATED or UNCALIBRATED */
!   char fl_density;		/* 0 = 360K/360K; 1 = 360K/1.2M; etc. */
  } floppy[NR_DRIVES];
  
  #define UNCALIBRATED       0	/* drive needs to be calibrated at next use */
***************
*** 132,144 ****
  PRIVATE int need_reset;		/* set to 1 when controller must be reset */
  PRIVATE int initialized;	/* set to 1 after first successful transfer */
  PRIVATE int d;			/* diskette/drive combination */
  
  PRIVATE message mess;		/* message buffer for in and out */
  
  PRIVATE char len[] = {-1,0,1,-1,2,-1,-1,3,-1,-1,-1,-1,-1,-1,-1,4};
! PRIVATE char interleave[] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
  
! /* Six combinations of diskette/drive are supported:
   * # Drive  diskette  Sectors  Tracks  Rotation Data-rate  Comment
   * 0  360K    360K      9       40     300 RPM  250 kbps   Standard PC DSDD
   * 1  1.2M    1.2M     15       80     360 RPM  500 kbps   AT disk in AT drive
--- 137,151 ----
  PRIVATE int need_reset;		/* set to 1 when controller must be reset */
  PRIVATE int initialized;	/* set to 1 after first successful transfer */
  PRIVATE int d;			/* diskette/drive combination */
+ PRIVATE int current_spec1;	/* latest spec1 sent to the controller */
  
  PRIVATE message mess;		/* message buffer for in and out */
  
  PRIVATE char len[] = {-1,0,1,-1,2,-1,-1,3,-1,-1,-1,-1,-1,-1,-1,4};
! PRIVATE char interleave[] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18};
  
! /* Seven combinations of diskette/drive are supported. 
!  *
   * # Drive  diskette  Sectors  Tracks  Rotation Data-rate  Comment
   * 0  360K    360K      9       40     300 RPM  250 kbps   Standard PC DSDD
   * 1  1.2M    1.2M     15       80     360 RPM  500 kbps   AT disk in AT drive
***************
*** 146,165 ****
   * 3  720K    720K      9       80     300 RPM  250 kbps   Toshiba, et al.
   * 4  1.2M    360K      9       40     360 RPM  300 kbps   PC disk in AT drive
   * 5  1.2M    720K      9       80     360 RPM  300 kbps   Toshiba in AT drive
   */
  PRIVATE int gap[NT] =
! 	{0x2A, 0x1B, 0x2A, 0x2A, 0x23, 0x23}; /* gap size */
  PRIVATE int rate[NT] = 
! 	{0x02, 0x00, 0x02, 0x02, 0x01, 0x01}; /* 250,300,500 kbps*/
  PRIVATE int nr_sectors[NT] = 
! 	{9,    15,   9,    9,    9,    9};   /* sectors/track */
  PRIVATE int nr_blocks[NT] = 
! 	{720,  2400, 720,  1440, 720,  1440}; /* sectors/diskette*/
  PRIVATE int steps_per_cyl[NT] = 
! 	{1,    1,    2,    1,    2,    1};   /* 2 = dbl step */
  PRIVATE int mtr_setup[NT] = 
! 	{HZ/4,3*HZ/4,HZ/4,2*HZ/4,3*HZ/4,3*HZ/4};/* in ticks */
  
  /*===========================================================================*
   *				floppy_task				     * 
   *===========================================================================*/
--- 153,200 ----
   * 3  720K    720K      9       80     300 RPM  250 kbps   Toshiba, et al.
   * 4  1.2M    360K      9       40     360 RPM  300 kbps   PC disk in AT drive
   * 5  1.2M    720K      9       80     360 RPM  300 kbps   Toshiba in AT drive
+  * 6  1.44M   1.44M    18	80     300 RPM  500 kbps   PS/2, et al.
+  *
+  * In addition, 720K diskettes can be read in 1.44MB drives, but that does 
+  * not need a different set of parameters.  This combination uses
+  *
+  * X  1.44M   720K	9	80     300 RPM  250 kbps   PS/2, et al.
   */
  PRIVATE int gap[NT] =
! 	{0x2A, 0x1B, 0x2A, 0x2A, 0x23, 0x23, 0x1B}; /* gap size */
  PRIVATE int rate[NT] = 
! 	{0x02, 0x00, 0x02, 0x02, 0x01, 0x01, 0x00}; /* 2=250,1=300,0=500 kbps*/
  PRIVATE int nr_sectors[NT] = 
! 	{9,    15,   9,    9,    9,    9,    18};   /* sectors/track */
  PRIVATE int nr_blocks[NT] = 
! 	{720,  2400, 720,  1440, 720,  1440, 2880}; /* sectors/diskette*/
  PRIVATE int steps_per_cyl[NT] = 
! 	{1,    1,    2,    1,    2,    1,     1};   /* 2 = dbl step */
  PRIVATE int mtr_setup[NT] = 
! 	{HZ/4,3*HZ/4,HZ/4,HZ,3*HZ/4,3*HZ/4,HZ};     /* in ticks */
! PRIVATE char spec1[NT] =
! 	{0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xAF}; /* step rate, etc. */
  
+ /* This driver hunts around for the proper density by simply trying them all
+  * until it finds one that works.  By defining DEFAULT_CLASS, one can reduce 
+  * the searching to only 5.25 inch or only 3.5 inch types.  The array
+  * drive_class contains a bit map for each drive, telling which of the NT
+  * combinations defined above should be tried.
+  */
+ PRIVATE char drive_class[NR_DRIVES] = {
+ #ifndef DEFAULT_CLASS
+ 	AUTOMATIC, AUTOMATIC		/* both drives can handle both types */
+ #else
+ #	if	(DEFAULT_CLASS==3)
+ 		THREE_INCH, THREE_INCH	/* drive 0 = 3.5 inch, drive 1 also */
+ #	endif
+ 
+ #	if	(DEFAULT_CLASS==5)
+ 		FIVE_INCH, FIVE_INCH	/* drive 0 = 5.25 inch, drive 1 also */
+ #	endif
+ #endif
+ };
+ 
  /*===========================================================================*
   *				floppy_task				     * 
   *===========================================================================*/
***************
*** 210,222 ****
    /* Decode the message parameters. */
    drive = m_ptr->DEVICE;
    if (drive < 0 || drive >= NR_DRIVES) return(EIO);
!   fp = &floppy[drive];		/* 'fp' points to entry for this drive */
    fp->fl_drive = drive;		/* save drive number explicitly */
    fp->fl_opcode = m_ptr->m_type;	/* DISK_READ or DISK_WRITE */
    if (m_ptr->POSITION % BLOCK_SIZE != 0) return(EINVAL);
    block = m_ptr->POSITION/SECTOR_SIZE;
!   if (block >= HC_SIZE) return(EOF);	/* sector is beyond end of 1.2M disk */
    d = fp->fl_density;		/* diskette/drive combination */
    fp->fl_cylinder = (int) (block / (NR_HEADS * nr_sectors[d]));
    fp->fl_sector = (int) interleave[(int)(block % nr_sectors[d])];
    fp->fl_head = (int) (block % (NR_HEADS*nr_sectors[d]) )/nr_sectors[d];
--- 245,263 ----
    /* Decode the message parameters. */
    drive = m_ptr->DEVICE;
    if (drive < 0 || drive >= NR_DRIVES) return(EIO);
!   fp = &floppy[drive];		/* 'fp' points to entry for this drive */  
    fp->fl_drive = drive;		/* save drive number explicitly */
    fp->fl_opcode = m_ptr->m_type;	/* DISK_READ or DISK_WRITE */
    if (m_ptr->POSITION % BLOCK_SIZE != 0) return(EINVAL);
    block = m_ptr->POSITION/SECTOR_SIZE;
!   if (block >= HC_SIZE) return(EOF); /* sector is beyond end of 1.44M disk */
    d = fp->fl_density;		/* diskette/drive combination */
+ 
+   /* Check the bit map to see if this density is allowed.  If not, skip it. */
+   while ((drive_class[drive] & (1 << d)) == 0) d = (d + 1) % NT;
+ 
+   /* Store the message parameters in the fp->fl array. */
+   fp->fl_density=d;
    fp->fl_cylinder = (int) (block / (NR_HEADS * nr_sectors[d]));
    fp->fl_sector = (int) interleave[(int)(block % nr_sectors[d])];
    fp->fl_head = (int) (block % (NR_HEADS*nr_sectors[d]) )/nr_sectors[d];
***************
*** 234,242 ****
  	 * means that we are trying at the wrong density.  Try another one.
  	 * Increment 'errors' here since loop is aborted on error.
  	 */
  	errors++;		/* increment count once per loop cycle */
   	if (errors % (MAX_ERRORS/NT) == 0) {
!  		d = (d + 1) % NT;	/* try next density */
   		fp->fl_density = d;
  		sectors = nr_sectors[d];
  		fp->fl_cylinder = (int) (block / (NR_HEADS * sectors));
--- 275,288 ----
  	 * means that we are trying at the wrong density.  Try another one.
  	 * Increment 'errors' here since loop is aborted on error.
  	 */
+ 	port_out(DMA_INIT, DMA_RESET_VAL);  /* reset the dma controller */
  	errors++;		/* increment count once per loop cycle */
   	if (errors % (MAX_ERRORS/NT) == 0) {
!                 d++;
! 
! 		/* Check bit map to skip illegal densities. */
! 		while ((drive_class[drive] & (1 << d)) == 0) d = (d + 1) % NT;
! 
   		fp->fl_density = d;
  		sectors = nr_sectors[d];
  		fp->fl_cylinder = (int) (block / (NR_HEADS * sectors));
***************
*** 249,254 ****
--- 295,310 ----
  	/* First check to see if a reset is needed. */
  	if (need_reset) reset();
  
+ 	/* Set the stepping rate */
+ 	if (current_spec1 != spec1[d]) {
+ 		fdc_out(FDC_SPECIFY);
+ 		fdc_out(current_spec1=spec1[d]);
+ 		fdc_out(SPEC2);
+ 	}
+ 
+  	/* Set the data rate */
+ 	if (pc_at) port_out(FDC_RATE, rate[d]);
+ 
  	/* Now set up the DMA chip. */
  	dma_setup(fp);
  
***************
*** 308,316 ****
     */
    if (user_phys == 0) panic("FS gave floppy disk driver bad addr", (int) vir);
    top_end = (int) (((user_phys + ct - 1) >> 16) & BYTE);
!   if (top_end != top_addr) panic("Trying to DMA across 64K boundary", top_addr);
  
    /* Now set up the DMA registers. */
    s = lock();
    port_out(DMA_M2, mode);	/* set the DMA mode */
    port_out(DMA_M1, mode);	/* set it again */
--- 364,373 ----
     */
    if (user_phys == 0) panic("FS gave floppy disk driver bad addr", (int) vir);
    top_end = (int) (((user_phys + ct - 1) >> 16) & BYTE);
!   if (top_end != top_addr)panic("Trying to DMA across 64K boundary", top_addr);
  
    /* Now set up the DMA registers. */
+   port_out(DMA_INIT, DMA_RESET_VAL);        /* reset the dma controller */
    s = lock();
    port_out(DMA_M2, mode);	/* set the DMA mode */
    port_out(DMA_M1, mode);	/* set it again */
***************
*** 410,416 ****
    if (r != OK) 
  	if (recalibrate(fp) != OK) return(ERR_SEEK);
    fp->fl_curcyl = (r == OK ? fp->fl_cylinder : -1);
!   if (r == OK && ps) {		/* give head time to settle on PS/2 */
  	clock_mess(2, send_mess);
  	receive(CLOCK, &mess);
    }
--- 467,473 ----
    if (r != OK) 
  	if (recalibrate(fp) != OK) return(ERR_SEEK);
    fp->fl_curcyl = (r == OK ? fp->fl_cylinder : -1);
!   if (r == OK && ((d == 6) || (d == 3))) {/* give head time to settle on 3.5 */
  	clock_mess(2, send_mess);
  	receive(CLOCK, &mess);
    }
***************
*** 574,580 ****
    } else {
  	/* Recalibration succeeded. */
  	fp->fl_calibration = CALIBRATED;
! 	if (ps) {		/* give head time to settle on PS/2 */
  		clock_mess(2, send_mess);
  		receive(CLOCK, &mess);
  	}
--- 631,637 ----
    } else {
  	/* Recalibration succeeded. */
  	fp->fl_calibration = CALIBRATED;
! 	if (ps||((d == 6) || (d == 3))) {/* give head time to settle on 3.5 */
  		clock_mess(2, send_mess);
  		receive(CLOCK, &mess);
  	}
***************
*** 605,623 ****
    restore(old_state);		/* interrupts allowed again */
    receive(HARDWARE, &mess);	/* collect the RESET interrupt */
  
!   /* Interrupt from the reset has been received.  Continue resetting. */
!   fp = &floppy[0];		/* use floppy[0] for scratch */
!   fp->fl_results[0] = 0;	/* this byte will be checked shortly */
!   fdc_out(FDC_SENSE);		/* did it work? */
!   r = fdc_results(fp);		/* get results */
!   status = fp->fl_results[0] & BYTE;
  
    /* Tell FDC drive parameters. */
    fdc_out(FDC_SPECIFY);		/* specify some timing parameters */
!   fdc_out(SPEC1);		/* step-rate and head-unload-time */
    fdc_out(SPEC2);		/* head-load-time and non-dma */
- 
-   for (i = 0; i < NR_DRIVES; i++) floppy[i].fl_calibration = UNCALIBRATED;
  }
  
  
--- 662,678 ----
    restore(old_state);		/* interrupts allowed again */
    receive(HARDWARE, &mess);	/* collect the RESET interrupt */
  
!   /* Interrupt from the reset has been received.  Clear each drive. */
!   for (i=0; i<NR_DRIVES; i++) {
! 	fdc_out(FDC_SENSE);	/* did it work? */
! 	fdc_results(&floppy[i]);/* get results (using each floppy[i]) */
! 	floppy[i].fl_calibration = UNCALIBRATED;
!   }
  
    /* Tell FDC drive parameters. */
    fdc_out(FDC_SPECIFY);		/* specify some timing parameters */
!   fdc_out(current_spec1=spec1[d]); /* step-rate and head-unload-time */
    fdc_out(SPEC2);		/* head-load-time and non-dma */
  }