.TITLE VTDRV .IDENT /02.00/ .ENABLE LC ; ; VIRTUAL TERMINAL DRIVER FOR RSX-11M VERSION 3 ; ; D. ELDERKIN 20-MAY-77 ; ; MODIFIED BY: ; ; Phil Stephensen-Payne 11-Nov-80 ; Include support for 'solicited write' (IO.WBT) ; Terminate unsolicited writes from TKTN or MCR ; ; THIS IS A VIRTUAL DRIVER FOR RSX-11M VERSION 3 SYSTEMS. ; IT PROVIDES THE CAPABILITY FOR NON-PRIVILEGED 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 ; CARRIAGE RETURN AS THE ONLY CHARACTER OF THE BUFFER. ; IF IT IS, THE READ FROM UNIT 0 IS TERMINATED WITH 0 ; BYTES TRANSFERRED. ; ; 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 THE READ. THIS ALLOWS THE USER ; TASK TO FORMAT PROPERLY 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 ; RECEIVES EOF STATUS. ; ; EQUATED SYMBOLS ; CR=15 ;CARRIAGE RETURN CTRLZ=32 ;CONTROL Z ; ; LOCAL DATA ; OURUCB: .BLKW 1 WRIKIL: .BYTE 0 ;FLAG TO NOTE THAT WRITE HAS BEEN KILLED ;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 ; ; 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 DIRECTIVE WHEN AN I/O REQUEST ; IS QUEUED AND AT THE END OF A PREVIOUS I/O OPERATION TO PROPAGATE THE ; EXECUTION 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. ;- .ENABL 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 NONE EXISTS, JUST EXIT ; ; 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 OF THE CONTROLLER TO BE INITIATED. ; ; VIRTUAL TERMINAL I/O REQUEST PACKET FORMAT: ; ; WD. 00 -- I/O QUEUE THREAD WORD ; WD. 01 -- REQUEST PRIORITY, EVENT FLAG NUMBER ; WD. 02 -- ADDRESS OF THE TCB OF THE REQUESTER TASK. ; WD. 03 -- POINTER TO SECOND LUN WORD IN REQUESTER TASK HEADER. ; WD. 04 -- CONTENTS OF THE FIRST LUN WORD IN REQUESTER TASK HEADER (UCB). ; WD. 05 -- I/O FUNCTION CODE. ; WD. 06 -- VIRTUAL ADDRESS OF I/O STATUS BLOCK ; WD. 07 -- RELOCATION BIAS OF I/O STATUS BLOCK ; WD. 10 -- I/O STATUS BLOCK ADDRESS (REAL OR DISPLACEMENT + 140000). ; WD. 11 -- 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 TRANSFERRED. ; 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 UNIT 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 SCB BITB #1,S.STS(R0) ;IS THE OTHER UNIT BUSY? BNE 7$ ; If NE yes - carry on CMP I.TCB(R1),$TKNPT ; Is it TKTN? BEQ 5$ ; If EQ yes - finish it off at once CMP I.TCB(R1),$MCRPT ; No - is it MCR? BNE VTDONE ; If NE no - wait for something else ; 5$: JMP FIN1 ; Else finish off I/O at once ; 7$: 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 UNIT1 ;TO COOL IT FOR A WHILE WITH AN EIF ;OPERATION IS 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 A 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, PROCEED TSTB WRIKIL ;SEE IF A WRITE WAS JUST KILLED FOR UNIT 0 BEQ VTDONE ;IF EQ NO, NORMAL CONDITION, AWAIT 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 PACKET ADDRESS FOR THE OTHER UNIT CMPB #IO.WLB/256.,I.FCN+1(R0) ;IS IT A WRITE? BNE FIN6 ;IF NE NO, TERMINATE THIS READ WITH AN EOF 30$: 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 OF THE OTHER UNIT'S PACKET CMPB #IO.WLB/256.,I.FCN+1(R0) ;IS IT A READ OR A WRITE? BEQ FIN6 ;IF EQ, TELL UNIT 0 TO SWITCH TO READS ;BY SENDING IT AN EOF ; ;READ ON UNIT 0, WRITE ON UNIT 1 ; 40$: CALL M1TO0 ;PERFORM THE TRANSFER BR VTINI1 ;TRY FOR THE NEXT TRANSFER ON THIS UNIT ; ;UNIT 1 HAS UNSOLICITED INPUT FOR UNIT 0 ; 50$: CMPB #TF.WBT,I.FCN(R1) ; Is it an IO.WBT? BEQ VTDONE ; If EQ yes - wait for it to be solicited 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 CMP U.CNT(R5),#80. ; IS HIS QIO TOO LARGE? BLE 70$ ; IF LE NO - WE'LL ALLOW IT MOV #80.,U.CNT(R5) ; YES - DROP IT DOWN 70$: CALL $GTBYT ;GET THE NEXT BYTE FROM THE USER'S BUFFER MOVB (SP)+,(R0)+ ;AND PUT 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 MCR CALL $QMCRL ;QUEUE THE MCR COMMAND LINE CALL FIN2 ;COMPLETE THE OPERATION IN COMMON CODE BR VTINI1 ;AND LOOK FOR MORE WORK ;+ ; **-VTPWF - POERFAIL ENTRY POINT ; ; INPUTS: ; ; R5=UCB ADDRESS ; R4=SCB ADDRESS ; R3=CONTROLLER INDEX ; ; OUTPUTS: ; ; THE RELEVANT UCB AND SCB DATA IS SAVED ;- VTPWF: MOV R5,UCBTBL(R3) ;SAVE THIS UNIT'S UCB ADDRESS MOV R4,SCBTBL(R3) ;AND ITS SCB ADDRESS ;+ ; **-VTOUT - DEVICE TIMEOUT ENTRY POINT ; ; INPUTS: ; ; R5=UCB ADDRESS ; R4=SCB ADDRESS ; R3=CONTROLLER INDEX ; R2=ADDRESS OF DEVICE CSR ; R0=I/O STATUS CODE OF IE.DNR ; ; OUTPUTS: ; ; NONE ;- VTOUT: VTDONE: $VTINT:: 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 CANCELLED CALLR $IOALT ;FINISH THE I/O OPERATION WITH 0 BYTES XFERRED ;+ ; **-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 ; CONTENTS 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 STOLEN 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 IT JUST GOT AN EOF BR FIN2 ;AND FINISH UP UNIT 1 NORMALLY ;+ ; **-M0TO1 - MOVE DATA FROM 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 INPUT CHARACTERS FROM INPUT BUFFER TO 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: 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 ;+ ; **-FIN0,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 ORIGINAL BYTE COUNT SUB U.CNT(R5),R1 ;SUBTRACT THOSE BYTES NOT TRANSFERRED CALLR $IODON ;AND LET THE EXEC DO THE REST .END