;
; file: drv.flop.text
; date: 22-June-1983
; Floppy driver for the new controller.
;
; Include files :
;       /ccos/os.gbl.asm.text   {not listed}
;       flop.equ.text           {driver equates}
;
; version 1.0a is initial version of driver. completed :  6/22/83 kb
;
; PROBLEMS:
;        1) When a time out on the controller occurs (send/rcv of cmd/result)
;           should the driver RESET the FDC or what??? to get it back to a known
;           state.
;
;        2) If get a fault line error (ST0 bit D4 = 1) then the door may be
;           open.
;
;        3) Driver will not work, as currently structured, with ASSIGN.  ASSIGN
;           does not call the driver twice if this is the first time anything
;           is attached to the slot; once for drive nuber = 0 and then once for
;           the drive number to install.  Second, it does not set the values for
;           UTdrv and UTslt, drive and slot numbers.
;
;        4) Floppy drive says 2-sided if diskette in drive is 2-sided even if
;           drive has only 1 head (not positive)?? OR drive 2-sided and diskette
;           2-sided but only side 0 formatted.  Possible solution if fail
;           on read/write operation to side 1 because can't find adr mark or ???
;           then assume diskette is single sided and restart whole operation from
;           side,track,starting calculation.  Requires user buffer address, starting
;           block, and ??? are NOT destroyed.
;
;        5) May have density variation of diskette when read/write may have to switch
;           density if get unformatted error.  Do this before check if no side 1, see
;           problem 4.
;
;        6) Switching diskettes with different density and/or sides does not always
;           work.  If the disk change bit is cleared by another program, such as the
;           formatter, the driver may not correctly determine the correct diskette
;           state.  Example, state is FM/1-side and palce in a new diskette and format
;           it FM/2-side.  The driver would never detect this change unless the disk
;           change bit was set, which it wouldn't be because the formatter changed it
;           with its select and deselect of the drive.  Therefore, the driver could
;           incorrectly determine the sector and block ordering.
;
;           Solution :  each program that changes the state of the diskette or prevents
;                       the driver from being aware of a diskette state change, such as
;                       the formatter, MUST update the device type flags for density
;                       and sides before calling the driver or terminating.
;
;        7) Physical track number is the track number wanted on the diskette.  If
;           using 5-1/4 drive with 48 TPI diskettes then to seek to track must
;           multiply track number by 2.  All the 5-1/4 drives are 96 TPI, therefore
;           must use every other track (even numbers).
;
; equates
;

; INCLUDE   '/ccos/os.gbl.asm.text' done here
         list      0
         INCLUDE   '/ccos/os.gbl.asm.text'
         list      1
         INCLUDE   'flop.equ.text'               ;{driver equates}
  page
;
; UNIT I/O PARAMETER PASSING DEFINITION
;
;   COMMAND       UNIT   ADDR   COUNT   BLOCK   MODE     IORESULT   BUSY
; 0 - INSTALL     DO.W                                   D7.W
; 1 - READ        D0.W   D1.L   D2.L    D3.L             D7.W
; 2 - WRITE       D0.W   D1.L   D2.L    D3.L             D7.W
; 3 - CLEAR       D0.W                                   D7.W
; 4 - BUSY        D0.W                                   D7.W       D0.W
; 5 - STATUS      D0.W   D1.L   D2.W <--FUNCTION CODE    D7.W
; 6 - UNMOUNT     D0.W                                   D7.W
;
; ALL REGISTER VALUES ON ENTRY ARE SAVED AND RESTORED EXCEPT D0 & D7
; 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 = driver command then # 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 = device table entry pointer
;        A5 = Slot base address
;        A6 = Static ram Base page pointer - local data
;
  page
         GLOBAL    NewFlopDRV
;
; Floppy DRIVER
;
NewFlopDRV
         BRA.S     FLOP001                       ;JUMP AROUND HEADER
         DATA.B    1                             ;DEVICE BLOCKED
         DATA.B    31                            ;VALID CMDS - ALL
         DATA.B    83,06,22,00                   ;DATE June 22, 1983
         DATA.B    hmlen                         ;HEADER MSG LENGTH
