please dont rip this site

IO Ide 8255-IDE.TXT

From:  Thomas Brandon [tom@psy.unsw.edu.au]
Sent: Friday, August 06, 1999 9:09 AM
To: James Newton
Subject: Re: 00000004

How to connect an IDE disk to a microcontroller using an 8255
=============================================================


Introduction
============

Some time ago I have dropped that I had connected an IDE harddisk
to one of my microcontrollers. This has provoced a response that
I had not forseen. Since that day I have received some one to two
e-mails a day requesting more details about what I had done. At
first I have mailed a more or less cryptic decription of my
interface to some of the requesters. That only resulted in more
e-mail asking for more details. As it seems some people out there
are really interested in how my contraption is made. In this
description I will attempt to satify the information-hunger of
all you out there who's appetite I seem to have awakened.

This interface first came to my mind when I re-read some old, old
computer magazines. In one of them, the German magazine called
C't there was a short description of how and IDE interface is put
together. This is in the November issue of 1990. The article
describes an IDE interface for both the PC-XT(!) and the PC-AT.
The circuit diagrams of the article indcate that the hardware of
an IDE interface is in fact very simple. It is essentially a data
bus extension from the PC-AT bus to an IDE device. For a PC the
hardware comes down to some bus buffers and some decoding. When a
disk is connected as an IDE device the PC-AT still 'sees' the
old-type control registers of the ancient MFM disk controller. In
the article the entire interface is implemented using simple TTL
chips. The main problem in a PC seems to be how to keep the
harddisk interface and the floppy interface from colliding on
some register addresses. If the IDE interface is implemented
based on some controller system this is of course no problem.

From a controller point of view an IDE interface could be
described as a set of I/O ports. The IDE interface has a 8/16
bits I/O bus, two /CS lines, a /WR and /RD line, three address
bits and one interrupt. In this description I assume the most
traditional IDE interface. In later IDE interfaces a series of
nice so-called PIO modes where added. These PIO modes add things
like a ready line, DMA facilities and higher speed data
transfers. As you read on you will understand that I only use the
so-called PIO mode 0. This is the slowest communication modus on
an IDE bus. It is also the easiest one to implement. The data bus
on an IDE interface is used mostly for 8-bits transfers. Only the
real disk data reads and writes use the 16-bits bus in full
width. You COULD even implement an IDE interface with an 8-bits
only data bus. That would mean that you use only half the disk
capacity (the lower bytes of the 16-bits-wide bus) but that
should work.

When scanning the net I did find an implementation of an IDE
interface for 8-bit controllers. This interface was for a (hope I
have this correct..) COCO bus. It was implemented in TTL, just
like the magazine's interface. The main idea was that whenever a
(16-bits) word was read from the IDE bus the upper 8 bits where
stored in a latch. The controller could retrieve them from the
latch later. Writing to the IDE bus was implemented in the same
manner. The IDE bus read/write cycles where in fact simple bus
read and write cycles. At first I was about to copy this design.
When thinking about it I thought that this TTL design was too
complex for what I wanted to do. You need quite some TTL to
implement a 16-bits read/write I/O port in TTL on an 8-bits
controller.


Hardware description
====================

When implementing a 16-bits I/O port all you need is a
bidirectional I/O port and some control bits to generate the /RD,
/WR etc... That is when the 8255 came in view. An 8255 has 3
8-bits I/O ports. It can be switched from output to input and
back under software control. I used 2 of the 8-bits I/O ports for
the data path and use port to generate the IDE control signals.
The 74HC04 came into the design later. Once I had the controller
and the 8255 strapped together with the IDE connector and a disk
I found out that the 8255 has a nasty trait. Whenever you switch
the I/O modus of the chip it resets ALL its memory bits. That
includes ALL output signals too. For the data bus that is not so
much of a problem. The control signals get a real shake when this
happens. In particular: The /RESET line of the interface is
activated. That makes all control of a disk on this interface
impossible (the disk gets a reset at all kinds of odd
moments...). I have solved this by simply inverting all the
control signals from the 8255 to the IDE bus. When the modus of
the 8255 is switched all outputs of the chip go to '0'. That
means that all the (low-active) control signals are made inactive
by the inversion. That is -in fact- the state where I have them
already when I'm about to change the 8255's modus.

At this point I would like to present a nice circuit diagram to
show what the contraption I have made looks like. Unfortunately I
know of no easy way to do that. This beautiful net is a marvel
when it comes down to transporting text, graphics is another
matter. A GIF picture would do the work; I do not have any means
to produce one. Some schematics drawing package could give a good
picture; I have no schematics package and I am not sure what
package would be universal enough to be usable by everyone. So I
am restricted to a more or less cryptic ASCII description of the
hardware. Please, the cryptology is out of need, not out of my
liking. Well here it comes:

1) The IDE bus pin connections themselves:
   ---------------------------------------

The IDE connector itself is a 40-pins two-row connector:

    1                  39    odd-numbered pins
    ....................
    ....................
    2                  40    even-numbered pins

In an IDE bus this connector is used as follows:

pin no:      name:     function:
---------   -------    ---------

1            /RESET    Al low signal level on this pin will reset
                       all connected devices

2,19,22      GND       ground, interconnect them all and tie to
24,26,30               controller's ground signal
40

3,5,7,9,11   D7..D0    low data bus, 3=D7 .. 17=D0. This part of
13,15,17               the bus is used for the command and
                       parameter transfer. It is also used for
                       the low byte in 16-bits data transfers.

4,6,8,10     D8..D15   high data bus, 4=D8 .. 18=D15. This part
12,14,16,18            of the bus is used only for the 16-bits
                       data transfer.

20           -         This pin is usually missing. It is used to
                       prevent mis-connecting the IDE cable.

21 and       /IOREADY  I do not use or connect to this pin. It is
27                     there to slow down a controller when it is
                       going too fast for the bus. I do not have
                       that problem...

23           /WR       Write strobe of the bus.

25           /RD       Read strobe of the bus.

28           ALE       Some relic from the XT time. I do not use
                       it, and I'm not the only one...

31           IRQ       Interrupt output from the IDE devices. At
                       this moment I do not use it. This pin
                       could be connected to a controller to
                       generate interrupts when a command is
                       finished. I have an inverter ready for
                       this signal (I need a /IRQ for my
                       controller, an IRQ is of no use to me..)

32           IO16      Used in an AT interface to enable the
                       upper data bus drivers. I do not use this
                       signal. It is redundant anyway, the ATA-3
                       definition has scrapped it.

34           /PDIAG    Master/slave interface on the IDE bus
                       itself. Leave it alone or suffer
                       master/slave communications problems. Not
                       used (or connected to ANYTHING) by me.

35           A0        Addresses of the IDE bus. With these
33           A1        you can select which register of the IDE
36           A2        devices you want to communicate.

37           /CS0      The two /CS signals of the IDE bus. Used
38           /CS1      in combination with the A0 .. A2 to select
                       the register on the IDE device to
                       communicate with.

39           /ACT      A low level on this pin indicates that the
                       IDE device is busy. I have connected a LED
                       on this pin. The real busy signal for the
                       controller I get from the IDE status
                       register.

Input/Output status of these signals:
-------------------------------------

The signals:
A0, A1, A2, /CS0, /CS1, /WR, /RD and /RESET are always outputs
from the controller to the IDE bus.

The signals:
IRQ and /ACT are always outputs from the IDE bus to the
controller (IRQ can be tri-stated by the IDE device when two
devices are connected to the IDE bus.) /ACT can drive a LED (with
resistor of course).

The signals:
D0, D1, D2, D3, D4, D5, D6, D7, D8, D9, D10, D11, D12, D13, D14,
D15 are bi-directional. They are output from the controller to
the IDE bus when writing, output from the IDE device to the
controller when reading information.

2) The 8255 <-> controller
   -----------------------

The 8255 is connected to the controller by means of its 8-bit
data bus, the A0 and A1 lines and the /CS, /WR and /RD lines. I
can not give that much info about this. If in doubt: consult the
8255 data sheet. This part depends as much on the controller you
use as anything else. I have the 8255 connected to my controller
(HD63B03R1CP, a Hitachi 6803-derivate..) as an I/O port. Perhaps
some of you have seen my previous e-mails asking for the pinout
of the HD63B03R1CP (52-pins PLCC) chip, well I did find it (Some
department of my work had an old Hitachi databook, voila the
pinout was there). My address decoding puts the 8255 on address
0500H to 0503H in the controller's memory map. That may help if
you decide to try to make sense of my software listing. On this
point you are on your own as to the how to connect a 8255 to your
controller.

3) 8255 <-> IDE connector
   ----------------------

I have used the 8255's port A to generate the IDE bus control
signals. Some of these control signals pass through an inverter
before I connected them to the IDE connector itself. All of these
signals are always used as output from the 8255 to the IDE bus.
When a control signal is inverted the 8255 pin is connected to
one of the inputs of the 74HC04 and the (corresponding) output of
the 74HC04 is connected to the IDE bus connector.

The ports B and C are used as the 16-bits data bus. There are no
special things in this, it's just a simple interconnection of the
8255 I/O pins to the D0..D15 pins of the IDE connector.

I have connected this as follows:
---------------------------------

PA.7  --> inverter --> IDE bus /RESET
PA.6  --> inverter --> IDE bus /RD
PA.5  --> inverter --> IDE bus /WR
PA.4  --> inverter --> IDE bus /CS1
PA.3  --> inverter --> IDE bus /CS0
PA.2  -->              IDE bus A2
PA.1  -->              IDE bus A1
PA.0  -->              IDE bus A0

PB.7 <--> IDE bus D7
PB.6 <--> IDE bus D6
PB.5 <--> IDE bus D5
PB.4 <--> IDE bus D4
PB.3 <--> IDE bus D3
PB.2 <--> IDE bus D2
PB.1 <--> IDE bus D1
PB.0 <--> IDE bus D0

PC.7 <--> IDE bus D15
PC.6 <--> IDE bus D14
PC.5 <--> IDE bus D13
PC.4 <--> IDE bus D12
PC.3 <--> IDE bus D11
PC.2 <--> IDE bus D10
PC.1 <--> IDE bus D9
PC.0 <--> IDE bus D8

I have put a 10 KOhm pull-down resistor from the IDE bus IRQ to
ground. The IDE IRQ signal is also connected to the input of the
(one-remaining) inverter. The output of the inverter is connected
to the controller's /IRQ input. As you can see, I do have the
hardware for interrupts here, I do not use it. I tried to use it,
but got unexplained errors (I probably did something wrong, I
have not yet found what)..

                   |\
IDE IRQ o-----+----+ >o--------o CPU's /IRQ
              |    |/
             +++
             | |
             | | 10 KOhm
             +++
              |
              |
             -+- GND
             ///

The IDE /ACT signal is connected to a 330 Ohm resistor, the other
end of the resistor is connected to a LED, the other end of the
LED is connected to the +5 Volts. This gives a nice LED
indication of when I'm using the disk. This is -as far as I know-
the same hardware a PC uses to produce the disk busy LED you may
find on the front of a PC box.

                 330 Ohm
               +-------+     LED
IDE /ACT o-----+       +------|<-----o +5 Volts
               +-------+

So much about the hardware of the IDE interface. I hope that is
all clear now. If I have been less than clear, please ask. If I
have made mistakes, please complain (I said complain, NOT FLAME!!!).


IDE read/write and register description
=======================================

