Floppy Disk Controller: Difference between revisions

From OSDev.wiki
Jump to navigation Jump to search
[unchecked revision][unchecked revision]
Content added Content deleted
m (linkfixing, typofixing)
(→‎Forum Posts:: - added PIO mode link)
Line 136: Line 136:
===Forum Posts:===
===Forum Posts:===
* [[topic:13538|[TUTORIAL]: How to read (and supposedly write) floppies]]
* [[topic:13538|[TUTORIAL]: How to read (and supposedly write) floppies]]
* [[topic:310|floppy in pmode]]
* [[topic:310|Floppy in pmode]]
* [[topic:12279|Floppy Disk Driver]]
* [[topic:12279|Floppy Disk Driver]]
* [[topic:8405|floppy programming tutorial]]
* [[topic:8405|Floppy programming tutorial]]
* [[topic:11181|Multitask Floppy Driver]]
* [[topic:11181|Multitask Floppy Driver]]
* [[topic:17274|PIO mode information]]


===Implementations===
===Implementations===

Revision as of 13:51, 21 June 2008

The Floppy Disk Controller (FDC) is a (legacy) device that controls hardware for access to 3.5/5.25 inch floppy disks. There are a range of chips that have been produced for this function and include: 8272A, 82078, 82077SL & 82077AA.

A quick look at floppy hardware

Floppy controller is programmed through 8 registers accessed from 0x3F0 through 0x3F7 I/O ports. As usual on PC architecture, some of those registers have different meaning depending on whether you read or write them. For extra info, refer to the datasheet (see link below). Note that snippets and datasheets name those registers from their trigrams (e.g. SRA, MSR, DIR, CCR, etc)

enum FloppyRegisters {
   STATUS_REGISTER_A        = 0x3F0, // read-only
   STATUS_REGISTER_B        = 0x3F1, // read-only
   DIGITAL_OUTPUT_REGISTER  = 0x3F2,
   TAPE_DRIVE_REGISTER      = 0x3F3,
   MAIN_STATUS_REGISTER     = 0x3F4, // read-only
   DATA_RATE_SELECT_REGISTER= 0x3F4, // write-only
   DATA_FIFO                = 0x3F5,
   DIGITAL_INPUT_REGISTER   = 0x3F7, // read-only
   CONFIGURATION_CONTROL_REGISTER = 0x3F7, //write only
};


Note that all command parameter information and disk data transfers go through the FIFO. The other registers barely tell you if the disk/controller is busy or not (MSR), control the transfer rate (DIR, CCR, DRS) and select disks (DOR).

Beyond that basic configuration, you talk to the floppy controller by the way of _commands_ sent through the data fifo. Each command is a sequence of bytes that start with a command code and optional parameters. Note that most of these commands have 'option' bits (such as MFM, MT) which are not covered here.

enum FloppyCommands {
   READ_TRACK = 2,
   SPECIFY = 3,
   SENSE_DRIVE_STATUS = 4,
   WRITE_DATA = 5,
   READ_DATA = 6,
   RECALIBRATE = 7,
   SENSE_INTERRUPT = 8,
   WRITE_DELETED_DATA = 9,
   READ_ID = 10,
   READ_DELETED_DATA = 12,
   FORMAT_TRACK = 13,
   SEEK = 15,
   VERSION = 16,
   SCAN_EQUAL = 17,
   PERPENDICULAR_MODE = 18,
   CONFIGURE = 19,
   VERIFY = 22,
   SCAN_LOW_OR_EQUAL = 25,
   SCAN_HIGH_OR_EQUAL = 29,
};

