1295 lines
104 KiB
Plaintext
1295 lines
104 KiB
Plaintext
*::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*
|
|
* *
|
|
* RWTSDRVR using READ *
|
|
* *
|
|
*----------------------------------------------------------------*
|
|
* *
|
|
* Just as the file manager is used to manipulate entire *
|
|
* files at once, RWTS reads or writes disk data one sector at a *
|
|
* time. The software interface between these two levels of DOS *
|
|
* management is represented by the RWTS driver routines *
|
|
* (RWTSDRVR, $B052 and RWTSDRV1, $B058). RWTSDRVR is called any *
|
|
* time the file manager wants to seek a given track or read or *
|
|
* write a sector of disk data. This routine is always entered *
|
|
* with the accumulator containing the RWTS opcode ($00=seek, *
|
|
* $01=read, $03=write) and the x- and y-registers housing the *
|
|
* target track and sector values. Although RWTSDRV1 is only *
|
|
* directly called via the INIT function handler (with the *
|
|
* accumlator containing the format ($04) opcode), execution *
|
|
* falls into RWTSDRV1 from RWTSDRVR. The driver routines check *
|
|
* to see if data are to be output to the disk and condition the *
|
|
* carry flag accordingly. The carry is set if the format or *
|
|
* write opcodes are detected. After setting up RWTS's parameter *
|
|
* list (also known as an input/output block, IOB), the driver *
|
|
* calls ENTERWTS ($B7B5). *
|
|
* ENTERWTS preserves the conditioned carry flag by pushing *
|
|
* the status register on the stack. Next the interrupt disable *
|
|
* flag is set to prevent any maskable interrupts from interfer- *
|
|
* ring with the real-time subroutines employed by RWTS. *
|
|
* Finally, ENTERWTS calls RWTS proper ($BD00) to do the desired *
|
|
* function. *
|
|
* RWTS selects the appropriate drive and moves the disk arm *
|
|
* to the desired track. Next the opcode is checked to determine *
|
|
* what function is to be performed (BEGINCMD, $BDAB). When a *
|
|
* read opcode ($01) is detected, the carry flag is set and *
|
|
* saved on the stack. The actual read process begins after the *
|
|
* read counter is initialized and the x-register is set to a *
|
|
* value = slot*16. *
|
|
* The RDADDR routine ($B944) is called to read the first *
|
|
* address header encountered. If the disk is flawed or *
|
|
* "protected" with altered header or checksum bytes, 772 *
|
|
* attempts are made to locate the correct address prologue *
|
|
* before a re-read is attempted. Similarly, up to 48 tries to *
|
|
* read the entire address header are allowed between *
|
|
* recalibrations. Two recalibrations are premitted before a *
|
|
* I/O error message is generated. Once an address header is *
|
|
* successfully read, the volume, track and sector numbers of the *
|
|
* address header are compared to the values wanted. If the *
|
|
* volume number found does not correspond to the volume number *
|
|
* wanted, the read function is aborted with a volume-mismatch *
|
|
* error message. Failure to find the expected track or sector *
|
|
* numbers causes an attempt to locate another address header. *
|
|
* If the track numbers did not match, a re-seek is performed *
|
|
* before the next read is attempted. *
|
|
* Once the correct address header has been read, the *
|
|
* READATA routine ($B8DC) is used to read in the disk data bytes *
|
|
* in two different stages. During the first stage, 86 bytes of *
|
|
* 2-encoded nibbles are stored in RWTSBUF2 from higher memory *
|
|
* down ($BC55-$BC00). The second stage reads 256 bytes of six- *
|
|
* encoded nibbles into RWTSBUF1 ($BB00-$BBFF). The data bytes *
|
|
* are followed by a checksum byte. As the data are read in, a *
|
|
* running checksum is calculated. If the calculated checksum *
|
|
* does not agree with the checksum read off the disk, an error *
|
|
* was located. As a result, an attempt is made to re-read the *
|
|
* sector. If the checksums match, the first two bytes of the *
|
|
* data epilogue are read. Next, POSTNB16 ($B8C2) is used to *
|
|
* convert the 6-and-2 encoded bytes in the RWTS buffers to *
|
|
* normal memory bytes. The post-nibbled data are USUALLY stored *
|
|
* in the DOS data sector buffer. Execution finally returns to *
|
|
* the ENTERWTS routine at $B7BA. *
|
|
* After the saved status byte is thrown off the stack, the *
|
|
* carry flag is cleared or set (depending on whether or not RWTS *
|
|
* encountered an error). Execution then returns to the caller *
|
|
* of ENTERWTS. *
|
|
* After updating the last-used volume value in the FM *
|
|
* parameter list, the RWTSDRV1 routine checks the carry flag to *
|
|
* see if RWTS detected an error. If an error was encountered, *
|
|
* the carry is reset and execution returns to the calling *
|
|
* routine via ERRWTSDR ($B0A1). If no error was detected, the *
|
|
* "BCS ERRWTSDR" instruction at $B09E is skipped and execution *
|
|
* returns to the caller of the driving routine with the carry *
|
|
* clear. *
|
|
* Note that ENTERWTS is the one and only DIRECT caller of *
|
|
* RWTS. THE DOS MANUAL recommends the following procedure be *
|
|
* employed to call RWTS from an assembly language program: *
|
|
* 1) Set up the IOB and DCT tables accordingly. *
|
|
* 2) Load the y-register and accumulator with the low and *
|
|
* high bytes (respectively) of the address of the IOB. *
|
|
* 3) JSR to $3D9. (The instruction at $3D9 normally *
|
|
* contains a jump to ENTERWTS.) *
|
|
* The execution pattern of RWTS and its associated sub- *
|
|
* routines is long, but not particularly complex. On the one *
|
|
* hand RWTS is rather simple because it can only perform four *
|
|
* types of functions (seek, read, write or format). However, *
|
|
* many people find RWTS difficult to understand because: *
|
|
* 1) It is the only portion of DOS that uses time-critical *
|
|
* code. *
|
|
* 2) Two different methods are used to encode information *
|
|
* on the disk. *
|
|
* 3) The actual method by which the read/write head is *
|
|
* moved to different track positions on the disk is not *
|
|
* well publicized. *
|
|
* Time-critical code and data encoding information are only *
|
|
* briefly described below. However, these concepts are clearly *
|
|
* explained in chapter 3 of BENEATH APPLE DOS. (When reading *
|
|
* this reference, you may find it elcudiating to keep in mind *
|
|
* that some protected disks (such as LOCKSMITH by ALPHA LOGIC *
|
|
* BUSINESS SYSTEMS) modify the read/write routines to EOR each *
|
|
* sector of data with its sector number.) *
|
|
* The positioning of the read/write head is the sole *
|
|
* responsibility of the SEEKIT routine ($BE6B). This routine is *
|
|
* highly commented in the disassembly given below. The follow- *
|
|
* ing comments are applicable to the SEEKIT routine and RWTS in *
|
|
* general: *
|
|
* - Data are written on the disk in 35 circular paths or *
|
|
* concentric circles called tracks. Track $00 is located at *
|
|
* the outer edge of the disk, whereas track $22 (#34) is *
|
|
* represented by the innermost concentric circle. Each track *
|
|
* is divided into 16 segments ($00 to $0F) called sectors. *
|
|
* - A disk controller card can be used in any peripheral slot *
|
|
* except slot $00. Each of the remaining seven slots ($01 to *
|
|
* $07) can contain a controller card. Two different drives *
|
|
* can be operated from one controller card. Therefore, you *
|
|
* can hang up to 14 different drives from a single Apple II, *
|
|
* II+ or IIe machine. *
|
|
* - The disk controller ROM is relocatable and is copied into *
|
|
* the computer's memory at $Cs00 to $CsFF (where s = slot *
|
|
* number). All drive functions are performed by indirectly *
|
|
* referencing base addrs $C000 to $C00F. The motor-on-off, *
|
|
* drive selection and read-write switches are indexed with an *
|
|
* offset equal to slot * 16. The four different stepper motor *
|
|
* magnets are all referenced via the $C080 base addr. The *
|
|
* index used = (slot*16)+bits0and1 of halftrack# + carry. The *
|
|
* slot and bit portions of the index are used to select the *
|
|
* desired magnet. The added carry is used to make the *
|
|
* effective addr even or odd in order to turn the magnet off *
|
|
* or on. The EFFECTIVE addresses for all drive functions are *
|
|
* shown below: *
|
|
* MAG0FF = $C0s0 ;Turn stepper motor magnet 0 off. *
|
|
* MAG0N = $C0s1 ;Turn stepper motor magnet 0 on. *
|
|
* MAG1OFF = $C0s2 ;Turn stepper motor magnet 1 off. *
|
|
* MAG1ON = $C0s3 ;Turn stepper motor magnet 1 on. *
|
|
* MAG2OFF = $C0s4 ;Turn stepper motor magnet 2 off. *
|
|
* MAG2ON = $C0s5 ;Turn stepper motor magnet 2 on. *
|
|
* MAG3OFF = $C0s6 ;Turn stepper motor magnet 3 off. *
|
|
* MAG3ON = $C0s7 ;Turn stepper motor magnet 3 on. *
|
|
* MTR0FF = $C0s8 ;Wake up controller and spin disk. *
|
|
* ;This switch must be thrown before a *
|
|
* ;specific drive (1 or 2) is selected. *
|
|
* MTR0N = $C0s9 ;Turn disk drive motor off. *
|
|
* SELDRV1 = $C0sA ;Select drive number 1. *
|
|
* SELDRV2 = $C0sB ;Select drive number 2. *
|
|
* The following addresses are used to read or write data *
|
|
* bytes or to check the status of the write protect switch. *
|
|
* As shown below, they are always used in specific combinations *
|
|
* to evoke a certain range of responses from the controller *
|
|
* card. The firmware affected on the controller card is called *
|
|
* a logic state sequencer. It is a nibble-based language that *
|
|
* only contains six different instructions and is transparent to *
|
|
* the monitor ROM disassembler. (See UNDERSTANDING THE APPLE II *
|
|
* by Jim Sather for further explaination.) *
|
|
* Q6L = $C0sC ;Shift byte in or out of data latch. *
|
|
* Q6H = $C0sD ;Load latch from data bus. *
|
|
* Q7L = $C0sE ;Prepare to read. *
|
|
* Q7H = $C0sF ;Prepare to write. *
|
|
* When used in combinations: *
|
|
* Q7L plus Q6L = select read sequence and then read a byte. *
|
|
* Q6H plus Q7L = check write protect switch and select write *
|
|
* sequence. *
|
|
* Q7H plus Q6H = select write sequence and load data register *
|
|
* with output byte. *
|
|
* Q6H plus Q6L = load latch from data bus and write byte. *
|
|
* (Must have previously selected Q7H.) *
|
|
* - Each disk drive contains two motors. One motor (usually *
|
|
* referred to as the "drive motor") spins the disk at a *
|
|
* constant speed. (When the drive motor is first turned on, a *
|
|
* delay is used to wait for the drive to come up to speed *
|
|
* before attempting to read or write disk bytes.) Another *
|
|
* motor (called a "stepper motor") moves the read/write head *
|
|
* across the disk to position the head at different track *
|
|
* positions. *
|
|
* - The stepper motor can be envisioned as containing a central *
|
|
* magnet on a rotatable shaft and a circle of four fixed *
|
|
* magnets (magnets 0 to 3) surrounding the shaft. Each time a *
|
|
* peripheral magnet is enegized, the central shaft is rotated *
|
|
* until its magnet is in line with the energized peripheral *
|
|
* magnet. By turning the fixed peripheral magnets on and off *
|
|
* in sequence, we can spin the shaft of the stepper motor. *
|
|
* Movement of this shaft causes the read/write head to "step" *
|
|
* across the disk. Each time the next magnet in sequence is *
|
|
* turned on, the shaft is rotated one quarter turn. One *
|
|
* quarter turn of the shaft moves the read/write head half a *
|
|
* track width. *
|
|
* - Normally, DOS only writes data at even magnet positions *
|
|
* because the drive head does not have good enough resolution *
|
|
* to distinguish information in adjacent half-track positions. *
|
|
* The drive head is stepped to a higher track position as the *
|
|
* magnets are turned on and off in ascending order. *
|
|
* Similarly, a descending reference to the magnets causes *
|
|
* movement to a lower track position. Each time a magnet is *
|
|
* turned on or off, a delay is used to give the shaft magnet *
|
|
* time to properly align with a peripheral magnet. The amount *
|
|
* of delay used is inversely proportional to the acceleration *
|
|
* of the motor. An example of the on/delay/off sequence used *
|
|
* to step the head from track $02 to track $04 is shown below: *
|
|
* 1on - delay - 0off - delay - 2on - delay - 1off - delay - *
|
|
* 3on - delay - 2off - delay - 0on - delay - 3off - delay - *
|
|
* 0off - delay. *
|
|
* Similarly, moving the head from track $04 to track $02 *
|
|
* requires the following sequence: *
|
|
* 3on - delay - 0off - delay - 2on - delay - 3off - delay - *
|
|
* 1on - delay - 2off - delay - 0on - delay - 1off - delay - *
|
|
* 0off. *
|
|
* Note that the last-energized magnet is always turned off. *
|
|
* This is done as a safety measure because magnet-1-on is *
|
|
* hard wired into the write protect switch. (The boot process *
|
|
* is an exception to this rule. The controller ROM leaves *
|
|
* magnet0 energized.) *
|
|
* - Some protected programs modify DOS to skip entire tracks or *
|
|
* write data at odd-numbered magnet positions. However, *
|
|
* because the controller ROM always uses track $00 and because *
|
|
* crosstalk occurs when data is less than one full track width *
|
|
* apart, the data is actually written on a half-track disk at *
|
|
* the following track positions: 0, 1+1/2, 2+1/2, 3+1/2, ..., *
|
|
* 31+1/2, 32+1/2, 33+1/2. For instance, if you wanted to move *
|
|
* the head from track $02 to track $04+1/2, you could add the *
|
|
* following sequence to that described above: *
|
|
* - delay - 1on - delay - 0off - 1 off -delay. *
|
|
* - Data can even be written on quarter track positions (that *
|
|
* is, tracks 0, 1+1/4, 2+1/4, ..., 31+1/4, 32+1/4, 33+1/4) by *
|
|
* turning on two adjacent magnets almost simultaneously in *
|
|
* order to position the head between the two magnets. For *
|
|
* instance, if you wanted to move from track $02 to track *
|
|
* $04+1/4, you could patch DOS to automatically add the *
|
|
* following instructions to the normal sequence described *
|
|
* above: - 1on - no delay - 0on - very short delay - 1off - *
|
|
* no delay - 0off - delay. *
|
|
* - Assembly language programmers that want to write their own *
|
|
* copy programs, may appreciate the following tidbits: *
|
|
* - Self-sync $FF's are not the only bytes in town that can be *
|
|
* used to put a bit stream in sync. As a matter of fact, *
|
|
* almost any bit stream will do if it is long enough. *
|
|
* - Believe it or not, DOS can actually "read" an unformatted *
|
|
* disk. If you use a track dump program on an unformatted *
|
|
* disk, the computer will obligingly produce a magic stream *
|
|
* of data. This occurs because the logic state sequencer *
|
|
* only waits so long for a valid bit stream. After a while, *
|
|
* the sequencer gets impatient and generates its own *
|
|
* spurious bytes. Because a read strobe (Q6L) is always *
|
|
* followed by a "BPL" instruction, the invented bytes have *
|
|
* their hi bits set. However, valid disk bytes can only *
|
|
* take on certain negative values. Therefore, the presence *
|
|
* of illegal values can be used to detect unformatted *
|
|
* portions of a disk. Some disk copying programs use this *
|
|
* technique to copy disks that are "protected" by one or *
|
|
* more unformatted tracks. *
|
|
* *
|
|
*::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*
|
|
|
|
|
|
(B052)
|
|
RWTSDRVR STX IBTRK ;Enter with (x) = trk wntd.
|
|
STY IBSECT ; (y) = sector wntd.
|
|
RWTSDRV1 STA IBCMD ; (a) = opcode for RWTS.
|
|
(B058) ;RWTSDRV1 is the entry point used by the
|
|
;Init function handler.
|
|
(B05B) CMP #2 ;Is cmd a write?
|
|
(B05D) BNE SKPWRSET ;No, so branch. Note: "CMP" conditions:
|
|
; (c)=0 if seek ($00) or read ($01).
|
|
; (c)=1 if write ($02) or format ($03).
|
|
(B05F) ORA UPDATFLG ;Condition UPDATFLG to designate that
|
|
(B062) STA UPDATFLG ;last operation was a write for next
|
|
;time around.
|
|
|
|
* Finish setting up RWTS's
|
|
* input/output block (IOB).
|
|
(B065)
|
|
SKPWRSET LDA VOLWA ;Put complimented vol in IOB.
|
|
EOR #$FF
|
|
STA IBVOL
|
|
LDA SLOT16WA ;Put slot*16 in IOB.
|
|
STA IBSLOT
|
|
LDA DRVWA ;Put drive in IOB.
|
|
STA IBDRVN
|
|
LDA SECSIZWA ;Put sector length in IOB
|
|
STA IBSECSZ ;(standard size of dec. 256
|
|
LDA SECSIZWA+1 ;or hex $0100 bytes).
|
|
STA IBSECSZ+1
|
|
LDA #1 ;ALWAYS designate table type as 1.
|
|
STA IBTYPE
|
|
LDY ADRIOB ;Set (y) & (a) to point
|
|
LDA ADRIOB+1 ;at RWTS's IOB.
|
|
(B090) JSR ENTERWTS ;Go call RWTS.
|
|
|
|
* Route execution to RWTS.
|
|
* (Normal entry route to RWTS for custom
|
|
* assembly language programs. See preamble
|
|
* for required entry conditions.)
|
|
(B7B5)
|
|
ENTERWTS PHP ;Save status reg (with conditioned carry) on stk.
|
|
;(c) = 0 if doing seek ($00) or read ($01).
|
|
;(c) = 1 if doing write ($02) or format ($03).
|
|
(B7B6) SEI ;Set interrupt disable flag to prevent
|
|
(B7B7) JSR RWTS ;any further maskable interrupts
|
|
;when doing real-time programming.
|
|
|
|
* Read/Write Track/Sector (RWTS).
|
|
* Enter with (y)/(a) pointing at
|
|
* RWTS's input/output block (IOB).
|
|
(BD00)
|
|
RWTS STY PTR2IOB ;Set up a zero page
|
|
STA PTR2IOB+1 ;pointer to RWTS's IOB.
|
|
LDY #2 ;Initialize cntr for max of 2 recalibs.
|
|
STY RECLBCNT
|
|
LDY #4 ;Initialize cntr for # or re-seeks betwn recalibs.
|
|
STY RSEEKCNT
|
|
LDY #1 ;Get slot*16 from IOB & put it
|
|
LDA (PTR2IOB),Y ;in (x) so can use it to index
|
|
(BD12) TAX ;base addresses for drive functions.
|
|
|
|
* Check if wanted slot*16 = last slot*16.
|
|
(BD13) LDY #15 ;Index to get val of last slot used.
|
|
CMP (PTR2IOB),Y ;Compare wanted vs last.
|
|
(BD17) BEQ SAMESLOT ;Take branch if using same slot.
|
|
|
|
* Want to use different slot so reset (x)
|
|
* back to index old slot so can test old motor.
|
|
(BD19) TXA ;Save slot*16 wanted on stk.
|
|
PHA
|
|
LDA (PTR2IOB),Y ;Get old slot*16 back and
|
|
TAX ;stick it in (x) to index base addrs.
|
|
PLA ;Get slot*16 wanted into (a) from stk
|
|
PHA ;and keep it saved on stk.
|
|
(BD20) STA (PTR2IOB),Y ;Update last-used slot*16 for next time.
|
|
|
|
* Check to see if last-used drive assoc with last-
|
|
* used slot is still spinning. If it is, wait for
|
|
* it to stop.
|
|
(BD22) LDA Q7L,X ;Prep latch for input.
|
|
CKSPIN LDY #8 ;Set cntr to insure at least 8 chks.
|
|
LDA Q6L,X ;Strobe latch to read.
|
|
CHKCHNG CMP Q6L,X ;Read again & cmp to last read.
|
|
BNE CKSPIN ;Data changed, so still spinning.
|
|
DEY ;No change, so chk with some
|
|
(BD30) BNE CHKCHNG ;delays just to make sure.
|
|
|
|
* Get index for slot wanted.
|
|
(BD32) PLA ;Get slot*16 back off stk
|
|
(BD33) TAX ;and put it in (x).
|
|
|
|
* Chk to see if a drive assoc with slot wanted
|
|
* is still spinning. (As soon as get a change then
|
|
* know it's spinning. If no change, chk at least
|
|
* 8 times to be certain it is off.)
|
|
(BD34)
|
|
SAMESLOT LDA Q7L,X ;Set read mode.
|
|
LDA Q6L,X ;Strobe latch to read.
|
|
LDY #8 ;Set cntr for 8 chks if needed.
|
|
STRBAGN LDA Q6L,X ;Strobe latch again.
|
|
PHA ;Delay 14 machine cycles.
|
|
PLA
|
|
PHA
|
|
PLA
|
|
STX SLOTPG5 ;Save slot*16 wntd in page 5.
|
|
CMP Q6L,X ;Has data changed yet?
|
|
BNE DONETEST ;Yes - data changed, so disk spinning.
|
|
DEY ;No - no change, see if chkd enough times.
|
|
BNE STRBAGN ;Chk at least 8 times.
|
|
DONETEST PHP ;Save test results on stk so can later
|
|
(BD4E) ;chk if need extra delay or not.
|
|
|
|
* Turn motor on in a drive assoc with slot wanted
|
|
* (just in case it wasn't already spinning).
|
|
* Note: This uses drive with same # as last
|
|
* drive used. This may or may not be the
|
|
* specific drive # we want. However, we must use
|
|
* this instruction to send power via the controller.
|
|
* Once this switch is thrown, we can later re-route
|
|
* that power to whichever drive we want by throwing
|
|
* another switch to select drive1 or drive2.
|
|
(BD4F) LDA MTRON,X ;Turn motor on.
|
|
|
|
* Establish z-page pointers to device characteristic
|
|
* table (DCT) and RWTS's I/O buffer (so can use z-page
|
|
* indirect addressing modes).
|
|
* IBDCTP ---> PTR2DCT (3C,3D)
|
|
* IBBUFP ---> PTR2BUF (3E,3F)
|
|
(BD52) LDY #6 ;Get ptrs from RWTS's IOB
|
|
MOVPTRS LDA (PTR2IOB),Y ;and put them in z-page.
|
|
(BD56) STA: PTR2DCT-6,Y ;(Note: ":" used to force
|
|
;a 3-byte instruction.)
|
|
(BD59) INY
|
|
CPY #10 ;4 bytes to copy (6 to 9).
|
|
(BD5C) BNE MOVPTRS
|
|
|
|
* Check drive status.
|
|
(BD5E) LDY #3 ;Save hi byte of motor-on-time
|
|
LDA (PTR2DCT),Y ;count in z-page.
|
|
STA MTRTIME+1
|
|
LDY #2 ;Get drive # wanted.
|
|
LDA (PTR2IOB),Y
|
|
LDY #16 ;Set (y) = index to last-used drive.
|
|
CMP (PTR2IOB),Y ;Drv wanted vs drv last used.
|
|
BEQ SAMEDRV
|
|
(BD6E) STA (PTR2IOB),Y ;Designate drv wanted as old drv
|
|
;for next time around.
|
|
(BD70) PLP ;Get status back off stk.
|
|
(BD71) LDY #0 ;Reset status (z-flag off) to signal that
|
|
;SPECIFIC DRIVE # we want in SPECIFIC SLOT
|
|
;wanted was not originally spinning.
|
|
(BD73) PHP ;Push updated status back on stk.
|
|
SAMEDRV ROR ;Put low bit of drv wanted in carry.
|
|
BCC USEDRV2 ;Branch if want drive 2.
|
|
LDA SELDRV1,X ;Route power to select drive 1.
|
|
BCS USEDRV1 ;ALWAYS.
|
|
USEDRV2 LDA SELDRV2,X ;Route power to select drive 2.
|
|
USEDRV1 ROR DRVZPG ;Put sign bit for which drive
|
|
(BD7F) ;using in z-page: neg = drive1.
|
|
; pos = drive2.
|
|
|
|
* Chk to see if a specific drive wanted in
|
|
* specific slot wanted was originally on or not.
|
|
(BD81) PLP ;Get previous test result.
|
|
PHP ;Put it back on stk for later use.
|
|
(BD83) BNE WASON ;Orig drv in orig slot was on.
|
|
|
|
* Specific drive wanted in specific slot
|
|
* wanted was ORIGINALLY OFF, so delay a
|
|
* bit to avoid positioning head during
|
|
* the period of heavy current flow that
|
|
* occurs when motor is turned on. (That
|
|
* is, give line/capacitor time to
|
|
* bleed down cause motor on/off switch
|
|
* requires more current than stepper motor.)
|
|
*
|
|
* (Amount of delay is not constant cause
|
|
* it depends on what is in accum & we don't
|
|
* know cause we were just accessing hardware.)
|
|
(BD85) LDY #7
|
|
WAIT4MTR JSR DELAY ;Stall.
|
|
(BD87)
|
|
|
|
* Main delay routine in DOS.
|
|
* (Amt of delay = 100 * (a) microsecs.)
|
|
(BA00)
|
|
DELAY LDX #17
|
|
DLY1 DEX
|
|
BNE DLY1
|
|
INC MTRTIME
|
|
BNE DLY2
|
|
INC MTRTIME+1
|
|
DLY2 SEC
|
|
SBC #1
|
|
BNE DELAY
|
|
(BA10) RTS
|
|
|
|
(BD8A) DEY
|
|
BNE WAIT4MTR ;Go stall some more.
|
|
(BD8D) LDX SLOTPG5 ;Restore (x) = slot*16.
|
|
(BD90)
|
|
WASON LDY #4 ;Get trk wanted.
|
|
LDA (PTR2IOB),Y
|
|
(BD94) JSR SEEKTRK ;Go move arm to desired trk.
|
|
|
|
(BE5A)
|
|
SEEKTRK PHA ;Save # of trk wntd on stk.
|
|
LDY #1 ;Get drive type (1- or 2-phase)
|
|
(BE5D) LDA (PTR2DCT),Y ;from DCT. (P.S. the "II" in the
|
|
;"DISK II" logo stamped on Apple's
|
|
;disk drive denotes a two-phase
|
|
;stepper motor.)
|
|
(BE5F) ROR ;Put low byte of drive type in carry.
|
|
PLA ;Get trk# wanted back in (a).
|
|
(BE61) BCC SEEKIT ;Not using standard DRIVEII, using a
|
|
;one-phase drive instead, ther4 skip
|
|
;doubling of trk # & use SEEKIT as part
|
|
;of routine instead of a subroutine.
|
|
|
|
* Using a two-phase drive.
|
|
(BE63) ASL ;Double trk # wanted to get
|
|
;number of 1/2 trk wanted.
|
|
(BE64) JSR SEEKIT ;Move disk arm to desired track.
|
|
|
|
(BE6B)
|
|
SEEKIT .
|
|
.
|
|
.
|
|
---------------------------------------------
|
|
l * Routine/subroutine to move drive arm
|
|
l * to a specific trk position.
|
|
l * Used as a subroutine when using Apple's
|
|
l * disk drive II. Note when SEEKIT is used as a
|
|
l * subroutine, DESTRK, PRESTRK, TRK4DRV1, TRK4DRV2,
|
|
l * STPSDONE and HOLDPRES are all expressed in half
|
|
l * tracks:
|
|
l * DESTRK = destination half-track position.
|
|
l * PRESTRK = present half-track position.
|
|
l * HOLDPRES = present half-track position.
|
|
l * TRK4DRV1 = base addr (when indexed by slot*16) pts
|
|
l * at the addr that contains the last half-
|
|
l * track that drive 1 was aligned on.
|
|
l * TRK4DRV2 = base addr (when indexed by slot*16) pts
|
|
l * at the addr that contains the last half-
|
|
l * track that drive 2 was aligned on.
|
|
l * STPSDONE = number of half tracks moved so far.
|
|
l * If not using a II-phase drive, change all
|
|
l * comments that read "half tracks" to read
|
|
l * "full tracks".
|
|
l (BE6B)
|
|
l SEEKIT STA DESTRK ;(a) = 2*trk # wanted.
|
|
l ; = # of halftrk wanted.
|
|
l (BE6D) JSR SLOTX2Y ;Set (y) = slot.
|
|
l
|
|
l * Convert slot*16 from
|
|
l * (x) to slot in (y).
|
|
l (BE8E)
|
|
l SLOTX2Y TXA ;Get slot*16 from (x).
|
|
l LSR ;Divide it by 16.
|
|
l LSR
|
|
l LSR
|
|
l LSR
|
|
l TAY ;Put slot # in (y).
|
|
l (BE94) RTS
|
|
l
|
|
l (BE70) LDA TRK4DRV1,Y ;Pres halftrk# assoc with drv1.
|
|
l (BE73) BIT DRVZPG ;Contains: neg = drive 1.
|
|
l ; pos = drive 2.
|
|
l (BE75) BMI SETPRSTK ;Branch if using drive 1.
|
|
l LDA TRK4DRV2,Y ;Using drv 2 so get pres 1/2trk#
|
|
l SETPRSTK STA PRESTRK ;Save present halftrk#.
|
|
l (BE7A)
|
|
l
|
|
l * Designate halftrk we are about to seek
|
|
l * as present halftrk for next time around.
|
|
l * (Put halftrk info in slot dependent locations.)
|
|
l (BE7D) LDA DESTRK
|
|
l BIT DRVZPG ;Chk to see which drive we are using.
|
|
l BMI DRV1USG ;Branch if using drive 1.
|
|
l STA TRK4DRV2,Y ;Using drv2 -store halftrk 4 next time.
|
|
l BPL DRV2USG ;ALWAYS.
|
|
l DRV1USG STA TRK4DRV1,Y ;Using drv1 -store halftrk 4 next time.
|
|
l DRV2USG JMP SEEKABS
|
|
l (BE8B) -----------
|
|
l
|
|
l * Move disk arm to a given halftrk position.
|
|
l * On entry: (x) = slot * 16.
|
|
l * (a) = destination halftrack pos'n.
|
|
l * PRESTRK = current halftrack pos'n.
|
|
l
|
|
l (B9A0)
|
|
l SEEKABS STX SLT16ZPG ;Save slot*16 in z-page.
|
|
l STA DESTRK ;Save destin halftrk #.
|
|
l CMP PRESTRK ;Dest halftrk = pres halftrk?
|
|
l BEQ ARRIVED ;Yes - we are already there, so exit.
|
|
l LDA #0 ;Init counter 4 # of halftrks moved.
|
|
l (B9AB) STA STPSDONE
|
|
l
|
|
l * Save current halftrk pos'n & calc
|
|
l * number of halftrks need to move minus 1.
|
|
l (B9AD)
|
|
l SAVCURTK LDA PRESTRK ;Save current halftrk pos'n.
|
|
l STA HOLDPRES
|
|
l SEC ;Calc (PRESTRK - DESTRK).
|
|
l SBC DESTRK
|
|
l BEQ ATDESTN ;At destin halftrk so go shutdown.
|
|
l (B9B7) BCS MOVDWN ;Pres halftrk > destin halftrk so
|
|
l ;want to move to lower trk.
|
|
l
|
|
l * Want to move to a higher halftrk #
|
|
l * (PRESTRK - DESTRK = neg result).
|
|
l
|
|
l (B9B9) EOR #$FF ;Convert neg to pos.
|
|
l (B9BB) INC PRESTRK ;Moving up, so inc current 1/2
|
|
l ;trk pos'n for next time around.
|
|
l (B9BE) BCC CKDLYNDX ;ALWAYS.
|
|
l ------------
|
|
l
|
|
l * Want to move to lower halftrk #
|
|
l * (PRESTRK - DESTRK = pos result).
|
|
l (B9C0)
|
|
l MOVDOWN ADC #$FE ;Simulate a subtract of 1. Actually
|
|
l ;adding minus 1 (#$FF) cause carry
|
|
l ;set. Want (a) to equal 1 less than
|
|
l ;number of halftrks to move.
|
|
l (B9C2) DEC PRESTRK ;Moving down so reduce pres 1/2
|
|
l ;trk number for next time around.
|
|
l
|
|
l * Check to see which index to use to
|
|
l * access the delay table. IF WE ARE
|
|
l * WITHIN 12 STEPS of the destination
|
|
l * or start positions, then use closest
|
|
l * distance to start or end pos'n to
|
|
l * index delay tables. Delay tables are
|
|
l * only 12 bytes long, so if more than 12
|
|
l * steps away from both start & destination,
|
|
l * then use last index (y=12) to access table.
|
|
l
|
|
l * Check if closer to destination or start pos'n.
|
|
l (B9C5)
|
|
l CKDLYNDX CMP STPSDONE ;Compare # of halftrks moved
|
|
l ;to # of halftrks need to move.
|
|
l (B9C7) BCC CLSR2ND ;Branch if closer to destn than start posn.
|
|
l
|
|
l * Closer to start.
|
|
l (B9C9) LDA STPSDONE ;Set (a) = dist from start pos'n.
|
|
l CLSR2ND CMP #12 ;Are we within 12 steps of start
|
|
l ;or destination pos'n?
|
|
l (B9CD) BCS TURNON ;We are at or beyond 12 steps from
|
|
l ;start or destn pos'n so use old
|
|
l ;index to access delay table.
|
|
l (B9CF)
|
|
l PRESNDX TAY ;Use present distance to index delay table.
|
|
l TURNON SEC ;Set carry so get odd index to base addr so
|
|
l (B9D0) ;magnet will be turned ON.
|
|
l (B9D1) JSR ONOROFF ;Turn magnet ON to suck stepper motor
|
|
l ;to correct halftrack pos'n.
|
|
l
|
|
l (B9EE)
|
|
l ONOROFF LDA PRESTRK ;Use lwr 2 bits of
|
|
l ENTRYOFF AND #%00000011 ;1/2 trk pos'n to
|
|
l ;index magnet.
|
|
l (B9F3) ROL ;2*halftrack+(c).
|
|
l ;If carry set,
|
|
l ;result is odd &
|
|
l ;magnet is energized.
|
|
l (B9F4) ORA SLT16ZPG ;Merge index to magnet
|
|
l ;with slot #.
|
|
l (B9F6) TAX ;Use (x) for indexing.
|
|
l (B9F7) LDA MAG0FF,X ;Use magnet0 off as
|
|
l ;base address.
|
|
l (B9FA) LDX SLT16ZPG ;Restore (x)=slot*16.
|
|
l ARRIVED RTS
|
|
l (B9FC)
|
|
l
|
|
l (B9D4) LDA ONTABLE,Y ;Get time 2 leave magnet on from tbl.
|
|
l (B9D7) JSR DELAY ;Delay to give drive time to act before
|
|
l ;magnet is turned off again cause computer
|
|
l ;too fast 4 peripheral & want smooth mov't.
|
|
l
|
|
l * Main delay routine in DOS.
|
|
l * (Amt of delay = 100 * (a) microsecs.)
|
|
l (BA00)
|
|
l DELAY LDY #17
|
|
l DLY1 DEX
|
|
l BNE DLY1
|
|
l INC MTRTIME
|
|
l BNE DLY2
|
|
l INC MTRTIME+1
|
|
l DLY2 SEC
|
|
l SBC #1
|
|
l BNE DELAY
|
|
l (BA10) RTS
|
|
l
|
|
l (B9DA) LDA HOLDPRES ;Get last halftrk pos'n in (a).
|
|
l (B9DE) CLC ;Clr carry so index will come out even
|
|
l ;and there4 magnet will be turned OFF.
|
|
l (B9DD) JSR ENTRYOFF ;Turn magnet assoc with prev pos'n off.
|
|
l
|
|
l (B9F1)
|
|
l ENTRYOFF AND #%00000011 ;Halftrk pos'n to
|
|
l ;index magnet.
|
|
l (B9F3) ROL ;2*halftrack+(c).
|
|
l ;If carry set,
|
|
l ;result is odd &
|
|
l ;magnet is energized.
|
|
l (B9F4) ORA SLT16ZPG ;Merge index to magnet
|
|
l ;with slot #.
|
|
l (B9F6) TAX ;Use (x) for indexing.
|
|
l (B9F7) LDA MAG0FF,X ;Use magnet0 off as
|
|
l ;base address.
|
|
l (B9FA) LDX SLT16ZPG ;Restore (x)=slot*16.
|
|
l ARRIVED RTS
|
|
l (B9FC)
|
|
l
|
|
l (B9E0) LDA OFFTABLE,Y ;Get time 2 leave magnet off from table.
|
|
l (B9E3) JSR DELAY ;Leave magnet off for a while to give
|
|
l ;arm time to be properly aligned.
|
|
l ;(Need time to suck it over & also to
|
|
l ;decrease bounce or over-shoot.)
|
|
l
|
|
l * Main delay routine in DOS.
|
|
l * (Amt of delay = 100 * (a) microsecs.)
|
|
l (BA00)
|
|
l DELAY LDY #17
|
|
l DLY1 DEX
|
|
l BNE DLY1
|
|
l INC MTRTIME
|
|
l BNE DLY2
|
|
l INC MTRTIME+1
|
|
l DLY2 SEC
|
|
l SBC #1
|
|
l BNE DELAY
|
|
l (BA10) RTS
|
|
l
|
|
l (B9E6) INC STPSDONE
|
|
l (B9E8) BNE SAVCURTK ;ALWAYS.
|
|
l ------------
|
|
l
|
|
l * Arrived at destination halftrack.
|
|
l (B9EA)
|
|
l ATDESTN JSR DELAY ;Wait for peripheral again.
|
|
l
|
|
l * Main delay routine in DOS.
|
|
l * (Amt of delay = 100 * (a) microsecs.)
|
|
l (BA00)
|
|
l DELAY LDY #17
|
|
l DLY1 DEX
|
|
l BNE DLY1
|
|
l INC MTRTIME
|
|
l BNE DLY2
|
|
l INC MTRTIME+1
|
|
l DLY2 SEC
|
|
l SBC #1
|
|
l BNE DELAY
|
|
l (BA10) RTS
|
|
l
|
|
l * Turn last-used magnet off so exit with all
|
|
l * phases (ie. magnets) off because MAG1ON is
|
|
l * wired into the write-protect switch.
|
|
l (B9ED) CLC ;Turn magnet OFF.
|
|
l
|
|
l * Turn magnet on or off.
|
|
l (B9EE)
|
|
l ONOROFF LDA PRESTRK ;Use halftrk pos'n 2 index magnet.
|
|
l ENTRYOFF AND #%00000011 ;Only keep lwr 2 bits of halftrk#
|
|
l (B9F1) ;cause only 4 magnets (0,1,2 & 3).
|
|
l (B9F3) ROL ;Multiply halftrk# * 2 and add (c)
|
|
l ;If (c)=1, result even, magnet off
|
|
l ;If (c)=0, result odd, magnet on
|
|
l (B9F4) ORA SLT16ZPG ;Merge index to magnet with slot #
|
|
l TAX ;Use (x) for indexing.
|
|
l LDA MAG0FF,X ;Use magnet-0-off as base addr.
|
|
l LDX SLT16ZPG ;Restore slot*16 in (x).
|
|
l ARRIVED RTS
|
|
l (B9FC)
|
|
l--------------------------------------------
|
|
.
|
|
.
|
|
.
|
|
-----------------------
|
|
l
|
|
l
|
|
(BE67) LSR PRESTRK ;Calc present whole trk #
|
|
;(ie. pres halftrk# / 2).
|
|
(BE6A) RTS
|
|
|
|
* Check to see if motor was originally on.
|
|
(BD97) PLP ;Get prev motor test result off stack.
|
|
BNE BEGINCMD ;Branch if motor was originally on.
|
|
(BD9A) LDY MTRTIME+1 ;Motor wasn't originally on. However, we
|
|
;have since turned it on. Now check if it
|
|
;has been on long enough.
|
|
(BD9C) BPL BEGINCMD ;Yes - no need to wait any longer.
|
|
|
|
* Although motor was turned on, it hasn't
|
|
* been on long enough to do accurate
|
|
* reading of bytes. There4, delay until
|
|
* motor on time is 1 second (at which time
|
|
* MTRTIME count is zero). (Part of time was
|
|
* taken up to seek track.)
|
|
(BD9E)
|
|
TIME1 LDY #18
|
|
TIME2 DEY
|
|
BNE TIME2
|
|
INC MTRTIME
|
|
BNE TIME1
|
|
INC MTRTIME+1
|
|
(BDA9) BNE TIME1
|
|
|
|
* Motor is up to speed so now process command.
|
|
* (Seek=00, Read=01, Write=02, Format=04.)
|
|
* Counters:
|
|
* READCNTR = allow up to 48 times to find correct
|
|
* addr prologue between re-seeking.
|
|
* RSEEKCNT = allow up to 4 re-seeks btwn recalibrations.
|
|
* RECLBCNT = allow up to 2 recalibrations.
|
|
* (There4, if necessary, allow up to 384
|
|
* attempts to find correct prologue addr.)
|
|
|
|
* Begin RWTS command processing.
|
|
(BDAB)
|
|
BEGINCMD LDY #12 ;Get cmd from IOB.
|
|
LDA (PTR2IOB),Y
|
|
BEQ WASEEK ;Branch if cmd was "seek".
|
|
CMP #4 ;Was cmd "format"?
|
|
(BDB3) BEQ FORMDSK ;Branch if command was "format".
|
|
|
|
* Command was READ or WRITE
|
|
* (ie. opcodes $01 or $02).
|
|
(BDB5) ROR ;SET carry if READ cmd (opcode $01).
|
|
;CLR carry if WRITE cmd (opcode $02).
|
|
(BDB6) PHP ;Save carry denoting cmd on stack.
|
|
(BDB7) BCS RESETCNT ;Branch if doing a read.
|
|
------------
|
|
|
|
* Common to READ or WRITE.
|
|
(BDBC)
|
|
RESETCNT LDY #48 ;Init counter for reading addr header.
|
|
STY READCNTR
|
|
SETXSLT LDX SLOT5PG ;Set (x) = slot*16.
|
|
(BDC4) JSR RDADDR ;Go read addr header to find sector
|
|
;that we want to read or write.
|
|
|
|
* Read the address header.
|
|
(B944)
|
|
RDADDR LDY #$FC ;Designate 772 chances (#$FCFC to #$10000)
|
|
;to find the correct address prologue.
|
|
(B946) STY PROSCRTH
|
|
KICKNTR INY
|
|
BNE TRYD5
|
|
INC PROSCRTH
|
|
(B94D) BEQ ERRTN ;Error - can't find proglogue.
|
|
|
|
* Look for address prologue ("D5 AA 96").
|
|
(B94F)
|
|
TRYD5 LDA Q5L,X
|
|
BPL TRYD5 ;Wait for a full byte.
|
|
VERSUSD5 CMP #$D5 ;Was it a "D5"?
|
|
BNE KICKNTR ;No - try again.
|
|
NOP ;Wait 2 cycles.
|
|
TRYAA LDA Q6L,X
|
|
BPL TRYAA ;Wait for full byte.
|
|
CMP #$AA ;Was it an "AA"?
|
|
BNE VERSUSD5 ;No - retry sequence.
|
|
(B962) LDY #3 ;Set (y) for later reading of
|
|
;vol, trk, sec, checksum info
|
|
;from address field.
|
|
(B964)
|
|
TRY96 LDA Q6L,X
|
|
BPL TRY96 ;Wait for full byte.
|
|
CMP #$96 ;Was it a "96"?
|
|
(B96B) BNE VERSUSD5 ;No - retry sequence.
|
|
|
|
* Read odd-even encoded volume, track,
|
|
* sector and checksum values from the
|
|
* address field. (When reading,
|
|
* calculate a running checksum.)
|
|
* From: byte1: 1 b7 1 b5 1 b3 1 b1
|
|
* byte2: b6 1 b4 1 b2 1 b0 1
|
|
* -------------------------------
|
|
* To: byte: b7 b6 b5 b4 b3 b2 b1 b0
|
|
|
|
(B96D) LDA #0 ;Initialize running checksum val.
|
|
CALCK STA CKSUMCAL
|
|
GETHDR LDA Q6L,X ;Get odd-encoded byte.
|
|
BPL GETHDR ;Wait for a full byte.
|
|
(B976) ROL ;Shift bits & put set
|
|
;carry in bit0 pos'n.
|
|
(B977) STA PROSCRTH ;Save shifted version.
|
|
RDHDR LDA Q6L,X ;Get even-encoded byte.
|
|
BPL RDHDR ;Wait for full byte.
|
|
AND PROSCRTH ;Merge to form normal memory bytes.
|
|
(B980) STA: CKSUMDSK,Y ;Store info read from addr
|
|
;field in zero page:
|
|
;$2F = vol found, $2E = trk found,
|
|
;$2D = sec found, $2C = checksum found.
|
|
;(Use ":" to force 3-byte instruction.)
|
|
(B983) EOR CKSUMCAL ;Update running checksum.
|
|
DEY
|
|
BPL CALCK
|
|
TAY ;Put checksum found in (y).
|
|
(B989) BNE ERRTN ;If checksum found < > 0, then error.
|
|
;Hackers often change these two bytes
|
|
;to "CLC" and "RTS" instructions in order
|
|
;to defeat the address checksum and ignore
|
|
;the address epilogue.
|
|
|
|
* Read first 2 bytes (only) of
|
|
* the address epilogue ("DE AA").
|
|
(B98B)
|
|
TRYEPIDE LDA Q6L,X ;Get first byte.
|
|
BPL TRYEPIDE ;Wait for a full byte.
|
|
CMP #$DE ;Was it a "DE"?
|
|
BNE ERRTN ;No - try again.
|
|
NOP ;Stall 2 cycles.
|
|
TRYEPIAA LDA Q6L,X ;Get second byte.
|
|
BPL TRYEPIAA ;Wait for a full byte.
|
|
CMP #$AA ;Was it an "AA"?
|
|
BNE ERRTN ;No - retry sequence.
|
|
GOODRTN CLC ;Signal good read.
|
|
(B99F) RTS
|
|
============
|
|
|
|
(B942)
|
|
ERRTN SEC ;Signal bad read.
|
|
(B943) RTS ;Hackers often change the "SEC" to
|
|
============ ;a "CLC" in order to defeat error
|
|
;checking.
|
|
|
|
(BDC7) BCC RDRIGHT ;Addr read was good.
|
|
|
|
* Bad address (or data) read.
|
|
(BDC9)
|
|
REDUCERD DEC READCNTR ;Reduce read counter.
|
|
(BDCC) BPL SETXSLT ;Try again.
|
|
|
|
* Do a recalibration cause exhausted
|
|
* all attempts to get a good read.
|
|
(BDCE)
|
|
DORECALB LDA PRESTRK ;Save trk wanted on stack.
|
|
PHA
|
|
(BDD2) LDA #96 ;Pretend presently on trk 96
|
|
;so force head against stop.
|
|
(BDD4) JSR SETTRK ;Go select drive & put trk wanted in
|
|
;memory location specific to drive.
|
|
|
|
(BE95)
|
|
SETTRK PHA ;Save present trk on stack.
|
|
LDY #2 ;Get drive # wanted from IOB.
|
|
LDA (PTR2IOB),Y
|
|
(BE9A) ROR ;Condition carry:
|
|
; clr=drv1, set = drv2.
|
|
(BE9B) ROR DRVZPG ;Condition zero-page loc:
|
|
; neg=drv1, pos=drv2.
|
|
(BE9D) JSR SLOTX2Y ;Set (y) = slot.
|
|
|
|
* Convert slot*16 from (x)
|
|
* to slot in (y).
|
|
(BE8E)
|
|
SLOTX2Y TXA ;Get slot*16 from (x).
|
|
LSR ;Divide it by 16.
|
|
LSR
|
|
LSR
|
|
LSR
|
|
TAY ;(y) = slot.
|
|
(BE94) RTS
|
|
|
|
(BEA0) PLA ;Get trk wanted off stk.
|
|
ASL ;Times 2 for half track.
|
|
BIT DRVZPG ;Check which drive to use.
|
|
BMI STORDRV1 ;Branch if using drive 1.
|
|
STA TRK4DRV2,Y ;Save halftrack wanted for drv2.
|
|
BPL RTNSETRK ;ALWAYS.
|
|
STORDRV1 STA TRK4DRV1,Y ;Save halftrack wanted for drv1.
|
|
RTNSETRK RTS
|
|
(BEAE)
|
|
|
|
(BDD7) DEC RECLBCNT ;Reduce recalibration counter.
|
|
BEQ DRVERR ;Error - exhausted recalib attempts.
|
|
LDA #4 ;Indicate 4 chances to reseek track
|
|
STA RSEEKCNT ;between recalibrations.
|
|
LDA #0 ;Seek track 0.
|
|
(BDE3) JSR SEEKTRK
|
|
|
|
(BE5A)
|
|
SEEKTRK .
|
|
.
|
|
(See dis'mbly above.)
|
|
.
|
|
.
|
|
(RTS)
|
|
|
|
(BDE6) PLA ;Get trk wanted from stack.
|
|
RESEEK JSR SEEKTRK ;Seek trk wanted.
|
|
|
|
(BE5A)
|
|
SEEKTRK .
|
|
.
|
|
(See dis'mbly above.)
|
|
.
|
|
.
|
|
(RTS)
|
|
|
|
(BDEA) JMP RESETCNT ;Go back to try & read again.
|
|
------------
|
|
|
|
* Got a good read.
|
|
(BDED)
|
|
RDRIGHT LDY TRKDSK ;(y)=trk# found in header.
|
|
CPY PRESTRK ;Trk found = trk wanted?
|
|
(BDF2) BEQ RTTRK ;Yes - seeked to correct trk.
|
|
|
|
* Bad seek (or some kind of protection scheme)
|
|
* because track wanted < > track found.
|
|
(BDF4) LDA PRESTRK ;Save trk wanted on stack.
|
|
PHA
|
|
TYA ;Set (a) = present trk = trk found.
|
|
(BDF9) JSR SETTRK ;Go select drive & put track wanted in
|
|
;memory location specific to drive.
|
|
|
|
(BE95)
|
|
SETTRK .
|
|
.
|
|
(See dis'mbly above.)
|
|
.
|
|
.
|
|
(RTS)
|
|
|
|
(BDFC) PLA ;Get trk wanted back off stack.
|
|
(BDFD) DEC RSEEKCNT ;Allow 4 attempts between recalibrations
|
|
;to find the correct trk.
|
|
(BE00) BNE RESEEK ;More attempts left to find track.
|
|
(BE02) BEQ DORECALB ;ALWAYS - go do a recalibration.
|
|
------------
|
|
|
|
* Got a drive error.
|
|
(BE04)
|
|
DRVERR PLA
|
|
LDA #$40 ;Error code for bad drive.
|
|
TOERRWTS PLP
|
|
(BE08) JMP RWTSERR
|
|
-----------
|
|
|
|
* Command was seek (null).
|
|
(BE0B)
|
|
WASEEK BEQ RWTSEXIT ;Not applicable when reading.
|
|
-----------
|
|
|
|
* Command was format ($04).
|
|
(BE0D)
|
|
FORMDSK JMP FORMAT ;Not applicable when reading.
|
|
----------
|
|
|
|
* Found the correct track for
|
|
* the READ or WRITE opcodes.
|
|
(BE10)
|
|
RTTRK LDY #3 ;Get vol wanted from IOB.
|
|
LDA (PTR2IOB),Y
|
|
PHA ;Save it on the stack.
|
|
LDA VOLDSK ;Get volume found and
|
|
LDY #14 ;save it on the IOB.
|
|
STA (PTR2IOB),Y
|
|
PLA ;Retrieve vol wanted off stk.
|
|
BEQ CRCTVOL ;Volume 0 good for all.
|
|
CMP VOLDSK ;Vol wanted = vol found?
|
|
(BE20) BEQ CRCTVOL ;Yes - got correct vol #.
|
|
|
|
* Got a volume mismatch.
|
|
(BE22) LDA #$20 ;Set code for vol mismatch.
|
|
(BE24) BNE TOERRWTS ;ALWAYS.
|
|
------------
|
|
|
|
* Found correct volume, so now
|
|
* check if sector is also correct.
|
|
(BE26)
|
|
CRCTVOL LDY #5 ;Get logical sector # wanted.
|
|
LDA (PTR2IOB),Y
|
|
TAY ;Set (y) = logical sector # wanted.
|
|
LDA PHYSECTR,Y ;Get # of physical sector wanted.
|
|
CMP SECDSK ;Phys sec wanted = phys sec found?
|
|
BNE REDUCERD ;No - go try again.
|
|
(BE32) PLP ;PULL OPERATION FROM STACK.
|
|
;Clr carry = write, set carry = read.
|
|
(BE33) BCC WRITE ;Branch if command was a write.
|
|
|
|
* Read data sector into RWTS's buffers.
|
|
(BE35) JSR READATA ;Go read a sector.
|
|
|
|
* Look for data prologue.
|
|
(B8DC)
|
|
READATA LDY #32 ;Set (y) to designate 32
|
|
REDUCEY DEY ;attempts to find data prologue.
|
|
BEQ ERRTN ;Error - couldn't find data prologue.
|
|
PRODATD5 LDA Q6L,X ;Get byte from data prologue.
|
|
BPL PRODATD5 ;Wait for a full byte.
|
|
VERSD5 EOR #$D5 ;Check if byte was a "D5".
|
|
BNE REDUCEY ;Wasn't a "D5, reduce counter.
|
|
NOP ;Stall 2 cycles.
|
|
PRODATAA LDA Q6L,X ;Read next data prologue byte.
|
|
BPL PRODATAA ;Wait for a full byte.
|
|
CMP #$AA ;Was it an "AA"?
|
|
BNE VERSD5 ;No - restart sequence.
|
|
(B8F4) LDY #86 ;Set (y) for later use
|
|
;in the read data routine.
|
|
(B8F6)
|
|
PRODATAD LDA Q6L,X ;Read next byte in data prologue.
|
|
BPL PRODATAD ;Wait for a full byte.
|
|
CMP #$AD ;Was it an "AD"?
|
|
(B8FD) BNE VERSD5 ;No - restart search sequence.
|
|
|
|
* Read first 86 bytes of data into
|
|
* RWTSBUF2 ($BC55 --> $BC00).
|
|
*
|
|
* Use disk byte as index to the
|
|
* NDX2NIBL table which contains
|
|
* offsets that we would be using
|
|
* if we were accessing a table
|
|
* of disk bytes when writing.
|
|
* (That is, we are just doing
|
|
* the opposite of writing.)
|
|
* EOR value from NDX2NIBL table
|
|
* with the previous EOR result.
|
|
* (On entry, use #$00 for previous
|
|
* EOR result.)
|
|
|
|
(B8FF) LDA #0 ;Initialize (a) for later EORing.
|
|
RDUCY DEY ;Reduce index to RWTSBUF2.
|
|
STY PROSCRTH ;Save index.
|
|
RDSKBYT LDY Q6L,X ;(y) = disk byte.
|
|
BPL RDSKBYT ;Wait for a full byte.
|
|
(B909) EOR NDX2NIBL-$96,Y ;Use (y) as index to tbl
|
|
; of 2-encoded nibbles.
|
|
(B90C) LDY PROSCRTH ;Store 2-encoded nibble in RWTSBUF2.
|
|
STA RWTSBUF2,Y
|
|
(B911) BNE RDUCY ;Conditioned from the "LDY PROSCRTH".
|
|
|
|
* Read rest of sector into RWTSBUF1
|
|
* ($BB00 --> $BBFF).
|
|
*
|
|
* Use disk byte as index to the
|
|
* NDX2NIBL table which contains
|
|
* offsets that we would be using
|
|
* if we were accessing a table
|
|
* of disk bytes when writing.
|
|
* (That is, we are just doing
|
|
* the opposite of writing.)
|
|
* EOR value from NDX2NIBL table
|
|
* with the previous EOR result.
|
|
|
|
(B913)
|
|
SAVYNDX STY PROSCRTH ;Save index to RWTSBUF1.
|
|
RDSKBYT2 LDY Q6L,X ;(y) = disk byte.
|
|
BPL RDSKBYT2 ;Wait for a full byte.
|
|
EOR NDX2NIBL-$96,Y ;Get 6-encoded nibble from tbl.
|
|
LDY PROSCRTH ;Get index to RWTSBUF1.
|
|
STA RWTSBUF1,Y ;Store 6-encoded nibble in RWTSBUF1.
|
|
INY
|
|
(B923) BNE SAVYNDX ;More disk bytes to read.
|
|
|
|
* Read the data checksum.
|
|
(B925)
|
|
RDCHECK LDY Q6L,X ;Get data checksum.
|
|
BPL RDCHECK ;Wait for full byte.
|
|
(B92A) CMP NDX2NIBL-$96,Y ;Converted checksum = val in $BBFF?
|
|
;Remember: Val in $#BBFF is result of
|
|
;previous cummulative EORing. There4,
|
|
;this comparison with (a) detects any
|
|
;(non-cancelling) error(s) that may
|
|
;have occurred in the entire sector!!!
|
|
(B92D) BNE ERRTN ;No - got an error.
|
|
;Hackers often change these two bytes
|
|
;to "CLC" and "RTS" instructions in order
|
|
;to defeat the data checksum and ignore
|
|
;the data epilogue.
|
|
|
|
* Read the first two bytes (only)
|
|
* of the data epilogue ("DE AA").
|
|
(B92F)
|
|
EPIRDDE LDA Q6L,X ;Read first byte of data epilogue.
|
|
BPL EPIRDDE ;Wait for a full byte.
|
|
CMP #$DE ;Was it a "DE"?
|
|
BNE ERRTN ;No - got an error.
|
|
NOP ;Stall for 2 cycles.
|
|
EPIRDAA LDA Q6L,X ;Read 2nd data epilogue byte.
|
|
BPL EPIRDAA ;Wait for a full byte.
|
|
CMP #$AA ;Was it an "AA"?
|
|
BEQ GOODRTN ;Yes - got a good read.
|
|
ERRTN SEC ;Signal bad read.
|
|
(B943) RTS ;Hackers often change the "SEC" to
|
|
============ ;a "CLC" in order to defeat error
|
|
;checking.
|
|
|
|
(B99E)
|
|
GOODRTN CLC ;Signal good read.
|
|
(B99F) RTS
|
|
=============
|
|
|
|
(BE38) PHP ;Save status of read on stack in case bad read.
|
|
(BE39) BCS REDUCERD ;Bad read - go try again.
|
|
;Leave set (c) on stack to denote reading.
|
|
;We previously pulled the saved status
|
|
;(denoting if reading or writing) off
|
|
;the stack. There4, we better put a set
|
|
;(c) back on stack because we are reading
|
|
;and are about to branch back to a routine
|
|
;that expects the read (c=1) or write (c=0)
|
|
;flag on the stack.
|
|
(BE3B) PLP ;Good read - not branching back, so no
|
|
;need to preserve flag on stack.
|
|
|
|
* Postnibble data & shut down.
|
|
(BE3C) LDX #0
|
|
STX PROSCRTH
|
|
(BE40) JSR POSTNB16
|
|
|
|
* Convert 6-&-2-encoded bytes in
|
|
* RWTS'S two buffers to normal
|
|
* memory bytes (usually placed in
|
|
* the DOS data sector buffer).
|
|
* (On entry: (x) = slot * 16,
|
|
* PTR2BUF points to data buffer.)
|
|
(B8C2)
|
|
POSTNB16 LDY #0
|
|
POSTNIB1 LDX #$56 ;(decimal #86.)
|
|
POSTNIB2 DEX
|
|
BMI POSTNIB1
|
|
LDA RWTSBUF1,Y ;Set (a) = 6-encoded byte.
|
|
LSR RWTSBUF2,X ;Put lower 2 bits of 2-encoded
|
|
ROL ;byte into original 6-encoded byte
|
|
LSR RWTSBUF2,X ;to get normal memory byte.
|
|
ROL
|
|
STA (PTR2BUF),Y ;Put normal memory byte in
|
|
INY ;RWTS's buffer (normally DOS
|
|
CPY PROSCRTH ;data sector buffer).
|
|
BNE POSTNIB2
|
|
(B8D8) RTS
|
|
|
|
(BE43) LDX SLOT5PG ;Set (x) = slot*16 from page 5.
|
|
|
|
* Signal success or failure & then shut down.
|
|
* Note: Several references erroneously state that
|
|
* the return code is zero if no errors occurrred.
|
|
* However, a lone seek command always sets the return
|
|
* code to zero. Even if a read or write operation
|
|
* is successful, the IOB return code will acquire a
|
|
* random value (as a result of accessing a hardware
|
|
* switch prior to entering this routine).
|
|
|
|
(BE46)
|
|
RWTSEXIT CLC ;Clr carry to signal successful operation.
|
|
HEX 24 ;"BIT $38" to skip "SEC" instruc below.
|
|
RWTSERR SEC ;Set carry to signal unsuccessful operation.
|
|
LDY #13 ;Store return code in IOB.
|
|
STA (PTR2IOB),Y
|
|
LDA MTROFF,X ;Turn motor off.
|
|
(BE50) RTS
|
|
|
|
(B7BA) BCS ERRENTER ;Branch if operation unsuccessful.
|
|
PLP ;Throw status off stk.
|
|
CLC ;Signal successful.
|
|
(B7BE) RTS
|
|
============
|
|
|
|
(B7BF)
|
|
ERRENTER PLP ;Throw status off stk.
|
|
SEC ;Signal UNsuccessful.
|
|
(B7C1) RTS
|
|
============
|
|
|
|
(B093) LDA IBSMOD ;Get vol found from IOB
|
|
STA VOLFM ;& put it in Fm parm list.
|
|
LDA #$FF ;Designate vol wanted in
|
|
(B09B) STA IBVOL ;IOB as 255 for next time.
|
|
;(Actually using 0 cause FF EOR FF = 0.)
|
|
(B09E) BCS ERRWTSDR ;Branch if UNsuccessful operation.
|
|
(B0A0) RTS
|
|
=============
|
|
|
|
* Operation was NOT successful.
|
|
(B0A1)
|
|
ERRWTSDR LDA IBSTAT ;Get RWTS'S error code.
|
|
|
|
* Translate IOB error code in (a) to
|
|
* FM error code in (y). (DOS later employs
|
|
* the FM error code in the routine used to
|
|
* print the error message.)
|
|
(B0A4) LDY #7 ;Set (y) for FM error code.
|
|
CMP #$20 ;Vol mismatch?
|
|
BEQ SETFMERR ;Yes.
|
|
LDY #$04 ;No.
|
|
CMP #$10 ;Write protected?
|
|
BEQ SETFMERR ;Yes.
|
|
LDY #8 ;Must have been other, so
|
|
SETFMERR TYA ;designate as general I/O error.
|
|
(B0B3) JMP BADFMXIT ;Go handler error.
|
|
------------
|
|
|
|
(B37F)
|
|
BADFMXIT SEC ;(c) = 1 to signal UNsuccessful.
|
|
FMEXIT PHP ;Preserve success of operation on stk.
|
|
STA RTNCODFM ;Put appropriate return code in FM parm list.
|
|
LDA #0 ;Avoid that infamous $48 bug.
|
|
STA STATUS
|
|
(B38E) JSR CPYFMWA
|
|
|
|
(AE7E)
|
|
CPYFMWA JSR SELWKBUF
|
|
|
|
* Point the A4L/H pointer at the DOS work buffer (chain).
|
|
(AF08)
|
|
SELWKBUF LDX #0 ;Designate work area buffer.
|
|
(AF0A) BEQ PT2FMBUF ;ALWAYS.
|
|
|
|
(AF12)
|
|
PT2FMBUF LDA WRKBUFFM,X ;Get addr of DOS's work buffer
|
|
STA A4L ;(chain) from FM parm list and put
|
|
LDA WRKBUFFM+1,X ;it in the A4L/H pointer.
|
|
STA A4L+1
|
|
(AF1C) RTS
|
|
|
|
* Copy work area buffer (non-chain)
|
|
* to DOS work buffer (chain).
|
|
(AE81) LDY #0
|
|
STORWRK LDA FMWKAREA,Y
|
|
STA (A4L),Y
|
|
INY
|
|
CPY #45
|
|
BNE STORWRK
|
|
(AE8D) RTS
|
|
|
|
(B391) PLP ;Exit with (c) conditioned accordingly.
|
|
LDX STKSAV ;Reset the stack pointer so execution will
|
|
TXS ;return to the caller of the function.
|
|
(B396) RTS ;(Normally returns to AFTRFUNC ($A6AB)
|
|
============ ;located in the FMDRIVER routine ($A6A8).)
|