PGM DKDRIV
        REL
**********************************************************
*                                                        *
* Driver for Intel floppy disk system (double density).  *
* The actions recognised are read, write, seek and dummy *
* (gets status).                                         *
*                                                        *
* Graham Adams  2 May 1980                               *
*              20 May 1980                               *
*              10 September 1980                         *
*              19 June 1981                              *
*              11 August 1981                            *
*                                                        *
**********************************************************
*
* States etc.
*
ERRVEC  EQU         #3FC          * error trap vector
*
* Device control block symbols
*
*               0               * device driver ptr (BCPL)
*               0               * code (0)
*               0
D.ID    EQU       6               * id
D.WKQ   EQU       8               * work queue
D.START EQU      10               * start routine - for QPKT
D.STOP  EQU      14               * stop routine - for DQPKT
D.CALL  EQU      18              * subroutine jump to
D.INT   EQU      20              * interrupt routine
D.I     EQU D.CALL+6              * offset for interrupt rtn
D.VEC   EQU      24              * interrupt vector
D.UMASK   EQU    26               * unit error bits mask
* IOPB starts here
D.IOPB    EQU 27
D.CHANWD  EQU 27                 * channel word
D.DISKIN  EQU 28                 * diskette instruction
D.NUMREC  EQU 29                 * no. of records
D.TRACK   EQU 30                 * track address
D.SECTOR  EQU 31                 * sector address
D.BUFF    EQU 32                 * buffer address (16 bits)
*             33
* end of IOPB
D.REQFLG  EQU 34
*
*
* Packet symbols
*
P.ID    EQU       2               * task or device id
P.TYPE  EQU       4               * type or action
P.RES1  EQU       6               * first result
P.RES2  EQU       8              * second result
P.A1    EQU      10              * argument 1
P.A2    EQU      12
P.A3    EQU      14
P.A4    EQU      16
P.A5    EQU      18
P.A6    EQU      20
P.ACT   EQU    P.TYPE             * controller action
P.DERR  EQU    P.RES1             * controller error code
P.STAT  EQU    P.RES2             * drive status
P.BUFF  EQU    P.A1               * store buffer addr
P.WCNT  EQU    P.A2               * word count
P.DRIV  EQU    P.A3               * drive number
P.CYL   EQU    P.A4               * disc cylinder
P.SUR   EQU    P.A5               * disc surface
P.SEC   EQU    P.A6               * disc sector
*
* Device actions
*
A.DUMMY   EQU     1000           * dummy - gets status
A.READ    EQU     1001           * read from disc
A.WRITE   EQU     1002           * write to disc
A.SEEK    EQU     1008             * seek track
*
* I/O addresses for the channel
DKBASE    EQU #78                * base address of channel
WSTART    EQU DKBASE+1           * write IOPB address and start
RESET     EQU DKBASE+7           * reset (output)
RDSS      EQU DKBASE+0           * read subsystem status
RDRESTP   EQU DKBASE+1           * read result type
RDRESBT   EQU DKBASE+3           * read result byte
*
* diskette operations
*
NOOP      EQU 0
SEEKOP    EQU 1
FORMOP    EQU 2
RECALOP   EQU 3
READOP    EQU 4
VEROP     EQU 5
WRITEOP   EQU 6
WDELOP    EQU 7
*
*
MPICMSK   EQU #C2                * master PIC address
STOPREQ   EQU   1
INTPEND   EQU   4
CALREQ    EQU   2
ACTREQ   EQU 4
*
* The rootnode
*
CRNTSK  EQU  #506
DEVMVP  EQU  #51A * MOVPKT for device drivers (MC addr)
DEVINT  EQU  #51E * INTENT for device drivers (MC addr)
DEVRET  EQU  #522 * INTRET for device drivers (MC addr)
        DW   DKINITI         * initialisation rtn
        DW   DKUNINI         * uninitialisation rtn
* Device initialisation routine. It is entered with the
* address of the DCB in BX. BX and DI must be preserved.
DKINIT   MOV D.START(BX),!DKSTART
         MOV D.STOP(BX),!DKSTOP
         MOV D.START+2(BX),CS
         MOV D.STOP+2(BX),CS
         PUSH SI
         MOV SI,!DKINT                 calculate CALL offset
         MOV D.INT(BX),SI              put in DCB
         MOV D.INT+2(BX),CS            correct code seg in DCB
         MOV SI,D.VEC(BX)              get interrupt number
         ADD SI,!40                    calculate interrupt vector address
         SHL SI
         SHL SI
         MOV (SI),BX                  plug vector with address of CALL
         ADD (SI),!D.CALL
        MOV 2(SI),!0                  code segment = 0
         POP SI
