        IF      IODRIVERINIT
        PAGE    SSB SASI DRIVER INIT CODE
        SETDPR  $F600
WDRESTORE
        PSHS    DP
        LDA     #$F6
        TFR     A,DP
        LDB     S.STAT                 DISABLE SASI INTERRUPT
        ANDB    #\S.INTE
        STB     S.STAT
*
        LDA     #DC.TOFOR              ENABLE ALL 4 CHANELS, DISABLE DATA
        STA     D.DATCN
        LDD     D.PRTCT                GET DMA REGISTERS
        ANDA    #\PC.ENCH2             DISABLE DMA CHANNEL 2
        ANDB    #\IC.IRQ2              DISABLE DMA CHANNEL 2 IRQ
        STD     D.PRTCT
*
*       COMPUTE PARKING LSN
*
        CLR     WD0.DCB+WDSEEK+1
        LDD     WD0.DCB+DSKINFO:NCYL
        STD     WD0.DCB+WDSEEK+2
*
        LDA     WD0.DCB+DSKINFO:NTPC+1 MUL BY NTPC
        BSR     WDRESTORE3
        LDA     WD0.DCB+DSKINFO:NSPT+1 MUL BY NSPT
        BSR     WDRESTORE3
*
        LDA     WD0.DCB+WDREADWRITE+1  GET A LUN
        ANDA    #%01100000             EXTRACT LUN BITS
        ORA     WD0.DCB+WDSEEK+1
        STA     WD0.DCB+WDSEEK+1
*
*       ASSIGN DISK PARAMETERS
*
        LDU     #WDADP+WD0.DCB         SELECT THE TARGET AND
        JSR     WDSELECT               SEND THE ASSIGN DISK PARAMS COMMAND
*       LDU     #WDDISKPARAMS+WD0.DCB  SEND THE DISK PARAMETERS
        LDA     WD0.DCB+DSKINFO:NTPC+1 SET THE NUMBER OF HEADS FIRST
        DECA
        STA     3,U
        LDB     #10                    10 BYTES TO SEND
        JSR     WDSENDDATA
        JSR     WDCOMPLETE
        BEQ     WDRESTORE1             B/ NO TROUBLE
        JSR     WDGETERROR
        LDX     #WDRSERROR+WD0.DCB     ADDRESS OF ERROR BLOCK
        SWI                            TROUBLE, A HAS ERROR CODE
*
WDRESTORE1
        LDA     #14                    SETUP THE MAP FOR DMA INTO SYSTEM SPACE
        LDX     #MAPRAM
WDRESTORE2
        STA     ,X+
        DECA
        BPL     WDRESTORE2
        STA     ,X
*
        CLRA                           CLEAR THE CARRY
        PULS    DP,PC                  DONE
*
WDRESTORE3
        PSHS    A
        LDU     #WD0.DCB+WDSEEK+4
        CLRA
WDRESTORE4
        LDB     ,-U
        STA     ,U
        LDA     ,S
        MUL
        ADDB    ,U
        ADCA    #0
        STB     ,U
        CMPU    #WD0.DCB+WDSEEK+1
        BNE     WDRESTORE4
        PULS    A,PC
        SETDPR  0
        FIN     IODRIVERINIT
        IF      IODRIVERRAM
        PAGE    SSB SASI DRIVER RAM
WDINTFREE       FCB     1              "Floppy Disk is NOT busy" flag
WDINTDCB        RMB     2              Address of current DCB
WDSTACK         RMB     2              USED TO FIX STACK ON ERROR
*
*       Disk interrupt time out block
*
WDINTTIMEOUTBLOCK
                FDB     NEXTTIMEOUT    timeout block for floppies
                FDB     0              fuse length
                FDB     WDTIMEOUT
NTIMEOUTS       SET     NTIMEOUTS+1
NEXTTIMEOUT     SET     WDINTTIMEOUTBLOCK
        PAGE
WD0.DCB FCB     1                      DCB:DONEFLAG
        FDB     0                      DCB:LASTERROR
        FDB     WD0.DCB+WDSIZE         DCB:NAME
        FDB     NEXTDISKDCB            DCB:NEXTDCB
        FDB     WDDRIVER               DCB:DRIVER
*
        FDB     256                    DSKINFO:NBPS
        FDB     32                     DSKINFO:NSPT
        FDB     WDNUMBEROFHEADS        DSKINFO:NTPC
        FDB     306+12                 DSKINFO:NCYL (I USE THE SPARES)
*
        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      (*-WD0.DCB)#DSKINFO:SIZE
        ?       INCORRECT DCB OFFSETS
        FIN
*
        FCB     $7E                    WDSTATEJ
        FDB     WDUNEXPECTEDINT        WDSTATE
        FDB     0                      WDMAPALGORITHM
        RMB     1                      WDK1MODNSPT
        RMB     1                      WDK2MODNSPT
        RMB     1                      WDK4MODNSPT
        RMB     1                      WDK8MODNSPT
        RMB     1                      WDK16MODNSPT
        RMB     32                     WDMAP
        RMB     1                      WDLOOPCOUNT
*                                      READ/WRITE COMMAND
        RMB     1                      WDREADWRITE
        FCB     0                      LUN, SECTOR ADDR 2
        FCB     0                      SECTOR ADDR 1
        FCB     0                      SECTOR ADDR 0
        FCB     1                      SECTOR TRANSFER COUNT
        FCB     0                      CONTROL FIELD
*                                      REQUEST SENSE COMMAND
        FCB     3                      C.RSEN
        FCB     0                      LUN
        FCB     0                      NOT USED
        FCB     0                      NOT USED
        FCB     0                      NOT USED
        FCB     0                      CONTROL FIELD
