Files
oldlinux-files/study/hardware/Floppy/FloppyPrograming.htm
2024-02-19 00:25:23 -05:00

3634 lines
100 KiB
HTML
Raw Blame History

<!doctype html public "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>
Programming the NEC &micro;PD765 and Intel 82072/7 Floppy Disk
Controller
</title>
</head>
<!---------------------------------------------------------------------------------------->
<body onResize='vPQ2()' onLoad='vPQ1()' onScroll='vPQ2()' onMove='vPQ2()' bgcolor="#ffffcc" link="red" vlink="purple" alink="olive">
<SCRIPT>
<!--
var vPQ3=0;
var vPQ4=62;
var vPQ5=470;
var vPQ6=12;
var vPQ7=200;
var vPQ8=0;
var vPQ9=0;
var vPQ0,vPQ10;
var vPQ11,vPQ12,vPQ13,vPQ8,vPQ14;
var vPQ15,vPQ16,vPQ17=500;
function vPQ1()
{
if(vPQ8==1)
return true;
if(document.all)
{
vPQ11=document.all("vPQ18").style;
vPQ12=document.all("vPQ19").style;
vPQ20=vPQ4+vPQ3+0;
vPQ21=vPQ5+vPQ6+vPQ3;
vPQ22='visible';
vPQ23='hidden';
}
else
{
vPQ22='show';
vPQ23='hide';
vPQ11=document.vPQ18;
vPQ12=document.vPQ19;
vPQ20=vPQ4+vPQ3+15;
vPQ21=vPQ5+vPQ6+vPQ3+15;
}
if(vPQ0=="00880204")
{
if(document.all)
{
vPQ12.display='none';
vPQ11.display='none';
}
return true;
}
if(document.all)
{
if(document.body.clientHeight<vPQ7||document.body.clientWidth<vPQ7)
return true;
}
else if(document.layers)
{
if(window.innerHeight<vPQ7||window.innerWidth<vPQ7)
return true;
}
vPQ8=1;
vPQ2();
vPQ11.visibility=vPQ22;
if(document.layers)
vPQ24();
}
function vPQ24()
{
if(pageXOffset!=vPQ15||pageYOffset!=vPQ16)
{
vPQ15=pageXOffset;
vPQ16=pageYOffset;
vPQ2();
if(vPQ17>=500)
{
vPQ17=20;
setTimeout("checkTime=500",1000);
}
}
vPQ10=setTimeout("vPQ24()",vPQ17);
}
function vPQ2()
{
if(!vPQ8)
return true;
if(document.all)
{
if(document.body.scrollHeight<document.body.clientHeight
||document.body.scrollTop<((document.body.scrollHeight-document.body.clientHeight)/2))
{
vPQ11.top=document.body.scrollTop+document.body.clientHeight-vPQ20;
vPQ12.top=document.body.scrollTop+document.body.clientHeight-vPQ20;
}
else
{
vPQ11.top=document.body.scrollTop+vPQ3;
vPQ12.top=document.body.scrollTop+vPQ3;
}
vPQ11.left=document.body.scrollLeft+document.body.clientWidth-vPQ21;
vPQ12.left=document.body.scrollLeft+document.body.clientWidth-vPQ21+vPQ5;
}
else if(document.layers)
{
if(window.pageYOffset<((window.outerHeight-window.innerHeight)/2))
{
vPQ11.top=window.pageYOffset+window.innerHeight-vPQ20;
vPQ12.top=window.pageYOffset+window.innerHeight-vPQ20;
}
else
{
vPQ11.top=window.pageYOffset+vPQ3;
vPQ12.top=window.pageYOffset+vPQ3;
}
vPQ11.left=window.pageXOffset+window.innerWidth-vPQ21;
vPQ12.left=window.pageXOffset+window.innerWidth-vPQ21+vPQ5;
}
}
function vPQ25()
{
vPQ9=1;
vPQ12.visibility=vPQ22;
vPQ11.visibility=vPQ23;
clearTimeout(vPQ14);
}
function vPQ26()
{
vPQ9=0;
vPQ12.visibility=vPQ23;
vPQ11.visibility=vPQ22;
}
//-->
</SCRIPT>
<DIV ID="vPQ19" STYLE="position: absolute; top: 0; left: 0; visibility: hidden; z-index: 2147483632;">
<table border=0 cellspacing=0 cellpadding=0 height=62 bgcolor="#EEEEEE">
<tr>
<td valign="top"><a href="" title="Click here to expand banner" onClick="vPQ26(); return false;"><img src="http://banner.easyspace.com/left.gif" width=12 height=12 border=0></a></td>
</tr>
<tr>
<td valign="bottom"><a href="http://www.easyspace.com/" target="_blank"><img src="http://banner.easyspace.com/littlelogo.gif" border=0></a></td>
</tr>
</table>
</DIV>
<DIV ID="vPQ18" STYLE="position: absolute; top: 0; left: 0; z-index: 2147483647; visibility: hidden">
<table border=0 cellspacing=0 cellpadding=0 height=62>
<tr>
<td rowspan=2 valign="top"><A TARGET="_new" HREF="http://www.easyspace.com/redirect.cgi?type=BNR-ESBIZ-468-2.gif&url=http://www.easyspace.com/domains/dotbiz.html"><IMG SRC="http://banner.easyspace.com/ESBIZ-468-2.gif?r=0.0294060870073736" width=468 height=60 border=0 ALT="Nothing personal. Just business."></A></td>
<td bgcolor="#EEEEEE" valign="top"><a href="" title="Click here to shrink banner" onClick="vPQ25(); return false;"><img src="http://banner.easyspace.com/right.gif" width=12 height=12 border=0></a></td>
</tr>
<tr>
<td bgcolor="#EEEEEE" valign="bottom"><a href="http://www.easyspace.com/" target="_blank"><img src="http://banner.easyspace.com/littlelogo.gif" border=0></a></td>
</tr>
</table>
</DIV>
<basefont face="Arial">
<table border="0" cellpadding="0" cellspacing="0" width="100%">
<tr>
<td>
<h1 align="center">
Programming Floppy Disk Controllers
</h1>
<br>
<p title="" align="center">
[
<a href="../../../index.html">Home
</a>]
[
<a title="Programming Home" href="../../index.html">Programming
</a>]
</p>
</td>
</tr>
</table>
<hr size="3" color="gray">
<br>
<br>
<!---------------------------------------------------------------------------------------->
<table width="100%" border="0" cellspacing="0" cellpadding="10">
<tr>
<td width="10" valign="top">[
<a href="../../../Index.html">Home
</a>]
<br>
[
<a title="Programming Home" href="../../Index.html">Programming
</a>]
</td>
<td>
<p>
The purpose of this document is to provide information
related to programming the NEC &micro;PD765 and the Intel
82072/7 Floppy Disk Controllers (FDCs), by use of the
registers. The document is split into several sections:
</p>
<br>
<ol>
<li>
<a href="#Overview">Overview
</a></li>
<li>
<a href="#Configuration">Configuration of an FDC on a
PC
</a></li>
<li>
<a href="#FDC Registers">FDC Registers
</a></li>
<li>
<a href="#Commands">Command Set
</a></li>
</ol>
<br>
</td>
</tr>
</table>
<hr size="3" color="gray"><!---------------------------------------------------------------------------------------->
<h2 align="center">
<a name="Overview">Overview
</a>
</h2>
<p>
The PC usually uses the NEC &micro;PD765 floppy disk controller. The
AT can also incorporate an Intel 82072A controller, while the PS/2
uses an Intel 82077A. The &micro;PD765 and the ROM code in the
controller form a microcontroller, and this handles the majority of
the work of the controller. All PC compatibles have FDCs which are
compatible with the controllers described in this document.
</p>
<p>
This document describes the registers used to interface with the
controller, and the commands which it recognises.
</p>
<p>
There are a lot of delays involved in communicating with the
controller. These delays are for a variety of reasons, including the
time needed to spin up the drive motor, and the time taken to move the
head to a new position and wait for it to settle in place.
</p>
<p>
When the drive motor is started up or the a seek is requested, there
will be a delay until the drive is ready for the next command. An
interrupt is issued by the hardware when it is ready for the next
command, and this will tell you that the drive is ready for your
command.
</p>
<p>
In a single tasked environment (such as DOS), the only option is to
have your driver constantly wait for an interrupt, and then respond to
it. However, in a multi-tasked or multi-threaded environment, it is
perfectly acceptable to write a driver which allows other tasks to be
executed while waiting for the interrupt.
</p>
<p>
When performing a read or write operation, data may be transferred a
byte at a time by reading from or writing to the appropriate port, or
a sector/track at a time through the use of DMA channel 2. Programming
the DMA controller is beyond the scope of this document, but will be
described in a future document to be added to this site.
</p>
<p>
The registers, what they do and what commands can be used are all
detailed in the following sections. If there are any errors, I will
gratefully accept any feedback you might like to send to me via
<a href="mailto:debs@savah.freeserve.co.uk">e-mail
</a>.
</p>
<hr size="3" color="gray">
<br>
<br>
<!---------------------------------------------------------------------------------------->
<h2 align="center">
<a name="Configuration">Configuration of an FDC on a PC
</a>
</h2>
<p>
The Floppy Controller on a PC uses a standard configuration. On the XT
there are 3 ports available for control and data access registers. On
the AT, there are 4, and on the PS/2 there are 6.
</p>
<p>
The base port address used for the controller is dependant on whether
the controller is configured as the primary or secondary controller.
This base address controls the port addresses used for each of the
registers on the controller. It can additionally be noted that all
floppy controllers on a PC use DMA channel 2 for data transfer during
a read or write, and they all issue a hardware interrupt via IRQ6 to
be serviced by INT 0eh by default.
</p>
<br>
<table border="1" cellspacing="0" cellpadding="0" align="center">
<tr>
<td width="300"><EFBFBD>
</td>
<td width="75"><strong> Primary
<br>
Address
</strong>
</td>
<td width="75"><strong> Secondary
<br>
Address
</strong>
</td>
<td width="75"><strong> Write (W)
<br>
Read (R)
</strong>
</td>
</tr>
<tr>
<td><EFBFBD>
</td>
<td><EFBFBD>
</td>
<td><EFBFBD>
</td>
<td><EFBFBD>
</td>
</tr>
<tr>
<td>base address
</td>
<td>3f0h
</td>
<td>370h
</td>
<td><EFBFBD>
</td>
</tr>
<tr>
<td>status register A (PS/2)
</td>
<td>3f0h
</td>
<td>370h
</td>
<td>R
</td>
</tr>
<tr>
<td>status register B (PS/2)
</td>
<td>3f1h
</td>
<td>371h
</td>
<td>R
</td>
</tr>
<tr>
<td>digital output register DOR
</td>
<td>3f2h
</td>
<td>372h
</td>
<td>W
</td>
</tr>
<tr>
<td>main status register
</td>
<td>3f4h
</td>
<td>374h
</td>
<td>R
</td>
</tr>
<tr>
<td>data rate select register (DSR)(PS/2)
</td>
<td>3f4h
</td>
<td>374h
</td>
<td>W
</td>
</tr>
<tr>
<td>data register
</td>
<td>3f5h
</td>
<td>375h
</td>
<td>R/W
</td>
</tr>
<tr>
<td>digital input register DIR (AT)
</td>
<td>3f7h
</td>
<td>377h
</td>
<td>R
</td>
</tr>
<tr>
<td>configuration control register (AT)
</td>
<td>3f7h
</td>
<td>377h
</td>
<td>W
</td>
</tr>
<tr>
<td>DMA channel
</td>
<td>2
</td>
<td>2
</td>
<td><EFBFBD>
</td>
</tr>
<tr>
<td>IRQ
</td>
<td>6
</td>
<td>6
</td>
<td><EFBFBD>
</td>
</tr>
<tr>
<td>INTR
</td>
<td>0eh
</td>
<td>0eh
</td>
<td><EFBFBD>
</td>
</tr>
</table>
<br>
<br>
<br>
<p>
Note that the controller can be configured differently from the
defaults for handling interrupts.
</p>
<hr size="3" color="gray">
<br>
<br>
<!---------------------------------------------------------------------------------------->
<h2 align="center">
<a name="FDC Registers">FDC Registers
</a>
</h2>
<p>
This section gives more detailed information on the use of the
registers listed in the above table. Additionally, the first registers
to be described are those common to each system described, and these
will be followed by descriptions of AT specific registers and PS/2
specific registers.
</p>
<p>
Common Registers:
</p>
<ol>
<li>
<a href="#DOR">Digital Output Register DOR
</a></li>
<li>
<a href="#MSR">Main Status Register
</a></li>
<li>
<a href="#Data">Data Register
</a></li>
</ol>AT Specific Registers:
<ol>
<li>
<a href="#DIR">Digital Input Register DIR
</a></li>
<li>
<a href="#CCR">Configuration Control Register
</a></li>
</ol>PS/2 Specific Registers:
<ol>
<li>
<a href="#Status">Status Register A
</a></li>
<li>
<a href="#Status">Status Register B
</a></li>
<li>
<a href="#DRSR">Data Rate Select Register
</a></li>
</ol>
<hr>
<h3>
<a name="DOR">Digital Output Register DOR
</a>
</h3>
<p align="center">
<img title="Digital Output Register" height="136" alt="image of Digital Output Register" src="./Gifs/DOR.gif" width="260">
</p>
<table width="550" cellpadding="2">
<tr>
<td><strong><font size="-1">MOTD, MOTC, MOTB, MOTA:</font></strong>
</td>
<td><font size="-1"><strong> Motor control for floppy drive D, C, B,
A
</strong></font>
</td>
</tr>
<tr>
<td>
</td>
<td>
<table width="100%">
<tr>
<td width="50%"><font size="-1">1 = Start motor</font>
</td>
<td><font size="-1">0 = Stop motor</font>
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td><strong><font size="-1">DMA:</font></strong>
</td>
<td><strong><font size="-1">DMA and IRQ channel</font></strong>
</td>
</tr>
<tr>
<td>
</td>
<td>
<table width="100%">
<tr>
<td width="50%"><font size="-1">1 = Enabled</font>
</td>
<td><font size="-1">0 = Disabled</font>
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td><strong><font size="-1"><a style="TEXT-DECORATION: overline">REST:
</a></font></strong>
</td>
<td><strong><font size="-1">Controller reset</font></strong>
</td>
</tr>
<tr>
<td>
</td>
<td>
<table width="100%">
<tr>
<td width="50%"><font size="-1">1 = Controller enabled</font>
</td>
<td><font size="-1">0 = Execute controller reset</font>
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td><strong><font size="-1">DR1,DR0:</font></strong>
</td>
<td><strong><font size="-1">Drive select</font></strong>
</td>
</tr>
<tr>
<td>
</td>
<td>
<table width="100%">
<tr>
<td width="25%"><font size="-1">00 = drive 0 (A)</font>
</td>
<td width="25%"><font size="-1">01 = drive 1 (B)</font>
</td>
<td width="25%"><font size="-1">10 = drive 2 (C)</font>
</td>
<td><font size="-1">11 = drive 3 (D)</font>
</td>
</tr>
</table>
</td>
</tr>
</table>
<br>
<br>
<br>
<p>
This register is write only, and controls the drive motors, as well as
selecting a drive and the DMA/IRQ mode, and resetting the controller.
</p>
<p>
If the REST bit is set, the controller is enabled, in order to accept
and execute commands. If it is equal to 0, the controller ignores all
commands and carries out an internal reset of all internal registers
(except the DOR).
</p>
<p>
Note that a drive cannot be selected unless its motor is on, although
setting the bits at the same time is acceptable.
</p>
<p>
Note also that drives 2 and 3 (floppy drives C and D) are not
supported in all systems.
</p>
<p>
<strong> Example:
</strong>
<br>
To start the motor on drive A and select it, ready for another
operation, you would use the following:
<br>
<br>
</p>
<dl>
<dd>MOTA = 1 (start motor for drive A)</dd>
<dd>DMA = 1 (assuming you want to use DMA and interrupts)</dd>
<dd>REST = 1 (Controller enabled, otherwise no other commands will be
executed)</dd>
<dd>DR1,DR0 = 00 (Select drive A)</dd>
<dd>All other bits are set to 0
<br>
<br>
Thus 16 + 8 + 4 + 0 = 28, or 01Ch, is sent to port 3f2h (Drive A is
usually on the primary controller).</dd>
</dl>
<p>
In assembly language, this would be written as:
</p>
<pre><strong><font size="+2">
mov al,01ch
mov dx,03f2h
out dx,al</font></strong>
</pre>
<br>
<p>
<strong> Example:
</strong>
<br>
To reset the controller, send 0 to port 3f2h. This turns off all
motors, selects no drives (because drive A's motor is not active, it
cannot be selected), disables the DMA and IRQ line, and resets the
controller.
</p>
<p>
The code for this is as follows:
<br>
<br>
</p>
<pre><strong><font size="+2">
mov al,0
mov dx,03f2h
out dx,al</font></strong>
</pre>
<br>
<hr><!---------------------------------------------------------------------------------------->
<h3>
<a name="MSR">Main Status Register
</a>
</h3>
<p align="center">
<img title="Main Status Register" height="136" alt="image of Main Status Register" src="./Gifs/MSR.gif" width="260">
</p>
<table width="550" cellpadding="2">
<tr>
<td><strong><font size="-1">MRQ:</font></strong>
</td>
<td><strong><font size="-1">main request</font></strong>
</td>
</tr>
<tr>
<td>
</td>
<td>
<table width="100%">
<tr>
<td width="50%"><font size="-1">1 = data register ready</font>
</td>
<td><font size="-1">0 = data register not ready</font>
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td><strong><font size="-1">DIO:</font></strong>
</td>
<td><font size="-1"><strong> data input/output
</strong></font>
</td>
</tr>
<tr>
<td>
</td>
<td>
<table width="100%" cellpadding="2">
<tr>
<td width="50%"><font size="-1">1 = controller ? CPU</font>
</td>
<td><font size="-1">0 = CPU ? controller</font>
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td><strong><font size="-1">NDMA:</font></strong>
</td>
<td><font size="-1"><strong> non-DMA mode
</strong></font>
</td>
</tr>
<tr>
<td>
</td>
<td>
<table cellpadding="2" width="100%">
<tr>
<td width="50%"><font size="-1">1 = controller not in DMA mode</font>
</td>
<td><font size="-1">0 = controller in DMA mode</font>
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td><strong><font size="-1">BUSY:</font></strong>
</td>
<td><font size="-1"><strong> instruction (device
busy)
</strong></font>
</td>
</tr>
<tr>
<td>
</td>
<td>
<table cellpadding="2" width="100%">
<tr>
<td width="50%"><font size="-1">1 = active</font>
</td>
<td><font size="-1">0 = not active</font>
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td><strong><font size="-1">ACTD, ACTC, ACTB, ACTA:</font></strong>
</td>
<td><font size="-1"><strong> drive D, C, B, A in positioning
mode
</strong></font>
</td>
</tr>
<tr>
<td>
</td>
<td>
<table cellpadding="2" width="100%">
<tr>
<td width="50%"><font size="-1">1 = active</font>
</td>
<td><font size="-1">0 = not active</font>
</td>
</tr>
</table>
</td>
</tr>
</table>
<br>
<br>
<br>
<p>
The MSR is read-only, and contains the controller's status
information. This register can be read whatever else the controller is
doing.
</p>
<p>
It should be noted that this register is different from status
registers ST0-ST3, which contain data concerning the last command
executed. These registers are accessed via the data registers.
</p>
<p>
Bit 7 (MRQ) indicates whether the controller is ready to receive or
send data or commands via the data register.
</p>
<p>
DIO is used to provide an indication of whether the controller is
expecting to receive data from the CPU, or if it wants to output data
to the CPU.
</p>
<p>
If the controller is set up to use DMA channel 2 to transfer data to
or from main memory, the NDMA bit is not set. If this bit is set, data
transfer is carried out exclusively by means of read or write commands
to the data register. In this case, the controller issues a hardware
interrupt every time that it either expects to receive or wants to
supply a data byte.
</p>
<p>
Bit 4 indicates whether the controller is busy or not. If the bit is
set, the controller is currently executing a command.
</p>
<p>
Bits 0-3 indicate which (if any) drive is currently in the process of
positioning it's read/write heads, or being recalibrated.
</p>
<p>
Note that the delay waiting for the controller to be ready for a read
or write can be as much as 175&micro;s on an Intel controller, and
longer on older controllers.
</p>
<p>
<strong> Example:
</strong>
<br>
To test whether the controller is ready to receive commands and data,
it is necessary to test MRQ and DIO. This involves reading the port,
masking the bits, and doing a comparison on the result (to test the
values of the bits).
</p>
<p>
In assembly language, this is can be written as:
<br>
<br>
</p>
<pre><strong><font size="+2">
mrqloop:
mov dx,03f4h
in al,dx
and al,0c0h
cmp al,080h
jne mrqloop</font></strong>
</pre>
<br>
<p>
This code will keep looping until the controller says that it is ready
to receive data. Note that if the controller is expecting to output
data to the CPU, this code will not spot that. You would need to add
another couple of lines of code if you need to check for both
possibilities (In general, you would know from previous commands
whether the controller should be expecting or offering data, and so
only need to check for one condition).
</p>
<hr><!---------------------------------------------------------------------------------------->
<h3>
<a name="Data">Data Register
</a>
</h3>
<p>
The data register is an 8 bit register, like each of the other
registers, which provides indirect access to a stack of registers. A
command can be one to nine bytes in length, and the first byte tells
the controller how many more bytes to expect. The controller sends the
command bytes to the correct registers in it's stack, saving the
programmer from the need to use a separate index register, as is the
case in some other devices (e.g. some VGA registers).
</p>
<p>
Some controllers, such as the i82077A, have a buffer, with a
programmable threshold, allowing the data to be transferred several
bytes at a time. This helps to speed up the transfer of data and
commands, as well as reducing the response time seen on the
&micro;PD765.
</p>
<p>
Following some of the commands, the values in the status registers are
returned. The layout of the status registers follows, with the
<a href="#Commands">commands
</a>being listed at the end of this
document.
</p>
<hr size="1"><!---------------------------------------------------------------------------------------->
<h4>
<a name="Status0">Status Register
</a>ST0
</h4>
<p align="center">
<img title="Status Register 0" height="136" alt="image of Status Register 0" src="./Gifs/ST0.gif" width="260">
</p>
<table cellpadding="2">
<tr>
<td><strong><font size="-1">IC
<sub> 1
</sub> ,IC
<sub> 0
</sub>
:</font></strong>
</td>
<td><strong><font size="-1">interrupt code</font></strong>
</td>
</tr>
<tr>
<td>
</td>
<td>
<table cellpadding="2" width="100%">
<tr>
<td valign="top"><font size="-1">00<EFBFBD>=<3D></font>
</td>
<td><font size="-1">normal termination;
<br>
command terminated without any errors</font>
</td>
</tr>
<tr>
<td valign="top"><font size="-1">01<EFBFBD>=<3D></font>
</td>
<td><font size="-1">abnormal termination;
<br>
the controller started execution of the command, but
couldn't terminate it correctly</font>
</td>
</tr>
<tr>
<td valign="top"><font size="-1">10<EFBFBD>=<3D></font>
</td>
<td><font size="-1">invalid command;
<br>
the controller could not start command execution</font>
</td>
</tr>
<tr>
<td valign="top"><font size="-1">11<EFBFBD>=<3D></font>
</td>
<td><font size="-1">abnormal termination by polling;
<br>
drive became not ready</font>
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td><strong><font size="-1">SE:</font></strong>
</td>
<td><font size="-1"><strong> seek end
</strong></font>
</td>
</tr>
<tr>
<td>
</td>
<td><font size="-1">The controller has completed a seek or a
calibration command,
<br>
or has correctly executed a read or write command which has an
implicit seek</font>
</td>
</tr>
<tr>
<td><strong><font size="-1">UC:</font></strong>
</td>
<td><font size="-1"><strong> unit check
</strong></font>
</td>
</tr>
<tr>
<td>
</td>
<td><font size="-1">Set if the drive faults or if a recalibrate
cannot find track 0 after 79 pulses.</font>
</td>
</tr>
<tr>
<td><strong><font size="-1">NR:</font></strong>
</td>
<td><font size="-1"><strong> drive not ready
</strong></font>
</td>
</tr>
<tr>
<td><strong><font size="-1">HD:</font></strong>
</td>
<td><font size="-1"><strong> head currently active
</strong></font>
</td>
</tr>
<tr>
<td>
</td>
<td>
<table cellpadding="2">
<tr>
<td><font size="-1">1<EFBFBD>=<3D></font>
</td>
<td><font size="-1">head 1</font>
</td>
</tr>
<tr>
<td><font size="-1">0<EFBFBD>=<3D></font>
</td>
<td><font size="-1">head 0</font>
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td><strong><font size="-1">US
<sub> 1
</sub> , US
<sub>
0
</sub></font>:
</strong>
</td>
<td><font size="-1"><strong> currently selected drive (unit
select)
</strong></font>
</td>
</tr>
<tr>
<td>
</td>
<td>
<table cellpadding="2">
<tr>
<td><font size="-1">00<EFBFBD>=<3D></font>
</td>
<td><font size="-1">drive 0 (A:)</font>
</td>
</tr>
<tr>
<td><font size="-1">01<EFBFBD>=<3D></font>
</td>
<td><font size="-1">drive 1 (B:)</font>
</td>
</tr>
<tr>
<td><font size="-1">10<EFBFBD>=<3D></font>
</td>
<td><font size="-1">drive 2 (C:)</font>
</td>
</tr>
<tr>
<td><font size="-1">11<EFBFBD>=<3D></font>
</td>
<td><font size="-1">drive 3 (D:)</font>
</td>
</tr>
</table>
</td>
</tr>
</table>
<br>
<br>
<hr size="1"><!---------------------------------------------------------------------------------------->
<h4>Status Register ST1
</h4>
<p align="center">
<img title="Status Register 1" height="136" alt="image of Status Register 1" src="./Gifs/ST1.gif" width="260">
</p>
<table cellpadding="2">
<tbody>
<tr>
<td><strong><font size="-1">EN:</font></strong>
</td>
<td><font size="-1"><strong> End of Cylinder
</strong></font>
</td>
</tr>
<tr>
<td>
</td>
<td><font size="-1">Set when the sector count exceeds the number
of sectors on a track.
<br>
i.e. the controller attempts to access a sector after the
last sector of a cylinder.</font>
</td>
</tr>
<tr>
<td><strong><font size="-1">xx:</font></strong>
</td>
<td><font size="-1"><strong> bit unused
</strong></font>
</td>
</tr>
<tr>
<td>
</td>
<td><font size="-1">value always equal to 0</font>
</td>
</tr>
<tr>
<td><strong><font size="-1">DE:</font></strong>
</td>
<td><font size="-1"><strong> data error
</strong></font>
</td>
</tr>
<tr>
<td>
</td>
<td><font size="-1">Set if the controller detected an error in
the ID address field or the data field of a sector</font>
</td>
</tr>
<tr>
<td><strong><font size="-1">TO:</font></strong>
</td>
<td><font size="-1"><strong> time-out
</strong></font>
</td>
</tr>
<tr>
<td>
</td>
<td><font size="-1">Set for a data overrun;
<br>
No signal received from the DMA controller or CPU within the
required time period.</font>
</td>
</tr>
<tr>
<td><strong><font size="-1">NDAT:</font></strong>
</td>
<td><font size="-1"><strong> no data
</strong></font>
</td>
</tr>
<tr>
<td>
</td>
<td>
<table cellpadding="2">
<tr>
<td valign="top" align="right"><strong><font size="-1">Set<EFBFBD>if:<3A></font></strong>
</td>
<td><font size="-1">The addressed sector in a
<i> read
sector
</i> or
<i> read deleted sector
</i> cannot be
found
<br>
by the controller.</font>
</td>
</tr>
<tr>
<td valign="top" align="right"><font size="-1"><strong> OR:
</strong> <20></font>
</td>
<td><font size="-1">The controller cannot read the ID
address mark in response to a
<i> read ID
</i>
command
<br>
without error.</font>
</td>
</tr>
<tr>
<td valign="top" align="right"><strong><font size="-1">OR:<3A></font></strong>
</td>
<td><font size="-1">The controller cannot correctly
determine the sequence of sectors in a
<i> read
track
</i>
<br>
command</font>
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td><strong><font size="-1">NW:</font></strong>
</td>
<td><font size="-1"><strong> not writable
</strong></font>
</td>
</tr>
<tr>
<td>
</td>
<td><font size="-1">Set if the disk in the selected drive is
write protected while the controller attempts to
<br>
execute a write command.</font>
</td>
</tr>
<tr>
<td><strong><font size="-1">NID:</font></strong>
</td>
<td><font size="-1"><strong> no address mark
</strong></font>
</td>
</tr>
<tr>
<td>
</td>
<td>
<table cellpadding="2">
<tbody>
<tr>
<td valign="top" align="right"><strong><font size="-1">Set<EFBFBD>if:<3A></font></strong>
</td>
<td><font size="-1">The ID address mark was not
found after one complete disk revolution</font>
</td>
</tr>
<tr>
<td valign="top" align="right"><strong><font size="-1">OR:<3A></font></strong>
</td>
<td><font size="-1">The controller could not
find:
<br></font>
<dl>
<dd><font size="-1">a data address mark
DAM</font></dd>
<dd><font size="-1">a deleted data address mark
DAM
<br>
on the specified track.</font></dd>
</dl>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
<br>
<br>
<hr size="1"><!---------------------------------------------------------------------------------------->
<h4>Status Register ST2
</h4>
<p align="center">
<img title="Status Register 2" height="136" alt="image of Status Register 2" src="./Gifs/ST2.gif" width="260">
</p>
<table cellpadding="2">
<tr>
<td><strong><font size="-1">xx:</font></strong>
</td>
<td><font size="-1"><strong> bit unused
</strong></font>
</td>
</tr>
<tr>
<td>
</td>
<td><font size="-1">value always equal to 0</font>
</td>
</tr>
<tr>
<td><strong><font size="-1">DADM:</font></strong>
</td>
<td><font size="-1"><strong> deleted address mark
</strong></font>
</td>
</tr>
<tr>
<td>
</td>
<td>
<table cellpadding="2">
<tr>
<td valign="top" align="right"><strong><font size="-1">Set if:<3A></font></strong>
</td>
<td><font size="-1">A deleted data address mark DAM is
detected when a
<i> read sector
</i> command is
<br>
being executed.</font>
</td>
</tr>
<tr>
<td valign="top" align="right"><strong><font size="-1">OR:<3A></font></strong>
</td>
<td><font size="-1">A valid data address mark DAM is
detected when a
<i> read deleted sector
</i> command
<br>
is being executed.</font>
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td><strong><font size="-1">CRCE:</font></strong>
</td>
<td><font size="-1"><strong> CRC error in data field
</strong></font>
</td>
</tr>
<tr>
<td>
</td>
<td><font size="-1">Set if a CRC error was detected in the data
field of the sector.</font>
</td>
</tr>
<tr>
<td><strong><font size="-1">WCYL:</font></strong>
</td>
<td><font size="-1"><strong> wrong cylinder
</strong></font>
</td>
</tr>
<tr>
<td>
</td>
<td><font size="-1">Set if the track address in the controller and
the track address in the ID address mark are
<br>
different.</font>
</td>
</tr>
<tr>
<td><strong><font size="-1">SEQ:</font></strong>
</td>
<td><font size="-1"><strong> seek equal
</strong></font>
</td>
</tr>
<tr>
<td>
</td>
<td>
<table cellpadding="2">
<tr>
<td valign="top" align="right"><strong><font size="-1">Set<EFBFBD>if:</font></strong>
</td>
<td><font size="-1">controller is a &micro;PD765 and the
condition
<i> seek equal
</i> is fulfilled</font>
</td>
</tr>
<tr>
<td valign="top" align="right"><strong><font size="-1">else:</font></strong>
</td>
<td><font size="-1">SGL is not used, this field is always
equal to 0</font>
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td><strong><font size="-1">SERR:</font></strong>
</td>
<td><font size="-1"><strong> seek error
</strong></font>
</td>
</tr>
<tr>
<td>
</td>
<td>
<table cellpadding="2">
<tr>
<td valign="top" align="right"><strong><font size="-1">Set<EFBFBD>if:</font></strong>
</td>
<td><font size="-1">controller is a &micro;PD765 and the
controller did not find the corresponding sector
when
<br>
seeking on the cylinder.</font>
</td>
</tr>
<tr>
<td valign="top" align="right"><strong><font size="-1">else:</font></strong>
</td>
<td><font size="-1">SERR is not used, this field is always
equal to 0</font>
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td><strong><font size="-1">BCYL:</font></strong>
</td>
<td><font size="-1"><strong> bad cylinder
</strong></font>
</td>
</tr>
<tr>
<td>
</td>
<td><font size="-1">This field indicates that the track address in
the ID address mark differs from the track
<br>
address in the controller.
<br>
The value equals
<strong> ffh
</strong> , indicating a bad track
with a physical error, according to the IBM soft
<br>
sector format.</font>
</td>
</tr>
<tr>
<td><strong><font size="-1">NDAM:</font></strong>
</td>
<td><font size="-1"><strong> not data address mark
DAM
</strong></font>
</td>
</tr>
<tr>
<td>
</td>
<td><font size="-1">Set if the controller cannot find a valid or
deleted data address mark DAM.</font>
</td>
</tr>
</table>
<br>
<br>
<hr size="1"><!---------------------------------------------------------------------------------------->
<h4>Status Register ST3
</h4>
<p align="center">
<img title="Status Register 3" height="136" alt="image of Status Register 3" src="./Gifs/ST3.gif" width="260">
</p>
<table cellpadding="2">
<tr>
<td><strong><font size="-1">ESIG:</font></strong>
</td>
<td><strong><font size="-1">error</font></strong>
</td>
</tr>
<tr>
<td>
</td>
<td>
<table cellpadding="2">
<tr>
<td valign="top" align="right"><strong><font size="-1">Set<EFBFBD>if:</font></strong>
</td>
<td><font size="-1">controller is a &micro;PD765 and the
drives error signal is active
<br>
i.e. an error has occurred</font>
</td>
</tr>
<tr>
<td valign="top" align="right"><strong><font size="-1">else:</font></strong>
</td>
<td><font size="-1">ESIG is not used and is always equal
to 0</font>
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td><strong><font size="-1">WPDR:</font></strong>
</td>
<td><strong><font size="-1">write protection</font></strong>
</td>
</tr>
<tr>
<td>
</td>
<td><font size="-1">Set if the disk is write protected (indicates
the write-protection line is active).</font>
</td>
</tr>
<tr>
<td><strong><font size="-1">RDY:</font></strong>
</td>
<td><strong><font size="-1">ready</font></strong>
</td>
</tr>
<tr>
<td>
</td>
<td>
<table cellpadding="2">
<tr>
<td valign="top" align="right"><strong><font size="-1">Set<EFBFBD>if:</font></strong>
</td>
<td><font size="-1">controller is a &micro;PD765 and the
drive is ready (indicates the ready signal of the
<br>
drive is active).</font>
</td>
</tr>
<tr>
<td valign="top" align="right"><strong><font size="-1">else:</font></strong>
</td>
<td><font size="-1">RDY is not used and this field is
always set</font>
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td><strong><font size="-1">TRK0:</font></strong>
</td>
<td><strong><font size="-1">track 0</font></strong>
</td>
</tr>
<tr>
<td>
</td>
<td><font size="-1">The head is above track 0 (the TRK0 signal of
the drive is active).</font>
</td>
</tr>
<tr>
<td><strong><font size="-1">DSDR:</font></strong>
</td>
<td><strong><font size="-1">double sided drive</font></strong>
</td>
</tr>
<tr>
<td>
</td>
<td><font size="-1">The drive is double sided (indicates the DSDR
signal is active).</font>
</td>
</tr>
<tr>
<td><strong><font size="-1">HDDR:</font></strong>
</td>
<td><strong><font size="-1">head</font></strong>
</td>
</tr>
<tr>
<td>
</td>
<td><font size="-1">This bit indicates the status of the HDSEL
signal of the drive:
<br>
1 = head 1 active,
<br>
0 = head 0 active</font>
</td>
</tr>
<tr>
<td><strong><font size="-1">DS1,<2C>DS0:</font></strong>
</td>
<td><strong><font size="-1">drive select</font></strong>
</td>
</tr>
<tr>
<td>
</td>
<td><font size="-1">Both bits indicate the select signals DS1 and
DS0 of the drive:
<br>
00 = drive 0 (A:)
<br>
01 = drive 1 (B:)
<br>
10 = drive 2 (C:)
<br>
11 = drive 3 (D:)
<br></font>
</td>
</tr>
</table>
<br>
<br>
<hr><!---------------------------------------------------------------------------------------->
<h3>
<a name="DIR">Digital Input Register
</a>
</h3>
<p align="center">
DIR (AT)
<br>
<img title="Digital Input Register (AT)" height="136" alt="image of Digital Input Register (AT)" src="./Gifs/DIR_(AT).gif" width="260">
<br>
</p>
<table cellpadding="2">
<tr>
<td align="middle">DIR (PS/2 except Model 30)
<br>
<img title="Digital Input Register (PS/2 except Model 30)" height="136" alt="image of Digital Input Register (PS/2 except Model 30)" src="./Gifs/DIR_(PS2_except_model_30).gif" width="260">
</td>
<td align="middle">DIR (PS/2 Model 30)
<br>
<img title="Digital Input Register (PS/2 Model 30)" height="136" alt="image of Digital Input Register (PS/2 Model 30)" src="./Gifs/DIR_(PS2_model_30).gif" width="260">
</td>
</tr>
</table>
<br>
<table cellpadding="2">
<tr>
<td><strong><font size="-1">CHAN:</font></strong>
</td>
<td><strong><font size="-1">Disk Change</font></strong>
</td>
</tr>
<tr>
<td>
</td>
<td>
<table cellpadding="2" width="100%">
<tr>
<td><font size="-1">1 = disk changed since last
command</font>
</td>
<td><font size="-1">0 = disk not changed</font>
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td><strong><font size="-1">RAT1,<2C>RAT2:</font></strong>
</td>
<td><strong><font size="-1">Data Rate</font></strong>
</td>
</tr>
<tr>
<td>
</td>
<td><font size="-1">00 = 500 kbits/s
<br>
01 = 300 kbits/s
<br>
10 = 250 kbits/s
<br>
11 = 1Mbits/s</font>
</td>
</tr>
<tr>
<td><strong><font size="-1"><a style="TEXT-DECORATION: overline">HiDe
</a></font></strong>
</td>
<td><strong><font size="-1">High-density Rate</font></strong>
</td>
</tr>
<tr>
<td>
</td>
<td><font size="-1">1 = data rate 250kbits/s or 300 kbits/s
<br>
0 = data rate 1Mbits/s or 500 kbits/s</font>
</td>
</tr>
<tr>
<td><strong><font size="-1">DMA:</font></strong>
</td>
<td><strong><font size="-1">Value of DMA bit in DOR</font></strong>
</td>
</tr>
<tr>
<td><strong><font size="-1">NOPR:</font></strong>
</td>
<td><strong><font size="-1">Value of NOPR bit in Control
Configuration Register</font></strong>
</td>
</tr>
</table>
<br>
<br>
<p>
This register is available only in the AT and the PS/2, and is read
only. The above diagrams show that there are 3 different
configurations for this register, differing in the AT, PS/2 and PS/2
Model 30. These registers all allow you to detect a disk change by
reading bit 7, with additional information available for the PS/2 and
Model 30 variants.
</p>
<p>
For each of the registers, when bit 7 is set, it indicates that the
disk has been changed since the last command was executed. This can be
used by a driver to speed up access to data, by buffering of data.
When buffering data, if the disk has not been changed, required
sectors can already be in memory when requested (for example, a whole
track can be read at a time and stored in a buffer, including the
possibility of storing multiple tracks when sectors are read from
different cylinders). If this happens, the data only needs reading
from disk when a new cylinder is being read from, or when the disk has
been changed.
</p>
<p>
In the PS/2 and Model 30, the registers also contain information about
the current data transfer rate. For Model 30, this information is read
from bits 1 and 0, while in the other PS/2 models, the data is read
from bits 2 and 1. This data can be used when the rate is set through
the Control Configuration Register to check that the rate has been
correctly set, or to check what rate the controller is set to at any
time.
</p>
<p>
In PS/2 models other than Model 30, bit 1 can be read to help
determine whether the controller is set to a high or low data transfer
rate for a high density disk.
</p>
<p>
In Model 30, bit 3 corresponds to bit 3 of the DOR. bit 2 corresponds
to bit 2 of the CCR (only used in this model). In both these cases,
the value is read-only in the DIR, and can be written to in the
corresponding register.
</p>
<hr><!---------------------------------------------------------------------------------------->
<h3>
<a name="CCR">Control Configuration Register
</a>
</h3>
<br align="center">
<table cellpadding="2">
<tr>
<td align="middle">CCR (AT and PS/2)
<br>
<img title="Control Configuration Register (AT and PS/2)" height="136" alt="image of Control Configuration Register (AT and PS/2)" src="./Gifs/CCR_(AT_PS2).gif" width="260">
</td>
<td align="middle">CCR (PS/2 Model 30)
<br>
<img title="Control Configuration Register (PS/2 Model 30)" height="136" alt="image of Control Configuration Register (PS/2 Model 30)" src="./Gifs/CCR_(PS2_Model_30).gif" width="260">
</td>
</tr>
</table>
<br>
<table cellpadding="2">
<tr>
<td><strong><font size="-1">RAT1,<2C>RAT0:</font></strong>
</td>
<td><strong><font size="-1">Data Transfer Rate</font></strong>
</td>
</tr>
<tr>
<td>
</td>
<td><font size="-1">00 = 500kbits/s
<br>
01 = 300kbits/s
<br>
10 = 250kbits/s
<br>
11 - 1Mbits/s</font>
</td>
</tr>
<tr>
<td><strong><font size="-1">NOPR:</font></strong>
</td>
<td><strong><font size="-1">(No) Precompensate</font></strong>
</td>
</tr>
<tr>
<td>
</td>
<td><font size="-1">0 = Precompensation Enabled (standard)
<br>
1 = No Precompensation</font>
</td>
</tr>
</table>
<br>
<br>
<p align="center">
<strong> Data Transfer Rates
</strong>
<br>
<br>
</p>
<table cellpadding="2">
<tr>
<td><strong><font size="-1">Rate</font></strong>
</td>
<td><strong><font size="-1">Disk
<br>
Capacity</font></strong>
</td>
<td><strong><font size="-1">Size</font></strong>
</td>
<td><strong><font size="-1">Drive
<br>
Capacity</font></strong>
</td>
</tr>
<tr>
<td><font size="-1">250 kbits/s</font>
</td>
<td><font size="-1">360<EFBFBD>kbyte</font>
</td>
<td><font size="-1">5&frac14;"</font>
</td>
<td><font size="-1">360<EFBFBD>kbyte</font>
</td>
</tr>
<tr>
<td>
</td>
<td><font size="-1">720<EFBFBD>kbyte</font>
</td>
<td><font size="-1">3&frac12;"</font>
</td>
<td><font size="-1">1.44<EFBFBD>Mbytes</font>
</td>
</tr>
<tr>
<td>
</td>
<td><font size="-1">720<EFBFBD>kbyte</font>
</td>
<td><font size="-1">3&frac12;"</font>
</td>
<td><font size="-1">720<EFBFBD>kbyte</font>
</td>
</tr>
<tr>
<td><font size="-1">300 kbits/s</font>
</td>
<td><font size="-1">720<EFBFBD>kbyte</font>
</td>
<td><font size="-1">3&frac12;"</font>
</td>
<td><font size="-1">720<EFBFBD>kbyte</font>
</td>
</tr>
<tr>
<td>
</td>
<td><font size="-1">360<EFBFBD>kbyte</font>
</td>
<td><font size="-1">5&frac14;"</font>
</td>
<td><font size="-1">1.2<EFBFBD><EFBFBD>Mbyte</font>
</td>
</tr>
<tr>
<td><font size="-1">500 kbits/s</font>
</td>
<td><font size="-1">1.2<EFBFBD><EFBFBD>Mbyte</font>
</td>
<td><font size="-1">5&frac14;"</font>
</td>
<td><font size="-1">1.2<EFBFBD><EFBFBD>Mbyte</font>
</td>
</tr>
<tr>
<td>
</td>
<td><font size="-1">1.44<EFBFBD>Mbytes</font>
</td>
<td><font size="-1">3&frac12;"</font>
</td>
<td><font size="-1">1.44<EFBFBD>Mbytes</font>
</td>
</tr>
</table>
<br>
<br>
<p>
In the AT and all PS/2 models, the data transfer rate can be set
through bits 1 and 0 of the CCR. Valid transfer rates are shown in the
table immediately above, and the table below the diagram shows what
values need to be sent to these two fields to set the appropriate
rate.
</p>
<p>
In the Model 30 it is also possible to program the precompensation
through bit 2 of this register. The default is for precompensate to be
enabled, with this field set to 0. Setting the field to 1 turns
precompensation off.
</p>
<hr><!---------------------------------------------------------------------------------------->
<h3>
<a name="Status">Status Registers A and B
</a>
</h3>
<br align="center">
<table cellpadding="2">
<tr>
<td align="middle">Status Register A (PS/2)
<br>
<img title="Status Register A (PS/2)" height="136" alt="image of Status Register A (PS/2)" src="./Gifs/StatusA1.gif" width="260">
</td>
<td align="middle">Status Register A (Model 30)
<br>
<img title="Status Register A (Model 30)" height="136" alt="image of Status Register A (Model 30)" src="./Gifs/StatusA2.gif" width="260">
</td>
</tr>
<tr>
<td valign="top" align="middle">
<table cellpadding="2">
<tr>
<td><strong><font size="-1">INTP:</font></strong>
</td>
<td><strong><font size="-1">Interrupt
Pending</font></strong>
</td>
</tr>
<tr>
<td>
</td>
<td><font size="-1">0 = interrupt signal inactive
<br>
1 = active</font>
</td>
</tr>
<tr>
<td valign="top"><strong><font size="-1"><a style="TEXT-DECORATION:
overline">DRV2
</a>:</font></strong>
</td>
<td><font size="-1">0 = 2 drives connected
<br>
1 = one drive only</font>
</td>
</tr>
<tr>
<td><strong><font size="-1">STEP:</font></strong>
</td>
<td><strong><font size="-1">Stepper Pulse</font></strong>
</td>
</tr>
<tr>
<td>
</td>
<td><font size="-1">0 = no pulse
<br>
1 = pulse is submitted</font>
</td>
</tr>
<tr>
<td><strong><font size="-1"><a style="TEXT-DECORATION:
overline">TRK0
</a>:</font></strong>
</td>
<td><strong><font size="-1">Track 0</font></strong>
</td>
</tr>
<tr>
<td>
</td>
<td><font size="-1">0 = head not above track 0
<br>
1 = head above track 0</font>
</td>
</tr>
<tr>
<td><strong><font size="-1">HDSL:</font></strong>
</td>
<td><strong><font size="-1">Head Select</font></strong>
</td>
</tr>
<tr>
<td>
</td>
<td><font size="-1">0 = head 0
<br>
1 = head 1</font>
</td>
</tr>
<tr>
<td><strong><font size="-1"><a style="TEXT-DECORATION:
overline">INDX
</a>:</font></strong>
</td>
<td><strong><font size="-1">Index Mark</font></strong>
</td>
</tr>
<tr>
<td>
</td>
<td><font size="-1">0 = detected
<br>
1 = not detected</font>
</td>
</tr>
<tr>
<td><strong><font size="-1"><a style="TEXT-DECORATION:
overline">WP
</a>:</font></strong>
</td>
<td><strong><font size="-1">Write
Protection</font></strong>
</td>
</tr>
<tr>
<td>
</td>
<td><font size="-1">0 = disk write protected
<br>
1 = not write protected</font>
</td>
</tr>
<tr>
<td><strong><font size="-1">DIR:</font></strong>
</td>
<td><strong><font size="-1">Direction of
Head</font></strong>
</td>
</tr>
<tr>
<td>
</td>
<td><font size="-1">0 = outwards (to smaller cylinder
numbers)
<br>
1 = inwards (to higher cylinder numbers)</font>
</td>
</tr>
</table>
</td>
<td valign="top" align="middle">
<table cellpadding="2">
<tr>
<td><strong><font size="-1">INTP:</font></strong>
</td>
<td><strong><font size="-1">Interrupt
Pending</font></strong>
</td>
</tr>
<tr>
<td>
</td>
<td><font size="-1">0 = interrupt signal inactive
<br>
1 = active</font>
</td>
</tr>
<tr>
<td><strong><font size="-1">DRQ:</font></strong>
</td>
<td><strong><font size="-1">DMA Request</font></strong>
</td>
</tr>
<tr>
<td>
</td>
<td><font size="-1">0 = not active
<br>
1 = active</font>
</td>
</tr>
<tr>
<td><strong><font size="-1">STEP:</font></strong>
</td>
<td><strong><font size="-1">Step Pulse</font></strong>
</td>
</tr>
<tr>
<td>
</td>
<td><font size="-1">0 = pulse is submitted
<br>
1 = no pulse</font>
</td>
</tr>
<tr>
<td><strong><font size="-1">TRK0:</font></strong>
</td>
<td><strong><font size="-1">Track 0</font></strong>
</td>
</tr>
<tr>
<td>
</td>
<td><font size="-1">0 = head above track 0
<br>
1 = head not above track 0</font>
</td>
</tr>
<tr>
<td><strong><font size="-1"><a style="TEXT-DECORATION:
overline">HDSL
</a>:</font></strong>
</td>
<td><strong><font size="-1">Head Select</font></strong>
</td>
</tr>
<tr>
<td>
</td>
<td><font size="-1">0 = head 1
<br>
1 = head 0</font>
</td>
</tr>
<tr>
<td><strong><font size="-1">INDX:</font></strong>
</td>
<td><strong><font size="-1">Index Mark</font></strong>
</td>
</tr>
<tr>
<td>
</td>
<td><font size="-1">0 = not detected
<br>
1 = detected</font>
</td>
</tr>
<tr>
<td><strong><font size="-1">WP:</font></strong>
</td>
<td><strong><font size="-1">Write
Protection</font></strong>
</td>
</tr>
<tr>
<td>
</td>
<td><font size="-1">0 = disk not write protected
<br>
1 = disk write protected</font>
</td>
</tr>
<tr>
<td><strong><font size="-1"><a style="TEXT-DECORATION:
overline">DIR
</a>:</font></strong>
</td>
<td><strong><font size="-1">Head Direction</font></strong>
</td>
</tr>
<tr>
<td>
</td>
<td><font size="-1">0 = inwards (to higher cylinder
numbers)
<br>
1 = outwards (to lower cylinder numbers)</font>
</td>
</tr>
</table>
</td>
</tr>
</table>
<br>
<br>
<br>
<br title="" align="center">
<table cellpadding="2">
<tr>
<td align="middle">Status Register B (PS/2)
<br>
<img title="Status Register B (PS/2)" height="136" alt="image of Status Register B (PS/2)" src="./Gifs/StatusB1.gif" width="260">
</td>
<td align="middle">Status Register B (Model 30)
<br>
<img title="Status Register B (Model 30)" height="136" alt="image of Status Register B (Model 30)" src="./Gifs/StatusB2.gif" width="260">
</td>
</tr>
<tr>
<td valign="top" align="middle">
<table cellpadding="2">
<tr>
<td><strong><font size="-1">DS0:</font></strong>
</td>
<td><strong><font size="-1">Drive Select</font></strong>
</td>
</tr>
<tr>
<td>
</td>
<td><font size="-1">0 = drive other than 0
<br>
1 = drive 0</font>
</td>
</tr>
<tr>
<td><strong><font size="-1">WDAT:</font></strong>
</td>
<td><strong><font size="-1">Write Data</font></strong>
</td>
</tr>
<tr>
<td>
</td>
<td><font size="-1">0 = no data can be written to
drive
<br>
1 = data can be transferred to drive</font>
</td>
</tr>
<tr>
<td><strong><font size="-1">RDAT:</font></strong>
</td>
<td><strong><font size="-1">Read Data</font></strong>
</td>
</tr>
<tr>
<td>
</td>
<td><font size="-1">0 = no data can be read from drive
<br>
1 = data can be transferred from drive</font>
</td>
</tr>
<tr>
<td><strong><font size="-1">WE:</font></strong>
</td>
<td><strong><font size="-1">Write Enabled</font></strong>
</td>
</tr>
<tr>
<td>
</td>
<td><font size="-1">0 = head is set to read data
<br>
1 = head is activated for data writes</font>
</td>
</tr>
<tr>
<td><strong><font size="-1">MOT1,<2C>MOT0:</font></strong>
</td>
<td><strong><font size="-1">Motor of drive 1,
0</font></strong>
</td>
</tr>
<tr>
<td>
</td>
<td><font size="-1">0 = motor is switched off
<br>
1 = motor is switched on</font>
</td>
</tr>
</table>
</td>
<td valign="top" align="middle">
<table>
<tr>
<td><strong><font size="-1"><a style="TEXT-DECORATION:
overline">DRV2
</a>:</font></strong>
</td>
<td><font size="-1">0 = two drives connected
<br>
1 = only one drive connected</font>
</td>
</tr>
<tr>
<td><strong><font size="-1"><a style="TEXT-DECORATION:
overline">DS1
</a>:</font></strong>
</td>
<td><strong><font size="-1">Drive Select 1</font></strong>
</td>
</tr>
<tr>
<td>
</td>
<td><font size="-1">0 = drive 1 selected
<br>
1 = drive not selected</font>
</td>
</tr>
<tr>
<td><strong><font size="-1"><a style="TEXT-DECORATION:
overline">DS0
</a>:</font></strong>
</td>
<td><strong><font size="-1">Drive Select 0</font></strong>
</td>
</tr>
<tr>
<td>
</td>
<td><font size="-1">0 = drive 0 selected
<br>
1 = drive not selected</font>
</td>
</tr>
<tr>
<td><strong><font size="-1">WDAT:</font></strong>
</td>
<td><strong><font size="-1">Write Data</font></strong>
</td>
</tr>
<tr>
<td>
</td>
<td><font size="-1">0 = no data can be written
<br>
1 = data can be transferred to the drive</font>
</td>
</tr>
<tr>
<td><strong><font size="-1">RDAT:</font></strong>
</td>
<td><strong><font size="-1">Read Data</font></strong>
</td>
</tr>
<tr>
<td>
</td>
<td><font size="-1">0<EFBFBD>= data cannot be read from drive
<br>
1 = data can be read from drive</font>
</td>
</tr>
<tr>
<td><strong><font size="-1">WE:</font></strong>
</td>
<td><strong><font size="-1">Write Enabled</font></strong>
</td>
</tr>
<tr>
<td>
</td>
<td><font size="-1">0 = head enabled to read data
<br>
1 = head enabled to write data</font>
</td>
</tr>
<tr>
<td><strong><font size="-1"><a style="TEXT-DECORATION:
overline">DS3
</a>:</font></strong>
</td>
<td><strong><font size="-1">Drive Select 3</font></strong>
</td>
</tr>
<tr>
<td>
</td>
<td><font size="-1">0 = drive 3 selected
<br>
1 = drive not selected</font>
</td>
</tr>
<tr>
<td><strong><font size="-1"><a style="TEXT-DECORATION:
overline">DS2
</a>:</font></strong>
</td>
<td><strong><font size="-1">Drive Select 2</font></strong>
</td>
</tr>
<tr>
<td>
</td>
<td><font size="-1">0 = drive 2 selected
<br>
1 = drive not selected</font>
</td>
</tr>
</table>
</td>
</tr>
</table>
<br>
<br>
<p>
These registers are read only, and only available on the PS/2.
</p>
<p>
By the use of the two status registers A and B on a PS/2, it is
possible to read the status of the control lines between the floppy
controller and drive. Register bits
<a style="TEXT-DECORATION:
overline">DRV2
</a>,
<a style="TEXT-DECORATION: overline">TRK0
</a>,
<a style="TEXT-DECORATION: overline">INDX
</a>,
<a style="TEXT-DECORATION: overline">WP
</a>and RDAT indicate the status of the
corresponding data lines. Note that the bit values vary between Status
Registers A and B on Model 30 and other PS/2 models. Some of the
values are also detectable through other registers and the Status
Registers readable through the data register on the AT.
</p>
<hr><!---------------------------------------------------------------------------------------->
<h3>
<a name="DRSR">Data Rate Select Register
</a>
</h3>
<br>
<p>
If you know of any source of information on this register (available
only on the PS/2), please
<a href="mailto:debs@savah.freeserve.co.uk">e-mail
</a>me with details. As soon as I am able to find the
information, it will be added to this page.
</p>
<hr color="gray" size="3"><!---------------------------------------------------------------------------------------->
<h2 align="center">
<a name="Commands">Command Set
</a>
</h2>
<p>
There are a total of 13 commands available on the &micro;PD765 and
compatible FDCs. A further 4 commands are available on the 8207x
controllers.
</p>
<p>
The
<i> sector identification
</i> consists of the cylinder, head,
sector number and sector size. This tells the controller the position
and number of sectors to perform this command on.
</p>
<p>
All commands and status bytes are transferred via the data register,
at port 37fh or 377h. Before the command can be written or the status
byte read, it is necessary to read the MRQ bit in the main status
register. This determines whether the data register is ready to supply
or receive a byte. It is also necessary to fix the drive format prior
to any read, write or format operation.
</p>
<p>
For most data transfers, DMA is used. Although the programming of the
DMA is beyond the scope of this document, I have provided some
<a href="#DMA_Prog">example code
</a>showing how to set up the DMA for
writing a single sector from a floppy disk drive to main memory. All
data transfers concern all sectors from the start sector to the end of
the track. The operation can be stopped earlier by either setting the
command byte
<i> track length/max. sector number
</i> to a value which
indicates the last sector to be operated on, or setting the count
value of the DMA controller so that it issues a TC (Terminal Count)
signal after the required number of sectors are transfered (the latter
method is demonstrated in the
<a href="#DMA_Prog">example DMA code
</a>below).
</p>
<p>
If you want a command to be executed on both heads, you need to set
the Multiple Track Bit. This tells the controller to operate on the
programmed head first, then to carry out the same command from the
start of the other head.
</p>
<p>
Once the command has been completed, the status registers ST0-3 return
information which can help you either confirm correct execution of the
command, or determine the cause of an error.
</p>
<p>
The commands fall into 3 categories. Data transfer commands and
control commands are available on all controllers. The extended
commands are only available on the AT or PS/2.
</p>
<hr><!---------------------------------------------------------------------------------------->
<h3>List of Valid Commands
</h3>
<!---------------------------------------------------------------------------------------->
<h4>
<a href="#datacom">Data Transfer Commands
</a>
</h4>
<pre>Command Name | Function
-----------------------------------------
read complete track | x2h
write sector | x5h
read sector | x6h
write deleted sector | x9h
read deleted sector | xch
format track | xdh
</pre><!---------------------------------------------------------------------------------------->
<h4>
<a href="#ctrlcom">Control Commands
</a>
</h4>
<pre>Command Name | Function
-----------------------------------------
fix drive data | x3h
check drive status | x4h
calibrate drive | x7h
check interrupt status | x8h
read sector ID | xah
seek/park head | xfh
invalid command | all invalid opcodes
</pre><!---------------------------------------------------------------------------------------->
<h4>
<a href="#extcom">Extended Commands
</a>
</h4>
<pre>Command Name | Function
-----------------------------------------------
register summary | 0fh
determine controller version | x10h
verify | x16h
seek relative | 1xfh
</pre>
<p>
In each of the above, 'x' refers to bits 7-5 of byte 0 of the command.
Note that in the
<i> seek relative
</i> command, x refers to just bits
6 and 5, as bit 7 is always set to 1. The remainder of the function
number refers to bits 4-0. Bit 4 is only used on the AT and PS/2, in 2
of the extended commands.
</p>
<p>
The function number given is the first byte of the command it applies
to. The commands vary in size from 1 to 9 bytes, and the controller
knows from the function number how many more bytes to expect. For
example, if the first byte received by the data register is
<b>
66h
</b>
, in which the upper 5 bits are 06h, the controller knows that
you are sending a
<i> read sector
</i> command, and it expects 8 more
bytes from the CPU. Additionally, you do nto need to worry about which
of the internal registers each byte of any command has to be directed
to, as the controller does that for you.
</p>
<p>
Fields common to many of the commands include the following:
</p>
<table cellpadding="2">
<tr>
<td valign="top"><strong><font size="-1">M:</font></strong>
</td>
<td><font size="-1"><strong> Multi-track operation
</strong>
<br>
1 = carry out operation on both tracks of programmed
cylinder.
<br>
0 = carry out operation on single track.</font>
</td>
</tr>
<tr>
<td valign="top"><strong><font size="-1">F:</font></strong>
</td>
<td><font size="-1"><strong> FM/MFM mode
</strong>
<br>
1 = operate in MFM (double density) mode (default)
<br>
0 = operate in FM (single density) mode</font>
</td>
</tr>
<tr>
<td valign="top"><strong><font size="-1">S:</font></strong>
</td>
<td><font size="-1"><strong> Skip mode
</strong>
<br>
1 = skip deleted data address marks, 0 = do not skip</font>
</td>
</tr>
<tr>
<td valign="top"><strong><font size="-1">HD:</font></strong>
</td>
<td><font size="-1"><strong> head number
</strong>
<br>
(always equal to head address in byte 3 of all commands using a
sector ID)</font>
</td>
</tr>
<tr>
<td valign="top"><strong><font size="-1">DR1,DR0:</font></strong>
</td>
<td><font size="-1"><strong> Drive:
</strong>
<br>
00 = drive 0 (A); 01 = drive 1 (B); 10 = drive 2 (C); 11 = drive
3 (D)</font>
</td>
</tr>
<tr>
<td><strong><font size="-1">Cylinder, Head, Sector
Number:</font></strong>
</td>
<td><strong><font size="-1">Address of first sector to
read.</font></strong>
</td>
</tr>
<tr>
<td valign="top"><strong><font size="-1">Sector size code:</font></strong>
</td>
<td><font size="-1"><strong> Sector size
</strong> = 128 * 2^x where
x is the value in this field.
<br>
eg If this field is 2 (the default), sector size = 128 * 2^2 =
512 bytes</font>
</td>
</tr>
<tr>
<td valign="top"><strong><font size="-1">Track length/Max sector
number:</font></strong>
</td>
<td><font size="-1"><strong> Number of sectors per track or max.
sector number
</strong> to operate command on.</font>
</td>
</tr>
<tr>
<td valign="top"><strong><font size="-1">Length of GAP 3:</font></strong>
</td>
<td><font size="-1">Standard value = 42, minimal value = 32
(5&frac14;")
<br>
standard value = 27 (3&frac12;")</font>
</td>
</tr>
<tr>
<td valign="top"><strong><font size="-1">Data length:</font></strong>
</td>
<td><font size="-1"><strong> length of data to read
</strong>
<br>
in bytes (only valid if sector size = 0, else equal 0ffh)</font>
</td>
</tr>
</table>
<hr><!---------------------------------------------------------------------------------------->
<h3>
<a name="datacom">Data Transfer Commands
</a>
</h3>
<p>
These are the commands used to transfer data between a disk and main
memory, or to format a track.
</p>
<p>
Each of these commands returns its'
<a href="#Results1">results
</a>in
the same format, which is only described for the
<i> read track
</i>
command.
</p>
<hr size="1"><!---------------------------------------------------------------------------------------->
<h4>Read Track (x2h)
</h4>
<p align="center">
<img title="Read Track Command" height="240" alt="Image of Read Track Command" src="./Gifs/rdtrk.gif" width="300">
</p>
<p>
When a
<i> read track
</i> command is issued, the data of a single
complete track is read. The sector specification in the command phase
is ignored for this command, and the reading starts with the first
sector after the index address mark
<strong> IDAM
</strong> , reading
sector by sector (paying no attention to the logical sector number
given in the ID address mark), until the end of the track is reached.
</p>
<p>
The track is treated as a contigusous data block, and the read buffer
in main memory should be large enough to store this amount of data.
Also, multi-track operations are not allowed with this command. If you
want to read the same track on both heads, you have to send the
command twice, unlike other read commands which allow for multi-track
operations with a single command.
</p>
<h4>
<a name="Results1">Results Phase
</a>
</h4>
<p align="center">
<img title="Data Transfer Command Results Phase" height="175" alt="image of Data Transfer Command Results Phase" src="./Gifs/Result1.gif" width="290">
</p>
<table>
<tr>
<td><strong><font size="-1">ST0,<2C>ST1,<2C>ST2:</font></strong>
</td>
<td><font size="-1"><a href="#Status0">Status Registers
</a>0 to
2</font>
</td>
</tr>
<tr>
<td><strong><font size="-1">Cylinder,<2C>head, sector number,<2C>sector
size:</font></strong>
</td>
<td><font size="-1">Sector ID. (see table below)</font>
</td>
</tr>
</table>
<br>
<strong>Sector ID in the Result Phase
</strong>
<br>
<table cellpadding="2">
<tr valign="top">
<td><strong><font size="-1">M<EFBFBD><EFBFBD></font></strong>
</td>
<td><strong><font size="-1">HD
<sub> prog
</sub> <20><></font></strong>
</td>
<td><strong><font size="-1">Last sector affected<65><64>
<br>
by command</font></strong>
</td>
<td><strong><font size="-1">Cylinder<EFBFBD><EFBFBD></font></strong>
</td>
<td><strong><font size="-1">Head<EFBFBD><EFBFBD></font></strong>
</td>
<td><strong><font size="-1">Sector<EFBFBD><EFBFBD></font></strong>
</td>
<td><strong><font size="-1">Sector Size</font></strong>
</td>
</tr>
<tr>
<td><font size="-1">0</font>
</td>
<td><font size="-1">0</font>
</td>
<td><font size="-1">before end of track</font>
</td>
<td><font size="-1">cyl
<sub> prog
</sub></font>
</td>
<td><font size="-1">HD
<sub> prog
</sub></font>
</td>
<td><font size="-1">sec
<sub> prog
</sub></font>
</td>
<td><font size="-1">siz
<sub> prog
</sub></font>
</td>
</tr>
<tr>
<td><font size="-1">0</font>
</td>
<td><font size="-1">0</font>
</td>
<td><font size="-1">end of track</font>
</td>
<td><font size="-1">cyl
<sub> prog+1
</sub></font>
</td>
<td><font size="-1">HD
<sub> prog
</sub></font>
</td>
<td><font size="-1">1</font>
</td>
<td><font size="-1">siz
<sub> prog
</sub></font>
</td>
</tr>
<tr>
<td><font size="-1">0</font>
</td>
<td><font size="-1">1</font>
</td>
<td><font size="-1">before end of track</font>
</td>
<td><font size="-1">cyl
<sub> prog
</sub></font>
</td>
<td><font size="-1">HD
<sub> prog
</sub></font>
</td>
<td><font size="-1">sec
<sub> prog
</sub></font>
</td>
<td><font size="-1">siz
<sub> prog
</sub></font>
</td>
</tr>
<tr>
<td><font size="-1">0</font>
</td>
<td><font size="-1">1</font>
</td>
<td><font size="-1">end of track</font>
</td>
<td><font size="-1">cyl
<sub> prog+1
</sub></font>
</td>
<td><font size="-1">HD
<sub> prog
</sub></font>
</td>
<td><font size="-1">1</font>
</td>
<td><font size="-1">siz
<sub> prog
</sub></font>
</td>
</tr>
<tr>
<td><font size="-1">1</font>
</td>
<td><font size="-1">0</font>
</td>
<td><font size="-1">before end of track</font>
</td>
<td><font size="-1">cyl
<sub> prog
</sub></font>
</td>
<td><font size="-1">HD
<sub> prog
</sub></font>
</td>
<td><font size="-1">sec
<sub> prog
</sub></font>
</td>
<td><font size="-1">siz
<sub> prog
</sub></font>
</td>
</tr>
<tr>
<td><font size="-1">1</font>
</td>
<td><font size="-1">0</font>
</td>
<td><font size="-1">end of track</font>
</td>
<td><font size="-1">cyl
<sub> prog
</sub></font>
</td>
<td><font size="-1">HD
<sub> prog
</sub></font>
</td>
<td><font size="-1">1</font>
</td>
<td><font size="-1">siz
<sub> prog
</sub></font>
</td>
</tr>
<tr>
<td><font size="-1">1</font>
</td>
<td><font size="-1">1</font>
</td>
<td><font size="-1">before end of track</font>
</td>
<td><font size="-1">cyl
<sub> prog
</sub></font>
</td>
<td><font size="-1">(inverse of) HD
<sub> prog
</sub></font>
</td>
<td><font size="-1">sec
<sub> prog
</sub></font>
</td>
<td><font size="-1">siz
<sub> prog
</sub></font>
</td>
</tr>
<tr>
<td><font size="-1">1</font>
</td>
<td><font size="-1">1</font>
</td>
<td><font size="-1">end of track</font>
</td>
<td><font size="-1">cyl
<sub> prog+1
</sub></font>
</td>
<td><font size="-1">(inverse of) HD
<sub> prog
</sub></font>
</td>
<td><font size="-1">1</font>
</td>
<td><font size="-1">siz
<sub> prog
</sub></font>
</td>
</tr>
</table>
<br>
<table>
<tr>
<td><font size="-1">HD
<sub> prog
</sub> :</font>
</td>
<td><font size="-1">programmed head<61><64></font>
</td>
<td><font size="-1">sec
<sub> prog
</sub> :</font>
</td>
<td><font size="-1">programmed sector</font>
</td>
</tr>
<tr>
<td><font size="-1">cyl
<sub> prog
</sub> :</font>
</td>
<td><font size="-1">programmed cylinder<65><72></font>
</td>
<td><font size="-1">siz
<sub> prog
</sub> :</font>
</td>
<td><font size="-1">programmed sector size</font>
</td>
</tr>
</table>
<br>
<br>
<p>
The Sector ID consists of Cylinder, Head, Sector and Sector Size.
Given the values programmed for M (multi-track) and HD (head number),
the values for the sector ID in the results phase indicate whether the
last sector affected was the last sector of the command or not.
</p>
<hr size="1"><!---------------------------------------------------------------------------------------->
<h4>Write Sector (x5h)
</h4>
<p align="center">
<img title="Write Sector Command" height="240" alt="Image of Write Sector Command" src="./Gifs/wrsec.gif" width="300">
</p>
<p>
The
<i> write sector
</i> command transfers one or more sectors from
main memory to the controller, from where it is transferred to the
disk. As the controller writes each sector, it also writes a valid
<i>
data address mark
</i> to the disk. This command can operate on both
heads, starting from the first sector of the second head after
reaching the end of the first head.
</p>
<p>
The
<a href="#Results1">results phase
</a>is the same as for the read
track command.
</p>
<hr size="1"><!---------------------------------------------------------------------------------------->
<h4>Read Sector (x6h)
</h4>
<p align="center">
<img title="Read Sector Command" height="240" alt="Image of Read Sector Command" src="./Gifs/rdsec.gif" width="300">
</p>
<p>
The
<i> read sector
</i> command reads one or more sectors with a valid
data address mark from the disk, and transfers the data into main
memory. This command can operate on both heads.
</p>
<p>
The
<a href="#Results1">results phase
</a>is the same as for the read
track command.
</p>
<hr size="1"><!---------------------------------------------------------------------------------------->
<h4>Write Deleted Sector (x9h)
</h4>
<p align="center">
<img title="Write Deleted Sector Command" height="240" alt="Image of Write Deleted Sector Command" src="./Gifs/wrdelsec.gif" width="300">
</p>
<p>
The
<i> write deleted sector
</i> command is the same as the
<i> write
sector
</i> command, except that for each sector written a
<i> deleted
data address mark
</i> is written instead of the normal data address
mark.
</p>
<p>
The
<a href="#Results1">results phase
</a>is the same as for the read
track command.
</p>
<hr size="1"><!---------------------------------------------------------------------------------------->
<h4>Read Deleted Sector (xch)
</h4>
<p align="center">
<img title="Read Deleted Sector Command" height="240" alt="Image of Read Deleted Sector Command" src="./Gifs/rddelsec.gif" width="300">
</p>
<p>
The
<i> read deleted sector
</i> command is the same as the
<i> read
sector
</i> command, except that only sectors with a deleted data
address mark can be read. All sectors with valid data address marks
will be ignored.
</p>
<p>
The
<a href="#Results1">results phase
</a>is the same as for the read
track command.
</p>
<hr size="1"><!---------------------------------------------------------------------------------------->
<h4>Format Track (xdh)
</h4>
<p align="center">
<img title="Format Track Command" height="180" alt="Image of Format Track Command" src="./Gifs/fmttrk.gif" width="300">
</p>
<p>
This command formats a single track. A 4 byte format buffer must be
provided for each sector of the track. The buffer holds the sector ID
of the corresponding sector. The format buffer should be large enough
to hold the data required for all sectors of the track. The
<a href="#fmtbuf">buffer format
</a>is shown below.
</p>
<p>
For ease of use, the DMA controller should be programmed to enable the
controller to read the format buffer via DMA channel 2. Alternatively,
the format data can be transferred by the use of interrupt-driven data
exchange. The controller issues a hardware interrupt before fromatting
each sector. The handler can then transfer the 4 byte format
information for the next sector to be formatted.
</p>
<p>
The formatting begins once the drive has provided a signal on the IDX
line, indicating the beginning of the track. Sectors are formatted
continuously until the drive passes the same signal again, this time
indicating the end of the track (note that the start and end of the
track are at the same point, marked by an index hole on the disk).
</p>
<p>
For the format command, the length of the GAP field is larger than
when reading or writing data. Unless i find any further information on
the GAP length, I can only include the default GAP length in this
document.
</p>
<p>
The
<a href="#Results1">results phase
</a>is the same as for the read
track command.
</p>
<h4>
<a name="fmtbuf">Format buffer for one sector
</a>
</h4>
<p align="center">
<img title="Format Buffer" height="120" alt="Image of Format Buffer" src="./Gifs/fmtbuf.gif" width="170">
</p>
<p>
In the above table,
<i> Sector Size
</i> uses the same codes as for
each of the commands described above.
</p>
<hr><!---------------------------------------------------------------------------------------->
<h3>
<a name="ctrlcom">Control Commands
</a>
</h3>
<p>
This set of commands includes various miscellaneous commands relating
to the status of a disk or drive, including seeking to a new cylinder
and responding to invalid commands.
</p>
<hr size="1"><!---------------------------------------------------------------------------------------->
<h4>Fix Drive Data (x3h)
</h4>
<p align="center">
<img title="Fix Drive Data Command" height="120" alt="image of Fix Drive Data Command" src="./Gifs/FixData.gif" width="300">
</p>
<table>
<tr>
<td valign="top"><strong><font size="-1">NDM:</font></strong>
</td>
<td><font size="-1"><strong> Non-DMA Mode
</strong>
<br>
0 = Data Transfer via DMA
<br>
1 = Data Transfer not via DMA</font>
</td>
</tr>
</table>
<br>
<br>
<br align="center">
<table>
<tr>
<td align="middle">Step Rate [ms]
<br>
<img title="Step Rate" height="190" alt="image of Step Rate" src="./Gifs/StepRate.gif" width="220">
</td>
<td align="middle">Head Unload Time [ms]
<br>
<img title="Head Unload Time" height="190" alt="image of Head Unload Time" src="./Gifs/HdUnload.gif" width="220">
</td>
<td align="middle">Head Load Time [ms]<img title="Head Load Time" height="190" alt="image of Head Load Time" src="./Gifs/HeadLoad.gif" width="220">
</td>
</tr>
</table>
<br>
<br>
<p>
This command is used to pass mechanical control data to the controller
for the connected drives. It should be noted, as shown in the bottom 3
diagrams, that the values are also dependent on the data transfer
rate, which in the AT and PS/2 is set in the
<a href="#CCR">control
configuration register
</a>.
</p>
<p>
This command doesn't have a result phase.
</p>
<hr size="1"><!---------------------------------------------------------------------------------------->
<h4>Check Drive Status (x4h)
</h4>
<p align="center">
<img title="Check Drive Status Command" height="100" alt="image of Check Drive Status Command" src="./Gifs/ChkDrvStatus.gif" width="300">
</p>
<p>
This command provides status information relating to the state of the
connected drives.
</p>
<h4>Result Phase
</h4>
<p align="center">
<img title="Check Drive Status Command Result Phase" height="50" alt="image of Check Drive Status Command Result Phase" src="./Gifs/Result2.gif" width="290">
</p>
<p>
Status Register 3 contains drive information.
</p>
<hr size="1"><!---------------------------------------------------------------------------------------->
<h4>Calibrate Drive (x7h)
</h4>
<p align="center">
<img title="Calibrate Drive Command" height="100" alt="image of Calibrate Drive Command" src="./Gifs/Calibrate.gif" width="300">
</p>
<p>
This command is used to position the read/write head to cylinder 0. If
a seek error occurs in the course of a sector access, the head can be
moved to an absolute cylinder to recalibrate the drive.
</p>
<p>
This command doesn't return a result phase, but after cmopletion an
interrupt is issued. To check the status information of this command,
you should issue a
<i> check interrupt status
</i> cmomand to determine
the commands status information.
</p>
<p>
When the controller sees this command, it sets the DIR signal to 0,
and passes the drive up to 79 step pulses. After each of these pulses,
the controller checks the TRK0 signal. If it is active (htat is, the
head is on track 0), the controller sets the SE bit in Status Register
0, and aborts the command. If TRK0 is not active after 79 step pulses,
the controller sets bits SE and EC in Status Register 0, and
terminates the command.
</p>
<p>
To calibrate the drive, you may have to issue several calibration
commands, especially if the drive being calibrated has more than 80
tracks. After completion of the command, you should always check
whether the head is correctly positioned over track 0, using the
<i>
check interrupt status
</i> command. A calibration is always necessary
after a power up, to initialise the head position correctly.
</p>
<hr size="1"><!---------------------------------------------------------------------------------------->
<h4>
<a name="chkint">Check Interrupt Status
</a>(x8h)
</h4>
<p align="center">
<img title="Check Interrupt Status Command" height="80" alt="image of Check Interrupt Status Command" src="./Gifs/ChkInts.gif" width="300">
</p>
<p>
This command is used to check status infromation about the state of
the controller in the result phase when the controller has returned an
interrupt.
</p>
<p>
The interrupt signal is reset by this command, which also determines
the source of the interrupt via status register ST0. If the command is
issued with no interrupts pending, a value of 80h is returned in ST0,
corresponding to the message
<i> invalid command
</i> .
</p>
<p>
Interrupts are issued in the following cases:
</p>
<pre>At the beginning of the result phase of the commands:
read sector
read deleted sector
write sector
write deleted sector
read track
format track
read sector ID
verify
After completion of the following commands without a result phase:
calibrate drive
seek
seek relative
For data exchange between main memory and controller when interrupt-driven data exchange
is active and the controller is not using DMA.
</pre>
<h4>Result Phase
</h4>
<p align="center">
<img title="Check Interrupt Status Command Return Phase" height="70" alt="Image of Check Interrupt Status Command Return Phase" src="./Gifs/Result3.gif" width="290">
</p>
<hr size="1"><!---------------------------------------------------------------------------------------->
<h4>Read Sector ID (xah)
</h4>
<p align="center">
<img title="Read Sector ID Command" height="100" alt="#image of Read Sector ID Command" src="./Gifs/RdSecID.gif" width="300">
</p>
<p>
This command is used to read the Sector ID of the first ID address
mark the controller is able to detect. Using this command, it is
possible to determine the current position of the read/write head. If
no ID address mark can be read in one complete disk revolution, the
controller issues an error message, detected from the values returned
in ST0-2.
</p>
<h4>Result Phase
</h4>
<p align="center">
<img title="Read Sector ID Command Result Phase" height="175" alt="image of Read Sector ID Command Result Phase" src="./Gifs/Result1.gif" width="290">
</p>
<p>
The values in the sector ID are calculated in the same way as for the
<a href="#Results1">result phase
</a>of the read track command.
</p>
<hr size="1"><!---------------------------------------------------------------------------------------->
<h4>Seek/Park Head (xfh)
</h4>
<p align="center">
<img title="Park Head Command" height="120" alt="image of Park Head Command" src="./Gifs/ParkHead.gif" width="300">
</p>
<p>
The Seek Head command, sometimes called the Park Head command, moves
the read/write head to the specified cylinder. When the controller
receives this command, it compares the programmed cylinder number and
the current cylinder number. The direction signal (DIR) is set, and
step pulses are issued until the two cylinder numbers match.
</p>
<p>
This command has no result phase. To verify successful completion of
the command, it is necessary to check the head position immediately
after completion of the command, using the
<a href="#chkint">check
interrupt status
</a>command.
</p>
<hr size="1"><!---------------------------------------------------------------------------------------->
<h4>invalid command (all invalid opcodes)
</h4>
<p align="center">
<img title="Invalid Opcode Command" height="80" alt="image of Invalid Opcode Command" src="./Gifs/Invalid.gif" width="300">
</p>
<p>
Whenever an invalid opcode is detected, the controller switches to a
standby state, and bit 7 of ST0 is set. The same happens if
<i> check
interrupt status
</i> is issued with no interrupts pending.
</p>
<h4>Result Phase
</h4>
<p align="center">
<img title="Invalid Opcode Command Result Phase" height="50" alt="image of Invalid Opcode Command Result Phase" src="./Gifs/Result4.gif" width="290">
</p>
<hr><!---------------------------------------------------------------------------------------->
<h3>
<a name="extcom">Extended Commands
</a>
</h3>
<p>
These commands are not available on all controllers, being introduced
in the AT and PS/2. If the controller does not support any of these
commands, it will treat them as invalid commands.
</p>
<hr size="1"><!---------------------------------------------------------------------------------------->
<h4>Register Summary (0fh)
</h4>
<p align="center">
<img title="Register Summary" height="80" alt="image of Register Summary" src="./Gifs/RegDump.gif" width="300">
</p>
<p>
This command returns the values read from the internal controller
registers.
</p>
<h4>Result Phase
</h4>
<p align="center">
<img title="Register Summary Command Result Phase" height="175" alt="image of Register Summary Command Result Phase" src="./Gifs/Result5.gif" width="290">
</p>
<table>
<tr>
<td valign="top"><strong><font size="-1">Current<EFBFBD>Cylinder<EFBFBD>DR0,<2C>DR1,<2C>DR2<52>DR3:</font></strong>
</td>
<td><font size="-1">Cylinder on drive 0, 1, 2, 3 where read/write
head is currently positioned.</font>
</td>
</tr>
<tr>
<td valign="top"><strong><font size="-1">step<EFBFBD>time, head<61>unload<61>time,
head<61>load<61>time:</font></strong>
</td>
<td><font size="-1">mechanical characteristics set by the
<i> fix
drive data
</i> command</font>
</td>
</tr>
<tr>
<td valign="top"><strong><font size="-1">NDM:</font></strong>
</td>
<td><font size="-1"><strong> non-DMA mode
</strong>
<br>
1 = DMA disabled
<br>
0 = DMA enabled</font>
</td>
</tr>
<tr>
<td><strong><font size="-1">number of sectors/track
length</font></strong>
</td>
<td><font size="-1"><strong> number of sectors per
track
</strong></font>
</td>
</tr>
</table>
<br>
<br>
<hr size="1"><!---------------------------------------------------------------------------------------->
<h4>Determine Controller Version (10h)
</h4>
<p align="center">
<img title="Get Controller Version Command" height="80" alt="image of Get Controller Version Command" src="./Gifs/CtrVer.gif" width="300">
</p>
<p>
This command determines whether there is a controller present which
supports the extended commands. If the controller does not support the
extended commands, this command is treated as an invalid opcode, and
an error message is returned.
</p>
<h4>Result Phase
</h4>
<p align="center">
<img title="Determine Controller Version Command Result Phase" height="50" alt="image of Determine Controller Version Command Result Phase" src="./Gifs/Result6.gif" width="290">
</p>
<p>
This result is only returned if an extended controller is installed.
</p>
<hr size="1"><!---------------------------------------------------------------------------------------->
<h4>Verify (x16h)
</h4>
<p align="center">
<img title="Verify Command" height="240" alt="image of Verify Command" src="./Gifs/Verify.gif" width="300">
</p>
<table cellpadding="2">
<tr>
<td valign="top"><strong><font size="-1">EC:</font></strong>
</td>
<td><font size="-1"><strong> enable count value
</strong>
<br>
1 = command byte 8 specifies the number of sectors to verify
<br>
0 = command byte 8 specifies the data length, if sector size =
0</font>
</td>
</tr>
<tr>
<td valign="top"><strong><font size="-1">data<EFBFBD>length/verify<66>sectors:<3A><></font></strong>
</td>
<td><font size="-1"><strong> If EC = 0 and sector size =
0:
</strong>
<br>
<EFBFBD><EFBFBD><EFBFBD><EFBFBD>length of data to verify, in bytes.
<br>
<strong> Else:
</strong>
<br>
<EFBFBD><EFBFBD><EFBFBD><EFBFBD>number of sectors to verify.</font>
</td>
</tr>
</table>
<br>
<br>
<p>
This command is similar to the read command, except that it doesn't
transfer data to main memory. One or more sectros with valid DAMs are
read from the disk, and their CRC is calculated. This value is
compared to the read CRC, inorder to check the internal consistency of
the data. As no data is transferred, the command cannot be aborted by
a TC signal from the DMA controller. However, if youset the EC bit to
1, the controller issues an implicit TC signal when the count value in
<i> data length/verify sectors
</i> is decremented to 0. In that case,
<i> data length/verify sectors
</i> indicates the number of sectors to
be verified. A value of 0 in this byte tells the controller to check
256 sectors. When EC is set to 0,
<i> data length/verify data
</i>
should be set to ffh.
</p>
<h4>Result Phase
</h4>
<p align="center">
<img title="Verify Command Result Phase" height="175" alt="image of Verify Command Result Phase" src="./Gifs/Result1.gif" width="290">
</p>
<hr size="1"><!---------------------------------------------------------------------------------------->
<h4>Seek Relative (1xfh)
</h4>
<p align="center">
<img title="Seek Relative Command" height="120" alt="image of Seek Relative Command" src="Gifs/SeekRel.gif" width="300">
</p>
<table cellpadding="2">
<tr>
<td valign="top"><strong><font size="-1">DIR:</font></strong>
</td>
<td><font size="-1"><strong> Step Direction
</strong>
<br>
1 = inward (to larger cylinder numbers)
<br>
0 = outward (to smaller cylinder numbers)</font>
</td>
</tr>
<tr>
<td><strong><font size="-1">cylinder step:</font></strong>
</td>
<td><strong><font size="-1">number of cylinders to
step</font></strong>
</td>
</tr>
</table>
<br>
<br>
<p>
This command is sued to move the read/write head relative to the
current cylinder. There is no result phase for this command, but its'
result can be checked through the use of either the
<i> read sector
ID
</i> or the
<i> register dump
</i> command.
</p>
<hr color="gray" size="3"><!---------------------------------------------------------------------------------------->
<h2 align="center">Sample code
</h2>
<hr size="1"><!---------------------------------------------------------------------------------------->
<h4>
<a name="DMA_Prog">Initialise DMA for writing a single sector from
FDC to main memory
</a>
</h4>
<!---------------------------------------------------------------------------------------->
<p>
Please note that this code was added in a hurry and has not yet been
tested. I wanted to publish this page with DMA initialisation code
before going on holiday. The code will be tested and, if necessary,
adjusted, late in June (1999), when I get back from my vacation.
</p>
<pre><strong>
;*******************************************************************************
; This code expects to receive the data buffer address in ES:BX
;
; ES: Buffer segment
; BX: Buffer offset
;
; The address is a 20 bit segmented address, calculated from Segment*16 + Offset
;
; Bits 19-16 of the address form the entry for the DMA page entry
; Bits 15-8 of the address form the high-order byte for the DMA address register
; Bits 7-0 of the address form the low-order byte for the DMA address register
;
; For more information on this, it will be necessary to read other literature
; specifically targetting the DMA controller.
;*******************************************************************************
disable_dma1: ; disable DMA 1
mov al, 14h ; output 14h to the command register to disable and
out 08h, al ; initialise the DMA controller
mode: ; set up DMA transfer mode for channel 2
mov al, 56h ; set up for a single write transfer to main memory
out 0bh, al ; using DMA channel 2
; for a read, output 5ah
get_address: ; get the buffer address, and split into component parts
; as required by the DMA controller.
mov ax, es ; load buffer segment into AX
mov cl, 04h ;
shl ax, cl ; shift value in ax left 4 times
add ax, bx ; add offest + buffer. AX now contains high and low
; for the DMA address register.
jc carry ; if carry is set, jump to carry
no_carry:
mov bx, es ; load segment of buffer into BX
mov cl, 04h ;
shr bh, cl ; shift right BH 4 times. BH now contains the value
; for the DMA page segment
jmp buffer_address; output the buffer address
carry:
mov bx, es ; load segment of buffer into BX
mov cl, 04h ;
shr bh, cl ; shift right BH 4 times. BH now contains the high
; bits of the segment
adc bh, 00h
buffer_address: ; output the address to the DMA controller
out 0ch, al ; reset flip-flop. I am not sure what this does,
; but my reference shows this as necessary.
out 04h, al ; output low order address byte to address register
mov al, ah ;
out 04h, al ; output high-order address byte to address register
mov al, bh ;
out 81h, al ; load page register with page value
count: ; set up count register
out 0ch, al ; reset flip-flop
mov al, 0ffh ;
out 05h, al ;
mov al, 01h ;
out 05h, al ; load 511 into count register (to read one sector)
; it should be noted that for multiple transfers, the
; second value output to port 05h is equivalent to
; (2*number of sectors)-1
release_channel:
mov al, 02h
out 0ah, al ; release channel 2
enable_dma1: ; enable DMA 1
mov al, 10h
out 08h, al ; this is the value for enabling the DMA for the mode required
</strong>
</pre>
<hr><!---------------------------------------------------------------------------------------->
<p>
Once I have written my own floppy disk drivers for the operating
system I am working on, I'll add a link to that. Until then, I'm happy
to continue replying to any questions I receive by email. Please note
that I am a programmer, not a hardware expert, and as such may not be
able to answer questions that relate to how to produce hardware using
a FDC.
</p>
<p>
If you have any comments on the content of this page, including errors
and typos, please contact me:</p>
<p align="center"><script language=javascript><!--
var contact = "<img src=../../../mail-me.jpg>"
var localhost = "Webmistress"
var emailHost = "dwiles"
var domain = "demon.co.uk"
var subject = "FDC"
document.write("<a href=" + "mail" + "to:" + localhost + "@" + emailHost + "." + domain +
"?" + "Subject" + "=" + subject + ">" + contact + "</a>" + ".")
//-->
</script>
</p>
</body>
</html>