PGM RDRIV
REL
**********************************************************
* *
* device driver for 4 channel expansion board. *
* it only uses ports 0, 1 and 2. *
* *
**********************************************************
*
*
MPICMSK EQU #C2
COKRDY EQU 2
COPRDY EQU 1
ERRVEC EQU #3FC * ERROR TRAP VECTOR
BRKERR EQU #30
PARERR EQU 8
BR4800 EQU 16 baud rate = 4800
CONBLOCK EQU #C
DATBLOCK EQU #D
PICOCW3 EQU 8
PIC0W1 EQU 8
PIC0W2 EQU 9
PIT0CTRL EQU 3
PIT0CNT EQU 0
BRESET EQU #F
*
* Pkt types
*
A.BRKSTP EQU 995
A.BRKREQ EQU 996
A.SPDCHG EQU 997
A.TTYIN EQU 999
A.TTYOUT EQU 1000
*
* 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
D.RWKQ0 EQU 30 work queues for each USART
D.RWKQ1 EQU 32
D.RWKQ2 EQU 34
D.WWKQ0 EQU 36
D.WWKQ1 EQU 38
D.WWKQ2 EQU 40
*
* 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, character
P.A2 EQU 12 * argument 2, port number
*
* 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. DX, DI, BP 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
* Reset board
PUSH DX
MOV DX,D.IOW(BX)
ADD DX,!BRESET
OUT
* PIT initialisation
* select control block
ADD DX,!CONBLOCK-BRESET
OUT
* write similar mode control word to all 3 PITs (timers)
* options = load 2 bytes, baud rate generator, binary counter
* sets up for 4800 bps in div. by 16 mode
ADD DX,!PIT0CTRL-CONBLOCK
MOV AL,!#T00110110 pit 0
OUT
ADD DX,!PIT0CNT-PIT0CTRL
MOV AX,!BR4800
OUT lsb of count
MOV AL,AH
OUT msb of count
MOV AL,!#T01110110 pit 1
ADD DX,!PIT0CTRL-PIT0CNT
OUT
ADD DX,!PIT0CNT-PIT0CTRL
MOV AX,!BR4800
OUT lsb of count
MOV AL,AH
OUT msb of count
MOV AL,!#T10110110 pit 2
ADD DX,!PIT0CTRL-PIT0CNT
OUT
ADD DX,!PIT0CNT-PIT0CTRL
MOV AX,!BR4800
OUT lsb of count
MOV AL,AH
OUT msb of count
* Now the PIC.
MOV DX,D.IOW(BX)
ADD DX,!DATBLOCK
OUT
* ICW1 no slaves
MOV AL,!#T00010110
MOV DX,D.IOW(BX)
ADD DX,!PIC0W1
OUT
* ICW2 0
XOR AL,AL
INC DX
OUT
* mask all interrupts (OCW1)
NOT AL
OUT
* set auto-rotate priority mode
MOV AL,!#T10100000
DEC DX
OUT
*
MOV D.INT(BX),!RINT make offset for CALL in DCB
MOV D.INT+2(BX),CS correct code seg in DCB
MOV SI,D.VEC(BX) get interrupt vector number
* Enable interrupts for board (individual devices are masked
* by local PIC).
MOV CX,SI
MOV DL,!1
SHL DL,CL
NOT DL
IN MPICMSK
AND AL,DL
OUT MPICMSK
* Set up 3 USARTs.
*
MOV DX,D.IOW(BX)
ADD DX,!DATBLOCK
OUT
SUB DX,!DATBLOCK-1 control address
CALL USET port 0
ADD DX,!2
CALL USET port 1
ADD DX,!2
CALL USET port 2
POP DX
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
RETS
* USET routine sets up USARTs, called by init. code.
* the board has been reset so can write mode word
* mode set is: 1 stop bit, no parity, 8 data bits, clock factor 16
USET MOV AL,!#T01001110
OUT
* set command: rts, dtr, error reset, enable rx and tx
MOV AL,!#T00110111
OUT
RET
* Device uninitialisation routine. It is entered with
* the address of the DCB in BX, which must be preserved.
RUNIN MOV AX,ERRVEC
PUSH BX
MOV BX,D.VEC(BX) get address to plug
* Disable interrupts
MOV CX,BX
MOV DL,!1
SHL DL,CL
IN MPICMSK
OR AL,DL
OUT MPICMSK
ADD BX,!40
SHL BX
SHL BX
MOV (BX),AX plug it
MOV AX,ERRVEC+2
MOV 2(BX),AX
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. It returns non-zero.
* Preserve BP and DI. Work q should always be empty.
* Examine pkt type
RSTART SHL SI
MOV D.WKQ(BX),!0 clear work q
MOV AX,P.TYPE(SI)
CMP AX,!A.SPDCHG
JNE R0
* speed change, arg 1 contains count to be loaded (2 bytes)
MOV AX,P.A2(SI) get port
MOV DX,D.IOW(BX) base address
ADD DX,!CONBLOCK
OUT select control block
MOV DX,D.IOW(BX)
ADD DX,AX counter address
MOV AX,P.A1(SI) get count
OUT lsb
MOV AL,AH
OUT msb
* return pkt
RSRT MOV CX,D.ID(BX)
MOV BX,CRNTSK
SHL BX
* and return from routine
JIS DEVMVP
*
R0 CMP AX,!A.BRKSTP
JNE R1
* stop break condition
MOV AX,P.A2(SI) port no
MOV DX,D.IOW(BX) board address
ADD DX,!DATBLOCK
OUT
MOV DX,D.IOW(BX)
INC DX
SHL AX
ADD DX,AX control address
MOV AL,!#T00110111
OUT stop break
DEC DX
MOV TIOW,DX save i/o add.
* enable interrupts if something on q
MOV CX,P.A2(SI)
SHL CX
PUSH BX
ADD BX,!D.WWKQ0
ADD BX,CX
CMP (BX),!0
POP BX
JE R00 leave interrupts disabled
INC CX
MOV CH,!1
SHL CH,CL
NOT CH
MOV DX,D.IOW(BX)
ADD DX,!PIC0W2
IN
AND AL,CH
OUT
R00 MOV DX,TIOW
XOR AL,AL
OUT provoke interrupt
B RSRT go return pkt and leave
R1 EQU $
MOV DX,D.IOW(BX)
MOV TBASE,DX save board address
ADD DX,!DATBLOCK
OUT
* put on a port work q
ADD BX,!D.RWKQ0
CMP P.TYPE(SI),!A.TTYIN
JE RST0
ADD BX,!D.WWKQ0-D.RWKQ0
RST0 MOV CX,P.A2(SI) get port
SHL CX
ADD BX,CX find q start
MOV DX,(BX)
SHL DX
JNE RST1
* enable interrupts for device
MOV DX,TBASE
ADD DX,CX
CMP P.TYPE(SI),!A.TTYIN
JE RST0A
INC CX
RST0A MOV CH,!1
SHL CH,CL
NOT CH
MOV DX,TBASE
ADD DX,!PIC0W2
IN
AND AL,CH enable
OUT
B RST2
RST1 MOV BX,DX
RST1A MOV DX,(BX)
SHL DX
JNE RST1 not end of q yet
RST2 MOV (SI),!0
SHR SI
MOV (BX),SI add to end of q
* 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 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)
* find interrupting device
MOV DX,D.IOW-D.I(BX)
MOV TBASE,DX base address of board
ADD DX,!DATBLOCK
OUT select 'data block'
MOV AL,!#C
ADD DX,!PICOCW3-DATBLOCK
OUT request poll
IN get device number (and top bit set)
SHL AL clear top bit
JCS RINT00
JMP RINTRET no interrupt! (?!)
RINT00 XOR AH,AH clear top byte
MOV TXFLG,!0
SHR AX
SHR AX AX contains port no. carry is set if tx interrupt
ADC TXFLG,!0
MOV TNUM,AX save port no.
SHL AX port no. * 2
MOV DX,TBASE
ADD DX,AX form i/o address
MOV TIOW,DX save it
INC DX form status address
MOV TSTAT,DX save it
TEST TXFLG,!1
JE RINT01
JMP TINT handle tx interrupt
RINT01 LEA SI,D.RWKQ0-D.I(BX)
ADD SI,AX form work q address
MOV TWKQ,SI save it
MOV SI,(SI) get pkt
SHL SI MC pkt address
JNE RINT02
JMP RINTRET
RINT02 EQU $
XOR CX,CX prepare pkt RES2
MOV DX,TSTAT
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,TIOW
IN clear bum char
MOV AL,!#37
MOV DX,TSTAT
OUT clear error
XOR AX,AX put 0 in pkt
B RINT2B
*
RINT2A TEST AL,!PARERR parity error
JE RINT2AB no
MOV DX,TIOW
IN
MOV AL,!#37
MOV DX,TSTAT
OUT clear error
B RINTRET
*
RINT2AB MOV DX,TIOW 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
PUSH BX
MOV BX,TWKQ
MOV (BX),DX
POP BX
OR DX,DX
JNE RINTPK
MOV CX,TNUM last dequeued so stop device
SHL CX port no. * 2
MOV CH,!1
SHL CH,CL
MOV DX,TBASE
ADD DX,!PIC0W2
IN read mask
OR AL,CH
OUT set mask
* return pkt
RINTPK MOV CX,D.ID-D.I(BX) id of sender
MOV BX,CRNTSK setting args for MOVPKT
SHL BX
CIS DEVMVP
PUSH AX
MOV AL,!#20
MOV DX,TBASE
ADD DX,!PIC0W1
OUT send EOI to local PIC
POP AX
JIS DEVINT exit via INTENT
*
RINTRET MOV AL,!#20 EOI to local PIC
OUT PIC0W1 then return immediately
JIS DEVRET
*
* Handle tx interrupt
TINT LEA SI,D.WWKQ0-D.I(BX)
ADD SI,AX form work q address
MOV TWKQ,SI save it
XOR CX,CX
MOV DX,TSTAT
IN get status
TEST AL,!COPRDY
JNE TINT0
NOT CX
MOV AL,!#37
OUT reset error
TINT0 MOV SI,(SI)
SHL SI
JNE TINT01
CALL TSTOP no pkts, make sure interrupts stopped
B RINTRET
TINT01 EQU $
MOV P.RES1(SI),CX set result
MOV DX,(SI) deq pkt
PUSH BX
MOV BX,TWKQ
MOV (BX),DX new q head
POP BX
OR DX,DX
JNE TINT1
CALL TSTOP
*
TINT1 CMP P.RES1(SI),!0
JNE RINTPK
CMP P.TYPE(SI),!A.BRKREQ
JNE TINT2
* make a break condition
MOV DX,TSTAT
MOV AL,!#T00111111
OUT
* stop interrupts
CALL TSTOP
* return pkt
B RINTPK
*
TINT2 MOV DX,TIOW
MOV AX,P.A1(SI) get char
OUT write it
JMP RINTPK return pkt
*
TSTOP MOV CX,TNUM get device number
SHL CX
INC CX form bit pos for tx
MOV CH,!1
SHL CH,CL
MOV DX,TBASE
ADD DX,!PIC0W2
IN
OR AL,CH
OUT
RET
EVEN
DSEG
RINITI DW RINIT
DW 0
RUNINI DW RUNIN
DW 0
TBASE DW 0 base address of board
TSTAT DW 0 current status address
TIOW DW 0 " i/o "
TWKQ DW 0 " work q "
TNUM DW 0 " interrupting device
TXFLG DW 0 non-zero if tx interrupt
END