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