The IDE device appears to the IDE bus as a set of regsiters. The
register selection is done with the /CS0, CS1 and A0, A1, A2
lines. Reading/writing is done with the /RD and /WR signals. I
have used the 8255 port A signals to make read/write cycles on
the IDE bus. What I do is the following:

read cycle:
-----------

1) put the port B and C of the 8255 in input modus.

2) set an address and /CS0 and /CS1 signal on the port A of the
   8255.

3) activate the /RD of the IDE bus by setting the equivalent bit
   in the port A of the 8255.

4) read the data from port B (and C) of the 8255.

5) deactivate the /RD signal on the IDE bus by resetting the
   equivalent bit of port A of the 8255.

write cycle:
-----------

1) put the port B and C of the 8255 in output modus.

2) set an address and /CS0 and /CS1 signal on the port A of the
   8255.

3) set a data word (or byte) on port B (and C) of the 8255.

4) activate the /WR of the IDE bus by setting the equivalent bit
   in the port A of the 8255.

5) deactivate the /WR signal on the IDE bus by resetting the
   equivalent bit of port A of the 8255.

The only difference between 8 bits and 16 bits transfers is the
following:

- In an 8-bit transfer I use only the port B of the 8255 for data
  transfer. When writing I put data only on the lower byte of the
  IDE bus; the upper byte is ignored anyway by the IDE device.
  When reading I read only port B of the 8255; I never even
  bother to look at the upper byte.

- In an 16-bit transfer I read/write to both the upper and the
  lower byte of the IDE bus; thus using both port B and port C.

The IDE device appears as the following registers:
--------------------------------------------------

/CS0=0, /CS1=1, A2=A1=A0=0: data I/O register (16-bits)
This is the data I/O register. This is in fact the only 16-bits
wide register of the entire IDE interface. It is used for all data
block transfers from and to the IDE device.

/CS0=0, /CS1=1, A2..A0=001B: This is the error information
register when read; the write precompensation register when
written. I have never bothered with the write precompensation at
all, I only read this register when an error is indicated in the
IDE status register (see below for the IDE status register).

/CS0=0, /CS1=1, A2..A0=010B: Sector counter register. This
register could be used to make multi-sector transfers. You'd have
to write the number of sectors to transfer in this register. I
use one-sector only transfers; So I'll only write 01H into this
register. I do use this register to pass the parameter the
timeout for idle modus command via this register.

/CS0=0, /CS1=1, A2..A0=011B: Start sector register. This register
holds the start sector of the current track to start reading/
writing to. For each transfer I write the start sector in this
register. For some fancy reason the sector count starts at 1 and
runs up to 256, so writing 00H results in sector number 256. Why
that is done is a mystery to me, all other counting in the IDE
interface starts at 0.....

/CS0=0, /CS1=1, A2..A0=100B: Low byte of the cylinder number.
This register holds low byte of the cylinder number for a disk
transfer.

/CS0=0, /CS1=1, A2..A0=101B: High two bits of the cylinder
number. The traditional IDE interface allows only cylinder
numbers in the range 0..1023. This register gets the two upper
bits of this number. I write the cylinder number's upper two bits
into this register before each transfer.

/CS0=0, /CS1=1, A2..A0=110B: Head and device select register. The
bits 3..0 of this register hold the head number (0..15) for a
transfer. The bit 4 is to be written 0 for access to the IDE
master device, 1 for access to the IDE slave device. The bits
7..5 are fixed at 101B in the traditional interface.

/CS0=0, /CS1=1, A2..A0=111B: command/status register. When
written the IDE device regards the data you write to this
register as a command. When read you get the status of the IDE
device. Reading his register also clears any interrupts from the
IDE device to the controller.

/CS0=1, /CS1=0, A2..A0=110B: 2nd status register/interrupt and
reset register. When read this register gives you the same status
byte as the primary (/CS0=0, /CS1=1, A2..A0=111B) status
register. The only difference is that reading this register does
not clear the interrupt from the IDE device when read. When
written you can enable/disable the interrupts the IDE device
generates; Also you can give a software reset to the IDE device.

/CS0=1, /CS1=0, A2..A0=111B: active status of the IDE device. In
this register (read-only) you can find out if the IDE master or
slave is currently active and find the currently selected head
number. In a PC environment you can also find out if the floppy
is currently in the drive. I have not used this register yet.

Some of these registers have bitwise meanings. I'll elaborate on
that here:

head and device register:
-------------------------
A write register that sets the master/slave selection and the
head number.

bits 3..0: head number [0..15]
bit  4   : master/slave select: 0=master,1=slave
bits 7..5: fixed at 101B. This is in fact the bytes/sector
           coding. In old (MFM) controllers you could specify if
           you wanted 128,256,512 or 1024 bytes/sector. In the
           IDE world only 512 bytes/sector is supported. This bit
           pattern is a relic from the MFM controllers age. The
           bit 6 of this pattern could in fact be used to access
           a disk in LBA modus.

Status register:
----------------
Both the primary and secondary status register use the same bit
coding. The register is a read register.

bit 0    : error bit. If this bit is set then an error has
           occurred while executing the latest command. The error
           status itself is to be found in the error register.
bit 1    : index pulse. Each revolution of the disk this bit is
           pulsed to '1' once. I have never looked at this bit, I
           do not even know if that really happens.
bit 2    : ECC bit. if this bit is set then an ECC correction on
           the data was executed. I ignore this bit.
bit 3    : DRQ bit. If this bit is set then the disk either wants
           data (disk write) or has data for you (disk read).
bit 4    : SKC bit. Indicates that a seek has been executed with
           success. I ignore this bit.
bit 5    : WFT bit. indicates a write error has happened. I do
           not know what to do with this bit here and now. I've
           never seen it go active.
bit 6    : RDY bit. indicates that the disk has finished its
           power-up. Wait for this bit to be active before doing
           anything (execpt reset) with the disk. I once ignored
           this bit and was rewarded with a completely unusable
           disk.
bit 7    : BSY bit. This bit is set when the disk is doing
           something for you. You have to wait for this bit to
           clear before you can start giving orders to the disk.

interrupt and reset register:
-----------------------------
This register has only two bits that do something (that I know
of). It is a write register.

bit 1    : IRQ enable. If this bit is '0' the disk will give and
           IRQ when it has finished executing a command. When it
           is '1' the disk will not generate interrupts.
bit 2    : RESET bit. If you pulse this bit to '1' the disk will
           execute a software reset. The bit is normally '0'. I
           do not use it because I have full software control of
           the hardware /RESET line.

Active status register:
-----------------------
This is a read register. I have -up till now- ignored this
register. I have only one IDE device (a disk) on my contraption.

bit 0    : master active. If this bit is set then the master IDE
           device is active.
bit 1    : slave active. If this bit is set then the slave IDE
           device is active.
bits 5..2: complement of the currently active disk head.
bit 6    : write bit. This bit is set when the device is writing.
bit 7    : in a PC environment this bit indicates if a floppy is
           present in the floppy drive. Here it has no meaning.

error register:
---------------
The error register indicates what went wrong when a command
execution results in an error. The fact that an error has
occurred is indicated in the status register, the explanation is
given in the error register. This is a read register.

bit 0    : AMNF bit. Indicates that an address mark was not
           found. What this means I not sure of. I have never
           seen this happen.
bit 1    : TK0NF bit. When this bit is set the drive was not able
           to find track 0 of the device. I think you will have
           to throw away the disk if this happens.
bit 2    : ABRT bit. This bit is set when you have given an
           indecent command to the disk. Mostly wrong parameters
           (wrong head number etc..) cause the disk to respond
           with error flag in the status bit set and the ABRT bit
           set. I have gotten this error a lot when I tried to
           run the disk with interrupts. Something MUST have been
           wrong with my interface program. I have not (yet)
           found what.
bit 3    : MCR bit. indicated that a media change was requested.
           What that means I do not know. I've ignored this bit
           till now.
bit 4    : IDNF bit. Means that a sector ID was not found. I have
           never seen this happen, I guess it means that you've
           requested a sector that is not there.
bit 5    : MC bit. Indicates that the media has been changed. I
           ignore this bit.
bit 6    : UNC bit. Indicates that an uncorrectable data error
           happened. Some read or write errors could provoce
           this. I have never seen it happen.
bit 7    : reserved.

Note: I have these registers descriptions from two sources:

1) The C't magazine I mentioned before. It's in German, not very
   complete (the error register description is missing) and very
   old. It does have a hardware description of and IDE interface
   however....

2) The document X3T10/2008D: Information Technology-
   AT Attachment-3 Interface (ATA-3), Working draft.
   This latter document gives an exhaustive overview of the IDE
   interface. It states more details of the IDE interface than I
   can ever hope to include in this short description. It does
   however have one disadvantage: it's BIG. I found the document
   on the net (on the Western Digital homepage) in the form of an
   .exe file. When you run this file on a PC you are rewarded
   with a bigger-than-1 Mbytes .DOC file. That is a Word
   document. When you print (Wondows-95 has a Word viewer/printer
   application) it you get a nearly-200-pages paper. If you want
   to get into the IDE interface seriously I recommend you print
   this thing.

I hope the description I have given here will allow those that do
not have a laser printer and a fast internet link available to
get to grips with the IDE bus.

Intermezzo: Disk size limitations on the IDE bus and LBA modus
-----------

In the PC world there has been (and still is) a lot of discussion
about 'limits' in the disk interface.

At first (MSDOS versions till 3.3) the disk interface was not
able to access more than 32MB on one volume. That was a
limitation of the MSDOS file system rather than of the disk
interface. The same DOS version that was unable to access bigger
partitions than 32MB *WAS* able to access 650MB CDROMs. The limit
came from the fact that each disk sector (512 bytes) was
registered in the FAT in a 16-bits word. The total partition size
was limited by the fact that only 65536 sectors could be
addressed. The partition size was thus limited to 65536 x 512
bytes = 32 MBytes.

Later -as the disks became larger- the disk interface itself ran
into its limits. The interface I describe here has room for 16
heads, 256 sectors per track and only 1024 cylinders. With the
standard sector size of 512 bytes that leaves you a maximum disk
size of 16 x 256 x 1024 x 512 = 2048 MBytes. That is a real
limitation of the IDE interface as I decribe it here. It can not
access more than some 2 GBytes of disk space.

This was overcome by introducing the so-called LBA modus. In LBA
modus the sectors are simply numbered from 0 to -big-. The lowest
byte of the LBA sector number is written into the sector number
register, the middle 16 bits of the LBA sector number are written
in the cylinder number registers (low and high, all 16 bits are
used). The highest 4 bits of the LBA sector number are written in
the head and device register. That gives you 28 bits of LBA
sector number. The sector size was again fixed at 512bytes, so in
LBA modus you have access to: 2^28 x 512 = 1.37 E 11 (some 137.4
gigabytes) of disk space. This LBA modus has been made mandatory
for all new disks (in the ATA-3 spec.) That should keep the disk
makers busy for some while to come... If you want to connect a
disk larger than 2 GBytes to this IDE interface you too will have
to use the LBA modus. How to do that: the bit 6 of the head and
device register is set to indicate that LBA modus is used (the
fixed pattern of 101B in the bits 7..5 of the head and device
register is to be changed into 111B). All other manipulation of
the IDE interface is the same for Sector/Head/Cylinder modus and
LBA modus.

All other limits in te MSDOS/Windows-whatever disk interface must
be due to the BIOS implementation or the file system used. I can
find no reason in the IDE definition for 512 MB limits or 8 GB
limits at all.

End of intermezzo
-----------------

