.TITLE RTDRV - Remote Terminal Driver .IDENT /V01.01/ .ENABL LC ;+ ; Title: RTDRV - Remote Terminal Driver ; Version: 01.01 ; By: Greg Thompson (Informatics/PMI) ; Date: 3/5/78 ; ; Modified: ; ; 10/25/78 Greg Thompson Moved task aborting to BYE ; Change device name to RT ; ; This drivers job is to provide an MCR interface to ; the TELNET task which provides DECNET remote terminal ; support. This driver accept all the function codes ; that the normal TT driver accepts. In addition it ; accepts IO.CON, IO.DIS, IO.XMT, IO.RCV and IO.CTL as ; described below, which originate from the TELNET task. ; ; IO.XMT (31,0) - Provide a message buffer for a terminal ; function. (Returns error if terminal not ; in use. See IO.CON) ; ; IO.RCV (32,0) - Return I/O status block and read data ; from a terminal function. A function ; code of IO.CTL in the buffer indicates ; that the buffer is an unsolicited input ; line for MCR. ; ; IO.CON (33,0) - Same as IO.XMT except indicates that this ; is the first buffer for a remote terminal. ; (Returns error if terminal aready in use.) ; ; IO.DIS (34,0) - Disconnect from remote terminal. Flushes ; I/O and issues BYE command on terminal. ; ; IO.CTL (35,0) - Passes an unsolicited character to the remote ; terminal driver in first word of parameter list. ; ; (All sizes are in bytes.) ; ; The message buffers passed to the driver (IO.XMT and IO.CON) ; are to be set up in the following format: ; _______________________________ ; ! ! ! ; ! Function code ! Modifier ! 0 ; !_______________!_______________! ; ! ! ! ; !Sequence number! Size (bytes) ! 2 ; !_______________!_______________! ; ! ! ; ! VFC parameter ! 4 ; !_______________________________! ; ! ! ; ! Prompt size parameter ! 6 ; !_______________________________! ; ! ! ; ! First word of message buffer ! 10 ; !_______________________________! ; ! ! ; ; The messages returned (IO.RCV) are of the following format: ; _______________________________ ; ! ! ! ; ! Function code ! Modifier ! 0 ; !_______________!_______________! ; ! ! ! ; !Sequence number! Size (bytes) ! 2 ; !_______________!_______________! ; ! ! ; ! First word of I/O Status Block! 4 ; !_______________________________! ; ! ! ; ! Second word of I/O Status Blk ! 6 ; !_______________________________! ; ! ! ; ! First word of message buffer ! 10 ; !_______________________________! ; ! ! ;- .MCALL DEVDF$,PKTDF$ DEVDF$ PKTDF$ .PAGE .SBTTL Equates and Constants ; ; Equates ; R$$T11 = 8. ; Number of remote terminals supported LD$RT = 0 ; This driver is loadable MAXCNT = 67.*2. ; Max buffer size (bytes) for terminal function M$$CRI = 80. ; Max # of characters in a MCR command buffer IGNCW2 = 160730 ; Bits not to touch in U.CW2 ; ; UCB queues and status words ; ; S.LHD is chain of packets waiting for an IO.XMT U.XMT = U.CNT+4 ; Chain of IO.XMT buffers waiting for a packet U.ACT = U.CNT+6 ; Chain of active packets waiting for IO.RCV U.SEQ = U.CNT+10 ; Current sequence number U.KIL = U.CNT+11 ; Issue IO.KIL or other function flags U.AST = U.CNT+12 ; Unsolicited input AST address ; ; U.KIL bits ; K.KIL = 200 ; Send IO.KIL to ...CON task K.ATA = 100 ; Send IO.ATA to ...CON task K.DET = 40 ; Send IO.DET to ...CON task ; CONMSG: .ASCIZ <15><12>/Connected to RT/ ; Connected message CONSIZ = .+6-1-CONMSG ; Length of connected message .EVEN ; TELTCB: .WORD 0 ; Telnet's TCB address ; ; Offsets into IO.CON, IO.XMT and IO.RCV buffers ; N.BUF = 0 ; Offset to buffer N.FUN = N.BUF ; Offset to function code in buffer N.SIZ = N.BUF+2 ; Offset to size parameter in buffer N.SEQ = N.BUF+3 ; Offset to sequence number N.VFC = N.BUF+4 ; Offset to VFC parameter N.SB1 = N.BUF+4 ; Offset to 1st word of returned IOSB N.PSZ = N.BUF+6 ; Offset to prompt size parameter N.SB2 = N.BUF+6 ; Offset to 2nd word of returned IOSB N.MES = N.BUF+10 ; Offset to message (TI) buffer .PAGE .SBTTL Driver code ; ; Driver dispatch table ; $RTTBL:: .WORD RTINI ; Device initiator entry point .WORD RTCAN ; Cancel I/O operation entry point .WORD RTTMO ; Device timeout entry point .WORD RTPWF ; Powerfail entry point ;+ ; **- RTINI - RT device service. ; ; Inputs: ; ; R1 = Packet address ; R4 = SCB address ; R5 = UCB address ;- RTINI: MOV R1,R3 ; Copy address of I/O packet CLR (R1) ; Clear packet link word CMPB #IO.XMT/400,I.FCN+1(R3) ; Is it an TELNET function? BHI 10$ ; No, it is a terminal function MOV I.TCB(R3),TELTCB ; Save TELNET's TCB address BR NOQUE ; Yes, bypass queueing it ; 10$: CMPB #IO.ATT/400,I.FCN+1(R3) ; Is it IO.ATT? BEQ 12$ ; Yes, make it go through $GTPKT CMPB #IO.DET/400,I.FCN+1(R3) ; Is it IO.DET? BNE 15$ ; No, check for write break through 12$: MOVB #377,I.PRI(R3) ; Force it to maximum priority BICB #US.BSY,U.STS(R5) ; Clear busys to allow it to get through CLRB S.STS(R4) ; (could still allow something else through) BR QUEIT2 ; Queue it for $GTPKT ; 15$: CMPB #IO.WLB/400,I.FCN+1(R3) ; Is it a IO.WLB? BNE 20$ ; No BITB #TF.WBT,I.FCN(R3) ; Write break through? BEQ QUEIT ; No, just queue it TST U.XMT(R5) ; Are there any IO.XMT packets for it? BEQ QUEIT ; No, oh well just queue it BR NOQUE ; Yes, process it now ; 20$: CMPB #IO.RPR/400,I.FCN+1(R3) ; Is it read after prompt? BNE QUEIT ; No, just queue it MOV I.PRM+10(R3),R0 ; Get virtual address of prompt buffer MOV R5,-(SP) ; Save UCB address .IF DF A$$CHK!M$$MGE MOV I.PRM+12(R3),R1 ; Get length of buffer BEQ ILLBF ; Must prompt with at least 1 character CMP R1,#MAXCNT ; Make sure it isnt to big BGT ILLBF ; Yes, issue illegal buffer error CALL $ACHKB ; Address check buffer BCC BUFOK ; If cc okay ILLBF: MOV #IE.SPC&377,R0 ; Return illegal buffer status MOV (SP)+,R5 ; Restore UCB address BR IOALT2 ; Packet address still in R3 .ENDC BUFOK: CALL $RELOC ; Relocate buffer address MOV R1,I.PRM+10(R3) ; Set relocation bias of buffer MOV R3,R1 ; Copy back I/O packet address MOV R3,R4 ; Create pointer into I/O packet ADD #I.PRM+14,R4 ; Point into I/O packet MOV (R4)+,(R4) ; Move vertical format ctrl down CMP -(R4),-(R4) ; Point at prompt buffer size MOV (R4)+,(R4) ; Move buffer size down too MOV R2,-(R4) ; Set displacement addr of buffer MOV (SP)+,R5 ; Restore UCB address MOV U.SCB(R5),R4 ; Restore SCB address ; QUEIT: BITB #US.DSB,U.STS(R5) ; Unit disabled? BNE IEDNR2 ; Yes, return "IE.DNR" QUEIT2: MOV R4,R0 ; Queue packet CALL $QINSP ; Queue I/O packet in device queue GTPKT: CALL $GTPKT ; Get an I/O packet from queue BCC NOQUE ; Got an I/O packet RETURN ; Just return if no packet ; IEDNR2: JMP IEDNR ; Jump to IEDNR ; NOQUE: MOV R1,R3 ; Also put I/O packet addr in R3 MOV #FTABL,R2 ; Dispatch on function code 10$: INC R2 ; Point at function code byte CMPB I.FCN+1(R1),(R2)+ ; Look for a match BNE 20$ ; No match, check for end of table JMP @(R2)+ ; Jump to routine to handle it ; 20$: TST (R2)+ ; Is that the end of the table? BNE 10$ ; No, try next entry MOV #IE.IFC&377,R0 ; Yes, invalid function code IOALT2: JMP IOALT ; Finish up I/O function ; ; Function code dispatch table ; FTABL: .WORD IO.XMT,XMT ; .WORD IO.RCV,RCV ; .WORD IO.WVB,TERM ; .WORD IO.WLB,TERM ; IO.CCO, IO.WAL, IO.WBT .WORD IO.RVB,TERM ; .WORD IO.RLB,TERM ; IO.RAL, IO.RNE, IO.RST .WORD IO.RPR,TERM ; .WORD IO.CTL,CTL ; .WORD IO.ATT,ATT ; IO.ATA .WORD IO.DET,DET ; .WORD IO.GTS,TERM ; SF.GMC, SF.SMC .WORD IO.CON,CON ; .WORD IO.DIS,DIS ; .WORD 0,0 ; End of table ; ; Unsolicited input for task received (IO.CTL) ; CTL: BITB #US.DSB,U.STS(R5) ; Is the terminal connected? BNE 10$ ; No, just ignore function TST U.AST(R5) ; But is there a task to take it? BNE 20$ ; Yes, give it to the task BISB #K.DET,U.KIL(R5) ; No, tell CON task there is no task CALL SNDKIL ; Send the message 10$: JMP IOSUC ; Complete the IO.CTL ; 20$: MOV R3,-(SP) ; Save addr of IO.CTL packet MOV R5,-(SP) ; Save UCB address MOV #6*2,R1 ; Length of AST block CALL $ALOCB ; Allocate an AST block BCS 30$ ; If CS didn't get one TST (R0)+ ; Skip over link word MOV R1,(R0)+ ; Save size of AST block MOV R0,R1 ; Save beginning addr of AST block CMP -(R1),-(R1) ; MOV #10*2,(R0)+ ; Save bytes to allocate on stack MOV U.AST(R5),(R0)+ ; Save AST trap address MOV #1,(R0)+ ; Save number of AST parameters MOV I.PRM(R3),(R0) ; Unsolicited input char is AST param MOV U.ATT(R5),R0 ; Get address of task's TCB BEQ 30$ ; Never can be too safe CALL $QASTT ; Insert AST in AST queue and call $SETCR 30$: MOV (SP)+,R5 ; Restore UCB address MOV (SP)+,R3 ; Restore IO.CTL packet address BR 10$ ; Return IS.SUC on it ; ; Request for disconnect (IO.DIS) from TELNET ; DIS: BITB #US.DSB,U.STS(R5) ; Is it already disconnected? BNE IOSUC ; Yes, just ignore disconnect then CALL CLEAN ; Flush the UCB out BR IOSUC ; Return IS.SUC on it ; ; Request for connection (IO.CON) from TELNET ; CON: BITB #US.DSB,U.STS(R5) ; Is the terminal free? BNE 10$ ; Yes, we can use it then MOV #IE.RSU&377,R0 ; Resource (terminal) in use error BR IOALT ; Return error on IO.CON ; 10$: CLR U.XMT(R5) ; Make sure all chains are inited CLR U.ACT(R5) ; CLR U.SEQ(R5) ; Init sequence number counter, kill flag CLR U.AST(R5) ; No unsolicited input AST CLR S.LHD(R4) ; MOV R4,S.LHD+2(R4) ; BICB #US.DSB,U.STS(R5) ; Indicate terminal active BISB #US.BSY,U.STS(R5) ; But indicate no IO.XMT queued INCB S.STS(R4) ; Set controller busy BIC #IGNCW2,I.PRM+6(R3) ; Clear bits to ignore in U.CW2 setting BIC #^C,U.CW2(R5) ; Clear old bit settings BIS I.PRM+6(R3),U.CW2(R5) ; Set desired U.CW2 bits BIC #U2.SLV,U.CW2(R5) ; Un-slave terminal initially MOV KISAR6,-(SP) ; Save APR6 on stack MOV I.PRM(R3),KISAR6 ; Map buffer to kernal APR6 MOV I.PRM+2(R3),R0 ; Displacement in block -> R0 MOV #IO.WLB,N.FUN(R0) ; Set function code to write MOV #CONSIZ,N.SIZ(R0) ; Set length and set seq. # to 0 CLR N.VFC(R0) ; Set format parameter MOV R0,R2 ; Copy buffer pointer ADD #N.MES,R2 ; Make R2 point at message buffer MOV #CONMSG,R1 ; Point R1 at string 20$: MOVB (R1)+,(R2)+ ; Move message BNE 20$ ; Move entire string DEC R2 ; Back up over 0 byte MOV U.UNIT(R5),R1 ; Get unit number ASR R1 ; Shift right 3 bits ASR R1 ; ASR R1 ; BIC #177770,R1 ; Clear high order bits TST R1 ; Is it a zero? BNE 30$ ; No, output it DECB N.SIZ(R0) ; Yes, suppress the leading zero BR 40$ ; But output low order digit ; 30$: ADD #'0,R1 ; Make it a character MOVB R1,(R2)+ ; Output first digit of unit number 40$: MOV U.UNIT(R5),R1 ; Now get second digit BIC #177770,R1 ; Clear high order bits ADD #'0,R1 ; Make it a character MOVB R1,(R2)+ ; Output second digit of unit number MOVB #':,(R2)+ ; Add a colon MOVB #15,(R2)+ ; Next is CR MOVB #12,(R2)+ ; LF MOVB #'>,(R2)+ ; Final char is ">" prompt MOV (SP)+,KISAR6 ; Restore mapping IOSUC: MOV #IS.SUC&377,R0 ; Return connection message BR IOALT ; to user ; ; Received a buffer (IO.XMT) from TELNET ; XMT: BITB #US.DSB,U.STS(R5) ; Is terminal connected? BEQ XMTQUE ; Yes, if bit off IEDNR: MOV #IE.DNR&377,R0 ; No, indicate error IOALT: CLR R1 ; Zero second word IOFIN: CALL $IOFIN ; Finish up I/O operation IONXT: BITB #US.DSB,U.STS(R5) ; Is the terminal disabled? BNE 10$ ; Yes, clear busys TST U.XMT(R5) ; Is there a IO.XMT queued? BEQ GTPKT2 ; No, don't clear busys 10$: MOV U.SCB(R5),R4 ; Restore SCB address BICB #US.BSY,U.STS(R5) ; and clear busys CLRB S.STS(R4) ; so another packet can be sent GTPKT2: JMP GTPKT ; And try again ; XMTQUE: MOV U.XMT(R5),(R3) ; Queue the IO.XMT MOV R3,U.XMT(R5) ; TSTB U.KIL(R5) ; Should we issue something? BEQ IONXT ; No, go check for more work CALL SNDKIL ; Yes, send an IO.KIL BR IONXT ; Check for more work ; ; Received results (IO.RCV) from TELNET ; RCV: BITB #US.DSB,U.STS(R5) ; Is terminal connected? BNE IEDNR ; IE.DNR error if not MOV KISAR6,-(SP) ; Save APR6 on stack MOV I.PRM(R3),KISAR6 ; Map IO.RCV buffer to APR6 MOV I.PRM+2(R3),R2 ; Get displacement into block CMP #IO.CTL,N.FUN(R2) ; Is it MCR input? BEQ MCRIN ; Yes, queue it to MCR TSTB N.SEQ(R2) ; Is sequence number zero? BEQ 60$ ; Yes, there is no packet for it then MOV R5,R1 ; Set R1 -> U.ACT(R5) ADD #U.ACT,R1 ; Look for original packet in active queue 10$: MOV R1,R0 ; Save address of last packet MOV (R0),R1 ; Is there another packet on queue? BEQ 60$ ; Ignore IO.RCV if we can't find it CMP I.FCN(R1),N.FUN(R2) ; Does function match? BNE 10$ ; No, try next one in queue CMPB I.PRM+16(R1),N.SEQ(R2) ; Does sequence number match? BNE 10$ ; No, try next packet ; ; Found the I/O packet for this IO.RCV message ; MOV (R1),(R0) ; Dequeue packet from U.ACT queue CMPB #IO.WLB/400,I.FCN+1(R1) ; Was it a Write Logical Block? BEQ 20$ ; Yes, check for segmented write CMPB #IO.WVB/400,I.FCN+1(R1) ; Write virtual Block? BNE 30$ ; No, keep checking 20$: BITB #200,I.PRM+16(R1) ; Are we sending it in segments? BEQ 50$ ; No, just complete it ; ; Process buffers that were broken in segments in-order to send them ; ADD #MAXCNT,I.PRM+2(R1) ; Yes, bump pointer to next segment SUB #MAXCNT,I.PRM+4(R1) ; Decrement for count just sent BLE 50$ ; Complete now if nothing left MOV (SP)+,KISAR6 ; Restore mapping MOV R1,-(SP) ; Save addr of terminal packet MOV #IS.SUC&377,R0 ; Complete the IO.RCV CLR R1 ; CALL $IOFIN ; $IOFIN the IO.RCV MOV (SP)+,R1 ; Restore terminal I/O packet address MOV U.SCB(R5),R4 ; Restore R4 -> SCB also JMP TERM ; Go process it again ; ; See if we need to copy anything back into the users buffer ; 30$: CMP #SF.SMC,I.FCN(R1) ; Set Multiple Charistics? BEQ 50$ ; Yes, nothing to copy back MOVB N.SIZ(R2),R0 ; Get return buffer size BIC #177400,R0 ; Clear high byte BEQ 50$ ; If zero, no data to copy ; ; Copy return data to users buffer ; ; Enter with: ; ; R0 = Byte count for move ; R1 = Destination terminal I/O packet ; R2 = Source buffer pointer ; R3 = Source IO.RCV I/O packet ; MOV R1,-(SP) ; Save needed registers MOV R2,-(SP) ; MOV R3,-(SP) ; MOV R5,-(SP) ; MOV UISAR5,-(SP) ; Save User mapping registers MOV UISAR6,-(SP) ; so we can use them MOV KISAR5,UISAR5 ; Save our mapping MOV KISAR6,UISAR6 ; MOV R1,R5 ; Copy dest I/O packet addr to R5 MOV I.PRM(R3),R1 ; Put source APR5 bias in R1 ADD #N.MES,R2 ; Point R2 at return data (source disp) BIC #160000,R2 ; Change R2 to map through APR5 BIS #120000,R2 ; MOV I.PRM(R5),R3 ; Put dest APR6 bias in R3 MOV I.PRM+2(R5),R4 ; Put dest displacement in R4 CALL $BLXIO ; Transfer the data MOV (SP)+,UISAR6 ; Restore User mapping MOV (SP)+,UISAR5 ; MOV (SP)+,R5 ; Restore registers MOV (SP)+,R3 ; MOV (SP)+,R2 ; MOV (SP)+,R1 ; ; ; Call $IOFIN on the terminal I/O packet ; 50$: MOV R3,-(SP) ; Save address of IO.RCV packet on stack MOV R1,R3 ; Put terminal packet addr in R3 MOV N.SB2(R2),R1 ; Get second I/O status block word MOV N.SB1(R2),R0 ; Get first I/O status block word CMPB #IE.ABO,R0 ; Was it aborted? (IO.KIL) BNE 55$ ; No, all is normal CLR I.AST(R3) ; Yes, don't do its AST on an abort 55$: CALL $IOFIN ; Complete its I/O MOV (SP)+,R3 ; Retrieve addr of IO.RCV packet 60$: MOV (SP)+,KISAR6 ; Restore mapping IOSUC2: JMP IOSUC ; Return IS.SUC on IO.RCV ; ; Unsolicited input for MCR ; ; R2 -> IO.RCV message buffer ; R3 -> IO.RCV I/O packet ; R5 -> UCB ; MCRIN: TST U.AST(R5) ; But is there a task that wants it BEQ 10$ ; No BISB #K.ATA,U.KIL(R5) ; Yes, remind CON of the fact CALL SNDKIL ; Send the message 10$: MOV R3,-(SP) ; Save IO.CTL packet addr on stack MOV R5,-(SP) ; Save UCB address on stack MOV #M$$CRB,R1 ; Set length of MCR buffer CALL $ALOCB ; Allocate one BCS 50$ ; Ignore line if no buffer space MOV R0,R4 ; Copy address of buffer CLR (R4)+ ; Clear link word MOV R5,(R4)+ ; Put UCB address in buffer MOVB #15,(R4) ; Make sure there is atleast a CR MOV I.PRM+2(R3),R2 ; Restore message buffer pointer MOV N.SB2(R2),R1 ; Get length BEQ 40$ ; Nothing to move CMP #M$$CRI,R1 ; Is length to big? BHIS 20$ ; No MOV #M$$CRI,R1 ; Yes, limit it 20$: ADD #N.MES,R2 ; Point R2 at MCR command line 30$: MOVB (R2)+,(R4)+ ; Move a byte DEC R1 ; Is that all? BNE 30$ ; No, move another byte 40$: MOV R0,R1 ; Addr buffer -> R1 for $QMCRL CALL $QMCRL ; Queue it to MCR 50$: MOV (SP)+,R5 ; Restore R5 MOV (SP)+,R3 ; Restore R3 MOV (SP)+,KISAR6 ; Restore mapping BR IOSUC2 ; Return IS.SUC on IO.RCV packet ; ; Attach (IO.ATT) received ; ATT: BITB #TF.AST,I.FCN(R1) ; Attach with unsol. input AST? BEQ IOSUC2 ; No, just return successful MOV I.PRM(R1),U.AST(R5) ; Yes, save AST address BISB #K.ATA,U.KIL(R5) ; Tell CON we want unsolicited input CALL SNDKIL ; Send the message BR IOSUC2 ; Now complete the IO.ATT ; ; Detach (IO.DET) received ; DET: TST U.AST(R5) ; Did it get unsolicited input? BEQ IOSUC2 ; No, nothing to do then CLR U.AST(R5) ; Yes, clear the unsol. input AST BISB #K.DET,U.KIL(R5) ; Tell CON we no longer want the input CALL SNDKIL ; Send the message BR IOSUC2 ; Now complete the IO.DET ; ; Terminal function received ; TERM: MOV U.XMT(R5),R3 ; R3 -> IO.XMT packet BNE 10$ ; Got an IO.XMT packet MOV R4,R0 ; Woops, queue it for later use CALL $QINSF ; Add it to end of S.LHD queue JMP GTPKT ; Go do something else ; ; Send a terminal function in a IO.XMT buffer ; ; R1 -> terminal I/O packet to send ; R3 -> IO.XMT I/O packet ; R4 -> SCB ; R5 -> UCB ; ; First check for acceptable buffer size ; 10$: MOV I.PRM+4(R1),R0 ; Get length parameter BMI 20$ ; Error if count negative CMP R0,#MAXCNT ; See if its within limits BLE 30$ ; Yes, accept packet CMPB #IO.WLB/400,I.FCN+1(R1) ; Is it a write logical? BEQ 15$ ; Yes CMPB #IO.WVB/400,I.FCN+1(R1) ; Is it a write virtual? BNE 20$ ; No, can't handle it then 15$: TST I.PRM+6(R1) ; No VFC? BEQ 30$ ; Yes, we can break it up CMP #40,I.PRM+6(R1) ; Just normal VFC? BEQ 30$ ; Yes, we can handle it too CMP #41,I.PRM+6(R1) ; Our special VFC? BEQ 30$ ; Yes, we are hacking it 20$: MOV R1,R3 ; Buffer size in packet illegal MOV #IE.SPC&377,R0 ; Return illegal buffer status JMP IOALT ; Cancel terminal request ; ; Queue terminal packet on U.ACT queue ; 30$: MOV (R3),U.XMT(R5) ; Unqueue the IO.XMT packet 40$: INCB U.SEQ(R5) ; Select next sequence number BICB #200,U.SEQ(R5) ; Clear segmented buffer flag BEQ 40$ ; Zero not allowed (used by IO.CON) MOV U.ACT(R5),(R1) ; Queue packet on U.ACT queue MOV R1,U.ACT(R5) ; ; ; Fill in IO.XMT packet ; MOV KISAR6,-(SP) ; Save APR6 mapping MOV I.PRM(R3),KISAR6 ; Map IO.XMT buffer to kernal APR6 MOV I.PRM+2(R3),R4 ; Displacement in R4 MOV I.FCN(R1),N.FUN(R4) ; Put function code in buffer MOVB U.SEQ(R5),N.SEQ(R4) ; Put sequence number in buffer MOV I.PRM+6(R1),N.VFC(R4) ; Put VFC code in packet CLR N.PSZ(R4) ; Init prompt size param CMP R0,#MAXCNT ; See if it needs to be broken up BLE 43$ ; No, its ok MOV #MAXCNT,R0 ; Yes, Send only MAXCNT at a time BISB #200,N.SEQ(R4) ; Set segmented flag CMP #40,I.PRM+6(R1) ; Is it normal VFC? BNE 41$ ; No MOV #41,I.PRM+6(R1) ; Yes, change to indicate 44 sent MOV #44,N.VFC(R4) ; 40 prompts are changed to 44,0,...,0,53 sequences BR 45$ ; Continue 41$: CMP #41,I.PRM+6(R1) ; Normal but already sent 44? BNE 45$ ; No, use what we were given CLR N.VFC(R4) ; Yes, send intermediate lines with no VFC BR 45$ ; Continue 43$: CMP #41,I.PRM+6(R1) ; Last part of a segmented VFC 40 message? BNE 45$ ; No MOV #53,N.VFC(R4) ; Yes, send last line with VFC of 53 45$: MOVB R0,N.SIZ(R4) ; Put buffer size in buffer ; ; Now depending on the function code ... ; CMPB #IO.RPR/400,I.FCN+1(R1) ; Read after prompt? BNE 50$ ; No MOV I.PRM+16(R1),N.VFC(R4) ; Yes, set VFC code in packet MOVB N.SEQ(R4),I.PRM+16(R1) ; Put sequence number in packet MOV I.PRM+14(R1),N.PSZ(R4) ; Set prompt size BEQ 100$ ; Nothing to move MOV I.PRM+14(R1),R0 ; Also put length in R0 BR 60$ ; Move prompt to buffer ; 50$: MOVB N.SEQ(R4),I.PRM+16(R1) ; Put sequence number in packet CMPB #IO.RLB/400,I.FCN+1(R1) ; Is it a read logical? BEQ 100$ ; Yes, all set CMPB #IO.RVB/400,I.FCN+1(R1) ; Is it a read virtual? BEQ 100$ ; Yes, all set CMP #IO.GTS,I.FCN(R1) ; Is it get terminal support? BEQ 100$ ; Yes, all set TST R0 ; Any bytes to move? BEQ 100$ ; No, all set ; ; Move data into IO.XMT buffer ; ; Enter with: ; ; R0 = Byte count for move ; R1 = Source terminal I/O packet ; R3 = Destination IO.XMT I/O packet ; R4 = Destination buffer pointer ; 60$: MOV R1,-(SP) ; Save needed registers MOV R3,-(SP) ; MOV R4,-(SP) ; MOV R5,-(SP) ; MOV UISAR5,-(SP) ; Save User mapping registers MOV UISAR6,-(SP) ; so we can use them MOV KISAR5,UISAR5 ; Save our mapping where $BLXIO MOV KISAR6,UISAR6 ; restores them from MOV R1,R5 ; Copy source I/O packet addr to R5 MOV I.PRM(R5),R1 ; Put source bias in R1 MOV I.PRM+2(R5),R2 ; Put source displacement in R2 CMPB #IO.RPR/400,I.FCN+1(R5) ; Is it Read after prompt? BNE 70$ ; No MOV I.PRM+10(R5),R1 ; Yes, map to prompt buffer instead MOV I.PRM+12(R5),R2 ; Point R2 at prompt string 70$: BIC #160000,R2 ; Change R2 to map through APR5 BIS #120000,R2 ; MOV I.PRM(R3),R3 ; Put dest APR6 bias in R3 ADD #N.MES,R4 ; Point R4 at destination buffer CALL $BLXIO ; Transfer the data MOV (SP)+,UISAR6 ; Restore User mapping MOV (SP)+,UISAR5 ; MOV (SP)+,R5 ; Restore registers MOV (SP)+,R4 ; MOV (SP)+,R3 ; MOV (SP)+,R1 ; ; ; Check for MCR prompt ; CMP $MCRPT,I.TCB(R1) ; Did it come from MCR? BNE 100$ ; No, continue on CMP #IO.WLB,I.FCN(R1) ; Is it a Write Logical Block? BNE 100$ ; No, continue on CMP #3,I.PRM+4(R1) ; String length = 3? BNE 80$ ; No CMPB #15,N.MES(R4) ; Is first character <15>? BNE 100$ ; No CMPB #12,N.MES+1(R4) ; Second character <12>? BNE 100$ ; No CMPB #'>,N.MES+2(R4) ; Third character ">" ? BNE 100$ ; No BR 90$ ; Yes, it is a MCR prompt then ; 80$: CMP #7,I.PRM+4(R1) ; String length = 7? BNE 100$ ; No CMPB #15,N.MES+2(R4) ; Is third character <15>? BNE 100$ ; No CMPB #12,N.MES+3(R4) ; Fourth character <12>? BNE 100$ ; No CMPB #'>,N.MES+4(R4) ; Fifth character ">" ? BNE 100$ ; No ; ; It is a MCR prompt, complete it now ; 90$: BISB #TF.CCO,N.FUN(R4) ; Make it cancel ctrl-o CLRB N.SEQ(R4) ; Clear sequence # because we are going CLRB I.PRM+16(R1) ; complete it now MOV (R1),U.ACT(R5) ; Yes, unchain us from U.ACT queue MOV R3,-(SP) ; Save IO.XMT packet address MOV R1,R3 ; Issue IS.SUC on MCRs IO.WVB MOV #IS.SUC&377,R0 ; So we don't hang MCR up CLR R1 ; CALL $IOFIN ; Complete MCRs I/O MOV (SP)+,R3 ; Restore IO.XMT ptr ; 100$: MOV (SP)+,KISAR6 ; Restore mapping JMP IOSUC ; IS.SUC on IO.XMT to start it off ;+ ; **- RTTMO - Timeouts ; ; Inputs: ; ; R0 = Address of active I/O packet ; R1 = Address of TCB of current task ; R3 = Controller index ; R4 = SCB address ; R5 = UCB address ;- RTTMO:: RETURN ; No timeouts handled here ;+ ; **- RTPWF - Startup (powerfail) ; ; Inputs: ; ; R3 = Controller index ; R4 = SCB address ; R5 = UCB address ;- RTPWF:: CALLR CLEAN ; Clean out the specifed UCB ; ; Clean out a UCB, enter with R5 -> UCB ; Flush U.XMT, U.ACT queues, turn on US.DSB ; CLEAN: MOV R3,-(SP) ; Save R3 on stack ; ; Flush U.XMT queue ; MOV R5,R2 ; Set ptr to U.XMT queue ADD #U.XMT,R2 ; 10$: MOV (R2),R3 ; Is there a packet queued? BEQ 20$ ; No MOV (R3),(R2) ; Yes, unchain it CALL ABORT ; Abort it BR 10$ ; Flush next packet ; ; Flush U.ACT queue ; 20$: MOV R5,R2 ; Set ptr to U.ACT queue ADD #U.ACT,R2 ; 30$: MOV (R2),R3 ; Is there a packet queued? BEQ 40$ ; No MOV (R3),(R2) ; Yes, unchain it CALL ABORT ; Abort it BR 30$ ; Flush next packet ; ; Deattach any task attached to terminal ; 40$: CLR U.ATT(R5) ; Insure it is detached ; ; Disable and un-busy terminal ; BITB #US.DSB,U.STS(R5) ; Is terminal already disabled? BNE 80$ ; Yes, so don't re-issue bye, etc. BISB #US.DSB,U.STS(R5) ; Disable terminal now MOV U.SCB(R5),R4 ; Get SCB address BICB #US.BSY,U.STS(R5) ; Clear busy for UCB CLRB S.STS(R4) ; Clear busy in SCB ; ; Issue BYE if terminal logged in ; BIT #FE.MUP,$FMASK ; System have multi-user stuff? BEQ 80$ ; No, so don't issue bye BIT #U2.LOG,U.CW2(R5) ; Is terminal logged in? BNE 80$ ; Branch if not MOV R5,-(SP) ; Save UCB address too MOV #M$$CRB,R1 ; Get size of MCR command line CALL $ALOCB ; Allocate MCR command line BCS 70$ ; If CS allocation failure MOV R0,R1 ; Copy ptr to buffer TST (R0)+ ; Point to UCB word MOV R5,(R0)+ ; Pass UCB pointer MOV #"BY,(R0)+ ; Stick in BYE command MOV (PC)+,(R0)+ ; .ASCII /E/<15> ; CALL $QMCRL ; Queue the command 70$: MOV (SP)+,R5 ; Restore UCB address 80$: MOV (SP)+,R3 ; Restore R3 RETURN ; Return to caller ; ; Issue an IE.ABO on a packet ; ABORT: MOV R1,-(SP) ; Save R1 (TCB) address MOV R2,-(SP) ; Save R2 around $IOFIN CMP I.TCB(R3),TELTCB ; Is it a TELNET task packet BEQ 10$ ; Yes, do ASTs on IE.ABOs for telnet CLR I.AST(R3) ; Don't do AST on abort 10$: MOV #IE.ABO&377,R0 ; Issue abort on packet CLR R1 ; Zero second word CALL $IOFIN ; MOV (SP)+,R2 ; Restore R2 MOV (SP)+,R1 ; Restore R1 RETURN ; Return to caller ;+ ; **-RTCAN - Cancel I/O ; ; Inputs: ; ; R1 = TCB address ; R4 = SCB address ; R5 = UCB address ;- RTCAN:: CMP R1,TELTCB ; Is it TELNET issuing the IO.KIL? BNE 10$ ; No, just some normal task CALL CLEAN ; Flush the UCB 5$: RETURN ; Done! ; 10$: BITB #US.DSB,U.STS(R5) ; Is the terminal active? BNE 20$ ; No, so dont queue up an IO.KIL BISB #K.KIL,U.KIL(R5) ; Set issue kill next chance flag CALL SNDKIL ; Send the IO.KIL 20$: MOV R5,R2 ; Set up last ptr ADD #U.ACT,R2 ; Point R2 at chain ptr 30$: MOV (R2),R3 ; Is there an active packet queued? BEQ 5$ ; No CMP R1,I.TCB(R3) ; Does this task own it? BEQ 40$ ; Yes, abort it MOV R3,R2 ; Move to next entry BR 30$ ; ; ; Return IE.ABO on canceled active packet ; 40$: MOV (R3),(R2) ; Unchain us from U.ACT chain CALL ABORT ; Abort packet BR 30$ ; Check next entry on chain ; ; Send an IO.KIL or other function to CON in an IO.XMT packet ; ; Enter with: (R1, R2, R3 and R5 unchanged) ; ; R5 = UCB address ; SNDKIL: MOV R1,-(SP) ; Save R1 MOV R2,-(SP) ; Save R2 MOV R3,-(SP) ; Save R3 MOV U.XMT(R5),R3 ; Get an IO.XMT packet BEQ 40$ ; No IO.XMT packet on queue, do it later MOV (R3),U.XMT(R5) ; Un-queue packet MOV KISAR6,-(SP) ; Save current mapping MOV I.PRM(R3),KISAR6 ; Map IO.XMT buffer MOV I.PRM+2(R3),R0 ; Set ptr to buffer in R0 BITB #K.KIL,U.KIL(R5) ; Should send IO.KIL? BEQ 10$ ; No BICB #K.KIL,U.KIL(R5) ; Yes, clear the bit MOV #IO.KIL,N.FUN(R0) ; Set function code BR 30$ ; Continue ; 10$: BITB #K.ATA,U.KIL(R5) ; Send a IO.ATA? BEQ 20$ ; No BICB #K.ATA,U.KIL(R5) ; Yes, clear the bit MOV #IO.ATA,N.FUN(R0) ; Set function code BR 30$ ; Continue ; 20$: BICB #K.DET,U.KIL(R5) ; Must be send a IO.DET MOV #IO.DET,N.FUN(R0) ; Set function code 30$: CLR N.SIZ(R0) ; Clear size param, and sequence # CLR N.VFC(R0) ; Clear VFC param CLR N.PSZ(R0) ; Clear prompt size param MOV (SP)+,KISAR6 ; Restore mapping MOV #IS.SUC&377,R0 ; Issue success on IO.XMT CLR R1 ; Zero second word CALL $IOFIN ; Complete the function 40$: MOV (SP)+,R3 ; Restore R3 MOV (SP)+,R2 ; Restore R2 MOV (SP)+,R1 ; Restore R1 RETURN ; Return to caller .PAGE .SBTTL Driver databases ; ; Macros to generate UCBs and SCBs ; .MACRO UCBGN UN RTST=. .IF DF M$$MUP ; Multi-user protection? .WORD 0 ; Login UIC .WORD 0 ; Owning terminal UCB address .ENDC .RT'UN:: ; UCB .WORD RTDCB ; DCB address .WORD .-2 ; Redirect pointer .BYTE UC.ATT!UC.QUE!UC.KIL!UC.PWF ; Control byte .BYTE US.DSB ; Status byte .BYTE 'UN ; Unit number .BYTE 0 ; Status extension .WORD DV.REC!DV.CCL!DV.TTY ; Device characteristics word 1 .WORD U2.RMT!U2.LOG ; Terminal dependent characteristics word .WORD 0 ; Device characteristics word 3 .WORD 132. ; Default buffer size .WORD $RT'UN ; Address of SCB .WORD 0 ; Attach pointer .WORD 0,0 ; Buffer addresses .WORD 0 ; Count .WORD 0 ; (terminal status word) .WORD 0 ; Chain of IO.XMTs .WORD 0 ; Chain of active packets .BYTE 0,0 ; Current sequence number, flags .WORD 0 ; Unsolicited input AST address .WORD 0 ; (reserved) .WORD 0 ; (reserved) .WORD 0 ; (reserved) .WORD 0 ; (UIC) .WORD 0 ; (reserved) .WORD 0 ; (aters status word) .WORD 0 ; (reserved) .WORD 0 ; (reserved) .WORD 0 ; (reserved) .WORD 0 ; (reserved) RTND=. .ENDM ; .MACRO SCBGN UN $RT'UN:: ; SCB .WORD 0 ; I/O request queue listhead .WORD .-2 ; .BYTE PR4,0 ; Priority, interrupt vector/4 .BYTE 0 ; Timeout counter .BYTE 0 ; Initial timeout count (sec) .BYTE 0,0 ; Controller index, status byte .WORD 0 ; CSR address .WORD 0 ; Address of current I/O packet .WORD 0,0,0,0 ; Fork block allocation .IF DF L$$DRV & M$$MGE ; Loadable drivers and memory mapping? .WORD 0 ; Extra word in fork block .ENDC .ENDM ; ; Device Control Block ; $RTDAT:: RTDCB:: .WORD 0 ; Link to next DCB .WORD .RT0 ; Link to first UCB .ASCII /RT/ ; Device name .BYTE 0,R$$T11-1 ; Unit number range .WORD RTND-RTST ; Length of UCB .WORD 0 ; Driver dispatch table addr. .WORD 161077 ; Legal Functions (15.-0.) .WORD 30 ; Control Functions .WORD 160000 ; NO-OP'ed Functions .WORD 0 ; ACP Functions .WORD 37007 ; Legal Functions (31.-16.) .WORD 30000 ; Control Functions (IO.CTL, IO.DIS) .WORD 1 ; NO-OP'ed Functions .WORD 6 ; ACP Functions .IF DF L$$DRV ; Check for loadable driver support .WORD 0 ; Address of partition control block .ENDC ; ; UCBs and SCBs ; UN=0 .REPT R$$T11 ; Generate one UCB for each terminal UCBGN \UN UN=UN+1 .ENDR UN=0 .REPT R$$T11 ; Generate one SCB for each terminal SCBGN \UN UN=UN+1 .ENDR ; $RTEND:: ; .END ; End of RT driver