;
; File: CC.PROM.FB.TEXT
; Date: 21-July-83
;       Keith Ball
;
; Floppy driver for the new controller.
;
;
; ALL REGISTER VALUES ON ENTRY ARE SAVED AND RESTORED EXCEPT D0, D7 & A0
; INTERNAL REGISTER USEAGE :
;
;        D0 = temporary register
;        D1 = temporary register
;        D2 = count of bytes user wants then number of bytes in
;              partial sector - if no partial then = sector size
;        D3 = temporary register
;        D4 = # of sectors to read/write (loop counter)
;        D5 = starting block then current logical sector number
;        D6 = count of bytes in command or result array
;        D7 = current IOresult code
;
;        A0 = temporary register
;        A1 = temporary register
;        A2 = pointer to local temporary variables (even word boundary)
;        A3 = pointer to user's buffer or parameter block
;        A4 = workstaton ram pointer
;        A5 = Slot base address
;        A6 = Static ram Base pg pointer - local data
;
  page
;
;       FBboot -- Rev B floppy disk boot processing
;
FBboot
        move.b  d0,CPbtslot.w       ;set boot slot number
        move.b  d0,CPosslot.w       ;set OS slot number
        clr.b   CPbtsrvr.w          ;set boot server number
        pea     FBblkIO             ;set boot disk blk i/o subr pointer
        move.l  (sp)+,CPblkio.w     ;*
        pea     FBsctIO             ;set boot disk sector i/o subr pointer
        move.l  (sp)+,CPdskio.w     ;*
        moveq   #0,d0               ;
        move.b  d0,CPossrvr.w       ;set OS server number
        move.w  d0,CPosblk+1.w      ;set OS volume block number
        move.b  d0,CPosdrv.w        ;set OS volume drive number
        clr.l   d1                  ;access drive 0
        bsr     FBinit              ;initialize floppy drive
        blt.s   FBboot9             ;just return if error
                                    ;
FBboot1 move.l  #USRbase,a0         ;get block buffer pointer
        moveq   #0,d0               ;1st boot block
        move.w  d0,d1               ;drive number
        moveq   #DskRead,d5         ;get read block function code
                                    ;
        bsr.s   FBblkIO             ;read block 0 of boot code
        blt.s   FBboot9             ;just return if error
        bsr.s   FBblkIO             ;read block 1 of boot code
        blt.s   FBboot9             ;just return if error
        move.l  #USRbase,a0         ;get block buffer pointer
                                    ;
FBboot9 rts                         ;return
  page
;
; FBblkIO - Read a Rev B floppy controller disk block subroutine
;
;       Enter:  A0.L - Buffer address
;               D0.W - Block number
;               D1.W - Drive number
;               D5.W - Read ($32) or Write ($33) command
;
;       Exit:   A0.L - Next free location in buffer
;               D0.W - Updated block number
;               D7.W - IORESULT
;
;       All other registers are preserved.
;
FBblkIO  MOVEQ     #IOEioreq, D7                 ;assume not read
         CMPI.W    #DskRead, D5                  ;is it a read?
         BNE.S     FBBerror                      ;no, exit

         CLR.L     D7
         MOVEM.L   D0-D6/A1-A6,-(SP)             ;setup regs for driver
         MOVE.L    A0, A3                        ;put buffer adr and
         MOVE.W    D0, D5                        ;block number in right regs
         MOVE.W    #BLKSZ, D2                    ;read 1 block
         SUBA.W    #LocalFrame, SP               ;allocate local stack frame
         MOVE.L    SP, A2                        ;must be even # of bytes
         MOVE.B    D1, DrvNumb(A2)               ;save drive number

; Get data Ram(A6), Slot(A5), and workstation Ram(A4) pointers
;
         BSR       FBbaseAdr

         BSR       FBread                        ;
         ADDA.W    #LocalFrame, SP               ;release local stack frame
         MOVE.L    A3, A0                        ; save next free location
         MOVEM.L   (SP)+,D0-D6/A1-A6             ;restore registers
         ADDQ.W    #1,D0                         ;INC BASE BLOCK
FBBerror TST.W     D7                            ;set return condition code
         RTS
  page
;
; FBsctIO - Read a Corvus floppy disk sector
;
;       Enter:  A0.L - Buffer address
;               D1.W - Drive number
;               D3.B - Track number
;               D4.B - Sector number
;               D5.W - Read ($32) or Write ($33) command
;               D6.B - side number (0 or 1)
;
;       Exit:   D7.W - IORESULT
;
;       All other registers are preserved.
;
FBsctIO  MOVEM.L   D0-D6/A0-A6,-(SP)             ;save registers
         MOVE.L    A0, A3                        ;put buffer adr in right reg
         SUBA.W    #LocalFrame, SP               ;allocate local stack frame
         MOVE.L    SP, A2                        ;must be even # of bytes
         MOVE.B    D1, DrvNumb(A2)               ;save drive number
         MOVE.B    D3, Ptrack(A2)                ;save track number
         MOVE.B    D6, Pside(A2)                 ;save side
         MOVE.L    D4, D5                        ;keep sector number

; Get data Ram(A6), Slot(A5), and workstation Ram(A4) pointers
;
         BSR       FBbaseAdr                     ; on Entry: D0 = unit number

         MOVEQ     #DMARead, D0                  ;use dma in read direction
         BSR       FBMtrON                       ;turn on motor and setup controller

; check for disk change
;
         BSR       FBdChng
         BNE.S     FBsio9                        ;I/O error, turn motor off & exit

         BSR       FBseek                        ;get to track
         BNE.S     FBsio9                        ;if error, return

FBsio2   BSR       FBsecRd                       ;read sector
         BNE.S     FBsio9                        ;failed
         BSR       FBmvOUT                       ;move data to user's space

FBsio9   BSR       FBmtrOFF                      ;turn off motor
         ADDA.W    #LocalFrame, SP               ;release local stack frame
         MOVEM.L   (SP)+,D0-D6/A0-A6             ;restore registers
         TST.W     D7                            ;set return condition code
         RTS
  page