*                                      
        RMB     4                      REQUEST SENSE ERROR INFO
*                                      ASSIGN DISK PARAMETERS COMMAND
        FCB     $C2                    C.ADP (6 BYTE COMMAND)
        FCB     %00000000              LUN
        FCB     0                      NOT USED
        FCB     0                      NOT USED
        FCB     0                      NOT USED
        FCB     0                      NOT USED
*                                      WDDISKPARAMS
        FCB     3                      STEP PULSE WIDTH
        FCB     0                      STEP PERIOD
        FCB     0                      STEP MODE
        RMB     1                      NUMBER OF HEADS-1
        FDB     306+13-1               CYL ADDR (+12 SPARES + 1 FOR PARKING) -1
        FCB     128                    REDUCED WRITE CURRENT CYLINDER
        FCB     0                      FIXED DISK DRIVE
        FCB     32-1                   SECTORS PER TRACK-1
        FCB     0                      RESERVED
*
        FCB     $0B                    SEEK
        RMB     4                      LUN
        FCB     0                      CONTROL FIELD
*
        FCB     $E6                    REQUEST LOGOUT
        FCB     0,0,0,0,0
*
        RMB     4                      REQUEST LOGOUT STATS
*
        FCC     /WD0:/                 DEVICE NAME
        FCB     0                      END OF DEVICE NAME
NEXTDISKDCB     SET     WD0.DCB
NDISKDCBS       SET     NDISKDCBS+1
        FIN     IODRIVERRAM
        IF      IODRIVERPOLL
        PAGE    SSB WINCHESTER DRIVER INTERRUPT POLL CODE
*
*       Interrupt poll code for WINCHESTER DISK
*
        LDA     S.STAT                 GET INTERRUPT STATUS
        BITA    #S.INTR                DID INTERRUPT HAPPEN?
        BEQ     NOTWDINTERRUPT         B/ NO, NOT ME
        BITA    #S.INTE                WAS IT ENABLED?
        BNE     WDINTR1                B/ YES
        SWI                            NO, ???
        SETDPR  $F600
WDINTR1 LDB     #$F6                   Set page zero for disk int routines
        TFR     B,DP
        ANDA    #\S.INTE               DISABLE SASI INTERRUPT
        STA     S.STAT
        LDA     D.PRTCT
        ANDA    #\PC.ENCH2             DISABLE CHANNEL 2
        STA     D.PRTCT
        LDA     D.DMAEN                WAS DMA ENABLED?
        BMI     WDINTR2                B/ YES
        SWI                            NO, ???
WDINTR2 CLR     D.DMAEN                DISABLE DMA ENABLE LATCH
        LDD     D.CH2CN                DMA COUNT ZERO?
        BEQ     WDINTR3                B/ YES
        CMPD    #$100                  IF NOT ZERO, THEN=256?
        BEQ     WDINTR3                B/ YES, PROBABLY READ/WRITE ERROR
        SWI                            NO, ???
WDINTR3 LDX     WDINTDCB               It is our device for sure
        LDY     DSKINFO:SECTORDB,X     Get address of RDSI
        LDU     WDSTATEJ+1,X
        STS     WDSTACK                REMEMBER STACK FOR ERROR RECOVERY
        PSHS    U
        LDU     #WDUNEXPECTEDINT
        STU     WDSTATEJ+1,X
        JMP     WDCOMPLETE             GO GET STATUS, MSG AND RESUME EXECUTION
WDUNEXPECTEDINT
        SWI
NOTWDINTERRUPT
        SETDPR  0
        FIN     IODRIVERPOLL
        IF      IODRIVERBODY
        PAGE    SSB OMTI DRIVER
        IFUND   WINCHESTERSIZE
WINCHESTERSIZE  EQU     20
        FIN
        IF      WINCHESTERSIZE=5
WDNUMBEROFHEADS EQU     2
        ELSEIF  WINCHESTERSIZE=10
WDNUMBEROFHEADS EQU     4
        ELSEIF  WINCHESTERSIZE=20
WDNUMBEROFHEADS EQU     8
        ELSE
        ?       INVALID WINCHESTER SIZE
        FIN
*
*       DCB extension, tacks on to the end of the standard Disk Info Table
*
::      SET     *                      Remember where we are
        ORG     DSKINFO:SIZE
WDSTATEJ        RMB     1              JMP instruction
WDSTATE         RMB     2              Address for jmp instruction
WDMAPALGORITHM  RMB     2              Current map algorithm
WDK1MODNSPT     RMB     1              Spiraling constant mod NSPT
WDK2MODNSPT     RMB     1              2*sc mod NSPT
WDK4MODNSPT     RMB     1              4*sc mod NSPT
WDK8MODNSPT     RMB     1              8*sc mod NSPT
WDK16MODNSPT    RMB     1              16*sc mod NSPT
WDMAP           RMB     32             Map for sector mapping
WDLOOPCOUNT     RMB     1              TIMEOUT COUNTER FOR CONTROLLER LOOPS
WDREADWRITE     RMB     6              READ/WRITE COMMAND
WDRSEN          RMB     6              REQUEST SENSE COMMAND
WDRSERROR       RMB     4              REQUEST SENSE ERROR STATUS
WDADP           RMB     6              ASSIGN DISK PARAMETERS COMMAND
WDDISKPARAMS    RMB     10             DISK PARAMETERS DATA BLOCK
WDSEEK          RMB     6              DISK SEEK TO PARKING SPACE
WDLOGO          RMB     6              REQUEST LOGOUT (GET ERROR STATS)
WDLOGOSTATS     RMB     4              REQUEST LOGOUT STATS
WDSIZE          equ     *              Size of the disk dcb
        ORG     ::                     Restore the p-counter
        PAGE
