IDE (4)

From: Ruud Baltissen (
Date: 1998-12-15 22:06:15

Hallo allemaal,

This document describes my interface and is a concept for my homepage at
the same time. Have fun with it. <xxx> is meant to be replaced by a HREF-
tag in the future. 

I also found out that the GIF I have sent some of you was even older version 
than I thought. The except the invertor I told you of, some of the connections 
from the 573s towards the 138 are wrong. I hope that the following info helps 
you to "make" the corrct connections. 


IDE-interface for your Commodore.

IDE-interface for the C64/128. Also general description how to adept it to
other types.

At this moment you can get small IDE-harddisks for peanuts. So I got
interested in connecting one to my C64 in one or another way. The result is
an interface of just eight common available cheap ICs. 

There is no specific knowledge of the IDE-bus needed to understand how it
works. For those people who are interested in more ins and out you can read
<this> document.
The IDE-bus is based on the the old MFM-, RLL- and ESDI-harddisks. The idea
raised to integrate the controler in the PC with the onboard controler of
the harddisk and to attach the result to the harddisk. The only signals
needed for the resulting board are the same signals after decoding the
complete ISA-bus. These signals together make the IDE-bus.

The signals we need for our interface are:
- 16 databits, D0..D15
- 3 addresslines, A0..A2
- 2 Registerblock-selectlines, CS0 and CS1
- 2 Read/write lines, IOW and IOR

Then there is one line I use but is not needed for the funtioning of the
interface: DASP. The function of this line is to blink a LED when the HD is
executing a command.

There are more lines on the IDE-bus but we don't need them. Most of the
time the reason is that the C64 is too slow to have use for them. For
example: there is an Interupt-line that becomes active everytime the
execution of a command has been finished. This may be very usefull with a
Pentium 300 MHz but I already found out that the handling of interupt costs
much more time then just waiting for the BUSY-flag to clear.

The IDE-HD has two blocks of eight registers each: the "Command Block
Registers" and the "Control Block Registers". The first block is choosen by
negating CS0, the second by negating CS1. Each register itself is choosen
by applying the right value to the addresslines A0..A2. Reading from or
writing to a register is done by negating IOR or IOW. The IDE bus is, as
said before, 16 bits wide but the registers are 8 bits wide.

My first design was based on a design used for the Tandy TRS80-Colour
computer. The first problem that I encountered was that the bytes that the
C64 read were corrupted. This problem was solved by delaying the positive
flank of the IOR-signal and OE-signal of the 74LS373 storing the high-byte.
The second problem I encountered was that I was missing bytes when reading
a complete sector. The nasty thing was that the number of missing bytes
varied as well as the place where they disappeared. After a day of "try and
error" the thought occured to me that it could be that the setup time of
the addresslines towards the interface could be to short. As you all
(should) know, the addresslines of the C64/128 are not valid until after
the rising edge of CLK2. Before this moment they reflect an address the VIC
(= videochip) is using for its own puposes. The complete decoding of an
address is just a matter of nano seconds and some devices need much more
for their so called setup time. So I changed the design by generating the
needed address- and CSx-lines by means of an 74ALS573. Bingo :-)

<The design>

The IDE bus is 16 bits wide. The bus of the C64 is only 8 bits wide. So we
have to store the extra 8 bits before a write-action and during a read-
action in some way. This is where U@ and U3 , both 74ALS573s, come in view.
U3 is used to store the high byte for writing, U2 is used to store read
high byte.

U1, the third 74ALS573, is used to generate the address- and CSx-lines for
the IDE bus. Only four bits are used. D0..D2 are used to generate the
address lines, D4 for both the CSx-lines. The last is realised by
connecting CS0 directly to Q4 and CS1 thru an 74LS14. The first idea was
connecting both CS-lines to their own Q-output. But the outputs of a
74ALS573 are undefined after a power-up and my experience is that they are
(L) most of the time. And the IDE-docs say it is illegal to negate both CS-
lines. I don't know if this illegal situation can damage the hardware but
"to prepare is better then to repair". 

