In article <36i1u3$gp8@senator-bedfellow.MIT.EDU> biomorph@athena.mit.edu
(Bayard Wenzel) writes:
>Ok. So i was wondering if someone could tell me about the interface
>with the drives on the apple2. like, which i/o addresses do what, and
>what exactly they signify. a pointer to some kind of on-line documentation
>would be most ideal, but even the sparcest summary of the card interface
>would be nice.
>
>I could also do with some understanding of the plug to external drives,
>even just a list of what each pin corresponds to (power, gnd, clock, ack,
>req, blah, blah, blah).
The pinouts of the current Apple II disk connectors are listed in Apple
II Miscellaneous Technical Note #6: IWM Port Description, available on
ftp.apple.com.
I have a file that I wrote up a while ago about the programming interface
to the 5.25-inch disk drive hardware. I'll attach it below. If you're
also interested in the 3.5-inch drive on the IIGS, then ftp to
cco.caltech.edu, cd to /pub/apple2/info, and get the file "iwmstuff".
Other things worth reading are _Beneath_Apple_DOS_ and _Beneath_Apple_
_ProDOS_ by Don Worth and Pieter Lechner.
-----(beginning of attached file)-----
The 5.25" drive is controlled by the interface card's I/O locations, found
at memory locations $C0n0 through $C0nF, where "n" is a hexadecimal digit
from 9 to F, depending on what slot the card is in (9 is slot 1, A is slot
2, and so on). These sixteen I/O locations are paired to form eight
"switches"--referencing location X turns the switch off, and referencing
location X+1 turns it on.
Addr. Name Purpose
----- ---- -------
$C0n0 PHASE0 Stepper motor phase 0
$C0n2 PHASE1 Stepper motor phase 1
$C0n4 PHASE2 Stepper motor phase 2
$C0n6 PHASE3 Stepper motor phase 3
$C0n8 ENABLE Turn disk drive off or on
$C0nA SELECT Select drive 1 or 2
$C0nC Q6 (see below)
$C0nE Q7 (see below)
To turn the disk drive on, reference location $C0n9. To turn it off,
reference location $C0n8. For example,
DRIVEON LDA $C089,X
RTS
DRIVEOFF LDA $C088,X
RTS
(In this example and the examples that follow, the X-register is assumed to
contain the desired slot number multiplied by $10--e.g. for slot 6, store
a $60 in the X-register.)
After turning the drive on, your program should delay for a while before
doing any reading or writing, in order to give the disk drive motor time to
come up to full speed. A delay of about one second is sufficient (DOS 3.3
is able to reduce the delay by watching the read data until it starts to
change).
To select drive one, reference $C0nA. To select drive two, reference
$C0nB. For example,
DRIVE1 LDA $C08A,X
RTS
DRIVE2 LDA $C08B,X
RTS
The Q6 and Q7 switches are used together to control several operations:
reading, writing, and testing the write-protect notch.
To read data from the disk drive, first reference location $C0nE--this
activates "read" mode. Then wait until the high bit at $C0nC turns on, and
read the data byte from $C0nC.
LDA $C08E,X
...
READLP LDA $C08C,X
BPL READLP
STA DATA
To sense the write-protect switch, reference location $C0nD, and read the
high bit of $C0nE. If the high bit is set, then the disk is
write-protected.
LDA $C08D,X
LDA $C08E,X
BMI WRPROT
Of all the operations the disk drive can perform, writing is probably the
most difficult. The hardware writes one bit every four cycles, which means
your program has to feed bytes to the controller at precise 32-cycle
intervals. The controller will not tell you when it's time to write the
next byte--your code has to figure this out all by itself, by carefully
counting out the execution times of the instructions in the write loop.
To prepare the controller for writing, first test the write-protect switch.
Then turn on "write" mode by referencing $C0nF. The byte written to $C0nF
or $C0nD will be stored within the controller, and your program should then
immediately reference $C0nC, which causes the controller to start
transmitting the bits to the disk. When your program is finished writing,
it should immediately reference $C0nE to turn off "write" mode.
LDA $C08D,X
LDA $C08E,X
BMI ERROR
LDA DATA1 (absolute load = 4 cycles)
STA $C08F,X (5 cycles)
ORA $C08C,X (4)
JSR PAUSE (6)
LDA DATA2 (4)
STA $C08D,X (5)
ORA $C08C,X (4)
JSR PAUSE (6)
...
LDA DATALAST (4)
STA $C08D,X (5)
ORA $C08C,X (4)
JSR PAUSE (6)
NOP (2)
NOP (2)
ORA $C08E,X (4)
RTS
PAUSE PHA (3)
PLA (4)
RTS (6)
Important things to notice in the above code:
* The first byte is written by storing at $C0nF, and subsequent bytes
are written by storing at $C0nD.
* There are exactly 32 cycles between references to $C0nD, and exactly
32 cycles between references to $C0nC.
* After writing the final byte, there is a delay before turning off
write mode, to give the bits time to get written.
* Interrupts must be disabled when writing. If an interrupt were to
occur, the timing would become hopelessly lost. (Interrupts should
also be disabled when reading, to avoid missing data.)
In practice, the above routine is horribly impractical for writing actual
data--more likely you would use a loop to write a sector of data at a time.
When writing the loop, be sure to remember the timing nuances of branches
taken or not taken, as well as indexing or branching across a page
boundary. (If you're on a IIGS, you don't need to worry about system
speed--the system automatically switches to "slow" speed when the disk
drive is turned on.)
If writing is the hardest action to perform, then moving the arm from one
track to another must be the hardest to explain.
The arm is attached to a stepper motor with four positions. This motor may
be though of like the hand of a clock, which can point to 12 o'clock
("phase 0"), 3 o'clock ("phase 1"), 6 o'clock ("phase 2") or 9 o'clock
("phase 3"). When the "hand" moves clockwise, the read/write head moves
inward, toward higher-numbered tracks, and when the hand moves
counter-clockwise, the head moves outward toward lower-numbered tracks. A
complete revolution of the "hand" causes the head to pass over four tracks.
At each of the four "clock positions," or "phases," there is an
electromagnet, and the "hand" may be attracted toward a position by turning
on the corresponding magnet and waiting. The "hand" may be made to revolve
around the "clock" by doing this several times in succession--for example
to move clockwise from 12 o'clock back to 12 o'clock,
Turn on the 3 o'clock magnet ("phase 1")
Wait
Turn off the 3 o'clock magnet
Turn on the 6 o'clock magnet ("phase 2")
Wait
Turn off the 6 o'clock magnet
Turn on the 9 o'clock magnet ("phase 3")
Wait
Turn off the 9 o'clock magnet
Turn on the 12 o'clock magnet ("phase 0")
Wait
Turn off the 12 o'clock magnet
After this is done, the head will be positioned four tracks farther inward
than it was previously.
Each position of the "clock hand" corresponds to a track on the disk.
Track 0 is "underneath" the 12 o'clock position. One would probably expect,
then, to find track 1 under the 3 o'clock position, track 2 under 6 o'clock
and so forth.
Unfortunately, this isn't quite the case. The read/write head on an Apple
drive is wider than the tracks, so if data were recorded on track 1, some
of it would "slop over" and interfere with the data on track 0 and track
2. Therefore, only the even-numbered tracks are actually used on a normal
disk. The operating system multiplies all track numbers by two before
moving the head--the operating system's track 1 is on physical track 2
(under the 6 o'clock position), the operating system's track 2 is on
physical track 4 (under the 12 o'clock position), etc. The 3 o'clock and 9
o'clock positions are unused on normal disks (but they are often used on
copy-protected disks, and are usually called "half tracks").
The disk hardware provides no means for determining what track the head is
currently positioned over, or what clock position the stepper motor is
pointing to. Therefore, the disk I/O routines must keep their own records
of the current head position. If the I/O routines don't know what the
current track is, the usual method of coping with the situation is to
assume that we're currently on some high-numbered track beyond the
innermost position, and move the head from there to track 0. This is what
causes the horrible grinding sound when a 5.25" disk boots.
Here are a couple of routines to move the head to any desired track:
; TRKMOVE -- move the head to any track on a 5.25" disk.
; Inputs: A-reg = desired track
; X-reg = slot * 16
; Assumes the disk drive has already been turned on
;
PHASE0 EQU $C080 ;Stepper motor phase 0
;
WAIT EQU $FCA8 ;Monitor routine to waste time
;
TRKMOVE ASL ;(Some assemblers require ASL A here)
TRKMOVE1 STX SLOT ;Save slot number
SEC
SBC CURPH ;Compare with current position
BEQ DONE ;Quit if already there
BCS IN1 ;Moving inward?
EOR #$FF ;No--take 2's-complement of track difference...
TAY ;...and save in Y reg
INY
BCC OUT1 ;(branch always taken)
IN1 TAY ;Save track diff in Y reg
IN2 INC CURPH ;Move one track inward
BCS STEP ;(branch always taken)
OUT1 DEC CURPH ;Move one track outward
STEP PHP ;Save step direction (in C flag)
LDA CURPH
AND #3 ;Convert track to phase number
ASL ;(Some assemblers require ASL A here)
ORA SLOT
TAX
LDA PHASE0+1,X ;Turn on motor phase
LDA #$56
JSR WAIT ;Waste some time
LDA PHASE0,X ;Turn off motor phase
PLP
DEY ;More steps left?
BEQ DONE ;If not, quit
BCS IN2 ;If going inward, do it again
BCC OUT1 ;If going outward, do it again
DONE RTS
CURPH DS 1 ;Space to save current track number
SLOT DS 1 ;Space to save slot number
;
; RECAL -- routine to recalibrate head to track 0
; Call this if you don't know what track you're on
; Inputs: X-reg = slot number * 16
; Assumes disk drive is already turned on
; (Warning: This routine is noisy!)
;
RECAL LDA #$50 ;Pretend we're on phase 80 (track 40)
STA CURPH
LDA #0 ;Go to track 0
JMP TRKMOVE1 ;Do it
If you want to position the head over one of the unused tracks (the 3
o'clock and 9 o'clock tracks), enter the subroutine at TRKMOVE1 instead of
TRKMOVE. This skips the conversion of the operating system's track number
into the physical track number.
- Neil Parker
--
Neil Parker No cute ASCII art...no cute quote...no cute
nparker@cie-2.uoregon.edu disclaimer...no deposit, no return...
nparker@cie.uoregon.edu (This space intentionally left blank: )