There are several things you need to take into account when programming a floppy controller:

  • motor spinning: You need to turn the motor on and wait for a suitable amount of time after that so that the motor has reached the speed needed for data transfer. You're also welcome to stop the motor after you're done reading (but preferably not too early). The recommended "motor on" delays are 300 mS for a 3.5 inch floppy and 500 mS for a 5.25 inch floppy (these values should be conservative). If you start a read immediately after turning the floppy motor on, the internal electronics will fail to "lock on" to the signal being read and you'll get an error. It is possible to use this to minimize (or avoid) the "motor on" delay by repeatedly retrying the read until it works successfully. Typically a delay of 2 seconds is used for the "motor off" delay - longer delays increase the chance that the "motor on" delay can be avoided for additional data transfers (but leaving the motor on for longer can give users the perception that data is still being transfered, and that it's taking too long!).
  • data transfers: Those occur using the ISA DMA controller. The "data fifo" register is mainly there to exchange commands and status information with the controller (e.g. what track/sector you want, etc.)
  • head positions: Before you can read/write to a track, you need to issue a "seek/recalibrate" command to reach that track.

How do I read/write to the floppy in protected mode?

You need to re-implement the driver yourself (or find a suitable existing implementation in someone else's work). A complete workflow diagram is available on page 44 of the 82077AA datasheet (see below). Note that most of the time, you may skip some of those steps if you know the former state (e.g. is your motor still spinning ? are you still on the correct track, etc).

Can I program the floppy without using DMA ?

Yes. In this case, you will get an interrupt for every byte transferred. You then have to get the byte on the controller and write it in memory. This method can improve the time it takes to transfer the data (up to 10% faster for a rough/inaccurate guess), but the large number of interrupts can cripple CPU performance when other work is being done at the same time (especially for slower computers - AFAIK Windows 95 did this and could cause a 166 MHz Pentium to become entirely unusable during floppy transfers). By using DMA the floppy drive can be working hard without any noticable difference to CPU performance.

What's that "Sense Interrupt" thing ?

It mainly allows you to check if an operation has completed or failed. It's also the way you tell the floppy controller that you received the interrupt.

When should I recalibrate/seek ?

Unless you set the "implied seek" control bit (EIS using the "configure" command, but this is not supported everywhere), you have to issue a 'seek' command everytime you read/write to another track. Failing to do so might lead to silently reading the wrong track. Recalibration is required only when an error has been encountered while reading/writing (e.g. as part of the 'reset' sequence)

Pseudocode

Resetting the controller

Taken and translated from "floppy_tutorial" (see below). steprate, head unload time and other parameters could be retrieved from BIOS tables, as specified in the tutorial.

   outportb(controller.DOR,0x00);
   outportb(controller.DOR,0x0C);
   wait_for(controller.IRQ);      // IRQ6
   // sense interrupt
   {
      send_command(SENSE_INTERRUPT);
      (void) read_data_byte();
      (void) read_data_byte();
   }
   outportb(controller.CCR,0x00);
   // configure the drive
   {
      send_command(SPECIFY);
      send_data_byte(steprate_headunload);
      send_data_byte(headload_ndma);
   }

Sequence of Events

Documentation is fine, but it helps to know in what order you are supposed to do things. I can add more detail later --Mike

  1. Reset the controller by clearing bit 2 (0x04) in the DOR (Digital Output Register)
  2. Wait for some number of ms - I'm not sure how long
  3. Set bit 2 in the DOR to end reset state. An interrupt will be triggered, to which you must send the sense interrupt command _four_ times and read the results each time.
  4. Then, send the Specify (0x03) command, using values in the disk parameter table (see thread below)
  5. Send a recalibrate command to move the read head to track 0; an interrupt will be triggered. In response to that interrupt, send a sense command and test if it needs to be recalibrated again. If not, continue.
  6. If the motor is off, turn it on by setting the appropriate bit in the DOR. Wait for a while to let it spin up.
  7. If the disk change bit is set, seek forwards a track, then back, and test the disk changed bit in the DIR. If it is still set, there is no disk in the drive.
  8. Seek to the proper track
  9. Set up DMA and send the read command. An interrupt will be triggered when completely done.
  10. Start a timer to turn the motor off in about 2 seconds. If another request to read is received before the timer elapses, stop and restart the timer.


SRT, HLT and HUT

The datasheets I've read aren't too specific on what values should be used for the "specify" command (typically there's partially complete tables with no indication of what values should be). Usually people tend to copy values from other sources without knowing what they are, how they change depending on the data rate or how they can be "tweaked". In general the specify command should be sent whenever the data rate changes (and after controller reset).

  • SRT: This is the "Step Rate Time", which determines how long the floppy drive should wait for the head to move between tracks. A reasonable amount of time to allow for this is around 8 mS, but the actual value used depends on the data rate. The formula for calculating the delay from SRT value and the data rate is "seconds = (16 - SRT_value)/data_rate * 500000". To find the "best" SRT value this can be transformed into "SRT_value = (16 - seconds * data_rate / 500000". For a 1.44 MB floppy and 8 mS delay this gives "SRT_value = (16 - 0.008 * 500000 / 500000" or a value of 8.
  • HLT: This is the "Head Load Time", which determines how long the floppy drive should wait for the head to become ready after the head has been moved. A reasonable value for this is around 10 mS, but like the SRT the delay depends on the data rate. To find the delay from the HLT value and the data rate the formula is "seconds = HLT_value / data_rate * 1000". To find the "best" HLT value this can be transformed into "HLT_value = seconds * data_rate / 1000". For a 1.44 MB floppy and a 10 mS delay this gives "HLT_value = 0.01 * 500000 / 1000" or 5. For the specify command, a HLT value of 16 is represented with zero (no delay isn't possible).
  • HUT: This is the "Head Unload Time", which determines how long the floppy drive should wait before setting the head to it's unloaded state after data is read or written (I think that if the another command begins before this time expires the HLT delay is avoided, but can't be sure). A reasonable value for this is around 240 mS, but this delay also depends on the data rate. To find the delay from the HUT value and the data rate the formula is "seconds = HUT_value / data_rate * 8000". To find the "best" HLT value this can be transformed into "HUT_value = seconds * data_rate / 8000". For a 1.44 MB floppy and a 240 mS delay this gives "HLT_value = 0.24 * 500000 / 8000" or 15. For the specify command, a HUT value of 128 is represented with zero (no delay isn't possible).


Gap Lengths

There's 2 different gap lengths that are controlled by software for specifying the amount of blank space between sectors. The gap lengths are used by the floppy hardware to help find the "start of sector" markers, and to avoid problems caused by speed variations in different floppy drives (for e.g. writing a sector on a slower drive would cause the sector to take up more physical space on the disk, potentially overwriting the next sector). The first gap length (typically called "GPL1") is used when reading or writing data, and sets the length of the gap between sectors that the floppy should expect (but doesn't change this gap). The second (typically called "GPL2") is the gap length for the "format track" command, which specifies the amount of space between sectors to use. The actual gap lengths depend on the media being used, but GPL1 is always a bit less than GPL2 so that the floppy hardware starts expecting the next sector near the end of the blank space.

Standard values for these gap lengths can be found in the floppy controller datasheets. However, it is possible to squeeze more sectors on each track by reducing the gap length. For example, by using a gap length of 0x1C when formatting (and 0x0C when reading/writing) instead of 0x54 (and 0x1B when reading/writing) it's possible to format a normal 1440 KB floppy disk with 21 sectors per track to create a 1680 KB floppy disk. These disks may be unreliable on very old computers (where accuracy and speed variation may be worse), but were considered reliable enough for Microsoft to distribute a version of Windows on 1680 KB floppies (Windows 95 on a set of 12 disks IIRC).


Other Devices

The floppy device controller can also be used to control "Zip Drives" (made by Iomega) and older tape drives. There's also a variation for 3.5 inch 2880 KB floppy drives (using 1 Mbps data rates) that is supported by almost all floppy drive controllers.

Related Links

Articles

Reference Documents

Forum Posts:

Implementations