PGM RDRIV
        REL
**********************************************************
*                                                        *
* Device driver for single character input devices ie.   *
* Console keyboard, paper tape reader etc.               *
*                                                        *
**********************************************************
*
* States and interrupt vectors
*
MPICMSK EQU  #C2
COKRDY  EQU  2
ERRVEC  EQU  #3FC                 * ERROR TRAP VECTOR
BRKERR  EQU #30
PARERR  EQU 8
*
* Device control block symbols
*
*               0               ; device driver ptr (BCPL)
*               0               ; link to 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 offset address
D.I     EQU D.CALL+6              * offset for interrupt rtn
D.VEC   EQU      24               * interrupt vector number
D.CSW   EQU      26               * control and status word
D.IOW   EQU      28            i/o address
*
* 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
*
* 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   RINITI          * initialisation rtn
        DW   RUNINI          * uninitialisation rtn
* Device initialisation routine. It is entered with the
* address of the DCB in BX. BX and DI must be preserved.
* assume console USART has been set up by the monitor
RINIT   MOV D.START(BX),!RSTART
        MOV D.STOP(BX),!RSTOP
        MOV D.START+2(BX),CS
        MOV D.STOP+2(BX),CS
        PUSH SI
        MOV SI,!RINT           make offset  for CALL in DCB
        MOV D.INT(BX),SI
        MOV D.INT+2(BX),CS      correct code seg in DCB
        MOV SI,D.VEC(BX)       get interrupt vector number
        ADD SI,!40
        SHL SI
        SHL SI
        MOV (SI),BX            plug interrupt vector with
        ADD (SI),!D.CALL       address
        MOV 2(SI),!0           interrupt CS = 0
        POP SI
        RETS
* Device uninitialisation routine. It is entered with
* the address of the DCB in BX, which must be preserved.
RUNIN   PUSH AX
        MOV AX,ERRVEC
        PUSH BX
        MOV BX,D.VEC(BX)       get address to plug
        ADD BX,!40
        SHL BX
        SHL BX
        MOV (BX),AX            plug it
        MOV AX,ERRVEC+2
        MOV 2(BX),AX
        POP BX
        POP AX
        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. It returns non-zero.
RSTART  MOV DX,D.IOW(BX)       get address of i/o register
        IN                       ignore chars in buffer
        IN
* work out which PIC bit should be unmasked
        MOV CX,D.VEC(BX)
        MOV DL,!1
        SHL DL,CL
        NOT DL                 0 bit now in correct position
        IN MPICMSK             get current mask
        AND AL,DL
        OUT MPICMSK            enable interrupt
        NOT DL                 return non-zero
        RETS
* Device stop routine. This is entered if the head pkt
* is dequeued from the device. It is entered with the
* DCB in BX, which must
* be preserved.
RSTOP   PUSH CX
        MOV DL,!1
        MOV CX,D.VEC(BX)       get interrupt number
        SHL DL,CL
        IN MPICMSK
        OR AL,DL
        OUT MPICMSK
        POP CX
        RETS
* This code is entered by the subroutine jump in the DCB
* which in turn was entered via the interrupt vector. The last
* word pushed onto the m/c stack therefore is the address of the
* DCB + D.I
RINT    PUSH BX
        PUSH AX
        PUSH CX
        PUSH DX
        PUSH SI
        MOV BX,SP              get stack pointer
        MOV BX,10(BX)          get IP (points into DCB)
        MOV DX,D.CSW-D.I(BX)   status register address
        MOV SI,D.WKQ-D.I(BX)   get pkt
        MOV AX,CRNTSK
        SHL AX
        SHL SI                 MC pkt address
        JE RINTOUT
        XOR CX,CX              prepare pkt RES2
        IN                       get device status
        TEST AL,!COKRDY        character received?
        JNE RINT2              ok USART agrees
        NOT CX                 error
RINT2   MOV P.RES2(SI),CX      set RES2
        TEST AL,!BRKERR        break hit?
        JE RINT2A              probably not
        MOV P.RES2(SI),!1
        MOV DX,D.IOW-D.I(BX)
        IN                    clear bum char
        MOV AL,!#37
        MOV DX,D.CSW-D.I(BX)
        OUT                    clear error
        XOR AX,AX              put 0 in pkt
        B RINT2B
*
RINT2A  TEST AL,!PARERR        parity error
        JE RINT2AB             no
        MOV DX,D.IOW-D.I(BX)
        IN
        MOV AL,!#37
        MOV DX,D.CSW-D.I(BX)
        OUT                    clear error
        MOV AX,CRNTSK
        SHL AX
        B RINTOUT
*
RINT2AB MOV DX,D.IOW-D.I(BX)   get address of i/o register
        IN                     fetch character
        XOR AH,AH              clear top byte of word
RINT2B  MOV P.RES1(SI),AX      store char. in pkt
        MOV DX,(SI)            deq pkt
        MOV D.WKQ-D.I(BX),DX
        OR DX,DX
        JNE RINT3
        MOV CX,D.VEC-D.I(BX)   last dequeued so stop device
        MOV DL,!1
        SHL DL,CL
        IN MPICMSK
        OR AL,DL
        OUT MPICMSK
*
RINT3   MOV CX,D.ID-D.I(BX)    id of sender
        MOV BX,CRNTSK          setting args for MOVPKT
        SHL BX
        CIS DEVMVP
RINTOUT JIS DEVINT              exit via INTENT
*
        EVEN
        DSEG
RINITI   DW    RINIT
         DW    0
RUNINI   DW    RUNIN
         DW    0
        END