.TITLE VTDRV .IDENT /01.00/ ; ; VIRTUAL TERMINAL DRIVER FOR RSX-11M VERSION 3.1 ; ; D. ELDERKIN 20-MAY-77 ; ; MODIFIED BY: ; ; ; THIS IS A VIRTUAL TERMINAL DRIVER FOR RSX11M VERSION 3.1 SYSTEMS. ; IT PROVIDES THE CAPABILITY FOR NON-PRIVLEDGED USER TASKS TO SUPPLY INPUT TO ; AND RECEIVE OUTPUT FROM THE SYSTEM. THIS INPUT AND OUTPUT IS TREATED AS IF ; IT HAD COME FROM A TERMINAL. THE DRIVER CONTROLS TWO UNITS. UNIT 0 IS USED ; TO COMMUNICATE WITH THE SYSTEM, WHILE UNIT 1 IS USED TO COMMUNICATE WITH THE ; USER TASK. UNDER NORMAL CIRCUMSTANCES THE DRIVER SIMPLY TAKES THE BUFFER ; FOR THE UNIT WHICH HAS DONE A WRITE AND TRANSFERS IT TO THE UNIT WHICH HAS ; DONE A READ. SPECIAL PROCESSING IS DONE AS FOLLOWS: ; ; 1) WHEN UNIT 1 IS WRITING TO UNIT 0, THE FIRST CHARACTER OF THE BUFFER ; IS CHECKED TO SEE IF IT IS A Z. IF IT IS, THE READ FROM ; UNIT 0 IS TERMINATED WITH EOF STATUS. THE BUFFER IS ALSO CHECKED ; FOR THE PRESENCE OF A SINGLE CHARRIAGE RETURN AS THE ONLY ; CHARACTER OF THE BUFFER. IF IT IS, THE READ FROM UNIT 0 IS ; TERMINATED WITH 0 BYTES TRANSFERED. ; ; 2) WHEN UNIT 0 IS WRITING TO UNIT 1, THE CARRIAGE CONTROL BYTE FROM ; UNIT 0'S WRITE IS INSERTED INTO UNIT 1'S BUFFER AS THE FIRST ; CHARACTER OF THER READ. THIS ALLOWS THE USER TASK TO PROPERLY ; FORMAT THE BUFFER IF IT MUST BE OUTPUT. ; ; 3) WHEN UNIT 0 ISSUES A WRITE AND UNIT 1 DOES NOT DO A READ SOON ENOUGH ; TO PREVENT THE WRITE ON UNIT 0 FROM BEING CANCELLED, A FLAG IS ; SET. WHEN THE UNIT 1 DOES ITS NEXT READ, IT RECIEVES EOF STATUS. ; ; EQUATED SYMBOLS ; CR=15 ; CARRIAGE RETURN CTRLZ=32 ; CONTROL Z ; ; LOCAL DATA ; OURUCB: .BLKW 1 ; FLAG TO NOTE THAT WRITE HAS BEEN KILLED WRIKIL: .BYTE 0 ; FOR UNIT 0. PREVENTS RACE CONDITION. .EVEN UCBTBL: ; TABLE OF UCB ADDRESSES FOR UNITS 0 AND 1 UCB0: .WORD 0 UCB1: .WORD 0 SCBTBL: ; TABLE OF SCB ADDRESSES SCB0: .WORD 0 ; SCB1: .WORD 0 ; PKTTBL: ;TABLE OF I/O PACKET ADDRESSES PKT0: .WORD 0 ; PKT1: .WORD 0 ; LD$VT=1 ; TO LET BE LOADABLE ; ; DRIVER DISPATCH TABLE ; $VTTBL::.WORD VTINI ; INITIATOR ENTRY POINT .WORD VTCAN ; CANCEL ENTRY POINT .WORD VTOUT ; TIMEOUT ENTRY POINT .WORD VTPWF ; POWER FAIL ENTRY POINT ;+ ; **-VTINI-VIRTUAL TERMINAL ENTRY POINT ; ; THIS ROUTINE IS ENTERED FROM THE QUEUE I/O DRECTIVE WHEN AN I/O REQUEST ; IS QUEUED AND AT THE END OF A PREVIOUS I/O OPERATION TO PROPAGATE THE EXECU- ; TION OF THE DRIVER. IF THE SPECIFIED CONTROLLER IS NOT BUSY, THEN AN ATTEMPT ; IS MADE TO DEQUEUE THE NEXT I/O REQUEST. ELSE A RETURN TO THE CALLER IS ; EXECUTED. IF THE DEQUEUE ATTEMPT IS SUCCESSFUL, THEN THE PROCESSING CAN ; CONTINUE. A RETURN TO THE CALLER IS THEN EXECUTED. ; ; INPUTS: ; ; R5=ADDRESS OF THE UCB OF THE CONTROLLER TO BE INITIATED. ;- .ENABLE LSB VTINI1: MOV OURUCB,R5 ; RESTORE UCB ADDRESS FOR THIS UNIT VTINI: ; ACTUAL ENTRY POINT CALL $GTPKT ; TRY TO FIND AN I/O PACKET FOR THIS UNIT BCS VTDONE ; IF CS THEN NONE EXISTS, JUST EXIT. ; ; THE FOLLOWING ARGUMENTS ARE RETURNED BY $GTPKT: ; ; R1=ADDRESS FO THE I/O REQUEST PACKES. ; R2=PHYSICAL UNIT NUMBER OF THE REQUEST UCB. ; R3=CONTROLLER INDEX. ; R4=ADDRESS OF THE STATUS CONTROL BLOCK. ; R5=ADDRESS OF THE UCB OF THE CONTROLLER TO BE INITIATED. ; ; VIRTUAL TERMINAL I/O REQUEST PACKES FORMAT: ; ; WD. 00 -- I/O QUE THREAD WORD. ; WD. 01 -- REQUEST PRIORITY, EVENT FLAG NUMBER. ; WD. 02 -- ADDRESS OF THE TCB OF THE REQUESTER TASK. ; WD. 03 -- POINTER TO THE SECOND LUN WORD IN THE REQUESTOR HASK HEADER. ; WD. 04 -- CONTENTS OF THE FIRST LUN WORD IN THE REQUESTER TASK HEADER (UCB). ; WD. 05 -- I/O FUNCITON CODE. ; WD. 06 -- VIRTUAL ADDRESS OF I/O STATUS BLOCK. ; WD. 07 -- RELOCATION BIAS OF I/O STATUS BLOCK. ; WD. 08 -- I/O STATUS BLOCK ADDRESS (REAL OR DISPLACEMENT + 140000). ; WD. 10 -- VIRTUAL ADDRESS OF AST SERVICE ROUTINE. ; WD. 12 -- RELOCATION BIAS OF I/O BUFFER. ; WD. 13 -- BUFFER ADDRESS OF I/O TRANSFER. ; WD. 14 -- NUMBER OF BYTES TO BE TRANSFERED. ; WD. 15 -- NOT USED. ; WD. 16 -- NOT USED. ; WD. 17 -- NOT USED. ; WD. 20 -- NOT USED. ; MOV R5,OURUCB ; SAVE THE ADDRESS OF THIS UCB MOV R1,PKTTBL(R3) ; SAVE THE ADDRESS OF THIS I.O PACKET TST R3 ; UNIT 0 OR UNT 1? BNE UNIT1 ; IF NE, UNIT 1 ; ; UNIT 0 PROCESSING. ; THIS UNIT IS USED FOR COMMUNICATION WITH THE ; OPERATING SYSTEM AND ITS TASKS. ; CLRB WRIKIL ; RESET THE 'WRITE KILLED' FLAG MOV SCB1,R0 ; FETCH THE ADDRESS OF THE OTHER UNIT'S UCB BITB #1,S.STS(R0) ; IS THE OTHER UNIT BUSY? BEQ VTDONE ; IF EQ NO, EXIT AND WAIT FOR SOMETHING TO DO MOV PKT1,R0 ; GET THE ADDRESS OF THE OTHER GUY'S PACKET CMPB #IO.WLB/256.,I.FCN+1(R1) ; IS THIS A READ OR A WRITE? BEQ WRITE0 ; IF EQ ITS A WRITE ; ; READ RECEIVED FOR UNIT 0 ; READ0: CMPB #IO.WLB/256.,I.FCN+1(R0) ; IS IT DOING A READ OR A WRITE? BEQ 40$ ; IF EQ ITS A WRITE, THATS EASY TO DO ; ; UNIT 1 STILL HAS A READ OUTSTANDING. INDICATE ; THAT IT HAS AN EOF, THEN WAIT FOR UNIT 1 TO ISSUE A WRITE. ; BR 10$ ; FIN 6 DOES THAT RATHER NICELY ; ; WRITE RECEIVED FOR UNIT 0 ; WRITE0: CMPB #IO.RLB/256.,I.FCN+1(R0) ; DOES THE OTHER GUY HAVE A READ? BEQ 30$ ; IF EQ ITS A READ, THAT CAN BE DONE 10$: JMP FIN6 ; BOTH PEOPLE TRYING TO WRITE, TELL UNIT 1 ; TO COOL IT FOR A WILE WITH AN EIF ; OPERATION IF FROM UNIT 1 UNIT1: ; MOV SCB0,R0 ; GET THE OTHER GUY'S SCB ADDRESS CMPB #IO.WLB/256.,I.FCN+1(R1) ; IS THIS A READ OR WRITE? BEQ WRITE1 ; IF EQ, ITS A WRITE ; ; READ OPERATION FOR UNIT 1 READ1: BITB #1,S.STS(R0) ; IS THE OTHER UNIT BUSY BNE 20$ ; IF NE YES, ALL IS WELL PROCEDE TSTB WRIKIL ; SEE IF A WRITE WAS JUST KILLED FOR UNIT 0 BEQ VTDONE ; IF EQ NO, NORMAL CONDITION. WAIT FOR ACTION CLRB WRIKIL ; RESET THE FLAG BR FIN6 ; FINISH THIS I/O WITH AN EOF, FORCING A ; WRITE ON UNIT 1 AND AVOIDING A RACE COND. 20$: MOV PKT0,R0 ; GET THE I.O PACKED ADDRESS FOR THE OTHER UNIT CMPB #IO.WLB/256.,I.FCN+1(R0) ; IS I A WRITE BNE FIN6 ; IF NE NO, TERMINATE THIS READ WITH AN EOF 30$: ; REF LABLE CALL M0TO1 ; PERFORM THE TRANSFER BR VTINI1 ; AND TRY AGAIN FOR THIS UNIT ; ; WRITE OPERATION FOR UNIT 1 ; WRITE1: CLRB WRIKIL ; PREVENT RACE CONDITION BITB #1,S.STS(R0) ; IS THE OTHER UNIT BUSY? BEQ 50$ ; IF EQ NO, THIS IS UNSOLICITED INPUT FOR UNIT 0 MOV PKT0,R0 ; GET THE ADDRESS FO THE OTHER UNIT'S PACKED CMPB #IO.WLB/256.,I.FCN+1(R0) ; IS IT READ OR WRITE? BEQ FIN6 ; IF EQ, TELL UNIT 0 TO SWITCH READS ; BY SENDING IT AN EOF ; ; READ ON UNIT 0, WRITE ON UNIT 1 ; 40$: ; REF LABLE CALL M1TO0 ; PERFORM THE TRANSFER BR VTINI1 ; TRY FOR THE NEXT TRANSFER OF THIS UNIT ; ; UNIT 1 HAS UNSOLICITED INPUT FOR UNIT 0 ; 50$: MOV #M$$CRB,R1 ; SET THE DEFAULT BUFFER LENGTH CALL $ALOCB ; ALLOCATE A BLOCK FOR THIS TRANSFER BCC 60$ ; IF CC ALL IS WELL MOV #IE.NOD&377,R0 ; SET OUR ERROR CODE TO 'NO POOL SPACE' MOV #2,R2 ; SET UNIT 1 BR FIN0 ; AND FINISH THE I/O OPERATION. 60$: MOV R0,-(SP) ; SAVE POINTER TO THE BUFFER TST (R0)+ ; SKIP PAST LINKAGE POINTER'S SPOT ??? MOV UCB0,(R0)+ ; INSERT UNIT 0'S UCB ADDRESS 70$: CALL $GTBYT ; GET THE NEXT BYTE FROM THE USER'S BUFFER MOVB (SP)+,(R0)+ ; AND BUT IT INTO THE DSR BUFFER DEC U.CNT(R5) ; DECREMENT THE CHARACTER COUNT BGE 70$ ; IF GE CONTINUE MOV (SP)+,R1 ; SET THE ADDRESS OF THE BUFFER TO THE MCR CALL $QMCRL ; QUEUE THE MCRCOMMAND LINE CALL FIN2 ; COMPLETE THE OPERATION IN COMMON CODE BR VTINI1 ; AND LOOK FOR MORE WORK ;+ ; **-VTPWF - POWERFAIL ENTRY POINT ; ; INPUTS: ; ; R5=UCB ADDRESS ; R4=SCB ADDRESS ; R3=CONTROLER INDEX ; ; OUTPUTS: ; ; THE RELEVENT UCB AND SCB DATA IS SAVED ;- VTPWF: ; BPT ; FOR CRASHING THE SYSTEM MOV R5,UCBTBL(R3) ; SAVE THIS UNIT'S UCB ADDRESS MOV R4,SCBTBL(R3) ; AND ITS SCB ADDRESS ;+ ; **- VTOUT -- DEVICE TIMOUT ENTRY POINT ; ; INPUTS:; ; R5=UCB ADDRESS ; R4=SCBADDRESS ; R3=CONTROLER INDEX ; R2=ADDRESS OF DEVICE CSR ; R0=I/O STATUS CODE OF IE.DNR ; ; OUTPUTS: ; ; NONE ;- VTOUT: VTDONE: RETURN ;+ ; VTCAN - CANCEL I/O OPERATION ENTRY POINT (FORCE I/O COMPLETE) ; ; AT ENTRY: ; R0-> ADDR OF ACTIVE I/O PACKET ; R1 -> ADDR OF TCB OF CURRENT TASK ; R3 -> CONTROLLER INDEX ; R4 -> ADDR OF SCB ; R5 -> ADDR OF UCB ; INTERRUPT PRIORITY=THAT OF DEVICE ;- VTCAN: TST R3 ; ARE WE KILLING FOR UNIT 0? BNE 80$ ; IF NE NO, JUST KILL IT CMPB #IO.WLB/256.,I.FCN+1(R0) ; ARE WE KILLING A WRITE? BNE 80$ ; IF NE NO, NO PROBLEMS INCB WRIKIL ; NOTE THAT WE JUST KILLED A WRITE ; 80$: ; MOV #IE.ABO&377,R0 ; NOTE THAT THIS I/O HAS BEEN CANCELED CALLR $IOALT ; FINISH THE I/O OPERATION WITH 0 BYTES ;+ ; **-M1TO0- MOVE CHARACTERS FROM UNIT 1 TO UNIT 0 ; ; INPUTS: ; ; NONE ; ; OUTPUTS: ; ; THE INPUT STREAM IS CHECKED FOR THE PRESENCE OF A CONTROL Z. ; IF IT IS PRESENT, THE STREAM TO UNIT 0 IS TERMINATED. ELSE, THE CONTENSS OF ; THE BUFFER IS PASSED FROM UNIT 1 TO UNIT 0. ;- M1TO0: MOV UCB1,R5 ; GET THE UCB ADDRESS OF UNIT 1 CALL $GTBYT ; GET THE FIRST BYTE FROM THE INPUT BUFFER CMPB #CR,(SP) ; IS THE FIRST CHARACTER A CR? BEQ 90$ ; IF EQ YES, FINISH THE OPERATIONS IMMEDIATELY DEC U.CNT(R5) ; NOTE THAT WE'VE STOLLEN A BYTE CMPB #CTRLZ,(SP) ; IS IT A CONTROL Z? BEQ 100$ ; IF EQ YES, PROCESS IT MOV R5,R3 ; PLACE INPUT UCB ADDRESS IN R3 MOV UCB0,R4 ; AND OUTPUT UCB ADDRESS IN R4 BR MOVE1 ; AND CONTINUE IN COMMON CODE 90$: TST (SP)+ ; CLEAN THE STACK BR FIN3 ; AND CONTINUE 100$: TST (SP)+ ; CLEAN THE STACK CALL FIN5 ; TELL UNIT 0 THAT IT JUST GOT AN EOF BR FIN2 ; AND FINISH UP UNIT 1 NORMALLY ;+ ; **-M0TO1 MOVE DATA ROM UNIT 0 TO UNIT 1 ; ; INPUTS: ; ; NONE ; ; OUTPUTS: ; ; THE INPUT STREAM IS MARKED AS NOT CONTAINING AN END OF FILE. ; THEN, THE CONTENTS OF THE BUFFER IS PASSED FROM UNIT 0 TO UNIT 1. ;- M0TO1: MOV PKT0,R1 ; GET THE ADDRESS OF THE INPUT PACKET MOV I.PRM+6(R1),-(SP) ; GET THE CARRIAGE CONTROL BYTE ; LEAVING IT ON THE STACK FOR $PTWRD MOV UCB0,R3 ; GET THE INPUT UCB MOV UCB1,R4 ; AND THE OUTPUT UCB BR MOVE1 ; AND FALL INTO COMMON CODE ;+ ; **-MOVE MOVE CHARACTERS FROM AN INPUT BUFFER TO AN OUTPUT BUFFER ; ; INPUT: ; ; R3=UCB OF INPUT DEVICE ; R4=UCB OF OUTPUT DEVICE ; ; OUTPUT: ; ; THE BUFFER ASSOCIATED WITH THE INPUT UCB IS COPIED TO THE BUFFER ; ASSOCIATED WITH THE OUTPUT UCB. ; ; R5 IS DESTROYED BY THIS ROUTINE. ;- MOVE: MOV R3,R5 ; SET R5 TO THE INPUT UCB ADDRESS CALL $GTBYT ; AND GET A BYTE FROM THE INPUT BUFFER DEC U.CNT(R5) ; NOTE THAT WE TOOK A BYTE MOVE1: ; REF LABEL MOV R4,R5 ; SET R5 TO THE OUTPUT UCB ADDRESS CALL $PTBYT ; AND PUT THE BYTE INTO THE OUTPUT BUFFER DEC U.CNT(R4) ; AND THE OUTPUT COUNT BEQ FIN3 ; IF EQ END OF BUFFER REACHED ; FINISH UP BOTH I/O OPERATIONS SUCCESSFULLY TST U.CNT(R3) ; IS THE OTHER BUFFER DONE? BGT MOVE ; IF GT MORE WORK TO DO, CONTINUE BR FIN3 ; END OF BUFFER REACHED. ;+ ; **-FINO,1,2,3,4,5,6 FINISH THE I/O OPERATION ON UNIT 0 AND/OR 1 ; ; THE ENTRY POINTS ARE: ; ; FIN6 - FINISH UNIT 1 WITH EOF STATUS ; FIN5 - FINISH UNIT 0 WITH EOF STATUS ; FIN3 - FINISH UNIT 0 WITH IS.CR STATUS AND UNIT 1 WITH IS.SUC STATUS ; FIN2 - FINISH UNIT 1 WITH IS.SIC STATUS ; FIN0 - FINISH, UNIT NUMBER IN R2, STATUS IN R0 ; ; INPUTS: ; ; NONE, EXCEPT FOR FIN0: ; ; R0=STATUS TO BE POSTED ; R2=CONTROLLER INDEX ; ;OUTPUTS: ; ; I/O OPERATIONS FOR ONE OR BOTH UNITS ARE COMPLETED WITH STATUS ;- FIN6: MOV #2,R2 ; SET US TO UNIT 1 BR FIN4 ; CONTINUE IN COMMON CODE FIN5: CLR R2 ; SET US TO UNIT 0 FIN4: MOV #IE.EOF&377,R0 ; SET END OF FILE STATUS BR FIN0 ; AND CONTINUE FIN3: CLR R2 ; SELECT UNIT 0 FIRST MOV #IS.CR,R0 ; SET SUCCESS WITH AT END OF LINE STATUS CALL FIN0 ; AND FINISH ITS I/O OPERATION FIN2: MOV #2,R2 ; SELECT UNIT 1 NOW FIN1: MOV #IS.SUC&377,R0 ; SET SUCCESSFUL STATUS FIN0: MOV UCBTBL(R2),R5 ; FETCH THE UNIT'S UCB ADDRESS MOV PKTTBL(R2),R3 ; AND THE UNIT'S I/O PACKET ADDRESS MOV I.PRM+4(R3),R1 ; GET THE ORIGIONAL BYTE COUNT SUB U.CNT(R5),R1 ; SUBTRACT THOSE BYTES NOT TRANSFORMED CALLR $IODON ; AND LET THE EXEC COMPLETE THE OPERATION .END