IDE registers usage
===================

Ok, now you know what registers the IDE system uses. Next
question: How to use them? I do not pretend I have tried every
possibility with these registers. As I stated before I have
restricted myself to reading/writing data blocks to/from the
disk. What to do for that is in fact fairly simple:

1) Before doing anything with a device you have to wait till it
   indicates that it is ready (RDY bit in the status register)

2) Next you load the parameters of a command into the appropriate
   registers. For read/write commands that comes down to writing
   the cylinder/head/sector numbers into the registers.

3) You issue a read or write command.

4) You wait till the device signals that it is ready for data
   transfer (DRQ in the status register).

5) Feed the device data (for write) or get the data from the
   device (for read). In case of a write you could wait for the
   operation to complete and read the status register to find out
   what has become of your data.

6) Finish!! That's all folks! The IDE interface is a surprisingly
   simple thing to get to work. If only I had an IDE disk and this
   kind of information when I was still programming my
   MSX-computer I'd have had a harddisk connected to it in no
   time.

IDE commands
============

What has been missing in this description till now is the command
set. I do not think I can describe the complete command set here.
The ATA-3 document is a better source for that than I can give
here (All I would be doing is re-entering the ATA-3 document; I
have neither the time nor any liking for that). The most usable
commands I do intend to describe. Mind: When giving a command you
first have to wait for device ready, next put the command
parameters in the registers and only then can you give a command
(by writing a command byte to the command register). The disk
will start executing the command right after you've written the
command into the command register.

IDE command:    Description:
------------    ------------

1XH             recalibrate the disk. NB: 1XH means that the lower
                nibble of the command byte is a don't care. All
                commands 10H..1FH will result in a recalibrate
                disk command being executed. This command has no
                parameters. You simply write the command code to
                the command register and wait for ready status to
                become active again.

20H             Read sector with retry. NB: 21H = read sector
                without retry. For this command you have to load
                the complete circus of cylinder/head/sector
                first. When the command completes (DRQ goes
                active) you can read 256 words (16-bits) from the
                disk's data register.

30H             Write sector (with retry; 31H = without retry).
                Here too you have to load cylinder/head/sector.
                Then wait for DRQ to become active. Feed the disk
                256 words of data in the data register. Next the
                disk starts writing. When BSY goes not active you
                can read the status from the status register.

7XH             Seek. This normally does nothing on modern IDE
                drives. Modern drives do not position the head if
                you do not command a read or write.

ECH             Identify drive. This command prepares a buffer
                (256 words) with information about the drive. If
                you want the details either look closely at the
                interface program I will add at the end of this
                description or get the ATA-3 document. To use it:
                simply give the command, wait for DRQ and read
                the 256 words from the drive. I have found that
                the modern drives I used give nice information
                about number of heads,sectors,cylinders etc...
                One of the disks I tried (a Miniscribe 8051A)
                gave wrong answers in this buffer. The disk is
                actually a 4 heads/28 sectors disk. It should be
                used in a translated modus with 5 heads/17
                sectors. In the ident drive response it reported
                as 4 heads/28 sectors and it will NOT work in
                that modus. Two other disks (a Quantum 127 MB
                disk and a Western Digital 212 MB disk) report
                nicely. If not for the Miniscribe I would use the
                parameters reported to auto-config the interface
                to match the disk configuration.

E0H             Spins down the drive at once. No parameters. My
                Miniscribe 8051A does not respond to this
                command, the other disks do execute this command.

E1H             Spins up the drive again. Same remarks as E0H
                command.

E2H and E3H     Auto-power-down the disk. Write in the sector
                count register the time (5 seconds units) of
                non-activity after which the disk will spin-down.
                Write the command to the command register and the
                disk is set in an auto-power-save modus. The disk
                will automatically spin-up again when you issue
                read/write commands. E2H will spin-down, E3H will
                keep the disk spinning after the command has been
                given. Example: write 10H in the sector count
                register, give command E2H and the disk will
                spin-down after 80 seconds of non-activity. BTW:
                You can use this command easily on a PC disk too.
                The harddisk of the computer I am working on now
                gets this exact command at boot. That saves a lot
                of noise when I'm typeing long stories like this
                one.

F2H and F3H     The same as E2H and E3H, only the unit in the
                sector count register is interpreted as 0.1 sec
                units. I have not tried this command. Ithink it
                will work (the E0H/E1H/E2H/E3H commands work, why
                should this one not work?)


Two-devices considerations
==========================

The IDE bus is intended for two devices. A master and a slave
device. I have not tried anything myself, but the descriptions
indicate that it is in fact very simple to connect two devices to
the IDE bus. All you have to do is:

1) Configure the master/slave jumpers of the devices.

2) Select a device before you start giving commands to the
   devices.

The head and device register has the bit you need to switch from
one device to another. You have to write the bit to either 0 for
master or 1 for slave and start controlling the other device.
Mind: BOTH devices will get their registers WRITTEN. Any data or
register READ will come from the selected device. ONLY the
selected device will execute commands.

Conclusions and ravings
=======================

ravings:
--------

This description should be about what you need to connect an IDE
disk to any controller. The only thing I have left out are my
unsuccessfull experiments with the interrupt. What happened there
is that I enabled the interrupt, made an interrupt handler that
simply read the status register (to get the interrupt to
disappear) and re-scheduled the disk interface task. I was
rewarded with occasional errors. Some of the read requests got an
ABRT error. I do not yet know what I did wrong. I can not have
been far off the mark, because most of the commands where
executed without comment from the disk. I do intend to try the
interrupt modus again later. I theory the interrupt modus should
give me a slightly bigger data rate. I have found data rates in
the order of 32 KBytes per seccond when I was using interrupts.

About interrupts: When you want to use the interrupt mechanism
all you have to do is enable the interrupt modus of the interface
by clearing the interrupt disable bit in the reset and interrupt
register. The disk will generate an interrupts as soon as it
has completed a command. That means that it will generate an
interrupt when it has read a sector from disk (as soon as DRQ
gets active) or when it has finished writing a sector to disk. I
am not sure about the other commands, but the description says
that the disk will generate an interrupt upon the completion of
each command.

About the data rate. This IDE interface will never win any award
for its speed. It is in one word slow. I get data transfer rates
in the order of 25 KBytes per seccond. I spend most of the time
reading/writing data words from/to the disk on a word-by-word
basis under software control. On the other hand, my controller
has a total (RAM) memory of 24 KBytes. With the current interface
I can dump the complete memory to disk in less than one seccond.
That in itself makes me think that for this application the low
speed is not so much of an issue.

About the future. I think I will give interrupt modus another
try. Not because it gives me a fantastic data rate, I just can't
stand it that it does not want to work good.

I am thinking about a proper file system to put on the disk. That
will enable me to access the disk in a more normal way that the
current 'sea of sectors' approach I use now. A FAT filesystem
looks like an absolute horror when combined with a controller, so
I'll have to either find something that looks good to me or strap
something together myself.

I think I will give the ATAPI command set a try. I have just
found and printed the 'SFF committee specification of ATA Packet
Interface for CDROMs' (SFF-8020i) document. That document gives a
description of how to control an ATAPI CDROM via the IDE bus. On
the other hand: I would really not know what to begin with a
CDROM connected to a microcontroller. But what the heck, I have
no really good idea what to do with a microcontroller with a 40MB
harddisk connected..... I have seen occasional questions in this
mailing list about how to control a CDROM via its backside
connector: I think this IDE interface with an ATAPI piggy-back
software could just do the work.

Till now I've been concentrating on only one side of the
interface. The IDE interface is in fact no more than a
ready-decoded, buffered interface of a (mostly-PC) hardware
system. I could in fact connect god-knows-what at the other end.
I have an interrupt, DMA and I/O available, what else could you
ask for? A disk or CDROM is only one of the less inspired devices
you can connect to an IDE interface. Given the fact that modern
PC-motherboards have two IDE interfaces aboard I can think of
some nice things I'd like to connect to them. How about some 8
parallel input/output ports, how about a data aquisition system?

Lately I have been able to get a C-mos version of the 8255. My
controller system is quite low-power (10 mA for the entire
thing). I did not feel good about the IDE extension with a 8255
that used some 80 mA all by itself. The controller system with
the D71055C (a C-mos NEC version of the 8255) has gone down to
its normal -about 10 mA- power usage. the change from N-mos 8255
to C-mos D71055C has had no other noticable effects than a lower
power usage.

Even when I have no disk connected to this contraption it
delivers me three 8-bits I/O ports. I am thinking about what
other things I could connect to that.

conclusions:
------------

As you can see it is far from difficult to connect an IDE disk to
some computer. My implementation with a 8255 and an inverter chip
may not be the fastest thing on earth, it works, it's simple and
may be usable for many applications where a controller with a
large backup store could save the day.

I'll wrap this up with a case-study: The software I use on my
63B03 controller to control the IDE bus. I hope it can clarify
any points I have not covered in this description. I know this
software to work, I hope it can give someone inspiration to do
something similar on some controller system somewhere.


Appendix:
=========

The software I use to control a single IDE harddisk using a 63B03
controller system and a 8255/74HC04 IDE interface.

Notes:
------

The interface program has been made on a 63B03 controller in
assembly code. The 63B03 code is nearly 6800 code. If in doubt,
consult some source of 6800 or 6803 or 63B03 instructions.

The source of the interface program is of course the most
complete description of how to program this interface, but
certainly not the most un-cryptic description. I try to program
without any 'dirty tricks' but who knows....

This interface program makes sporadic use of the underlying 63B03
real-time scheduler. If you see a 'jsr Sys' somewhere that is
where I called the scheduler to do something for me. As far as I
can see I only use Sys to produce a 10-ms delay. I have left the
code of the scheduler itself out. Not beacuse it is anything but
public domain, but beacuse it is another 150 KBytes of source
file. This description is long enough as it stands here. The
scheduler is not really needed, so I left it out. If you are
interested in the scheduler as such, drop me a line (the address
is a little up in the text).

The IDE interface software as such I hereby donate to the public
domain. Use it as pleases you. Change it when you think it is
wrong, suffer the consequences if you make mistakes. I do not
take responsibility if you blow up harddisks, interface hardware
or if anything goes wrong when you use it. I have found it to
work on my system and, so sorry, that is all the guarantee I can
give you about it.

>>>>>>>>>>>>>>>>>>>>>>>> 63B03 assembly listing <<<<<<<<<<<<<<<<<<<

