Path: utzoo!utgpu!news-server.csri.toronto.edu!rpi!zaphod.mps.ohio-state.edu!think.com!snorkelwacker.mit.edu!mintaka!ogicse!orstcs!jacobs.CS.ORST.EDU!parkern From: parkern@jacobs.CS.ORST.EDU (Neil Parker) Newsgroups: comp.sys.apple2 Subject: Controlling the 3.5 Drive hardware (IIGS only) (1 of 2) Summary: Neil FINALLY gets off his lazy *ss and gets it written! Keywords: IIGS disk hardware Message-ID: <1991May17.105554.865@lynx.CS.ORST.EDU> Date: 17 May 91 10:55:54 GMT Article-I.D.: lynx.1991May17.105554.865 Sender: @lynx.CS.ORST.EDU Reply-To: parkern@jacobs.CS.ORST.EDU (Neil Parker) Organization: The Universal Society for the Prevention of Reality Lines: 497 Nntp-Posting-Host: jacobs.cs.orst.edu Finally! Many months ago, I inquired about the level of interest in some technical information about various bits of Apple IIGS hardware. There was a very positive response, especially for the information on the 3.5 Drive, so I sat down and started writing. Unfortunately, I never really managed to finish it, and the project soon got shoved onto one of my numerous back burners, where it sat essentially untouched until recently. Anyway, I finally decided I'd wasted enough time, so here it is. Enjoy. Please feel free to contact me if you have any questions, comments, corrections, etc... -------------------------------------------------------------------------- Controlling the 3.5 Drive Hardware on the Apple IIGS Part 1 of 2: Lotsa Really Confusing Information By Neil Parker First, the standard Dire Warnings: The following article is based on information found in several publications (listed at the end of this article), my own disassemblies of the relevant Apple IIGS ROM routines, and on some experimentation. I can make no guarantees as to the accuracy of this information--it should probably be considered as a starting point for your own explorations rather than as an authoritative source. Remember that when you use this information you're dealing directly with the Naked Hardware, and the myriad protective features of the firmware and operating system are not available. Should you be so foolish as to try out this information with a non-expendable disk in the drive, I won't be held responsible for any lost data. (End of Dire Warnings.) A note about machine code: All the sample routines in this article assume the the processor is in emulation mode or 8-bit native mode. All I/O locations mentioned are in bank $E0 or $E1, and also in bank 0 or 1 if I/O shadowing is enabled. Controlling the Apple 3.5 Drive hardware directly requires a knowledge of two separate pieces of hardware--the drive itself, and the IWM interface chip. The IWM chip in the Apple IIGS is configured to reside in internal slot 6. Its I/O locations are the same as the original Disk ][ interface in slot 6: CA0 EQU $C0E0 ;stepper phase 0 / control line 0 CA1 EQU $C0E2 ;stepper phase 1 / control line 1 CA2 EQU $C0E4 ;stepper phase 2 / control line 2 LSTRB EQU $C0E6 ;stepper phase 3 / control strobe ENABLE EQU $C0E8 ;disk drive off/on SELECT EQU $C0EA ;select drive 1/2 Q6 EQU $C0EC Q7 EQU $C0EE Each of these locations represents a two-way switch--accessing location X turns off the switch; accessing location X+1 turns it on. For the 5.25-inch drive, the switches CA0...LSTRB control the stepper motor which positions the read/write head over the desired track. For the 3.5-inch drive, these switches have become general-purpose control lines--more on this later. The ENABLE switch is used to turn the drive off and on. This switch turns on the red "in use" light, holds the disk in the drive, and prepares the drive to receive further commands. Unlike the 5.25-inch drive, it does not start the spindle motor spinning--a special command is needed for that (again, more on this later). The SELECT switch still fully retains its original function--if it is off, drive 1 will be accessed; turning it on selects drive 2. The switches Q6 and Q7 together form a single four-way switch. The function of this switch is somewhat complex, and will be covered in detail later. The following additional memory locations are also important when dealing with the 3.5-inch drive: SLTROMSEL EQU $C02D ;Clear bit 6 to enable internal slot 6 hardware DISKREG EQU $C031 ;Additional disk drive control register CYAREG EQU $C036 ;System speed and motor-on detect bits Bit 6 of SLTROMSEL controls whether the internal hardware and firmware for slot 6 is available, or whether an external card in slot 6 is available. Before any access to the disk drive is possible, the internal hardware for slot 6 must be selected by turning off bit 6. Before modifying this register, its original contents should be saved somewhere so that your routine can restore the original system state when it's through with the drive. The 3.5-inch drive does its I/O twice as fast as the 5.25-inch drive, so it is necessary to set the system speed to fast to when reading or writing data in order to avoid getting out of step with the drive. This isn't as simple as turning on bit 7 of CYAREG--it's possible (certain, in fact, if you have a 5.25-inch drive) that the slot 6 motor-on detect bit will be on, which causes the system speed to go back to slow as soon as the drive is turned on (this is done for compatibility with 8-bit operating systems which don't know about the system speed bit). Thus you must also turn off bit 2 of CYAREG--this disables the motor-on detect. As with SLTROMSEL, the original contents of CYAREG should be saved by your routine and restored when it's through with the drive. DISKREG contains two bits of interest to the 3.5-inch drive. Bit 7 (called the SEL line) is a general-purpose control line which works in conjunction with the CA0...LSTRB switches (note that the Hardware Reference and the Firmware both state that this bit selects between the upper and lower heads of the drive--this is INCORRECT). Bit 6 enables the 3.5-inch drive--if this bit is off, the 5.25-inch drive and the smartport devices are available, and if it is on, the 3.5-inch drive is available. The other bits are reserved and should not be modified. One of the first things a 3.5-inch drive routine should do is turn on bit 6 of DISKREG (otherwise the wrong device will be accessed), and when it's done it should turn this bit back off (to prevent other programs from becoming hopelessly confused). The IWM chip has several internal registers available to programs. Access to these registers is controlled by the Q6 and Q7 switches. Q6 Q7 Register -- -- -------- off off Read data register off on Read handshake register on off Read status register on on Write mode register (if drive is off) or data register (if drive is on) The mode register is a write-only register containing several flag bits which control various features if the IWM. To access it, turn off the drive (by accessing ENABLE), turn on Q6 and Q7, and write to any odd-numbered address in the $C0E0...$C0EF range. Note that the drive may remain active for a second or two after the ENABLE access, and that the write to the mode register will fail unless the drive is fully deactivated. Therefore, it is necessary to write to the mode register repeatedly until the status register (see below) indicates that the desired changes have taken effect. The IIGS ROM uses a routine like the following to accomplish this (enter with the desired mode in the Y-register): SELIWM LDA ENABLE ;turn drive off LDA Q6+1 ;prepare to access mode & status regs BRA SELIWM1 SELIWM2 TYA STA Q7+1 ;try writing to mode reg SELIWM1 TYA EOR Q7 ;check status reg AND #$1F ;(only bits 0-4 matter) BNE SELIWM2 ;if different, try writing again RTS The bits of the mode register are as follows: Bit Function --- -------- 7-5 Reserved 4 Clock speed. 0=7 MHz, 1=8 MHz. Should always be 0. 3 Bit cell time. 0=4 usec/bit (for 5.25), 1=2 usec/bit (for 3.5). 2 Motor-off timer. 0=leave drive on for 1 sec after program turns it off, 1=don't delay. Should be 0 for 5.25 and 1 for 3.5. 1 Handshake protocol. 0=synchronous (software must supply proper timing for writing data), 1=asynchronous (IWM supplies timing). Should be 0 for 5.25 and 1 for 3.5. 0 Latch mode. 0=read-data stays valid for about 7 usec, 1=read-data stays valid for full byte time. Should be 0 for 5.25 and 1 for 3.5. Before doing I/O to the 3.5-inch drive, the mode register should be set to $0F. When your routine is done, it should be sure to set the mode register back to $00. The status register is a read-only register which contains information about the current status of the drive and the IWM. To access it, turn Q7 off and Q6 on, and read from any even-numbered address in the $C0E0...$C0EF range. The bits of the status register are as follows: Bit Function --- -------- 7 Sense input. This is the write-protect indicator for the 5.25-inch drive, and a general status line for the 3.5-inch drive. 6 Reserved. 5 Drive enabled. If this bit is 1, a disk drive is on. 4-0 Same as bit 4-0 of the mode register. The handshake register is a read-only register used when writing to the disk in asynchronous mode (when bit 1 of the mode register is on). It indicates whether the IWM is ready to receive the next data byte. To read the handshake register, turn switches Q6 off and Q7 on, and read from any even-numbered address in the $C0E0...$C0EF range. The bits of the mode register are as follows: Bit Function --- -------- 7 Register ready. 0=IWM is busy, 1=IWM is ready for data. 6 Underrun. 0=write underrun has occurred (the program took too long to write the next byte), 1=no underrun. 5-0 Reserved. The data register is the register that you read to get the actual data from the disk and write to store data on the disk. To read it, turn Q6 and Q7 off and read from any even-numbered address in the $C0E0...$C0EF range. To write it, turn Q6 and Q7 on and write to any odd-numbered address in the $C0E0...$C0EF range. When reading, the high bit of the data register becomes 1 when the data is valid (this is due to the structure of data on the disk--all valid disk bytes have the high bit set). Writing is a bit tricky--see the example below. Once the disk is properly configured, reading data is quite simple--the following code illustrates the technique: LDA Q7 ;insure read mode R1 LDA Q6 ;ready yet? BPL R1 ;if not, try again STA DATA1 ;got a valid byte, so save it R2 LDA Q6 ;repeat ad nauseam... BPL R2 STA DATA2 R3 LDA Q6 BPL R3 STA DATA3 etc... Writing data is somewhat more difficult, but mercifully it is not necessary for the user's program to count out precise 32-cycle intervals as it was with the 5.25-inch drive--turning on asynchronous mode causes the IWM to take care of the details of the counting. The following code illustrates the technique: BIT Q6+1 ;prepare for writing LDA DATA1 ;get first data STA Q7+1 ;in one fell swoop, set write mode and write data LDA DATA2 ;get second data W1 BIT Q6 ;ready yet? BPL W1 ;if not, try again STA Q6+1 ;write second data LDA DATA3 ;do it again... W2 BIT Q6 BPL W2 STA Q6+1 LDA DATA4 ;and again... W3 BIT Q6 BPL W3 STA Q6+1 etc... WLAST BIT Q6 ;wait until last data underruns BVS WLAST BIT Q7 ;be VERY SURE to turn off write mode! RTS Note that in the write routine, the first byte is written differently than the rest--the STA Q7+1 activates write mode and writes the byte all in one step. In actual practice, you would probably want to use a loop to read and store (or load and write) the data. In addition to programming the IWM, it is also necessary to program the drive itself, which is somewhat "smarter" than the 5.25-inch drive (even though it's a "dumb" device). The 3.5-inch drive contains several internal status bits which the user's program can examine, and several internal control switches which the user's program can use to control various functions of the drive. These status and control bits are accessed by the CA0...LSTRB switches mentioned above and by the SEL line (bit 7 of DISKREG). CA0...CA2 and SEL form a 16-way switch which selects the desired control or status function, and the LSTRB switch signals the drive to perform a control function. The IIGS ROM uses the following routine to select a status or control function (enter with desired function in A-reg): SEL35 BIT CA0 ;set switches to known state BIT CA1+1 BIT LSTRB BIT CA2 LSR BCC SEL35A BIT CA2+1 ;if bit 0 on, turn on CA2 SEL35A LSR PHA LDA DISKREG AND #$7F ;if bit 1 off, turn off SEL BCC SEL35B ORA #$80 ;else turn on SEL SEL35B STA DISKREG PLA LSR BCC SEL35C BIT CA0+1 ;if bit 2 on, turn on CA0 SEL35C LSR BCS SEL35D BIT CA1 ;if bit 3 off, turn off CA1 SEL35D RTS To read a status bit, turn Q6 off, Q7 on, and ENABLE on, configure CA0...CA2 and SEL for the desired function, and read the status bit from bit 7 of the IWM status register. The IIGS ROM uses the following code to accomplish this: STAT35 JSR SEL35 ;select desired status bit BIT Q6+1 BIT Q7 ;test status register RTS ;(returns result in processor N-flag) The status bits are as follows: Param for CA2 CA1 CA0 SEL STAT35 Function --- --- --- --- --------- -------- off off off off $00 Step direction. 0=head set to step inward (toward higher-numbered tracks), 1=head set to step outward (toward lower-numbered tracks). off off off on $02 Disk in place. 0=disk in drive, 1=drive is empty. off off on off $04 Disk is stepping. 0=head is stepping between tracks, 1=head is not stepping. off off on on $06 Disk locked. 0=disk is write protected, 1=disk is write-enabled. off on off off $08 Motor on. 0=spindle motor is spinning, 1=motor is off. off on off on $0A Track 0. 0=head is at track 0, 1=head is at some other track. This bit becomes valid beginning 12 msec after the step that places the head at track 0. off on on off $0C *Disk switched? 0=user ejected disk by pressing the eject button, 1=disk not ejected. off on on on $0E Tachometer. 60 pulses per disk revolution on off off off $01 Instantaneous data from lower head. Reading this bit configures the drive to do I/O with the lower head. on off off on $03 Instantaneous data from upper head. Reading this bit configures the drive to do I/O with the upper head. on on off off $09 Number of sides. 0=single-sided drive, 1=double-sided drive. on on off on $0B *Disk ready for reading? 0=ready, 1=not ready. I'm not too sure about this bit--the firmware waits for this bit to go low before trying to read a sector address field. on on on on $0F Drive installed. 0=drive is connected, 1=no drive is connected. * Functions marked with an asterisk are used by the IIGS ROM but not documented in any publication available to me. I'm fairly sure about the function of status bit $0C (used by the firmware to test for disk-switched errors), but I'm rather uncertain about status bit $0B (if my programs neglect to test for it, the drive displays an annoying tendency to start reading while the head is still stepping). Note the the settings of most of these bits are "backwards"--0 means yes and 1 means no. To perform a control function, turn off LSTRB, configure CA0, CA1, and SEL for the desired function, set CA2 to the desired value (all control functions can be turned on or off), and then turn LSTRB on and back off. The IIGS ROM uses the following code to accomplish this: CONT35 JSR SEL35 ;select desired function BIT LSTRB+1 ;strobe on BIT LSTRB ;strobe off RTS The control functions are as follows: Param for CA1 CA0 SEL CA2 CONT35 Function --- --- --- --- --------- -------- off off off off $00 Set step direction inward (toward higher- numbered tracks.) off off off on $01 Set step direction outward (toward lower- numbered tracks. off off on on $03 *Reset disk-switched flag? (The firmware uses this to clear disk-switched errors.) off on off off $04 Step one track in current direction (takes about 12 msec). on off off off $08 Turn spindle motor on. on off off on $09 Turn spindle motor off. on on off on $0D Eject the disk. This takes about 1/2 sec to complete. The drive may not recognize further control commands until this operation is complete. * Again, the asterisk marks a function used by the ROM but not documented in any publication available to me. The following is a greatly simplified description of the steps that a simple program might take to I/O with the 3.5-inch drive. Save SLTROMSEL and CYAREG Switch in internal slot 6 and set fast speed Turn off disk I/O switches (to insure a "safe" state) Select the 3.5-inch drive (turn on bit 6 of DISKREG) Set IWM mode register to $0F Select drive 1 or 2 (access SELECT or SELECT+1) Turn on drive (access ENABLE+1) Turn on spindle motor (LDA #$08; JSR CONT35) IF we don't know what track we're currently on THEN Set step direction=out (LDA #$01; JSR CONT35) WHILE Not at track 0 (LDA #$0A; JSR STAT35; BPL ...) DO Step one track (LDA #$04; JSR CONT35) WHILE still stepping (LDA #$04; JSR STAT35; BPL ...) DO nothing END WHILE END WHILE Set current track=0 END IF IF current track < desired track THEN Set step direction=in Set number of steps= desired track - current track ELSE IF current track > desired track THEN Set step direction=out Set number of steps= current track - desired track ELSE Set number of steps=0 END IF WHILE number of steps > 0 DO Step one track WHILE still stepping DO nothing END WHILE number of steps -= 1 END WHILE Set current track= desired track Select desired side (LDA #$01 or LDA #$03; JSR STAT35) WHILE not ready to read (LDA #$0B; JSR STAT35; BMI ...) DO nothing END WHILE Read or write your data (this is the FUN part!) Turn off spindle motor (LDA #$09; JSR CONT35) Turn off drive (LDA ENABLE) Turn off CA0...LSTRB Set IWM mode register to $00 Deselect 3.5 drive (turn off bit 6 of DISKREG) Restore slot and speed configuration Return to caller You will probably notice that I glossed over the most important part--the "read or write your data" part. The basic method is to use routines like those listed above under the description of the IWM data register. Unfortunately, the data must undergo considerable preparation before writing and after reading. Those of you who are lucky enough to own a copy of _Beneath_Apple_DOS_ will understand the kind of work that is necessary. For those not so lucky, I must plead that a proper discussion would require another article every bit as long as this one. Rather than try to tackle that subject here, I will content myself with providing a sample program (with commented source code) which shows one way the above information can be put together to make a working program. I'll be posting it in a separate article a few minutes after this one. REFERENCES: Apple Computer, Inc., _Apple_IIGS_Firmware_Reference_. This contains a lengthy description of the SmartPort firmware, which includes some clues as to the functioning of the 3.5 Drive hardware and a diagram of the layout of an individual block of data. You will also need Apple IIGS Technical Note 25, which contains some error corrections. Apple Computer, Inc., _Apple_IIGS_Hardware_Reference_. This contains a description of the disk interface register (DISKREG, $C031) and the internal registers of the IWM chip. You will also need Apple IIGS Technical Note 30, which corrects numerous errors in the IWM descriptions. Apple Computer, Inc., _Inside_Macontosh,_Volume_III_. This contains a description of most of the 3.5 Drive status and control bits. Apple Computer, Inc., _Macintosh_Family_Hardware_Reference_. The 3.5 Drive information from Inside Macintosh is also reprinted in this book (in several different locations). Don Worth and Pieter Lechner, _Beneath_Apple_DOS_, Quality Software, Reseda, CA, 1981. This is THE classic reference for anything and everything having to do with DOS 3.3 and the 5.25 Drive hardware. Although the 3.5 Drive is a much more complex and powerful device, and uses a slightly different data format, much of the low-level information in this book is still quite relevant. Don Worth and Pieter Lechner, _Beneath_Apple_ProDOS_, Reston Publishing Company, Reston, VA, 1984. This does for ProDOS what _Beneath_Apple_ _DOS_ did for DOS 3.3. It contains a somewhat abbreviated version of the previous volume's description of low-level formatting, and in addition offers some valuable information on the functioning of the disk interface hardware. - Neil Parker (Opinions are those of the author, and do not necessarily represent the opinions of anybody else anywhere.) -- Neil Parker No cute ASCII art...no cute quote...no cute parker@corona.uoregon.edu disclaimer...no deposit, no return... parkern@jacobs.cs.orst.edu (This space intentionally left blank: )