; FBinit - init device ram and internal variables.
;          initialize controller
;        Entry: D1.W - Drive number
;
; Get local data(A2), data Ram(A6), Slot(A5), and workstation Ram(A4) pointers
;
FBinit   SUBA.W    #LocalFrame, SP               ;allocate local stack frame
         MOVE.L    SP, A2                        ;must be even # of bytes
         MOVE.B    D1, DrvNumb(A2)               ;save drive number
         BSR       FBbaseAdr

; Put default values in device table entry
;
         BSR.S     FBsltinit

; Bug in FDC - must specify step rate 1 ms. faster than want it
;
         MOVE.W    #4, (A6)                      ;3 millisecond step rate
         MOVE.L    #FDDTimeOut, RdyTO(A6)        ;init ready timeout counter
         CLR.W     FBflags(A6)                   ;clear all flags
         BSR.S     FBctlinit                     ;init controller
         BNE.S     FBIerror                      ;if failed try to recover

; restore
;
         MOVEQ     #DMARead, D0                  ;use dma in read direction
         BSR       FBMtrON                       ;turn on motor and wait for Read
         BNE.S     FBIexit1                      ;timed out waiting for ready
         BSR       FBrstor                       ;restore to track 0
         BNE.S     FBIerror                      ;failed, D7 has error code

; do read id and sense drive status to get disk and drive info
;
         BSR       FBdrvInfo                     ;assumes motor is on

FBIexit1 BSR       FBmtrOFF                      ;turn off motor and deselect dri
FBIexit  ADDA.W    #LocalFrame, SP               ;release stack frame
         TST.W     D7
         RTS

; possible serious I/O error reset chip again to try to recover
;
FBIerror BSR.S     FBctlinit                     ;init controller also turns off
         BEQ.S     FBIexit                       ;if ok exit
         BSR.S     FBintFDC                      ;else reset controller and leave
         BRA.S     FBIexit

; FBctlinit - init the FDC and do a Specify command
;
FBctlinit
         BSR.S     FBintFDC                      ;reset the FDC
         BRA       FBspecify                     ;do a specify command
  page
;
;FBintFDC - reset the floppy disk controller
;        Entry : (A5) = Slot base address
;
FBintFDC MOVE.B    #MRstFDC, FDClocl(A5)         ;reset FDC
         MOVE.B    #MMtrOFF, FDClocl(A5)         ;set it back to normal
         RTS

; FBsltinit - init variables based on floppy type
;
FBsltinit
         MOVE.B    #D8TYPE, CPftyp.W             ; single density and 1 side
         MOVE.B    #D8TRKSID, CPftps.W           ; default TRACKS PER SIDE
         MOVE.B    #D8SECTRK,CPfspt.W            ; default SECTORS PER TRACK
         MOVE.W    #D8SctrLn, CPfbps.W           ; default sector length

         BTST      #LSeight, FDClocl(A5)         ;is it 8 inch?
         BON.S     FBSINex                       ;yes, exit else do for 5 1/4 inch

         MOVE.B    #D5TYPE, CPftyp.W             ; double density and 2 sides
         MOVE.B    #D5TRKSID, CPftps.W           ; default TRACKS PER SIDE
         MOVE.B    #D5SECTRK,CPfspt.W            ; default SECTORS PER TRACK
         MOVE.W    #D5SctrLn, CPfbps.W           ; default sector length
FBSINex  RTS
  page
;
; FBread - read block of floppy
;
; Turn on motor
;
FBread   MOVEQ     #DMARead, D0                  ;use dma in read direction
         BSR       FBMtrON
         BNE.S     FBRmtrOFF                     ;drive not come ready

; check for disk change
;
         BSR.S     FBdChng
         BNE.S     FBRmtrOFF                     ;I/O error, exit

; get starting sector, track and side and # of sectors to read
;
         BSR       FBstSTS
         BNE.S     FBRmtrOFF                     ;invalid block number
         BSR       FBnumbSctr
         SUBQ.W    #1, D4                        ;turn into loop counter

; seek to track and build interleave table
;
         BSR       FBseek
         BNE.S     FBRmtrOFF

