| |
A beginners guide to IDE
This document is written to help people to start
using IDE hard drives outside the PC environment.
It is based on my own findings when trying to
connect a hard drive to my Atmel AVR Mega163 microcontroller. Therefore
the direct I/O names and sample code instructions are specific to the
AVR architecture and the Mega163 chip, but I'll try to write it in a way that makes it easy
to convert to different controllers and architectures.
This document only covers PIO (Programmed
Input/Output) data transfer, as it is probably all you are ever going to
need, and it's fairly simple once you get then hang of it. |
|
The hardware
Wires, Pins and Ports. |
First, we'll have a look at the hardware we are
going to use, and how to wire it all togheter.
 |
Hard drive
To get started, It's a good idea
to have one of these, that you are not too worried about
destroying.
Most likely you are not going to break the drive, but if you
follow this guide to the end, you'll be writing stuff to the
disk, that makes it unreadable for the PC.
The picture shows a Western
Digital "Caviar 136AA", and it's this drive I've been working
whit. |
 |
Old style 40-lead IDE cable
You'll probably need one of these
too...
If you have a µC developing
board like the AVR STK500 that has 2x4 or 2x5 pin headers for
the I/O ports, I have written a little guide on
how to modify the IDE cable to easily
connect it directly to the board. |
Pinout table for the IDE connectors.
All lines are 5v TTL level.
| Pin # |
Signal Function |
Pin # |
Signal Function |
| 1 |
Reset |
2 |
Ground |
| 3 |
Data 7 |
4 |
Data 8 |
| 5 |
Data 6 |
6 |
Data 9 |
| 7 |
Data 5 |
8 |
Data 10 |
| 9 |
Data 4 |
10 |
Data 11 |
| 11 |
Data 3 |
12 |
Data 12 |
| 13 |
Data 2 |
14 |
Data 13 |
| 15 |
Data 1 |
16 |
Data 14 |
| 17 |
Data 0 |
18 |
Data 15 |
| 19 |
Ground |
20 |
Key |
| 21 |
DMARQ |
22 |
Ground |
| 23 |
DIOW- |
24 |
Ground |
| 25 |
DIOR- |
26 |
Ground |
| 27 |
IORDY |
28 |
CSEL |
| 29 |
DMACK- |
30 |
Ground |
| 31 |
INTRQ |
32 |
IOCS16- |
| 33 |
DA1 |
34 |
PDIAG- |
| 35 |
DA0 |
36 |
DA2 |
| 37 |
CS1FX- |
38 |
CS3FX- |
| 39 |
DASP- |
40 |
Ground |
Let's have a look at what these pins
are, which one we'll use, and where to connect them.
|
Reset |
This is the hardware reset
line on the IDE/ATA bus. The signal is input to the IDE devices
and is active low. This means that pulling it to logical "0"
would reset the devices connected to the IDE bus.
I connected it to Pin7 on PORTA.
Optionally, it can be connected to Vcc. |
Data 0 -
Data 15 |
These are the actual data
lines on the IDE/ATA bus. All these lines are bidirectional I/O
and are used to transfer data, instructions, parameters and
status information between the HDD and the host (in our case,
the µC).
It seems that when these lines are configured as outputs, they
need pull-up resistors provided by the host side to function
properly.
It is possible to operate the HDD using only the lower 8 bits of
this data bus, as all the commands and parameters are 8 bits.
The downside is that you'll loose every 2nd byte of the space on
the drive.I have
connected the lower 8 lines to PORTC and the upper 8 lines to
PORTB on my µC. |
DMARQ
DMACK |
These are used for DMA
transfer, wich is not covered in this document.
DMARQ is the DMA request sent from the IDE device to the host.
DMARK is the DMA acknowledge that the host sends to the IDE
device.I left the DMARQ
line unconnected, and connected DMACK to Vcc. |
| CS1FX, CS3FX |
Chip select lines. Active Low.
For basic PIO operations, it's enough to know that CS1FX should
be logical "0" and CS3FX should be logical "1".
Other combinations of these lines are used for DMA transfer, and
other specialized tasks.
I connected CS3FX to Pin0 and CS1FX to Pin1 on PORTA. |
| DA0:DA2 |
Register Address lines.
These lines are used to select which register in the IDE device
that is going to be "connected" to the data lines when the read
or write strobe is activated.
I connected DA2 to Pin2, DA1 to
Pin3 and DA0 to Pin4 on PORTA. |
| DIOR |
Read Strobe. Active Low.
Sending a logical "0" onto this line causes the IDE device to
place the content of the register selected by A2:A0 onto the data lines.
Data is present on the data bus after the falling edge of DIOR.
This one, I connected to Pin5 on
PORTA. |
|
DIOW |
Write strobe. Active Low.
This line is used to signal the IDE device that there is data
ready on the data bus.
Setting it to logical "0" makes the IDE device read the data on
the data lines (8 or 16 bits) into the device register specified
by the signals on the DA2:DA0 lines.
Data is read into the IDE device from the data bus after the falling edge of this
line.This one, I
connected to Pin6 on PORTA. |
| DASP |
Device active.
Connecting a LED from Vcc (through resistor) to this line would
work as an activity indicator.
Wiring it directly to any of the 8 LED pins on the STK500 also
works fine, as they are active low, and have pull-up resistors
built in. |
| GND |
Ground I soldered
them to the GND pins on the connectors I made in the
Cable Hack guide. |
All the lines that are not covered here, I left
unconnected. Most of them have a purpose, but they are not needed for
the simple operations described in this guide.
|
|
Inside the HDD
First glance at the
registers. |
Inside the electronics on the HDD, there are
several registers. In the following table, I have included the ones that
we'll be using, the data pattern to put on the A2:A0 and CS1FX&CS3FX
lines if you have wired it to PORTA in the same way as I did, and a
brief description of the function of each register.
We'll get back to them in more detail later.
| Name |
Bit Pattern |
Description |
| Status |
0b11111101 |
This is a read-only register
that holds information about the state of the IDE device. |
| Data |
0b11100001 |
This is the only 16 bit
register. It's both read and write, and is used to read from or
write to the data buffer in the IDE device. |
| Command |
0b11111101 |
This is a write-only register,
that is used to send commands to the IDE device. |
| Sector Count |
0b11101001 |
Used for additional command
parameters. |
| Sector Number |
0b11111001 |
Used for additional command
parameters. |
| Cylinder High |
0b11110101 |
Used for additional command
parameters. |
| Cylinder Low |
0b11100101 |
Used for additional command
parameters. |
| Device/Head |
0b11101101 |
Used for additional command
parameters. |
At this point, it's time to start doing some
"real world" stuff.
If you have modified and connected the IDE cable as described earlier,
this is going to be a straightforward process, and you should be able to
observe the results straight away.
What we are going to attempt, is to read the
content of the Status register, and show the result on the STK500
LED's.
Using the 10pin jumper cables that came whit the STK500, connect all
PORTD pins to the LED pins.
The first thing we need to do, is configure the
I/O direction for the ports we are going to use.
PORTA is always going to be configured as output, so we can set the data
direction register for PORTA (UDRA) to all "1"s.
The same is true for PORTD.
Because we are going to read from the data bus,
we need to configure PORTC (and PORTB) as inputs. We also need to enable
the built-in pull-up resistors on the input ports.
Writing "0"s to DDRC and DDRB configures them as inputs. Writing "1"s to
PORTB and PORTC enables the pull-up resistors.
The next thing to do, is to activate the proper
register address lines on the IDE bus.
For the status register, this is done by setting CS1FX to "0",
CF3FX to "1" and all DA lines to "1".
RESET, DIOR and DIOW should all be deactivated by
setting them to "1".
Outputting the value "0b11111101" to PORTA makes light work of
that.
To read the status register data, we need to
activate the Read Strobe, by writing a logical "0" to Pin5 on
PORTA.
When the read strobe is active (logical "0"), the data in the Status
register is available on the lower 8 bits of the IDE data bus. My
experience shows that it is necessary to wait a few cycles before the
data bus is read.
While the read strobe is "0", we can read the content of the Status
register by reading PINC.
We'll put the content of PINC into r16.
After PINC have been read, we can deactivate the read strobe by setting
it to logical "1".
The next thing to do is to invert the content of
r16, and write it to PORTD.
The reason for inverting it, is that the LEDs on the STK500 are active
low, resulting in a "0" being a lit LED, and a "1" being a dark LED.
The last thing to do is to put the
inverted data from the status register onto the LEDs, and start over.
Get the complete ASM source
code and paste it into a new project.
Here is a table of what the different bits
in the status register are.
| Bit number |
Name |
Description |
| 7 |
BSY |
Busy flag. If this flag is "1"
the device is busy, no other data is valid |
| 6 |
DRDY |
Device ready flag. If this
flag is "0" the device is not ready |
| 5 |
WFT |
Obsolete. Indicates a write
error. |
| 4 |
SKC |
Oboslete. Indicates that the
device performed a seek operation successfully |
| 3 |
DRQ |
Data Request. This bit
indicates that the device is ready to transfer data |
| 2 |
ECC |
Obsolete. Indicates that an
EEC correction was performed on the data. |
| 1 |
INDEX |
Obsolete. This bit is pulsed
to "1" each revolution of the disk. |
| 0 |
ERR |
This bit is set to "1" if
there has been any errors. All other data is then invalid. |
If you tried the sample code provided,
you should be able to see that when the drive is powering up, the BSY
flag is set to "1" while all the other flags are "0".
After a short while, the BSY flag should be cleared, and the
DRDY and the SKC flag should be set to "1". You should also
be able to observe a faint glow in the LED indicating the INDEX
flag. This is because this flag is pulsed to "1" as the heads pass the
index marker on the disk, and this happens only once every disk
revolution.
It should be noted that the state of the obsolete flags may not be
indicated properly, as the device manufacturers are no longer required
to implement them in new designs.
|
The ASM code
samples provided are for the Atmega163 chip, running at 4.000MHz, using
the latest AVR studio as the development environment.
You should be able easily port the code to any other device or platform. |
Real work
Sending the first commands
to the device |
The next step in this guide, is to send a
command to the IDE device.
The commands are the stuff that makes it all work.
The first command we are going to send, is the
"Idle Immediate" command.
This command causes most hard drives to park the heads an spin down,
which can be verified by simply listening to the hard drive.
To prepare to send the command, there
is a few things we have to do first.
The first thing to do, is to place the I/O ports connected to the data
lines into output mode.
In the AVR, writing "1"s to to the data direction registers takes care
of that.
The next, not so obvious, step is to
inform the devices (there can be two devices on each IDE bus, remember?
One Slave and one Master) which one of them we want to perform the
command.
This information is hiding inside the Device/Head register, bit
number 4.
Setting this bit to "0" selects the Master device. Setting it to "1"
selects the slave.
Assuming the HDD we are working on are set as Master, we need to write a
"0" to this bit.
The easiest way to do this, is to write "0"s to all the bits in this
register, as the other bits are ignored for this command.
To do this, we need to "connect" the Device/Head register to the
data lines, by setting DA0 to "0", DA1 and DA2 to
"1", CS1FX to "0" and
CS3FX to "1".
Writing "0b11101101" to PORTA takes care of that.
Now that the data lines are connected to the Device/Head
register, we can write all those "0"s to it.
First, we have to write the "0"s to the lower 8 bits of the data lines,
by outputting them on PORTC.
Then we make the IDE device read the content on the data bus into the
Device/Head register, by activating the Write Strobe.
Activate the write strobe by setting Pin6 to "0".
Once the Write Strobe have been activated, the IDE device
reads the data on the lower 8 bits if the data lines into the
Device/Head register. It is wise to wait a few cycles before
disabling the write strobe again, by setting Pin6 in PORTA to "1"
Now that we have decided which devise is going to execute the
command, we'll send it the actual command.
First we need to configure the DA and CSnFX lines to
access the Command register.
Outputting "0b11111101" to PORTA
takes care of that.
When the Command register is
selected, the data we write onto the data lines is interpreted by the
IDE device as a command.
The command we are going to send, is "$E0". We'll write "$E0" to PORTC,
then activate the Write Strobe by setting Bit6 in PORTA to "0",
wait a few cycles for the IDE device to receive the command, and then
deactivate the Write Strobe by setting Pin6 in PORTA to "1".
Within a timeframe of 2 seconds after
the command have been sent, the device should go into idle mode, and
spin down.
For the following
code sample to work, we need to disconnect
the 10 Pin jumper cable going to the LEDs, and place it on the SWITCHES
header.
The code is going to monitor the status register until the device is
ready. Then it waits for someone to press the SW0 button on the STK.
The buttons on the STK500 is also inverted, so pressing the button gives
a logical "0" to the controller, and releasing it gives a logical "1".
If everything went well, the HDD should
now be in idle mode.
This is not a very productive command, but it works as an insurance that
everything is working as it should.
In some rare cases, the HDD might not support this command, and would
simply do nothing.
If nothing happens whit your HDD, you might try it on another, more
modern one.
Nothing should happen to the data on the HDD, unless there's a freak
accident and you happen to zap it whit ESD, or drop it on the floor.
|
|
Reading data
Getting some real data from
the buffer on the hard drive |
In this section we are going to read some real
data from the HDD we are working whit. The data we are going to read, is
the data that is generated by the "Identify Device" command.
This is also a fairly simple command, as then only
additional parameter we need to specify is which of the two IDE devices
that are going to execute it.
When the command is finished, the device have
generated a data set of 512 bytes in it's internal data buffer. To read
from this buffer, we have to read from the Data register. As
mentioned before, this is the only 16 bits wide register, so we have to
use both PORTB and PORTC to properly read it.
When data is read from the Data register, it comes 2 bytes at the
time. This means that we have to read from the register 256 times to get
all the data.
To pull this one off, we can examine
the data in two ways.
One way is to store it in the µC EEPROM and from there, read it from the
µC using the SPI programming interface.
The other way, is to use the built-in UART in the µC to send it directly
to the PC, using the spare RS323 port on the STK500.
Using the EEPROM is fast going to eat away at the limited number of
guaranteed write cycles of the EEPROM, but might be the best aproach if
you don't have the STK500 board.
For the UART approach, the first thing
to do is to connect one of the two-wire jumper cables that came whit the
STK500 from PORTD Pin0 and Pin1 to the "RS232 SPARE" header on the
STK500. "RXD" goes to "PD0" and "TXD" goes to "PD1".
If you have another serial cable, and two COM ports on your computer,
you can just connect the "RS323 SPARE" connector on the STK500 to COM2
or whatever on your PC. If not, you can simply swap the one you got
between the "RS232 CTRL" plug and the "RS232 SPARE" plug on the STK500.
If you are not familiar whit how the UART works, that's a disadvantage.
You can still continue in this guide, as the UART settings are covered
in the code sample.
After we have worked out the new
connections regarding the UART, we are ready to go on.
The first thing we have to do, is to check if the HDD is busy. We
already know how to do that, so I'll just go on to the next step.
When the device is ready, We'll write "0"s to the Device/Head
register to select the Master device as the target for the following
command.
The command we are going to send this time is "$EC".
The process of sending the command is the same as for sending the "Idle
Immediate" command. To sum it up:
-Set PORTB and PORTC as outputs
-Output the address for the Device/Head register (0b11101101) on
PORTA
-Write "0"s to PORTC
-Pulse the Write Strobe to "0"
-Output the address for the Command register (0b11111101) on
PORTA
-Output "$EC" to PORTC, and optionally "0"s to PORTB. PORTB is ignored
nonetheless.
-Pulse the Write Strobe to "0"
The command we just sent is going to
take the drive some time to execute, so we have to check the BSY
flag before we continue.
As long as the BSY flag is "1" we should just loop back and check
it again and again until it changes to "0".
By the time the BSY flag changes from "1" to "0", the DRQ
flag should have changed from "0" to "1" to signal that the device is
ready to transfer data.
To read the data from the HDD data
buffer, we have to repeatedly read from the Data register, and
each time we read it, we get another two "new" bytes from the data
buffer.
Reading from the Data register
is just as easy as reading from the Status register. The only
difference is what signals we put out on the DAn and CSnFX
lines.
It is also a good idea to check the BSY flag before each new read
from the DATA register. In most cases, it'll be cleared by the
time we get about to checking it, but not checking it may result in
false data readout, and we don't want that.
Here's a quick summary of the read process:
-Set PORTB and PORTC as inputs.
-Check the BSY flag until it's cleared.
-Output "0b11100001" on PORTA to select the Data register
-Activate the Read Strobe by setting PORTA Pin5 to "0"
-Wait for a few cycles (nop)
-Read all the 16 bits on the data lines, using both PORTB and PORTC.
-Deactivate the Read Strobe
-Put the data just read from the data lines somewhere "safe"
-Repeat 255 more times.
A nice place to put the data we get
from each single read cycle would be either the UART or the EEPROM.
You could just place it in SRAM, but it would be hard to examine later
on.
Whit the UART, you have the data right away, or by using the EEPROM, you
can easily read it out using any ISP device.
This time, there are 2 almost similar
code samples. One for UART, and one for
EEPROM.
If you are not using the exact same
hardware as I am, you propably have to modify the code a bit. In doing
so, you are likeley to discover that both the UART and EEPROM code are
more or less the same.
These samples are direct links to the ASM files, to preserve the proper
formatting.
To use the UART sample, you need to run
a properly configured terminal program on your PC.
A good such program is
Br@y++ Terminal.
Here's how you make it work:
-If you use just one COM port, close the AVR programming window.
-Connect a serial cable to the "RS323 SPARE" plug on the STK500
-Start Br@y++ Terminal, and select the COM port you have connected to
the "RS323 SPARE"
-Select 19200 as the baud rate
-Press the "Connect" button.
-In the long text-box in the lower part of the program window, type the
number 1
-Press the " -> Send" button, located immediately to the right of the
text-box.
If everything went well, you should see
some data streaming into the upper frame of the program window.
There's going to be alot of <0>'s but you should also be able to make
out the serial number (20 ASCII characters) and the model number (40
ASCII characters).
If you get nothing, you might have
messed up the settings in the terminal program.
try to disconnect the wire going to the "RS323 SPARE" header TXD and RXD
and short the two pins togheter.
While they are shorted, press the " -> Send" button again and observe
the upper frame.
If you don't get a single "1" in there, something is very wrong.
Either you got the wrong COM port or you have some faulty wiring
somewhere.
If you used the EEPROM solution, the µC
immediately starts polling the BSY and RDY flag, and once
the device is ready, it executes the command and saves the 512 byte long
result to EEPROM, then stops.
AVRStudio is buggy when trying to read the EEPROM content directly into
the simulator.
In most cases, you just get a bunch of "FF"s.
If you read the EEPROM to a file, and then uses the "Up/Download Memory"
tool in the "Debug" menu to load the hex file into EEPROM debug memory,
you should be able to view the results from the Identify Device command
in the EEPROM section in the memory window.
|
|
More Reading
Reading from the first
sector on the hard drive |
By this time, we should have the basic
principles of reading the HDD data buffer nailed down.
The next logical step from here, is to put some more fascinating data
into this buffer.
One way to do that is to send the Read Sectors ($20) command to
the drive.
Depending on what we put in the command parameter registers, we can read
any sectors we like, from the entire disk, and put that data into the
data buffer for reading.The
Read Sectors command is a bit more complicated, as it uses no less
then five different parameters.
For the HDD to be able to give us the data we want, we have to tell it
where it is.
More to the point; we have to tell it from which sector it's going to
read and how many sectors to read in one go.
The sectors on the HDD can be accessed
in at least two different ways. One way is to imagine all the sectors
lined up in a vast table, where the sectors are simply numbered starting
from 0 and going up to the total number of sectors on the drive. This is
called Logical Block Addressing or LBA. The other way, is to specify
which Sector, Head and Cylinder we want to read from. This is called
CHS.
CHS is a pain to work whit, and has a capacity limit at 2048MB. Luckily,
most hard drives today support LBA addressing, so that's what we are
going to use.
The LBA address is a 28bit wide
address, and knowing that each sector have 512 byte of data each, it's
fairly easy to calulate that the limit is much higher for this methode.
If you really want to know, then here it is: 2^28 x 512 = 137438953472
bytes, or roughly 128GB.
The LBA address starts in the Sector
Number register, goes on to Cylinder Low and Cylinder High
and has it's last 4 bits at the end of the Device/Head register.
The Device/Head also holds another secret in it's Bit6. This bit
is used to select either CHS or LBA mode. Setting it to "1" enables LBA
addressing.
On top of all this, we have the
Sector Count register.
The Sector Count register is used to specify how many sectors we
want to read, starting from the one specified by the LBA address.
This register is somewhat tricky. If we want just one sector, we write
"$01" to it. If we want 5 sectors in one go, we write "$05" to it.
If we write "$FF" to it, we get 255 sectors. Writing "$00" on the other
hand, doesn't give us 0 sectors, but 256.
To read sector 0, also known as the
boot sector, first we need to write values to five different registers
before we can issue the command.
The first register we'll write to is the Sector Count register.
We only want one sector, so we'll write "$01" to it.
Next, we'll write "$00" to all the other registers (We want sector 0,
remember?) except the Device/Head register.
To the Device/Head register, we'll write "$40", or "0b01000000",
to set the Enable LBA (Bit6) flag. We still want the master
device, so we'll put a "0" in the DEV (Bit4) flag.
Sending the Read Sectors ($20)
command is done in much the same way as when sending the Identify
Device command, only we have set some more parameters before we
execute the command. The whole process goes like this:
-Wait for the HDD to become ready
-Write "$01" to the Sector Count register
-Write "$00" to the Sector Number register
-Write "$00" to the Cylinder Low register
-Write "$00" to the Cylinder High register
-Write "$40" to the Device/Head register
-Write "$20" to the Command register
-Wait until the HDD is ready.
-Read from the Data register and store it in a "safe" place
-Repeat the two last steps 255 more times.
It is also possible to read more then
just one sector at a time.
By putting a number other then "$01" into the sector count register is
going to prepare more then just 512 bytes for reading.
In those cases, the DRQ flag, we haven't paid too much attention,
is going to remain "1" even after we have finished reading the first 512
bytes from the data buffer.
To get the next 512 bytes, we can simply read from the Data
register 256 more times. Over and over again, until we have got all the
data we requested. Checking the DRQ flag after each 512 byte data
block have been transferred might be a good idea.
If you have followed this guide from
the start, these steps for reading a single sector should be quite easy by now.
There's one MAJOR difference from
reading the "Identify Device" and reading data stored on the HDD, and it
has to do whit big Indians and small Indians. No.. Endians. Big Endian
and Small Endian.
The byte order are reversed when reading data generated by the Microsoft
file system.
If the HDD you are using, used to have a FAT file system on it, reading
sector 0 would show some Italian looking ASCII if you mix up the byte
order: "daptrtioi
natlb<0>erEor roldani gporetani gystsmeM<0>siisgno eparitgns".
If you get it right, you should be able to read: "Invalid
partition table<0>Error loading operating system<0>Missing operating
system".
Also for this section, I have included
two code samples. One for sending the data to the UART and one for
saving it in EEPROM. |
|
Writing data
And messing up the hard
drive |
Writing data to the hard drive involves much
the same procedures as reading from it.
There are a few important differences though. The command we are going
to use is the Write Sectors ($30) command, and instead of reading
from the Data register, we'll write to it.
Another thing to note is that the data sent to the
HDD is not saved until the entire data buffer is filled.
The Write Sectors command also
requires the five parameters we used for the Read Sectors
command.
Setting up the LBA addressing is done exactly the same way. The
difference is in writing the data to the HDD.
For this, it is preferable to set up a data buffer in the µC before we
start writing to the HDD.
This is not required, but it could lead to some unpredictable results if
the write sequence was to be interrupted before it was finished.
In the code sample for the Write
Sectors command we are not going to use any buffers. We are simply
going to write the data "On the fly".
We'll write to LBA address $000FFFF just to get a feel for the LBA
addressing concept.
Later, we can modify the code for the Read Sectors command to
read from LBA $000FFFF to verify that it worked.
For the UART sample, the µC waits for any data on the UART, and writes
it to the HDD buffer each time it get another byte, until the write
buffer is filled. When the DRQ flag is cleared, the program will
stop.
The EEPROM sample is a bit simpler. It waits for the device to become
ready. Then it writes the content of the EEPROM onto the drive and
stops.
A typical Write Sectors
operation would follow this pattern:
-Wait for BSY flag to be cleared
-Write the number of sectors to be written into the Sector Count
register
-Write LBA address Bit00:Bit07 to the Sector Number register
-Write LBA address Bit08:Bit15 to the Cylinder Low register
-Write LBA address Bit16:Bit23 to the Cylinder High register
-Write LBA address Bit27:Bit24 to the low nibble of Device/Head
register, "1" to the LBA enable flag and "0" to the DEV
flag.
-Send the Write Sectors command to the Command register
-Wait for BSY flag to be cleared
-Write 16 bits of data to the Data register
-Repeat the last two items 255 more times
-Check the DRQ flag to see if the HDD is expecting more data
-If DRQ is still "1", then write 512 more bytes to the Data
register. |
|
| |
|
|