.TITLE YRDRV .IDENT /14.3XC/ ; RECEIVER DL11 DRIVER ; RECEIVE HALF ONLY (1ST CSR/DATA REG OF PAIR) ; ; DL11 ASYNCHRONOUS COMMUNICATIONS DRIVER ; ; INPUT TERMINATION ON HEX FF (377 OCTAL) CHARACTER. ALSO ON ; BYTE COUNT... ; ; MACRO LIBRARY CALLS ; LD$YR=0 ; REMOVE IF YR: NOT LOADABLE .IIF NDF,$XDT,NRM$YD=0 .MCALL HWDDF$,PKTDF$ HWDDF$ ;DEFINE HARDWARE REGISTERS PKTDF$ ;DEFINE I/O PACKET OFFSETS .MCALL UCBDF$,CUCDF$ UCBDF$ ; DEFINE UCB OFFSETS CUCDF$ ;COMMUNICATIONS UCB LABELS ; ; CONFIGURATION DEPENDENT PARAMETERS ; ; ; EQUATED SYMBOLS ; ; ; RXCSR BIT ASSIGNMENTS ; DTSTCH= 100000 ;DATA SET CHANGE RING= 40000 ;RING LINE ASSERTED CTS= 20000 ;CLEAR TO SEND CRRIER= 10000 ;CARRIER STATE RECACT= 4000 ;RECEIVER ACTIVE RXDONE= 200 ;RECEIVER DONE RCVENB= 100 ;RECEIVER INTERRUPT ENABLE DSINTE= 40 ;DATA SET CHANGE INTERRUPT ENABLE RTS= 4 ;REQUEST TO SEND TRMRDY= 2 ;DATA TERMINAL READY ; ; RXDBUF BIT ASSIGNMENTS ; RXERR= 100000 ;RECEIVER ERROR "OR" BIT OVRNER= 40000 ;RECEIVER OVERRUN ERROR IF SET FRMERR= 20000 ;RECEIVER FRAMING ERROR ; ; TXCSR BIT ASSIGNMENTS ; TXRDY= 200 ;TRANSMITTER DONE BIT TXINTE= 100 ;TRANSMITTER INTERRUPT ENABLE BIT BREAK= 1 ;CLAMP OUTPUT TO SPACE ; LOCAL DATA ; ; UNIT IMPURE DATA TABLE (INDEXED BY UNIT, POINTS TO UCB) ; D$$E11=1 CNTBL: ;REF LABEL UNITBL: ;UCB ADDRESSES .REPT D$$E11 .WORD 0 ;ENSURE SET TO ZERO .ENDM .IF GT D$$E11-1 TEMP: ;REF LABEL UNIT: .BLKW 1 ;TEMPORARY STORAGE FOR UNIT NUMBER .ENDC .ENABL LSB ; ; DRIVER DISPATCH TABLE ; $YRTBL::.WORD DLINIT ;DEVICE INITIALIZATION .WORD DLCANC ;DEVICE I/O CANCELLATION .WORD DLTMO ;TIMEOUT ENTRY POINT .WORD DLPWRF ;POWER FAIL ROUTINE ;+ ;**- DLINIT - DL-11 SYNCHRONOUS COMMUNICATION CONTROLLER I/O INITIATOR ; ; DLINIT IS ENTERED WHEN AN I/O REQUEST IS QUEUED ON THE DEVICE, AND AT THE END ;OF EACH QIO REQUEST WHICH OBEYS THE NORMAL RSX-11M INPUT/OUTPUT LOGIC FLOW. ;IF THE DEVICE IS AVAILABLE AND A REQUEST IS IN THE QUEUE FOR THAT UNIT, THE ;REQUEST IS INITIATED. IF NO REQUEST EXISTS FOR THAT UNIT OR IF IT IS BUSY, AN ;EXIT IS TAKEN TO THE CALLER. NOTE THAT BECAUSE OF THE NATURE OF THE DL-11, ;EACH UNIT IS A CONTROLLER ITSELF, HAS ITS OWN SCB, AND THEREFORE ITS OWN ;QUEUE. EACH TIME DLINIT IS CALLED, IT IS CALLED TO SERVICE ONLY THE UNIT ;SPECIFIED IN THE CALL. NOTE ALSO THAT ONLY 8-BIT, NO PARITY DATA IS SUPPORTED. ; ; INPUTS: ; ; R4 = STATUS CONTROL BLOCK ADDRESS ; R5 = ADDRESS OF THE UCB TO BE INITIATED. ; ; OUTPUTS: ; ; IF A REQUEST IS SUCCESSFULLY DEQUEUED, THE ; DEVICE IS INITIATED APPROPRIATELY. ;- J140$: JMP 140$ DLINIT: .IF DF,NRM$YD ; NOP .IFF BPT ;DEBUG XDT CALL .ENDC CALL $GTPKT ;GET AN I/O PACKET TO PROCESS BCS J140$ ;NO REQUEST OR UNIT BUSY ; ; SAVE UCB ADDRESS MOV R5,UNITBL ; NOTE MUST USE UNITBL(R3) IF MULTI-UNIT DRIVER ; (CHECK IF R3 IS BYTE OR WORD OFFSET ALSO...) ; ; THE FOLLOWING ARGUMENTS ARE RETURNED BY $GTPKT: ; ; R1 = ADDRESS OF THE I/O REQUEST PACKET. ; R2 = PHYSICAL UNIT NUMBER OF THE REQUEST UCB. ; R3 = CONTROLLER INDEX ; R4 = ADDRESS OF THE STATUS CONTROL BLOCK. ; R5 = ADDRESS OF THE UCB SPECIFIED IN THE ; DLINIT CALL. ; ; DL11 I/O REQUEST PACKET FORMAT ; ; WORD CONTENT ; ; 0 I/O QUEUE THREAD WORD ; 1 REQUEST PRIORITY, EVENT FLAG NUMBER ; 2 ADDRESS OF THE TCB OF THE REQUESTER TASK ; 3 POINTER TO SECOND LUN WORD IN TASK HEADER ; 4 CONTENTS OF FIRST LUN WORD (UCB) ; 5 I/O FUNCTION CODE ; 6 VIRTUAL ADDRESS OF I/O STATUS BLOCK ; 7 RELOCATION BIAS OF I/O STATUS BLOCK ; 10 I/O STATUS BLOCK ADDRESS (REAL OR DISPLACEMENT +140000) ; 11 VIRTUAL ADDRESS OF AST SERVICE ROUTINE ; 12 RELOCATION BIAS OF I/O BUFFER ; 13 BUFFER ADDRESS FOR TRANSFER ; 14 TOTAL BYTE COUNT TO TRANSFER ; 15 BYTE COUNT FOR SECOND PART OF TRANSMISSION OR 0 ; 16 NOT USED ; 17 NOT USED ; 20 NOT USED ; MOV S.CSR(R4),R2 ;GET RECEIVER CSR ADDRESS ADD #I.FCN+1,R1 ;POINT TO I/O FUNCTION CODE CMPB #IO.RVB/256.,(R1);READ VIRTUAL? BEQ 20$ ; IF SO TREAT LIKE READ LOGICAL CMPB #IO.WVB/256.,(R1);WRITE VIRT? BEQ 24$ ;IF SO GO HANDLE CMPB #IO.INL/256.,(R1) ;CHECK IF TRANSFER FUNCTION BLOS 40$ ;BRANCH ON CONTROL FUNCTION CMPB #IO.RLB/256.,(R1) ;READ LOGICAL? BEQ 20$ ;YES, SERVICE READ REQUEST ; ; FALL THROUGH ON TRANSMIT (WRITE LOGICAL) ; 24$: BR DLSUCC ;TRANSMIT...RETURN ALL WELL ;(REALLY NOTHING TO DO...) ; ; RECEIVE FUNCTION INITIATION ; 20$: ;REFERENCE LABEL 30$: ;REF LABEL ; SET UP TIMEOUT MOVB S.ITM(R4),S.CTM(R4) ;INIT TIMER... MOV R5,R0 ;SET UP TRANSFER VECTOR ADD #U.RBUF,R0 ;RECEIVE TRANSFER INFORMATION HERE MOV U.BUF(R5),(R0)+ ;TRANSFER BIAS MOV U.BUF+2(R5),(R0)+ ;AND VIRTUAL ADDRESS MOV U.CNT(R5),(R0) ;AND COUNT CLR DLFLG ;;; SAY WE'RE STILL TAKING TYPE AHEAD ; Note that the reset of DLFLG should be redundant but since the next ; section of code explicitly depends on it, be safe rather than sorry. ; ; ??? NEXT INST? COMMENTED OUT GCE 10/30/1986 ; WOULD HAVE READ TERMINAL DATA REGISTER AND THROWN OUT THE BYTE. ; CAN'T HAVE THAT WHERE WE NEED INPUT OPEN ALL THE TIME... ; TSTB 2(R2) ; CLEAR RECEIVE BUFFER STATUS ; ; TRY AT THIS POINT TO GRAB WHAT WE CAN INTO THE USER'S BUFFER IF THERE ; IS TYPE-AHEAD THERE. ; ********************************************************************** CMP DLINP,DLOUT ; NEED TO GET TYPE AHEAD? BEQ 1136$ ; IF NOT BRANCH AND SKIP CRUFT MOV R0,-(SP) MOV R1,-(SP) ;;;NEED SOME REGS MOV R2,-(SP) PSW=177776 ;PDP11 Processor Status Word address MOVB @#PSW,-(SP) MOVB #340,@#PSW ;;; TO PRIO 7 .IF DF,M$$MGE MOV @#KISAR6,-(SP) ;;;SAVE CURRENT MAPPING MOV U.RBUF(R5),@#KISAR6 ;;;MAP TO USER BUFFER .ENDC MOV PC,R1 ;;;POINT WITH R1 AT... ADD #,R1 ;;; DLBUF (PIC) ;;; Now R1 holds absolute address of base of DLBUF for later use. ;;; DLOUT and DLINP pointers are relative to this base and always ;;; modulo its length... 1132$: MOV DLOUT,R0 ;;; GET OUTPUT POINTER 1134$: ;;; OPEN AN INTERRUPT WINDOW ONCE PER CHARACTER HERE TO MAKE SURE ;;; THE INTERRUPT HANDLER GETS A CRACK AT ALL THE CHARACTERS. ;;; ;;; note: This DEPENDS on DLOUT not changing in the interrupt handler. ;;; Since we're still in the mode DLFLG=0 here, the interrupt ;;; handler only touches DLINP so this logic is OK. We mess with DLOUT ;;; here, but if anything is left in the ring buffer (typeahead ;;; buffer) when we get into interrupt mode, we clean it up ;;; there. ;;; CLRB @#PSW ;;; TO PRIO 0 NOP ;;; LET INT OCCUR NOP ;;; (nop's just in case inst. that ;;; alters PSW cannot itself be interrupted. This is ;;; not as I recall it, but a couple insts. just make ;;; a bit larger window. We are still not protected ;;; against wrap-around, except that the buffer is ;;; so large (512 bytes) that it's hard to see how ;;; it can occur while a task is actively trying to ;;; read the device at full speed. If a 512 byte ;;; typeahead buffer won't save the day, probably ;;; nothing will... ;;; MOVB #340,@#PSW ;;; BACK TO PRIO 7 ;;; Since this segment manipulates the out pointer, we'll make sure ;;; these manipulations all occur together... easier to deal with ;;; if an interrupt tries to mess them over... CMP R0,DLINP ;;; SEE IF WE HAVE ALL CHARS BEQ 1133$ ;;;IF EQ YES MOV R1,R2 ;;; USE R2 AS ACCUMULATOR ADD R0,R2 ;;; POINT AT DESIRED BYTE MOVB @R2,@U.RBUF+2(R5) ;;; STORE BYTE FOR USER ;;; ;;; CHECK FOR TERMINATOR CHAR IN TYPE AHEAD BUFFER... CMPB YRTRM,@R2 ;;;IF EQUAL, THIS IS A TERMINATOR CHAR SO ;;;DON'T COUNT DOWN THE I/O AND GET OUTTA HERE BNE 3130$ ;;; IF NOT TERMINATOR JUST KEEP STORING CHARS ;;; WHOOPS... ;;; Got a terminator out of the typeahead buffer... gotta finish. ;;; WE HAVE TO GET OUT OF THIS I/O. ;;; SO ARRANGE TO GET OUT IN A USABLE WAY. CLR DLFLG ;;; SAY WE'RE STILL TAKING TYPE AHEAD INC R0 ;;; BUMP DLOUT BIC #177000,R0 ;;; MODULO 512 TO PASS X'FF' MOV R0,DLOUT ;;; SO NEXT TIME WILL WORK OK ;;; ;;; NOW WE HAVE TO GO OUT WITHOUT DOUBLE COUNTING THE CHAR IN R4 ;;; .IF DF,M$$MGE MOV (SP)+,@#KISAR6 ;;;RESTORE CURRENT MAPPING .ENDC MOVB (SP)+,@#PSW ;;; RESTORE OLD PRIO MOV (SP)+,R2 ;;; replace all the regs we've pushed MOV (SP)+,R1 MOV (SP)+,R0 JMP DLSUCC ;;; GO FINISH UP ;;; NORMAL PROCESSING HERE... NO X'FF' TERMINATOR SEEN 3130$: DEC U.RCNT(R5) ;;;SEE IF COUNT SATISFIED BLE 1137$ ;;;YES, IF EQUAL INC U.RBUF+2(R5) ;;;BUMP BYTE ADDRESS .IF DF,M$$MGE BIT #20000,U.RBUF+2(R5) ;;;OVERFLOWED 4K BOUNDARY? BEQ 1138$ ;;;NO, IF ZERO BIC #20000,U.RBUF+2(R5) ;;;CLEAR OVERFLOW BIT ADD #200,U.RBUF(R5) ;;;BUMP BIAS ADD #200,@#KISAR6 ;;; IN UCB AND APR 6 .ENDC 1138$: INC R0 BIC #177000,R0 ;;;BUMP OUT POINTER MOD 512 MOV R0,DLOUT ;;; COPY TO MEMORY FOR NEXT I/O BR 1134$ ;;; GO TRY ANOTHER COPY 1133$: ;;; NOW GRAB LAST BYTE 1137$: ;;; THE RESULT OF THIS IS TO GIVE US AN I/O TYPEAHEAD THAT FILLS IN ;;; TYPED AHEAD DATA FIRST WHEN THE FIRST INTERRUPT THAT WE DO GET ;;; COMES IN. .IF DF,M$$MGE MOV (SP)+,@#KISAR6 ;;;RESTORE CURRENT MAPPING .ENDC MOVB (SP)+,@#PSW ; RESTORE PRIO MOV (SP)+,R2 MOV (SP)+,R1 MOV (SP)+,R0 1136$: ; ********************************************************************** ; Now all KNOWN type ahead has been flushed to user's buffer and we're ; going to have to go wait for more data to come in. So flag the ; interrupt handler that there's now a user buffer to deal with and ; it'll terminate normally (we hope). If any characters came in while ; we were flushing out the old type-ahead just above, the typeahead ; flushing code in the interrupt handler section will get that out ahead ; of any new characters. The advantage of this scheme is that the code ; above is at low hard prio often enough that even while flushing thru ; a large buffer, no characters should be lost. The 512 byte buffer ; size is not sacred, but if it gets changed, the masks that mask ; DLOUT and DLINP after incrementing need to be changed. Also if the ; buffer exceeds 2K words, it probably won't fit in the driver's ; APR area and thus the code here will need to be augmented to handle ; saves/restores with it. (Driver code and buffer BOTH have to fit ; into one APR). This practice also assumes that the driver is mapped ; by both I and D space registers on machines with I and D space ; enabled in kernel mode. I believe this is true for RSX11M, RSX11S, ; and RSX11M+ though. MOV #1,DLFLG ;FLAG THAT WE ARE USING USER BUFFER ; AND THAT THERE MAY BE TYPE-AHEAD THERE BIS #RCVENB,(R2) ; ENABLE RECEIVER ; N.B. RECEIVE INTERRUPT WILL MOST OF THE TIME ALREADY BE ENABLED. ; BR 140$ ;RETURN - INTERRUPT CODE PROPAGATES RECEIVE ; ; CONTROL FUNCTION INITIATION (INITIATE OR TERMINATE CONTROLLER, ; OR CHANGE OPERATING MODE) ; 40$: BNE 70$ ;IF NOT EQUAL, MODE CHANGE REQUEST CLR (R2) ;CLEAR RXCSR ; ; DO START FUNCTION INITIATION ; MOV R5,UNITBL(R3) ;INITIALIZE UCB ADDRESS IN UNIT TABLE BR 120$ ;RETURN SUCCESSFUL ; ; SERVICE DEVICE MODE CHANGE REQUEST (NO OP HERE) ; 70$: ; DLSUCC: ;REFERENCE LABEL 120$: MOV #IS.SUC&377,R0 ;RETURN SUCCESSFUL STATUS DLFIN: ;REFERENCE LABEL 130$: CLRB S.STS(R4) ;CLEAR CONTROLLER STATUS BICB #US.BSY,U.STS(R5) ;CLEAR UNIT STATUS MOV R0,-(SP) ;SAVE STATUS WORD 1 MOV S.PKT(R4),R1 ;GET I/O PKT ADDRESS MOV R1,-(SP) ;SAVE PKT ADDRESS TO STASH IN R3 FOR $IOFIN ; HERE TRY AND COMPUTE THE REAL COUNT READ... ; DO SO BY SUBTRACTING U.RCNT FIELD FROM R1 TO GET ; NUMBER OF BYTES ACTUALLY SENT... MOV I.PRM+4(R1),R1 ;AND REQUESTED BYTE COUNT SUB U.RCNT(R5),R1 ;SUBTRACT BYTES NOT TRANSFERRED (IF ANY) MOV R1,-(SP) ;SAVE ON STACK FOR A CALL TO DLINIT... CALL DLINIT ;ATTEMPT TO INITIATE SOMETHING ; WITH A HARD CODED TERMINATOR POSSIBLE, GOTTA RETURN WHAT WE ; ACTUALLY READ... MOV (SP)+,R1 ;GET BYTES TRANSFERRED AS STATUS WORD MOV (SP)+,R3 ;GET I/O PKT ADDRESS TO FINISH ON... MOV (SP)+,R0 ;RESTORE STATUS WORD 1 CALLR $IOFIN ;TERMINATE LAST PACKET 140$: RETURN ; ; POWER FAIL SERVICE ; DLPWRF: ;REFERENCE LABEL CLRB S.CTM(R4) ;DISABLE TIMEOUTS BIC #RCVENB,@S.CSR(R4) ;;;DISABLE RECEIVER mov s.csr(r4),-(sp) ;;; get data buffer address add #2,(sp) ;;;on stack tst @(SP)+ ;;; read it with TST instruction CLR DLFLG CLR DLBFI ;;; RESET POINTERS CLR DLBFO MOV #IE.DNR$377,R0 ;NOT RDY BR 130$ ;FINISH I/O ; ; I/O CANCELLATION ; DLCANC: ;;;REFERENCE LABEL CMP R1,I.TCB(R0) ;;;CANCEL FOR THIS TASK? BNE 140$ ;;;JUST RETURN IF NOT CMPB #IO.WLB/256.,I.FCN+1(R0) ;;;WAS FUNCTION TRANSMIT? BEQ 140$ ;;;JUST LET FINISH IF YES TST U.RCNT(R5) ;;;RECEIVE FINISHED? BLE 140$ ;;;BR IF YES ;;; LEAVE INTERRUPT DISABLE HERE BIC #RCVENB,@S.CSR(R4) ;;;DISABLE RECEIVER mov s.csr(r4),-(sp) ;;; get data buffer address add #2,(sp) ;;;on stack tst @(SP)+ ;;; read it with TST instruction CLR DLFLG CLR DLBFI ;;; RESET POINTERS CLR DLBFO MOV #IE.ABO&377,R0 ;;;PUT ERROR CODE IN R0 CLR U.RCNT(R5) ;;;MARK COUNT SATISFIED BR 150$ ;;;FINISH OFF I/O ; ; TIMEOUT SERVICE ROUTINE ; ; INPUTS: ; ; R0 = DEVICE TIMEOUT STATUS 'IE.DNR' ; R3 = CONTROLLER INDEX ; R4 = ADDRESS OF SCB ; R5 = ADDRESS OF UCB ; DLTMO: ;;;TIMEOUT ENTRY POINT ; MOV S.CSR(R4),R3 ;;;GET RECEIVER CSR ;;; FORCE RECEIVER NOT TO HAVE ANY MORE INTERRUPTS... BIC #RCVENB,@S.CSR(R4) ;;;DISABLE RECEIVER mov s.csr(r4),-(sp) ;;; get data buffer address add #2,(sp) ;;;on stack tst @(SP)+ ;;; read it with TST instruction CLR DLFLG CLR DLBFI ;;; RESET POINTERS CLR DLBFO 150$: ;;;REFERENCE LABEL MTPS #0 ;;;ALLOW INTERRUPTS MOV U.SCB(R5),R4 JMP DLFIN ;;;WAIT FOR SOFTWARE TIMEOUT TO FINISH .DSABL LSB ; ; **- $YRINT - DL-11 INPUT INTERRUPT SERVICE ; $YRINT:: ;;;REFERENCE LABEL INTSV$ YR,PR5,D$$E11 ;;;GENERATE INTERRUPT SAVE CODE ;;; NOTE: COMMENT OUT DLSET CALL ;;; ALSO SKIP CHECK OF R5 =0 SINCE IT NEEDS TO BE UCB ;;; ADDRESS ON ENTRY TO INTERRUPT FROM ITB. ; CALL DLSET ;;;SET CSR IN R4, UCB IN R5 MOV U.SCB(R5),R4 ;;;FIRST GET SCB ADDRESS MOVB S.ITM(R4),S.CTM(R4) ;;;RESET TIMEOUT. MOV S.CSR(R4),R4 ;;;NOW DEVICE CSR ADDRESS MOV (R4),-(SP) ;;;SAVE CSR, TEST DATA SET CHANGE 10$: MOV 2(R4),R4 ;;;CAPTURE BUFFER REGISTER IN R4 ;;; THE ABOVE GRABS THIS CHAR QUICKLY. NOW SEE IF WE HAVE TO ;;; HANDLE TYPE-AHEAD (DLFLG=1). 20$: TSTB (SP)+ ;;;RECEIVER DONE? BPL 4501$ ;;;IF NOT, DISMISS INTERRUPT CMP DLFLG,#1 ;;; SEE IF MODE IS TYPEAHEAD, USER START OR ;;; USER CONTINUE BGE 31$ ;;; IF GE THERE'S A USER BUFFER THERE ;;; DLFLG=0. TYPE-AHEAD MODE. ;;; STORE CHAR IN DLBUF MOV R0,-(SP) ;;; NEED A REG MOV DLINP,R0 ;;; GET COUNTER MOV PC,-(SP) ;;; GET ABS ADDRESS OF DLBUF ADD #,(SP) ;;; ON STACK ADD R0,(SP) MOVB R4,@(SP)+ ;;; STORE BYTE INC R0 BIC #177000,R0 ;;; BUMP INPUT MODULO 512 BYTES MOV R0,DLINP ;;; THEN SAVE AS NEXT INPUT LOCATION MOV (SP)+,R0 ;;; PUT REG BACK 4501$: return ;;; 70$ just exits... CARE ... ;;; if we ever need to modify at 70$ ;;; put back the next line instead: ;4501$: JMP 70$ ;;; THEN EXIT THIS INTERRUPT 31$: BGT 36$ ;;; IF DLFLG=2 NORMAL USER PROCESSING OCCURS MOV #2,DLFLG ;;; DLFLG WAS 1. SET IT TO 2 TO FLAG WE GOT ;;; HERE ALREADY. ; ;;; HERE WE JUST STARTED A USER I/O AND HAVE TYPE-AHEAD POTENTIALLY ;;; PRESENT. WE WANT TO FLUSH THE TYPE-AHEAD TO THE USER'S BUFFER ;;; AND SO FLAG IT, AND THEN PLACE THE CURRENT BYTE INTO THERE TOO ;;; AFTER IT. THIS SHOULD GIVE US THE DESIRED TYPE-AHEAD EFFECT, ;;; THOUGH IT REQUIRES THAT AT LEAST ONE CHARACTER ARRIVE AFTER ;;; THE I/O START, SO WE CAN'T MISS AN ENTIRE I/O, JUST THE FIRST ;;; FEW CHARACTERS OF IT. CMP DLINP,DLOUT ;;; DO WE NEED TO GO THRU CRUFT? ;;; Hope that we have nothing to flush out here... BEQ 36$ ;;; IF EQ NO, SKIP IT. MOV R0,-(SP) MOV R1,-(SP) ;;;NEED SOME REGS MOV R2,-(SP) .IF DF,M$$MGE MOV @#KISAR6,-(SP) ;;;SAVE CURRENT MAPPING MOV U.RBUF(R5),@#KISAR6 ;;;MAP TO USER BUFFER .ENDC MOV PC,R1 ;;;POINT WITH R1 AT... ADD #,R1 ;;; DLBUF (PIC) 32$: MOV DLOUT,R0 ;;; GET OUTPUT POINTER 34$: CMP R0,DLINP ;;; SEE IF WE HAVE ALL CHARS BEQ 33$ ;;;IF EQ YES MOV R1,R2 ;;; USE R2 AS ACCUMULATOR ADD R0,R2 ;;; POINT AT DESIRED BYTE MOVB @R2,@U.RBUF+2(R5) ;;; STORE BYTE FOR USER ;;; ;;; CHECK FOR TERMINATOR CHAR IN TYPE AHEAD BUFFER... CMPB YRTRM,@R2 ;;;IF EQUAL, THIS IS A TERMINATOR CHAR SO ;;;DON'T COUNT DOWN THE I/O AND GET OUTTA HERE BNE 2130$ ;;; ??? ;;; WHOOPS... ;;; R4 NOW HAS THE CHAR WE JUST GOT, WE NEED TO GO TO MODE ;;; 0 (TYPEAHEAD),AND WE HAVE TO GET OUT OF THIS I/O. ;;; SO CAPTURE R4 AND ARRANGE TO GET OUT IN A USABLE WAY. INC R0 ;;; BUMP DLOUT BIC #177000,R0 ;;; MODULO 512 TO PASS X'FF' MOV R0,DLOUT ;;; SO NEXT TIME WILL WORK OK MOV R0,-(SP) ;;; NEED A REG mov r1,-(sp) ;;; save pointer too ;;; Here have to store R4 (char from this int) in typeahead buffer ;;; for a later read since we're done this read now. MOV DLINP,R0 ;;; GET COUNTER ; R1 already points at base of DLBUF so use it here instead of ; recomputing dlbuf address. ;;;; MOV PC,-(SP) ;;; GET ABS ADDRESS OF DLBUF ;;;; ADD #,(SP) ;;; ON STACK ;;;; ADD R0,(SP) ;;;; MOVB R4,@(SP)+ ;;; STORE BYTE add r0,r1 ;;; point r1 at dlbuf address movb r4,@r1 ;;; store byte now INC R0 BIC #177000,R0 ;;; BUMP INPUT MODULO 512 BYTES MOV R0,DLINP ;;; THEN SAVE AS NEXT INPUT LOCATION mov (sp)+,r1 ;;; Make R1 point at DLBUF start again MOV (SP)+,R0 ;;; PUT REG BACK so R0 is DLOUT again ; CLR DLFLG ;;; SAY WE'RE STILL TAKING TYPE AHEAD ;;; ;;; NOW WE HAVE TO GO OUT WITHOUT DOUBLE COUNTING THE CHAR IN R4 ;;; .IF DF,M$$MGE MOV (SP)+,@#KISAR6 ;;;RESTORE CURRENT MAPPING .ENDC MOV (SP)+,R2 MOV (SP)+,R1 MOV (SP)+,R0 JMP 80$ ;;; GO FINISH UP the I/O ;;; NORMAL PROCESSING HERE... NO X'FF' TERMINATOR SEEN 2130$: DEC U.RCNT(R5) ;;;SEE IF COUNT SATISFIED BLE 637$ ;;;YES, IF EQUAL (if lt 0, recover from err) INC U.RBUF+2(R5) ;;;BUMP BYTE ADDRESS .IF DF,M$$MGE ; Physical buffer address alignment can be on any 64 byte boundary ; for base and any offset. Thus we must check 4K word segment ; overflow at each character. This could be avoided if we KNEW ; that buffers were always less than 4K long or never crossed a ; 4KW segment in physical memory. This would require them to live ; in a separate partition aligned that way by hand, though. Buffers ; in a task's area cannot be so easily aligned. BIT #20000,U.RBUF+2(R5) ;;;OVERFLOWED 4K BOUNDARY? BEQ 38$ ;;;NO, IF ZERO BIC #20000,U.RBUF+2(R5) ;;;CLEAR OVERFLOW BIT ADD #200,U.RBUF(R5) ;;;BUMP BIAS ADD #200,@#KISAR6 ;;; IN UCB AND APR 6 .ENDC 38$: INC R0 BIC #177000,R0 ;;;BUMP OUT POINTER MOD 512 MOV R0,DLOUT ;;; COPY TO MEMORY FOR NEXT I/O BR 34$ ;;; GO TRY ANOTHER COPY 637$: ; Get here if we counted down to zero while reading typeahead ; buffer. Must finish the I/O but leave typeahead going. ; Inserted here since it adds no instructions to any other ; code path in this location. .IF DF,M$$MGE MOV (SP)+,@#KISAR6 ;;;RESTORE CURRENT MAPPING .ENDC MOV (SP)+,R2 MOV (SP)+,R1 MOV (SP)+,R0 JMP 80$ ;;; EXIT WITH I/O COUNT BACK TO ZERO. 33$: ;;; NOW GRAB LAST BYTE 37$: ;;; THE RESULT OF THIS IS TO GIVE US AN I/O TYPEAHEAD THAT FILLS IN ;;; TYPED AHEAD DATA FIRST WHEN THE FIRST INTERRUPT THAT WE DO GET ;;; COMES IN. .IF DF,M$$MGE MOV (SP)+,@#KISAR6 ;;;RESTORE CURRENT MAPPING .ENDC MOV (SP)+,R2 MOV (SP)+,R1 MOV (SP)+,R0 36$: TST U.RCNT(R5) ;;;COUNT ALREADY SATISFIED? BLE 70$ ;;;IF SO, DISMISS INTERRUPT 60$: ;;;REFERENCE LABEL .IF DF M$$MGE MOV @#KISAR6,-(SP) ;;;SAVE CURRENT MAPPING MOV U.RBUF(R5),@#KISAR6 ;;;MAP TO USER BUFFER MOVB R4,@U.RBUF+2(R5) ;;;STORE BYTE IN USER BUFFER MOV (SP)+,@#KISAR6 ;;;RESTORE CURRENT MAPPING .IFF MOVB R4,@U.RBUF+2(R5) ;;;STORE BYTE IN USER BUFFER .IFTF DEC U.RCNT(R5) ;;;SEE IF COUNT SATISFIED BLE 80$ ;;;YES, IF EQUAL INC U.RBUF+2(R5) ;;;BUMP BYTE ADDRESS .IFT BIT #20000,U.RBUF+2(R5) ;;;OVERFLOWED 4K BOUNDARY? BEQ 73$ ;;;NO, IF ZERO BIC #20000,U.RBUF+2(R5) ;;;CLEAR OVERFLOW BIT ADD #200,U.RBUF(R5) ;;;BUMP BIAS .ENDC 73$: ;;; CHECK FOR TERMINATOR CHARACTER AND END RIGHT NOW IF WE GOT IT. ;;; HARDCODED TERMINATOR IS 377 OCTAL CMPB R4,(PC)+ ;;; COMPARE TO BYTE IN NEXT WORD YRTRM==. .WORD 177777 ;;; BYTE OF FF HEX (DUPLICATED FOR ;;; THE HECK OF IT...) IN GLOBAL. ; BEQ 80$ ;;; IF EQ THEN FINISH RIGHT NOW... BNE 70$ ;;; IF EQ THEN WE HAVE A FINISH ON TERMINATOR INC U.RCNT(R5) ;;;SO BACK UP COUNT TO EXCLUDE THE X'FF' FROM ;;; THE COUNT. BR 80$ ;;; THEN TERMINATE 70$: ;;;REFERENCE LABEL RETURN ;;;EXIT INTERRUPT ; ; REQUEST SATISFIED ; 80$: ;;;REFERENCE LABEL MOV U.SCB(R5),R4 ;;;GET SCB ADDRESS ; BIC #RCVENB,@S.CSR(R4) ;;;DISABLE RECEIVER CLR DLFLG ;;; SAY WE USE TYPE AHEAD BUFFER ; IN THIS VERSION OF THE DRIVER WE FILL IN REQUEST FROM TYPE ; AHEAD AT ONCE IF POSSIBLE. CALL $FORK ;;;GO TO FORK LEVEL JMP DLSUCC ;FINISH I/O SUCESSFUL ; ; ; DLSET - SET UP REGISTER R4 WITH CSR ADDRESS, R5 WITH ; UCB ADDRESS. UNIT NUMBER IN LOW ORDER 4 BITS OF UNIT. ; ;DLSET: ;;;REFERENCE LABEL ; TST R5 ;;;CHECK FOR UCB ADDRESS ; BEQ 10$ ;;;IF NO UCB, TROUBLE ; MOV U.SCB(R5),R4 ;;;FIRST GET SCB ADDRESS ; MOVB S.ITM(R4),S.CTM(R4) ;;;RESET TIMEOUT. ; MOV S.CSR(R4),R4 ;;;NOW DEVICE CSR ADDRESS ; RETURN ;;;AND NOW RETURN ;10$: TST (SP)+ ;;;CLEAR STACK OF RETURN ADDRESS ;DLSXT: JMP $INTXT ;;;DISMISS INTERRUPT DLFLG: .WORD 0 ;;; FLAG THAT I/O IS STARTED IF NONZERO DLINP: DLBFI: .WORD 0 ;;; ADDRESS FILLED IN BUFFER DLOUT: DLBFO: .WORD 0 ;;; ADDRESS EMPTIED IN BUFFER DLBUF: DLBFR: .BLKB 512. ;;; RING BUFFER FOR BIIIIG TYPE AHEAD ;;; NOTE DLBFR IS A POWER OF 2 LONG SO WE CAN DO CIRCULAR ;;; OPS ON IT. .WORD 0,0 ;SAFETY .END