WDDRIVER                               ; Driver entry points
        FDB     WDRESTORE
        FDB     WDREAD
        FDB     WDWRITE
        FDB     WDWAITDONE
        FDB     ILLDEVICEOP            WDSTATUS handled completely by SDOS1.1
        FDB     WDCONTROL
*
*       WDCONTROL -- CONTROL OPERATION ENTRY POINT FOR SECTOR I/O DRIVER
*
WDCONTROL
        CMPA    #CC:DISMOUNTDISK       SINCE SDOS PASSES THIS THRU
        BEQ     WDDISMOUNT
*       CMPA    #CC:GETERRORSTATS
*       BEQ     WDGETERRORSTATS
        JMP     ILLDEVICEOP            NOT A LEGAL CONTROL CALL
WDDISMOUNT
        BSR     WDPARK
        BCS     WDWRITE2               B/ ERROR
        BSR     WDWAITDONE
        BCS     WDWRITE2               B/ ERROR
WDGETERRORSTATS
        LDA     #C.LOGO                GET ERROR STATS
        BRA     WDWRITE1
WDPARK  LDA     #C.SEEK
        BRA     WDWRITE1
*
*       WDREAD/WRITE -- START SINGLE SECTOR TRANSFER
*
WDREAD  LDA     #C.READ
        BRA     WDWRITE1
WDWRITE LDA     #C.WRIT
WDWRITE1        ; Assert SDOS checks DCB:DONEFLAG before calling
        JSR     WDSETUPDRIVE           GO SET UP ALL THE PARAMETERS IN THE DCB
        BCS     WDWRITE2               B/ ERROR
        LDX     #WDINTFREE             See if controller is busy
        LDA     0,X
        BNE     WDSTARTIO              B/ not busy
        JSR     SDOS+SDOS:WAITEVENT    It's busy, wait for controller free
WDSTARTIO
        LDX     DCBPOINTER             NOW NOBODY'S USING DRIVE
        CLR     DCB:DONEFLAG,X         KICK INTERRUPT ROUTINE INTO MOTION
        TFR     X,D
        LDX     #WDINTSTART
        JSR     SDOS+SDOS:STARTIO
WDWAIT2 CLRB                           CLEAR THE CARRY
WDWRITE2
        RTS
*
WDWAITDONE      ; ASSERT X CONTAINS DCB POINTER ???
        LDX     DCBPOINTER             COULD COME HERE FROM DISMOUNT
        LDA     DCB:DONEFLAG,X         IS IT DONE?
        BNE     WDWAIT1                B/ YES
        JSR     SDOS+SDOS:WAITEVENT    X conveniently points to condition byte
        LDX     DCBPOINTER             DCB POINTER INVALID HERE ???
WDWAIT1 LDX     DCB:LASTERROR,X
        BEQ     WDWAIT2                B/ NO ERRORS
        JMP     ERRETX
        PAGE
*
*       Build the map
*       A,B contain the map algorithm
*       X contains the DCB pointer
*
WDBUILDMAP
        STD     WDMAPALGORITHM,X       Remember the new map
        NEGB
        LDA     #32                    Do it this many times
        STX     TEMPX                  Remember beginning (end) of list
WDBUILDMAP1
        LDX     DCBPOINTER
        ADDB    DSKINFO:MAPALGORITHM+1,X Compute next physical sector number
WDBUILDMAP2
        ANDB    #32-1                  Shave off the excess
        LDX     DCBPOINTER
        LEAX    -1,X
WDBUILDMAP3
        LEAX    1,X                    End of list?
        CPX     TEMPX
        BEQ     WDBUILDMAP4            B/ yes, use this hole
        CMPB    WDMAP,X                Is it in the list already?
        BNE     WDBUILDMAP3            B/ no, keep looking
        INCB                           Yes, duplicate; add one and try again
        BRA     WDBUILDMAP2
WDBUILDMAP4
        STB     WDMAP,X                Use this sector number
        LEAX    1,X
        STX     TEMPX
        DECA                           Done yet?
        BNE     WDBUILDMAP1            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=32, the modulo),