DKINITA1 IN RDSS                       if interrupt pending
         TEST AL,!INTPEND
         JE DKINIT1
         IN RDRESTP                     clear by reading result
         PUSH BX
         POP BX
         IN RDRESBT
DKINIT1  OUT RESET
         JMP  DKENINT        enable interrupts and return
         RETS
* Device uninitialisation routine. It is entered with
* the address of the DCB in BX, which must be preserved.
DKUNIN   PUSH BX
         MOV AX,ERRVEC
         MOV BX,D.VEC(BX)
         MOV CX,BX
         ADD BX,!40                 calculate address to reset
         SHL BX
         SHL BX
         MOV (BX),AX                plug it
         MOV AX,ERRVEC+2
        MOV 2(BX),AX
         MOV DL,!1
         SHL DL,CL
         IN MPICMSK
         OR AL,DL
         OUT MPICMSK
         POP BX
         RETS
* Device start routine. This is entered whenever a pkt
* is sent to the device and its work queue is empty. It
* is entered with the address of the DCB in BX, and the
* BCPL address of the packet in SI.
DKSTART   PUSH SI
          SHL SI                pkt ptr
         MOV AX,CRNTSK
         SHL AX
         MOV DKTSK,AX
         CALL DKACT
         POP SI
         RETS
* Device stop routine. This is entered if the head pkt
* is dequeued from the device. It is entered with the
* address of the packet in SI and the DCB in BX, which
* must be preserved.
* CX must also be preserved.
DKSTOP   IN RDSS
         TEST AL,!INTPEND            interrupt pending?
         JNE DOSTOP                  yes, ok to stop
         OR D.REQFLG(BX),!STOPREQ[B]    set stop request state
         RETS
*
DOSTOP   IN RDRESTP                  clear interrupt
         IN RDRESBT
         RETS
*
*
* Disc interrupt routine.
* This is entered whenever the disc completes the last
* requested action.  This can be when a read or write or seek
* is complete, in which case the packet which requested
* it is returned. If the disc interrupts to give an error,
* the error field of the packet will be set non-zero.
* If the interrupt is due to the ready status of one of the
* drives changing, check for drive ready and see if any pkts
* are waiting.
* The DCB address may be found from the subroutine return address
* on the stack.
DKINT    PUSH BX
         PUSH AX
         PUSH CX
         PUSH DX
         PUSH SI
         MOV BX,SP
         MOV BX,10(BX)             find return address in DCB
         SUB BX,!D.I               calculate start of DCB
         MOV SI,D.WKQ(BX)          head pkt
         SHL SI                    MC head pkt ptr
         MOV AX,CRNTSK
         SHL AX
         MOV DKTSK,AX
         IN RDRESTP                 read result type
         MOV AH,AL
         IN RDRESBT                  read result byte
         OR AH,AH                    res type=0? (error bits)
         JNE DKINT7                  ready status has changed
         MOV DKTEMP,AL               save error bits
         MOV AL,D.REQFLG(BX)
         TEST AL,!STOPREQ            stop device?
         JE DKINT1
* stop device
         AND D.REQFLG(BX),!~STOPREQ[B] clear
         PUSH CS
         CALL DOSTOP
         B DKINT6                    INTRET
*
DKINT1   TEST AL,!CALREQ             recalibrating?
         JE DKINT1A
* was recalibrating so now try Action
         AND D.REQFLG(BX),!~CALREQ[B]   clear flag
         MOV AX,CRNTSK
         SHL AX
         B DKINT3                    check for pkts
*
DKINT1A  TEST AL,!ACTREQ
         JE DKINT6                   IGNORE
* action complete, return pkt
DKINT2   TEST DKTEMP,!4[B]            seek error?
         JNE DKINTR                   yes, recalibrate
         AND D.REQFLG(BX),!~ACTREQ
         CALL SENDPKT
DKINT3   MOV SI,D.WKQ(BX)
         SHL SI
*
*
*
         JE DKINT5                   no more pkts
         CALL DKACT                  initiate next action
         JE DKINT3                   pkt was sent back