U5, a 74LS245, is used as buffer between the interface and the C64/128.

The decoding C64-bussignals can be split up in two parts. The first part
takes care of decoding the needed signal to enable the 245. The second part
takes care of addressing the 573s and the IOR/IOW-lines of the IDE-bus.
The first part is made out of a 74LS260, dual 5-input NOR-gates and two
invertors, U7B and U7C. The whole construction can be seen as a 7-input OR-
gate decoding the addresslines A2..A7 and IO2. The output is used to enable
the 245 and U6, a 74LS138.

This 138 is the second part of the decoding and takes care of addressing
the 573s and the IOW/IOR-lines. CLK2 is used to enable the G-input of the
138. This is done to minimize the effects of the delaytimes introduced by
all the gates used for the decoding. The 138 decodes the last 2 addreslines
and separates the write- and read-actions. 
-     Writing to $DE00 enables the output of U3, putting the previous
      stored high byte on D8..D15 of the IDE-bus. This action also negates
      IOW telling the HD to read the contents of its databus. The low byte
      is put on the bus by the C64/128 itself thru U5, the 245.
-     Reading $DE00 negates IOR, telling the HD to put its data on the bus.
      The low byte of this data is read by the C64/128 thru U5, the 245.
      The high byte is clocked into U2 thru an invertor, U4C. To give U2
      and the C64/128 some extra time to read the contents of the IDE-
      databus, the IOR signal is delayed by using 2 74F14-invertors and a
-     Writing to $DE01 latches the wanted values for the address- and CS-
      lines of the IDE-bus into U1
-     Writing to $DE02 latches the byte on the databus of the C64/128 into
      U3 (thru an invertor, U4D). 
-     Reading $DE02 puts the contents of U2 on the databus of the C64/128.
      Because I encountered some difficulties reading the contents, I
      delayed this signal as well using U4E, U4F and a RC-network.

Writing to $DE03 does nothing. Reading $DE02 or $DE03 returns an
unpredictable value.

Unexplained items:

The resistors R3..R6 are there because the <IDE-specifications> say to do
so. R2 and D1 enable you to see if the HD is performing a command or a


No SW has been develloped yet which enables you to work with the drive
except some SW to test the interface. For the moment you have to do with
the following "how to do it and why".

Changing the design from "direct addressing" to "latch addressing"
complicated the way to enable a datatransfer. Instead of one read- or
writecommand, we need two extra writecommands. But I can assure you right
now that this has almost no impact on the time needed to read data from or
write data to the HD itself.

The IDE-HD has two sets of registers with each the following registers:

| reg | CS0 | CS1 |A2 |A1 |A0 | Read (-IOR)    | Write (-IOW)  |
|     |  0  |  0  | X | X | X | ILLEGAL        | ILLEGAL       |
|  0  |  0  |  1  | 0 | 0 | 0 | Data Port      | Data Port     | <--+     |
|  1  |  0  |  1  | 0 | 0 | 1 | Error Register | Precomp       |    |     |
|  2  |  0  |  1  | 0 | 1 | 0 | Sector Count   | Sector Count  | Command  |
|  3  |  0  |  1  | 0 | 1 | 1 | Sector Number  | Sector Number | Block    |
|  4  |  0  |  1  | 1 | 0 | 0 | Cylinder Low   | Cylinder Low  | Registers|
|  5  |  0  |  1  | 1 | 0 | 1 | Cylinder High  | Cylinder High |    |     |
|  6  |  0  |  1  | 1 | 1 | 0 | Drive / Head   | Drive / Head  |    |     |
|  7  |  0  |  1  | 1 | 1 | 1 | Status         | Command       | <--+     |
|     |  1  |  1  | X | X | X | High Impedance | Not Used      |          |
|  0  |  1  |  0  | 0 | 0 | 0 | High Impedance | Not Used      | <--+     |
| ... |  1  |  0  |    ...    | High Impedance | Not Used      | Control  |
|  5  |  1  |  0  | 1 | 0 | 1 | High Impedance | Not Used      | Block    |
|  6  |  1  |  0  | 1 | 1 | 0 | Altern Status  | Device Control| Registers|
|  7  |  1  |  0  | 1 | 1 | 1 | Drive Address  | Not Used      | <--+     |