xxx010   DATA.B    'DDDS Floppy driver (v 1.0a)' ;HEADER MSG
hmlen    EQU       %-xxx010
;
FLOP001
         MOVEQ     #IOEioreq,D7                  ;assume invalid command
         CMPI.W    #UNMCMD,D4                    ;VALID COMMAND
         BHI.S     FlopERR                       ;no, exit
         CLR.L     D7                            ;clear IOresult
         MOVEM.L   D1-D6/A0-A6,-(SP)             ;SAVE REGISTERS
         MOVE.L    D3, D5                        ;move block adr to internal global reg
         MOVEA.L   D1,A3                         ;ADDRESS OF USERS BUFFER
         SUBA.W    #LocalFrame, SP               ;allocate local stack frame
         MOVE.L    SP, A2                        ;save address(must be even # of bytes)

; Get data Ram(A6), Slot(A5), and Device table entry(A4) pointers
;
         BSR       GetBaseAdrs                   ; on Entry: D0 = unit number
         BNE.S     NFDexit                       ; if invalid unit number error

         LEA       FlopTBL,A0                    ;TURN THE COMMAND INTO A
         LSL.W     #1,D4                         ;INDEX TO THE FUNCTION
         MOVE.W    0(A0,D4.W),D4
         JSR       0(A0,D4.W)                    ;DO FUNCTION

NFDexit  ADDA.W    #LocalFrame, SP               ;release local stack frame
         MOVEM.L   (SP)+,D1-D6/A0-A6             ;RESTORE registers
FlopERR  RTS

; THE FLOPPY DRIVER JUMP TABLE
;
FlopTBL  DATA.W    FLPINST-FlopTBL               ;UNITINSTALL
         DATA.W    FLPREAD-FlopTBL               ;UNITREAD
         DATA.W    FLPWRIT-FlopTBL               ;UNITWRITE
         DATA.W    FLPCLER-FlopTBL               ;UNITCLEAR
         DATA.W    FLPBUSY-FlopTBL               ;UNITBUSY
         DATA.W    FLPSTAT-FlopTBL               ;UNITSTATUS
         DATA.W    FLPUNMT-FlopTBL               ;UNITUNMOUNT
  page
;
; UnitInstall - init device table and internal variables.
;               initialize controller
;
; Put default values in device table entry
;
FLPINST                                          ;assume 8
         CLR.B     UTro(A4)                      ; not a read only device
         MOVE.B    #D8Type, UTtyp(A4)            ;type = single density and 1 side
         MOVE.B    #D8TrkSd, UTtps(A4)           ; default TRACKS PER SIDE
         MOVE.B    #D8ScTrk, UTspt(A4)           ; default SECTORS PER TRACK
         MOVE.W    #D8SctLen, UTblk+2(A4)        ; default sector length

         BTST      #LSeight, FDClocl(A5)         ;is it 8?
         BON.S     FINis8                        ;yes, else do for 5
         MOVE.B    #D5Type, UTtyp(A4)            ;type = double density and 2 sides
         MOVE.B    #D5TrkSd, UTtps(A4)           ; default TRACKS PER SIDE
         MOVE.B    #D5ScTrk, UTspt(A4)           ; default SECTORS PER TRACK
         MOVE.W    #D5SctLen, UTblk+2(A4)        ; default sector length


; initialize internal variables
;
FINis8   BSR.S     InitIntrnal

; if drive number is 0 then init controller & find # of drives on card
;
         TST.B     UTdrv(A4)
         BNE.S     FINrstr                       ;not drive 0, do restore
         BSR.S     InitCntrlr                    ;init controller
         BNE.S     FINerror                      ;if failed try to recover
         BSR.S     GetNumDrvs                    ;get number of drives at card
         BRA.S     FINexit                       ;always exit

; else do restore then
; do read id and sense drive status to get disk and drive info
;
FINrstr  BSR       FLPCLER                       ;does all this
FINexit  RTS

; possible serious I/O error reset chip again to try to recover
;
FINerror BSR.S     InitCntrlr                    ;init controller also turns off motor
         BEQ.S     FINexit                       ;if ok exit
         BRA       InitFDC                       ;else reset controller and leave
  page
;
; InitIntrnal - initialze all internal variables to their default state
;
;        Entry : (A6) = data ram base address
;                (A4)=ptr to device table entry
;
; Init the interleave, skew, start track & step rate table
;
InitIntrnal
         MOVEQ     #LgclTbl, D0                  ;field index
         LEA       DefIntTbl, A0                 ;default table

IINmoveI MOVE.W    (A0)+, 0(A6,D0)               ;
         ADDQ.W    #2, D0                        ;each field is an integer
         CMPI.W    #LgclTLen, D0                 ;do for each field in table
         BCS.S     IINmoveI                      ;branch when Dest < Src

         MOVE.L    #FDDTimeOut, RdyTO(A6)        ;init ready wait timeout
         CLR.W     FLAGS(A6)                     ;clear flag word

         LEA       CurTrack(A6), A0              ;clear current track
         MOVE.B    UTdrv(A4), D0                 ;number for this drive
         BEQ.S     IINexit                       ;if drive number = 0 don't do
         EXT.W     D0
         SUBQ.W    #1, D0                        ;make into index into array
         CLR.B     0(A0,D0)
IINexit  RTS

; Default table
;
DefIntTbl
         DATA.W    1                             ;1 interleave
         DATA.W    0                             ;0 skew
         DATA.W    0                             ;starting track = 0
; Bug in FDC - must specify step rate 1 ms. faster than want it
         DATA.W    4                             ;3 millisecond step rate
;
; InitCntrllr - init the FDC and do a Specify command
;
InitCntrllr
         BSR       InitFDC                       ;reset the FDC
         BSR       SpecifyCmd                    ;do a specify command
ICRexit  RTS
  page
;
; GetNumDrvs - get number of drives attached to controller at current slot.
;              Only called when drive number in device table entry = 0.
;
;       Entry : (A4)=ptr to device table entry
;               (A5)=ptr to slot
;               (D7) = 0 (no error)
;       Exit  : (NE)=error D7 has error code
;               (EQ)=no error, D7 = 0
;
; SEE IF DRIVE 0 THRU 3 IS ATTACHED.  DRIVE NUMBERS MUST BE DENSE.
;
GetNumDrvs
         MOVEQ     #1, D3                        ;drive number
GTNchk   MOVE.B    D3, UTdrv(A4)                 ;needed FOR MotorON
         MOVEQ     #DMARead, D0                  ;use dma in read direction
         BSR       MotorON                       ;turn on motor and wait for Ready
         BNE.S     GTNdone                       ;drv never came ready so assume no drv
         ADDQ.W    #1, D3                        ;inc to next drive number
         CMPI.W    #MAXdrive, D3                 ;did for all the drives?
         BLS.S     GTNchk                        ;no

;  Put the number of drives in the slot table entry for this slot.
;  always put at least a 1 in slot table ndr field
;
GTNdone  BSR       MotorOFF                      ;turn off motor
         CLR.L     D7                            ;clear timeout error code
         SUBQ.W    #1, D3                        ;drive number back to last correct
         CLR.B     UTdrv(A4)                     ;set drive # BACK TO ZERO
         MOVEA.L   pSysCom.W, A0                 ;syscom pointer
         MOVEA.L   SCslttbl(A0), A0              ;A0 ==> PTR TO SLOT TABLE
         ADDA.W    #STinfo+Stndrv, A0            ;A3  => PTR FIRST ENTRY, FIELD NDR
         CLR.L     D0                            ;D0 is lsot number
         MOVE.B    UTslt(A4), D0
         SUBQ.W    #1, D0                        ;get index into slot table
         MULU      #STinfoL, D0
         MOVE.W    D3, 0(A0,D0.W)                ;PUT NUMBER OF DRIVES IN SLOT TABLE

; IF NUMBER OF DRIVES = 0 THEN ERROR --- NO DRIVES ATTACHED
;
         TST.W     D3                            ;drive number will be zero if no drive 1
         BNE.S     GTNexit                       ;if not 0 then ok
         MOVEQ     #IOEnfdrv, D7                 ;NO FLOPPY DRIVE ERROR
GTNexit  TST.W     D7
         RTS
  page
;
; FLPREAD - UnitRead of floppy
;
; save user buffer address and starting block in local stack frame
;
FLPREAD  MOVE.L    A3, StrtBfAdr(A2)
         MOVE.L    D3, StrtBlk(A2)

; Turn on motor
;
         MOVEQ     #DMARead, D0                  ;use dma in read direction
         BSR       MotorON
         BNE.S     FRDmtrOFF                     ;drive not come ready

; check for disk change
;
         BSR       DskChng
         BNE.S     FRDmtrOFF                     ;I/O error, exit

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

; seek to track and build interleave table
;
         BSR       Seek
         BNE.S     FRDmtrOFF
         BSR       BuildIntrl

; for i := 1 to (# of sectors to read) do
; read sector, move data to user's buffer, update sector,track and side
;
FRDread  BSR       ReadSector                    ;read sector
         BNE.S     FRDmtrOFF                     ;I/O error exit
         BSR       MoveOUT                       ;move data to user's space
         BSR       UpdtSTS                       ;update sector,track and side
         DBNE      D4, FRDread                   ;do until(bad block) or (read all sectors)

; turn motor off and exit
;
FRDmtrOFF
         BSR       MotorOFF
FRDexit  RTS
  page
;
; FLPWRIT - UnitWrite of floppy
;
; Turn on motor
;
FLPWRIT  MOVEQ     #DMAWrite, D0                 ;use dma in write direction
         BSR       MotorON
         BNE.S     FWRmtrOFF                     ;drive not come ready

; check for disk change
;
         BSR       DskChng
         BNE.S     FWRmtrOFF                     ;I/O error, exit

; get starting sector, track and side and # of sectors to write
;
         BSR       StrtSTS
         BNE.S     FWRmtrOFF                     ;invalid block number
         BSR       CalcNmSctr
         SUBQ.W    #1, D4                        ;turn into loop counter

; seek to track and build interleave table
;
         BSR       Seek
         BNE.S     FWRmtrOFF
         BSR       BuildIntrl

; for i := 1 to (# of sectors to write) do
; move data from user's buffer, write sector, update sector,track and side
;
FWRread  TST.W     D4                            ;if last sector and is partial
         BNE.S     FWRnlast                      ;then read sector into controller
         CMP.W     UTblk+2(A4), D2               ;buffer first
         BEQ.S     FWRnlast                      ;last sector is full, don't read first
         MOVEQ     #DMARead, D0                  ;reset dma to read direction
         BSR       MotorON
         BNE.S     FWRmtrOFF                     ;drive not come ready
         BSR       ReadSector                    ;read sector
         BNE.S     FWRmtrOFF                     ;I/O error exit
         MOVEQ     #DMAWrite, D0                 ;put dma back as write direction
         BSR       MotorON
         BNE.S     FWRmtrOFF                     ;drive not come ready

FWRnlast BSR       MoveIN                        ;move data from user's space
         BSR       WriteSector                   ;write sector
         BNE.S     FWRmtrOFF                     ;I/O error exit
         BSR       UpdtSTS                       ;update sector,track and side
         DBNE      D4, FWRread                   ;do until(bad block) or (read all sectors)

; turn motor off and exit
;
FWRmtrOFF
         BSR       MotorOFF
FWRexit  RTS
  page
;
; FLPCLER - UnitClear
;           Restore drive then find out diskete states.
;
FLPCLER
         BSR       InitCntrllr                   ;reset FDC
         BNE.S     FCLexit                       ;failed to reset
         MOVEQ     #DMARead, D0                  ;use dma in read direction
         BSR       MotorON                       ;turn on motor and wait for Ready
         BNE.S     FCLexit                       ;timed out waiting for ready
         BSR       Restore                       ;restore to track 0
         BNE.S     FCLexit                       ;timed out waiting for FDC
         BSR       GetDrvInfo                    ;init for current disk
FCLexit  BRA       MotorOFF                      ;turn off motor and deselect drive

; FLPBUSY - UnitBusy :   always return true.
;
FLPBUSY  MOVEQ     #1, D0
         RTS

; UNITUNMOUNT - make sure motor is off
;
FLPUNMT  MOVE.B    #MMtrOFF, FDClocl(A5)
         RTS
  page
;common functions - put in groups so can make include files if necessary
;routines to do macro I/O operations

;
; DskChng - 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
;
DskChng  BTST      #LSdskchg, FDClocl(A5)        ;disk changed?
         BOFF.S    DKCexit                       ;no, exit (cc = EQ)

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

; get diskette info
;
         BSR.S     GetDrvInfo

DKCexit  RTS
  page
; GetDrvInfo - 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
;
GetDrvInfo
         MOVEQ     #3, D0                        ;try each density at least twice

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

GDIok    BSR       RdIDstat                      ;see if command worked
         BEQ.S     GDIsave                       ;worked, save state
         BCHG      #TYPdnsty, UTtyp(A4)          ;try other density
         MOVE.W    (SP)+, D0                     ;get retry count
         DBRA      D0, GDIagain                  ;do until no more retries
         BRA.S     GDIexit                       ;failed, exit

;save sector length from read id result
;
GDIsave  BSR       CnvSLenCd                     ;convert sector length code and save
         BNE.S     GDIexit1                      ;sector size to large error

; see if drive has side 1
;
         BTST      #FLGsides, Flags(A6)          ;if user sets sides then don't
         BON.S     GDIclr7                       ;change or calc
         MOVEQ     #Mside0, D0                   ;need look at side 0 only
         BSR       SensDrvStat                   ;do drive status to get # of sides
         BNE.S     GDIexit1                      ;timed out, exit
         BSET      #TYPsides, UTtyp(A4)          ;assume single sided
         BTST      #ST32sid, (A2)                ;test for number of sides
         BOFF.S    GDIspt                        ;is single sided (1=2 sided)

; make sure can read side 1
;
         MOVEQ     #Mside1, D3                   ;look at side 1
         BSR       ReadID                        ;if works will clear D7
         BNE.S     GDIexit1                      ;command failed timeout, exit
         BSR       RdIDstat                      ;see if command worked
         BNE.S     GDIclr7                       ;failed leave as 1 sided
         BCLR      #TYPsides, UTtyp(A4)          ;is double sided
GDIclr7  CLR.L     D7                            ;if read of side 1 failed - ignore err
GDIspt   BSR       CalcSPT                       ;calculate sectors per track
GDIexit1 MOVE.W    (SP)+, D0                     ;remove D0 from stack
GDIexit  TST.W     D7                            ;show error state
         RTS
  page
;
; ReadSector - 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
;
ReadSector
         MOVEQ     #RTRYread, D0                 ;read sector retry count

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

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

         BSR       RDstatus                      ;see if worked
         BEQ.S     RSCexit1                      ;read sector, all done
         CMPI.W    #IOEnfmtd, D7                 ;not formatted error
         BNE.S     RSCother                      ;no, retry with same density
         BSR.S     GetDrvInfo                    ;get drive parameters
         BNE.S     RSCexit1                      ;maybe drive is not formatted
         MOVEQ     #IOEnfmtd, D7                 ;restore error code

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

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

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

         MOVEQ     #CMDwrite, D0                 ;get in command
         BSR       RdWr1                         ;do write operation
         BNE.S     WSCexit1                      ;timed out, error exit

         BSR       WRstatus                      ;see if worked
         BEQ.S     WSCexit1                      ;read sector, all done
         CMPI.W    #IOEnfmtd, D7                 ;not formatted error
         BNE.S     WSCother                      ;no, retry with same density
         BSR       GetDrvInfo                    ;get drive parameters
         BNE.S     WSCexit1                      ;maybe drive is not formatted
         MOVEQ     #IOEnfmtd, D7                 ;restore error code

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

WSCexit1 MOVE.W    (SP)+, D0                     ;remove retry count from stack
WSCexit  TST.W     D7                            ;show error condition
         RTS
  page
;FDC, FDD commands
;
; SpecifyCmd - do a specify command to FDC
;        Entry : (A2) = pointer to command and result array
;                (A4) = device table entry pointer
;                (A5) = Slot base address
;                (A6) = Static ram Base page pointer - local data
;        Exit  : (NE) = failed
;                (EQ) = completed
;
SpecifyCmd
         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 inclusive
; 3rd byte is HLT code in bits D7 to D1 and NOT DMA in bit D0
;
         MOVEQ     #HLT8code+NDMAcode, D3        ;HLT code is pre-shifted/assume 8
         MOVEQ     #16, D1                       ;step rate code goes
         MOVE.W    StepRate(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?
         BON.S     SPCis8                        ;yes, else formula for 5
         LSR.W     #1, D0                        ;has step rate div 2
         MOVEQ     #HLT5code+NDMAcode, D3        ;use HLT code for 5 1/4

SPCis8   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,D6                         ;specify command 3 bytes in length
         BSR       WrController                  ;write command array to controller
SPCexit  RTS                                     ;WrController sets cc for error state
  page
;
; SensIntStat - do a Sense Interrupt Status command to FDC.  Find out cause of
;               interrupt.
;        Entry : (A2) = pointer to command and result array
;                (A4) = device table entry pointer
;                (A5) = Slot base address
;                (A6) = Static ram Base page pointer - local data
;        Exit  : (NE) = failed
;                (EQ) = completed
;                IF completed THEN at (A2) is result array of command
;                                          (2 bytes)
;
SensIntStat
         MOVE.B    #CMDsnsint, (A2)              ;put command byte in buffer
         MOVEQ     #1, D6                        ;only 1 byte to command
         BSR       WrController                  ;send command
         BNE.S     SISexit                       ;if timed out exit

         MOVEQ     #RSLTsnsint, D6               ;length of result array
         BSR       RdController                  ;read result
SISexit  RTS
  page
;
; SensDrvStat - 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 page 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)
;
SensDrvStat
         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
;
         MOVE.B    UTdrv(A4), D1                 ;get drive number
         SUBQ.B    #1, D1                        ;turn into dev sel bits
         OR.B      D0, D1                        ;put in side select
         MOVE.B    D1, (A0)                      ;put in command array

         MOVEQ     #2, D6                        ;2 bytes to command
         BSR       WrController                  ;send command
         BNE.S     SDSexit                       ;if timed out exit

         MOVEQ     #RSLTsnsdrv, D6               ;length of result array
         BSR       RdController                  ;read result
SDSexit  RTS
  page
;
; Restore - 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 page pointer - local data
;        Exit  : (NE) = failed
;                (EQ) = completed
;
Restore  MOVEQ     #RTRYrstr, D0                 ;restore retry count

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

; 2nd byte is drive selected in bits D1 and D0, the other bits are 0
;
         MOVE.B    UTdrv(A4), D0                 ;get drive number
         SUBQ.B    #1, D0                        ;turn into dev sel bits
         MOVE.B    D0, (A0)                      ;put in command array
         MOVEQ     #2, D6                        ;2 bytes in command array

         BSR       WrController                  ;write command array to controller
         BNE.S     RSTexit                       ;command timed out

; wait for command to complete
;
RSTwait  BSR       WaitFDC
         BNE.S     RSTexit                       ;should there be a timeout on cmd???

; do Sense Interrupt Status command to get command results
;
         BSR.S     SensIntStat
         BNE.S     RSTexit

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

RSTexit  MOVE.W    (SP)+, D0                     ;remove retry count from stack
RSTexit1 TST.W     D7                            ;show error condition
         RTS
  page
; Seek - 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 page pointer - local data
;                physical side and track setup
;        Exit  : (NE) = failed
;                (EQ) = completed
;
Seek     MOVEQ     #RTRYseek, D0                 ;restore retry count

SEKrtry  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       DevSelHD                      ;get device and head select byte
         MOVE.B    D1, (A0)+                     ;put in command array
         MOVE.B    Ptrack(A2), D0                ;3rd byte: track wanted
         BTST      #FLGtpi, Flags(A6)            ;if 48 TPI is selected
         BOFF.S    SEKis96                       ;then multipy the physical track
         LSL.B     #1, D0                        ;by 2 to get 96 TPI position.
SEKis96  MOVE.B    D0, (A0)                      ;save track number in array

         MOVEQ     #3, D6                        ;2 bytes in command array
         BSR       WrController                  ;write command array to controller
         BNE.S     SEKexit                       ;command timed out

; wait for command to complete
;
SEKwait  BSR       WaitFDC
         BNE.S     SEKexit                       ;should there be a timeout on cmd???

; do Sense Interrupt Status command to get command results
;
         BSR       SensIntStat
         BNE.S     SEKexit

; see if seek end is good
;
         BSR       SeekStatus                    ;find out type of error
         BCS.S     SEKwait                       ;wrong drive wait for correct
         BEQ.S     SEKexit                       ;was good, so exit
         MOVE.W    (SP)+, D0                     ;get retry count
         DBRA      D0, SEKrtry                   ;do until no more retries then
         BRA.S     SEKexit1                      ;error exit if no more

SEKexit  MOVE.W    (SP)+, D0                     ;remove retry count from stack
SEKexit1 TST.W     D7                            ;show error condition
         RTS
  page
;
; ReadID - 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 page pointer - local data
;                (D3) = side select: side 1=Mside1, side 0=Mside0
;        Exit  : (NE) = failed
;                (EQ) = completed
;
ReadID   MOVE.L    A2, A0                        ;save addr of cmd array
         MOVEQ     #CMDredID, D0                 ;get in command
         BSR       AddDensity                    ;combine command and MFM/FM bit
         MOVE.B    D0, (A0)+                     ;put command byte in buffer

; 2nd byte:  bit D2 is side and bits D1 and D0 are the device select
;
         MOVE.B    UTdrv(A4), D1                 ;get drive number
         SUBQ.B    #1, D1                        ;turn into dev sel bits
         OR.B      D3, D1                        ;put in side select
         MOVE.B    D1, (A0)                      ;put in command array

         MOVEQ     #2, D6                        ;2 bytes to command
         BSR       WrController                  ;send command
         BNE.S     RIDexit                       ;if timed out exit

; wait for command to complete
;
         BSR       WaitFDC
         BNE.S     RIDexit                       ;should there be a timeout on cmd???

; get results of operation
;
         MOVEQ     #RSLTredID, D6                ;length of result array
         BSR       RdController                  ;read result
RIDexit  RTS
  page
;
; RdWr1 - 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 page pointer - local data
;                D5 = logical sector number
;        Exit  : (NE) = failed, time out
;                (EQ) = completed
;
RdWr1    MOVE.L    A2, A0                        ;save addr of cmd array
         BSR.S     SetRWcmd                      ;setup the rest of the cmd array
         BNE.S     RW1exit                       ;invalid sector length error

         BSR       SetBfCntr                     ;setup controller counter

; send command
;
         MOVEQ     #RWcmdlen, D6                 ;9 bytes to command
         BSR       WrController                  ;send command
         BNE.S     RW1exit                       ;if timed out exit

; wait for command to complete
;
         BSR       WaitFDC
         BNE.S     RW1exit                       ;should there be a timeout on cmd???

; get results of operation
;
         MOVEQ     #RSLTrdwr, D6                 ;length of result array
         BSR       RdController                  ;read result
RW1exit  RTS
  page
;command operations support functions

;
; SetRWcmd - 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
;
SetRWcmd BSR.S     AddDensity                    ;expects D0 to have command byte
         MOVE.B    D0, (A0)+                     ;put command byte in buffer

         BSR.S     DevSelHD                      ;calc device sel and head sel byte
         MOVE.B    D1, (A0)+                     ;put in command array

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

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

         MOVE.B    UTspt(A4), (A0)+              ; 7th byte: EOT

         BSR.S     GPLcode                       ; 8th byte: GPL, gap 3 length code
         MOVE.B    D1, (A0)+

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

SRWexit  TST.W     D7
         RTS
  page
;
; DevSelHD - calc device and head select values
;
;        Exit  : (D1) = device and head select byte for command array
;
DevSelHD MOVE.B    UTdrv(A4), D1                 ;get drive number
         SUBQ.B    #1, D1                        ;turn into dev sel bits
         MOVE.B    Pside(A2), D0                 ;get head select
         LSL.B     #2, D0                        ;put in correct position
         OR.B      D0, D1                        ;add to drive select
         RTS
;
; GPLcode - calculate Gap 3 length (GPL) code for read/write operations.
;CANNOT change value in D0 or use A0
;
;        Entry : (D0) = sector size code
;        Exit  : (D1) = GPL code
;
GPLcode  CLR.W     D1                            ;make sure index has hi byte
         MOVE.B    D0, D1                        ;of word clear
         LEA       GPLtable, A1                  ;use sector size code as index
         MOVE.B    0(A1,D1), D1                  ;get GPL code from table
GPLexit  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
;
;
; AddDensity - add density bit to command
;CANNOT use D3
;        Entry : D0 = command byte
;        Exit  : D0 = command byte with MFM/FM bit
;
AddDensity
         MOVEQ     #MfmBit, D1                   ;assume is FM
         BTST      #TYPdnsty, UTtyp(A4)          ;is it FM?
         BON.S     ADNisFM                       ;yes (1=FM)
         MOVEQ     #Mmfmbit, D1                  ;no then use MFM
ADNisFM  OR.B      D1, D0                        ;combine command and MFM/FM bit
         RTS
  page
;
; Cnv2SLCd - 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
;
Cnv2SLCd MOVE.W    UTblk+2(A4), D1               ;get sector length in device table
         CLR.B     D0                            ;start with code = 0
         LSR.W     #7, D1                        ;put 4 possible bits in low order nibble

C2Cfind  LSR.W     #1, D1                        ;shift out next bit
         BCS.S     C2Ctest                       ;if set then D0 = correct code
         BEQ.S     C2Cerror                      ;if word clear then invalid length
         ADDQ.B    #1, D0                        ;do next code
         BRA.S     C2Cfind

C2Ctest  CMPI.B    #2, D0                        ;is code in range (512 <= sector size)
         BLS.S     C2Cexit                       ;yes
C2Cerror MOVEQ     #IOEinvsct, D7                ;invalid sector length error

C2Cexit  TST.W     D7                            ;show error status
         RTS
  page
;
; CnvSLenCd - 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
;
CnvSLenCd
         MOVE.W    #SLfmLow, D0                  ;get min. sector length(128)
         CLR.W     D1                            ;make sure high order byte is clear
         MOVE.B    RSLTsctrl(A2), D1             ;get sector length code
         LSL.W     D1, D0                        ;length = multiply of integral of 2 of code
         MOVE.W    D0, UTblk+2(A4)               ;save length in device table

         CMPI.B    #2, D1                        ;is sector length to large?
         BLS.S     CSLexit                       ;no
         MOVEQ     #IOEinvsct, D7                ;invalid sector length error
         CLR.W     UTblk+2(A4)                   ;sector size = 0 means invalid sector size

CSLexit  TST.W     D7                            ;show error status
         RTS
  page
;address functions
;
; GetBaseAdrs - Get static ram base page address for this slot, get slot base
;               address and device table entry address
; CANNOT use D4
;        Entry: D0 = unit number (destroyed)
;        Exit : A4 = device table entry pointer
;               A5 = Slot base address
;               A6 = Static ram Base page pointer - local data
;               (NE) = failed, invalid unit number
;               (EQ) = worked
;
GetBaseAdrs
         BSR.S     DevTblEntry                   ;must be called first
         BNE.S     GBAexit
         BSR.S     CALCADRS
         BSR.S     SlotAddr
GBAexit  TST.W     D7                            ;set cc
         RTS
;
; CALCADRS - calculate base page address of static ram data area
;
; CANNOT use D4
;        Entry : (A4) = device table entry pointer
;        Exit  : (A6) = Base page address
;
CALCADRS LEA       DataBase.W, A6                ;address of I/o slot Static Ram base page
         MOVE.B    UTslt(A4), D0                 ;Slot number
         EXT.W     D0                            ;base :=
         SUBQ.W    #1,D0                         ;   ((slot - 1) *
         MULU      #DBPoffst, D0                 ;   (page length)) +
         ADDA.L    D0, A6                        ;   (Slot Static ram base address)
         RTS

  page
;
;DevTblEntry - Fetches the beginning of the entry of the device table
;              for this unit and the associated slot address.
;
; CANNOT use D4
;        Entry: D0 = unit number (destroyed)
;        Exit : A4 = device table entry pointer
;               (NE) = error and D7 = IOresult code
;               (EQ) = good, A4 and A5 valid
;
DevTblEntry
; check unit number in range
;
         TST.W     D0                            ;See if the volume is negative
         BMI.S     DESerror                      ;NEGATIVE, bad unit number
         MOVE.L    pSysCom.W, A4                 ;see if to large
         MOVE.L    SCdevtab(A4), A4              ;PTR TO DEV TBL --> A4
         CMP.W     (A4)+, D0                     ;Compare Vol num to MAXDEV
         BLS.S     DESgood                       ;good unit number
;
DESerror MOVEQ     #IOEinvdev, D7                ;To large, bad unit number
         BRA.S     DESexit

;  Get the beginning of the device entry for this vol
;
DESgood  MULU      #UTlen, D0
         ADDA.L    D0, A4

DESexit  TST.W     D7
         RTS
;
;SlotAddr - Fetches the slot base address.
;
; CANNOT use D4
;        Entry : (A4) = device table entry pointer
;        Exit  : (A5) = Slot base address
;
SlotAddr
         MOVE.L    #IOSbase, A5                  ;slot 0 Base adrs
         MOVE.B    UTslt(A4), D0                 ;slot * offset + slot 0 base
         EXT.W     D0                            ;equals this slot's base address
         MULU      #IOSoffst, D0
         ADDA.L    D0, A5
         RTS
  page
;
; RdController - 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
;        Exit  : (D6) = number of bytes not sent - 1
;                (NE) = timed out and array AND (D6) are invalid
;                (EQ) = worked array is valid
;
RdController
         SUBQ.W    #1, D6                        ;decrement for loop counter
         MOVE.L    A2, A0                        ;get address of array
         MOVEQ     #IOEflpto, D7                 ;assume timeout error

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

;repeat until(timeout) or (FDC is ready)
;
RDCrdy   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     RDCok                         ;Ready so stop waiting
         SUBQ.L    #1, D0                        ;count down timeout
         BMI.S     RDCexit                       ;timed out so exit  (also NE)
         BRA.S     RDCrdy                        ;else check again

RDCok    MOVE.B    FDCdata(A5), (A0)+            ;get data from reg into array
         DBRA      D6, RDCloop                   ;see if more to get
         CLR.L     D7                            ;got all the data show no error
RDCexit  RTS
  page
;
; WrController - 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 read
;        Exit  : (NE) = timed out and did not finish sending array
;                (EQ) = worked array is sent
;
WrController
         SUBQ.W    #1, D6                        ;decrement for loop counter
         MOVE.L    A2, A0                        ;get address of array
         MOVEQ     #IOEflpto, D7                 ;assume timeout error

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

;repeat until(timeout) or (FDC is ready)
;
WRCrdy   BTST      #FDCready, FDCstat(A5)        ;Ready := FDC is ready
         SNE       D3                            ;         and
         BTST      #DataDir, FDCstat(A5)         ;         Data direction is WRITE
         SEQ       D1
         AND.B     D3, D1
         BNE.S     WRCok                         ;Ready so stop waiting
         SUBQ.L    #1, D0                        ;count down timeout
         BMI.S     WRCexit                       ;timed out so exit (also NE)
         BRA.S     WRCrdy                        ;else check again

WRCok    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, WRCloop                   ;see if more to get
         CLR.L     D7                            ;got all the data show no error
WRCexit  RTS
  page
; MotorON - select drive, dma direction and turn motor on.  Wait til
;           drive comes ready.
;CANNOT use D3.
;
;        Entry : (A4) = device table entry 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
;
MotorON  ORI.B     #MMtrON, D0

         MOVE.B    UTdrv(A4), D1                 ;get drive number (1..4)
         SUBQ.B    #1, D1                        ;convert to 0..3
         OR.B      D1, D0                        ;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                 ;get time out counter

MONwait  BTST      #LSNdrRdy, FDClocl(A5)        ;is drive(FDD) ready?
         BOFF.S    MONexit                       ;Ready so stop waiting
         SUBQ.L    #1, D1                        ;count down timeout
         BPL.S     MONwait                       ;not timed out so continue
         MOVEQ     #IOEflpto, D7                 ;show timeout error
MONexit  RTS
;
; MotorOFF - deselect drive and turn motor off (if 8" drive).
;
;        Entry : (A4) = device table entry pointer
;                (A5) = Slot base address
;
MotorOFF MOVE.B    #MRstDvS, FDClocl(A5)
MOFexit  RTS
  page
;
;InitFDC - reset the floppy disk controller
;        Entry : (A5) = Slot base address
;
InitFDC  MOVE.B    #MRstFDC, FDClocl(A5)         ;reset FDC
         MOVE.B    #MMtrOFF, FDClocl(A5)         ;set it back to normal
         RTS
;
; WaitFDC - 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
;
WaitFDC  MOVE.L    #CMDTimeOut, D0               ;timeout counter

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

WFDCexit RTS
;
; SetBfCntr - 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
;
SetBfCntr
         MOVE.W    UTblk+2(A4), D0               ;counter := (SectorSize
         SUBQ.W    #1, D0                        ;             - 1)
         LSR.W     #1, D0                        ;           DIV 2
         MOVE.B    D0, FDCcntr(A5)
SBCexit  RTS
  page
;
; MoveOUT - move data out of controller buffer into user's buffer
;
;        Entry : (D2) = number of bytes in partial sector
;                (D4) = current sector loop counter
;
MoveOUT  MOVE.W    UTblk+2(A4), D1               ;if full sector move full sector
         TST.W     D4                            ;is this last sector
         BNE.S     MVOfull                       ;no
         MOVE.W    D2, D1                        ;then use partial length
MVOfull  SUBQ.W    #1, D1                        ;make into loop counter
         BSR.S     SetBfCntr                     ;init buffer counter

MVOmove  MOVE.B    FDCbffr(A5), (A3)+            ;get data from buffer to user
         DBRA      D1, MVOmove
MVOexit  RTS
;
; MoveIN - move data into controller buffer from user's buffer
;
;        Entry : (D2) = number of bytes in partial sector
;                (D4) = current sector loop counter
;
MoveIN   MOVE.W    UTblk+2(A4), D1               ;if full sector move full sector
         TST.W     D4                            ;is this last sector
         BNE.S     MVIfull                       ;no
         MOVE.W    D2, D1                        ;then use partial length
MVIfull  SUBQ.W    #1, D1                        ;make into loop counter
         BSR.S     SetBfCntr                     ;init buffer counter

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

; StrtSTS - calculate starting side, track, and sector from starting block
;           do logical track and sector and physical side and track
;
; A zero (0) sector size (UTblk+2) means invalid sector size.
;
;        Entry : (D5) = starting block
;        Exit  : (NE) = invalid block sector size, other return params invalid
;                (EQ) = worked
;                (D3) = logical track  (word)
;                (D5) = logical sector (word)
;                Pside and Ptrack inited with physical side and track
;
StrtSTS  CMPI.L    #$7FFF, D5                    ;block bigger than maxint
         BHI.S     SSSinvblk                     ;yes, invalid block number
         TST.W     UTblk+2(A4)                   ;is sector size invalid?
         BEQ.S     SSSinvsct                     ;yes, error exit

; Logical Track := ( ((block #) * (block size)) DIV (sector size) ) DIV (sectors/track)
;
         MULU      #BlockSz, D5                  ;sector size divides evenly into block size
         DIVU      UTblk+2(A4), D5               ;sector size must be 128,256, or 512
         CLR.L     D1                            ;get byte and make it a word
         MOVE.B    UTspt(A4), D1                 ;
         DIVU      D1, D5                        ;D5 low word is logical track
         MOVE.W    D5, D1                        ;save for later use and validation
         MOVE.B    UTdrv(A4), D0                 ;save logical track number
         EXT.W     D0
         SUBQ.W    #1, D0
         MOVE.B    D1, CurTrack(A6,D0)           ;in drive's current track loc
         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     CalcPhys                      ;calc physical and exit

SSSinvsct
         MOVEQ     #IOEinvsct, D7                ;invalid sector size
         BRA.S     SSSexit
SSSinvblk
         MOVEQ     #IOEinvblk, D7                ;invalid block number
SSSexit  RTS
  page
;
; CalcPhys - calculate physical track and side from logical track
;
; Physical track number is the track number wanted on the diskette.  If
; using 5-1/4 drive with 48 TPI diskettes then to seek to track must
; multiply track number by 2.  All the 5-1/4 drives are 96 TPI, therefore
; must use ever other track (even numbers).
;
;        Entry : D1 = logical track number
;        Exit  : Pside and Ptrack set up
;                (NE) = invalid block, other return params invalid
;                (EQ) = worked
;
CalcPhys
         ADD.W     StartTrk(A6), D1              ;add starting track offset to logical track
         CLR.W     Ptrack(A2)                    ;clear track and side, assume side 0
         BTST      #TYPsides, UTtyp(A4)
         BON.S     CPHsd0                        ;is side 0 only.

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

CPHsd0   CLR.L     D0                            ;get max # of tracks per side in
         MOVE.B    UTtps(A4), D0                 ;a word
         CMP.W     D0, D1                        ;is physical track greater than max.?
         BHI.S     SSSinvblk                     ;yes, invalid block number
         MOVE.B    D1, Ptrack(A2)                ;save physical track number
         TST.W     D7
         RTS
  page
;
; UpdtSTS - 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
;
UpdtSTS  TST.W     D4                            ;was this the last sector?
         BEQ.S     UPSexit                       ;yes, exit
         ADDQ.W    #1, D5                        ;treat as word but value is a byte
         CMP.B     UTspt(A4), D5                 ;still in same track
         BLS.S     UPSexit                       ;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
         MOVE.B    UTdrv(A4), D0
         EXT.W     D0
         SUBQ.W    #1, D0
         ADDQ.B    #1, CurTrack(A6,D0)           ;inc logical track number
         MOVE.B    CurTrack(A6,D0), D1           ;get updated track number
         BSR.S     CalcPhys                      ;calc physical side and track
         BNE.S     UPSexit                       ;invalid block error,track out of range

; do seek then calc new interleave table
;
         BSR       Seek                          ;seek physical track
         BNE.S     UPSexit                       ;I/O error, exit
         BSR.S     BuildIntrl                    ;build new interleave table
UPSexit  TST.W     D7
         RTS
  page
;
; CalcNmSctr - calculate number of sectors to read/write and number
;              of bytes in partial sector
;
; Must call StrtSTS before call this routine.  Sector size of 0 is invalid sector size.
;
;        Entry : D2 = number of bytes to read (long)
;        Exit  : D2 = number of bytes in partial sector - if no partial then = sector size
;                D4 = total number of sectors to read,
;                     includes the partial sector
;
CalcNmSctr
         MOVE.W    UTblk+2(A4), D0               ;# of full sectors :=
         DIVU      D0, D2                        ;(# of bytes) DIV (sector size)
         MOVE.W    D2, D4                        ;save # of full sectors
         CLR.W     D2                            ;the remainder is the number
         SWAP      D2                            ;of bytes in partial sector
         TST.W     D2                            ;if partial is empty
         BEQ.S     CNSpart                       ;then don't read it, exit
         ADDQ.W    #1, D4                        ;else total := (# of full sectors) + 1
         MOVE.W    D2, D0                        ;and use partial byte count
CNSpart  MOVE.W    D0, D2                        ;if partial empty set it to sec length
CNSexit  RTS
  page
;
; CalcSPT - calculate the number of sectors per track from the sector size
;           parameter in the device table.
;        Exit  : (NE) = invalid sector length
;                (EQ) = worked
;
CalcSPT  BTST      #FLGspt, Flags(A6)            ;should calc spt?
         BON.S     CSPTexit                      ;NO

         MOVE.W    UTblk+2(A4), D0               ;get number of bytes/sector
         LSR.W     #8, D0                        ;change length into index into SPTtable
         LSL.W     #1, D0                        ;which is a matrix of sector per track
         BTST      #TYPdnsty, UTtyp(A4)          ;for 128,256 and 512 byte sectors
         BON.S     CSPTsngl                      ;the first value is for single density
         ADDQ.W    #1, D0                        ;the 2nd value is for double density

CSPTsngl LEA       SPT8table, A0                 ;assume 8 table
         BTST      #LSeight, FDClocl(A5)         ;is it 8?
         BON.S     CSPTis8                       ;yes, else do for 5
         LEA       SPT5table, A0                 ;use 5 table

CSPTis8  MOVE.B    0(A0,D0), UTspt(A4)
         BNE.S     CSPTexit                      ;a value of 0 is invalid sector length
         MOVEQ     #IOEinvsct, D7
CSPTexit TST.W     D7
         RTS
;
SPT8table                                        ;8 inch drives
         DATA.B    26,0                          ;128 byte sector(Dbl dnsty 128 invalid)
         DATA.B    15,26                         ;256 byte sector
         DATA.B    8,15                          ;512 byte sector
SPT5table                                        ;5 inch drives
         DATA.B    16,0                          ;128 byte sector(Dbl dnsty 128 invalid)
         DATA.B    8,16                          ;256 byte sector
         DATA.B    4,9                           ;512 byte sector
  page
;
; BuildIntrl - build interleave table from logical track number
;
BuildIntrl
         MOVE.W    Intrleav(A6), D0              ;if interleave = 1
         SUBQ.W    #1, D0
         BNE.S     IntlvTABLE                    ;no, do full interleave calc
         TST.W     Skew(A6)                      ;and skew = 0
         BNE.S     IntlvTABLE                    ;then no interleave
;
; NointTABLE - build an interleave translation table when
;              interleave = 1 and skew = 0.  Therefore, logical
;              sector number = physical number.
;
;        Entry : (A6) = slot's base page
;
NointTABLE
         MOVE.B    UTspt(A4), D0                 ;MUST be <= 40
         EXT.W     D0                            ;standard max is 26
NITloop  MOVE.B    D0, LtoPmap(A6,D0)            ;for i:=1 to UTspt do
         DBRA      D0, NITloop                   ;LtoPmap[i] := i
NITexit  RTS                                     ;does for 1 plus # of sectors
  page
;
; IntlvTABLE - build the interleave translation table.  Logical to
;              physical sector mapping
;use skew - done every time seek
;do a seek every time enter driver for a read or a write
;        Entry : interleave and skew in Logical table initialized
;                (A4) = device table entry pointer
;                (A6) = slot's base page
;
IntlvTABLE
         LEA       SectrLst(A6), A0              ;tracking of which physical sectors used
         LEA       LtoPmap(A6), A1               ;logical to physical sector map
         MOVE.B    UTspt(A4), D0                 ;MUST be <= 40
         EXT.W     D0                            ;standard max is 26

         MOVE.L    D0, D1                        ;sector numbering starts with 1
ITTlop1  CLR.B     (A0)+                         ;init all ents + 1 to zero
         DBRA      D1, ITTlop1

         LEA       SectrLst(A6), A0              ;D3 is j {init starting sector entry}
         MOVE.B    UTdrv(A4), D3                 ;get logical track number
         EXT.W     D3
         SUBQ.W    #1, D3
         MOVE.B    CurTrack(A6,D3), D3
         MULU      Skew(A6), D3                  ; j is starting sector to begin with
         DIVU      D0, D3                        ; j := ((track * skew) mod UTspt) + 1
         CLR.W     D3                            ;make sure hi word is all 0
         SWAP      D3                            ;use remainder as result of mod
         ADDQ.W    #1, D3                        ;add the 1 to j
         MOVE.B    #1,0(A0,D3)                   ;SECTOR = array[1..UTspt] of byte
         MOVE.B    D3,1(A1)                      ;logical= array[1..UTspt] of byte

         MOVEQ     #2,D1                         ;FOR i:=2 TO UTspt DO
ITTlop2  ADD.W     Intrleav(A6), D3              ;D1 is i, just a loop counter
         SUBQ.W    #1, D3                        ;j := j + interleave - 1
                                                 ;repeat
ITTlop3  DIVU      D0, D3                        ;j := (j mod UTspt) + 1
         CLR.W     D3                            ;UTspt is D0
         SWAP      D3                            ;use remainder as result of mod
         ADDQ.W    #1,D3                         ;adding the one to j
         TST.B     0(A0,D3)                      ;until sectors[j]<>0
         BNE.S     ITTlop3

         MOVE.B    D1, 0(A0,D3)                  ;sectors[j] := i
         MOVE.B    D3, 0(A1,D1)                  ;logical[i] := j

         ADDQ.W    #1, D1                        ;next logical sector
         CMP.W     D0, D1                        ;did all sectors
         BLS.S     ITTlop2                       ;no

ITTexit  RTS
  page
; error routines

;
; RstrStatus - 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
;
RstrStatus                    ;find out type of error
         BSR       ChkST0
         BCS.S     RRSexit                       ;wrong drive, exit don't do TST
         BEQ.S     RRSexit                       ;no error, exit
         CMPI.W    #IOEnoT0, D7                  ;is it type w/ equipment error
         BNE.S     RRSexit                       ;no
         BTST      #ST0equp, (A2)                ;yes,drive won't ack Track 0 after
         BON.S     RRSexit                       ;77 step pulses
         MOVEQ     #IOEnebhrd, D7                ;give nebulous hardware err code
RRSexit  RTS
;
; SeekStatus - 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
;
SeekStatus
         BSR       ChkST0                        ;check ST0 for operation state
         BCS.S     SKSexit                       ;wrong drive, exit don't do TST
         BEQ.S     SKSexit                       ;no error, exit
         CMPI.W    #IOEnoT0, D7                  ;did cmd stop in middle?
         BNE.S     SKSexit                       ;no, give IOresult ChkST0 found
         MOVEQ     #IOEnebhrd, D7                ;give nebulous hardware err code
SKSexit  RTS
  page
;
;RdIDstat - 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
;
RdIDstat BSR       ChkST0                        ;see if command worked
         BEQ.S     RDSexit                       ;worked so exit
         CMPI.W    #IOEnoT0, D7                  ;did cntrllr not finish cmd?
         BNE.S     RDSexit                       ;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 byte
         BTST      #ST1snfnd, D0                 ;sector not found?
         BON.S     RDSexit                       ;yes, maybe unformatted
         BTST      #ST1nadrm, D0                 ;missing address mark?
         BON.S     RDSexit                       ;yes, maybe unformatted

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

RDSexit  TST.W     D7
         RTS
  page
;
;RDstatus - check error for read sector operation
;
;WRstatus - check error for write sector operation
;  **** temp ??? **** are there any other bits to check different than read
;
;        Entry : (A2) = address of result array of read id command
;        Exit  : (D7) = IOresult code
;                (NE) = I/O error
;                (EQ) = no error
;
WRstatus
RDstatus BSR.S     RdIDstat                      ;do same tests as Read ID operation
         BEQ.S     RSUexit                       ;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 result array
         BTST      #ST2wrngC, D0                 ;read the wrong track?
         BOFF.S    RSUchker                      ;no
         BSR       Restore                       ;do restore
         BNE.S     RSUexit                       ;I/O error
         BSR       Seek                          ;do seek
         BNE.S     RSUexit                       ;I/O error
         MOVEQ     #IOEwrngC, D7                 ;tell higher level what failed
         BRA.S     RSUexit

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

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

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

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

         MOVEQ     #IOEnebhrd, D7                ;give nebulous hardware err code

RSUexit  TST.W     D7
         RTS
  page
;
;ChkST0 - 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
;
ChkST0   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     CS0exit                       ;yes

; see if drive changed ready line
;
         CMPI.B    #MABrdy, D0
         BNE.S     CS0invcd
         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
         ADDQ.B    #1, D0                        ;make drive number 1..4
         CMP.B     UTdrv(A4), D0                 ;is drive the same?
         BEQ.S     CS0exit                       ;correct drive, exit carry clear
         MOVE.W    #1, CCR                       ;set NE and C got wrong drive
         BRA.S     CS0exit1                      ;exit

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

CS0exit  TST.W     D7                            ;clears the carry flag
CS0exit1 RTS
  page

; FLPSTAT - Unitstatus functions
;
FLPSTAT  MOVEQ     #IOEfnccd, D7
         CMPI.W    #LastFnc, D2                  ;VALID FUNCTION CODE
         BHI.S     FSTerr                        ;NO
         CLR.L     D7                            ;ok, clear ioresult and do fuction
         MOVE.W    (A3), D0                      ;get users value
         LEA       FSTTABL, A1                   ;TURN THE FUNCTION CODE INTO
         LSL.W     #1, D2                        ;AN INDEX TO THE FUNCTION
         MOVE.W    0(A1,D2.W), D2
         JSR       0(A1,D2.W)                    ;DO FUNCTION
FSTerr   RTS

; THE Floppy DRIVER STATUS JUMP TABLE
;

FSTTABL  DATA.W    STIntrl-FSTTABL               ;Change Logical Interleave
         DATA.W    STlSkew-FSTTABL               ;Change Logical Skew
         DATA.W    STStrTk-FSTTABL               ;Change Starting Track
         DATA.W    STStpRt-FSTTABL               ;Change Step Rate
         DATA.W    STchSPT-FSTTABL               ;Change sectors per track
         DATA.W    STchTPS-FSTTABL               ;Change tracks per side
         DATA.W    STtimot-FSTTABL               ;Change Ready wait timeout counter
         DATA.W    STchTPI-FSTTABL               ;Change tracks per inch selection
         DATA.W    STchSDS-FSTTABL               ;Change sides
         DATA.W    STdrvst-FSTTABL               ;Get driver status
LastFnc  EQU       ((FSTTABL-%)/2)-1
  page
;STIntrl - Change Logical Interleave
;
;        Parameter Block : Interleave = word {range = 1..spt}
;
;STlSkew - Change Logical Skew
;
;        Parameter Block : Skew = word {range = 0..spt}
;
STIntrl  TST.W     D0                            ;for interleave, check param
         BEQ       STbadprm                      ;if 0 then bad
         MOVEQ     #Intrleav, D3                 ;good, get interleave index
         BRA.S     STIS10

STlSkew  MOVEQ     #Skew, D3                     ;get skew index

STIS10   CLR.L     D1                            ;get sectors per track
         MOVE.B    UTspt(A4), D1
         CMP.W     D1, D0                        ;if greater than spt then
         BHI.S     STbadprm                      ;error

         MOVE.W    D0, 0(A6,D3)                  ;valid skew or interleave, save it
STISexit RTS

;STStrTk - Change Starting Track
;
;        Parameter Block : StartTrack = word {range = 0..tps}
;
STStrTk  CLR.L     D1                            ;put byte tps in word
         MOVE.B    UTtps(A4), D1                 ;if user's start track is
         CMP.W     D1, D0                        ;greater than tps then
         BHI.S     STbadprm                      ;error
         MOVE.W    D0, StartTrk(A6)              ;valid skew, save it
         RTS

;STtimot - Change Ready wait timeout counter
;
;        Parameter Block : TimeOut = long word {range = positive integer}
;
STtimot  TST.L     (A3)                          ;if negative
         BMI.S     STbadprm                      ;then invalid number
         MOVE.L    (A3), RdyTO(A6)               ;valid timeout cntr, save it
         RTS
  page
;STStpRt - Change Step Rate
;
;        Parameter Block : StepRate = word {range = 1..16}
;
STStpRt  CMPI.W    #StpRLow, D0                  ;if (steprate<1) or
         BCS.S     STbadprm                      ;   (steprate>16) then
         CMPI.W    #StpRtHi, D0                  ; invalid step rate
         BHI.S     STbadprm
         MOVE.W    D0, StepRate(A6)              ;valid step rate, save it

         MOVEQ     #DMARead, D0                  ;use dma in read direction
         BSR       MotorON                       ;turn motor on
         BNE.S     STSTPex                       ;timed out, exit
         BSR       SpecifyCmd                    ;init step rate in FDC
         BNE.S     STSTPex                       ;timed out, exit
         BSR       Restore                       ;restore to track 0
STSTPex  BRA       MotorOFF                      ;turn motor off

;STchSPT - Change sectors per track
;
;        Parameter Block : Sector/Track = word {range = 0..127}
;
; a value of 0 means use the table calculation based on density and
; sector size
;
STchSPT  CMPI.W    #127, D0                      ;if greater than 127
         BHI.S     STbadprm                      ;then invalid number
         MOVE.B    D0, UTspt(A4)                 ;save in device table
         BSET      #FLGspt, Flags(A6)            ;show not to calc with new disk

         TST.W     D0                            ;if zero then
         BNE.S     STCSexit
         BCLR      #FLGspt, Flags(A6)            ;show calc with new disk
         BSR       CalcSPT                       ;just in case no floppy in drive

;entry to get diskette state
;
STCSmtrON
         MOVEQ     #DMARead, D0                  ;use dma in read direction
         BSR       MotorON                       ;turn on motor and wait for Ready
         BNE.S     STCSex10                      ;timed out waiting for ready
         BSR       GetDrvInfo                    ;init for current disk
STCSex10 BSR       MotorOFF                      ;turn off motor and deselect drive
         CLR.L     D7                            ;if failed don't return I/O erro
         RTS

STbadprm MOVEQ     #IOEuiopm, D7                 ;invalid parameter error
         RTS
  page
;
;STchTPS - Change tracks per side
;
;        Parameter Block : Track/Side = word {range = 0..127}
;
; a value of 0 means use the default number of tracks for drive
;
STchTPS  CMPI.W    #127, D0                      ;if greater than 127
         BHI.S     STbadprm                      ;then invalid number

         TST.W     D0                            ;if zero use default
         BNE.S     STCTsave
         MOVEQ     #D8TrkSd, D0                  ;assume 8, 48 TPI standard
         BTST      #LSeight, FDClocl(A5)         ;is it 8?
         BON.S     STCTsave                      ;yes, else do for 5
         MOVEQ     #D5TrkSd, D0                  ;96 TPI 5 1/4 standard
         BTST      #FLGtpi, Flags(A6)            ;is it 96 TPI?
         BOFF.S    STCTsave                      ;yes
         LSR.W     #1, D0                        ;no, use 48 TPI standard
STCTsave MOVE.B    D0, UTtps(A4)                 ;save in device table
STCSexit RTS

;STchTPI - Change tracks per inch selection
;
; Only valid for 5-1/4 inch drives
;
;        Parameter Block : Track/Side = word {range = 0..1}
;
; a value of 0 means use 96 TPI, 1 means use 48 TPI.
;
STchTPI  BTST      #LSeight, FDClocl(A5)         ;is it 8?
         BON.S     STbadprm                      ;yes, invalid call for 8

         CMPI.W    #1, D0                        ;if greater than 1
         BHI.S     STbadprm                      ;then invalid selection code

         BCLR      #FLGtpi, Flags(A6)            ;assume 96 TPI
         MOVEQ     #D5TrkSd, D1                  ;set tps for 96 TPI default
         TST.W     D0                            ;is it 96?
         BEQ.S     STCIexit                      ;yes
         BSET      #FLGtpi, Flags(A6)            ;no, its 48 TPI
         MOVEQ     #D5TrkSd/2, D1                ;save tps as 1/2 of 96 TPI
STCIexit MOVE.B    D1, UTtps(A4)
         RTS
  page
;
; STchSDS - Change number of sides parameter
;
;        Parameter Block : Number of Sides = word {range = 0..2}
;
; a value of 0 means let driver calc number sides.
;
STchSDS  CMPI.W    #2, D0                        ;if greater than 2
         BHI.S     STbadprm                      ;then invalid selection code

         BCLR      #FLGsides, Flags(A6)          ;assume 0 parameter
         TST.W     D0                            ;is it 0?
         BEQ.S     STCSmtrON                     ;yes, calc sides of floppy

         BSET      #FLGsides, Flags(A6)          ;user is setting sides
         BSET      #TYPsides, UTtyp(A4)          ;assume 1 side only
         SUBQ.W    #1, D0                        ;is it 1 or 2 sides
         BEQ.S     STSDSex                       ;1 side, so exit
         BCLR      #TYPsides, UTtyp(A4)          ;user setting 2 sided

STSDSex  RTS
  page
;STdrvst - Get driver status
;
; Parameter Block : record
;                          intrlv,
;                          skew,
;                          StartTrack,
;                          StepRate,
;                          spt,
;                          tps,
;                          SectorSize : integer;
;                          TimeOut : longint;
;                          Diskette,             {true if diskette in drive}
;                          1Sided,               {true if 1 side only}
;                          SnglDensity,          {true if single density}
;                          UserSPT,              {true if user set spt}
;                          tpi48,                {true if 48 TPI}
;                          EightDrv : boolean;   {true if 8 inch drive}
;                          end;
;
; Diskette field must be true to have valid SectorSize, 1Sided, and
;        SnglDensity fields.
; tpi48 field valid only if EightDrv is false, 5-1/4 inch drive.
;
STdrvst  MOVEQ     #DMARead, D0                  ;use dma in read direction
         BSR       MotorON                       ;turn on motor and wait for Ready
         BNE.S     STDSnod                       ;timed out waiting for ready, no disk
         BSR       GetDrvInfo                    ;get diskette state
STDSnod  SEQ       D0                            ;if fails say no diskette
         ANDI.B    #1, D0                        ;make boolean for Diskette
         BSR       MotorOFF                      ;turn off motor and deselect drive
         CLR.L     D7                            ;always clear IOresult

         MOVE.W    Intrleav(A6), (A3)+           ;interleave
         MOVE.W    Skew(A6), (A3)+               ;skew
         MOVE.W    StartTrk(A6), (A3)+           ;starting track
         MOVE.W    StepRate(A6), (A3)+           ;step rate
         CLR.B     (A3)+                         ;make sure hi byte = 0
         MOVE.B    UTspt(A4), (A3)+              ;number of sectors per track
         CLR.B     (A3)+                         ;make sure hi byte = 0
         MOVE.B    UTtps(A4), (A3)+              ;number of tracks per side
         MOVE.W    UTblk+2(A4), (A3)+            ;sector size
         MOVE.L    RdyTO(A6), (A3)+              ;ready timeout counter
         MOVE.B    D0, (A3)+                     ;save diskette boolean
         BTST      #TYPsides, UTtyp(A4)          ;1-sided
         BSR.S     MakeBool
         BTST      #TYPdnsty, UTtyp(A4)          ;single density
         BSR.S     MakeBool
         BTST      #FLGspt, Flags(A6)            ;user running spt
         BSR.S     MakeBool
         BTST      #FLGtpi, Flags(A6)            ;TPI selection flag
         BSR.S     MakeBool
         BTST      #LSeight, FDClocl(A5)         ;8 inch drive flag

MakeBool SNE       D0
         ANDI.B    #1, D0
         MOVE.B    D0, (A3)+
         RTS

         END       NewFlopDRV