DKINT5   JIS DEVINT                  exit via INTENT
DKINT6   JIS DEVRET                  exit via INTRET
* the ready state of one of the drives has changed
DKINT7   IN RDSS
         MOV DKTEMP,!0[B]     clear error bits
         MOV AX,CRNTSK               set task
         SHL AX
         B DKINT3                    check for pkts
* set up recalibrate operation
DKINTR   MOV AX,P.DRIV(SI)           get drive number
         SHL AL
         SHL AL
         SHL AL
         SHL AL
         OR AL,!RECALOP              construct DISKINS byte
         MOV D.DISKIN(BX),AL
         OR D.REQFLG(BX),!CALREQ[B]
         MOV AX,BX
         ADD AX,!D.IOPB              get IOPB address
         OUT WSTART
         MOV AL,AH
         OUT WSTART+1                go
         B DKINT6                    exit
*
* This routine initiates a disc action or returns a status
* request pkt. It is entered with the MC pkt address in SI
* and the MC DCB address in BX.
* If the pkt is sent back then ZF is set (0 return).
* New TCB is in AX.
DKACT    MOV AX,P.DRIV(SI)       get unit number
         SHL AL
         SHL AL
         SHL AL
         SHL AL                   shifted left four places
         MOV D.DISKIN(BX),AL      put in IOPB
         MOV AX,P.CYL(SI)         get track
         MOV D.TRACK(BX),AL
         MOV AX,P.SEC(SI)         get sector
         SHL AL
         SHL AL
         INC AL                   h/w address = 4*sector + 1
         MOV D.SECTOR(BX),AL
         MOV AX,P.WCNT(SI)
         OR AL,AL
         JE DKACT1              exact multiple of 256?
         INC AH                 no
DKACT1   SHL AH                 logical -> physical
         SHL AH
         MOV D.NUMREC(BX),AH    number of physical records
*
*
         CMP P.ACT(SI),!A.DUMMY status request?
         JE SENDPKT             yes
         IN RDSS
* see if drive is ready, if expanded to 4 drives this will
* need changing.
         MOV AH,P.DRIV(SI)
         INC AH
         TEST AL,AH             test ready bit in status byte
         JE DKACT4A             not ready, wait for interrupt
* ok, setup the action
         MOV DX,P.BUFF(SI)      get buffer address
         SHL DX
         MOV D.BUFF(BX),DX      store in IOPB
* Set ACT flag
         OR D.REQFLG(BX),!ACTREQ
         MOV AX,P.ACT(SI)       get action
         CMP AX,!A.READ
         JNE DKACT3
         OR D.DISKIN(BX),!READOP[B]
         B DKACT4
DKACT3   CMP AX,!A.WRITE
         JNE SENDPKT
         OR D.DISKIN(BX),!WRITEOP[B]
DKACT4   MOV AX,BX
         ADD AX,!D.IOPB           get IOPB address
         OUT WSTART
         MOV AL,AH
         OUT WSTART+1             start the channel
DKACT4A  MOV AX,DKTSK             return set task, clear ZF
         CMP AX,!0
         RET
*
SENDPKT  IN RDSS                 get subsys. status
         XOR AH,AH
         MOV P.STAT(SI),AX        goes to RESULT2 in SENDPKT
         AND AL,D.UMASK(BX)
         XOR AL,D.UMASK(BX)      if bits are 1 -> 0
         MOV AH,DKTEMP            get saved error bits
         MOV P.DERR(SI),AX        becomes result of SENDPKT (in BLIB)
         MOV CX,D.ID(BX)          id for MOVPKT
         MOV DX,(SI)              DEQ pkt
         MOV D.WKQ(BX),DX
         PUSH SI
         PUSH BX
         MOV BX,DKTSK             task with which to compare priority
         CIS DEVMVP               MOVPKT
         POP BX
         POP SI
         MOV DKTSK,AX
         XOR DX,DX                set ZF, new TCB in AX
         RET
*
DKENINT  MOV DL,!1
         MOV CX,D.VEC(BX)
         SHL DL,CL
         NOT DL
         IN MPICMSK
         AND AL,DL
         OUT MPICMSK
         RETS
*
         DSEG
* save area!
         EVEN
DKINITI  DW DKINIT
         DW 0
DKUNINI  DW DKUNIN
         DW 0
DKTEMP   DB 0
DKTSK    DW 0
         END