Floppy Disk Controller: Difference between revisions

m
Bot: Replace deprecated source tag with syntaxhighlight
[unchecked revision][unchecked revision]
(Better info on "disk change" flag)
m (Bot: Replace deprecated source tag with syntaxhighlight)
 
(28 intermediate revisions by 15 users not shown)
Line 6:
 
=== Accessing Floppies in Real Mode ===
For bootloaders or OSes that run with the CPU remaining in [[Real Mode]], use [[BIOS (PC)#BIOS functions|BIOS Function]]
INT13h AH=2 (read) or AH=3 (write) to access floppy drives. You need to know the "drive number" (typically 0 or 1), and put that value in DL.
More detailed info can be found in the [[ATA in x86 RealMode (BIOS)]] article, because accessing a floppy is identical to accessing a hard disk
(using CHS) in Real Mode. The rest of this article deals with creating Protected Mode drivers for the floppy subsystem.
 
Note: the Extended BIOS Int13h functions do not work with floppies.
Note: the BIOS IRQ0 handler remembers a timeout for turning the motor off, from the last BIOS floppy access. The last access may have been
 
NoteNote2: the BIOS IRQ0 handler remembers a timeout for turning the motor off, from the last BIOS floppy access. The last access may have been
attempting to load your bootloader. So, in the distant future, if the BIOS ever receives 36 more IRQ0 ticks (if you ever return to Real Mode)
it may turn off your floppy motors for you, once.
Line 27 ⟶ 29:
subsystem is the primary remaining one that accesses the obsolete and terrible ISA DMA system for its data transfers.
 
=== How Many Controllers? One! Usually. ===
Each ribbon cable for floppy drives can support 2 drives. One floppy controller chip can control 2 floppy cables, for a total
of 4 drives. It is theoretically possible for a system to have more than one controller chip, but you will not find anymany existing systems with
more than one. If a system were to havehas more than one floppy controller, the second controller wouldwill be found at a base IO port address of 0x370.
 
Some systems also have a third floppy controller, but these are exceedingly rare. Most of these will have originally been disk
=== How Many Drives? ===
duplication systems. The third floppy controller can be found at a base IO port address of 0x360.
 
=== How Many Drives? ===
It is probably wisest to always get the drive count and types from [[CMOS]], register 0x10.
 
Line 52 ⟶ 56:
numbers are typically 0 to 79 inclusive, heads are 0 or 1, and sector numbers are 1 to 18 inclusive. Asking for sector number 0
is always highly illegal and this is a major source of errors in prototype driver code.
 
It is, however, much more logical to address things in LBA (Logical Block Addressing), as the first sector is at 0 (like an array).
Conversion between the two is rather simple. The equations are as follows:
 
''CYL = LBA / (HPC * SPT)''
 
''HEAD = (LBA % (HPC * SPT)) / SPT''
 
''SECT = (LBA % (HPC * SPT)) % SPT + 1''
 
''LBA = ( ( CYL * HPC + HEAD ) * SPT ) + SECT - 1''
 
This can be described in C with the following code:
 
<syntaxhighlight lang="c">
void lba_2_chs(uint32_t lba, uint16_t* cyl, uint16_t* head, uint16_t* sector)
{
*cyl = lba / (2 * FLOPPY_144_SECTORS_PER_TRACK);
*head = ((lba % (2 * FLOPPY_144_SECTORS_PER_TRACK)) / FLOPPY_144_SECTORS_PER_TRACK);
*sector = ((lba % (2 * FLOPPY_144_SECTORS_PER_TRACK)) % FLOPPY_144_SECTORS_PER_TRACK + 1);
}
</syntaxhighlight>
 
You would then send this data to the floppy controller.
 
=== DMA Data Transfers ===
Line 77 ⟶ 105:
 
If you are using PIO mode in a singletasking environment then the IRQ6s just waste CPU cycles, and you should be using polling instead.
You can usually shut the IRQs off by setting bit 3 (value = 8) of the Digital OuptputOutput Register (see below). If you want, you can even
toggle that bit on and off for specific commands, so that you receive some types of interrupts but not others.
 
Line 86 ⟶ 114:
if there were any errors. See below for more detail.
 
==== Bochs canCan't handleHandle Polling PIO ====
If you try to turn off the IRQs in Bochs (to use pure polling PIO mode), Bochs will panic. It may also not be able to handle
non-DMA data transfers properly, even if IRQs are turned on. It is currently mainly designed to model the floppy using DMA.
Line 98 ⟶ 126:
in all 3 modes.
 
=== Most commandsCommands runRun "silentlySilently" ===
There are only a few commands/conditions that produce interrupts: a reset (in polling mode only), Seek, Recalibrate, or one of the
read/write/verify/format commands. Several commands do not produce any result bytes, either. If you want to verify that a silent
Line 114 ⟶ 142:
However, using IO Port reads to generate delays (or polling MSR) leads to poor performance in a multitasking environment, and
you may want to put the driver to sleep for the shortest possible period (microsleep), instead.
 
=== Reliability ===
In real hardware, floppy drives are extremely unreliable. On emulators, it doesn't matter -- but for code that is intended for
real hardware, make sure to retry every command at least twice more after any error.
 
== Registers ==
Line 123 ⟶ 155:
The basic set of floppy registers can be found in the following enumeration:
 
<sourcesyntaxhighlight lang="c">
enum FloppyRegisters
{
Line 136 ⟶ 168:
CONFIGURATION_CONTROL_REGISTER = 0x3F7 // write-only
};
</syntaxhighlight>
</source>
 
All commands, parameter information, result codes, and disk data transfers go through the FIFO port.
Line 149 ⟶ 181:
access STATUS_REGISTER_B on FDC 1, you would use FDC1_BASE_ADDRESS + STATUS_REGISTER_B = 0x370 + 1 = 0x371.
 
=== Bitflags and Registers that you can rely on ===
As said above, the most common controller chip has 3 modes, and many bitflags in the registers are different (or opposite!) depending on
the mode. However, all of the important registers and bitflags remain the same between modes.
They are the following:
 
=== FIFO ===
FIFO: The FIFO register may not have a 16byte buffer in all modes, but this is a minor difference that does not really affect its operation.
 
=== Tape Drive Register ===
TDR: The Tape Drive Register is identical in all modes, but it is useless (you will never find functional equipment that requires it).
 
TDR: The Tape Drive Register is a R/W register which is identical in all modes. According to the documentation for the Intel 82077AA, the two least significant bits form a number between 1 and 3 (0 is an invalid value, drive 0 may not be selected) that assigns tape support to one particular drive out of four total drives. It is probably irrelevant for modern uses, is not supported by any emulator and is probably not worth implementing support for unless you have all the real hardware to test this functionality on.
 
==== DOR bitflag definitions ====
 
{| {{wikitable}}
Line 207 ⟶ 241:
 
Note2: if you want to execute a command that accesses a disk (see the command list below), then that respective disk must have its
motor spinning (and up to speed), '''and''' its "select" bits must be set in the DOR, '''first''' -- but if you wait more than a.
couple of seconds after turning a motor on, it will turn itself off automatically and your command will fail.
 
Note3: toggling DOR reset state requires a 4 microsecond delay. It may be smarter to use DSR reset mode, because
the hardware "untoggles" reset mode automatically after the proper delay.
 
==== MSR bitflag definitions ====
 
{| {{wikitable}}
Line 268 ⟶ 301:
DIO and BUSY should be checked to verify proper command termination (the end of "result phase" and beginning of "command phase").
 
==== CCR and DSR ====
The bottom 2 bits of DSR match CCR, and setting one of them sets the other. The upper 6 bits on both DSR and CCR default to 0, and
can always be set to zero safely. So, even though they have different bit definitions, you always set them with identical values, and
Line 293 ⟶ 326:
Note: There is also a 300Kbps (value = 1), and a 250Kbps setting (value = 2) but they are only for utterly obsolete drives/media.
 
==== DIR register, Disk Change bit ====
This bit (bit 7, value = 0x80) is fairly useful. It gets set if the floppy door was opened/closed. Sadly, almost all the emulator
programs set and clear this bit completely inappropriately (especially after a reset).
Line 303 ⟶ 336:
 
Note2: You must turn on the drive motor bit before you access the DIR register for a selected drive (you do not have to wait
for the motor to spin up, though). It may also be necessary to read the register twicefive (andtimes (discard the first value4 values), when
'''changing''' the selected drive -- because "selecting" sometimes takes a little time.
 
Basically, you want to keep a flag for whether there is media in each drive. If Disk Change is set and there was media,
the OS should get a signal that the previous media was ejected.
 
Once the Disk Change bit getssignals set"true" (and you have "processed that message"event"), you need to try to clear the bit. The main way to
clear the bit is with a '''successful''' Seek/Recalibrate to a '''new''' cylinder on the media. (Recalibrate orA Resetreset dodoes not work. And ifIf the
controller thinks the heads are already on the correct cylinder, it will eat thea Seek command without clearing the Disk
Change bit.) If the seekheads fails,are youalready canon becylinder fairly certain0, thata thereRecalibrate is also a no-op.) media inIf the driveseek anymore.'''fails''', Ityou iscan importantbe to note thatfairly
certain that there is no media in the drive anymore. It is important to note that
this means you should check the value of Disk Change just prior to every Seek command that you do, because otherwise you
will lose any Disk Change information. This is also true for implied seeks, and relative seeks.
Line 322 ⟶ 356:
 
== Programming Details ==
Overall, the controller needs to be initialized and reset once (see below for the steps involved). Then a particular drivedrives can be
accessed. To doaccess that, youdrives:
# Turn on the drive's motor and select the drive, using an "outb" command to the DOR IO port.
# Wait for awhile for the motor to get up to speed, using some waiting scheme (but don't wait too long).
# Issue your command byte plus some parameter bytes (the "command phase") to the FIFO IO port.
# Exchange data with the drive / "seek" the drive heads (the "execution phase"), on the FIFO IO port.
Line 347 ⟶ 381:
The reason to leave the motor on is that your driver may not know if there is a queue of sector reads or writes
that are going to be executed next. If there are going to be more drive accesses immediately, they won't need to wait
for the motor to spin up, if the motor is still on.
 
The suggested delays when turning the motor on are:
Line 387 ⟶ 421:
==== Reinitialization ====
 
The BIOS probably leaves the controller in its default state. Polling"Drive polling mode" on, FIFO off, threshold = 1, implied seek off, lock off. This
lock off. This is a lousy state for the controller to be in, and your OS will need to fix it. The BIOS probably also does not have any better
any better guess as to the proper values for the "Specify" command than your OS does (the values are specific to the particular drive). There
drive). There is certainly no reason why you should trust the BIOS to have done any of it '''correctly.'''
 
So, when your OS is initializing:
# Send 4 Sense Interrupt commands to the controller. This may clear a problem with default "polling mode".
# Send a Version command to the controller.
# Verify that the result byte is 0x90 -- if it is not, it might be a good idea to abort and not support the floppy subsystem. Almost all of the code based on this article will work, even on the oldest chipsets -- but there are a few commands that will not.
# If you don't want to bother having to send another Configure command after every Reset procedure, then:
## Send a better Configure command to the controller. A suggestion would be: drive polling mode off, FIFO on, threshold = 8, implied seek on, precompensation 0.
## send a Lock command.
# Do a Controller Reset procedure.
# Send a Recalibrate command to each of the drives. (You can do them all at the same time, or you can use this as an opportunity to handle the "Disk Change" bit, and/or you can use this as a chance to detect which drives actually have media in them.)
 
==== Controller Reset ====
Line 411 ⟶ 444:
## Bit 2 (value = 4) in the DOR: Save the current/"original" value of the DOR, write a 0 to the DOR, wait 4 microseconds, then write the original value (bit 2 is always set) back to the DOR.
## '''or''' Bit 7 (value = 0x80) in the DSR: Precalculate a good value for the DSR (generally 0), and OR it with 0x80. Write that value to the DSR.
# Wait for the resulting IRQ6. HIHI!!(unless onlyyou have IRQs turned off in pollingthe mode?DOR)
# SendIf a(and only if) [[#Configure|drive polling mode]] is turned on, send 4 Sense Interrupt commandcommands (required).
# If your OS/driver never turnedsent offa polling mode (see the ConfigureLock command), then you haveprobably need to send 3a morenew SenseConfigure Interrupt commands.command (Alternately, you can tell if you need the extrafifo 3settings bywere examininglost the top 2 bits ofin the st0 byte returned by the first Sense Interruptreset). If both bits are set, you need to send 3 more.)
# If your OS/driver never sent a Lock command, then you need to send a new Configure command (the fifo information was lost in the reset).
# Do a Drive Select procedure for the next drive to be accessed.
 
Line 421 ⟶ 453:
 
Note2: Emulators will often set the Disk Change flag to "true" after a reset, '''but this does not happen on real hardware''' -- it is
a shared bug in all the emulators that do it. Another shared bug is that most emulators do not fire an IRQ6 if disk polling mode is off.
 
Note3: A reset does not change the [[#Configure|drive polling mode or implied seek]] settings.
 
==== Drive Selection ====
Line 438 ⟶ 472:
command fails -- this may be the reason why.
 
# Turn the drive motor bit on.
# Read DIR. If the "Disk Change" bitflag is set to "true", then the floppy drive door was opened, so the OS needs to test if a new disk is in the drive.
 
Line 453 ⟶ 488:
# Verify that RQM = 1 and DIO = 0 ((Value & 0xc0) == 0x80) -- if not, reset the controller and start all over.
# Send your chosen command byte to the FIFO port (port 0x3F5).
# In a loop: loop on reading MSR until RQM = 1. Verify DIO = 0, then send the next parameter byte for the command to the FIFO port.
# Either Execution or Result Phase begins when all parameter bytes have been sent, depending on whether you are in PIO mode, and the command has an Execution phase. If using DMA, or the command does not perform read/write/head movement operations, skip to the Result Phase.
# (In PIO Mode Execution phase) read MSR, verify NDMA = 1 ((Value & 0x20) == 0x20) -- if it's not set, the command has no Execution phase, so skip to Result phase.
Line 480 ⟶ 515:
with a * and a comment.
 
<sourcesyntaxhighlight lang="c">
enum FloppyCommands
{
Line 494 ⟶ 529:
READ_DELETED_DATA = 12,
FORMAT_TRACK = 13, // *
DUMPREG = 14,
SEEK = 15, // * seek both heads to cylinder X
VERSION = 16, // * used during initialization, once
Line 504 ⟶ 540:
SCAN_HIGH_OR_EQUAL = 29
};
</syntaxhighlight>
</source>
 
==== "Deleted sectors" ====
Deleted sectors is a legacy feature, dating back to the 1970s, when IBM data entry terminals stored a single database record in each (128 byte) floppy sector, and a record could be marked as deleted by writing a different address mark before the sector. This feature was almost never used with IBM PC and compatibles – exceptions were occasional abuse by copy protection schemes, and (possibly) hiding bad sectors. Few (if any) emulators emulate this functionality, and many late model FDCs didn't implement it.
 
The "WRITE_DELETED_DATA" command can be used to create a deleted sector, and the "READ_DELETED_DATA" command can be used to read one back.
 
==== Option bits ====
Line 514 ⟶ 555:
===== Bit MF =====
Value = 0x40. "MFM" magnetic encoding mode. Always set it for read/write/format/verify operations.
 
A zero value represents the old "single density" FM format, which (on IBM PCs) was only used by 8-inch floppies (which were extremely rare). Few people ever used FM format, except for data interchange with non-PC systems (such as CP/M or minicomputers). Later model FDCs (e.g. Intel 82078) dropped support for it.
 
===== Bit SK =====
Value = 0x20. Skip mode. IgnoreThis thisskips bit"deleted andsectors" leave(see itexplanation cleared,in unlesssection you have a really good reason not toabove).
 
Ignore this bit and leave it cleared, unless you have a really good reason not to.
 
==== Status Registers ====
Line 544 ⟶ 590:
==== Configure ====
This command initializes controller-specific values: the data buffer "threshold" value, implied seek enable, FIFO disable, polling enable.
(And "Write Precompensation".) A good setting is: implied seek on, FIFO on, drive polling mode off, threshold = 8, precompensation 0.
 
If you enable implied seeks, then you don't have to send Seek commands (or Sense Interrupt commands for the Seek commands).
Line 552 ⟶ 598:
transfer!).
 
PollingDrive polling mode is just an annoyance that is there for backwards software compatibility. It makes you need extra Sense Interrupts
after every reset. Always turn it off, if you send a Configure command.
 
Line 563 ⟶ 609:
* Configure command = 0x13
* First parameter byte = 0
* Second parameter byte = (implied seek enableENable << 6) | (fifo '''DIS'''able << 5) | (drive polling enablemode '''DIS'''able << 4) | thresh_val (= threshold - 1)
* Third parameter byte = precomp_val = 0
* No result bytes.
Line 614 ⟶ 660:
 
Keeping statistics in that way only works when the drive in question is used often. Now that internal floppy drives are nearly obsolete,
it is worthless. So the current recommendataionrecommendation is just to use very safe values, and forget about performance.
 
If you look up spec sheets for individual floppy drives, they usually show a worst-case "track to track seek time" = SRT, but not the other two.
Line 637 ⟶ 683:
bitflag in the controller. It is required in three circumstances that produce interrupts.
 
# After doing a Controller Reset procedure (youwith may[[#Configure|drive needpolling tomode]] sendturned 4 of them, then)on.
# After the completion of a Seek command (or Relative Seek).
# After the completion of a Recalibrate command.
 
These are the only times when you should send a Sense Interrupt. You should still send them even if you have IRQs turned off in
the DOR and you are using PIO polling instead. If you send Sense Interrupt commands at other times: the command will complete, return
a 0x80, and then lock up the controller until you do a Reset.
 
* Sense Interrupt command = 0x8
Line 652 ⟶ 699:
Note: if you try to read the result bytes without waiting for RQM to set, then you are likely to always get an incorrect result value
of 0. This is also likely to get your driver out of sync with the FDC for input/output. The correct value of st0 after a reset should
be 0xC0 | drive number (drive number = 0 to 3). After a Recalibrate/Seek it should be 0x20 | drive number.
 
==== Recalibrate ====
Line 713 ⟶ 760:
* Second parameter byte = cylinder number
* Third parameter byte = head number (yes, this is a repeat of the above value)
* ThirdFourth parameter byte = starting sector number
* FourthFifth parameter byte = 2 (all floppy drives use 512bytes per sector)
* FifthSixth parameter byte = EOT (end of track, the last sector countnumber on tothe transfertrack)
* SixthSeventh parameter byte = 0x1b (GAP1 default size)
* SeventhEighth parameter byte = 0xff (all floppy drives use 512bytes per sector)
 
* First result byte = [[#st0|st0]] status register
Line 730 ⟶ 777:
value of 0. This is also likely to get your driver out of sync with the FDC for input/output.
 
Note2: You'll see that there's no sector count parameter. Instead, the FDC figures out when to stop by the DMA signaling to the FDC
Note2: Floppy media and electronics are well known for being unreliable. Any read or write command that fails should be retried
that it's done, or in PIO mode, by the FDC experiencing a FIFO overrun or underrun.
 
Note2Note3: Floppy media and electronics are well known for being unreliable. Any read or write command that fails should be retried
at least twice, unless it was a write and failed on "write protect".
 
Line 753 ⟶ 803:
== Code Examples ==
 
=== A common coding error example ===
The following code intentionally contains a common bug that causes an infinite loop (waiting for IRQ6) on most emulators.
 
<sourcesyntaxhighlight lang="c">
volatile byte ReceivedIRQ = false;
 
Line 780 ⟶ 830:
WaitForIRQ();
}
</syntaxhighlight>
</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
Line 786 ⟶ 836:
set it to false again and then infinitely loop, waiting for an IRQ that has already been received. It's usually better to do something like:
 
<sourcesyntaxhighlight lang="c">
volatile byte ReceivedIRQ = false;
 
Line 821 ⟶ 871:
outb(Controller.FIFO, headload_ndma);
}
</syntaxhighlight>
</source>
 
== Related Links ==
Line 832 ⟶ 882:
* http://bos.asmhackers.net/docs/floppy/docs/floppy_tutorial.txt
* [http://www.intel.com/design/archives/periphrl/docs/29046803.htm Intel 82078 CHMOS SINGLE-CHIP FLOPPY DISK CONTROLLER datasheet (useless)]
* http://www.brokenthorn.com/Resources/OSDev20.html
 
=== Forum Posts ===
Line 842 ⟶ 893:
 
=== Implementations ===
* [httphttps://kodersgithub.com/cFDOS/fid051291340B94EC7F5D1A38EF6843466C0B07627Bkernel/blob/master/drivers/floppy.aspx?s=fdcasm freedosFreeDOS] (C, GPL)
* [http://bos.asmhackers.net/docs/floppy/snippet_9/fdc.c GazOS] (C, GPL)
* [http://bos.asmhackers.net/docs/floppy/snippet_5/FLOPPY.ASM RDOS] (ASMAssembly, GPL)
* [httphttps://wwwgithub.gelato.unsw.edu.aucom/lxrtorvalds/sourcelinux/blob/master/drivers/block/floppy.c Linux] (C, GPL)
 
[[de:Floppy Disk Controller]]
[[Category:Storage]]
[[Category:Common Devices]]