[comp.os.vms] VMS pseudo-terminal drivers 4/5

KVC@BUSTER.NRC.COM (Kevin Carosso) (06/22/88)

+-+-+-+ Beginning of part 4 +-+-+-+
X;`009the request with immediate success or error if necessary.
X;`009The routine then immediately start cramming the characters into
X;`009the associated units typeahead buffer by calling putnxtchr.
X;
X; Inputs:
X;
X;`009R0,R1,R2`009= Scratch
X;`009R3`009`009= IRP Address
X;`009R4`009`009= Address of PCB for current process
X;`009R5`009`009= Device UCB address
X;`009R6`009`009= Address of CCB
X;`009R7`009`009= I/O function code
X;`009R8`009`009= FDT Dispatch addr
X;`009R9,R10,R11`009= Scratch
X;`009AP`009`009= Address of function parameter list
X;`009`009P1(AP)`009= Buffer Address
X;`009`009P2(AP)`009= Buffer Size
X;
X; Outputs:
X;
X;`009R0,R1,R2`009= Destroyed
X;`009R3-R7,AP`009= Preserved (pickled)
X;
X;
X;`009N O T E:
X;`009`009This routine now assumes that TW and PY's fork and DEVICE
X;`009locks are the same lock.  This allows use to keep from having to 
X;`009do an extra unecessary lock acquisition.
X;`009
X;
X;
X; External Routines:
X;
X;`009EXE$ABORTIO - FDT abort io routine
X;`009Input Parameters:
X;`009`009R0 - First longword of IOSB
X;`009`009R3 - IRP Address
X;`009`009R4 - PCB Address
X;`009`009R5 - UCB Address
X;
X;`009EXE$FINISHIOC - FDT finish IO routine
X;`009Input Parameters:
X;`009`009R0 - First longword of IOSB
X;`009`009R3 - IRP Address
X;`009`009R4 - PCB Address
X;`009`009R5 - UCB Address
X;
X;`009EXE$WRITECHK - Check access to buffer
X;`009Input Parameters:
X;`009`009R0 - Address of buffer
X;`009`009R1 - Size of buffer
X;`009`009R3 - IRP Address
X;`009Output Parameters:
X;`009`009R0,R1,R3 - Preserved
X;`009`009R2 - clear
X;
X;`009@UCB$L_TT_PUTNXT(R5) - Port driver input character routine
X;`009Input Parameters:
X;`009`009R3 - character
X;`009`009R5 - UCB Address
X;`009Output Parameters:
X;`009`009R3 - if EQL then nothing
X;`009`009     if LSS then Burst address to output
X;`009`009     if GTR then char to output
X;`009`009R5 - UCB Address
X;`009`009R1,R2,R4 - trashed
X;`009`009R0 - Is this trashed or preserved? Documentation say preserve.
X;
X;--
XPY$FDTWRITE::
X`009MOVZWL`009P2(AP),R1`009`009; Get buffer Size
X`009BNEQ`00910$`009`009`009; Is the non zero? If so, do it easy.
X`009BRW`009160$`009`009`009; Zero size buffer just finish it now
X
X10$:
X`009MOVL`009P1(AP),R0`009`009; Get buffer Address
X`009JSB`009G^EXE$WRITECHK`009`009; Do we have access to the buffer
X`009`009`009`009`009; No return means no access
X`009MOVL`009R1,R9`009`009`009; Number of characters to send
X`009CLRL`009R10`009`009`009; Clear count of characters sent
X`009MOVL`009R5,R11`009`009`009; Save away PY UCB ptr
X`009PUSHR`009#^M<R3,R4,R5>`009`009; Save PCB, IRP,and UCB address
X;
X;`009User request ok.
X;
X;`009R0`009-> `009Address of characters to input
X;`009R5`009->`009Address of TW's UCB 
X;`009R8`009->`009Number of characters to send this time
X;`009R9`009->`009Total number of characters to send
X;`009R10`009->`009Numbers of characters already sent
X;`009R11`009->`009Address of Py's UCB
X;
X20$:
X`009SUBL3`009R10,R9,R8`009`009; Get number of characters send
X`009CMPB`009R8,#BUFFER_SIZE`009`009; More data than buffer can hold
X`009BGEQ`00930$`009`009`009; GEQ then use BUFFER_SIZE segment
X`009CMPB`009R8,#1`009`009`009; Test for one byte only 
X`009BGTR`00940$`009`009`009; GTR more than one fill up buffer
X`009MOVZBL`009(R0),R3`009`009`009; Move single character into R3
X`009BRB`00960$`009`009`009; Now go ahead and send it
X30$:
X`009MOVC3`009#BUFFER_SIZE,(R0)[R10],-; Store data in UCB buffer
X`009`009UCB$T_PY_BUFFER(R11)`009;
X`009MOVZBL`009#BUFFER_SIZE,R8`009`009; Number of characters in burst
X`009BRB`00950$`009`009`009; Now finish setup for transfer
X40$:
X`009MOVC3`009R8,(R0)[R10],`009-`009; Store data in UCB buffer
X`009`009UCB$T_PY_BUFFER(R11)`009;
X50$:
X`009MOVAB`009UCB$T_PY_BUFFER(R11),R0`009; Get buffer address
X`009MOVZBL`009(R0)+,R3`009`009; Get first character 
X60$:
X`009MOVL`009UCB$L_PY_XUCB(R11),R5`009; Get TW's UCB address
X
X.IF`009DEFINED`009VMS_V4
X`009DSBINT`009UCB$B_DIPL(R5)
X.IF_FALSE
X`009DEVICELOCK`009`009-`009; Take device lock for TW device
X`009`009LOCKADDR=UCB$L_DLCK(R5),- ;
X`009`009LOCKIPL=UCB$B_DIPL(R5), - ;
X`009`009SAVIPL=-(SP),`009-`009;
X`009`009PRESERVE=YES`009`009;
X.ENDC
X
X70$:
X`009INCL`009R10`009`009`009; Increment sent character count
X`009JSB`009@UCB$L_TT_PUTNXT(R5)`009; Buffer character
X`009BLSS`00990$`009`009`009; LSS burst 
X`009BGTR`009100$`009`009`009; GTR single character
X80$:`009BBS`009#TTY$V_TP_XOFF,`009-`009; See if XOFFED is so then
X`009`009UCB$B_TP_STAT(R5),120$`009; stop input and check for echoed data
X`009DECW`009R8`009`009`009; Decrease number to send in burst
X`009BLEQ`009110$`009`009`009; LEQ block done see if request done
X`009MOVZBL`009(R0)+,R3`009`009; Get next character
X`009BRB`00970$`009`009`009; Send next character
X90$:
X`009BISW`009TTY$M_TANK_BURST, -`009; Signal burst
X`009`009UCB$W_TT_HOLD(R5)`009;
X`009BRB`00980$`009`009`009; Continue processing
X100$:
X`009MOVB`009R3,UCB$W_TT_HOLD(R5)`009; Store character in tank
X`009BISW`009#TTY$M_TANK_HOLD, -`009; Signal character in tank
X`009`009UCB$W_TT_HOLD(R5)`009;
X`009BRB`00980$`009`009`009; Continue processing
X
X;
X; See if this request is done or if more to do
X;
X110$:
X`009CMPL`009R10,R9`009`009`009; All done
X`009BGEQ`009120$`009`009`009; GEQ done so check for echo
X
X.IF`009DEFINED`009VMS_V4
X`009ENBINT
X.IF_FALSE
X`009DEVICEUNLOCK`009`009-`009; More in request release lock 
X`009`009LOCKADDR=UCB$L_DLCK(R5), - ; and go back and get it
X`009`009NEWIPL=(SP)+,`009-`009;
X`009`009PRESERVE=YES`009`009;
X.ENDC
X
X`009MOVL`009P1(AP),R0`009`009; Restore users buffer address
X`009BRW`00920$`009`009`009; 
X
X;
X; See if need to start up pending read
X;
X120$:
X`009BBS`009#TTY$V_TANK_HOLD, -`009; If storing character in hold
X`009`009UCB$W_TT_HOLD(R5),130$`009;  start output
X`009BBC`009#TTY$V_TANK_BURST, -`009; If no output burst then all done
X`009`009UCB$W_TT_HOLD(R5),140$`009;  finish up
X130$:
X`009BBC`009#UCB$V_BSY,`009-`009; PY ready to take data 
X`009`009UCB$W_STS(R11),140$`009;
X`009MOVL`009UCB$L_IRP(R11),R3`009; Get IRP address
X`009MOVL`009R11,R5`009`009`009; Get PY UCB address
X`009JSB`009G^IOC$INITIATE`009`009; Now start PY read 
X140$:
X
X.IF`009DEFINED`009VMS_V4
X`009ENBINT
X.IF_FALSE
X`009DEVICEUNLOCK`009`009-`009; Done release lock 
X`009`009LOCKADDR=UCB$L_DLCK(R11), - ; NOTE PY & TW lock are the same
X`009`009NEWIPL=(SP)+`009`009;
X.ENDC
X
X`009BBS`009#TTY$V_TP_XOFF,`009-`009; See if XOFFED report this special
X`009`009UCB$B_TP_STAT(R5),170$`009; case
X;
X; Finish up the read
X;
X150$:
X`009POPR`009#^M<R3,R4,R5>`009`009; Restore IRP, PCB, and UCB 
X160$:
X`009INSV`009R10,#16,#16,R0`009`009; Move number of bytes INPUT
X`009MOVW`009#SS$_NORMAL,R0`009`009; Everything is just fine
X`009JMP`009G^EXE$FINISHIOC`009`009; Complete the I/O request
X
X;+
X; Special code to deal with input while xoffed.
X;-
X`009TTY$M_TP_XOFF = 8
X`009TTY$V_TP_XOFF = 3
X`009ASSUME`009TTY$M_TP_XOFF EQ TTY$M_TP_DLLOC+4
X`009ASSUME  TTY$V_TP_XOFF EQ TTY$V_TP_DLLOC+1
X
X170$:          
X`009POPR`009#^M<R3,R4,R5>`009`009; Restore IRP, PCB, and UCB 
X`009INSV`009R10,#16,#16,R0`009`009; Move number of bytes INPUT
X`009MOVW`009#SS$_DATAOVERUN,R0`009; Cannot input more data 
X`009JMP`009G^EXE$FINISHIOC`009`009; Complete the I/O request
X`012
X`009.PAGE
X`009.SBTTL`009PY$CANCEL - Cancel the IO on the PY device
X;++
X;
X; Functional Description:
X;
X;`009This routine is entered to stop io on a PY unit.  If this is the last
X;`009deassign on the PY device, issue a CLASS_DISCONNECT on our associated
X;`009TW device to get it away from any processes using it.
X;
X; Inputs:
X;
X;`009R2 = Negative of the Channel Number,
X;`009`009also called channel index number
X;`009R3 = Current IO package address
X;`009R4 = PCB of canceling process
X;`009R5 = UCB Address
X;`009R8 = CAN$C_CANCEL on CANCEL IO or CAN$C_DASSGN on DEASSIGN
X;
X; Outputs:
X;`009Everything should be preserved
X;--
XPY$CANCEL::`009`009`009`009`009; Cancel PY usage
X`009JSB`009G^IOC$CANCELIO`009`009`009; Call the cancel routine
X`009BBC`009#UCB$V_CANCEL,UCB$W_STS(R5),10$`009; Branch if not for this guy
X`009MOVQ`009#SS$_ABORT,R0`009`009`009; Status is request canceled
X`009BICW`009#<UCB$M_BSY!UCB$M_CANCEL>,-`009;
X`009`009UCB$W_STS(R5)`009`009`009; Clear unit status flags
X`009JSB`009G^IOC$REQCOM`009`009`009; Complete request
X10$:`009TSTW`009UCB$W_REFC(R5)`009`009`009; Last Deassign
X`009BNEQ`009100$`009`009`009`009; No, just exit
X;
X; Last DEASSIGN we need to get rid of AST's 
X;
X`009pushr`009#^M<r2, r3, r4, r5, r7>
X`009movl`009UCB$L_PY_XUCB(r5), r5`009`009; Switch to TW UCB
X`009beql`00920$`009`009`009`009; if not there, skip
X`009MOVAB`009UCB$L_TW_XON_AST(R5),R7`009`009; Get XON list head address
X`009TSTL`009(R7)`009`009`009`009; Any ast to deliver
X`009BEQL`00911$`009`009`009`009; EQL 0 do not flush it
X`009JSB`009G^COM$FLUSHATTNS`009`009; Flush it
X11$:`009MOVAB`009UCB$L_TW_XOFF_AST(R5),R7`009; Get XOFF list head address
X`009TSTL`009(R7)`009`009`009`009; Any ast to deliver
X`009BEQL`00912$`009`009`009`009; EQL 0 do not flush it
X`009JSB`009G^COM$FLUSHATTNS`009`009; Flush it
V12$:`009MOVAB`009UCB$L_TW_SET_AST(R5),R7`009`009; Get SET_LINE list head addres
Xs
X`009TSTL`009(R7)`009`009`009`009; Any ast to deliver
X`009BEQL`00913$`009`009`009`009; EQL 0 do not flush it
X`009JSB`009G^COM$FLUSHATTNS`009`009; Flush it
X;
X; Do a DISCONNECT on the TW device.
X;
X13$:
X`009clrl`009UCB$L_TW_XUCB(r5)`009`009; Clear backlink to PY device
X`009bisl2`009#UCB$M_DELETEUCB, UCB$L_STS(r5) ; Set it to go bye-bye
X`009bicw2`009#UCB$M_ONLINE,UCB$W_STS(R5)`009; Mark offline
X`009bicb2`009#UCB$M_INT, UCB$W_STS(r5)`009; Don't expect interrupt
X`009movl`009UCB$L_TT_LOGUCB(r5), r1`009`009; Look at logical term UCB
X`009tstw`009UCB$W_REFC(r1)`009`009`009; See if TW has any references
X`009bneq`00915$`009`009`009`009; If so, go and do disconnect
X`009jsb`009G^IOC$DELETE_UCB`009`009; if not, delete the UCB
X`009brb`00920$
X15$:`009clrl`009r0`009`009`009`009; indicate that we must hangup
X`009movl`009UCB$L_TT_CLASS(r5), r1
X`009jsb`009@CLASS_DISCONNECT(r1)`009`009; Force disconnect
X20$:`009popr`009#^M<r2, r3, r4, r5, r7>`009`009; Switch back to PY UCB
X`009clrl`009UCB$L_PY_XUCB(r5)`009`009; Clear link to deleted TW
X`009bisl2`009#UCB$M_DELETEUCB, UCB$L_STS(r5)`009; Set our own delete bit
X100$:`009rsb
X`012
X`009.PAGE
X`009.SBTTL PY$INITIAL - Initialize Pseudo terminal interface
X 
X;++
X; PY$INITIAL - Initialize the interface
X;
X; Functional Description:
X;
X;`009This routine is entered at device connect time and power recovery.
X;`009There isn't much to do to the device.
X;
X; Inputs:
X;
X;`009R4 = The devices CSR  (but there is no csr!)
X;`009R5 = address of IDB
X;`009R6 = address of DDB
X;`009R7 = address of CRB
X;
X; Outputs:
X;
X;`009All registers preserved
X;
X;--
XPY$INITIAL::
X`009RSB
X`012
X`009.PAGE
X`009.SBTTL`009PY$CLONE_INIT - initialize the unit
X;++
X; PY$CLONE_INIT - Initialize new PY device
X;
X; Functional Description:
X;
X;`009Main thing we do here is clone up an associated terminal device
X;`009and initialize fields in the two new UCB's.
X;
X; Inputs:
X;
X;`009R5`009= Address of UCB
X;
X; Outputs:
X;
X;`009All preserved
X;--
X 
XPY$CLONE_INIT::
X 
X;+ ---
X;`009Ignore inits on UNIT #0 (the template PY UCB)
X;- ---
X`009TSTL`009UCB$W_UNIT(R2)`009`009`009;UNIT #0??
X`009BNEQ`00910$`009`009`009`009;No: Initialize
X`009RSB`009`009`009`009`009;Yes: Return
X 
X10$:`009PUSHR`009#^M<R0,R1,R2,R4,R6,R7,R8>
X`009Bicl2`009#UCB$M_DELETEUCB,UCB$L_STS(R2)`009; Clear ucbdelete - dec
X`009Movl`009R2,R5
X;
X; Find the associated device.
X;
X; NOTE: We can't call IOC$SEARCHDEV because it expects the string to
X;`009be accessible from the previous access mode. (It executes the
X;`009prober instruction with mode=#0). I don't know how to make the
X;`009string accessible from the previous access mode cleanly, but I
X;`009do know how to move most of IOC$SEARCHDEV into the py driver.
X;
X`009MOVAL`009G^IOC$GL_DEVLIST-DDB$L_LINK,-`009; Get address of i/o database
X`009`009R8`009`009`009`009; listhead
X`009CLRL`009R6`009`009`009`009; Desired mate = PTY UNIT 0
X`009MOVAB`009L^TWSTRING,R7`009`009`009; String address for TWA
X`009MOVL`009#TWLENGTH,R4`009`009`009; String length
X`009BSBW`009SEARCHDEV`009`009`009; Find the DDB
X`009BNEQ`00920$`009`009`009`009; Device not found
X`009BSBW`009SEARCHUNIT`009`009`009; Search for specific unit
X`009BNEQ`00930$`009`009`009`009; unit found
X20$:`009POPR`009#^M<R0,R1,R2,R4,R6,R7,R8>`009; NOT FOUND: Return
X`009RSB
X`009
X;
X; Create the PTY, R1 has template UCB of TW device
X;
X30$:`009PUSHL`009R5`009`009`009`009; Save R5
X`009MOVL`009UCB$L_DDB(R5),R0`009`009; Find UNIT #0 UCB FOR PY DEV.
X`009MOVL`009DDB$L_UCB(R0),R0
X`009MOVL`009R1,R5`009`009`009`009; R5 = UCB to CLONE
X`009JSB`009G^IOC$CLONE_UCB`009`009`009; Clone UCB
X 
X`009MOVL`009R2,R1`009`009`009`009; Put PTY UCB back into R1
X`009POPL`009R5`009`009`009`009; Restore R5
X`009BLBS`009R0,40$`009`009`009`009; WIN!!! (big deal.)
X;+ ---
X;`009CREATE_UCB failed, mark our PY device offline
X;- ---
X`009BICW2`009#UCB$M_ONLINE,UCB$W_STS(R5)`009; Mark offline
X`009BRW`009100$`009`009`009`009; And return
X;+ ---
X;`009PTY UCB created successfully, link the UCBs together
X;- ---
X40$:`009MOVL`009R1,UCB$L_PY_XUCB(R5)`009`009; Store associated UCB
X`009MOVL`009R5,UCB$L_TW_XUCB(R1)`009`009; Store the other one back
X`009CLRL`009UCB$L_PID(R1)`009`009`009; Clear the owner PID in PTY
X`009CLRW`009UCB$W_REFC(R1)`009`009`009; Reference count is ZERO
X`009Bicl2`009#UCB$M_DELETEUCB,UCB$L_STS(R1)`009; Inhibit deletion
X`009MOVW`009UCB$W_UNIT(R1),-`009`009; Set associated TW unit
X`009`009UCB$L_DEVDEPEND(R5)`009`009; number in PY devdepend
X;+ ---
X;`009Call the PTY unit init routine
X;- ---
X`009MOVL`009UCB$L_DDT(R1),R0`009`009; Get DDT
X`009MOVL`009DDT$L_UNITINIT(R0),R0`009`009; Get Unit Init Addr in DDT
X`009CMPL`009R0,#IOC$RETURN`009`009`009; Null Address??
X`009BNEQ`00950$`009`009`009`009; No: Call it
X`009MOVL`009UCB$L_CRB(R1),R0`009`009; Yes: Look in the CRB
X`009MOVL`009CRB$L_INTD+VEC$L_UNITINIT(R0),R0
X`009BEQL`009100$`009`009`009`009; No: Unit init routine
X 
X50$:`009PUSHL`009R5`009`009`009`009; Save R5
X`009MOVL`009R1,R5`009`009`009`009; R5 = PTY UCB
X`009JSB`009(R0)`009`009`009`009; CALL THE UNIT INIT ROUTINE
X`009MOVL`009G^CTL$GL_PCB,R4`009`009`009; Use current PCB
X`009MOVL`009UCB$L_ORB(R5),R0`009`009; Fetch ORB address
X`009MOVL`009PCB$L_UIC(R4),ORB$L_OWNER(R0)`009; Set device owner
V`009BISB`009#ORB$M_PROT_16,ORB$B_FLAGS(R0)`009; Indicate using SOGW device prot
Xection
X`009BICW`009#^XFF,ORB$W_PROT(R0)`009`009; Make device S:O:RWLP
X `009POPL`009R5`009`009`009`009; Restore R5
X 
X100$:`009POPR`009#^M<R0,R1,R2,R4,R6,R7,R8>
X`009RSB
X`012
X`009.PAGE
X`009.SBTTL`009DDB finding Routines
X;++
X; SearchDev - Search for device DDB
X;
X; This routine is called to search the device database for a DDB.
X; This is the first step in finding another devices UCB.
X;
X; This routine copied out of IOC$SEARCHDEV in IOSUBPAGD
X;
X; Inputs:
X;
X;`009R8 = DDB Head
X;`009R7 = Address of String
X;`009`009String = ddc format: example = /TTA/
X;`009R4 = Length of string
X;
X; Outputs:
X;
X;`009R8 = DDB of desired device if EQL, otherwise not found
X;`009R0 is trashed
X;`009R1 is trashed
X;--
XSEARCHDEV:`009`009`009`009; Search for device name
X10$:`009MOVL`009DDB$L_LINK(R8),R8`009; Get address of next ddb
X`009BEQL`00920$`009`009`009; If eql end of list
X`009MOVAL`009DDB$T_NAME(R8),R0`009; Get address of generic device name
X`009MOVB`009(R0)+,R1`009`009; Calculate len of string to compare
X`009CMPB`009R1,R4`009`009`009; Length of names match?
X`009BNEQ`00910$`009`009`009; If neq no
X`009CMPC`009R4,(R0),(R7)`009`009; Compare device names
X`009BNEQ`00910$`009`009`009; If neq names do not match
X`009RSB
X20$:`009INCL`009R8`009`009`009; indicate search failure
X`009RSB
X 
X`009.SBTTL`009UCB finding routine
X;++
X; SEARCHUNIT - Subroutine to search for UCB given DDB
X;
X; Given the DDB of a device, get the UCB and run down the ucb list until
X; we get the ucb with the desired unit number.  This code is taken out of
X; IOC$SEARCHDEV in IOSUBPAGD.
X;
X; Inputs:
X;
X;`009R8 = DDB of device
X;`009R6 = unit number of desired UCB
X;
X; Outputs:
X;
X;`009R1 = UCB of device if NEQ, otherwise not found
X;`009R0 is trashed
X;
X;--
XSEARCHUNIT:`009`009`009`009; Search for unit number
X`009MOVAL`009DDB$L_UCB-UCB$L_LINK(R8),-
X`009`009R1`009`009`009; Get address of next ucb address
X10$:`009MOVL`009UCB$L_LINK(R1),R1`009; Get Address of next ucb
X`009BEQL`00940$`009`009`009; If EQL then end of list
X`009CMPW`009R6,UCB$W_UNIT(R1)`009; Unit number match?
X`009BEQL`00930$`009`009`009; If eql yes
X`009BRB`00910$
X30$:`009MOVL`009#SS$_NORMAL,R0`009`009; Indicate match
X40$:`009RSB
X`012
X`009.PAGE
X`009.SBTTL`009PY$STARTIO - Device Startio routines
X;++
X; PY$STARTIO`009- Start Input on idle device
X;
X; Functional Description:
X;
X;`009If after the read FDT routines are done and nobody is doing
X;`009anything on the device (UCB$V_BSY = 0) then call the start io
X;`009routine.
X;
X; Called from:
X;
X;`009Called from any one of five places:
X;`009- The EXE$QIODRVPKT in the PY FDT READ routine
X;`009`009which calls EXE$INSIOQ which calls IOC$INITIATE
X;`009- The IOC$REQCOM at the end of this PY startio routine
X;`009`009which calls IOC$INITIATE
X;`009- The TW startio routine which calls IOC$INITIATE
X;`009- The PY write fdt routine which calls IOC$INITIATE.
X;`009`009In case we must echo a character.
X;`009- The PY$RESUME routine which calls IOC$INITIATE.
X;
X; Inputs:
X;
X;`009R3 = IRP Address
X;`009R5 = UCB Address
X;`009`009UCB$W_BCNT and UCB$L_SVAPTE are written by IOC$INITIATE
X;
X; Outputs:
X;
X;`009R5 - UCB Address
X;
X;--
XPY$STARTIO::
X`009.ENABLE LSB
X 
X`009MOVL`009@UCB$L_SVAPTE(R5),-`009`009; Initialize buffer
X`009`009UCB$L_SVAPTE(R5)`009`009;  pointers
XPY_OUT_LOOP:
X;
X; Here R5 must point at the PY device UCB and not at
X;  the UCB of the associated TW device.
X;
X5$:`009TSTW`009UCB$W_BCNT(R5)`009`009`009; Any space left in rd packet
X`009BLEQ`00950$`009`009`009`009; No, Completed I/O
X;
X; Switch to terminal UCB
X;
X`009MOVL`009UCB$L_PY_XUCB(R5),R5`009`009; Set to TW ucb
X;
X; Look for next output in state tank
X;
X; Change Case statement to reflect V4 changes in routines - DEC 
X;
X10$:`009FFS`009#0,#6,UCB$W_TT_HOLD+1(R5),R3
X`009CASE`009R3,TYPE=B,<-`009`009`009; Dispatch
X`009`009PY_PREMPT,-`009`009`009; Send Prempt Characte - DEC 
X`009`009PY_STOP,-`009`009`009; Stop output
X`009`009PY_CHAR,-`009`009`009; Char in tank
X`009`009PY_BURST,-`009`009`009; Burst in progress
X`009`009>
X;
X; No Pending Data - Look for next character
X;
X`009BICB`009#UCB$M_INT, UCB$W_STS(R5)`009; Clear interrupt expected
X;
X; Call class driver for more output
X;
X`009JSB`009@UCB$L_TT_GETNXT(R5)`009; Get the next character
X`009CASEB`009UCB$B_TT_OUTYPE(R5),#-1,#1
X1$:`009.WORD`009PY_START_BURST-1$`009; Burst specified
X`009.WORD`009PY_DONE-1$`009`009; None
X`009BRW`009BUFFER_CHAR`009`009; Buffer the character
X;
X; Output queue exhausted
XPY_DONE:
X`009MOVL`009UCB$L_TW_XUCB(R5),R5`009; Switch UCBs to PY UCB
X`009BBC`009#UCB$V_BSY,-`009`009; If not BSY then ignore
X`009`009UCB$W_STS(R5),47$`009; the char
X`009MOVL`009UCB$L_IRP(R5),R3`009; Restore IRP
X`009CMPW`009IRP$W_BCNT(R3),-`009; Any characters moved
X`009`009UCB$W_BCNT(R5)
X`009BNEQ`00950$`009`009`009; Yes complete I/O
X47$:`009RSB
X;
X; read buffer exhausted
X;
X50$:`009MOVL`009UCB$L_IRP(R5),R3`009; Restore IRP
X`009MOVW`009#SS$_NORMAL,-`009`009; Set successful completetion
X`009`009IRP$L_IOST1(R3)
X`009SUBW`009UCB$W_BCNT(R5),-`009; Update byte count
X`009`009IRP$W_BCNT(R3)
X`009MOVW`009IRP$W_BCNT(R3),-`009; Set in status
X`009`009IRP$L_IOST1+2(R3)
X;
X; If we wanted to here we could set the second longword of the device status
X;
X`009CLRL`009IRP$L_IOST2(R3)`009`009; No status
X`009MOVQ`009IRP$L_IOST1(R3),R0`009; Load IOSB return values
X 
X`009JMP`009G^IOC$REQCOM
X;
X; Put the character into the read buffer
X;
XBUFFER_CHAR:
X`009MOVL`009UCB$L_TW_XUCB(R5),R5`009; Switch UCBs to PY UCB
X`009BBC`009#UCB$V_BSY,-
X`009`009UCB$W_STS(R5), 60$`009; If no PY IRP, ignore
X`009MOVB`009R3,@UCB$L_SVAPTE(R5)`009; Add character to buffer
X`009INCL`009UCB$L_SVAPTE(R5)`009; Bump pointer
X`009DECW`009UCB$W_BCNT(R5)`009`009; Show character added
X60$:`009BRW`009PY_OUT_LOOP`009`009; Go for another char
X;
X; Take care of Burst mode R5 must be TW UCB
X;
XPY_START_BURST:
X`009BISW`009#TTY$M_TANK_BURST,-`009; Signal burst active
X`009`009UCB$W_TT_HOLD(R5)
X;
X; Continue burst
X;
XPY_BURST:
X`009MOVL`009UCB$L_TW_XUCB(R5),R1`009; Save PY UCB in R1
X`009CLRL`009R3`009`009`009; Initialize output size
X`009CMPW`009UCB$W_TT_OUTLEN(R5),UCB$W_BCNT(R1)  ; Is buffer too small?
X`009BGTR`00961$`009`009`009; Yes
X`009MOVW`009UCB$W_TT_OUTLEN(R5),R3`009; Nope, so output all
X`009BRB`00962$
X61$:`009MOVW`009UCB$W_BCNT(R1),R3`009; Just output what we can
X 
X62$:`009PUSHR`009#^M<R0,R1,R2,R3,R4,R5>`009; MOVC3 destroys these registers
X`009MOVC3`009R3,@UCB$L_TT_OUTADR(R5),@UCB$L_SVAPTE(R1)
X`009`009`009`009`009; Transfer burst to the buffer
X`009POPR`009#^M<R0,R1,R2,R3,R4,R5>`009; Restore the registers
X 
X`009ADDL2`009R3,UCB$L_SVAPTE(R1)`009; Update output pointer
X`009SUBW2`009R3,UCB$W_BCNT(R1)`009; Update output count
X`009ADDL2`009R3,UCB$L_TT_OUTADR(R5)`009; Update input pointer
X`009SUBW2`009R3,UCB$W_TT_OUTLEN(R5)`009; Update input count
X`009BNEQ`00965$`009`009`009; Not the last character
X`009BICW`009#TTY$M_TANK_BURST,-
X`009`009UCB$W_TT_HOLD(R5)`009; Reset burst not active
X65$:`009MOVL`009UCB$L_TW_XUCB(R5),R5`009; Swicht UCBs to PY UCB
X`009BRW`009PY_OUT_LOOP
X;
X; Get a single char from tt and put in read buffer R5 = TW UCB
X;
XPY_CHAR:
X`009MOVB`009UCB$W_TT_HOLD(R5),R3`009; Get the next byte
X`009BICW`009#TTY$M_TANK_HOLD,-`009; Show tank empty
X`009`009UCB$W_TT_HOLD(R5)
X`009BRW`009BUFFER_CHAR
X;
X; Stop the output R5 = TW UCB
X;
X; Deleted PY_STOP2 routine and changed bit clear to byte operation - DEC 
X;
XPY_STOP:
X`009BICB`009#UCB$M_INT, -
X`009`009UCB$W_STS(R5)`009`009; Reset output active
X`009BRW`009PY_DONE`009`009`009; DON'T go for anymore
X`009`009`009`009`009; Or we'll get into an infinite loop
X;
X; Send Xon or Xoff characters, R5 = TW UCB
X;
X; Changed PY_XOFF and PY_XON to be PY_PREMPT - DEC 
X;
XPY_PREMPT:
X`009movb`009UCB$B_TT_PREMPT(r5), r3`009; Pick up the character
X`009BICW`009#TTY$M_TANK_PREMPT,-`009; Reset Xoff state
X`009`009UCB$W_TT_HOLD(R5)
X`009BRW`009BUFFER_CHAR
X`009.DISABLE LSB
X`012
X`009.SBTTL`009PY$SET - Set up ATTENTION AST's
X;++
X; PY$FDTSET - FUNCTION DECISION ROUTINE FOR SET MODE/CHAR FUNCTIONS
X;
X; FUNCTIONAL DESCRIPTION:
X;
X; This routine is the function decision routine for SET MODE/CHAR 
X; functions.
X;
X; P4(AP)`009Determines what AST get's setup.
X;`009`009`0091 -> XON AST
X;`009`009`0092 -> XOFF AST
X;`009`009`0093 -> SET_LINE AST
X;
X; INPUTS:
X;
X;`009R3 = I/O PACKET ADDRESS
X;`009R4 = PCB ADDRESS OF CURRENT PROCESS
X;`009R5 = UCB ADDRESS
X;`009R6 = CCB ADDRESS FOR ASSIGNED UNIT
X;`009AP = ADDRESS OF ARGUMENT LIST AT USER PARAMETERS
X;
X; OUTPUTS:
X;
X;`009The function is completed here by "EXE$FINISHIO".
X;
X; IMPLICIT OUTPUTS:
X;
X; `009R3,R5 ARE PRESERVED.
X;--
XPY$FDTSET:
X`009PUSHL`009R5
X`009MOVZWL`009#SS$_NOSUCHDEV,R0`009; Assume no TW device
X`009MOVL`009UCB$L_PY_XUCB(R5),R5`009; Switch to TW UCB
X`009BEQL`009SET_ABORT`009`009; No TW UCB exit
X`009CASE`009P4(AP),LIMIT=#1,TYPE=B,<- ; Figure out what to set
X`009`009SET_XON_AST,-`009`009;
X`009`009SET_XOFF_AST,-`009`009;
X`009`009SET_LINE_AST>`009`009;
XBAD_SET:
X`009MOVZWL`009#SS$_BADPARAM,R0`009; Failure reason
XSET_ABORT:
X`009POPL`009R5
X`009JMP`009G^EXE$ABORTIO
X
XSET_XON_AST:
X`009MOVAB`009UCB$L_TW_XON_AST(R5),R7`009; XON AST list head address
X`009BRB`009SET_CMN`009`009`009; Go to common code
X
XSET_XOFF_AST:
X`009MOVAB`009UCB$L_TW_XOFF_AST(R5),R7 ; XOFF AST list head address
X`009BRB`009SET_CMN`009`009`009; Go to common code
X
XSET_LINE_AST:
X`009MOVAB`009UCB$L_TW_SET_AST(R5),R7`009; SET_LINE AST list head address
X
XSET_CMN:
X`009POPL`009R5
X`009JSB`009G^COM$SETATTNAST`009; Insert into AST list
X`009JMP`009G^EXE$FINISHIOC`009`009; Complete request
X`012
X`009.SBTTL`009PY$FDTSENSEM - Sense mode routine
X;++
X; TTY$FDTSENSEM - SENSE MODE
X;
X; FUNCTIONAL DESCRIPTION:
X;
X; This routine passes the the current characteristics for SENSEMODE.
X; The buffer returned is a 8 or 12 bytes depending upon users request.
X;
X; INPUTS:
X;
X;`009R3 = I/O PACKET ADDRESS
X;`009R4 = CURRENT PCB ADDRESS
X;`009R5 = UCB ADDRESS
X;`009R6 = CCB ADDRESS
X;`009R7 = FUNCTION CODE
X;`009AP = ARG LIST FROM QIO
X;
X; OUTPUTS:
X;
X;`009CONTROL IS PASSED TO EXE$ABORTIO ON FAILURE
X;`009OR COMPLETED VIA EXE$FINISHIO
X;
X; STATUS RETURNS:
X;
X;`009SS$_NORMAL - SUCCESSFULL
X;`009SS$_ACCVIO - BUFFER NOT ACCESSIBLE
X;
X; NOTE:
X;`009The following code assumes that the DEVICE and FORK lock
X;`009for the TW device are the same.  If this changes then this
X;`009code is broken.
X;
X;--
XPY$FDTSENSEM::
X`009BSBW`009VERIFY_SENSE`009`009; VERIFY USER STORAGE
X`009PUSHR`009#^M<R5,R6,R7,R8,R10,R11>
X`009MOVL`009UCB$L_PY_XUCB(R5),R5`009; Switch to TW UCB
X`009BEQL`009PY$SENSE_ERR`009`009; Have UCB
X`009MOVL`009UCB$L_TT_LOGUCB(R5),R5`009; Switch to logical device is one exists
X`009BSBW`009GET_DCL`009`009`009; BUILD SPECIAL CHARACTERISTICS
X
X.IF`009DEFINED`009VMS_V4
X`009DSBINT`009UCB$B_DIPL(R5)
X.IF_FALSE
X`009DEVICELOCK LOCKADDR=UCB$L_DLCK(R5),- ; Lock out TW activity
X`009`009LOCKIPL=UCB$B_DIPL(R5),-; RAISE IPL
X`009`009SAVIPL=-(SP),`009-`009; SAVE CURRENT IPL
X`009`009PRESERVE=YES`009`009;
X.ENDC
X
X`009MOVL`009UCB$B_DEVCLASS(R5),R6`009; BUILD TYPE, AND BUFFER SIZE
X`009MOVL`009UCB$L_DEVDEPEND(R5),R7`009;RETURN 1ST CHARACTERISTICS LONGWORD
X`009BISL3`009R2,UCB$L_DEVDEPND2(R5),R8;AND 2ND LONGWORD (IF REQUESTED)
X`009MOVL`009UCB$W_TT_SPEED-2(R5),R10; RETURN SPEED
X`009MOVL`009UCB$B_TT_PARITY-2(R5),R11; RETURN PARITY INFO
X`009BICL`009#^XFF000000,R11`009`009; ZERO HIGH BYTE
X`009MOVW`009UCB$B_TT_CRFILL(R5),R11`009; AND CR/LF FILL
X
X.IF`009DEFINED`009VMS_V4
X`009ENBINT
X.IF_FALSE
X`009DEVICEUNLOCK LOCKADDR=UCB$L_DLCK(R5),- ; RELEASE INTERLOCK
X`009`009NEWIPL=(SP)+,`009-`009; RESTORE IPL
X`009`009CONDITION=RESTORE, -`009;
X`009`009PRESERVE=YES`009`009;
X.ENDC
X
X`009MOVL`009R6,(R1)`009`009`009; RETURN USER DATA
X`009MOVL`009R7,4(R1)`009`009;
X`009CMPB`009R0,#12`009`009`009; DID HE ASK FOR 2ND ?
X`009BLSS`00910$`009`009`009; NO
X`009MOVL`009R8,8(R1)`009`009;
X10$:`009MOVL`009R10,R0`009`009`009; RETURN IOSB DATA
X`009MOVL`009R11,R1`009`009`009;
X`009BRW`009CMN_EXIT`009`009; EXIT RETURNING R0,R1`009
X`012
X;`009If no TW device abort with SS$_NOSUCHDEV
XPY$SENSE_ERR:
X`009POPR`009#^M<R5,R6,R7,R8,R10,R11>
X`009MOVZWL`009#SS$_NOSUCHDEV,R0`009; Save error reason
X`009JMP`009G^EXE$ABORTIO`009`009; Abort request
X`012
X`009.SBTTL`009PY$FDTSENSEC - Sense char routine
X;++
X; TTY$FDTSENSEC - SENSE CHARACTERISTICS
X;
X; FUNCTIONAL DESCRIPTION:
X;
X; This routine passes the the permanent characteristics for SENSECHAR.
X; The buffer returned is a 8 or 12 bytes depending upon users request.
X;
X; INPUTS:
X;
X;`009R3 = I/O PACKET ADDRESS
X;`009R4 = CURRENT PCB ADDRESS
X;`009R5 = UCB ADDRESS
X;`009R6 = CCB ADDRESS
X;`009R7 = FUNCTION CODE
X;`009AP = ARG LIST FROM QIO
X;
X; OUTPUTS:
X;
X;`009CONTROL IS PASSED TO EXE$ABORTIO ON FAILURE
X;`009OR COMPLETED VIA EXE$FINISHIO
X;
X; STATUS RETURNS:
X;
X;`009SS$_NORMAL - SUCCESSFULL
X;`009SS$_ACCVIO - BUFFER NOT ACCESSIBLE
X;
X; NOTE:
X;`009The following code assumes that the DEVICE and FORK lock
X;`009for the TW device are the same.  If this changes then this
X;`009code is broken.
X;--
XPY$FDTSENSEC::`009`009`009`009; SENSE CHAR
X`009BSBW`009VERIFY_SENSE`009`009; VERIFY USER STORAGE
X`009PUSHR`009#^M<R5,R6,R7,R8,R10,R11>
X`009MOVL`009UCB$L_PY_XUCB(R5),R5`009; Switch to TW UCB
X`009BEQL`009PY$SENSE_ERR`009`009; Have UCB
X`009BSBW`009GET_DCL`009`009`009; BUILD SPECIAL CHARACTERISTICS
X
X.IF`009DEFINED`009VMS_V4
X`009DSBINT`009UCB$B_DIPL(R5)
X.IF_FALSE
X`009DEVICELOCK LOCKADDR=UCB$L_DLCK(R5),- ; Lock out TW activity
X`009`009LOCKIPL=UCB$B_DIPL(R5),-; RAISE IPL
X`009`009SAVIPL=-(SP)`009`009; SAVE CURRENT IPL
X.ENDC
X
X`009MOVL`009UCB$B_TT_DETYPE-1(R5),R6; BUILD TYPE, AND BUFFER SIZE
X`009MOVB`009#DC$_TERM,R6`009`009; BUILD DEVICE CLASS
X`009MOVL`009UCB$L_TT_DECHAR(R5),R7`009;RETURN 1ST CHARACTERISTICS LONGWORD
X`009BISL3`009R2,UCB$L_TT_DECHA1(R5),R8;AND 2ND LONGWORD (IF REQUESTED)
X`009MOVL`009UCB$W_TT_DESPEE-2(R5),R10; RETURN SPEED
X`009MOVL`009UCB$B_TT_DEPARI-2(R5),R11; RETURN PARITY INFO
X`009BICL`009#^XFF000000,R11`009`009; ZERO HIGH BYTE
X`009MOVW`009UCB$B_TT_DECRF(R5),R11`009; AND CR/LF FILL
X
X.IF`009DEFINED`009VMS_V4
X`009ENBINT
X.IF_FALSE
X`009DEVICEUNLOCK LOCKADDR=UCB$L_DLCK(R5),- ; RELEASE INTERLOCK
X`009`009NEWIPL=(SP)+,`009-`009; RESTORE IPL
X`009`009CONDITION=RESTORE`009;
X.ENDC
X
X`009MOVL`009R6,(R1)`009`009`009; RETURN USER DATA
X`009MOVL`009R7,4(R1)`009`009;
X`009CMPB`009R0,#12`009`009`009; DID HE ASK FOR 2ND ?
X`009BLSS`00910$`009`009`009; NO
X`009MOVL`009R8,8(R1)`009`009;
X10$:`009MOVL`009R10,R0`009`009`009; RETURN IOSB DATA
X`009MOVL`009R11,R1`009`009`009;
X`009BRW`009CMN_EXIT`009`009; EXIT RETURNING R0,R1`009
X`012
X;`009THIS ROUTINE BUILDS DCL PRIVATE CHARACTERISTICS
X
XGET_DCL:
X`009CLRL`009R2`009`009`009; INIT RETRUN ARGUMENT
X`009TSTL`009UCB$L_AMB(R5)`009`009; ANY ASSOCIATED MAILBOX?
X`009BEQL`00910$`009`009`009; NO
X`009BISL`009#TT2$M_DCL_MAILBX,R2`009; YES, SO BUILD CHARACTERISTIC
X10$:`009RSB
X`012
X;`009Common exit path for PY$FDTSENSEM and PY$FDTSENSEC
X
XCMN_EXIT:
X`009POPR`009#^M<R5,R6,R7,R8,R10,R11> ; RESTORE SCRATCH REGISTERS
X`009MOVW`009#SS$_NORMAL,R0
X`009JMP`009G^EXE$FINISHIO`009`009; COMPLETE REQUEST IOSB WORD 0,1
X`012
X;`009This routine verifies that the user buffer is accessable
X
XVERIFY_SENSE:`009`009`009`009;
X`009MOVL`009P1(AP),R1`009`009; ADDRESS USER BUFFER
X`009IFNOWRT`009#8,(R1),20$`009`009; BR IF NO ACCESS TO QUADWORD BUFFER
X`009MOVL`009#8,R0`009`009`009; INIT DEFAULT ARGUMENT SIZE
X`009CLRQ`009(R1)`009`009`009; INIT RETURN DATA
X`009MOVZWL`009P2(AP),R2`009`009; GET SIZE ARGUMENT
X`009CMPL`009R2,#12`009`009`009; ROOM FOR SECOND DEVDEPEND SPECIFIED?
X`009BLSSU`00910$`009`009`009; NO
X`009IFNOWRT`009#12,(R1),20$`009`009; CHECK IF WRITE ACCESS
-+-+-+-+-+ End of part 4 +-+-+-+-+-