; for i := 1 to (# of sectors to read) do
; read sector, move data to user's buffer, update sector,track and side
;
FBRread  BSR.S     FBsecRd                       ;read sector
         BNE.S     FBRmtrOFF                     ;I/O error exit
         BSR       FBmvOUT                       ;move data to user's space
         BSR       FBupSTS                       ;update sector,track and side
         DBNE      D4, FBRread                   ;do until(bad block) or (read al

; turn motor off and exit
;
FBRmtrOFF
         BRA       FBmtrOFF
  page
;common functions - put in groups so can make include files if necessary
;routines to do macro I/O operations

;
; FBdChng - check if disk has changed.  If it has then find out diskette
;           state and restore the drive.
; ASSUMES motor is alreay on and up to speed.
;
;        Exit  : (NE) = failed, I/O error
;                (EQ) = worked
;
FBdChng  BTST      #LSdskchng, FDClocl(A5)       ;disk changed?
         BOFF.S    FBDCexit                      ;no, exit (cc = EQ)

; restore drive
;
         BSR       FBrstor                       ;restore to track 0
         BNE.S     FBDCexit                      ;failed, D7 has error code

; get diskette info
; Drop into drive info routine.
  page
; FBdrvInfo  - get sector length, density and number of sides.
;              get sides from sense drive status command and get sector
;              length and density from read id command.
;        try read id with current density if fails flip density flag.
;        if that fails report error and stop.
;
;        Exit  : (NE) = failed, I/O error or no density????
;                (EQ) = worked
; do read id to find density
;
FBdrvInfo
         MOVEQ     #3, D0                        ;try each density at least twice

FBDIagn  MOVE.W    D0, -(SP)                     ;save retry count
         MOVEQ     #Mside0, D3                   ;need look at side 0 only
         BSR       FBrdid                        ;if works will clear D7
         BEQ.S     FBDIok                        ;did command
         BRA.S     FBDIex1                       ;timedout, error exit

FBDIok   BSR       FBrdidST                      ;see if command worked
         BEQ.S     FBDIsav                       ;worked, save state
         BCHG      #TYPdnsty, CPftyp.W         ;try other density
         MOVE.W    (SP)+, D0                     ;get retry count
         DBRA      D0, FBDIagn                   ;do until no more retries
         BRA.S     FBDIext                       ;failed, exit

;save sector length from read id result
;
FBDIsav  BSR       FBcnvSLCd                     ;convert sector length code and
         BNE.S     FBDIex1                       ;sector size to large error

; see if drive has side 1
;
         MOVEQ     #Mside0, D0                   ;need look at side 0 only
         BSR       FBsnsDrvSt                    ;do drive status to get # of sid
         BNE.S     FBDIex1                       ;timed out, exit
         BSET      #TYPsides, CPftyp.W         ;assume single sided
         BTST      #ST32sid, (A2)                ;test for number of sides
         BOFF.S    FBDIex1                       ;is single sided (1=2 sided)

; make sure can read side 1
;
         MOVEQ     #Mside1, D3                   ;look at side 1
         BSR       FBrdid                        ;if works will clear D7
         BNE.S     FBDIex1                       ;command failed timeout, exit
         BSR       FBrdidST                      ;see if command worked
         BNE.S     FBDIclr                       ;failed leave as 1 sided
         BCLR      #TYPsides, CPftyp.W         ;is double sided
FBDIclr  CLR.L     D7                            ;if read of side 1 failed - igno

FBDIex1  MOVE.W    (SP)+, D0                     ;remove D0 from stack
FBDIext  TST.W     D7                            ;show error state
FBDCexit RTS
  page
;
; FBsecRd    - read 1 sector.  Finds disk parameters if read function
;              returns an "UNFORMATTED" error.
;
;        Entry : (D5) = logical sector number
;                (A2) = pointer to local stack frame
;        Exit  : (NE) = failed, I/O error
;                (EQ) = worked
;
FBsecRd
         MOVEQ     #RTRYread, D0                 ;read sector retry count

; if read fails and get "UNFORMATTED" error then retry with other density
;
FBSRrtry MOVE.W    D0, -(SP)                     ;save retry count
         CLR.L     D7                            ;make sure error code cleared

         MOVEQ     #CMDread, D0                  ;get in command
         BSR       FBrd1                         ;do read operation
         BNE.S     FBSRex1                       ;timedout, error exit

         BSR       FBreadST                      ;see if worked
         BEQ.S     FBSRex1                       ;read sector, all done
         CMPI.W    #IOEnfmtd, D7                 ;not formatted error
         BNE.S     FBSRnot                       ;no, retry with same density
         BSR.S     FBdrvInfo                     ;get drive parameters
         BNE.S     FBSRext                       ;maybe drive is not formatted

FBSRnot  MOVE.W    (SP)+, D0                     ;get retry count
         DBRA      D0, FBSRrtry                  ;do until no more retries then
         BRA.S     FBSRext                       ;exit with error in D7

FBSRex1  MOVE.W    (SP)+, D0                     ;remove retry count from stack
FBSRext  TST.W     D7                            ;show error condition
         RTS
  page
;FDC, FDD commands
;
; FBspecify  - do a specify command to FDC
;        Entry : (A2) = pointer to command and result array
;                (A4) = prom data space pointer
;                (A5) = Slot base address
;                (A6) = Static ram Base pg pointer - local data
;        Exit  : (NE) = failed
;                (EQ) = completed
;
FBspecify
         MOVE.L    A2, A0                        ;save addr of cmd array
         MOVE.B    #CMDspcfy, (A0)+              ;put specify in command buffer

; 2nd byte is step rate code in bits D7 to D4 inclusive, HUT in D3 to D0 inclusiv
; 3rd byte is HLT code in bits D7 to D1 and NOT DMA in bit D0
;
         MOVE.B    #HLT8code+NDMAcode, D3        ;HLT code is pre-shifted, assume 8 inch
         MOVEQ     #16, D1                       ;step rate code goes
         MOVE.W    (A6), D0                      ;from F to 0 where F = 1 ms.,
                                                 ;E = 2 ms., D = 3 ms. and so on
         BTST      #LSeight, FDClocl(A5)         ;is it 8 inch?
         BON.S     FBSPis8                       ;yes, leave step as is else step div 2
         LSR.W     #1, D0                        ;5 goes 2,4,6,..,32 milliseconds
         MOVE.B    #HLT5code+NDMAcode, D3        ;use 5 inch HLT code

FBSPis8  SUB.B     D0, D1                        ;formula = 16 - step rate
         LSL.B     #4, D1                        ;put step rate in correct bits
         ORI.B     #HUTcode, D1                  ;add head unload time code
         MOVE.B    D1, (A0)+                     ;put 2nd byte in command array
         MOVE.B    D3, (A0)                      ;put 3rd byte in command array

         MOVEQ     #3-1, D6                      ;specify command is 3 bytes
         BRA       FBwritCtl                     ;write command array to cntrllr
                                                 ;FBwritCtl sets cc for error
  page
; FBsnsIntSt - do a Sense Interrupt Status command to FDC.  Find out cause of
;               interrupt.
;        Entry : (A2) = pointer to command and result array
;                (A4) = prom data space pointer
;                (A5) = Slot base address
;                (A6) = Static ram Base pg pointer - local data
;        Exit  : (NE) = failed
;                (EQ) = completed
;                IF completed THEN at (A2) is result array of command
;                                          (2 bytes)
;
FBsnsIntSt
         MOVE.B    #CMDsnsint, (A2)              ;put command byte in buffer
         MOVEQ     #1-1, D6                      ;only 1 byte to command
         BSR       FBwritCtl                     ;send command
         BNE.S     FBSISex                       ;if timed out exit
         MOVEQ     #RSLTsnsint-1, D6             ;length of result array
         BSR       FBctlRead                     ;read result
FBSISex
FBSDSex  RTS

; FBsnsDrvSt  - do a Sense Drive Status command to FDC.
; ASSUMES motor is on and up to speed.
;        Entry : (A2) = pointer to command and result array
;                (A4) = device table entry pointer
;                (A5) = Slot base address
;                (A6) = Static ram Base pg pointer - local data
;                (D0) = side select: side 1=Mside1, side 0=Mside0
;        Exit  : (NE) = failed
;                (EQ) = completed
;                IF completed THEN at (A2) is result array of command
;                                          (1 byte: ST3)
;
FBsnsDrvSt
         MOVE.L    A2, A0                        ;save addr of cmd array
         MOVE.B    #CMDsnsdrv, (A0)+             ;put command byte in buffer

; 2nd byte:  bit D2 is side and bits D1 and D0 are the device select
;
         OR.B      DrvNumb(A6), D0               ;put side select in drive number
         MOVE.B    D0, (A0)                      ;put in command array

         MOVEQ     #2-1, D6                      ;2 bytes to command
         BSR       FBwritCtl                     ;send command
         BNE.S     FBSDSex                       ;if timed out exit

         MOVEQ     #RSLTsnsdrv-1, D6             ;length of result array
         BRA       FBctlRead                     ;read result
  page
;
; FBrstor - do a recalibrate command to FDC.  Puts head on Track 0.
; ASSUMES motor is on and up to speed.
;        Entry : (A2) = pointer to command and result array
;                (A4) = device table entry pointer
;                (A5) = Slot base address
;                (A6) = Static ram Base pg pointer - local data
;        Exit  : (NE) = failed
;                (EQ) = completed
;
FBrstor  MOVEQ     #RTRYrstr, D0                 ;restore retry count

FBRSrtry MOVE.W    D0, -(SP)                     ;save retry count
         MOVE.L    A2, A0                        ;save addr of cmd array
         MOVE.B    #CMDrclbrt, (A0)+             ;put recalibrate in command buff

; 2nd byte is drive selected in bits D1 and D0, the other bits are 0
;
         MOVE.B    DrvNumb(A2), (A0)             ;get drive number, put in command array
         MOVEQ     #2-1, D6                      ;2 bytes in command array

         BSR       FBwritCtl                     ;write command array to controller
         BNE.S     FBRSexit                      ;command timed out

; wait for command to complete
;
FBRSwait BSR       FBw8FDC
         BNE.S     FBRSexit                      ;should there be a timeout on cm

; do Sense Interrupt Status command to get command results
;
         BSR.S     FBsnsIntSt
         BNE.S     FBRSexit

; first byte of result array (pointed at by A2) is ST0, see if seek end is good
;
         BSR       FBrstrST                      ;find out type of error
         BCS.S     FBRSwait                      ;wrong drive wait for correct one
         BEQ.S     FBRSexit                      ;was good, so exit
         MOVE.W    (SP)+, D0                     ;get retry count
         DBRA      D0, FBRSrtry                  ;do until no more retries then
         BRA.S     FBRSext1                      ;return failuere

FBRSexit MOVE.W    (SP)+, D0                     ;remove retry count from stack
FBRSext1 TST.W     D7                            ;show error condition
         RTS
  page
;
; FBseek - do a seek to track command to FDC.
; ASSUMES motor is on and up to speed.
;        Entry : (A2) = pointer to local stack frame
;                (A4) = device table entry pointer
;                (A5) = Slot base address
;                (A6) = Static ram Base pg pointer - local data
;                physical side and track setup
;        Exit  : (NE) = failed
;                (EQ) = completed
;
FBseek   MOVEQ     #RTRYseek, D0                 ;restore retry count

FBSKrtry MOVE.W    D0, -(SP)                     ;save retry count
         MOVE.L    A2, A0                        ;save addr of cmd array
         MOVE.B    #CMDseek, (A0)+               ;put seek cmd in command buffer

; 2nd byte is drive selected in bits D1 and D0 and head select in D2
;
         BSR       FBdselHD                      ;get device and head select byte

         MOVE.B    Ptrack(A2), (A0)              ;3rd byte: track wanted

         MOVEQ     #3-1, D6                      ;2 bytes in command array
         BSR       FBwritCtl                     ;write command array to controll
         BNE.S     FBSKexit                      ;command timed out

; wait for command to complete
;
FBSKwait BSR       FBw8FDC
         BNE.S     FBSKexit                      ;should there be a timeout on cm

; do Sense Interrupt Status command to get command results
;
         BSR       FBsnsIntSt
         BNE.S     FBSKexit

; see if seek end is good
;
         BSR       FBskSTAT                      ;find out type of error
         BCS.S     FBSKwait                      ;wrong drive wait for correct one
         BEQ.S     FBSKexit                      ;was good, so exit
         DBRA      D0, FBSKrtry                  ;do until no more retries then
         BRA.S     FBSKext1                      ;report error

FBSKexit MOVE.W    (SP)+, D0                     ;remove retry count from stack
FBSKext1 TST.W     D7                            ;show error condition
         RTS
  page
;
; FBrdid - do a read id command to FDC.
; ASSUMES motor is on and up to speed.
;        Entry : (A2) = pointer to command and result array
;                (A4) = device table entry pointer
;                (A5) = Slot base address
;                (A6) = Static ram Base pg pointer - local data
;                (D3) = side select: side 1=Mside1, side 0=Mside0
;        Exit  : (NE) = failed
;                (EQ) = completed
;
FBrdid   MOVE.L    A2, A0                        ;save addr of cmd array
         MOVEQ     #CMDredID, D0                 ;get in command
         BSR       FBadDnsty                     ;combine command and MFM/FM bit

; 2nd byte:  bit D2 is side and bits D1 and D0 are the device select
;
         MOVE.B    DrvNumb(A6), D1               ;get drive number
         OR.B      D3, D1                        ;put in side select
         MOVE.B    D1, (A0)                      ;put in command array

         MOVEQ     #2-1, D6                      ;2 bytes to command
         BSR       FBwritCtl                     ;send command
         BNE.S     FBRIext                       ;if timed out exit

; wait for command to complete
;
         BSR       FBw8FDC
         BNE.S     FBRIext                       ;rts on next page

; get results of operation
;
         MOVEQ     #RSLTredID-1, D6              ;length of result array
         BRA       FBctlRead                     ;read result
  page
;
; FBrd1 - do 1 read or write sector operation.
; ASSUMES motor is on and up to speed and on correct track.
;        Entry : D0   = command byte
;                (A2) = pointer to command and result array
;                (A4) = device table entry pointer
;                (A5) = Slot base address
;                (A6) = Static ram Base pg pointer - local data
;                D5 = logical sector number
;        Exit  : (NE) = failed, time out
;                (EQ) = completed
;
FBrd1    MOVE.L    A2, A0                        ;save addr of cmd array
         BSR.S     FBsetrdcmd                    ;setup the rest of the cmd array
         BNE.S     FBR1exit                      ;invalid sector length error

         BSR       FBbufCntr                     ;setup controller counter

; send command
;
         MOVEQ     #RWcmdlen-1, D6               ;9 bytes to command
         BSR       FBwritCtl                     ;send command
         BNE.S     FBR1exit                      ;if timed out exit

; wait for command to complete
;
         BSR       FBw8FDC
         BNE.S     FBR1exit                      ;should there be a timeout on cm

; get results of operation
;
         MOVEQ     #RSLTrdwr-1, D6               ;length of result array
         BSR       FBctlRead                     ;read result
FBRIext
FBR1exit RTS
  page
;command operations support functions

;
; FBsetrdcmd - setup common portions of Read and Write command arrays.
;
;        Entry : A0 = points at 2nd byte of command array
;                A2 = local stack frame pointer
;                A4 = device table entry pointer
;                D0 = command byte
;                D5 = logical sector number
;        Exit  : (NE) = invalid length error
;                (EQ) = got code
;
; 2nd byte:  bit D2 is side and bits D1 and D0 are the device select
;
FBsetrdcmd
         BSR.S     FBadDnsty                     ;expects D0 to have command byte
         BSR.S     FBdselHD                      ;device sel and head sel byte

         MOVE.B    Ptrack(A2), (A0)+             ; 3rd byte: track number
         MOVE.B    Pside(A2), (A0)+              ; 4th byte: head number
         MOVE.B    D5, (A0)+                     ; 5th byte: sector number

         BSR.S     FBcnv2slc                     ; 6th byte: sector size code
         BNE.S     FBSCexit                      ; invalid length
         MOVE.B    D0, (A0)+

         MOVE.B    CPfspt.W, (A0)+               ; 7th byte: EOT

         EXT.W     D0                            ; 8th byte: GPL, gap 3 length co
         MOVE.B    GPLtable(D0.W), (A0)+

         MOVEQ     #-128, D1                     ; 9th byte: DTL
         TST.B     D0                            ;use a DTL value ($80) if code
         BEQ.S     FBSCmin                       ;is 0, = 128 bytes sector
         MOVEQ     #-1, D1                       ;use FF if length code > 0
FBSCmin  MOVE.B    D1, (A0)+

FBSCexit TST.W     D7
         RTS

; Gap 3 length code translation table
;
GPLtable DATA.B    $07                 ;for  128 byte sectors
         DATA.B    $0E                 ;for  256 byte sectors
         DATA.B    $1B                 ;for  512 byte sectors
         DATA.B    $35                 ;for 1024 byte sectors
  page
;
; FBdselHD - put device and head select values in byte of command array
;        Entry : A0 = pointer to byte in command array
;
FBdselHD
         MOVE.B    Pside(A2), D0                 ;get head select
         LSL.B     #2, D0                        ;put in correct position
         OR.B      DrvNumb(A6), D0               ;add drive select
FBdsel10 MOVE.B    D0, (A0)+                     ;put in command array
         RTS
;
; FBadDnsty - add density bit to command
;CANNOT use D3
;        Entry : D0 = command byte
;                A0 = pointer to byte in command array
;
FBadDnsty
         MOVEQ     #MfmBit, D1                   ;assume is FM
         BTST      #TYPdnsty, CPftyp.W           ;is it FM?
         BON.S     FBADfm                        ;yes (1=FM)
         MOVEQ     #Mmfmbit, D1                  ;no then use MFM
FBADfm   OR.B      D1, D0                        ;combine command and MFM/FM bit
         BRA.S     FBdsel10                      ;put it in command array
  page
; FBcnv2slc - convert sector length to sector length code.
;CANNOT use A0
;        Entry : (A4) = device table entry pointer
;        Exit  : (D0) = sector length code
;                (NE) = invalid length error
;                (EQ) = got code
;                             FM        MFM
;        sector lengths      code      code
;               128           00        --
;               256           01        01       controller's buffer is 512 bytes
;               512           02        02       therefore it can not read/write
;              1024           --        03       1024 byte sectors
;
FBcnv2slc
         MOVE.W    CPfbps.W, D1                ;get sector length in device tab
         CLR.B     D0                            ;start with code = 0
         LSR.W     #7, D1                        ;put 4 possible bits in low orde

FBC2find LSR.W     #1, D1                        ;shift out next bit
         BCS.S     FBC2tst                       ;if set then D0 = correct code
         BEQ.S     FBC2err                       ;if word clear then invalid leng
         ADDQ.B    #1, D0                        ;do next code
         BRA.S     FBC2find

FBC2tst  CMPI.B    #2, D0                        ;is code in range - 512 <= sector
         BLS.S     FBC2exit                      ;yes
FBC2err  MOVEQ     #IOEinvsct, D7                ;invalid sector length error

FBC2exit TST.W     D7                            ;show error status
         RTS
;
; FBcnvSLCd - convert sector length code to a number and save in device table.
;        Entry : (A2) = address of result array from READ ID command
;                (A4) = device table entry pointer
;        Exit  : (NE) = sector to long
;                (EQ) = ok
;                             FM        MFM
;        sector length       code      code
;               128           00        --
;               256           01        01       controller's buffer is 512 bytes
;               512           02        02       therefore it can not read/write
;              1024           --        03       1024 byte sectors
;
FBcnvSLCd
         MOVE.W    #SLfmLow, D0                  ;get min. sector length(128)
         CLR.W     D1                            ;make sure high order byte is cl
         MOVE.B    RSLTsctrl(A2), D1             ;get sector length code
         LSL.W     D1, D0                        ;length = multiply of integral o
         MOVE.W    D0, CPfbps.W                  ;save length in device table
         MOVE.B    D1, D0                        ;check if value is greater than
         BRA.S     FBC2tst                       ;512 byte sectors
  page
; FBbaseAdr   - Get static ram base pg address for this slot, get slot base
;               address and device table entry address
;
;
;        Exit : A5 = Slot base address
;               A6 = Static ram Base pg pointer - local data
;
FBbaseAdr
;
; FBSlotRam - calculate base pg address of static ram data area
;
;        Exit  : (A6) = Base pg address (a page = 256 bytes)
;
FBSlotRam
         LEA       CPsl1ram.W, A6                ;address of I/O slot Static Ram
         MOVE.B    CPbtslot.W, D0                ;Slot number
         EXT.W     D0                            ;base :=
         MOVE.W    D0, D1
         SUBQ.W    #1,D0                         ;   ((slot - 1) *
         MULU      #DBPoffst, D0                 ;   (pg length)) +
         ADDA.L    D0, A6                        ;   (Slot Static ram base address)
;
;FBSltAdr - Fetches the slot base address.
;
;        Entry : (D1) = slot number
;        Exit  : (A5) = Slot base address
;
FBSltAdr LEA       IOSbase.L, A5                 ;slot 0 BaseBadrs
         MULU      #IOSoffst, D1                 ;slot * offset + slot 0 base
         ADDA.L    D1, A5                        ;equals this slot's base address
         RTS
  page
;
; FBctlRead - read n bytes from controller into command/result buffer
;
;        Entry : (A2) = pointer to command/result array
;                (A5) = slot base address
;                (D6) = number of bytes to read - 1
;        Exit  : (D6) = number of bytes not sent - 1
;                (NE) = timed out and array AND (D6) are invalid
;                (EQ) = worked array is valid
;
FBctlRead
         MOVE.L    A2, A0                        ;get address of array

; while (not timeout) and (haven't sent all the bytes) do
;
FBCRloop MOVE.W    #FDCTimeOut, D0               ;time out counter

;repeat until(timeout) or (FDC is ready)
;
FBCRrdy  BTST      #FDCready, FDCstat(A5)        ;Ready := FDC is ready
         SNE       D3                            ;         and
         BTST      #DataDir, FDCstat(A5)         ;         Data direction is READ
         SNE       D1
         AND.B     D3, D1
         BNE.S     FBCRok                        ;Ready so stop waiting
         SUBQ.W    #1, D0                        ;count down timeout
         BMI.S     FBCRrdy                       ;no, check again
         MOVEQ     #IOEflpto, D7                 ;timeout error
         BRA.S     FBCRexit                      ;timed out so exit (also NE)

FBCRok   MOVE.B    FDCdata(A5), (A0)+            ;get data from reg into array
         DBRA      D6, FBCRloop                  ;see if more to get
         CLR.L     D7                            ;got all the data show no error
FBCRexit RTS
  page
;
; FBwritCtl - write n bytes to controller from command/result buffer
;
;        Entry : (A2) = pointer to command/result array
;                (A5) = slot base address
;                (D6) = number of bytes to write - 1
;        Exit  : (NE) = timed out and did not finish sending array
;                (EQ) = worked array is sent
;
FBwritCtl
         MOVE.L    A2, A0                        ;get address of array

; while (not timeout) and (haven't sent all the bytes) do
;
FBWCloop MOVE.W    #FDCTimeOut, D0               ;time out counter

;repeat until(timeout) or (FDC is ready)
;
FBWCrdy  BTST      #FDCready, FDCstat(A5)        ;Ready := FDC is ready
         SNE       D3                            ;         and
         BTST      #DataDir, FDCstat(A5)         ;         Data direction is WRIT
         SEQ       D1
         AND.B     D3, D1
         BNE.S     FBWCok                        ;Ready so stop waiting
         SUBQ.W    #1, D0                        ;count down timeout
         BNE.S     FBWCrdy                       ;no, check again
         MOVEQ     #IOEflpto, D7                 ;timeout error
         BRA.S     FBWCexit                      ;timed out so exit (also NE)

FBWCok   MOVE.B    (A0)+, FDCdata(A5)            ;get data from array into reg

;FDC will not give a valid ready state for 12 microseconds after
;after issuing the COMMAND byte of the command array
;code is probably slow enough to compensate w/o a wait loop (????)
;;         MOVEQ     #5, D1                        ;wait 12 microsecs. for RQM
;;WRCwait  DBRA      D1, WRCwait                   ;to settle to correct state

         DBRA      D6, FBWCloop                  ;see if more to get
         CLR.L     D7                            ;got all the data show no error
FBWCexit RTS
  page
; FBMtrON - select drive, dma direction and turn motor on.  Wait til
;           drive comes ready.
;CANNOT use D3.
;
;        Entry : (A2) = local stack frame pointer
;                (A5) = Slot base address
;                (D0) = DMARWdir bit = 1 if read dma, = 0 if write dma
;                            all other bits of low order byte clear!!
;        Exit  : (NE) = timed out waiting for drive
;                (EQ) = worked array is sent
;
; Add to DMA direction bit Enable device select,motor on, disable INTS
;        and NOT reset FDC
;
FBMtrON  ORI.B     #MMtrON, D0
         OR.B      DrvNumb(A2), D0               ;get drive number (0..3)
                                                 ;put as dev sel 0 and 1
         MOVE.B    D0, FDClocl(A5)               ;put in local command register

; wait until ready or timeout
;
         MOVE.L    RdyTO(A6), D1                 ;time out counter

FBMOwait BTST      #LSNdrRdy, FDClocl(A5)        ;is drive(FDD) ready?
         BOFF.S    FBMOexit                      ;Ready so stop waiting
         SUBQ.L    #1, D1                        ;count down timeout
         BPL.S     FBMOwait                      ;not timed out so continue
         MOVEQ     #IOEflpto, D7                 ;show timeout error
FBMOexit RTS
;
; FBmtrOFF - deselect drive
;
;        Entry : (A4) = device table entry pointer
;                (A5) = Slot base address
;
FBmtrOFF MOVE.B    #DisblDS, FDClocl(A5)         ;deselect drive
         RTS
  page
;
; FBw8FDC - wait for a command to finish.  FDC generates an IRQ when done.
;
;        Entry : (A5) = Slot base address
;        Exit  : (NE) = timed out waiting for command to finish
;                (EQ) = command completed
;
FBw8FDC  MOVE.L    #CMDTimeOut, D0               ;timeout counter

FBW8wait BTST      #LSNirq, FDClocl(A5)          ;did IRQ happen
         BOFF.S    FBW8exit                      ;yes, command done
         SUBQ.L    #1, D0                        ;no, see if ran out of time
         BPL.S     FBW8wait                      ;if more counts do again else
         MOVEQ     #IOEflpto, D7                 ;show timeout error

FBW8exit RTS
;
; FBbufCntr - setup controller counter to point to big enough buffer
;   Counter is 8 bit interface to a 9 bit down count register which is used
;   as address selector for the controller's buffer.
;   D7 thru D1, inclsive, of interface goto D8 thru D2 of register.
;   D0 of interface goes to D1 and D0 of counter register.
;
;        Entry : (A5) = Slot base address
;
FBbufCntr
         MOVE.W    CPfbps.W, D0                ;counter := (SectorSize
         SUBQ.W    #1, D0                        ;             - 1)
         LSR.W     #1, D0                        ;           DIV 2
         MOVE.B    D0, FDCcntr(A5)
         RTS
  page
;
; FBmvOUT - move data out of controller buffer into user's buffer
;
;        Entry : (D2) = number of bytes in partial sector
;                (D4) = current sector loop counter
;
FBmvOUT  MOVE.W    CPfbps.W, D1                ;if full sector move full sector
         TST.W     D4                            ;is this last sector
         BNE.S     FBMVOful                      ;no
         MOVE.W    D2, D1                        ;then use partial length
FBMVOful SUBQ.W    #1, D1                        ;make into loop counter
         BSR.S     FBbufCntr                     ;init buffer counter

FBMVOmov MOVE.B    FDCbffr(A5), (A3)+            ;get data from buffer to user
         DBRA      D1, FBMVOmov
         RTS
  page
; sector,track,side calc support routines

;
; FBstSTS - calculate starting side, track, and sector from starting block
;           do logical track and sector and physical side and track
;
;        Entry : (D5) = starting block
;        Exit  : (NE) = invalid block, other return params invalid
;                (EQ) = worked
;                (D3) = logical track  (word)
;                (D5) = logical sector (word)
;                Pside and Ptrack inited with physical side and track
;
FBstSTS  CMPI.L    #$7FFF, D5                    ;block bigger than maxint
         BHI.S     FBSSinvblk                    ;yes, invalid block number

; Logical Track := ( ((block #) * (block size)) DIV (sector size) ) DIV (sectors/
;
         MULU      #BLKSZ, D5                  ;sector size divides evenly into
         DIVU      CPfbps.W, D5                ;sector size must be 128,256, or
         CLR.L     D1                            ;get byte and make it a word
         MOVE.B    CPfspt.W, D1                ;
         DIVU      D1, D5                        ;D5 low word is logical track
         MOVE.W    D5, D1                        ;save for later use and validation
         MOVE.B    D1, CurTrack(A2)              ;keep logical track for update
         MOVE.W    D1, D3                        ;save logical track

; logical sector number := remainder + 1
;
         CLR.W     D5                            ;hi word of D5 is logical sector
         SWAP      D5                            ; minus 1.  must add one to it
         ADDQ.W    #1, D5

; Calc physical side and track
;
         BRA.S     FBphysTS

FBSSinvblk
         MOVEQ     #IOEinvblk, D7                ;invalid block number
         RTS
  page
; FBphysTS - calculate physical track and side from logical track
;        Entry : D1 = logical track number
;        Exit  : Pside and Ptrack set up
;                (NE) = invalid block, other return params invalid
;                (EQ) = worked
;
FBphysTS
         CLR.W     Ptrack(A2)                    ;clear track and side, assume si
         BTST      #TYPsides, CPftyp.W
         BON.S     FBPsd0                        ;is side 0 only.

         LSR.W     #1, D1                        ;ptrack := track div 2
         ROXL.W    Ptrack(A2)                    ;pside := track mod 2  shift in

FBPsd0   CLR.L     D0                            ;get max # of tracks per side in
         MOVE.B    CPftps.W, D0                ;a word
         CMP.W     D0, D1                        ;is physical track greater than
         BHI.S     FBSSinvblk                    ;yes, invalid block number
         MOVE.B    D1, Ptrack(A2)                ;save physical track number
FBUexit  TST.W     D7
         RTS
;
; FBupSTS - update side, track and sector
;        Entry : (D5) = current logical sector number
;                (D4) = loop counter of sectors to read/write
;        Exit  : (NE) = invalid block or I/O error
;                (EQ) = worked
;                (D5) = updated logical sector number
;                Pside and Ptrack set up
;                Did seek if track changed
;
FBupSTS  TST.W     D4                            ;was this the last sector?
         BEQ.S     FBUexit                       ;yes, exit
         ADDQ.W    #1, D5                        ;treat as word but value is a byte
         CMP.B     CPfspt.W, D5                ;still in same track
         BLS.S     FBUexit                       ;yes, then all done

; must update track and side
;
         MOVEQ     #1, D5                        ;reset sector number back to 1
         CLR.L     D1                            ;holds logical track number
         ADDQ.B    #1, CurTrack(A2)              ;inc logical track number
         MOVE.B    CurTrack(A2), D1              ;get updated track number
         BSR.S     FBphysTS                      ;calc physical side and track
         BNE.S     FBUexit                       ;invalid block error,track out of range

; do seek then calc new interleave table
;
         BRA       FBseek                        ;seek physical track
  page
;
; FBnumbSctr - calculate number of sectors to read/write and number
;              of bytes in partial sector
;
;        Entry : D2 = number of bytes to read (word)
;        Exit  : D2 = number of bytes in partial sector - if no partial then = sector length
;                D4 = total number of sectors to read,
;                     includes the partial sector
;
FBnumbSctr
         MOVE.W    CPfbps.W, D0                  ;D0 = bytes per sector
         CLR.L     D1                            ;make sure no garbage in hi word
         MOVE.W    D2, D1                        ;# of full sectors :=
         DIVU      D0, D1                        ;(# of bytes) DIV (sector size)
         MOVE.W    D1, D4                        ;save # of full sectors
         CLR.W     D1                            ;the remainder is the number
         SWAP      D1                            ;of bytes in partial sector
         TST.W     D1                            ;if partial is empty
         BEQ.S     FBNpart                       ;then use full sector size for last
         ADDQ.W    #1, D4                        ;else total := (# of full sectors) + 1
         MOVE.W    D1, D0                        ;and use partial byte count for last
FBNpart  MOVE.W    D0, D2                        ;if no partial then set it to sector
         RTS                                     ;size else use partial count
  page
; error routines

;
; FBrstrST   - check error for restore operation
;
;        Entry : (A2) = address of result array from sense int command
;                       2 bytes. 1st is ST0, 2nd is cylinder number.
;        Exit  : (D7) = IOresult code
;                (NE) = I/O error
;                (EQ) = no error
;                (C)  = wrong drive
;
FBrstrST BSR       FBchkST0
         BCS.S     FBrstrex                      ;wrong drive, exit
         BEQ.S     FBrstrex                      ;no error, exit
         CMPI.W    #IOEnoT0, D7                  ;is it type w/ equipment error
         BNE.S     FBrstrex                      ;no
         BTST      #ST0equp, (A2)                ;yes,drive won't ack Track 0 aft
         BON.S     FBrstrex                      ;77 step pulses
         MOVEQ     #IOEnebhrd, D7                ;give nebulous hardware err code
FBrstrex RTS
;
; FBskSTAT - check error for seek operation
;
;        Entry : (A2) = address of result array from sense int command
;                       2 bytes. 1st is ST0, 2nd is cylinder number.
;        Exit  : (D7) = IOresult code
;                (NE) = I/O error
;                (EQ) = no error
;                (C)  = wrong drive
;
FBskSTAT BSR.S     FBchkST0                      ;check ST0 for operation state
         BCS.S     FBsksex                       ;wrong drive, exit
         BEQ.S     FBsksex                       ;no error, exit
         CMPI.W    #IOEnoT0, D7                  ;did cmd stop in middle?
         BNE.S     FBsksex                       ;no, give IOresult FBchkST0 found
         MOVEQ     #IOEnebhrd, D7                ;give nebulous hardware err code
FBsksex  RTS
  page
;
;FBrdidST - check error for read ID operation
;        Entry : (A2) = address of result array of read id command
;        Exit  : (D7) = IOresult code
;                (NE) = I/O error
;                (EQ) = no error
;
FBrdidST BSR.S     FBchkST0                      ;see if command worked
         BEQ.S     FBRIDSex                      ;worked so exit
         CMPI.W    #IOEnoT0, D7                  ;did cntrllr not finish cmd?
         BNE.S     FBRIDSex                      ;no, then exit

; check if can't read disk - maybe unformatted?  {check ST1}
;
         MOVEQ     #IOEnfmtd, D7                 ;assume not formatted
         MOVE.W    (A2), D0                      ;get 2nd byte of array in low by
         BTST      #ST1snfnd, D0                 ;sector not found?
         BON.S     FBRIDSex                      ;yes, maybe unformatted
         BTST      #ST1nadrm, D0                 ;missing address mark?
         BON.S     FBRIDSex                      ;yes, maybe unformatted

; see if CRC error
;
         MOVEQ     #IOEcrcer, D7                 ;assume have CRC error
         BTST      #ST1dataer, D0                ;CRC error?
         BON.S     FBRIDSex                      ;yes
         MOVEQ     #IOEnebhrd, D7                ;give nebulous hardware err code

FBRIDSex TST.W     D7
         RTS
  page
;
;FBreadST - check error for read sector operation
;
;        Entry : (A2) = address of result array of read id command
;        Exit  : (D7) = IOresult code
;                (NE) = I/O error
;                (EQ) = no error
;
FBreadST BSR.S     FBrdidST                      ;do same tests as Read ID operat
         BEQ.S     FBRDSTex                      ;no errors, read worked

; always try wrong cylinder. if is then restore and re-seek
;
         MOVE.B    ST2(A2), D0                   ;get status register 2 from resu
         BTST      #ST2wrngC, D0                 ;read the wrong track?
         BOFF.S    FBRDST10                      ;no
         BSR       FBrstor                       ;do restore
         BNE.S     FBRDSTex                      ;I/O error
         BSR       FBseek                        ;do seek
         BNE.S     FBRDSTex                      ;I/O error
         MOVEQ     #IOEwrngC, D7                 ;tell higher level what failed
         BRA.S     FBRDSTex

; try read/write on bad track, write protect or missing data address mark
;
FBRDST10 CMPI.W    #IOEnebhrd, D7                ;did FBrdidST fail to find cause
         BNE.S     FBRDSTex                      ;no, then exit

         MOVEQ     #IOEbdtrk, D7                 ;track marked as bad (IBM spec)
         BTST      #ST2bdtrk, D0
         BON.S     FBRDSTex

         MOVEQ     #IOEnfmtd, D7                 ;missing data address mark
         BTST      #ST2ndtam, D0                 ;if is disk needs formatting
         BON.S     FBRDSTex                      ;yes disk in bad shape

;not doing write
;        MOVEQ     #IOEwrprot, D7                ;disk write protected?
;        BTST      #ST1wrpro, ST1(A2)            ;
;        BON.S     FBRDSTex                      ;yes

         MOVEQ     #IOEnebhrd, D7                ;give nebulous hardware err code

FBRDSTex TST.W     D7
         RTS
  page
;
;FBchkST0 - check ST0 to see if command failed or succeded
;        Entry : (A2) = address of ST0 byte
;        Exit  : (D7) = IOresult code from ST0 check
;                (NE) = I/O error
;                (EQ) = no error
;                (C)  = wrong drive
;
FBchkST0 MOVE.B    (A2), D0                      ;get ST0
         ANDI.B    #MIntCod, D0                  ;check interrupt code

; see if controller tried but didn't finish
;
         MOVEQ     #IOEnoT0, D7                  ;give can't get to Track 0
         CMPI.B    #MABstp, D0                   ;if cmd stopped in middle
         BEQ.S     FBC0exit                      ;yes

; see if drive changed ready line
;
         CMPI.B    #MABrdy, D0
         BNE.S     FBC0inv
         MOVEQ     #IOEoffln, D7                 ;drive went off line, lost ready
         MOVEQ     #3, D0                        ;see if have correct drive response
         AND.B     (A2), D0                      ;leave just drive sel bits
         CMP.B     DrvNumb(A6), D0               ;is drive the same?
         BEQ.S     FBC0exit                      ;correct drive, exit carry clear
         MOVE.W    #1, CCR                       ;set NE and C got wrong drive
         BRA.S     FBC0ex1                       ;exit

; see if invalid code, if is then have either severe hardware error or software b
;
FBC0inv  MOVEQ     #IOEunknwn, D7                ;give unknown hardware err code
         CMPI.B    #MInvCod, D0                  ;is it invalid?
         BEQ.S     FBC0exit                      ;no, exit no errors
         CLR.L     D7                            ;show no error

FBC0exit TST.W     D7                            ;should clear carry flag
FBC0ex1  RTS