0000
|Begin----------------------------------------------------------
0000              |
0000              | Disk interface task for the 63B03 system
0000              |
0000              | Update history:
0000              | ---------------
0000              | 02-02-1998 :  Started with the thing. The hardware has
been
0000              |               finished today. All seems to work. Now I
need
0000              |               software to get some life into it.
0000              |
0000              | 05-02-1998 :  Hit a bloody trick of the 8255: When you
change
0000              |               the modus byte ALL outputs are set to 0. I
now
0000              |               know why nobody wants to use this bugger.
The
0000              |               solution for this shit is easy: I plan to
put an
0000              |               inverter in all the control lines. The
following
0000              |               lines will be inverted:
0000              |               /CS0, /CS1, /IORD, /IOW, /RES, IRQ (the
latter
0000              |               for the 63B03, it has a /IRQ input)..
0000              |               This is the software change needed to make
things
0000              |               work again...
0000              |
0000              | 06-02-1998 :  Hardware change done, disk reset works
0000              |               Started testing the disk interface
functions.
0000              |               The recalibrate command works too, the
stop
0000              |               disk/start disk functions seem to be not
0000              |               present in this 40 MB disk. I also made a
LBA
0000              |               routine, I can now access the disk as a
set of
0000              |               sequential blocks, without counting heads
etc..
0000              |               It seems the PC hardware starts counting
sectors
0000              |               starting at 1 (WHY???), all other numbers
start
0000              |               at 0...
0000              |
0000              |               My disk (about 40 MB) tells me it has
0000              |               980 cyls, 5 heads, 17 secs/track. In fact
it does
0000              |               tell me different numbers, only the label
0000              |               indicates that it has been translated...
0000              |
0000              |               At this moment I only support disk block
read
0000              |               and disk block write. I want to make some
file-
0000              |               system too. Besides, this thing works
code-bound.
0000              |               I want to start using interrupts too...
0000              |
0000              | 08-02-1998 :  /IRQ hardware made ok. The disk can now
generate
0000              |               interrupts. These will come into the
system via
0000              |               the CPU's /IRQ signal. I even crashed the
system
0000              |               by giving /IRQ's with no handler in
place...
0000              |               Start making the disk software
/IRQ-driven.
0000              |
0000              | 10-02-1998 :  Changed the interrupt generation/detection
0000              |               software Found one nasty bug in the
interrupt
0000              |               usage as I did it: I first gave a disk
reset on
0000              |               the IDE bus, then started giving commends
right
0000              |               away. On this ONE occasion I do NOT have
the
0000              |               disk's interrupt facility available, the
disk is
0000              |               still executing its internal reset. I have
to
0000              |               wait for ready there, THEN start issuing
0000              |               commands... Also set the bus control
signals to
0000              |               output BEFORE I start using the bus at
all... Now
0000              |               the interrupt mechanism works like the
beauty it
0000              |               is...
0000              |
0000              | 11-02-1998 :  Cleaned up the data transfer code. The
disk I/O
0000              |               was very slow due to very systematic code.
0000              |               ReadWord/WriteWord code substituted in the
0000              |               ReadBlock/WriteBlock code. Routines
ReadWord/
0000              |               WriteWord removed from the code. There was
an
0000              |               error in the writeword routine, I've
removed it.
0000              |
0000              | 15-02-1998 :  I have given up about the interrupt-driven
disk
0000              |               control. It KEEPS on giving unexplained
errors.
0000              |               Does NOT want to work independently of the
disk
0000              |               type and is a general pain in the ass.
What
0000              |               exactly goes wrong I have no idea about. I
now
0000              |               use the scheduler's delay routines to get
ready
0000              |               status from the disk and that works fine.
Both
0000              |               the 40 MB disk and the 127 MB disk run
like the
0000              |               sunshine in this modus. The non-interrupt
modus
0000              |               does in fact not make any significant
differenct
0000              |               for the transfer speed. I dropped from 32
KB/s to
0000              |               25 KB/s. That is very acceptable for this
0000              |               microcontroller. The controller is in this
modus
0000              |               a lot slower than the disk....
0000              |
0000              |               I'm now going to set the thing up for the
127 MB
0000              |               disk with proper track/head numbers. Works
ok
0000              |               too.
0000              |
0000              |               Next test is with the 212 MB disk. I have
to take
0000              |               care that I stay below track 600 for this
disk,
0000              |               tracks 630 .. 660 are bad... Sunshine
again..
0000              |
0000              | 16-02-1998 :  Made Identify working. It now dumps a nice
0000              |               display of what the disk can do. My
antique 42MB
0000              |               disk gives only half an answer (and a
wrong one
0000              |               as well, it does NOT tell about the 5
heads,17
0000              |               sectors translation it does..) but the
other ones
0000              |               like this ident command/display a lot.
Also
0000              |               shifted the buffers in memory a bit. I now
want
0000              |               to start writing to the disk (till now I
have
0000              |               only been reading...).
0000              |               Ok, I really messed up the data on my 42
MB disk,
0000              |               but it DOES write as well as read. I get
back
0000              |               what I have written, so far, so good. Now
I have
0000              |               to make:
0000              |
0000              |               1) A proper disk I/O task. That means that
I have
0000              |                  to implement some way of communicating
with
0000              |                  the disk I/O task. signals? I REALLY
would
0000              |                  like to use some sort of mailbox
mechanism.
0000              |                  For that I will have to extend my
scheduler.
0000              |
0000              |               2) some sort of file-system. I am already
0000              |                  contemplating a MFS (Microcontroller
File
0000              |                  System) for some time now. It's about
time to
0000              |                  start working on one..
0000              |
0000              | 18-02-1998 :  Started making task # 0E into a disk
monitor.
0000              |
0000
|End------------------------------------------------------------
0000
* START INCLUDE *         incl    "debug.inc"     | use all debugger
routines etc..
0000              |------------------------------------------------
0000              | include file for the 63B03 debugger
0000              | file : debug.inc
0000              |------------------------------------------------
0000
0000              |------------------------------------------------
0000              | defines for the 63B03 internal I/O
0000              |------------------------------------------------
0000              |
0000 = 0000       DdrP1   equ     $0000   | DDR Port 1
0000 = 0001       DdrP2   equ     $0001   | DDR Port 2
0000 = 0002       DataP1  equ     $0002   | Data Port1
0000 = 0003       DataP2  equ     $0003   | Data Port 2
0000 = 0008       Tcon    equ     $0008   | Timer Control
0000 = 0009       THigh   equ     $0009   | Timer high byte
0000 = 000A       TLow    equ     $000A   | Timer low  byte
0000 = 0009       Timer   equ     $0009   | Timer for word access
0000 = 000B       TComH   equ     $000B   | Timer output compare high byte
0000 = 000C       TComL   equ     $000C   | Timer output compare low  byte
0000 = 000B       TCom    equ     $000B   | Timer output compare word access
0000 = 000D       TCapH   equ     $000D   | Timer input capture high byte
0000 = 000E       TCapL   equ     $000E   | Timer input capture low  byte
0000 = 000D       TCap    equ     $000D   | Timer input capture word address
0000 = 0010       ModBaud equ     $0010   | Sio baudrate and mode control
register
0000 = 0011       SioCon  equ     $0011   | Sio control register
0000 = 0012       SioRec  equ     $0012   | Sio receive register
0000 = 0013       SioSnd  equ     $0013   | Sio transmit register
0000 = 0014       RamCon  equ     $0014   | Ram Control register
0000              |
0000              |------------------------------------------------
0000              | Some of the Interrupts I do not use here and now.
0000              |------------------------------------------------
0000              |
0000 = 7FF0       ToiInt  equ     $7FF0   | vector them to RAM
0000 = 7FF4       IciInt  equ     $7FF4   |
0000 = 7FF8       IrqInt  equ     $7FF8   |
0000 = 7FFC       NMIInt  equ     $7FFC   |
0000              |
0000              |------------------------------------------------
0000              | The bytes SioByte and SioStat are used as
0000              | follows:
0000              | SioByte is the latest received byte from the Sio
0000              | SioStat indicates the status of the sio with the
0000              | following bits:
0000              |
0000 = 0080       SioEcho         equ     %10000000       | echo modus ON
0000 = 0040       SioRaw          equ     %01000000       | RAW input modus
0000 = 0020       SioInAv         equ     %00100000       | input received
0000 = 0010       SioEsc          equ     %00010000       | Run modus
interrupt enable
0000              |
0000              |-------------------------------------------------
0000              | constans etc.. for the program
0000              |-------------------------------------------------
0000              |
0000 = 000A       lf      equ     $0A     | <LF>
0000 = 000D       cr      equ     $0D     | <CR>
0000 = 0000       eom     equ     $00     | end of message
0000              |
0000              |-------------------------------------------------
0000              | a few constants for single-step control
0000              |-------------------------------------------------
0000              |
0000 = 003F       swic    equ     $3F     | opcode for swi
0000 = 4000       start   equ     $4000   | default start adres voor
single-step
0000 = FFFF       nil     equ     $FFFF   | nil pointer
0000              |
0000              |---------------------
0000              | System entry points
0000              |---------------------
0000              |
0000 = F000       SndData equ $F000       | send a data byte to the sio
0000 = F003       RecSio  equ $F003       | receive a (raw) data byte from
the sio
0000 = F006       RecData equ $F006       | receive a (coocked) data byte
from the sio
0000 = F009       SndStr  equ $F009       | send (X) ASCIZ string to the sio
0000 = F00C       Prompt  equ $F00C       | give a prompt
0000 = F00F       NewLine equ $F00F       | print new line
0000 = F012       WaitRet equ $F012       | wait for a return
0000 = F015       RecHex  equ $F015       | receive hex byte from the sio
0000 = F018       RecHex1 equ $F018       | receive non-space byte from the
sio
0000 = F01B       ConvHex equ $F01B       | convert byte from ASCII -> hex
0000 = F01E       SndHex  equ $F01E       | send byte as two hex ASCII chars
0000 = F021       SndHex1 equ $F021       | no-space send byte asc ASCII
chars
0000 = F024       SndHDig equ $F024       | send one hex digit
0000 = F027       RecAdr  equ $F027       | receive starta and enda from sio
0000 = F02A       RecAdr1 equ $F02A       | receive enda from the sio
0000 = F02D       CI      equ $F02D       | Command Interpreter
0000 = F030       GetName equ $F030       | get name string pointer for
address
0000 = F033       Go      equ $F033       | edit memory
0000 = F039       Edit    equ $F039       | go start at some address
0000 = F03F       HexFile equ $F03F       | download hexfile
0000 = F042       ModReg  equ $F042       | modify single-step registers
0000 = F045       Ver     equ $F045       | print version info
0000 = F048       Help    equ $F048       | give help message
0000 = F04B       NoCmd   equ $F04B       | No Command  found
0000 = F04E       Adr     equ $F04E       | set start Adres
0000 = F051       Break   equ $F051       | set breakpoint address
0000 = F054       Cont    equ $F054       | Continue till address
0000 = F057       JmpSub  equ $F057       | -> JmpSub
0000 = F05A       Step    equ $F05A       | -> single-step
0000 = F05D       GoBrks  equ $F05D       | -> GoBreaks
0000 = F060       Trap    equ $F060       | -> Trap
0000 = F063       Stop    equ $F063       | -> Stop
0000 = F066       RunInt  equ $F066       | -> RunInt
0000 = F069       SndRegs equ $F069       | -> SndRegs
0000 = F06C       NumByt  equ $F06C       | -> NumByt
0000 = F06F       UnAsm   equ $F06F       | -> UnAsm
0000 = F072       DisAsm  equ $F072       | -> DisAsm
0000 = F075       Tmon    equ $F075       | -> TMon
0000 = F078       SYS     equ $F078       | -> SYS
0000 = F07B       SysIrq  equ $F07B       | -> SysIrq
0000              |
0000              |---------------------
0000              | System calls function numbers
0000              |---------------------
0000              |
0000 = 0000       sysSusp         equ     $00     | suspend task
0000 = 0001       sysSleep        equ     $01     | sleep for B clock ticks
0000 = 0002       sysPer          equ     $02     | periodic schedule
0000 = 0003       sysSched        equ     $03     | schedule a task
0000 = 0004       sysPar          equ     $04     | get scheduling par
0000 = 0005       sysSusSig       equ     $05     | suspend for signal
0000 = 0006       sysSndSig       equ     $06     | send a signal
0000 = 0007       sysGetRes       equ     $07     | get/suspend on resources
0000 = 0008       sysGivRes       equ     $08     | release resources
0000 = 0009       sysSioSnd       equ     $09     | send data to sio
0000 = 000A       sysSioRec       equ     $0A     | receive/wait for sio
byte
0000 = 000B       sysIntWait      equ     $0B     | wait for an interrupt
0000 = 000C       sysIntArm       equ     $0C     | clear interrupt status
0000
** END INCLUDE **         end
0000
0000
|Defs-----------------------------------------------------------
0000              |
0000              | The I/O ports of the IDE controller
0000              |
0000 = 0500       IoBase          equ     $0500   | I/O base address for the
disk
0000 = 0500       IoCtl           equ     $0500   | Control byte for IDE
0000 = 0501       IoDatL          equ     $0501   | Low  byte data IDE
0000 = 0502       IoDatH          equ     $0502   | High byte data IDE
0000 = 0503       IoMod           equ     $0503   | I/O modus IDE
0000              |
0000              | The I/O modi I use
0000              |
0000 = 0080       DMout           equ     $80     | all output
0000 = 008B       DMin            equ     $8B     | ctl = output, data input
0000              |
0000              | The bits of the control byte
0000              |
0000 = 0000       DCNOP           equ     %00000000       | Nothing on IDE
ctl bus
0000 = 0080       DCRes           equ     %10000000       | /reset bit
0000 = 0040       DCIor           equ     %01000000       | /IORD  bit
0000 = 0020       DCIow           equ     %00100000       | /IOWR  bit
0000 = 0010       DCCs1           equ     %00010000       | /CS1   bit
0000 = 0008       DCCs0           equ     %00001000       | /CS0   bit
0000 = 0007       DCADR           equ     %00000111       | ADR mask bits
0000              |
0000              | IDE adresses
0000              |
0000 = 0007       IDECmd          equ     $07     | addres for command
0000 = 0007       IDESts          equ     $07     | addres status register
0000 = 0006       IDEHd           equ     $06     | addres head number
0000 = 0005       IDECylH         equ     $05     | addres cylinder number
high
0000 = 0004       IDECylL         equ     $04     | addres cylinder number
low
0000 = 0003       IDEsec          equ     $03     | addres sector number
0000 = 0002       IDEnum          equ     $02     | addres number of sectors
0000 = 0000       IDEData         equ     $00     | addres for data bus
0000 = 000E       IDERIRQ         equ     $0E     | addres Reset/IRQ
register
0000 = 0001       IDEErr          equ     $01     | addres Error register
0000              |
0000              | The head number (0..F) also has the mask for
master/slave
0000              | I fix this at Master, I do not think I will use two
drives
0000              | on my IDE interface.
0000              |
0000 = 000F       IDEHdA          equ     %00001111       | head number and
mask
0000 = 00A0       IDEHdO          equ     %10100000       | head number or
mask
0000              |
0000              | The Reset/IRQ register has two interesting bits
0000              |
0000 = 0100       IDESRes         equ     $00000100       | Soft Reset bit
0000 = 0010       IDENIRQ         equ     $00000010       | 0 = IRQ active
0000              |
0000              | The bits from IDESts
0000              |
0000 = 0080       StsBsy          equ     %10000000       | busy flag
0000 = 0040       StsRdy          equ     %01000000       | ready flag
0000 = 0020       StsWft          equ     %00100000       | Write error
0000 = 0010       StsSKC          equ     %00010000       | seek complete
0000 = 0008       StsDRQ          equ     %00001000       | Data Request
0000 = 0004       StsCorr         equ     %00000100       | ECC executed
0000 = 0002       StsIdx          equ     %00000010       | Index found
0000 = 0001       StsErr          equ     %00000001       | error flag
0000              |
0000              | Command opcodes
0000              |
0000 = 0010       CmdRecal        equ     $10     | recalibrate disk
0000 = 0020       CmdRead         equ     $20     | write a block
0000 = 0030       CmdWrite        equ     $30     | read block
0000 = 00E0       CmdStop         equ     $E0     | Stop disk
0000 = 00E1       CmdStrt         equ     $E1     | Start disk
0000 = 00EC       CmdIdent        equ     $EC     | Identify disk
0000              |
0000              | The default state (I will always leave it in this state)
is:
0000              | IDE bus on input, control word == IDENOP ($FF)
0000              |
0000              | I use a memory command packet to tell the disk what to
do
0000              | This packet has the following makeup:
0000              |
0000 = 0000       SDA     equ     0               | offset
source/destination
0000                                              | address in memory for
the
0000                                              | operation
0000 = 0002       LBA3    equ     2               | offset LBA
0000 = 0003       LBA2    equ     3               | 32-bits number for a
disk
0000 = 0004       LBA1    equ     4               | of max 2.1E12 bytes...
0000 = 0005       LBA0    equ     5               | should be enough!
0000              |
0000              | I use Lineair Block Access (LBA) ALL THE TIME. For this
disk
0000              | (that does not support it by itself) I have a routine
called
0000              | SetLBA to convert the LBA to a CHS configuration.
0000              |
0000              | I use two parameters to decribe the disk geometry:
0000              |
0000              | - The number of blocks per cylinder:
0000              |   in CHS terms this is HxS
0000              |
0000              | - The number of blocks per track
0000              |   in CHS terms this is S
0000              |
0000              | From these two I can convert LBA to CHS completely. They
are
0000              | stored in the following two words:
0000              |
0000              | 42 MB disk:
0000 = 0055       SPC     equ     85              | Sectors per Cylinder
0000 = 0011       SPT     equ     17              | Sectors per track
0000              |
0000              | 127 MB disk:
0000              |SPC     equ     272             | Sectors per Cylinder
0000              |SPT     equ     17              | Sectors per track
0000              |
0000              | 212 MB disk:
0000              |SPC     equ     420             | Sectors per Cylinder
0000              |SPT     equ     35              | Sectors per track
0000              |
0000              | I now use the CPU's /IRQ input to handle the disk's IRQ
0000              | signals. The below parameters are for handling these
IRQ-s
0000              |
0000 = 0001       irqdisk equ     %00000001       | IRQ bit 0 used for the
disk
0000              |
0000
|End------------------------------------------------------------
0000
0000
|Debug----------------------------------------------------------
0000              |
0000              | debug defines for this code
0000              |
0000
|End------------------------------------------------------------
0000
0000                     | make the thing as a task
0000                      org     $4000           | some address
4000 54534B0E             db      "TSK",$0E       | low prio task, a disk is
SLOOOW
4004 4040                 dw      DiskTask        | code address
4006 4FFF                 dw      DiskStk         | stack
4008 4469736B5461         db      "DiskTask"      | module name
4010
4010 = 4FFF       DiskStk equ     $4FFF           | leave some room (a LOT,
in
4010                                              | fact)
4010
4010                      | command block 0
4010 4800         CMDBLK0:dw      $4800           | SDA = $4800
4012 0000                 dw      $0000           | LBA = $00000000
4014 0000                 dw      $0000           |
4016
4016                      | command block 1
4016 4A00         CMDBLK1:dw      $4A00           | SDA = $4A00
4018 0000                 dw      $0000           | LBA = $00000000
401A 0000                 dw      $0000           |
401C
401C                      org     $4040
4040                      | start of the real code
4040              DiskTask:
4040 868B                 ldaa    #DMin           | control bits output, the
rest
4042 B70503               staa    IoMod           | input
4045
4045                      | preset control word
4045 8600                 ldaa    #DCNOP          | control word on not
active
4047 B70500               staa    IoCtl           |
404A
404A                      | make reset pulse on IDE bus
404A 8680                 ldaa    #DCRes          | make a Reset control
word
404C B70500               staa    IoCtl           | set on output
404F
404F C601                 ldab    #1              | spec says 25 us minimum
4051 8601                 ldaa    #sysSleep       | suspend for 10 ms
4053 BDF078               jsr     Sys             |
4056
4056                      | set control word to not active again
4056 8600                 ldaa    #DCNOP          |
4058 B70500               staa    IoCtl           |
405B
405B BDF00C       diskl:  jsr     Prompt          | give prompt
405E BDF006       diskt1: jsr     RecData         |
4061 8120                 cmpa    #' '            | skip spaces
4063 27F9                 beq     diskt1          |
4065 810D                 cmpa    #CR             |
4067 27D7                 beq     disktask        |
4069
4069                      | let CI do the real work again
4069 CE4072               ldx     #diskttab       |
406C BDF02D               jsr     CI              |
406F 7E405B               jmp     diskl           |
4072
4072              diskttab:
4072 3F                   db      '?'             | help
4073 408A                 dw      DHelp           |
4075 49                   db      'I'             | identify
4076 4126                 dw      DIdent          |
4078 52                   db      'R'             | Read sector
4079 4130                 dw      DRead           |
407B 57                   db      'W'             | Write sector
407C 4137                 dw      DWrite          |
407E 53                   db      'S'             | Stop disk
407F 413E                 dw      DStop           |
4081 57                   db      'W'             | Start disk
4082 4142                 dw      DStart          |
4084 4E                   db      'N'             | Reset disk
4085 4146                 dw      DReset          |
4087 00                   db      $00             | what remains
4088 F04B                 dw      NoCmd           |
408A
408A CE4091       DHelp:  ldx     #DHelpTxt       |
408D BDF009               jsr     SndStr          |
4090 39                   rts                     |
4091
4091              DHelpTxt:
4091 0D0A4469736B         db      cr,lf,"Disk task commands:"
40A6 0D0A                 db      cr,lf
40A8 0D0A49203D20         db      cr,lf,"I = Identify disk"
40BB 0D0A52203D20         db      cr,lf,"R = Read disk sector"
40D1 0D0A57203D20         db      cr,lf,"W = Write disk sector"
40E8 0D0A53203D20         db      cr,lf,"S = Stop the disk"
40FB 0D0A55203D20         db      cr,lf,"U = Wake up the disk"
4111 0D0A4E203D20         db      cr,lf,"N = Reset the disk"
4125 00                   db      eom
4126
4126 CE4010       DIdent: ldx     #CMDBLK0        |
4129 BD43A1               jsr     IdentDisk       |
412C BD43C5               jsr     IdentReport     |
412F 39                   rts                     |
4130
4130 CE4010       DRead:  ldx     #CMDBLK0        |
4133 BD432C               jsr     ReadSec         |
4136 39                   rts                     |
4137
4137 CE4010       DWrite: ldx     #CMDBLK0        |
413A BD4361               jsr     WriteSec        |
413D 39                   rts                     |
413E
413E BD4193       DStop:  jsr     StopDisk        |
4141 39                   rts                     |
4142
4142 BD419C       DStart: jsr     StartDisk       |
4145 39                   rts                     |
4146
4146 BD414A       DReset: jsr     InitIDE         |
4149 39                   rts                     |
414A
414A
|---------------------------------------------------------------
414A              | Routine InitIDE
414A              | purp: Init the IDE bus
414A              | in  : nothing
414A              | out : nothing
414A              | uses: nothing
414A
414A                      | set I/O port modus to activate the control
signals
414A 868B         InitIDE:ldaa    #DMin           | control bits output, the
rest
414C B70503               staa    IoMod           | input
414F
414F                      | preset control word
414F 8600                 ldaa    #DCNOP          | control word on not
active
4151 B70500               staa    IoCtl           |
4154
4154                      | make reset pulse on IDE bus
4154 8680                 ldaa    #DCRes          | make a Reset control
word
4156 B70500               staa    IoCtl           | set on output
4159
4159 C601                 ldab    #1              | spec says 25 us minimum
415B 8601                 ldaa    #sysSleep       | suspend for 10 ms
415D BDF078               jsr     Sys             |
4160
4160                      | set control word to not active again
4160 8600                 ldaa    #DCNOP          |
4162 B70500               staa    IoCtl           |
4165
4165                      | select the master device, I use a set head
number for
4165                      | that, The head byte also holds the master/slave
select
4165 BD418B               jsr     SetOut          | bus on output
4168 8606                 ldaa    #IDEHd          | set address
416A BD41DA               jsr     SetAdr          |
416D 8600                 ldaa    #$00            | select head no 0
416F 8AA0                 oraa    #IDEHdO         | or byte
4171 BD41C4               jsr     WriteByte       | write to the bus
4174 BD4183               jsr     SetIn           |
4177
4177                      | wait till reset executed
4177 BD4212               jsr     WaitNBSY        | wait till disk is
ready..
417A                                              | THEN start giving
orders.
417A
417A                      | give the disk a recalibrate command
417A 8610                 ldaa    #CMDRecal       |
417C BD41F1               jsr     WriteCmd        |
417F
417F                      | wait till command executed
417F BD4212               jsr     WaitNBSY        |
4182
4182                      | done
4182 39                   rts                     |
4183
4183
|---------------------------------------------------------------
4183              | Routine SetIn
4183              | purp: set all ports of the 8255 for input from the IDE
bus
4183              | in  : nothing
4183              | out : nothing
4183              | uses: nothing
4183              | NB  : This also resets ALL control lines and adr
selection
4183
4183 36           SetIn:  psha                    | save accu
4184 868B                 ldaa    #DMin           | get control word
4186 B70503               staa    IoMod           |
4189 32                   pula                    |
418A 39                   rts                     |
418B
418B
|---------------------------------------------------------------
418B              | Routine SetOut
418B              | purp: set all ports of the 8255 for output to the IDE
bus
418B              | in  : nothing
418B              | out : nothing
418B              | uses: nothing
418B              | NB  : This also resets ALL control lines and adr
selection
418B
418B 36           SetOut: psha                    | save accu
418C 8680                 ldaa    #DMout          | get control word
418E B70503               staa    IoMod           |
4191 32                   pula                    |
4192 39                   rts                     |
4193
4193
|---------------------------------------------------------------
4193              | Routine StopDisk
4193              | purp: spin down the disk
4193              | in  : nothing
4193              | out : nothing
4193              | uses: nothing
4193
4193              StopDisk:
4193 BD4212               jsr     WaitNBSY        | wait till disk is ready
4196 86E0                 ldaa    #CMDStop        | give the command
4198 BD41F1               jsr     WriteCMD        |
419B 39                   rts                     |
419C
419C
|---------------------------------------------------------------
419C              | Routine StartDisk
419C              | purp: spin up the disk
419C              | in  : nothing
419C              | out : nothing
419C              | uses: nothing
419C
419C              StartDisk:
419C BD4212               jsr     WaitNBSY        | wait till disk is ready
419F 86E1                 ldaa    #CMDStrt        | give the command
41A1 BD41F1               jsr     WriteCMD        |
41A4 39                   rts                     |
41A5
41A5
|---------------------------------------------------------------
41A5              | Routine RecalDisk
41A5              | purp: ReCalibrate the disk
41A5              | in  : nothing
41A5              | out : nothing
41A5              | uses: nothing
41A5
41A5              RecalDisk:
41A5 BD4212               jsr     WaitNBSY        | wait till disk is ready
41A8 8610                 ldaa    #CMDRecal       | give the command
41AA BD41F1               jsr     WriteCMD        |
41AD 39                   rts                     |
41AE
41AE
|---------------------------------------------------------------
41AE              | Routine ReadByte
41AE              | purp: Read one byte from the IDE bus
41AE              | in  : nothing
41AE              | out : byte in A
41AE              | uses: nothing
41AE              | nb  : assumes address and bus have been set
41AE
41AE              ReadByte:
41AE 37                   pshb                    | save b
41AF F60500               ldab    IoCtl           | get current
41B2 CA40                 orab    #DCIor          | set Io read
41B4 F70500               stab    IoCtl           | set
41B7 B60501               ldaa    IoDatL          | get data
41BA F60500               ldab    IoCtl           |
41BD C4BF                 andb    #low(not DCIor) | reset IoRead again
41BF F70500               stab    IoCtl           |
41C2 33                   pulb                    |
41C3 39                   rts                     |
41C4
41C4
|---------------------------------------------------------------
41C4              | Routine WriteByte
41C4              | purp: Read one byte from the IDE bus
41C4              | in  : byte in A
41C4              | out : nothing
41C4              | uses: nothing
41C4
41C4              WriteByte:
41C4 37                   pshb                    | save b
41C5 F60500               ldab    IoCtl           | get current
41C8 CA20                 orab    #DCIow          | assert Io Write
41CA F70500               stab    IoCtl           |
41CD B70501               staa    IoDatL          | get data
41D0 F60500               ldab    IoCtl           | negate Io Write
41D3 C4DF                 andb    #low(not DCIow) |
41D5 F70500               stab    IoCtl           |
41D8 33                   pulb                    |
41D9 39                   rts                     |
41DA
41DA
|---------------------------------------------------------------
41DA              | Routine SetAdr
41DA              | purp: Set an address on the IDE bus
41DA              | in  : address in A [0 .. F]
41DA              | out : nothing
41DA              | uses: nothing
41DA
41DA 37           SetAdr: pshb                    | save B
41DB 36                   psha                    | A too for the moment
41DC
41DC                      | set which CS to assert
41DC 8408                 anda    #%00001000      | see if adres > 8
41DE 2704                 beq     wrad1           |
41E0 8610                 ldaa    #DCCs1          | high address
41E2 2002                 bra     wrad2           |
41E4 8608         wrad1:  ldaa    #DCCs0          | low address
41E6              wrad2:
41E6                      | put low bits in place
41E6 33                   pulb                    | addres -> B
41E7 37                   pshb                    | back on stack
41E8 C407                 andb    #DCAdr          | low addres in B
41EA 1B                   aba                     | get low addres in A
41EB B70500               staa    IoCtl           | set on control output
41EE
41EE                      | get registers back
41EE 32                   pula                    |
41EF 33                   pulb                    |
41F0 39                   rts                     |
41F1
41F1
|---------------------------------------------------------------
41F1              | Routine WriteCmd
41F1              | purp: Write one command to the IDE device
41F1              | in  : command in A
41F1              | out : nothing
41F1              | uses: nothing
41F1
41F1              WriteCmd:
41F1 36                   psha                    | save a
41F2 BD418B               jsr     SetOut          | set bus to output
41F5 8607                 ldaa    #IDECmd         | set address
41F7 BD41DA               jsr     SetAdr          |
41FA 32                   pula                    | get command byte back
41FB BD41C4               jsr     WriteByte       | write the byte
41FE 39                   rts                     | done
41FF
41FF
|---------------------------------------------------------------
41FF              | Routine ReadSts
41FF              | purp: get status if the IDE device
41FF              | in  : nothing
41FF              | out : status byte in A
41FF              | uses: nothing
41FF
41FF              ReadSts:
41FF BD4183               jsr     SetIn           | set bus to input
4202 8607                 ldaa    #IDESts         | set address
4204 BD41DA               jsr     SetAdr          |
4207 BD41AE               jsr     ReadByte        | read the byte
420A 39                   rts                     | done
420B
420B
|---------------------------------------------------------------
420B              | Routine ReadReg
420B              | purp: get byte from the IDE device
420B              | in  : address in A
420B              | out : data byte in A
420B              | uses: nothing
420B
420B              ReadReg:
420B BD41DA               jsr     SetAdr          |
420E BD41AE               jsr     ReadByte        |
4211 39                   rts                     |
4212
4212
|---------------------------------------------------------------
4212              | Routine WaitNBSY
4212              | purp: wait till the drive indicates ready status
4212              | in  : nothing
4212              | out : nothing
4212              | uses: nothing
4212
4212 36           WaitNBSY:psha                    | save registers
4213 37                   pshb                    |
4214
4214                      | test if device ready
4214 BD41FF       wtrdy1: jsr     ReadSts         | get status byte
4217 8480                 anda    #StsBsy         | get status bits
4219 2709                 beq     wtrdy2          |
421B
421B                      | not ready, wait 10 ms
421B 8601                 ldaa    #sysSleep       | sleep
421D C601                 ldab    #1              | one clock cycle
421F BDF078               jsr     Sys             |
4222
4222                      | try again
4222 20F0                 bra     wtrdy1          |
4224
4224                      | device is ready
4224 33           wtrdy2: pulb                    |
4225 32                   pula                    |
4226 39                   rts                     | done
4227
4227
|---------------------------------------------------------------
4227              | Routine WaitDRDY
4227              | purp: wait till the drive indicates ready status
4227              | in  : nothing
4227              | out : nothing
4227              | uses: nothing
4227
4227              WaitDRDY:
4227 36                   psha                    | save registers
4228 37                   pshb                    |
4229
4229                      | test if device ready
4229 BD41FF       wtdrdy1:jsr     ReadSts         | get status byte
422C 8440                 anda    #StsRdy         | get status bits
422E 2609                 bne     wtdrdy2         |
4230
4230                      | not ready, wait 10 ms
4230 8601                 ldaa    #sysSleep       | sleep
4232 C601                 ldab    #1              | one clock cycle
4234 BDF078               jsr     Sys             |
4237
4237                      | try again
4237 20F0                 bra     wtdrdy1          |
4239
4239                      | device is ready
4239 33           wtdrdy2:pulb                    |
423A 32                   pula                    |
423B 39                   rts                     | done
423C
423C
|---------------------------------------------------------------
423C              | Routine WaitDrq
423C              | purp: wait till the drive indicates ready for data
status
423C              | in  : nothing
423C              | out : nothing
423C              | uses: nothing
423C
423C 36           WaitDrq:psha                    | save registers
423D 37                   pshb                    |
423E
423E                      | test if device ready for data
423E BD41FF       wtdrq1: jsr     ReadSts         | get status byte
4241 8408                 anda    #StsDrq         | get status bits
4243 2609                 bne     wtdrq2          |
4245
4245                      | not ready, wait 10 ms
4245 8601                 ldaa    #sysSleep       | sleep
4247 C601                 ldab    #1              | one clock cycle
4249 BDF078               jsr     Sys             |
424C
424C                      | try again
424C 20F0                 bra     wtdrq1          |
424E
424E                      | device is ready for data
424E 33           wtdrq2: pulb                    |
424F 32                   pula                    |
4250 39                   rts                     | done
4251
4251
|---------------------------------------------------------------
4251              | Routine ReadBlock
4251              | purp: read one block from the IDE device
4251              | in  : X -> result address (512 bytes)
4251              | out : nothing
4251              | uses: nothing
4251
4251              ReadBlock:
4251                      | save registers
4251 36                   psha                    |
4252 37                   pshb                    |
4253 3C                   pshx                    |
4254
4254                      | setup for data read
4254 BD4183               jsr     SetIn           | set to input
4257 8600                 ldaa    #IDEData        | set address
4259 BD41DA               jsr     SetAdr          |
425C
425C                      | init loop counter
425C 8600                 ldaa    #$00            | 256 word reads
425E
425E F60500       rdblk1: ldab    IoCtl           | get current
4261 CA40                 orab    #DCIor          | assert Io Read
4263 F70500               stab    IoCtl           | set
4266
4266 F60501               ldab    IoDatL          | get low byte
4269 E700                 stab    0,X             | store
426B 08                   inx                     |
426C
426C F60502               ldab    IoDatH          | get high data
426F E700                 stab    0,X             | store
4271 08                   inx                     |
4272
4272 F60500               ldab    IoCtl           |
4275 C4BF                 andb    #low(not DCIor) | negate Io Read
4277 F70500               stab    IoCtl           |
427A
427A 4A                   deca                    |
427B 26E1                 bne     rdblk1          | loop
427D
427D                      | done, get things back
427D 38                   pulx                    |
427E 33                   pulb                    |
427F 32                   pula                    |
4280 39                   rts                     |
4281
4281
|---------------------------------------------------------------
4281              | Routine WriteBlock
4281              | purp: write one block of data to the IDE device
4281              | in  : X -> data address (512 bytes)
4281              | out : nothing
4281              | uses: nothing
4281
4281              WriteBlock:
4281                      | save registers
4281 36                   psha                    |
4282 37                   pshb                    |
4283 3C                   pshx                    |
4284
4284                      | setup for data write
4284 BD418B               jsr     SetOut          | bus to output
4287 8600                 ldaa    #IDEData        | set address
4289 BD41DA               jsr     SetAdr          |
428C
428C                      | init loop counter
428C 8600                 ldaa    #$00            | 256 word write
428E
428E E600         wrblk1: ldab    0,X             | write data byte
4290 F70501               stab    IoDatL          | to I/O ports
4293 08                   inx                     |
4294 E600                 ldab    0,X             |
4296 F70502               stab    IoDatH          |
4299 08                   inx                     |
429A
429A F60500               ldab    IoCtl           | strobe write
429D CA20                 orab    #DCIoW          | assert Io Write
429F F70500               stab    IoCtl           |
42A2 C4DF                 andb    #low(not DCIoW) | negate Io Write
42A4 F70500               stab    IoCtl           |
42A7
42A7 4A                   deca                    | loop
42A8 26E4                 bne     wrblk1          |
42AA
42AA                      | done, get things back
42AA 38                   pulx                    |
42AB 33                   pulb                    |
42AC 32                   pula                    |
42AD 39                   rts                     |
42AE
42AE
|---------------------------------------------------------------
42AE              | Routine SetLBA
42AE              | purp: sets the LBA for the next transfer
42AE              | in  : X -> command packet
42AE              |       X+0 = ..\ not used or changed here
42AE              |       X+1 = ../
42AE              |       X+2 = LBA3 (high)
42AE              |       X+2 = LBA2
42AE              |       X+3 = LBA1
42AE              |       X+4 = LBA0 (low)
42AE              | out : registers of the IDE controller loaded
42AE              | uses: nothing
42AE              |
42AE              | NB: I select ONE sector to be processed
42AE              |
42AE              | I make a stack frame to convert, these are the offsets
in
42AE              | the stack frame (after tsx, relative to X)
42AE = 0000       LBS3    equ     0               | stack frame LBAS
42AE = 0001       LBS2    equ     1               |
42AE = 0002       LBS1    equ     2               |
42AE = 0003       LBS0    equ     3               | cylinder high
42AE
42AE              SetLBA:
42AE                      | prepare for disk output
42AE BD418B               jsr     SetOut          |
42B1
42B1                      | load input data to stack
42B1 3C                   pshx                    | save X
42B2 A605                 ldaa    LBA0,X          |
42B4 36                   psha                    | 3
42B5 A604                 ldaa    LBA1,X          |
42B7 36                   psha                    | 2
42B8 A603                 ldaa    LBA2,X          |
42BA 36                   psha                    | 1
42BB A602                 ldaa    LBA3,X          |
42BD 36                   psha                    | 0
42BE 30                   tsx                     | make X stack frame
pointer
42BF
42BF                      | init for divide by SPC (=sectors/cylinder)
42BF                      | to get the cylinder number. I know the result
42BF                      | will fit in 16 bits, so 16 bits result is
enough.
42BF C610                 ldab    #16             | 16 divide loops
42C1
42C1 37           lbac1:  pshb                    | save counter on stack
42C2
42C2                      |shift 1 place to left, fill with 0 bit
42C2 6803                 asl     LBS0,X          | shift divident,
42C4 6902                 rol     LBS1,X          | fill up with 0
42C6 6901                 rol     LBS2,X          |
42C8 6900                 rol     LBS3,X          |
42CA
42CA                      | test if substract fits
42CA EC00                 ldd     LBS3,X          | get high word
42CC 830055               subd    #SPC            |
42CF 2504                 bcs     lbac2           | does not fit, next loop
again
42D1
42D1                      | fits, set lower cylinder bit, store result
42D1 ED00                 std     LBS3,X          |
42D3 6C03                 inc     LBS0,X          |
42D5
42D5                      | handle loop things
42D5 33           lbac2:  pulb                    |
42D6 5A                   decb                    |
42D7 26E8                 bne     lbac1           |
42D9
42D9                      | write result to disk registers
42D9 8604                 ldaa    #IDECylL        |
42DB BD41DA               jsr     SetAdr          |
42DE A603                 ldaa    LBS0,X          |
42E0 BD41C4               jsr     WriteByte       |
42E3 8605                 ldaa    #IDECylH        |
42E5 BD41DA               jsr     SetAdr          |
42E8 A602                 ldaa    LBS1,X          |
42EA BD41C4               jsr     WriteByte       |
42ED
42ED                      | see about head number
42ED 8600                 ldaa    #0              | simple substract will do
the
42EF A703                 staa    LBS0,X          | work, max H times...
42F1
42F1                      | get remaining blocks count
42F1 EC00                 ldd     LBS3,X          |
42F3
42F3                      | test if one head more
42F3 830011       lbac3:  subd    #SPT            | minus one track's
sectors
42F6 2504                 bcs     lbac4           | see if reult is minus
42F8 6C03                 inc     LBS0,X          | no, one more head
42FA 20F7                 bra     lbac3           |
42FC
42FC                      | gone one too far already
42FC C30011       lbac4:  addd    #SPT            | and sector count
42FF C30001               addd    #$0001          | sector count starts at 1
!!
4302 ED00                 std     LBS3,X          | save for a moment
4304
4304                      | write to disk registers
4304 8606                 ldaa    #IDEHd          | Head is special
4306 BD41DA               jsr     SetAdr          |
4309 A603                 ldaa    LBS0,X          |
430B 840F                 anda    #IDEHdA         | have to set some bits
430D 8AA0                 oraa    #IDEHdO         | to get it working good
430F BD41C4               jsr     WriteByte       |
4312
4312 8603                 ldaa    #IDESec         | sectors is easy again
4314 BD41DA               jsr     SetAdr          |
4317 A601                 ldaa    LBS2,X          | low byte only as sector
#
4319 BD41C4               jsr     WriteByte       |
431C
431C                      | set up for ONE sector transfer
431C 8602                 ldaa    #IDEnum         |
431E BD41DA               jsr     SetAdr          |
4321 8601                 ldaa    #1              |
4323 BD41C4               jsr     WriteByte       |
4326
4326                      | clean up the stack
4326 31                   ins                     | four bytes LBA
4327 31                   ins                     |
4328 31                   ins                     |
4329 31                   ins                     |
432A
432A                      | get X back too
432A 38                   pulx                    |
432B
432B                      | done
432B 39                   rts                     |
432C
432C
|---------------------------------------------------------------
432C              | Routine ReadSec
432C              | purp: reads one sector from the disk
432C              | in  : X -> command packet
432C              |       X+0 = dest addres high
432C              |       X+1 = dest addres low
432C              |       X+2 = LBA3
432C              |       X+3 = LBA2
432C              |       X+4 = LBA1
432C              |       X+5 = LBA0
432C              | out : data read into the destination address
432C              | uses: nothing
432C
432C                      | wait for bsy bit cleared
432C BD4212       ReadSec:jsr     WaitNBSY        | wait till disk is ready
432F
432F                      | select the device 0
432F 8606                 ldaa    #IDEHd          | set address
4331 BD41DA               jsr     SetAdr          |
4334 8600                 ldaa    #$00            | set head no = 0
4336 840F                 anda    #IDEHdA         |
4338 8AA0                 oraa    #IDEHdO         |
433A BD41C4               jsr     WriteByte       |
433D
433D                      | load parameters
433D BD42AE       rdsec0: jsr     SetLBA          | set address
4340
4340 8620                 ldaa    #CmdRead        | give read command
4342 BD41F1               jsr     WriteCmd        |
4345
4345                      | wait for busy gone
4345 BD4212               jsr     WaitNBSY        |
4348
4348                      | test on errors
4348 BD41FF               jsr     ReadSts         | get status byte
434B 16                   tab                     |
434C 8401                 anda    #StsErr         | check error bit
434E 2703                 beq     rdsec1          | no error -> cont
4350
4350                      | error, notify
4350 BD44D6               jsr     DiskErr         | report the error
4353 17           rdsec1: tba                     | test if DRQ
4354 8408                 anda    #StsDRQ         |
4356 2601                 bne     rdsec2          |
4358
4358                      | no data requested, quit
4358 39                   rts                     |
4359
4359                      | transport the data block to destination
4359 3C           rdsec2: pshx                    |
435A EE00                 ldx     SDA,X           | get dest address
435C BD4251               jsr     ReadBlock       | get the data
435F 38                   pulx                    |
4360
4360 39                   rts                     | done
4361
4361
|---------------------------------------------------------------
4361              | Routine WriteSec
4361              | purp: Write one sector to the disk
4361              | in  : X -> command packet
4361              |       X+0 = source addres high
4361              |       X+1 = source addres low
4361              |       X+2 = LBA High
4361              |       X+3 = LBA Middle
4361              |       X+4 = LBA low
4361              | out : data written to the disk
4361              | uses: nothing
4361
4361              WriteSec:
4361 BD4212               jsr     WaitNBSY        | wait till disk is ready
4364
4364                      | select the device 0
4364 8606                 ldaa    #IDEHd          | set address
4366 BD41DA               jsr     SetAdr          |
4369 8600                 ldaa    #$00            | set head no = 0
436B 840F                 anda    #IDEHdA         |
436D 8AA0                 oraa    #IDEHdO         |
436F BD41C4               jsr     WriteByte       |
4372
4372 BD42AE               jsr     SetLBA          | set address
4375
4375 8630                 ldaa    #CmdWrite       | give write command
4377 BD41F1               jsr     WriteCmd        |
437A
437A                      | wait till command ready
437A BD4212               jsr     WaitNBSY        |
437D
437D                      | test on errors
437D BD41FF               jsr     ReadSts         | get status byte
4380 16                   tab                     |
4381 8401                 anda    #StsErr         | check error bit
4383 2703                 beq     wrsec1          | no error -> cont
4385
4385                      | error, notify
4385 BD44D6               jsr     DiskErr         | report the error
4388 17           wrsec1: tba                     | test if DRQ
4389 8408                 anda    #StsDRQ         |
438B 2601                 bne     wrsec2          |
438D
438D                      | no data requested, quit
438D 39                   rts                     |
438E
438E                      | transport the data block to destination
438E 3C           wrsec2: pshx                    |
438F EE00                 ldx     SDA,X           | get dest address
4391 BD4281               jsr     WriteBlock      | get the data
4394 38                   pulx                    |
4395
4395 39                   rts                     | done
4396
4396 BD423C               jsr     WaitDRQ         | wait for data
4399 3C                   pshx                    |
439A EE00                 ldx     SDA,X           | get dest address
439C BD4281               jsr     WriteBlock      | get data
439F 38                   pulx                    |
43A0 39                   rts                     |
43A1
43A1
|---------------------------------------------------------------
43A1              | Routine IdentDisk
43A1              | purp: Get the disk's parameters from the disk itself
43A1              | in  : X -> command packet
43A1              |       X+0 = dest addres high
43A1              |       X+1 = dest addres low
43A1              | out : ident packet from the disk in memory
43A1              | uses: nothing
43A1
43A1              IdentDisk:
43A1 BD4212               jsr     WaitNBSY        | wait till disk ready
43A4
43A4                      | give command
43A4 86EC                 ldaa    #CmdIdent       | send Ident command
43A6 BD41F1               jsr     WriteCmd        |
43A9
43A9                      | wait for busy gone
43A9 BD4212               jsr     WaitNBSY        |
43AC
43AC                      | test on errors
43AC BD41FF               jsr     ReadSts         | get status byte
43AF 16                   tab                     |
43B0 8401                 anda    #StsErr         | check error bit
43B2 2703                 beq     idget1          | no error -> cont
43B4
43B4                      | error, notify
43B4 BD44D6               jsr     DiskErr         | report the error
43B7 17           idget1: tba                     | test if DRQ
43B8 8408                 anda    #StsDRQ         |
43BA 2601                 bne     idget2          |
43BC
43BC                      | no data requested, quit
43BC 39                   rts                     |
43BD
43BD                      | transport the data block to destination
43BD 3C           idget2: pshx                    |
43BE EE00                 ldx     SDA,X           | get dest address
43C0 BD4251               jsr     ReadBlock       | get the data
43C3 38                   pulx                    |
43C4
43C4 39                   rts                     | done
43C5
43C5
|---------------------------------------------------------------
43C5              | Routine IdentReport
43C5              | purp: give extensive disk identification report
43C5              | in  : X -> disk command packet
43C5              | out : disk ident info via the sio
43C5              | uses: registers
43C5
43C5              IdentReport:
43C5                      | data packet pointed to by the disk command
packet
43C5 36                   psha                    | save registers
43C6 37                   pshb                    |
43C7 3C                   pshx                    | save x too
43C8
43C8                      | make X -> ident info NB: this is in LOW-HIGH
(Intel)
43C8                      | word format. I want to be SOMEWHAT compatible
with
43C8                      | a PC. Perhaps I can someday read the file-system
I make
43C8                      | with a PC too...
43C8 EE00                 ldx     SDA,X           | x -> ident data
43CA 3C                   pshx                    |
43CB
43CB                      | go dump info
43CB CE445D               ldx     #IDModel        | model name
43CE BDF009               jsr     SndStr          |
43D1 38                   pulx                    |
43D2 3C                   pshx                    |
43D3 C636                 ldab    #54             | start in SDA block
43D5 3A                   abx                     |
43D6 C614                 ldab    #20             | 20 words
43D8
43D8                      | see if any name there
43D8 6D01                 tst     1,X             |
43DA 2711                 beq     idnm            | zero byte -> no model
there
43DC
43DC                      | go print the model name
43DC A601         idml:   ldaa    1,X             | first byte (LOW-HIGH)
43DE BDF000               jsr     SndData         |
43E1 A600                 ldaa    0,X             |
43E3 BDF000               jsr     SndData         |
43E6 08                   inx                     |
43E7 08                   inx                     |
43E8 5A                   decb                    |
43E9 26F1                 bne     idml            |
43EB 2006                 bra     idme            |
43ED
43ED                      | no name given
43ED CE446D       idnm:   ldx     #IDNoMod        |
43F0 BDF009               jsr     SndStr          |
43F3
43F3                      | name done
43F3 01           idme:   nop                     |
43F4
43F4                      | see about ATA/ATAPI modus
43F4 38                   pulx                    |
43F5 3C                   pshx                    |
43F6 CE447B               ldx     #IdAta          |
43F9 BDF009               jsr     SndStr          |
43FC 38                   pulx                    |
43FD 3C                   pshx                    |
43FE A601                 ldaa    1,X             | get ATA/ATAPI status
4400 8480                 anda    #$80            | highest bit
4402 2705                 beq     riata           |
4404
4404                      | is an ATAPI device
4404 CE4491               ldx     #IdMAtapi       |
4407 2003                 bra     israta          |
4409
4409                      | is an ATA device
4409 CE448B       riata:  ldx     #IdMAta         |
440C
440C                      | print device type
440C BDF009       israta: jsr     SndStr          |
440F
440F                      | see about removable/fixed
440F 38                   pulx                    |
4410 3C                   pshx                    |
4411 A600                 ldaa    0,X             |
4413 8440                 anda    #$40            | seems to be bit 6
4415 2705                 beq     rirem           |
4417
4417                      | is a fixed disk
4417 CE4499               ldx     #IDFix          |
441A 2003                 bra     rifrm           |
441C
441C                      | is a removable disk
441C CE449F       rirem:  ldx     #IDRem          |
441F BDF009       rifrm:  jsr     SndStr          |
4422
4422                      | get number of cylinders
4422 CE44A9               ldx     #IDCyl          |
4425 BDF009               jsr     SndStr          |
4428 38                   pulx                    |
4429 3C                   pshx                    |
442A A603                 ldaa    3,X             |
442C BDF01E               jsr     SndHex          |
442F A602                 ldaa    2,X             |
4431 BDF021               jsr     SndHex1         |
4434
4434                      | get number of heads
4434 CE44B8               ldx     #IDHead         |
4437 BDF009               jsr     SndStr          |
443A 38                   pulx                    |
443B 3C                   pshx                    |
443C A607                 ldaa    7,X             |
443E BDF01E               jsr     SndHex          |
4441 A606                 ldaa    6,X             |
4443 BDF021               jsr     SndHex1         |
4446
4446                      | get number of sectors
4446 CE44C7               ldx     #IDSec          |
4449 BDF009               jsr     SndStr          |
444C 38                   pulx                    |
444D 3C                   pshx                    |
444E A60D                 ldaa    13,X            |
4450 BDF01E               jsr     SndHex          |
4453 A60C                 ldaa    12,X            |
4455 BDF021               jsr     SndHex1         |
4458
4458 38                   pulx                    | X -> data block
4459 38                   pulx                    | X -> command packet
445A 33                   pulb                    | restore registers
445B 32                   pula                    |
445C 39                   rts                     | done
445D
445D 0D0A44657669 IDModel:db      cr,lf,"Device name: ",eom
446D 4E6F74207370 IDNoMod:db            "Not specified",eom
447B
447B 0D0A44657669 IDAta:  db      cr,lf,"Device type: ",eom
448B 4154412C2000 IDMAta: db      "ATA, ",eom
4491 41544150492C IDMAtapi:db     "ATAPI, ",eom
4499 466978656400 IDFix:  db      "Fixed",eom
449F 52656D6F7661 IDRem:  db      "Removable",eom
44A9
44A9 0D0A43796C69 IDCyl:  db      cr,lf,"Cylinders  :",eom
44B8 0D0A48656164 IDHead: db      cr,lf,"Heads      :",eom
44C7 0D0A53656374 IDSec:  db      cr,lf,"Sectors    :",eom
44D6
44D6
|---------------------------------------------------------------
44D6              | Routine DiskErr
44D6              | purp: give extensive error reporting for disk errors
44D6              | in  : X -> disk command packet
44D6              | out : error messages about what went wrong with a disk
access
44D6              | uses: registers
44D6
44D6 36           DiskErr:psha                    | save registers
44D7 37                   pshb                    |
44D8 3C                   pshx                    | save pointer to LBA
44D9
44D9                      | general error message
44D9 CE4542               ldx     #ErrDsk         |
44DC BDF009               jsr     SndStr          |
44DF
44DF                      | status
44DF CE4555               ldx     #ErrSts         | give error string
44E2 BDF009               jsr     SndStr          |
44E5 8607                 ldaa    #IDESts         | first the IDE Staus
register
44E7 BD420B               jsr     ReadReg         |
44EA BDF01E               jsr     SndHex          |
44ED
44ED                      | the error bits
44ED CE4563               ldx     #ErrBit         |
44F0 BDF009               jsr     SndStr          |
44F3 8601                 ldaa    #IDEErr         |
44F5 BD420B               jsr     ReadReg         |
44F8 BDF01E               jsr     SndHex          |
44FB
44FB                      | LBA here it happened
44FB CE4571               ldx     #ErrLBA         |
44FE BDF009               jsr     SndStr          |
4501 38                   pulx                    |
4502 3C                   pshx                    |
4503 EC02                 ldd     LBA3,X          |
4505 BDF01E               jsr     SndHex          |
4508 17                   tba                     |
4509 BDF021               jsr     SndHex1         |
450C EC04                 ldd     LBA1,X          |
450E BDF021               jsr     SndHex1         |
4511 17                   tba                     |
4512 BDF021               jsr     SndHex1         |
4515
4515                      | the CHS where it happened
4515 CE457F               ldx     #ErrCHS         |
4518 BDF009               jsr     SndStr          |
451B 8605                 ldaa    #IDECylH        |
451D BD420B               jsr     ReadReg         |
4520 BDF01E               jsr     SndHex          |
4523 8604                 ldaa    #IDECYLL        |
4525 BD420B               jsr     ReadReg         |
4528 BDF021               jsr     SndHex1         |
452B 8606                 ldaa    #IDEHd          |
452D BD420B               jsr     ReadReg         |
4530 BDF01E               jsr     SndHex          |
4533 8603                 ldaa    #IDEsec         |
4535 BD420B               jsr     ReadReg         |
4538 BDF01E               jsr     SndHex          |
453B
453B                      | end with a new line
453B BDF00F               jsr     NewLine         |
453E
453E                      | get registers back
453E 38                   pulx                    |
453F 33                   pulb                    |
4540 32                   pula                    |
4541
4541                      | done
4541 39                   rts                     |
4542
4542 0D0A3E3E2044 ErrDsk: db      cr,lf,">> Disk Error <<",eom
4555 0D0A3E3E2053 ErrSts: db      cr,lf,">> Status :",eom
4563 0D0A3E3E2045 ErrBit: db      cr,lf,">> Error  :",eom
4571 0D0A3E3E204C ErrLBA: db      cr,lf,">> LBA    :",eom
457F 0D0A3E3E2043 ErrCHS: db      cr,LF,">> CHS    :",eom
458D
458D              |----------------------------------------------------
458D
458D > 0000               end

>>>>>>>>>>>>>>>>>>>> End 63B03 assembly listing <<<<<<<<<<<<<<<<<<


file: /Techref/io/ide/8255-ide.txt, 109KB, , updated: 1999/8/6 10:15, local time: 2024/12/2 03:05,
TOP NEW HELP FIND: 
18.97.9.170:LOG IN

 ©2024 These pages are served without commercial sponsorship. (No popup ads, etc...).Bandwidth abuse increases hosting cost forcing sponsorship or shutdown. This server aggressively defends against automated copying for any reason including offline viewing, duplication, etc... Please respect this requirement and DO NOT RIP THIS SITE. Questions?
Please DO link to this page! Digg it! / MAKE!

<A HREF="http://massmind.org/techref/io/ide/8255-ide.txt"> io ide 8255-ide</A>

Did you find what you needed?

 

Welcome to massmind.org!

 

Welcome to massmind.org!

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  .