.TITLE VTDRV .IDENT /V1.11/ ;******************************************************************** ; ; VIRTUAL TERMINAL DRIVER FOR USE WITH BATCH SYSTEM *ONLY* ; ; S.M. THOMPSON, JANUARY 1980 ; ; THIS VERSION SUPPORTS MULTIPLE BATCH STREAMS. ; ;******************************************************************** LNKCNT = 4 ; SIZE OF LINK WORD AND BYTE COUNT FIELDS IN ; STACK PACKETS TSKNAM: .RAD50 /BATMAN/ ; BATCH MANAGER TASK NAME ; ; DRIVER DISPATCH TABLE ; $VTTBL::.WORD VTINI ; INITIATOR ENTRY POINT .WORD VTCAN ; CANCEL I/O ENTRY POINT .WORD VTOUT ; TIMEOUT ENTRY POINT .WORD VTPWF ; PWER FAIL ENTRY POINT ;+ ; *** VTINI VIRTUAL TERMINAL DRIVER 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 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 #$VIRT0,R5 ; SET TO EXAMINE CONTROLLER #1 BR VTINI ; GET AN I/O PACKET VTINI2: MOV #$VIRT1,R5 ; SET TO EXAMINE CONTROLLER #2 ; ANY UCB ADDRESS WILL DO HERE (EXCEPT THAT ; OF VT0:) SINCE THEY ARE ALL ON THE SAME ; CONTROLLER VTINI: ; INITIATOR ENTRY POINT CALL $GTPKT ; TRY TO FIND AN I/O PACKET FOR THIS UNIT BCS VTDONE ; RETURN TO EXEC IF NO WORK ; ; 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 (0 FOR VT0:, 2 OTHERWISE). ; R4 ADDRESS OF THE STATUS CONTROL BLOCK. ; R5 ADDRESS OF THE UCB TO WHICH I/O IS DIRECTED ; ; 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 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 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 TRANSFERED. ; WD. 15 NOT USED. ; WD. 16 NOT USED. ; WD. 17 NOT USED. ; WD. 20 NOT USED. ; CMPB #IO.WLB/256.,I.FCN+1(R1) ; IS IT A WRITE FUNCTION? BEQ WRITE ; YES ; IF IT'S NOT A WRITE, IT MUST BE A READ ; OR IT WOULDN'T HAVE GOT THIS FAR ; ; READ FUNCTION. ; IF FROM VT0:, IT MUST BE FROM BATMAN TO BE LEGAL, AND IMPLIES A DATA ; TRANSFER TO BATMAN'S BUFFER FROM A WRITE FROM ANOTHER VT:. ; IF FROM OTHER THAN VT0:, IT IS A REQUEST TO UNLINK A PACKET FROM ; THAT VIRTUAL TERMINAL'S STACK (CREATED BY "STACK" COMMAND). ; READ: MOV I.TCB(R1),R0 ; GET TCB ADDRESS OF REQUESTING TASK ; WE CAN'T USE $TKTCB HERE SINCE THE TASK ; CONTEXT MAY NOT BE PRESERVED AFTER $GTPKT TST R3 ; IS IT A READ FROM VT0:? ; CONTROLLER INDEX IS ZERO ONLY FOR VT0: BNE STACK ; NO,IT'S ANOTHER VT: CMP T.NAM(R0),TSKNAM ; IT'S VT0:,IS IT BATMAN? BNE IEPRI ; NO, PRIVILEGE VIOLATION CMP T.NAM+2(R0),TSKNAM+2 ; MAYBE BEQ 10$ ; YES, OK TO PROCEED IEPRI: MOV #IE.PRI&377,R0 ; NO, THEN IT'S A PRIVILEGE VIOLATION IOALT: CALL $IOALT ; SO FINISH I/O WITH ZERO BYTES TRANSFERRED BR VTINI ; TRY FOR MORE WORK 10$: TSTB $VTS1+S.STS ; OK, DOES SOMEONE ELSE HAVE A WRITE PENDING? ; (IF CONTROLLER #2 IS BUSY IT MUST BE A PENDING ; WRITE (NOT A READ) SINCE A READ ON CONTROLLER #2 ; IS ALWAYS SATISFIED IN A SINGLE PASS THROUGH ; THE DRIVER) BEQ VTINI2 ; NO, CHECK I/O QUEUE FOR CONTROLLER #2 TO SEE ; IF THERE'S ANYTHING WE CAN USE MOV $VTS1+S.PKT,R3 ; GET WRITER'S I/O PACKET ADDRESS MOV I.UCB(R3),R4 ; GET WRITER'S UCB ADDRESS CALL MOVE ; PERFORM THE DATA TRANSFER BR VTINI ; GET MORE WORK (CONTROLLER #2, BATMAN ; WILL NOT YET HAVE ANOTHER READ PENDING) STACK: CMP T.UCB(R0),R5 ; WE MUST CHECK TO SEE IF THE BATCH JOB IS ; TRYING TO READ FROM ITS OWN OR ANOTHER ; JOB'S STACK (IN CASE THE JOB HAS A LUN ; ASSIGNED THIS WAY) BNE IEPRI ; READING FROM A DIFFERENT STACK IS A PRIVILEGE ; VIOLATION MOV R5,R0 ; POINT R0 TO THIS VT:'S STACK LISTHEAD ADD #U.STHD,R0 ; CALL $QRMVF ; REMOVE AN ENTRY BCC 30$ ; OK, WE GOT ONE IEEOF: MOV #IE.EOF&377,R0 ; NO STACK, END OF FILE MUST BE SENT BR IOALT ; FINISH I/O WITH ZERO BYTES TRANSFERRED 30$: MOV 2(R1),R3 ; GET SIZE OF THIS STACK ENTRY SUB R3,U.STSZ(R5) ; UPDATE USER'S STACK SIZE MOV R1,R2 ; COPY PACKET ADDRESS TO R2 ADD #LNKCNT,R2 ; POINT TO START OF DATA 40$: MOVB (R2)+,R0 ; GET A BYTE FROM THE "STACK" MOV R0,-(SP) ; PUT IT ON THE "STACK" ; NOTE THAT WE HAVE TWO DIFFERENT "STACK"S ; THE FIRST ONE IS THE BATCH JOB STACK ; THE SECOND ONE IS THE EXEC STACK, AS ; POINTED TO BY SP. CALL $PTBYT ; AND THEN INTO THE USER'S BUFFER DEC U.CNT(R5) ; DONE YET? BEQ 50$ ; YES DEC R3 ; NO, BUT IS STACK EXHAUSTED? BNE 40$ ; NO, WE CAN MOVE SOME MORE 50$: MOV R1,R0 ; EITHER READER'S BYTE COUNT IS SATISFIED ; OR STACK IS EXHAUSTED. NOW RETURN THE ; STACK PACKET TO THE DYNAMIC STORAGE ; REGION MOV 2(R1),R1 ; SET R1 TO TOTAL PACKET SIZE ADD #LNKCNT,R1 ; CALL $DEACB ; RETURN IT TO THE POOL MOV $VTS1+S.PKT,R3 ; GET READER'S I/O PACKET ADDRESS MOV I.PRM+4(R3),R1 ; GET READER'S ORIGINAL BYTE COUNT MOV #IS.CR,R0 ; TERMINATOR IS CALL DONE ; FINISH THE OPERATION BR VTINI ; GET MORE WORK ; ; WRITE FUNCTION. ; IF FROM VT0:, THIS IS ALWAYS MARKED AS SUCCESSFUL ALTHOUGH NO DATA ; TRANSFER IS ACTUALLY PERFORMED ; IF FROM ANOTHER VT:, THIS WRITE EVENTUALLY RESULTS IN A TRANSFER ; TO THE BUFFER SPECIFIED IN A READ BY BATMAN. ; WRITE: TST R3 ; IS IT A WRITE ON VT0:? BNE 52$ ; NO, IT'S NOT 51$: MOV U.CNT(R5),R1 ; SAY WE MOVED ALL THE OUTPUT MOV #IS.SUC&377,R0 ; SUCCESS CODE CALL $IODON ; FINISH THE I/O BR VTINI ; GET MORE WORK 52$: MOV I.TCB(R1),R0 ; GET TCB OF WRITING TASK CMP T.UCB(R0),R5 ; IS IT WRITING TO ITS OWN VT:? BEQ 53$ ; YES, DO IT RIGHT AWAY CMP T.NAM(R0),TSKNAM; NO, IS IT BATMAN? BNE 51$ ; NO, JUST SAY WE DID IT CMP T.NAM+2(R0),TSKNAM+2 ; MAYBE BNE 51$ ; NO 53$: TSTB $VTS0+S.STS ; IS CONTROLLER #1 BUSY? BEQ VTINI1 ; IF EQ THEN CONTROLLER #1 IS NOT ; BUSY, THAT IS, BATMAN DOES NOT HAVE ; A READ PENDING. WE THEN GO AND CHECK ; THE I/O QUEUE FOR CONTROLLER #1 TO SEE ; IF THERE'S ONE WAITING MOV R1,R3 ; SET R3 TO WRITER'S I/O PACKET ADDRESS MOV R5,R4 ; SET R4 TO WRITER'S UCB ADDRESS MOV $VTS0+S.PKT,R1 ; SET R1 TO READER'S I/O PACKET ADDRESS MOV I.UCB(R1),R5 ; SET R5 TO READER'S UCB ADDRESS CALL MOVE ; PERFORM THE DATA TRANSFER BR VTINI2 ; GET MORE WORK ; ; POWERFAIL ENTRY POINT ; VTPWF: ; FALL THROUGH TO VTOUT ; ; DEVICE TIMEOUT ENTRY POINT ; VTOUT: VTDONE: RETURN ; THAT'S ALL ; ; CANCEL I/O ENTRY POINT ; VTCAN: MOV #IE.ABO&377,R0 ; SET FIRST STATUS WORD CALLR $IOALT ; FINISH I/O WITH ZERO BYTES ; TRANSFERRED ;+ ; MOVE -- THIS ROUTINE PERFORMS A DATA TRANSFER BETWEEN THE WRITER'S ; BUFFER AND THE READER'S BUFFER. ; ; INPUTS: ; R1 READER'S I/O PACKET ADDRESS ; R3 WRITER'S I/O PACKET ADDRESS ; R4 WRITER'S UCB ADDRESS ; R5 READER'S UCB ADDRESS ; ; OUTPUTS: ; R5 CORRECT UCB FOR NEXT OPERATION ; BOTH I/O OPERATIONS ARE TERMINATED, UNLESS THE READER HAD AN ; ILLEGAL BUFFER CHECK (LESS THAN 3 BYTES REQUESTED) ; ;- MOVE: CMP U.CNT(R5),#3 ; READ REQUEST LONG ENOUGH? BGE 85$ ; YES MOV #IE.BAD&377,R0 ; SET BAD PARAMETERS CODE CALLR $IOALT ; FINISH I/O, NO BYTES TRANSFERRED 85$: MOV U.UNIT(R4),-(SP) ; PUT WRITER'S UNIT NUMBER ON STACK CALL $PTBYT ; PUT IT INTO READER'S BUFFER DEC U.CNT(R5) ; UPDATE READER'S BYTE COUNT MOV I.PRM+6(R3),-(SP) ; PUT CARRIAGE CONTROL BYTE ON STACK CALL $PTBYT ; PUT IT INTO READER'S BUFFER DEC U.CNT(R5) ; AND UPDATE THE COUNT 15$: MOV R4,R5 ; GET WRITER'S UCB ADDRESS IN R5 CALL $GTBYT ; GET A BYTE FROM THE WRITER'S BUFFER MOV I.UCB(R1),R5 ; GET READER'S UCB ADDRESS CALL $PTBYT ; AND PUT THE BYTE IN READER'S BUFFER DEC U.CNT(R4) ; UPDATE WRITER'S BYTE COUNT BNE 16$ ; IF NE THEN WE'RE NOT AT THE END DEC U.CNT(R5) ; AT THE END OF WRITER'S BUFFER; DON'T FORGET ; THAT WE PUT THE BYTE IN READER'S BUFFER BR 17$ ; FINISH BOTH I/O OPERATIONS 16$: DEC U.CNT(R5) ; UPDATE READER'S BYTE COUNT BNE 15$ ; IF NE WE CAN DO MORE 17$: MOV I.PRM+4(R1),R1 ; GET READER'S ORIGINAL BYTE COUNT ; (R1 IS READER'S I/O PACKET ADDRESS) MOV R3,-(SP) ; SAVE WRITER'S I/O PACKET ADDRESS MOV R4,-(SP) ; SAVE WRITER'S UCB ADDRESS ON STACK MOV #IS.CR,R0 ; FINAL STATUS IS AS TERMINATOR, ALL OK. CALL DONE ; FINISH I/O OPERATION MOV (SP)+,R5 ; RESTORE WRITER'S UCB ADDRESS TO R5 MOV (SP)+,R3 ; RESTORE WRITER'S I/O PACKET ADDRESS MOV I.PRM+4(R3),R1 ; GET WRITER'S ORIGINAL BYTE COUNT MOV #IS.SUC&377,R0 ; WRITE SUCCESSFUL ; FALL THROUGH TO FINISH I/O DONE: SUB U.CNT(R5),R1 ; SUBTRACT THOSE BYTES NOT TRANSFERRED CALLR $IODON ; FINISH THE OPERATION .END