Floppy Disk Controller: Difference between revisions

From OSDev.wiki
Jump to navigation Jump to search
[unchecked revision][unchecked revision]
Content added Content deleted
(Cleaned up the article and added some additional information.)
(Added even more information.)
Line 73: Line 73:
Before you can read or write to a track, you need to issue a Seek/Recalibrate command to reach that track.
Before you can read or 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|protected mode]]?==
== How do I read/write to the floppy in [[Protected Mode|Protected Mode]]? ==
You need to reimplement 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 the motor still spinning?", "am I still on the correct track?", etc.).

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 ?====


=== 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.
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 ?====
=== 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.
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 ?====
=== When should I seek/recalibrate? ===

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)
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==
== Pseudo-code ==
=== Resetting The FDC ===

This code was 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.
===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.
<source lang="c">
<source lang="c">
void ResetFDC()
outportb(controller.DOR,0x00);
{
outportb(controller.DOR,0x0C);
// Disable and then enable the controller again.
wait_for(controller.IRQ); // IRQ6
outb(Controller.DOR,0x00); // Note that DOR uses another port on FDC 0 and FDC 1, as stated in 'Quick Look at the Hardware'.
outb(Controller.DOR,0x0C);

wait_for_irq(); // Usually this is IRQ 6.

// sense interrupt
// sense interrupt
{
{
send_command(SENSE_INTERRUPT);
send_command(SENSE_INTERRUPT);
(void) read_data_byte();
read_data_byte();
(void) read_data_byte();
read_data_byte();
}
}

outportb(controller.CCR,0x00);
outb(controller.CCR,0x00);

// configure the drive
// configure the drive
{
{
Line 111: Line 113:
send_data_byte(headload_ndma);
send_data_byte(headload_ndma);
}
}
}
</source>
</source>


=== When waiting for IRQ6, I get stuck! ===
===Sequence of Events===
This can have a number of problems. Either something went wrong somewhere (an error was generated that you didn't see), or you just configured the FDC incorrectly. Another problem that can appear is with code such as the following:


<source lang="c">
volatile byte ReceivedIRQ = false;

// This function gets called when an IRQ6 is generated.
void FloppyHandler()
{
ReceivedIRQ = true;
}

// Waits for an IRQ to be issued.
void WaitForIRQ()
{
ReceivedIRQ = false;
while(!ReceivedIRQ) ;
}

// Start of the function above.
void ResetFloppy()
{
DisableController();
EnableController();

WaitForIRQ();
}
</source>

Sure this code ''looks'' OK, but some emulators or floppy drives might manage to be faster than your code. What if you've just returned from ''EnableController()'' and the floppy already issued the IRQ6? Then ''ReceivedIRQ'' will be set to true, your OS will enter ''WaitForIRQ()'', set it to false again and then infinitely loop, waiting for the IRQ that has already been received. Therefor it's usually better to do something like:

<source lang="c">
volatile byte ReceivedIRQ = false;

// This function gets called when an IRQ6 is generated.
void FloppyHandler()
{
ReceivedIRQ = true;
}

// Start of the function above.
void ResetFloppy()
{
ReceivedIRQ = false; // This will prevent the FDC from being faster than us!

DisableController();
EnableController();

while(!ReceivedIRQ) ; // We'll get the IRQ that happened between the first line of this function and here.
}
</source>

=== 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''
Documentation is fine, but it helps to know in what order you are supposed to do things. ''I can add more detail later --Mike''


Line 129: Line 183:




===SRT, HLT and HUT===
=== 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).
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.
* 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).
* 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).
* 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===


=== 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.
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).
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 ===

===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.
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==
== Related Links ==
===Articles===
=== Articles ===
* [[DMA]]
* [[DMA]]


===Reference Documents===
=== Reference Documents ===
* http://www.osdever.net/documents/82077AA_FloppyControllerDatasheet.pdf?the_id=41
* http://www.osdever.net/documents/82077AA_FloppyControllerDatasheet.pdf?the_id=41
* http://bos.asmhackers.net/docs/floppy/
* http://bos.asmhackers.net/docs/floppy/
Line 159: Line 208:
* [http://www.intel.com/design/archives/periphrl/docs/29046803.htm Intel 82078 CHMOS SINGLE-CHIP FLOPPY DISK CONTROLLER datasheet]
* [http://www.intel.com/design/archives/periphrl/docs/29046803.htm Intel 82078 CHMOS SINGLE-CHIP FLOPPY DISK CONTROLLER datasheet]


===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]]
Line 167: Line 216:
* [[topic:17274|PIO mode information]]
* [[topic:17274|PIO mode information]]