0    r/w     data register

1    r       error register
             diagnostic mode errors:
                 bit 7-3        reserved
                 bit 2-1 = 001  no error detected
                         = 010  formatter device error
                         = 011  sector buffer error
                         = 100  ECC circuitry error
                         = 101  controlling microprocessor error
             operation mode:
                  bit 7  = 1  bad block detected
                         = 0  block OK
                  bit 6  = 1  uncorrectable ECC error
                         = 0  no error
                  bit 5       reserved
                  bit 4  = 1  ID not found
                         = 0  ID found
                  bit 3       reserved
                  bit 2  = 1  command aborted
                         = 0  command completed
                  bit 1  = 1  track 000 not found
                         = 0  track 000 found
                  bit 0  = 1  DAM not found
                         = 0  DAM found (CP-3022 always 0)

1    w       WPC/4  (Write Precompensation Cylinder divided by 4)

2    r/w     sector count

3    r/w     sector number

4    r/w     cylinder low

5    r/w     cylinder high

6    r/w     drive/head
                 bit 7   = 1
                 bit 6   = 0
                 bit 5   = 1
                 bit 4   = 0  drive 0 select
                         = 1  drive 1 select
                 bit 3-0      head select bits

7    r       status register
                 bit 7 = 1  controller is executing a command
                 bit 6 = 1  drive is ready
                 bit 5 = 1  write fault
                 bit 4 = 1  seek complete
                 bit 3 = 1  sector buffer requires servicing
                 bit 2 = 1  disk data read successfully corrected
                 bit 1 = 1  index - set to 1 each disk revolution
                 bit 0 = 1  previous command ended in an error

7    w       command register
                 98 E5   check power mode       (IDE)
                 90      execute drive diagnostics
                 50      format track
                 EC      identify drive         (IDE)
                 97 E3   idle                   (IDE)
                 95 E1   idle immediatete       (IDE)
                 91      initialize drive parameters
                 1x      recalibrate
                 E4      read buffer            (IDE)
                 C8      read DMA with retry    (IDE)
                 C9      read DMA without retry (IDE)
                 C4      read multiplec         (IDE)
                 20      read sectors with retry
                 21      read sectors without retry
                 22      read long with retry
                 23      read long without retry
                 40      read verify sectors with retry
                 41      read verify sectors without retry
                 7x      seek
                 EF      set features           (IDE)
                 C6      set multiple mode      (IDE)
                 99 E6   set sleep mode         (IDE)
                 96 E2   standby                (IDE)
                 94 E0   standby immediate      (IDE)
                 E8      write buffer           (IDE)
                 CA      write DMA with retry   (IDE)
                 CB      write DMA with retry   (IDE)
                 C5      write multiple         (IDE)
                 E9      write same             (IDE)
                 30      write sectors with retry
                 31      write sectors without retry
                 32      write long with retry
                 33      write long without retry
                 3C      write verify           (IDE)
                 9A      vendor unique          (IDE)
                 C0-C3   vendor unique          (IDE)
                 8x      vendor unique          (IDE)
                 F0-F4   EATA standard          (IDE)
                 F5-FF   vendor unique          (IDE)

Writing or reading a register.

As said before, we need three actions to read from or write to a register.
Writing a value in "Cylinder low", register 4 of the Control Block, we
first write the value $0C to $DE01. This sets the addresslines and disables
CS0. Then we write the wanted value to $DE00. For reading the register we
must perform the same first two actions as with writing to the register but
the third step is reading $DE00. If we have to perform several actions with
the same register, the first two steps don't need to be repeated everytime.
To check if the "Cylinder low" register contains the value we just wrote to
it, we only have to read $DE00. 

Reading a sector.

To tell the HD which sector we want to read, we have to give it the
following commands:

.ba $8000                           ; begin addres
.eq DATALO = $DE00
.eq REGIST = $DE01
.eq DATAHI = $DE02

