; ---------------------------- ; KBDHNDLR TTY Receive Handler ; ---------------------------- ; ; Two routines are provided that maintain an interrupt-driven ; TTY-receive queue. Appropriate PASCAL declarations are: ; ; CONST KQSIZE = maximum number of queued characters ; ; TYPE QUEUE = RECORD (* These are order-dependent !!! *) ; QSIZE: INTEGER; ; INP: INTEGER; ; OUTP: INTEGER; ; MAXCHAR: INTEGER; ; DATA: PACKED ARRAY [0..RCVQSIZE] OF CHAR; ; END; ; VAR KQ: QUEUE; (* must be declared in outermost block *) ; ; PROCEDURE KBDINIT (VAR Q: QUEUE; SIZE:INTEGER); EXTERNAL; ; PROCEDURE KBDFINIT; EXTERNAL; ; ; KBDINIT (KQ,KQSIZE); (* initialize the queue handler *) ; ; WHILE TRUE DO ; WITH KQ DO ; IF INP <> OUTP THEN (* characters available *) ; BEGIN ; CH := DATA[OUTP]; ; OUTP := OUTP+1; IF OUTP > QSIZE THEN OUTP := 0; ; ... ; END; ; ; KBDFINIT; (* terminate the queue handler *) ; ; The RECORD declaration for the queue must appear exactly as it ; does above except that you can of course use any names you like. ; Do NOT attempt to lump the first four integer variables together ; into a single group of the form list:INTEGER. In that case, ; the compiler allocates them in reverse order, so that your code ; and the interrupt handler will not agree about which words have ; what meaning. ; ; The queue handler runs continuously as an interrupt-driven task ; at high priority. As characters come in, it advances the queue ; INP pointer and keeps track of the maximum number of characters in ; the queue in the MAXCHAR variable. Queue overflow is indicated ; by MAXCHAR > QSIZE. You must terminate by calling KBDFINIT, or ; the TTY receive interrupts will be left enabled and you will end ; up crashing the system by executing garbage code when the next ; character is received. (KBDFINIT also repairs the interrupt ; vectors for breakpoints and the clock, so failing to call it will ; quite likely crash the system even in the absence of incoming ; TTY characters.) ; ; The manipulation of the clock and BPT interrupt vectors is borrowed ; from UCSD's old communications program. The purpose is to allow ; the clock handler to be interrupted by incoming TTY characters. ; KDB .EQU 177562 ; Receive Data Buffer absolute address KSR .EQU 177560 ; Receive Status Register absolute address KINTV .EQU 60 ; Receiver Interrupt Vector address CLKINTV .EQU 100 ; Clock interrupt vector address BPTINTV .EQU 14 ; BPT interrupt vector address QXCINTV .EQU 250 ; QX controller interrupt vector ; .PROC KBDINIT,2 ; (VAR Q:QUEUE, SIZE:INTEGER) ; .DEF KBDLOC ; holds vector address .DEF KBDPR ; holds old priority Q .EQU 4 ; stack offset for Q address SIZE .EQU 2 ; stack offset for QSIZE value ; MOV Q(SP),R0 ; obtain the Q record address MOV R0,KQADRS ; remember Q address MOV SIZE(SP),(R0)+ ; store size in QSIZE word MOV #0,(R0)+ ; clear INP, OUTP, and MAXCHAR MOV #0,(R0)+ MOV #0,(R0) ; ; MOV @#KINTV,KBDLOC ; save old interrupt vector MOV @#KINTV+2,KBDPR ; and old priority MOV #KHNDLR,@#KINTV ; store interrupt handler address MOV #200,@#KINTV+2 ; set interrupt priority 4 for TTY input ;MOV #100,@#KSR ; enable interrupts for TTY input ; MOV (SP)+,R0 ; pop return address from stack ADD #4,SP ; discard 2 parameters (4 bytes) JMP @R0 ; and return to PASCAL interpreter ; KQADRS .WORD 0 ; holds Q address for handler KBDLOC .WORD 0 ; holds old interrupt vector KBDPR .WORD 0 ; holds old interrupt priority ; QSIZE .EQU 0 ; offset from Q INP .EQU 2 ; likewise OUTP .EQU 4 MAXCHAR .EQU 6 DATA .EQU 10 ; KHNDLR: MOV R0,-(SP) ; free registers R0, R1, R2 for use MOV R1,-(SP) MOV R2,-(SP) MOV KQADRS,R2 ; fetch Q address saved by KBDINIT MOV INP(R2),R0 ; fetch INP value MOV R0,R1 ; make a working copy ADD R2,R1 ; R1 = address (Q) + value (INP) MOVB @#KDB,DATA(R1) ; DATA[INP] := input character BICB #200,DATA(R1) ; clear bit 8 (parity) BEQ EXIT ; ignore nulls (do not bump INP) INC R0 ; INP := INP+1 CMP QSIZE(R2),R0 BPL NOWRAP ; if QSIZE >= INP then no wraparound CLR R0 ; else INP := 0 NOWRAP MOV R0,INP(R2) ; restore INP ; SUB OUTP(R2),R0 BMI INOUT BEQ INOUT BR OUTIN ; if INP > OUTP, # char = INP - OUTP INOUT ADD QSIZE(R2),R0 ; otherwise, # char = QSIZE+1 + INP - OUTP ADD #1,R0 OUTIN CMP MAXCHAR(R2),R0 BPL EXIT ; if MAXCHAR >= # char, exit MOV R0,MAXCHAR(R2) ; otherwise, store new MAXCHAR ; EXIT MOV (SP)+,R2 ; restore registers for caller MOV (SP)+,R1 MOV (SP)+,R0 RTT ; return from interrupt ; CLKHNDLR: COM CLKFLG ; do not reexecute BPT if BPT handler BEQ CLKEXIT ; takes so long that clock ticks again BPT ; let breakpoint transfer to old clock CLKEXIT COM CLKFLG ; reset flag RTI ; and exit ; CLKFLG .WORD 0 ; flags reentry before BPT exit ; .PROC KBDFINIT .REF KBDLOC ; old interrupt vector saved by KBDINIT .REF KBDPR ; old kbd priority saved by KBDINIT ; MOV @#KBDPR,@#KINTV+2 ; restore interrupt priority MOV @#KBDLOC,@#KINTV ; and interrupt vector RTS PC ; and return ; .END