===Implementations===
=== Implementations ===
* [http://koders.com/c/fid051291340B94EC7F5D1A38EF6843466C0B07627B.aspx?s=fdc freedos] (C,GPL)
* [http://koders.com/c/fid051291340B94EC7F5D1A38EF6843466C0B07627B.aspx?s=fdc freedos] (C,GPL)
* [http://bos.asmhackers.net/docs/floppy/snippet_9/fdc.c GazOS] (C,GPL)
* [http://bos.asmhackers.net/docs/floppy/snippet_9/fdc.c GazOS] (C,GPL)

Revision as of 11:23, 5 September 2009

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.

Quick Look at the Hardware

The floppy controller is programmed through 8 registers, which can be accessed from ports 0x3F0 through 0x3F7. As usual on the PC architecture, some of those registers have different meanings depending on whether you read from or write to them. For extra information, please refer to the datasheet (see the link below). Note that snippets and datasheets name those registers based on their trigrams (e.g. SRA, MSR, DIR, CCR, etc.).

The most common floppy registers can be found in the following enumeration:

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 this only applies to FDC (Floppy Disk Controller) 0. FDC 1 can usually be found at address 0x370, some people prefer to give the registers values based on their offset from the base address, and then add the FDC's base address based on what FDC you want to use. So STATUS_REGISTER_A would have value 0, STATUS_REGISTER_B value 1, and so on. Then when you want to access STATUS_REGISTER_B on FDC 1, you would use FDC1_BASE_ADDRESS + STATUS_REGISTER_B = 0x370 + 1 = 0x371.

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 sending commands through the data FIFO. Each command is a sequence of bytes that starts with a command code and optionally has some parameters. Note that most of these commands have 'option' bits, such as MFM and 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
};

Programming Notes

There are several things you need to take into account when programming the floppy controller(s). These things include motor spinning, data transfers and head positions.

Motor Spinning

What you need to do is turn the motor on and then wait for a while so the motor gets the time it needs to reach the speed needed for data transfer. After you're done reading (or writing), you can stop the floppy motor if you want (but be careful not to stop it too early). The recommended delays when turning the motor on are:

  • 300 milliseconds (for a 3.5" floppy).
  • 500 milliseconds (for a 5.25" floppy).

Note that these are 'approximate' values and serve as an example, but they should be more than enough for a floppy drive to spin up correctly. When you start reading immediately after the motor is turned on, the internal electronics will fail to 'lock on' to the signal you sent and you'll get error.

Another way of performing an operation is not delaying or waiting at all, but just enter a loop and keep trying to perform the operation until it works.

When you want to turn the motor off, you should typically wait an additional 2 seconds. You could leave the motor on too, but it might give the user the idea that the floppy is still transfering data and that it's taking an awfully long time.

Data Transfers

These occur when you're using the ISA DMA controller. The data FIFO register is mainly used to exchange commands and status information with the controller (e.g. what track/sector to use).

Head Positions

Before you can read or 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 reimplement 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 the motor still spinning?", "am I 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 seek/recalibrate?

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)

Pseudo-code

Resetting The FDC

This code was 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.

void ResetFDC()
{
    // Disable and then enable the controller again.
    outb(Controller.DOR,0x00); // Note that DOR uses another port on FDC 0 and FDC 1, as stated in 'Quick Look at the Hardware'.
    outb(Controller.DOR,0x0C);

    wait_for_irq(); // Usually this is IRQ 6.

    // sense interrupt
    {
       send_command(SENSE_INTERRUPT);
       read_data_byte();
       read_data_byte();
    }

    outb(controller.CCR,0x00);

    // configure the drive
    {
       send_command(SPECIFY);
       send_data_byte(steprate_headunload);
       send_data_byte(headload_ndma);
    }
}

When waiting for IRQ6, I get stuck!

This can have a number of problems. Either something went wrong somewhere (an error was generated that you didn't see), or you just configured the FDC incorrectly. Another problem that can appear is with code such as the following:

volatile byte ReceivedIRQ = false;

// This function gets called when an IRQ6 is generated.
void FloppyHandler()
{
   ReceivedIRQ = true;
}

// Waits for an IRQ to be issued.
void WaitForIRQ()
{
   ReceivedIRQ = false;
   while(!ReceivedIRQ) ;
}

// Start of the function above.
void ResetFloppy()
{
    DisableController();
    EnableController();

    WaitForIRQ();
}

Sure this code looks OK, but some emulators or floppy drives might manage to be faster than your code. What if you've just returned from EnableController() and the floppy already issued the IRQ6? Then ReceivedIRQ will be set to true, your OS will enter WaitForIRQ(), set it to false again and then infinitely loop, waiting for the IRQ that has already been received. Therefor it's usually better to do something like:

volatile byte ReceivedIRQ = false;

// This function gets called when an IRQ6 is generated.
void FloppyHandler()
{
   ReceivedIRQ = true;
}

// Start of the function above.
void ResetFloppy()
{
    ReceivedIRQ = false; // This will prevent the FDC from being faster than us!

    DisableController();
    EnableController();

    while(!ReceivedIRQ) ; // We'll get the IRQ that happened between the first line of this function and here.
}

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