*       and the modulo of any number to the base NSPT yields a number less
*       than 32 (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    WDK1MODNSPT,X
        LDA     DSKINFO:MAPALGORITHM,X
        LDB     #5
WDBUILDMAP5
        ANDA    #32-1
        STA     ,U+
        ASLA
        DECB
        BNE     WDBUILDMAP5
        RTS                            Done
        PAGE
*
*       WDSETUPDRIVE -- SETS UP WDDRIVE TABLE FOR INTERRUPT DRIVEN TRANSFER
*
WDSETUPDRIVE 
        LDX     DCBPOINTER             GET DCB POINTER
        LDY     DSKINFO:SECTORDB,X     GET RDSI POINTER
        STA     WDREADWRITE,X
        CLR     DCB:LASTERROR,X        "NO ERRORS"
        CLR     DCB:LASTERROR+1,X
        CMPA    #C.LOGO
        BEQ     WDSETUPDRIVE1          B/ IT'S A GET ERROR STATS CALL
        CMPA    #C.SEEK                B/ IT'S A PARK CALL
        BNE     WDTEARLSN              B/ MUST BE A READ OR A WRITE
WDSETUPDRIVE1   ; THIS IS ALL WE HAVE TO DO FOR SEEK/GET STATS
        OKRTS                          
        PAGE
*
*       Take RDSI:LSN and split it up into RDSI:CYLINDER,
*       RDSI:TRACK, and RDSI:SECTOR, applying the map
*
WDTEARLSN
        LDX     DCBPOINTER
        LDY     DSKINFO:SECTORDB,X     GET ADDRESS OF RDSI
        CLR     RDSI:SECTOR,Y          CLEAR MSB SECTOR, TRACK, CYLINDER
*       CLR     RDSI:TRACK,Y
*       CLR     RDSI:CYLINDER,Y
        LDD     DSKINFO:MAPALGORITHM,X HAS THE MAP CHANGED?
        CMPD    WDMAPALGORITHM,X       ...?
        BEQ     WDTEARLSN1             B/ NO
        BSR     WDBUILDMAP             YES, RE-BUILD THE MAP
WDTEARLSN1
*
*       ASSERT RDSI:LSN ALREADY CHECKED FOR LEGAL BY SDOS
*       COMPUTE CYLINDER
*
        LDA     RDSI:LSN,Y
        STA     RDSI:CYLINDER,Y
        LDA     DSKINFO:NTPC+1,X       USE NTPC TO FIGURE OUT # BITS TO SHIFT
        ASLA
        ASLA
        ASLA
        ASLA
        STA     RDSI:TRACK,Y           USING TRACK AS TEMPORARY
        STA     RDSI:TRACK+1,Y
        LDD     RDSI:LSN+1,Y
        ASL     RDSI:TRACK,Y
        BEQ     WDTEARLSN2             B/ 8 HEADS
        ASLB
        ROLA
        ROL     RDSI:CYLINDER,Y
        ASL     RDSI:TRACK,Y
        BEQ     WDTEARLSN2             B/ 4 HEADS
        ASLB
        ROLA
        ROL     RDSI:CYLINDER,Y
        ASL     RDSI:TRACK,Y
*       BEQ     WDTEARLSN2             B/ 2 HEADS
WDTEARLSN2      ; ASSERT RDSI:TRACK,Y IS ZERO
        STA     RDSI:CYLINDER+1,Y
*
*       COMPUTE THE TRACK NUMBER
*
        LDA     RDSI:TRACK+1,Y         THIS HAS NTPC*16 FROM ABOVE
        ASLA                           FORM A BIT MASK TO EXTRACT TRACK NUMBER
        SUBA    #$20
        ANDA    RDSI:LSN+2,Y
        LSRA                           THROW AWAY THE SECTOR NUMBER
        LSRA
        LSRA
        LSRA
        LSRA
        STA     RDSI:TRACK+1,Y         TRACK # IN (A) IS USED BELOW @ THE MUL
*
*       MAP THE SECTOR AND ADD IN TRACK LATENCY
*
        LDB     RDSI:LSN+2,Y
        ANDB    #32-1
        LEAU    WDMAP,X
        LEAU    B,U
*
*       If we are switching heads, add in another round of latency tuning
*
        LDB     WDMAPALGORITHM+1,X
        MUL                            TRACK * MAP
        ADDB    ,U
*
*       Start with the cylinder number mod NSPT
*       and add in the spiraling
*
WD.MAP  LDA     RDSI:CYLINDER+1,Y
        ASRA
        BCC     WD.MAP1
        ADDB    WDK1MODNSPT,X
WD.MAP1 ASRA
        BCC     WD.MAP2
        ADDB    WDK2MODNSPT,X
WD.MAP2 ASRA
        BCC     WD.MAP3
        ADDB    WDK4MODNSPT,X
WD.MAP3 ASRA
        BCC     WD.MAP4
        ADDB    WDK8MODNSPT,X
WD.MAP4 ASRA
        BCC     WD.MAP5
        ADDB    WDK16MODNSPT,X
WD.MAP5 ANDB    #32-1
        STB     RDSI:SECTOR+1,Y
        OKRTS
        PAGE    SSB Chieftain DCB-4 1791 Disk interrupt routines
        SETDPR  $F600                  Valid for all int level code
        PAGE
D.CH0AD         EQU     $F6C0          CHANNEL 0 ADDRESS REGISTER
D.CH1AD         EQU     $F6C4          CHANNEL 1 ADDRESS REGISTER
D.CH2AD         EQU     $F6C8          CHANNEL 2 ADDRESS REGISTER
D.CH3AD         EQU     $F6CC          CHANNEL 3 ADDRESS REGISTER
*
D.CH0CN         EQU     $F6C2          CHANNEL 0 BYTE COUNT REGISTER
D.CH1CN         EQU     $F6C6          CHANNEL 1 BYTE COUNT REGISTER
D.CH2CN         EQU     $F6CA          CHANNEL 2 BYTE COUNT REGISTER
D.CH3CN         EQU     $F6CE          CHANNEL 3 BYTE COUNT REGISTER
*
D.CH0CC         EQU     $F6D0          CHANNEL 0 CHANNEL CONTROL
D.CH1CC         EQU     $F6D1          CHANNEL 1 CHANNEL CONTROL
D.CH2CC         EQU     $F6D2          CHANNEL 2 CHANNEL CONTROL
D.CH3CC         EQU     $F6D3          CHANNEL 3 CHANNEL CONTROL
*
CC.DMEND        EQU     %10000000      DMA END BIT
CC.DBUSY        EQU     %01000000      DMA BUSY BIT
CC.UPDWN        EQU     %00001000      ADDRESS U/DOWN BIT
CC.MODE1        EQU     %00000100      MODE 1 (TSC STEAL)
CC.MODE2        EQU     %00000000      MODE 2 (CYCLE STEAL)
CC.MODE3        EQU     %00000010      MODE 3 (HALT BURST)
CC.RDWRT        EQU     %00000001      READ/WRITE BIT
*
D.PRTCT         EQU     $F6D4          PRIORITY CONTROL
*
PC.RDTAT        EQU     %10000000      ROTATING PRIORITY BIT
PC.ENCH3        EQU     %00001000      REQUEST ENABLE CHANNEL 3 BIT
PC.ENCH2        EQU     %00000100      REQUEST ENABLE CHANNEL 2 BIT
PC.ENCH1        EQU     %00000010      REQUEST ENABLE CHANNEL 1 BIT
PC.ENCH0        EQU     %00000001      REQUEST ENABLE CHANNEL 0 BIT
*
D.INTC          EQU     $F6D5          INTERRUPT CONTROL
*
IC.IRQ          EQU     %10000000      DMA IRQ FLAG
IC.IRQ3         EQU     %00001000      IRQ ENABLE CHANNEL 3 BIT
IC.IRQ2         EQU     %00000100      IRQ ENABLE CHANNEL 2 BIT
IC.IRQ1         EQU     %00000010      IRQ ENABLE CHANNEL 1 BIT
IC.IRQ0         EQU     %00000001      IRQ ENABLE CHANNEL 0 BIT
*
D.DATCN         EQU     $F6D6          DATA CHAIN REGISTER
*
DC.TOFOR        EQU     %00001000      TWO/FOUR CHANNEL SELECT BIT
DC.DCCH0        EQU     %00000000      DATA CHAIN TO CHANNEL 0
DC.DCCH1        EQU     %00000010      DATA CHAIN TO CHANNEL 1
DC.DCCH2        EQU     %00000100      DATA CHAIN TO CHANNEL 2
DC.DCENB        EQU     %00000001      DATA CHAIN ENABLE BIT
*
D.SENSE         EQU     $F6E6          SENSE SWITCHES (READ ONLY)
*
S.HDAPFP        EQU     %00000001      PRIAM FORMAT PROTECT
S.HDASFP        EQU     %00000010      SASI FORMAT PROTECT
S.HDASWP        EQU     %00000100      SASI WRITE PROTECT
*
D.DMAEN         EQU     $F6E7          DMA ENABLE LATCH
MAPRAM          EQU     $F6F0          DYNAMIC ADDRESS TRANSLATOR RAM
*
*       HDA-S EQUATES
*
S.DATA          EQU     $F6E4          DATA I/O BUS
S.STAT          EQU     $F6E5          STATUS REGISTER
*
*       HDA-S STATUS REGISTER BIT DEFINITIONS
*
S.BUSY          EQU     %00000001      I/O BUS BUSY
S.RDWR          EQU     %00000010      READ/WRITE
S.CMDA          EQU     %00000100      COMMAND/DATA
S.MSG           EQU     %00001000      MESSAGE
S.INTE          EQU     %00010000      INTERRUPT ENABLE (LATCH)
S.SEL           EQU     %00100000      SELECT (LATCH)
S.INTR          EQU     %01000000      INTERRUPT REQUEST
S.REQ           EQU     %10000000      REQUEST
*
*       HDA-S STATE DEFINITIONS
*
S.SEND          EQU     S.MSG+S.RDWR   SEND COMMAND BYTES
S.WDATA         EQU     S.MSG+S.CMDA+S.RDWR WRITE DATA
S.RDATA         EQU     S.MSG+S.CMDA   READ DATA
S.RSTA          EQU     S.MSG          READ STATUS BYTES
S.DONE          EQU     0              COMMAND SEQUENCE DONE
*
*       OMT 20C CONTROLLER COMMAND LIST
*
C.SENS          EQU     %00000000      SENSE STATUS
C.RZER          EQU     %00000001      RECALIBRATE (RESTORE, WHERE I COME FROM)
C.RSEN          EQU     %00000011      REQUEST SENSE
C.FMTD          EQU     %00000100      FORMAT DRIVE
C.CHKT          EQU     %00000101      CHECK TRACK FORMAT
C.FMTT          EQU     %00000110      FORMAT TRACK
C.FMTB          EQU     %00000111      FORMAT BAD TRACK
C.READ          EQU     %00001000      READ SECTOR(S)
C.INIT          EQU     %00001001      INITIALIZE (RESET)
C.WRIT          EQU     %00001010      WRITE SECTOR(S)
C.SEEK          EQU     %00001011      SEEK
C.AATK          EQU     %00001110      ASSIGN ALTERNATE TRACK
C.COPY          EQU     %00100000      COPY
C.SCEQ          EQU     %01000000      SCAN EQUAL
C.SHEQ          EQU     %01000001      SCAN HIGH OR EQUAL
C.SLEQ          EQU     %01000010      SCAN LOW OR EQUAL
C.DEFN          EQU     %11000000      DEFINE DRIVE LIMITS
C.ADKP          EQU     %11000010      ASSIGN DISK PARAMETERS
C.LOGO          EQU     %11100110      REQUEST LOGOUT
*
*       OMT 20C ERROR TYPES
*
E.TYPE0         EQU     %00000000      TYPE 0 ERROR (DRIVE ERRORS)
E.TYPE1         EQU     %00010000      TYPE 1 ERROR (CONTROLLER ERROR)
E.TYPE2         EQU     %00100000      TYPE 2 ERROR (COMMAND ERRORS)
E.TYPE3         EQU     %00110000      TYPE 3 ERROR (MISC ERRORS)
        PAGE
*
*       SELECT THE TARGET
*
WDSELECT
        LDA     S.STAT                 CHECK TO SEE IF CONTROLLER IS AVAILABLE
        BITA    #S.BUSY
        BNE     WDSELECT1              B/ IT IS
        SWI                            IT ISN'T !???
WDSELECT1
        LDA     #1                     ASSERT THE CONTROLLER ID
        STA     S.DATA
        LDA     S.STAT                 ASSERT SEL
        ORA     #S.SEL
        STA     S.STAT
        LDB     S.STAT                 CHECK TO SEE THAT CONTROLLER ASSERTS BSY
        BITB    #S.BUSY                ...?
        BEQ     WDSELECT2              B/ IT DID
        SWI                            CONTROLLER DID NOT ASSERT BUSY
WDSELECT2
        EORA    #S.SEL                 DEASSERT SEL
        STA     S.STAT
        LDB     #6
*
*       SEND THE STANDARD 6-BYTE COMMAND (AFTER TARGET SELECTION)
*       THESE SIGNALS ARE CONSTANT THROUGHOUT THIS SEQUENCE:
*       SEL HIGH, BSY LOW, C/D LOW, I/O HIGH, MSG HIGH
*       FOR EACH BYTE TRANSFER, TARGET ASSERTS (MAKES LOW) REQ,
*       HOST OUTPUTS DATA BYTES, HOST ASSERTS ACK, TARGET RECEIVES
*       DATA BYTES, TARGET DEASSERTS REQ.
*
WDSENDCMD
        BSR     WDLOOPINIT
WDSENDCMD1
        LDA     S.STAT
        DEC     WDLOOPCOUNT,X
        BEQ     WDLOOPTIMEDOUT         B/ TIMED OUT
        ANDA    #S.REQ+S.MSG+S.CMDA+S.RDWR+S.BUSY
        CMPA    #S.SEND
        BNE     WDSENDCMD1             B/ REQ NOT ASSERTED
        LDA     ,U+                    OUTPUT A COMMAND BYTE
        STA     S.DATA
        DECB                           DONE YET?
        BNE     WDSENDCMD1             B/ NO
        RTS                            YES, DONE
*
*       SEND DATA BYTES TO THE TARGET. ASSERT TARGET PREVIOUSLY
*       SELECTED, AND THE 6 BYTE COMMAND SEQUENCE HAS BEEN SENT TO TARGET.
*       THESE SIGNALS ARE CONSTANT THROUGHOUT THIS SEQUENCE:
*       SEL HIGH, BSY LOW, C/D HIGH, I/O HIGH, MSG HIGH
*       FOR EACH BYTE TRANSFER, TARGET ASSERTS (MAKES LOW) REQ,
*       HOST OUTPUTS DATA BYTES, HOST ASSERTS ACK, TARGET RECEIVES
*       DATA BYTES, TARGET DEASSERTS REQ.
*
WDSENDDATA
        BSR     WDLOOPINIT
WDSENDDATA1
        LDA     S.STAT
        DEC     WDLOOPCOUNT,X
        BEQ     WDLOOPTIMEDOUT         B/ TIMED OUT
        ANDA    #S.REQ+S.MSG+S.CMDA+S.RDWR+S.BUSY
        CMPA    #S.WDATA
        BNE     WDSENDDATA1            B/ REQ NOT ASSERTED
        LDA     ,U+                    OUTPUT A COMMAND BYTE
        STA     S.DATA
        DECB                           DONE YET?
        BNE     WDSENDDATA1            B/ NO
        RTS                            YES, DONE
*
WDLOOPINIT
        LDA     #60
        STA     WDLOOPCOUNT,X
        RTS
WDLOOPTIMEDOUT
        STA     DSKINFO:READERRSTS,X
        CLRA
        LDB     WDREADWRITE,X          WAS IT A READ OR WRITE OP?
        CMPB    #C.READ
        LBEQ    WDINTREADERROR         B/ IT WAS A READ THAT TIMED OUT
        JMP     WDINTWRITEERROR        B/ IT WAS A WRITE THAT TIMED OUT
        PAGE
WDISSUECMD
        STA     D.CH2CC                SET DMA MODE AND DIRECTION
        LDD     RDSI:SECTORBASE,Y      GET ADDRESS OF SECTOR BUFFER
        STD     D.CH2AD                SET CHANNEL ADDRESS REGISTER
        LDD     DSKINFO:NBPS,X         SET DMA TRANSFER COUNT
        STD     D.CH2CN
        LDA     D.PRTCT                ENABLE DMA CHANNEL 2
        ORA     #PC.ENCH2
        STA     D.PRTCT
        LDA     #%10000000             ENABLE DMA
        STA     D.DMAEN
        BSR     WDSELECT               SELECT THE TARGET AND SEND THE COMMAND
        LDA     S.STAT                 ENABLE SASI INTERRUPT
        ORA     #S.INTE
        STA     S.STAT
        LDD     #20*TICKSPERSECOND+NTIMEOUTBLOCKS Set up time-out on operation
        STD     WDINTTIMEOUTBLOCK+TIMEOUT:FUSE
        PULS    D                      Re-aim state vector to return addr
        STD     WDSTATEJ+1,X
        INC     DSKINFO:OPSCOUNT+2,X   COUNT # OPERATIONS ISSUED TO WINCHESTER
        BNE     WDCOUNTCOMMAND1
        INC     DSKINFO:OPSCOUNT+1,X
        BNE     WDCOUNTCOMMAND1
        INC     DSKINFO:OPSCOUNT,X
WDCOUNTCOMMAND1
        JMP     SDOS+SDOS:RTI
*
WDSETERRLSN                            ; SET ERRORLSN
        LDD     RDSI:LSN,Y
        STD     DSKINFO:ERRLSN,X
        LDA     RDSI:LSN+2,Y
        STA     DSKINFO:ERRLSN+2,X
        RTS
*
*       Start I/O for SSB winchester disk controller
*       ASSERT: INTERRUPTS ARE DISABLED HERE!
*
WDINTSTART
        STS     WDSTACK                USED FOR ERROR RECOVERY
        STD     WDINTDCB               SAVE DCB POINTER
        LDA     #$F6                   SET THE DP
        TFR     A,DP
        DEC     WDINTFREE              Mark controller busy
        BEQ     WDINTSTART1            B/ CONTROLLER NOT PREVIOUSLY BUSY
        SWI                            ENTERED TWICE!!
WDINTSTART1
        LDX     WDINTDCB
        LDY     DSKINFO:SECTORDB,X     Get pointer to RDSI
        LDD     RDSI:LSN+1,Y           SETUP LSN
        ANDB    #-32                   MASK OFF LOWER BITS
        ADDB    RDSI:SECTOR+1,Y        REPLACE THEM WITH MAPPED SECTOR
        LEAU    WDREADWRITE,X          POINTER TO COMMAND
        STD     2,U                    SAVE THE 2 LS BYTES OF LSN
        LDA     0,U                    IS THIS A PARK OR GET ERROR STATS?
        CMPA    #C.SEEK
        BEQ     WDINTSEEK              B/ IT'S A PARK
        CMPA    #C.LOGO
        BEQ     WDINTLOGO              B/ IT'S A GET ERROR STATS
        LDA     #%11100000             CHECK LEGAL LSN (SHEER PARANOIA)
        BITA    RDSI:LSN,Y
        BEQ     WDINTSTART2            B/ IT'S OK
        SWI                            SDOS IS NOT SUPPOSED TO LET THIS HAPPEN
WDINTSTART2
        ANDA    1,U                    GET THE OMTI UNIT NUMBER
        ORA     RDSI:LSN,Y             COMBINE WITH MS BYTE OF LSN
        STA     1,U                    SAVE IT
        LDA     0,U                    ARE WE READING OR WRITING?
        CMPA    #C.READ
        BEQ     WDINTREAD              B/ READING
        CMPA    #C.WRIT
        BEQ     WDINTWRITE             B/ IT'S A WRITE COMMAND
        SWI
        PAGE
*
*       REQUEST LOGOUT
*
WDINTLOGO
        LEAU    WDLOGO,X
        BSR     WDSELECT
*       LEAU    WDLOGOSTATS,X
        LDB     #4                     GO GET 4 BYTES OF SENSE INFORMATION
        JSR     WDREADDATA
        JSR     WDCOMPLETE             AND FINISH UP
        BEQ     WDINTLOGO1             B/ NO ERROR
        SWI                            NOT SUPPOSED TO HAPPEN
WDINTLOGO1
        LEAU    WDLOGOSTATS,X
        LDD     ,U++
        ASLD
        CMPD    #0
        BEQ     WDINTLOGO2
        INCB
        STD     DSKINFO:READERRSTS,X
WDINTLOGO2
        LDD     ,U
        ASLD
        CMPD    #0
        BEQ     WDDONE
        INCB
        STD     DSKINFO:WRITEERRSTS,X
        BRA     WDDONE
        PAGE
*
*       SEEK (PARK THE HEADS)
*
WDINTSEEK
        LEAU    WDSEEK,X               POINTER TO COMMAND
*       BRA     WDINTREAD
WDINTREAD
        LDA     #CC.MODE1              SET DMA MODE 1 + READ
        BSR     WDISSUECMD
        BEQ     WDDONE                 B/ NO ERRORS
        BSR     WDGETERROR             GET THE ERROR
        CLR     DSKINFO:READERRSTS,X
WDINTREADERROR
*
*       A WORD HERE ABOUT ERROR STATS INTERPRETATION
*       IF THE UPPER BYTE IS ZERO AND THE LOWER BYTE IS EVEN
*       THEN STATUS/2 IS ERROR CODE FROM REQUEST SENSE COMMAND
*       IF THE UPPER BYTE IS NON-ZERO AND THE LOWER BYTE IS EVEN
*       THEN
*           THE DISK TIMED OUT DURING A COMMAND OR DATA TRANSFER
*           AND THE THE UPPER BYTE CONTAINS S.STAT
*       FI
*       IF THE LOWER BYTE OF READ STATUS IS ODD
*       THEN STATUS/2 IS RETRY COUNT FROM REQUEST LOGOUT COMMAND
*       IF THE LOWER BYTE OF WRITE STATUS IS ODD
*       THEN STATUS/2 IS THE PERMENENT ERROR COUNT FROM REQ LOGO COMMAND
*
        ASLA
        STA     DSKINFO:READERRSTS+1,X SAVE READ ERROR STATUS
        INC     DSKINFO:READERRCNT+1,X COUNT # READ ERRORS
        BNE     WDINTREAD1
        INC     DSKINFO:READERRCNT,X
WDINTREAD1
        BSR     WDSETERRLSN
        BRA     WDREADERROR
        PAGE
WDINTWRITE
        LDA     D.SENSE                IS IT WRITE PROTECTED?
        BITA    #S.HDASWP
        BNE     WDWRITEPROTECTERR      B/ YES, YOU'RE DEAD
        LDA     #CC.MODE1+CC.RDWRT     SET DMA MODE 1 + WRITE
        BSR     WDISSUECMD             GO DO THE WRITE
        BEQ     WDDONE                 B/ NO ERROR
        BSR     WDGETERROR             GET THE ERROR
        CLR     DSKINFO:WRITEERRSTS,X
WDINTWRITEERROR
        ASLA
        STA     DSKINFO:WRITEERRSTS+1,X SAVE WRITE ERROR STATUS
        INC     DSKINFO:WRITEERRCNT+1,X COUNT # WRITE ERRORS
        BNE     WDINTWRITE1
        INC     DSKINFO:WRITEERRCNT,X
WDINTWRITE1
        BSR     WDSETERRLSN
        BRA     WDWRITEERROR
        PAGE
*
*       WE GOT SOME ERROR, TIME TO FIND OUT WHAT IT IS
*
WDGETERROR
        LEAU    WDRSEN,X               GET ADDRESS OF REQUEST SENSE COMMAND
        BSR     WDSELECT               SELECT THE TARGET AND REQUEST SENSE
*       LEAU    WDRSERROR,X
        LDB     #4                     GO GET 4 BYTES OF SENSE INFORMATION
        BSR     WDREADDATA
        BSR     WDCOMPLETE             AND FINISH UP
        BEQ     WDGETERROR1            B/ NO ERROR
        SWI                            NOT SUPPOSED TO HAPPEN
WDGETERROR1
        LDA     WDRSERROR,X            GET THE ERROR
        RTS
*
WDWRITEPROTECTERR
        LDD     #ERR:DSKWRTPROT
        BRA     WDERROR
WDREADERROR
        LDD     #ERR:DISKREAD
        BRA     WDERROR
WDWRITEERROR
        LDD     #ERR:DISKWRITE
*       BRA     WDERROR
WDERROR STD     DCB:LASTERROR,X
WDDONE  LDX     WDINTDCB               To handle entry when DiskRead/Write done
        INC     DCB:DONEFLAG,X         Signal "disk done"
        INC     WDINTFREE              So task knows we're free
        LDS     WDSTACK                IN CASE WE ERRORED IN NESTED SUBROUTINES
        JMP     SDOS+SDOS:RTI
*
*       GET DATA BYTES FROM THE TARGET. ASSERT TARGET IS SELECTED.
*       THESE SIGNALS ARE CONSTANT THROUGHOUT THIS SEQUENCE:
*       SEL HIGH, BSY LOW, C/D HIGH, I/O LOW, MSG HIGH
*       FOR STATUS BYTE TRANSFER, TARGET OUTPUTS STATUS BYTE,
*       TARGET ASSERTS (MAKES LOW) REQ, HOST READS STATUS BYTE,
*       HOST ASSERTS ACK, TARGET DEASSERTS REQ.
*
WDREADDATA
        BSR     WDLOOPINIT
WDREADDATA1
        LDA     S.STAT                 SEE IF STATUS BYTE IS READY
        DEC     WDLOOPCOUNT,X
        BEQ     WDLOOPTIMEDOUT         B/ TIMED OUT
        ANDA    #S.REQ+S.MSG+S.CMDA+S.RDWR+S.BUSY
        CMPA    #S.RDATA
        BNE     WDREADDATA1            B/ REQ NOT ASSERTED
        LDA     S.DATA                 READ DATA BYTE
        STA     ,U+
        DECB
        BNE     WDREADDATA1
        RTS
*
*       COME HERE AFTER DATA TRANSFER IS COMPLETE
*       GET STATUS BYTE FROM THE TARGET. ASSERT TARGET IS SELECTED
*       AND THE DATA TRANSFER TO/FROM THE HOST IS COMPLETE.
*       THESE SIGNALS ARE CONSTANT THROUGHOUT THIS SEQUENCE:
*       SEL HIGH, BSY LOW, C/D LOW, I/O LOW, MSG HIGH
*       FOR STATUS BYTE TRANSFER, TARGET OUTPUTS STATUS BYTE,
*       TARGET ASSERTS (MAKES LOW) REQ, HOST READS STATUS BYTE,
*       HOST ASSERTS ACK, TARGET DEASSERTS REQ.
*
WDCOMPLETE
        BSR     WDLOOPINIT
WDCOMPLETE1
        LDA     S.STAT                 SEE IF STATUS BYTE IS READY
        DEC     WDLOOPCOUNT,X
        BEQ     WDLOOPTIMEDOUT         B/ TIMED OUT
        ANDA    #S.REQ+S.MSG+S.CMDA+S.RDWR+S.BUSY
        CMPA    #S.RSTA
        BNE     WDCOMPLETE1            B/ REQ NOT ASSERTED
        LDB     S.DATA                 READ STATUS BYTE
*
*       GET MESSAGE BYTE FROM THE TARGET.
*       THESE SIGNALS ARE CONSTANT THROUGHOUT THIS SEQUENCE:
*       SEL HIGH, BSY LOW, C/D LOW, I/O LOW, MSG LOW
*       FOR MESSAGE BYTE TRANSFER, TARGET OUTPUTS MESSAGE BYTE,
*       TARGET ASSERTS (MAKES LOW) REQ, HOST READS MESSAGE BYTE,
*       HOST ASSERTS ACK, TARGET DEASSERTS REQ.
*
WDCOMPLETE2
        LDA     S.STAT                 SEE IF STATUS BYTE IS READY
        DEC     WDLOOPCOUNT,X
        BEQ     WDLOOPTIMEDOUT         B/ TIMED OUT
        ANDA    #S.REQ+S.MSG+S.CMDA+S.RDWR+S.BUSY
        CMPA    #S.DONE
        BNE     WDCOMPLETE2            B/ REQ NOT ASSERTED
        LDA     S.DATA                 READ MESSAGE BYTE
*
*       IGNORE THE MESSAGE BYTE (ALL ZEROES)
*       CHECK STATUS BYTE FOR ERROR
*
        BITB    #%00000010             ANY ERRORS?
        RTS
*
*       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
*
WDTIMEOUT
        LDA     #$F6
        TFR     A,DP
        LDX     WDINTDCB               point at DCB, again
        LDY     DSKINFO:SECTORDB,X     POINT AT RDSI, AGAIN
        LDA     DCB:DONEFLAG,X
        BNE     WDCOUNTCOMMAND1        B/ DISK IS DONE, GO AWAY
        DEC     WDINTFREE
        LDD     #ERR:DEVICETIMEDOUT
        BRA     WDERROR                This marks DCB as 'done'
*
        SETDPR  $0000
        FIN     IODRIVERBODY
        END