;** number of sectors (1 in case)
            lda   #$0A
            sta   REGIST
            lda   #$02
            sta   REGIST
            lda   #$01
            sta   DATALO

;**  which sector (the first one in this case)
            lda   #$0B
            sta   REGIST
            lda   #$03
            sta   REGIST
            lda   #$01
            sta   DATALO

;**  which track/cylinder (the first one in this case), low byte
            lda   #$0C
            sta   REGIST
            lda   #$04
            sta   REGIST
            lda   #$00
            sta   DATALO

;**  which track/cylinder (the first one in this case), high byte
            lda   #$0D
            sta   REGIST
            lda   #$05
            sta   REGIST
            lda   #$00
            sta   DATALO

;**  which head (the first one in this case)
            lda   #$0E
            sta   REGIST
            lda   #$06
            sta   REGIST
            lda   #$A0
            sta   DATALO

;**  pass the command (read a sector)
            lda   #$0F
            sta   REGIST
            lda   #$07
            sta   REGIST
            lda   #$21
            sta   DATALO

;**  wait until the HD has accepted the command
WAIT        lda   DATALO
            bmi   WAIT

;**  read the sector thru $DE00
            lda   #$08
            sta   REGIST
            lda   #$00
            sta   REGIST

            ldy   #0
LOOP        lda   DATALO      ; read the low byte
            sta  $0400,y      ; put it on screen
            lda  DATAHI       ; read the stored high byte
            sta  $0518,y      ; put it on screen as well
            iny               ; 256 * 2 bytes?
            bne   LOOP        ; no, ->

            rts               ; end of program

I used this program to check visually if I always read the same data by
performing it several times in a row. 

Writing a sector

To know if you read the correct data you must know what you are going to
read and therefor you firts have to write to a sector. In the following
example it will the same sector as above. Therefor I skip a part of the
text to make it more easy for you to read it:

.ba $9000                           ; begin addres
;**  pass the command (write a sector)
            lda   #$0F
            sta   REGIST
            lda   #$07
            sta   REGIST
            lda   #$31
            sta   DATALO

;**  wait until the HD has accepted the command
WAIT        lda   DATALO
            bmi   WAIT

;**  write to the sector thru $DE00
            lda   #$08
            sta   REGIST
            lda   #$00
            sta   REGIST

            ldx   #$FF
            ldy   #$00

LOOP        stx   DATAHI      ; store highbyte
            sty   DATALO      ; write both bytes to HD
            iny               ; next byte?
            bne   LOOP        ; yes, ->

            rts               ; end of program

By swapping the above 'stx' and 'sty', NOT 'DATAHI' and 'DATALO', the
contents of the screen must change as well after performing a read of the

I haven't tried to read multple sectors yet. As far as I understood you can
read up to 256 sectors in a row (by writing the value 0 to register 2)
without the need to reprogram the registers. 256 sectors are equivalent to
128 KB which simply means that with a little bit of luck ie. unfragmented
file, you can fill the RAM of the C64 with one big read cycle :-)

Other Commodores:

There is, as far as I know, one major difference with the other C=s: the lack 
of an equivalent of IO2. So in my opinion the only thing you have to do is to 
supply the equivalent of IO2 to the interface.


End of doc for the moment. Gentlemen, please think it over. Comments, ideas and 
whatever (including critics) are welcome. As there is no other interface 
available, except the one from our Czech friends which is not freeware and 
reproducable, what we decide here could become a standard for the future.

I already posted my ideas about the filesystem and I'm thinking about to
email our Czech friends for info about their system. If theirs is good
enough for OUR (!) needs, then we could use that instead inventing the
wheel again. Still I would be pleased with any comment or idea, how ever
small it is like Andre's great idea of using an little jumper. (I hope this
compensates my remark of "greedy and complaining people" :-) )

Groetjes, Ruud

This message was sent through the cbm-hackers mailing list.
To unsubscribe: echo unsubscribe | mail

Archive generated by hypermail 2.1.1.