PGM PDRIV
REL
**********************************************************
* *
* Device driver for single character output devices ie. *
* Console printer, paper tape punch etc. *
* *
**********************************************************
*
* States and interrupt vectors
*
MPICMSK EQU #C2
COPRDY EQU 1
ERRVEC EQU #3FC * ERROR TRAP VECTOR
*
* Device control block symbols
*
* 0 ; DEVICE DRIVER PTR (BCPL)
* 0 ; CODE LINK (=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 PINITI * initialisation rtn
DW PUNINI * 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
PINIT MOV D.START(BX),!PSTART
MOV D.STOP(BX),!PSTOP
MOV D.START+2(BX),CS
MOV D.STOP+2(BX),CS
PUSH SI
MOV SI,!PINT 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 CS := 0 for interrupt
POP SI
RETS
* Device uninitialisation routine. It is entered with
* the address of the DCB in BX, which must be preserved.
PUNIN 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.
PSTART MOV DX,D.CSW(BX) get address of status register
IN
TEST AL,!COPRDY device ready?
JNE PST yes - write immediately
PSTARTI MOV DL,!1 no - enable interrupts
MOV CX,D.VEC(BX) interrupt vector number
SHL DL,CL
NOT DL
IN MPICMSK
AND AL,DL
OUT MPICMSK
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.
PSTOP 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 routine writes the head pkt and returns it
PST SHL SI
* check for line-feed, if found send 3 idles
CMP P.A1(SI),!#A line feed?
JNE PSTA1
MOV P.A1(SI),!0 set up idling
MOV P.RES2(SI),!3 idle count
MOV AL,!#A
MOV DX,D.IOW(BX)
OUT
PUSH CS
CALL PSTARTI enable interrupts
B PSTA2
*
PSTA1 CMP P.A1(SI),!0 idling?
JNE PSTA3 no
DEC P.RES2(SI)
JE PSTA3 handle last idle normally
CMP P.RES2(SI),!3 see if count in range
JAE PSTA3 not one of mine
PUSH CS
CALL PSTARTI enable interrupts with impunity
MOV DX,D.IOW(BX)
XOR AL,AL
OUT
PSTA2 MOV AX,CRNTSK
SHL AX
RETS
*
PSTA3 MOV DX,(SI) dequeue pkt
MOV D.WKQ(BX),DX
OR DX,DX
JNE PST1
MOV CX,D.VEC(BX) disable interrupts
MOV DL,!1
SHL DL,CL
IN MPICMSK
OR AL,DL
OUT MPICMSK
PST1 MOV CX,D.ID(BX)
MOV DX,D.IOW(BX) write char after disabling
MOV AX,P.A1(SI) get it
OUT write it
MOV BX,CRNTSK
SHL BX getting args ready for MOVPKT
JIS DEVMVP MOVPKT will execute RETS instruction
* 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
PINT 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)
SUB BX,!D.I
MOV SI,D.WKQ(BX) get BCPL pkt ptr
OR SI,SI
JE PINTOUT MAY GET SPUROUS INT. AFTER STOPPING
PUSH CS
CALL PST
JIS DEVINT return via INTENT
PINTOUT JIS DEVRET
*
EVEN
DSEG
EVEN
PINITI DW PINIT
DW 0
PUNINI DW PUNIN
DW 0
END