        IF      IODRIVERINIT
        PAGE    SSB DCB-4 DRIVER INIT CODE
FDRESTORE
*
*       Initialize the PIA
*
        LDD     #\$0000                Reset the PIA
        STD     SSBPIACRA              This gets CRA and CRB
        LDD     #\$00FF                A side is input, B side is output
        STD     SSBPIAORA              This gets DDRA and DDRB
        LDD     #\$0404                No int
        STD     SSBPIACRA              This gets CRA and CRB
        LDD     SSBPIAORA              THIS CLEARS INTS
        OKRTS
        FIN     IODRIVERINIT
        IF      IODRIVERRAM
        PAGE    SSB DCB-4 DRIVER RAM
*
*       SSB Chieftain Working Storage
*
FDINTFREE       FCB     1              "Floppy Disk is NOT busy" flag
FDINTDCB        RMB     2              Address of current DCB
FDINTDRIVE      FCB     -1             Last drive selected (initz'd to "NONE")
*
*       Disk interrupt time out block
*
FDINTTIMEOUTBLOCK
                FDB     NEXTTIMEOUT    timeout block for floppies
                FDB     0              fuse length
                FDB     FDTIMEOUT
NTIMEOUTS       SET     NTIMEOUTS+1
NEXTTIMEOUT     SET     FDINTTIMEOUTBLOCK
        PAGE
IBM.DCB FCB     1                      DCB:DONEFLAG
        FDB     0                      DCB:LASTERROR
        FDB     IBM.DCB+FDSIZE         DCB:NAME
        FDB     NEXTDISKDCB            DCB:NEXTDCB
        FDB     FDDRIVER               DCB:DRIVER
*
        FDB     128                    DSKINFO:NBPS
        FDB     26                     DSKINFO:NSPT
        FDB     1                      DSKINFO:NTPC
        FDB     77                     DSKINFO:NCYL
*
        RMB     1                      DSKINFO:NSPC
        RMB     LCN:SIZE               DSKINFO:MINALLOC
        RMB     LCN:SIZE               DSKINFO:MIDALLOC
        FDB     1                      DSKINFO:MAPALGORITHM
*
        RMB     1                      DSKINFO:LOG2NBPS
        RMB     2                      DSKINFO:NBPSM1
        RMB     LSN:SIZE               DSKINFO:NLSN
        RMB     LCN:SIZE               DSKINFO:NLCN
        RMB     2                      DSKINFO:NBPC
        RMB     LCN:SIZE               DSKINFO:RANDMAP
        RMB     LSN:SIZE               DSKINFO:MAPLSN
*
        RMB     2                      DSKINFO:DIRFCB
        RMB     2                      DSKINFO:MAPFCB
        RMB     2                      DSKINFO:SECTORDB
        RMB     LSN:SIZE               DSKINFO:BADLSN
        FDB     0                      DSKINFO:SEEKERRCNT
        FDB     0                      DSKINFO:SEEKERRSTS
        FDB     0                      DSKINFO:WRITEERRCNT
        FDB     0                      DSKINFO:WRITEERRSTS
        FDB     0                      DSKINFO:READERRCNT
        FDB     0                      DSKINFO:READERRSTS
        FCB     0,0,0                  DSKINFO:OPSCOUNT
        RMB     LSN:SIZE               DSKINFO:ERRLSN
        RMB     1                      DSKINFO:WRITEPROTSTATE
        IF      (*-IBM.DCB)#DSKINFO:SIZE
        ?       INCORRECT DCB OFFSETS
        FIN
*
        FDB     .8INCHCHAINHEAD        FDHEADCHAIN
        FDB     NEXT8INCHDRIVE         FDNEXTCHAIN
        FCB     %00000000              FDDRVCAPABILITY X/X/X/SS/ST/SD/SD/X
        FCB     %00000001              FDDSKCAPABILITY X/X/X/SS/ST/SD/SD/IBM
        FCB     %00001001              FDRESTORECMD + STEP RATE
        FCB     %00011001              FDSEEKCMD + STEP RATE
        FCB     %01001001              FDSTEPINCMD + STEP RATE
        FCB     %01101001              FDSTEPOUTCMD + STEP RATE
        RMB     1                      FDREADCMD + SIDE SELECT
        RMB     1                      FDWRITECMD + SIDE SELECT
        FCB     $7E                    FDSTATEJ
        FDB     FDUNEXPECTEDINT        FDSTATE
        FCB     26                     FDNSPT
        FDB     DIV26                  FDDIVNSPT
        FCB     128/32                 FDNBPSDIV32
        RMB     1                      FDREADWRITE
        RMB     1                      FDDOUBLESEEK
        RMB     1                      FDDOUBLESECTOR
        RMB     1                      FDPORTB
        RMB     1                      FDSEEKRETRY
        RMB     1                      FDRETRY
        FCB     4                      FDUNIT
        FCB     -1                     FDCYL
        FDB     0                      FDMAPALGORITHM
        RMB     1                      FDK1MODNSPT
        RMB     1                      FDK2MODNSPT
        RMB     1                      FDK4MODNSPT
        RMB     1                      FDK8MODNSPT
        RMB     1                      FDK16MODNSPT
        RMB     26                     FDMAP
*
        FCC     /IBM:/                 DEVICE NAME
        FCB     0                      END OF DEVICE NAME
NEXTDISKDCB     SET     IBM.DCB
NDISKDCBS       SET     NDISKDCBS+1
NEXT8INCHDRIVE  SET     IBM.DCB
        PAGE
SD0.DCB FCB     1                      DCB:DONEFLAG
        FDB     0                      DCB:LASTERROR
        FDB     SD0.DCB+FDSIZE         DCB:NAME
        FDB     NEXTDISKDCB            DCB:NEXTDCB
        FDB     FDDRIVER               DCB:DRIVER
*
        FDB     128                    DSKINFO:NBPS
        FDB     18                     DSKINFO:NSPT
        FDB     1                      DSKINFO:NTPC
        FDB     35                     DSKINFO:NCYL
*
        RMB     1                      DSKINFO:NSPC
        RMB     LCN:SIZE               DSKINFO:MINALLOC
        RMB     LCN:SIZE               DSKINFO:MIDALLOC
        FDB     1                      DSKINFO:MAPALGORITHM
*
        RMB     1                      DSKINFO:LOG2NBPS
        RMB     2                      DSKINFO:NBPSM1
        RMB     LSN:SIZE               DSKINFO:NLSN
        RMB     LCN:SIZE               DSKINFO:NLCN
        RMB     2                      DSKINFO:NBPC
        RMB     LCN:SIZE               DSKINFO:RANDMAP
        RMB     LSN:SIZE               DSKINFO:MAPLSN
*
        RMB     2                      DSKINFO:DIRFCB
        RMB     2                      DSKINFO:MAPFCB
        RMB     2                      DSKINFO:SECTORDB
        RMB     LSN:SIZE               DSKINFO:BADLSN
        FDB     0                      DSKINFO:SEEKERRCNT
        FDB     0                      DSKINFO:SEEKERRSTS
        FDB     0                      DSKINFO:WRITEERRCNT
        FDB     0                      DSKINFO:WRITEERRSTS
        FDB     0                      DSKINFO:READERRCNT
        FDB     0                      DSKINFO:READERRSTS
        FCB     0,0,0                  DSKINFO:OPSCOUNT
        RMB     LSN:SIZE               DSKINFO:ERRLSN
        RMB     1                      DSKINFO:WRITEPROTSTATE
        IF      (*-SD0.DCB)#DSKINFO:SIZE
        ?       INCORRECT DCB OFFSETS
        FIN
*
        FDB     .5INCHCHAINHEAD        FDHEADCHAIN
        FDB     NEXT5INCHDRIVE         FDNEXTCHAIN
        FCB     %00011110              FDDRVCAPABILITY X/X/X/DS/DT/DD/DD/X
        FCB     %00000001              FDDSKCAPABILITY SS/ST/SD/SD/IBM
        FCB     %00001000              FDRESTORECMD + STEP RATE
        FCB     %00011000              FDSEEKCMD + STEP RATE
        FCB     %01001000              FDSTEPINCMD + STEP RATE
        FCB     %01101000              FDSTEPOUTCMD + STEP RATE
        RMB     1                      FDREADCMD + SIDE SELECT
        RMB     1                      FDWRITECMD + SIDE SELECT
        FCB     $7E                    FDSTATEJ
        FDB     FDUNEXPECTEDINT        FDSTATE
        FCB     18                     FDNSPT
        FDB     DIV18                  FDDIVNSPT
        FCB     128/32                 FDNBPSDIV32
        RMB     1                      FDREADWRITE
        RMB     1                      FDDOUBLESEEK
        RMB     1                      FDDOUBLESECTOR
        RMB     1                      FDPORTB
        RMB     1                      FDSEEKRETRY
        RMB     1                      FDRETRY
        FCB     0                      FDUNIT
        FCB     -1                     FDCYL
        FDB     0                      FDMAPALGORITHM
        RMB     1                      FDK1MODNSPT
        RMB     1                      FDK2MODNSPT
        RMB     1                      FDK4MODNSPT
        RMB     1                      FDK8MODNSPT
        RMB     1                      FDK16MODNSPT
        RMB     26                     FDMAP
*
        FCC     /SD0:/                 DEVICE NAME
        FCB     0                      END OF DEVICE NAME
NEXTDISKDCB     SET     SD0.DCB
NDISKDCBS       SET     NDISKDCBS+1
NEXT5INCHDRIVE  SET     SD0.DCB
        PAGE
D0.DCB  FCB     1                      DCB:DONEFLAG
        FDB     0                      DCB:LASTERROR
        FDB     D0.DCB+FDSIZE          DCB:NAME
        FDB     NEXTDISKDCB            DCB:NEXTDCB
        FDB     FDDRIVER               DCB:DRIVER
*
        FDB     256                    DSKINFO:NBPS
        FDB     9                      DSKINFO:NSPT
        FDB     1                      DSKINFO:NTPC
        FDB     319                    DSKINFO:NCYL
*
        RMB     1                      DSKINFO:NSPC
        RMB     LCN:SIZE               DSKINFO:MINALLOC
        RMB     LCN:SIZE               DSKINFO:MIDALLOC
        FDB     1                      DSKINFO:MAPALGORITHM
*
        RMB     1                      DSKINFO:LOG2NBPS
        RMB     2                      DSKINFO:NBPSM1
        RMB     LSN:SIZE               DSKINFO:NLSN
        RMB     LCN:SIZE               DSKINFO:NLCN
        RMB     2                      DSKINFO:NBPC
        RMB     LCN:SIZE               DSKINFO:RANDMAP
        RMB     LSN:SIZE               DSKINFO:MAPLSN
*
        RMB     2                      DSKINFO:DIRFCB
        RMB     2                      DSKINFO:MAPFCB
        RMB     2                      DSKINFO:SECTORDB
        RMB     LSN:SIZE               DSKINFO:BADLSN
        FDB     0                      DSKINFO:SEEKERRCNT
        FDB     0                      DSKINFO:SEEKERRSTS
        FDB     0                      DSKINFO:WRITEERRCNT
        FDB     0                      DSKINFO:WRITEERRSTS
        FDB     0                      DSKINFO:READERRCNT
        FDB     0                      DSKINFO:READERRSTS
        FCB     0,0,0                  DSKINFO:OPSCOUNT
        RMB     LSN:SIZE               DSKINFO:ERRLSN
        RMB     1                      DSKINFO:WRITEPROTSTATE
        IF      (*-D0.DCB)#DSKINFO:SIZE
        ?       INCORRECT DCB OFFSETS
        FIN
*
        FDB     .5INCHCHAINHEAD        FDHEADCHAIN
        FDB     NEXT5INCHDRIVE         FDNEXTCHAIN
        FCB     %00011110              FDDRVCAPABILITY X/X/X/DS/DT/DD/DD/X
        FCB     %00011011              FDDSKCAPABILITY DS/DT/SD/DD/IBM
        FCB     %00001000              FDRESTORECMD + STEP RATE
        FCB     %00011000              FDSEEKCMD + STEP RATE
        FCB     %01001000              FDSTEPINCMD + STEP RATE
        FCB     %01101000              FDSTEPOUTCMD + STEP RATE
        RMB     1                      FDREADCMD + SIDE SELECT
        RMB     1                      FDWRITECMD + SIDE SELECT
        FCB     $7E                    FDSTATEJ
        FDB     FDUNEXPECTEDINT        FDSTATE
        FCB     18                     FDNSPT
        FDB     DIV18                  FDDIVNSPT
        FCB     256/32                 FDNBPSDIV32
        RMB     1                      FDREADWRITE
        RMB     1                      FDDOUBLESEEK
        RMB     1                      FDDOUBLESECTOR
        RMB     1                      FDPORTB
        RMB     1                      FDSEEKRETRY
        RMB     1                      FDRETRY
        FCB     0                      FDUNIT
        FCB     -1                     FDCYL
        FDB     0                      FDMAPALGORITHM
        RMB     1                      FDK1MODNSPT
        RMB     1                      FDK2MODNSPT
        RMB     1                      FDK4MODNSPT
        RMB     1                      FDK8MODNSPT
        RMB     1                      FDK16MODNSPT
        RMB     26                     FDMAP
*
        FCC     /D0:/                  DEVICE NAME
        FCB     0                      END OF DEVICE NAME
NEXTDISKDCB     SET     D0.DCB
NDISKDCBS       SET     NDISKDCBS+1
NEXT5INCHDRIVE  SET     D0.DCB
        PAGE
SDT0.DCB
        FCB     1                      DCB:DONEFLAG
        FDB     0                      DCB:LASTERROR
        FDB     SDT0.DCB+FDSIZE        DCB:NAME
        FDB     NEXTDISKDCB            DCB:NEXTDCB
        FDB     FDDRIVER               DCB:DRIVER
*
        FDB     128                    DSKINFO:NBPS
        FDB     18                     DSKINFO:NSPT
        FDB     1                      DSKINFO:NTPC
        FDB     80                     DSKINFO:NCYL
*
        RMB     1                      DSKINFO:NSPC
        RMB     LCN:SIZE               DSKINFO:MINALLOC
        RMB     LCN:SIZE               DSKINFO:MIDALLOC
        FDB     1                      DSKINFO:MAPALGORITHM
*
        RMB     1                      DSKINFO:LOG2NBPS
        RMB     2                      DSKINFO:NBPSM1
        RMB     LSN:SIZE               DSKINFO:NLSN
        RMB     LCN:SIZE               DSKINFO:NLCN
        RMB     2                      DSKINFO:NBPC
        RMB     LCN:SIZE               DSKINFO:RANDMAP
        RMB     LSN:SIZE               DSKINFO:MAPLSN
*
        RMB     2                      DSKINFO:DIRFCB
        RMB     2                      DSKINFO:MAPFCB
        RMB     2                      DSKINFO:SECTORDB
        RMB     LSN:SIZE               DSKINFO:BADLSN
        FDB     0                      DSKINFO:SEEKERRCNT
        FDB     0                      DSKINFO:SEEKERRSTS
        FDB     0                      DSKINFO:WRITEERRCNT
        FDB     0                      DSKINFO:WRITEERRSTS
        FDB     0                      DSKINFO:READERRCNT
        FDB     0                      DSKINFO:READERRSTS
        FCB     0,0,0                  DSKINFO:OPSCOUNT
        RMB     LSN:SIZE               DSKINFO:ERRLSN
        RMB     1                      DSKINFO:WRITEPROTSTATE
        IF      (*-SDT0.DCB)#DSKINFO:SIZE
        ?       INCORRECT DCB OFFSETS
        FIN
*
        FDB     .5INCHCHAINHEAD        FDHEADCHAIN
        FDB     NEXT5INCHDRIVE         FDNEXTCHAIN
        FCB     %00011110              FDDRVCAPABILITY X/X/X/DS/DT/DD/DD/X
        FCB     %00001001              FDDSKCAPABILITY SS/DT/SD/SD/IBM
        FCB     %00001000              FDRESTORECMD + STEP RATE
        FCB     %00011000              FDSEEKCMD + STEP RATE
        FCB     %01001000              FDSTEPINCMD + STEP RATE
        FCB     %01101000              FDSTEPOUTCMD + STEP RATE
        RMB     1                      FDREADCMD + SIDE SELECT
        RMB     1                      FDWRITECMD + SIDE SELECT
        FCB     $7E                    FDSTATEJ
        FDB     FDUNEXPECTEDINT        FDSTATE
        FCB     18                     FDNSPT
        FDB     DIV18                  FDDIVNSPT
        FCB     128/32                 FDNBPSDIV32
        RMB     1                      FDREADWRITE
        RMB     1                      FDDOUBLESEEK
        RMB     1                      FDDOUBLESECTOR
        RMB     1                      FDPORTB
        RMB     1                      FDSEEKRETRY
        RMB     1                      FDRETRY
        FCB     0                      FDUNIT
        FCB     -1                     FDCYL
        FDB     0                      FDMAPALGORITHM
        RMB     1                      FDK1MODNSPT
        RMB     1                      FDK2MODNSPT
        RMB     1                      FDK4MODNSPT
        RMB     1                      FDK8MODNSPT
        RMB     1                      FDK16MODNSPT
        RMB     26                     FDMAP
*
        FCC     /SDT0:/                DEVICE NAME
        FCB     0                      END OF DEVICE NAME
NEXTDISKDCB     SET     SDT0.DCB
NDISKDCBS       SET     NDISKDCBS+1
NEXT5INCHDRIVE  SET     SDT0.DCB
        FIN     IODRIVERRAM
        IF      IODRIVERPOLL
        PAGE    SSB DCB-4 DRIVER INTERRUPT POLL CODE
*
*       Interrupt poll code for DCB-4
*       HERE'S A VERY IMPORTANT NOTE THAT IS NOT PRINTED ANYWHERE TO
*       MY KNOWLEGE AND WAS A LESSON LEARNED THE HARD WAY.
*       PIA STATUS CANNOT BE READ DURING A TRANSFER TO OR FROM
*       THE DCB-4 BUFFER AND THE DISK OR THE STATUS READ WILL DESTROY
*       A BYTE OF DATA DURING THE TRANSFER. THEREFORE, USE THE
*       EXTERNAL STATUS TO DETERMINE IF THE DCB-4 IS DONE
*
        LDA     SSBEXTSTATUS           IS DCB-4 DONE
        RORA
        BCC     NOTFLOPPYINTERRUPT
        LDA     SSBPIACRB              Is it a 1791 int?
        BMI     NOTFLOPPYINTERRUPT     B/ no (inverted sense)
        LDB     SSBPIAORB              yes, ack the interrupt
*
*       LEGEND HAS IT THAT CERTAIN CONDITIONS CAN CAUSE PIA'S TO INTERRUPT
*       WHEN NOT ENABLED, SO WE CHECK INT ENABLE HERE TO MAKE SURE
*
        BITA    #%00000001             Was the int enabled?
        BNE     NOTFLOPPYINTERRUPT     B/ no (inverted sense)
        LDA     SSBWDSTATUS            Interrupt goes here, get disk status
        BITA    #WDBUSY                Is 1791 done ?
        BNE     NOTFLOPPYINTERRUPT     B/ no, must be some other device
        LDX     FDINTDCB               It is our device for sure
        LDY     DSKINFO:SECTORDB,X     Get address of RDSI
        LDB     #$F7                   Set page zero for disk int routines
        TFR     B,DP
        LDB     #\$04                  DISABLE THE PIA INTERRUPT
        STB     SSBPIACRB
        LDU     FDSTATEJ+1,X
        PSHS    U
        LDU     #FDUNEXPECTEDINT
        STU     FDSTATEJ+1,X
        TSTA                           SET CONDITION CODES ON INT STATUS BYTE
        RTS                            GO TO IT
FDUNEXPECTEDINT
        SWI
NOTFLOPPYINTERRUPT
        FIN     IODRIVERPOLL
        IF      IODRIVERBODY
        PAGE    SSB DCB-4 Floppy disk driver
*
*       DCB extension, tacks on to the end of the standard Disk Info Table
*
::      SET     *                      Remember where we are
        ORG     DSKINFO:SIZE
FDHEADCHAIN     RMB     2              Head of shared-head queue
FDNEXTCHAIN     RMB     2              Next on shared-head queue
FDDRVCAPABILITY RMB     1              Capability of the drive
FDDSKCAPABILITY RMB     1              Capability of the diskette
FDRESTORECMD    RMB     1              1791 Restore command + step rate
FDSEEKCMD       RMB     1              1791 Seek command + step rate
FDSTEPINCMD     RMB     1              1791 StepIn command + step rate
FDSTEPOUTCMD    RMB     1              1791 StepOut command + step rate
FDREADCMD       RMB     1              1791 Read command with side select
FDWRITECMD      RMB     1              1791 Write command with side select
FDSTATEJ        RMB     1              JMP instruction
FDSTATE         RMB     2              Address for jmp instruction
FDNSPT          RMB     1              PHYSICAL NSPT (DSKINFO:NSPT IS LOGICAL)
FDDIVNSPT       RMB     2              Address of fast divide routine
FDNBPSDIV32     RMB     1              NBPS/32
FDREADWRITE     RMB     1              0=READ, 1=WRITE
FDDOUBLESEEK    RMB     1              If single track disk, double track drive
FDDOUBLESECTOR  RMB     1              If SD/DD and LSN < 13
FDPORTB         RMB     1              Contains the stuff for the pia
FDSEEKRETRY     RMB     1              Number of re-seeks
FDRETRY         RMB     1              Read/write retry count
FDUNIT          RMB     1              Drive number to use
FDCYL           RMB     1              What track we're on (-1 if lost)
FDMAPALGORITHM  RMB     2              Current map algorithm
FDK1MODNSPT     RMB     1              Spiraling constant mod NSPT
FDK2MODNSPT     RMB     1              2*sc mod NSPT
FDK4MODNSPT     RMB     1              4*sc mod NSPT
FDK8MODNSPT     RMB     1              8*sc mod NSPT
FDK16MODNSPT    RMB     1              16*sc mod NSPT
FDMAP           RMB     26             Map for sector mapping
FDSIZE          equ     *              Size of the disk dcb
        ORG     ::                     Restore the p-counter
*
*       Disk capability parameters
*
CAP:BITSINVALID EQU     32             Bits invalid, use defaults on mount
CAP:DOUBLESIDE  EQU     16             Double sided disk
CAP:DOUBLETRACK EQU     8              Double track disk
CAP:DDTRACK1    EQU     4              Double density on first track
CAP:DDTRACK2    EQU     2              Double density on second track
CAP:IBMNUMBERS  EQU     1              Sector numbers are IBM style (1..26)
       PAGE
FDDRIVER                               ; Driver entry points
        FDB     FDRESTORE
        FDB     FDREAD
        FDB     FDWRITE
        FDB     FDWAITDONE
        FDB     ILLDEVICEOP            FDSTATUS handled completely by SDOS1.1
        FDB     FDCONTROL
*
*       FDCONTROL -- CONTROL OPERATION ENTRY POINT FOR SECTOR I/O DRIVER
*
FDCONTROL
        CMPA    #CC:DISMOUNTDISK       SINCE SDOS PASSES THIS THRU
        BEQ     FDDISMOUNT             B/ ITS A DISMOUNT!
        JMP     ILLDEVICEOP            NOT A LEGAL CONTROL CALL
*
*       FDREAD/WRITE -- START SINGLE SECTOR TRANSFER
*
FDREAD  LDA     #RDSISTATE:READING
        BRA     FDWRITE0
FDWRITE LDA     #RDSISTATE:WRITING
FDWRITE0        ; Assert SDOS checks DCB:DONEFLAG before calling
        JSR     FDSETUPDRIVE           GO SET UP ALL THE PARAMETERS IN THE DCB
        BCS     FDWRITE1               B/ ERROR
        LDX     #FDINTFREE             See if controller is busy
        LDA     0,X
        BNE     FDSTARTIO              B/ not busy
        JSR     SDOS+SDOS:WAITEVENT    It's busy, wait for controller free
FDSTARTIO
        LDX     DCBPOINTER             NOW NOBODY'S USING DRIVE
        CLR     DCB:DONEFLAG,X         KICK INTERRUPT ROUTINE INTO MOTION
        TFR     X,D
        LDX     #FDINTSTARTDCB4
        JSR     SDOS+SDOS:STARTIO      DCBPOINER PASSED TO
FDDISMOUNT,FDWAIT2
        CLRB                           CLEAR THE CARRY
FDWRITE1
        RTS
*
FDWAITDONE      ; ASSERT X CONTAINS DCB POINTER ???
        LDA     DCB:DONEFLAG,X         IS IT DONE?
        BNE     FDWAIT1                B/ YES
        JSR     SDOS+SDOS:WAITEVENT    X conveniently points to condition byte
        LDX     DCBPOINTER             DCB POINTER INVALID HERE ???
FDWAIT1 LDX     DCB:LASTERROR,X
        BEQ     FDWAIT2                B/ NO ERRORS
        JMP     ERRETX
        PAGE
*
*       Build the map
*       A,B contain the map algorithm
*       X contains the DCB pointer
*
FDBUILDMAP
        STD     FDMAPALGORITHM,X       Remember the new map
        NEGB
        LDA     FDNSPT,X               Do it this many times
        STX     TEMPX                  Remember beginning (end) of list
FDBUILDMAP1
        LDX     DCBPOINTER
        ADDB    DSKINFO:MAPALGORITHM+1,X Compute next physical sector number
FDBUILDMAP2
        LDX     DCBPOINTER
        BSR     FDMODULONSPTB          Shave off the excess
        LEAX    -1,X
FDBUILDMAP3
        LEAX    1,X                    End of list?
        CPX     TEMPX
        BEQ     FDBUILDMAP4            B/ yes, use this hole
        CMPB    FDMAP,X                Is it in the list already?
        BNE     FDBUILDMAP3            B/ no, keep looking
        INCB                           Yes, duplicate; add one and try again
        BRA     FDBUILDMAP2
FDBUILDMAP4
        STB     FDMAP,X                Use this sector number
        LEAX    1,X
        STX     TEMPX
        DECA                           Done yet?
        BNE     FDBUILDMAP1            B/ no, go manufacture next sector number
*
*       Build up the spiraling info
*
*       This info is used later to compute the Physical Sector Number
*       on a track. It is intended to provide enough latency to allow the
*       head to step from cylinder to adjacent cylinder with enough head load
*       time to optimize reading or writing across a track boundary.
*       To find the correct PSN, the cylinder number is multiplied by the
*       spiraling constant, then sector latency tuning is applied, modulo NSPT.
*       It is a happy circumstance that the modulo of a product is the same as
*       the modulo of the modulo of the terms of the product:
*               mod(x*y)=mod(mod(x)*mod(y))
*       It is also true that:
*               mod(x+y)=mod(mod(x)+mod(y))
*       This allows us to do some tricks.
*       We wish to compute
*               offset=mod(spiraling constant*cylinder number)
*       We certainly can take the modulo of the terms before the multiply:
*               offset=mod(mod(spiraling)*mod(cylinder))
*       It is obvious that the cylinder can be expressed as a sum of
*       powers of 2:
*               mod(cyl)=a7*2^7 + a6*2^6 +...+ a0*2^0
*       Furthermore, since mod(cyl)<NSPT (NSPT=26, the modulo),
*       and the modulo of any number to the base NSPT yields a number less
*       than 26 (5 bits), a7, a6, and a5 are therefore 0:
*               mod(cyl)=a4*2^4 + a3*2^3 +...+ a0*2^0
*       and, using the notation ms=mod(spiraling), we have:
*               offset=ms*a4*2^4 + ms*a3*2^3 +...+ ms*a0*2^0
*               =mod(mod(ms*a4*2^4) + mod(ms*a3*2^3) +...+ mod(ms*a0*2^0))
*       If we pre-compute these terms, finding the offset reduces to
*       adding in a constant for each one-bit in the cylinder number,
*       and taking the modulo of that sum.
*               =mod(a4*c4 + a3*c3 + ... + a0*c0)
*       where a4..a0 are the bits in the cylinder number
*       and c4..c0 are the pre-computed constants.
*
        LDX     DCBPOINTER
        LEAU    FDK1MODNSPT,X
        LDA     DSKINFO:MAPALGORITHM,X
        LDB     #5                     DO IT THIS MANY TIMES
FDBUILDMAP5
        BSR     FDMODULONSPT
        STA     ,U+
        ASLA
        DECB
        BNE     FDBUILDMAP5
        RTS                            Done
        PAGE
FDMODULONSPT
        SUBA    FDNSPT,X
        BCC     FDMODULONSPT
        ADDA    FDNSPT,X
        RTS
FDMODULONSPTB
        SUBB    FDNSPT,X
        BCC     FDMODULONSPTB
        ADDB    FDNSPT,X
        RTS
*
*       FDSETUPDRIVE -- SETS UP FDDRIVE TABLE FOR INTERRUPT DRIVEN TRANSFER
*
FDSETUPDRIVE 
        LDX     DCBPOINTER             GET DCB POINTER
        LDY     DSKINFO:SECTORDB,X     GET RDSI POINTER
        STA     FDREADWRITE,X
        LDD     #2*256+8               --> 2 "RESTORE TO HOME" TRIES
        STD     FDSEEKRETRY,X
*       STB     FDRETRY,X              SAVE THE READ/WRITE RETRY COUNT
        CLRA
        CLRB
        STD     DCB:LASTERROR,X        "NO ERRORS"
        BSR     FDTEARLSN              FIND OUT WHAT CYL, TRACK AND SECTOR
*
*       Check track capability of drive against track configuration of disk.
*       If capability and configuration match, do a vanilla (plain) seek.
*       Else, it must be single track disk, double track drive (double track
*       disk, single track drive would already have been disallowed at mount
*       time). In this case, seek twice.
*
        CLR     FDDOUBLESEEK,X         Assume no double seek required
        LDA     FDDSKCAPABILITY,X      Same capability?
        EORA    FDDRVCAPABILITY,X
        ANDA    #CAP:DOUBLETRACK
        BEQ     FDSETUP1               B/ yes, do nothing special
        INC     FDDOUBLESEEK,X         no, do double seek
        LDA     FDDRVCAPABILITY,X      IS IT A DOUBLE TRACK DRIVE?
        ANDA    #CAP:DOUBLETRACK
        BNE     FDSETUP1               B/ YES, DOUBLE SEEK IS NO PROBLEM
        LDX     #ERR:WRONGDISKTYPE     NO, CAN'T DOUBLE SEEK ON THIS DRIVE
        ERRORRTS

*
*       Build the control word for the DCB-4 (PIA PORT B).
*
FDSETUP1
        LDB     FDUNIT,X               Start with the unit number
        ORB     #%10010000             Set head load enable, xfer enable
*                                      assume SD, read, side 0
*
*       The sector is double density if DD/DD or (SD/DD & LSN >= 13(9))
*
        LDA     FDDSKCAPABILITY,X      Get disk capability
        ANDA    #CAP:DDTRACK1+CAP:DDTRACK2 Only look at density
        CMPA    #CAP:DDTRACK1+CAP:DDTRACK2 DD/DD?
        BEQ     FDSETUP2               B/ yes, DD/DD
        CMPA    #CAP:DDTRACK2          SD/DD?
        BNE     FDSETUP3               B/ no
        LDU     RDSI:LSN+1,Y           Get the LSN
        CMPU    DSKINFO:NSPT,X         Yes, see if >= 13(9)
        BLO     FDSETUP3               B/ no, SD
FDSETUP2
        ORB     #%01000000             It's a double density sector
*
*       Factor in the read/write data direction
*
FDSETUP3
        LDA     FDREADWRITE,X
*       LDA     RDSI:STATE,Y
        CMPA    #RDSISTATE:WRITING     Are we writing?
        BNE     FDSETUP4               B/ no, reading or verifying
        ORB     #%00100000             Yes, set the write direction
*
*       Side 1 is selected if RDSI:TRACK=1
*
FDSETUP4
        LDA     RDSI:TRACK+1,Y         Get the track (head) number
        BEQ     FDSETUP5               B/ side 0
        ORB     #%0001000              Set the side
FDSETUP5
        COMB                           It's inverted data
        STB     FDPORTB,X              Remember this for later
*
*       Set the side select for the 1791 read and write commands
*
        LDA     #%10000010             STANDARD READ COMMAND
        LDB     RDSI:TRACK+1,Y         GET TRACK (HEAD) NUMBER
        BEQ     FDSETUP6               B/ SIDE 0
        ORA     #%00001000             SET SIDE 1
FDSETUP6
        STA     FDREADCMD,X
        ORA     #%00100000
        STA     FDWRITECMD,X
        OKRTS
        PAGE
*
*       Take RDSI:LSN and split it up into RDSI:CYLINDER,
*       RDSI:TRACK, and RDSI:SECTOR, applying the map
*
FDTEARLSN
        LDX     DCBPOINTER
        LDY     DSKINFO:SECTORDB,X     GET ADDRESS OF RDSI
        CLR     RDSI:SECTOR,Y          CLEAR MSB OF SECTOR, TRACK AND CYL
        CLR     RDSI:TRACK,Y
        CLR     RDSI:CYLINDER,Y
        LDD     DSKINFO:MAPALGORITHM,X HAS THE MAP CHANGED?
        CMPD    FDMAPALGORITHM,X       ...?
        BEQ     FDTEARLSN1             B/ NO
        BSR     FDBUILDMAP             YES, RE-BUILD THE MAP
FDTEARLSN1
*       ASSERT RDSI:LSN ALREADY CHECKED FOR LEGAL BY SDOS
*
*       Since there are 31 flavors of disks, I am laying down some
*       notes here about this.
*
*       For SDOS 1.1, the disk configuration switches (S-1 on the DCB-4)
*       are ignored and the parameters are hard wired into the DCB
*       (Device Control Block). For SDOS 1.2, the disk configuration
*       switches are used as a default and a control call allows
*       dynamically changing the characteristics of the DCB (Device Control
*       Block) when the disk is opened as a disk device, and before a
*       mount or after a dismount.
*
*       The formulae for computing the DCB parameters as a function
*       of number of sides, single density (SD) or double density (DD)
*       first track, SD or DD on other than first track, and number of
*       tracks on the drive are:
*               SD/SD   NBPS = 128
*                       NSPT = 26
*                       NTPC = 1 (See note 1)
*                       NCYL = #sides * #tracks (See note 2)
*
*               SD/DD   NBPS = 256 (See note 3)
*                       NSPT = 13 (See note 4)
*                       NTPC = 1 (See note 1)
*                       NCYL = 2 * #sides * #tracks - 1 (See note 2,4,5)
*
*               DD/DD   NBPS = 256
*                       NSPT = 26
*                       NTPC = 1 (See note 1)
*                       NCYL = #sides * #tracks (See note 2)
*
*               note 1: This is a lie to SDOS in the case of a double
*                       sided disk, but it makes the NLSN (NSPT*NTPC*NCYL)
*                       work out to be correct.
*               note 2: #tracks is the number of tracks on the disk, including
*                       double tracks on a double track disk.
*               note 3: On the first track of a SD/DD disk, the 26 sectors
*                       of 128 bytes are treated as 13 sectors of 256 bytes
*               note 4: Since the first track of a SD/DD disk is treated as
*                       13 sectors of 256, the remaining tracks are considered
*                       to be 2 chunks of 13 sectors. This makes the disk look
*                       like an integral multiple of half-tracks, rather than
*                       a fractional multiple of whole-tracks.
*               note 5: The -1 comes from the first track of the SD/DD disk
*                       having the capacity of one half-track, while the other
*                       tracks contain 2 half-tracks (a half-track is 13
*                       sectors).
*
*     +-----------------------------------------------------+
*     |        8" DRIVE       | SINGLE SIDED | DOUBLE SIDED |
*     | # TRACKS ON DRIVE --> |       77     |      77      |
*     |         NSPT NBPS NTPC|      NCYL    |     NCYL     |
*     |-----------------------+--------------+--------------|
*     |   SD/SD  26   128    1|       77     |     154      |
*     |   SD/DD  13   256    1|      153     |     307      |
*     |   DD/DD  26   256    1|       77     |     154      |
*     +-----------------------------------------------------+
*
*     +---------------------------------------------------------------+
*     |        5" DRIVE       |   SINGLE SIDED    |   DOUBLE SIDED    |
*     | # TRACKS ON DRIVE --> | 35   40   70   80 | 35   40   70   80 |
*     |         NSPT NBPS NTPC|NCYL NCYL NCYL NCYL|NCYL NCYL NCYL NCYL|
*     |-----------------------+-------------------+-------------------|
*     |   SD/SD  18   128    1| 35   40   70   80 | 70   80  140  160 |
*     |   SD/DD   9   256    1| 69   79  139  159 |139  159  279  319 |
*     |   DD/DD  18   256    1| 35   40   70   80 | 70   80  140  160 |
*     +---------------------------------------------------------------+
*
*       To compute the sector, track and cylinder, use this formula:
*               LSN/26 --> SECTOR#
*                      --> REM/2 --> TRACK (HEAD)
*                                --> REM --> CYLINDER
*       where REM is the remainder from the previous step. If SD/DD disk,
*       and the LSN >=13, substitute (LSN+13) instead of (LSN) in the above
*       formula (this accounts for the first physical track containing a
*       logical half-track of data).
*
*       This is a double sector operation if the disk is SD/DD
*       and the sector number <= 12. This is because on DD disks
*       with SD on first track, 2 physical sectors = one logical sector.
*
        CLR     FDDOUBLESECTOR,X       Assume single sector operation
        LDB     FDDSKCAPABILITY,X      Check for SD/DD disk
        LDA     RDSI:LSN+1,Y           Get half of the LSN
        ANDB    #CAP:DDTRACK1+CAP:DDTRACK2 Is it SD/DD?
        CMPB    #CAP:DDTRACK2          ...?
        BNE     FDTEARLSN2             B/ no
        LDB     DSKINFO:NSPT+1,X       Yes, get NSPT/2 (13 or 9)
        TSTA                           Is the LSN >=13(9)?
        BNE     FDTEARLSN3             B/ yes, add in 13(9)
        CMPB    RDSI:LSN+2,Y           ...?
        BLS     FDTEARLSN3             B/ yes, add in 13(9)
        INC     FDDOUBLESECTOR,X       It's a double sector operation
FDTEARLSN2
        CLRB                           No, must be 1st track, no compensation
FDTEARLSN3
        ADDB    RDSI:LSN+2,Y
        ADCA    #0
        CLR     RDSI:TRACK+1,Y         Assume track (head) 0
        JSR     [FDDIVNSPT,X]          GO DO THE FAST DIVIDE BY NSPT
        STB     RDSI:CYLINDER+1,Y      B CONTAINS CYL
        STA     RDSI:SECTOR+1,Y        A CONTAINS SECTOR
        LDB     FDDOUBLESECTOR,X       Is this a double sector operation?
        BNE     FDMAPDONE              B/ yes, no mapping for this sector
*
*       Factor in the latency tuning
*
        LEAU    FDMAP,X
        LDB     A,U
*
*       If we are switching heads, add in another round of latency tuning
*
        LDA     FDDSKCAPABILITY,X      IS THIS A DOUBLE SIDE DISK?
        BITA    #CAP:DOUBLESIDE
        BEQ     FD.MAP                 B/ not switching heads
        LSR     RDSI:CYLINDER+1,Y      DIVIDE CYLINDER BY 2
        ROL     RDSI:TRACK+1,Y         REMAINDER IS TRACK NUMBER
        BEQ     FD.MAP                 B/ SIDE 0
        ADDB    FDMAPALGORITHM+1,X     This offset for same cyl, dif track
*
*       Start with the cylinder number mod NSPT
*       and add in the spiraling
*
FD.MAP  LDA     RDSI:CYLINDER+1,Y
        BSR     FDMODULONSPT
        ASRA
        BCC     FD.MAP1
        ADDB    FDK1MODNSPT,X
FD.MAP1 ASRA
        BCC     FD.MAP2
        ADDB    FDK2MODNSPT,X
FD.MAP2 ASRA
        BCC     FD.MAP3
        ADDB    FDK4MODNSPT,X
FD.MAP3 ASRA
        BCC     FD.MAP4
        ADDB    FDK8MODNSPT,X
FD.MAP4 ASRA
        BCC     FD.MAP5
        ADDB    FDK16MODNSPT,X
FD.MAP5 BSR     FDMODULONSPTB
        STB     RDSI:SECTOR+1,Y
FDMAPDONE OKRTS
*
*       Fancy divide algorithm takes (A,B) and divides by NSPT (18).
*       If we have 80 track, double side,
*       18 sectors/track this is 80*2*18 LSNs =0..2879, with a max
*       quotient of 159.
*
*       Divide (A,B) by 18 (NSPT), quotient in B, remainder in A
*
DIV18   ASLB
        ROLA
        ADDA    #-18                   ADD -NSPT TO GENERATE QUOTIENT BIT
        BCS     DIV18.1                B/ Q BIT IS ONE
        SUBA    #-18                   RESTORE BY ADDING DIVISOR; LEAVE CARRY=0
DIV18.1 ROLD                           SHIFT QUOTIENT BIT INTO QUOTIENT
        ADDA    #-18
        BCS     DIV18.2
        SUBA    #-18
DIV18.2 ROLD
        ADDA    #-18
        BCS     DIV18.3
        SUBA    #-18
DIV18.3 ROLD
        ADDA    #-18
        BCS     DIV18.4
        SUBA    #-18
DIV18.4 ROLD
        ADDA    #-18
        BCS     DIV18.5
        SUBA    #-18
DIV18.5 ROLD
        ADDA    #-18
        BCS     DIV18.6
        SUBA    #-18
DIV18.6 ROLD
        ADDA    #-18
        BCS     DIV18.7
        SUBA    #-18
DIV18.7 ROLD
        ADDA    #-18
        BCS     DIV18.8
        SUBA    #-18
DIV18.8 ROLB
        RTS
*
*       Fancy divide algorithm takes (A,B) and divides by NSPT (26).
*       If we have 77 track, double side,
*       26 sectors/track this is 77*2*26 LSNs =0..4003, with a max
*       quotient of 153.
*
*       Divide (A,B) by 26 (NSPT), quotient in B, remainder in A
*
DIV26   ASLB
        ROLA
        ADDA    #-26                   ADD -NSPT TO GENERATE QUOTIENT BIT
        BCS     DIV26.1                B/ Q BIT IS ONE
        SUBA    #-26                   RESTORE BY ADDING DIVISOR; LEAVE CARRY=0
DIV26.1 ROLD                           SHIFT QUOTIENT BIT INTO QUOTIENT
        ADDA    #-26
        BCS     DIV26.2
        SUBA    #-26
DIV26.2 ROLD
        ADDA    #-26
        BCS     DIV26.3
        SUBA    #-26
DIV26.3 ROLD
        ADDA    #-26
        BCS     DIV26.4
        SUBA    #-26
DIV26.4 ROLD
        ADDA    #-26
        BCS     DIV26.5
        SUBA    #-26
DIV26.5 ROLD
        ADDA    #-26
        BCS     DIV26.6
        SUBA    #-26
DIV26.6 ROLD
        ADDA    #-26
        BCS     DIV26.7
        SUBA    #-26
DIV26.7 ROLD
        ADDA    #-26
        BCS     DIV26.8
        SUBA    #-26
DIV26.8 ROLB
        RTS
        PAGE    SSB Chieftain DCB-4 1791 Disk interrupt routines
        SETDPR  $F700                  Valid for all int level code
*
*       Equates for SSB Chieftain DCB-4 1791 floppy disk controller
*
SSBIOBUFFER     EQU     $F760 OR $F761 READ OR WRITE 1K BUFFER ON DCB-4
SSBIOBUFADRHI   EQU     $F762          WRITE ADDRESS COUNTER FOR DCB-4 BUFFER
SSBIOBUFADRLO   EQU     $F763          WRITE ADDRESS COUNTER FOR DCB-4 BUFFER
SSBEXTSTATUS    EQU     $F762 OR $F763 READ EXTERNAL STATUS
SSBPIAORA       EQU     $F764          PIA register controlling floppy
SSBPIAORB       EQU     $F765          PIA register controlling floppy
SSBPIACRA       EQU     $F766          PIA register controlling floppy
SSBPIACRB       EQU     $F767          PIA register controlling floppy
SSBWDSTATUS     EQU     $F76C          WD 1791 Status Register
SSBWDCMD        EQU     $F76C          WD 1791 Command Register
SSBWDTRACK      EQU     $F76D          WD 1791 Track Editor
SSBWDSECTOR     EQU     $F76E          WD 1791 Sector Register
SSBWDDATA       EQU     $F76F          WD 1791 Data Register
*
WDLATE          EQU     %00000100      1791 DATA LATE SENSE MASK
WDDRQ           EQU     %00000010      Data Request Bit sense mask
WDBUSY          EQU     %00000001      1791 Busy Flag Sense mask
        PAGE
*
*       Start I/O for SSB DCB-4 1791 disk controller
*       ASSERT: INTERRUPTS ARE DISABLED HERE!
*       and in all routines labelled FDINT...
*
FDINTSTARTDCB4
        STD     FDINTDCB
        LDA     #$F7                   SET THE DP
        TFR     A,DP
        DEC     FDINTFREE              Mark controller busy
        BEQ     FDINTSTART1            B/ CONTROLLER NOT PREVIOUSLY BUSY
        SWI                            ENTERED TWICE!!
FDINTSTART1
        LDX     FDINTDCB
        LDY     DSKINFO:SECTORDB,X     Get pointer to RDSI
        PAGE
*
*       SEEK -- See if the head must be moved with a seek operation;
*       if it must, the seek is done without verification, as a seek
*       failure will be picked up by a subsequent read/write as
*       "record not found" status, for which the remedy will be
*       a restore operation.  The restore operation IS verified; if
*       it fails, a "seek error" is registered, and the restore is
*       retried, up to the seek-retry count.
*
*       I assert that a seek with verify is not normally reqired
*       when we switch units, because we know where the head is
*       and it did not (assumption) move while the drive was de-selected.
*       Besides, if it is now on the wrong cylinder, the read or write
*       will tell us 'record not found.' However, there is one dissenting
*       opinion that says 'when the drive is de-selected, power that normally
*       holds the heads on track is removed and the heads are free to move,
*       presumably by being jarred or vibrated. Well, we'll try it my way,
*       and if it proves to be a mistake, the commented-out code following
*       should correct the problem (3 instructions).
*
*       Smoke implementation reqires seek always to ensure head is loaded.
*
FDSEEK  ; See if Seek is needed
        LDA     FDPORTB,X              SELECT THE DRIVE WE WANT
        STA     SSBPIAORB              SO WE CAN DO THE SEEK
        LDA     FDCYL,X                Tell the 1791 where the heads are
        CMPA    #-1                    Am I lost?
        BEQ     FDSEEKHOME             B/ yes, go restore the drive
        STA     SSBWDTRACK
*       LDB     FDINTDRIVE             Drive changed ?
*       CMPB    FDUNIT,X               ...?
*       BNE     FDSEEKREQUIRED         B/ yes, seek needed to verify on track
*SSB*   CMPA    RDSI:CYLINDER,X        Are we already on the right track?
*SSB*   BEQ     FDSEEKDONE             B/ yes, no seek needed
FDSEEKREQUIRED  ; No way to avoid seeking
        LDA     RDSI:CYLINDER+1,Y      Where the heads have to go
        STA     SSBWDDATA
        LDB     FDDOUBLESEEK,X         Is a double seek required?
        BEQ     SAMETRACKCAPABILITY    B/ no, same track capability
        LDA     FDSEEKCMD,X            Get seek command/step rate for drive
        JSR     FDINTISSUECOMMAND      Tell 1791 what to do
        BITA    #%00011000             Seek or CRC error?
        BEQ     FDSEEK1                B/ NO
        SWI                            B / yes, hang (See discussion below)
FDSEEK1 LDA     FDCYL,X                Tell the 1971 where the heads are
        STA     SSBWDTRACK
        LDA     RDSI:CYLINDER+1,Y      Where the heads should be
        STA     SSBWDDATA              This will get us to the right track
SAMETRACKCAPABILITY
*
*       I am not doing seek with verify because I assert that the drive
*       is reliable (assumption). If the drive is reliable, then it is
*       pointless to seek with verify, considering the read and write
*       verify automatically. If the drive is not reliable, then (get
*       one that is) the read and write will error and recover correctly.
*       Furthermore, if we do end up on the wrong track, both the seek
*       with verify and the read/write take 5 revs to complain about the
*       wrong track, so again assume it's right and let the read/write
*       tell us otherwise.
*
        LDA     FDSEEKCMD,X            Get seek command/step rate for drive
        JSR     FDINTISSUECOMMAND      Tell 1791 what to do
*
*       A seek without verify can't have a CRC or SEEK ERROR status
*       from the 1791. It's like adding 1 and 1 to get 3, it just can't
*       happen. If it does, something is really wrong.
*
        BITA    #%00011000             Seek or CRC error?
        BEQ     FDSEEK2                B/ NO
        SWI                            B/ yes, hang
FDSEEK2 LDA     RDSI:CYLINDER+1,Y      I assert that I am on the right track
        JSR     FDSETCYLADD            (Well, I'll find out when I try to read)
FDSEEKDONE      ; (X) contains DCB address,
*
*       This code is here because mini-floppies don't have ready line
*       (unbelievable!!)
*
*       Wait for Disk Ready (index hole not present)
*
        LDX     #20000*2/(8+(2+4+2+60+4+4+4+5)+2+6+5+3+5+5+3)
*               = delay longer than index hole time
*
*       Note: the summed numbers in the divisor are cycle counts...
*       of instructions executed in FDINTWAITREADY
*       (the nested parentheses are cycle counts for FDABORT)
*       The "4000" represents a 4 millisecond delay (nominal hole time)
*       and the 2 represents a 2Mhz computer
*       ps, 4000 not enough using 20000 now
*
FDINTWAITREADY
        JSR     FDABORT                Get type I status
        BITB    #%00000010             Over index hole ?
        LBEQ    FDINTREADY             No, must be diskette in drive
        PSHS    CC                     INTS ON FOR A MOMENT
        CLI
        PULS    CC
        DEX                            Down count the delay
        BNE     FDINTWAITREADY         B/ could still be index hole...
        LDD     #ERR:DEVICENOTREADY
        LDX     FDINTDCB               RESTORE THE DCB POINTER
        BRA     FDERROR                Go tell the world
*
*       I HEARD A RUMOR ABOUT STEPPING IN (5 TRACKS) FIRST BEFORE
*       ISSUING THE RESTORE COMMAND, WITH SOME REFERENCE TO A 1791
*       APP NOTE ??? (NOT DONE HERE)
*
FDSEEKHOME
        JSR     FDABORT                Kill whatever disk is doing
        LDA     FDRESTORECMD,X         Grab restore command
        BSR     FDINTISSUECOMMAND      Go do the restore
        BITA    #%00000100             Check for arrival at cylinder zero
        BEQ     FDSEEKHOME1            B/ didn't get to cyl 0
        CLRA
        JSR     FDSETCYLADD            SET "I'M AT CYLINDER 0"
        BRA     FDSEEK                 GO TRY SEEK TO PROPER TRACK AGAIN
FDSEEKHOME1
        STA     DSKINFO:SEEKERRSTS+1,X count a seek error, and
        INC     DSKINFO:SEEKERRCNT+1,X save the error status
        BNE     FDSEEKHOME2
        INC     DSKINFO:SEEKERRCNT,X
FDSEEKHOME2
        LDA     #-1                    Say that I lost my place...
        BSR     FDSETCYLADD
        DEC     FDSEEKRETRY,X          DOWN COUNT # TRIES LEFT
        BNE     FDSEEKHOME             B/ try again
        PAGE
FDSEEKERROR
        LDD     #ERR:DISKSEEK
        BRA     FDERROR
WRITEPROTECTERR
        LDD     #ERR:DSKWRTPROT
        BRA     FDERROR
READERROR
        LDD     #ERR:DISKREAD
        BRA     FDERROR
WRITEERROR
        LDD     #ERR:DISKWRITE
*       BRA     FDERROR
FDERROR STD     DCB:LASTERROR,X
FDDONE  LDX     FDINTDCB               To handle entry when DiskRead/Write done
        INC     DCB:DONEFLAG,X         Signal "disk done"
        INC     FDINTFREE              So task knows we're free
*
*       Deselect all the drives (select drive 7)
*       This makes the indicator light go out
*       (NOT REQUIRED FOR SMOKE IMPLEMENTATION)
*
*SSB*   LDA     #\%10000111            Deselect all drives by selecting #7
*SSB*   STA     SSBPIAORB
        JMP     SDOS+SDOS:RTI
        PAGE
*
*       Issue the 1791 command in (A)
*       Return address points to the interrupt routine continuation point
*       Also count the statistics
*
FDINTISSUECOMMAND
        LDB     #\$07
        STB     SSBPIACRB              INT ON POSITIVE CB1 (1791 INTERRUPT)
        LDB     SSBPIAORB              CLEAR THE INT
        STA     SSBWDCMD               Tell the 1791 to stick this up his...
        LDD     #6*TICKSPERSECOND+NTIMEOUTBLOCKS Set up time-out on operation
        STD     FDINTTIMEOUTBLOCK+TIMEOUT:FUSE
COUNTCOMMAND    ; COMMAND HAS BEEN ISSUED, COUNT IT IN DCB
        INC     DSKINFO:OPSCOUNT+2,X   COUNT # OPERATIONS ISSUED TO FLOPPY
        BNE     COUNTCOMMAND1
        INC     DSKINFO:OPSCOUNT+1,X
        BNE     COUNTCOMMAND1
        INC     DSKINFO:OPSCOUNT,X
COUNTCOMMAND1
        PULS    D                      Re-aim state vector to return addr
        STD     FDSTATEJ+1,X
FDTIMEOUT2
        JMP     SDOS+SDOS:RTI
*
*       Come here when a time out happened
*       which is 6 seconds after the last disk operation
*       if the operation is complete, then all is well
*       otherwise, give timed out error
*
FDTIMEOUT
        LDA     #$F7
        TFR     A,DP
        LDX     FDINTDCB               point at DCB, again
        LDY     DSKINFO:SECTORDB,X     POINT TO RDSI, AGAIN
        LDA     DCB:DONEFLAG,X         IS THE DISK DONE?
        BNE     FDTIMEOUT2             B/ DISK IS DONE, GO AWAY
        BSR     FDABORT                Kill any disk activity
        LDA     #-1                    Timed out --> Head location is unknown!
        STA     FDINTDRIVE             FORCE SEEK W/VERIFY ON NEXT READ/WRITE
        BSR     FDSETCYLADD
        LDD     #ERR:DEVICETIMEDOUT
        BRA     FDERROR                This marks DCB as 'done'
*
*       Make the controller idle and get status
*       Issue abort command, wait 28us for status to be valid,
*       then read it and ack the interrupt (before it happens).
*
FDABORT LDA     #%11010000             abort with no interrupts
        STA     SSBWDCMD
        LDA     #(28*2)//(2+3)         = # times around loop to make 28 Us.
        DECA                           2 cycles
        BNE     *-1                    3 cycles
        LDA     SSBWDDATA              To acknowledge any surprise interrupt
*                                      (i.e., Data Late just before Abort cmd)
        LDA     SSBPIAORB              Make any interrupt dissapear before
*                                      it can cause trouble
        LDB     SSBWDSTATUS            return with status in B
        RTS
*
*       Set the cylinder address of the current DCB to (A),
*       also setting all other DCB's sharing the same head
*
FDSETCYLADD
        LDU     FDHEADCHAIN,X
FDSETCYLADD.1
        STA     FDCYL,U                all DCB's sharing the same head
        LDU     FDNEXTCHAIN,U            mechanism are chained together
        BNE     FDSETCYLADD.1              so that all FDCYL values will
        RTS                                  be equally correct
        PAGE
*
*       Disk is ready for the read, write or verify
*
FDINTREADY
        LDX     FDINTDCB               RESTORE THE DCB POINTER
        TSTB
        BPL     FDINTREADY1            B/ Ready, go do read or write
*
*       Rumor has it that this code is unnecessary for Tandon drives,
*       But Rumor did not say if it was needed for T & E drives!
*
FDMAKEREADY     ; Disk is not ready, make it ready
        LDA     #%11010001             abort with interrupt on Ready transition
        JSR     FDINTISSUECOMMAND
FDINTREADY1     ; Now disk is Ready (Up to speed); Do the Read or Write
*
*       Set density, data direction, side, transfer enable.
*       Head load and unit unchanged
*
        LDB     FDPORTB,X
        STB     SSBPIAORB
        LDA     FDUNIT,X               IS THIS THE SAME UNIT AS LAST TIME?
        CMPA    FDINTDRIVE
        BEQ     FDINTSAMEUNIT          B/ YES
        STA     FDINTDRIVE             NO, REMEMBER IT FOR NEXT TIME
        EORB    #$80                   AND FLOG THE HEAD
        STAB    SSBPIAORB
        EORB    #$80                   AND FLOG THE HEAD
        STAB    SSBPIAORB
FDINTSAMEUNIT
        LDA     RDSI:SECTOR+1,Y        Get the desired sector number
        LDB     FDDOUBLESECTOR,X       Is this a double sector operation?
        BEQ     FDRW1                  B/ no
        ASLA                           yes, *2
FDRW1   LDB     FDDSKCAPABILITY,X      Add IBM offsets if required
        RORB
        ADCA    #0
        STA     SSBWDSECTOR            Tell 1791 what sector to search for
        LDD     #\$0000                RESET THE DCB-4 BUFFER ADDRESS COUNTER
        STD     SSBIOBUFADRHI
*       LDA     RDSI:STATE,Y           Go off and do the read or write
        LDA     FDREADWRITE,X
        CMPA    #RDSISTATE:WRITING
        BEQ     FDINTWRITE
        CMPA    #RDSISTATE:READING
        BNE     *
        JMP     FDINTREAD
        PAGE
*
*       Sector number register is set, write a sector
*
FDINTWRITE
        JSR     FDABORT                Get disk status
        BITB    #%01000000             IS DISK WRITE PROTECTED?
        BNE     WRITEPROTECTERR        B/ Congrat, User is Prize Dummy!
        LDY     RDSI:SECTORBASE,Y      Point to write buffer
        LEAY    16,Y
        LDA     FDNBPSDIV32,X          Get number of loop iterations
*
*       Fill up the on-board I/O buffer
*       186 CYCLES/32 BYTES
*       =93us/32 BYTES
*       =2.9us/BYTE
*       =372us/128 BYTE SECTOR
*
FDINTWRITE1
        LDU     -16,Y                  Get two bytes
        STU     SSBIOBUFFER            Output two bytes
        LDU     -14,Y
        STU     SSBIOBUFFER
        LDU     -12,Y
        STU     SSBIOBUFFER
        LDU     -10,Y
        STU     SSBIOBUFFER
        LDU     -8,Y
        STU     SSBIOBUFFER
        LDU     -6,Y
        STU     SSBIOBUFFER
        LDU     -4,Y
        STU     SSBIOBUFFER
        LDU     -2,Y
        STU     SSBIOBUFFER
        LDU     0,Y
        STU     SSBIOBUFFER
        LDU     2,Y
        STU     SSBIOBUFFER
        LDU     4,Y
        STU     SSBIOBUFFER
        LDU     6,Y
        STU     SSBIOBUFFER
        LDU     8,Y
        STU     SSBIOBUFFER
        LDU     10,Y
        STU     SSBIOBUFFER
        LDU     12,Y
        STU     SSBIOBUFFER
        LDU     14,Y
        STU     SSBIOBUFFER
        LEAY    32,Y
        DECA                           Done?
        BNE     FDINTWRITE1            B/ no
*
*       LDY     DSKINFO:SECTORDB,X     Get back pointer to RDSI
        LDD     #\$0000                Init the floppy buffer pointer
        STD     SSBIOBUFADRHI
        LDA     FDWRITECMD,X           "write sector" command + side select
        BSR     FDINTISSUECOMMAND      go tell the disk what to do
        BNE     FDINTWRITE2            B/ error status, try to recover
        LDA     FDDOUBLESECTOR,X       Is this a double sector?
        BEQ     FDINTVERIFY            B/ no, go do verify
*
*       This is a double sector operation.
*       We are on the first track of a SD/DD disk.
*       One logical sector (256 bytes) = 2 physical sectors,
*       so we must do it one more time
*
        INC     SSBWDSECTOR            Do the next sector also
        LDA     FDWRITECMD,X           "write sector" command + side select
        BSR     FDINTISSUECOMMAND      go tell the disk what to do
        BNE     FDINTWRITE2            B/ ERROR
        DEC     SSBWDSECTOR            BACKUP THE SECTOR NUMBER FOR THE VERIFY
        BRA     FDINTVERIFY
FDINTWRITE2
        STA     DSKINFO:WRITEERRSTS+1,X Save write error status
        INC     DSKINFO:WRITEERRCNT+1,X Count # write errors
        BNE     FDINTWRITE3
        INC     DSKINFO:WRITEERRCNT,X
FDINTWRITE3
        CLR     DSKINFO:ERRLSN,X       Record the error LSN
        LDU     RDSI:LSN+1,Y
        STU     DSKINFO:ERRLSN+1,X
        DEC     FDRETRY,X
        BEQ     WRITEERROR
        LDB     FDRETRY,X              Multiple of 4 tries left?
        BITB    #%11                   ...?
        BNE     FDSEEK                 B/ no, go try it again
        BRA     FDSEEKHOME             Go see if re-seek helps
        PAGE
*
*       Sector number register is set, verify a sector
*
FDINTVERIFY
        LDA     FDPORTB,X              FLIP THE DATA DIRECTION
        ORA     #%00100000             SET READ DIRECTION
        STA     SSBPIAORB
        LDD     #\$0000                RESET DCB-4 BUFFER ADDRESS COUNTER
        STD     SSBIOBUFADRHI
        LDA     FDREADCMD,X            "read sector" command + side select
        BSR     FDINTISSUECOMMAND      go tell the disk what to do
        BNE     FDINTWRITE2            B/ error status, try to recover
        LDA     FDDOUBLESECTOR,X       Is this a double sector?
        BEQ     FDINTVERIFY1           B/ no, go read the bytes
*
*       This is a double sector operation.
*       We are on the first track of a SD/DD disk.
*       One logical sector (256 bytes) = 2 physical sectors,
*       so we must do it one more time
*
        INC     SSBWDSECTOR            Do the next sector also
        LDA     FDREADCMD,X            "read sector" command + side select
        BSR     FDINTISSUECOMMAND      go tell the disk what to do
        BNE     FDINTWRITE2            B/ error status, try to recover
*
*       We read the sector, now check it byte for byte
*
FDINTVERIFY1
        LDD     #\$0000                Init the floppy buffer pointer
        STD     SSBIOBUFADRHI
        LDY     RDSI:SECTORBASE,Y      Point to write buffer
        LEAY    16,Y
        LDA     FDNBPSDIV32,X          Get number of loop iterations
*
*       Verify the on-board I/O buffer
*       250 CYCLES/32 BYTES
*       =125us/32 BYTES
*       =3.9us/BYTE
*       =500us/128 BYTE SECTOR
*
FDINTVERIFY2
        LDX     -16,Y                  Get two bytes
        CMPX    SSBIOBUFFER            Check the two bytes
        BNE     FDINTVERIFY3           B/ no match
        LDX     -14,Y
        CMPX    SSBIOBUFFER
        BNE     FDINTVERIFY3
        LDX     -12,Y
        CMPX    SSBIOBUFFER
        BNE     FDINTVERIFY3
        LDX     -10,Y
        CMPX    SSBIOBUFFER
        BNE     FDINTVERIFY3
        LDX     -8,Y
        CMPX    SSBIOBUFFER
        BNE     FDINTVERIFY3
        LDX     -6,Y
        CMPX    SSBIOBUFFER
        BNE     FDINTVERIFY3
        LDX     -4,Y
        CMPX    SSBIOBUFFER
        BNE     FDINTVERIFY3
        LDX     -2,Y
        CMPX    SSBIOBUFFER
        BNE     FDINTVERIFY3
        LDX     0,Y
        CMPX    SSBIOBUFFER
        BNE     FDINTVERIFY3
        LDX     2,Y
        CMPX    SSBIOBUFFER
        BNE     FDINTVERIFY3
        LDX     4,Y
        CMPX    SSBIOBUFFER
        BNE     FDINTVERIFY3
        LDX     6,Y
        CMPX    SSBIOBUFFER
        BNE     FDINTVERIFY3
        LDX     8,Y
        CMPX    SSBIOBUFFER
        BNE     FDINTVERIFY3
        LDX     10,Y
        CMPX    SSBIOBUFFER
        BNE     FDINTVERIFY3
        LDX     12,Y
        CMPX    SSBIOBUFFER
        BNE     FDINTVERIFY3
        LDX     14,Y
        CMPX    SSBIOBUFFER
        BNE     FDINTVERIFY3
        LEAY    32,Y
        DECA                           Done?
        BNE     FDINTVERIFY2           B/ no
*
        LDX     FDINTDCB
        LDY     DSKINFO:SECTORDB,X     Get back pointer to RDSI
        BRA     FDDONE                 Successful verify
FDINTVERIFY3
        LDX     FDINTDCB
        LDY     DSKINFO:SECTORDB,X     Get back pointer to RDSI
        LDA     #%00101001             Set verify status= "crc+busy"
        BRA     FDINTWRITE2
        PAGE
*
*       Sector number register is set, read a sector
*
FDINTREAD
        LDA     FDREADCMD,X            "read sector" command + side select
        BSR     FDINTISSUECOMMAND      go tell the disk what to do
        BITA    #%11011111             Ignore delete data mark status
        BNE     FDINTREAD3             B/ error status, try to recover
        LDA     FDDOUBLESECTOR,X       Is this a double sector?
        BEQ     FDINTREAD1             B/ no, go read the bytes
*
*       This is a double sector operation.
*       We are on the first track of a SD/DD disk.
*       One logical sector (256 bytes) = 2 physical sectors,
*       so we must do it one more time
*
        INC     SSBWDSECTOR            Do the next sector also
        LDA     FDREADCMD,X            "read sector" command + side select
        BSR     FDINTISSUECOMMAND      go tell the disk what to do
        BITA    #%11011111             Ignore delete data mark status
        BNE     FDINTREAD3             B/ error status, try to recover
*
*       We read the sector, now move it
*
FDINTREAD1
        LDD     #\$0000                Init the floppy buffer pointer
        STD     SSBIOBUFADRHI
        LDY     RDSI:SECTORBASE,Y      Point to write buffer
        LEAY    16,Y
        LDA     FDNBPSDIV32,X          Get number of loop iterations
*
*       Read the on-board I/O buffer
*       186 CYCLES/32 BYTES
*       =93us/32 BYTES
*       =2.9us/BYTE
*       =372us/128 BYTE SECTOR
*
FDINTREAD2
        LDU     SSBIOBUFFER            Get two bytes
        STU     -16,Y                  Store the two bytes
        LDU     SSBIOBUFFER
        STU     -14,Y
        LDU     SSBIOBUFFER
        STU     -12,Y
        LDU     SSBIOBUFFER
        STU     -10,Y
        LDU     SSBIOBUFFER
        STU     -8,Y
        LDU     SSBIOBUFFER
        STU     -6,Y
        LDU     SSBIOBUFFER
        STU     -4,Y
        LDU     SSBIOBUFFER
        STU     -2,Y
        LDU     SSBIOBUFFER
        STU     0,Y
        LDU     SSBIOBUFFER
        STU     2,Y
        LDU     SSBIOBUFFER
        STU     4,Y
        LDU     SSBIOBUFFER
        STU     6,Y
        LDU     SSBIOBUFFER
        STU     8,Y
        LDU     SSBIOBUFFER
        STU     10,Y
        LDU     SSBIOBUFFER
        STU     12,Y
        LDU     SSBIOBUFFER
        STU     14,Y
        LEAY    32,Y
        DECA                           Done?
        BNE     FDINTREAD2             B/ no
*
        LDY     DSKINFO:SECTORDB,X     Get back pointer to RDSI
        BRA     FDDONE
FDINTREAD3
        STA     DSKINFO:READERRSTS+1,X Save read error status
        INC     DSKINFO:READERRCNT+1,X Count # write errors
        BNE     FDINTREAD4
        INC     DSKINFO:READERRCNT,X
FDINTREAD4
        CLR     DSKINFO:ERRLSN,X       Record the error LSN
        LDU     RDSI:LSN+1,Y
        STU     DSKINFO:ERRLSN+1,X
        DEC     FDRETRY,X
        BEQ     READERROR
        LDB     FDRETRY,X              Multiple of 4 tries left?
        BITB    #%11                   ...?
        BNE     FDSEEK                 B/ no, go try it again
        BRA     FDSEEKHOME             Go see if re-seek helps
        SETDPR  $0000
        FIN     IODRIVERBODY
        END
