.TITLE MBDRV .SBTTL Inter-task Mailbox driver .IDENT /050389/ .NLIST MEB .ENABL GBL .ENABL LC ; ;+ ;***** ; * * * NOTE * * * ; ; All programs, information and copies are provided "As ; Is". American Electric Power Service Corporation and ; the contributor disclaim all warranties on the programs, ; including without limitation, all implied warranties ; of merchantibility and fitness. ; ; ; MBDRV - Patterned after VSDRV (see Fall '81 DECUS RSXSIG ; symposium tape, [300,120] submission by John Osudar) and the ; VAX/VMS Mailbox driver, this driver performs virtually the ; same functions with a new twist. The driver maintains a set ; of named queues/mailboxes similar to VS: but rather than ; copying data buffers destined between tasks ; to its internal buffer pool, MB: re-links the I/O packets ; themselves and copies data directly from the writing task's ; virtual address space to the target task's address space. ; This can represent a significant performance improvement for ; large QIOs and is much more immune to running out of ; "pool" because the data is maintained in the requesting task's ; address space. (VAXELN supposedly does this too). ; ; There are, however, attendent disadvantages to this scheme. ; By necessity, write-QIOs NEVER complete until a corresponding ; read-QIO (or cancel) operation is issued for the queue, and ; tasks with outstanding I/O cannot be checkpointed or shuffled. ; ; In contrast to VS:, the mechanism to target QIOs for named ; queues is slightly different. Each QIO transfer function, ; i.e. IO.RLB/IO.WLB, designates a target queue using QIO ; parameters 3 and 4 (QIO offsets Q.IOPL+4 and Q.IOPL+6). ; These parameters are set to the 6 character queue name ; in RAD50, and default to the issuing task's name if ; QIO parameter 3 is 0. For a transfer QIO to be accepted, ; the task must have previously "accessed" the queue for ; read and/or write. This security check was not implemented ; in VS:, and may be used to block indescriminate I/O. ; ; Access to the mailbox is provided through the "IO.INL" control ; function with qualifying subfunction bits SF.ACR or SF.ACW ; for read or write operations. To synchronize task's with ; the mailbox structure, the task's "buffered" I/O count is ; incremented to force I/O rundown through the driver if the ; task exits or aborts. The control function IO.DEA is provided ; to deaccess mailboxes selectively or globally through ; the SF.ALL subfunction for "normal" cleanup prior to task exit. ; ; For consistancy with the transfer function QIOs (and VS:) QIO ; parameters 3 and 4 are reserved to designate the RAD50 name of the ; target queue for the operation. Valid IO.INL functions are, ; ; IO.INL!SF.ACR - Accesses the queue named in P3/P4 QIO parameter ; list to the task for read operations ; IO.INL!SF.ACW - Accesses the queue named in P3/P4 QIO parameter ; list to the task for write operations ; IO.INL!SF.AST - Identical to IO.INL!SF.ACR, but grants ; "exclusive" read access to issuing task and the ; additional function of delivering an "unsolicited ; input AST" on each write-QIO sent to the named queue ; when a read-QIO is not pending on the queue. ; P1 QIO parameter is the task's AST routine virtual ; address. ; ; NOTE: only one task is allowed to access a queue with unsolicited ; input AST notification; all IO.ACR requests once attached ; are aborted with the device not available error. ; ; If a queue with the same name as that specified in an IO.INL ; function does not exist, it will automatically be created. ; When a queue is created, the QIO P2 argument may specify a ; maximum queue depth from 0-255. Queue depth of 0 indicates ; an unlimited queue. Once the number of pending QIOs in a queue ; exceeds the maximum queue depth, additional QIOs are blocked ; and return the IE.DFU ("device full") error code. This feature ; is implemented to provide some form of flow control through the ; driver. As currently implemented, establishing a queue depth ; is applicable only to the first task attaching to a queue ; (implicit create-queue function). ; ; Accessing a queue with an unsolicited input AST allows the ; requesting task to be notified of each unsolicited write QIO ; directed to the specified queue. Only one task may be attached ; for unsolicited input ASTs and implies read access. It is possible ; to attach with unsolicited input ASTs and write access through ; IO.INL!SF.AST and IO.INL!SF.ACR function code. Upon receipt of ; a write-QIO with no read-QIO pending, any task attached via ; IO.INL!SF.AST will receive an AST with the following information ; on the task's stack: ; ; SP+04 = 2nd half of sender's task name ; SP+02 = 1st half of sender's task name (in RAD50) ; SP+00 = Write-QIO byte count ; ; These three words must be removed from the stack prior to ; exiting the AST routine via ASTX$S directive. ; ; Once a task is attached to a queue, it is allowed to issue QIOs ; to that queue applicable to the access specified when the ; queue was attached (read and/or write). ; ; The IO.ABO control function allows for selective rejection ; of a task's QIOs on a mailbox name basis. The use of IO.KIL ; forces cancellation of ALL QIOs the task has issued to ALL ; mailboxes the task has access to. The special sub-function ; bit SF.REJ allows a task accessed to a mailbox for read to ; reject all pending write-QIOs on the mailbox with an optionally ; specified error code in QIO parameter P1. This control ; function (IO.ABO!SF.REJ) is used to flush mailbox queues. ; ; The IO.DEA function deaccesses the task from a specified mailbox. ; Using the SF.ALL sub-function bit removes access from all mailboxes ; the task has previously requested access to. ; Outstanding QIOs issued by the task are killed. ; ; ; Addendum: On access, read or write QIO completion, the number of ; outstanding QIOs on a mailbox are returned in the I/O status block. ; This value is returned in the top byte of the 1st I/O status word as a ; negative number for queued reads and a positive number for ; queued writes. ; ; ;***** ; ; Paul Sorenson ; AEP/Engineering Computer Support Center ; Columbus, OH 43215 ; ;***** ; ; Update 2/10/86 -- PRS; Return # pending write QIOs for a mailbox ; to reader with I/O status ; Update 2/23/86 -- PRS; Change logic from using IO.ATT and IO.DET to ; IO.ACR, IOACW IO.ACE, and IO.DAC. Return outstanding I/O count ; on access so user can flush buffer. ; Update 3/10/86 -- PRS; Change logic from using IO.ACR/ACW/ACE/DAC to ; IO.INL/DEA control functions. Add ability to flush mailbox ; write queue. Add "redirect" diagnostic function for privileged ; tasks. Add ability for selective abort of task's QIOs on ; specific mailbox. Add ability to skip insertion of sender's ; task name on read/write QIOs. ; Update 4/8/87 -- PRS; Fix bug in MBCOPY, add check of executive fork ; queue during copy operations and fork to avoid hogging ; system time. ; Update 11/12/87 -- PRS; Change default I/O status returned for ; QIOs cancelled by IO.REJ from IE.ABO to IE.REJ ; Update 12/3/87 -- PRS; Change I/O status block returned on IO.REJ ; to always set status of IE.REJ with optional P1 parameter ; in 2nd I/O status word. ; Update 5/3/89 -- PRS; Pickup I/O function codes from MBIO$ macro ; rather than preassembly file (/PA:1). ; ;***** ;- .MCALL CLKDF$,HWDDF$,PKTDF$,DCBDF$,UCBDF$,SCBDF$,TCBDF$,PCBDF$ .MCALL MBIO$ MBIO$ ; CLKDF$ HWDDF$ PKTDF$ DCBDF$ UCBDF$ SCBDF$ TCBDF$ PCBDF$ ; .page .SBTTL Queue Control Block (QCB) Definition ; ; *** Queue Control Block offsets. The "QCB" is allocated when a ; named queue is created; deallocated when the last attached task ; issues IO.DET QIO. ; ; QCB Formatted as: ; ; +------------------------------+ ; ! Link to next QCB (or 0) ! = Q.CLNK ; +------------------------------+ ; ! QCB re-direct pointer ! = Q.CRED ; +------------------------------+ ; ! Queue Name in RAD50 ! = Q.CNAM ; ! ! ; +------------------------------+ ; ! # Pending QIO ! Max # QIOs ! = Q.CMPC/Q.CCPC ; +------------------------------+ ; ! Pending Write QIO List Head ! = Q.CWQL ; ! ! ; +------------------------------+ ; ! Pending Read QIO List Head ! = Q.CRQL ; ! ! ; +------------------------------+ ; ! Write Attachment List Head ! = Q.CWAL ; +------------------------------+ ; ! Read Attachment List Head ! = Q.CRAL ; +------------------------------+ ; ! TCB for Unsolicited Input AST! = Q.CTCB ; +------------------------------+ ; !Virtual Address of AST Routine! = Q.CAST ; +------------------------------+ ; .MACRO QCBDF$ A ; .ASECT .=0 Q.CLNK:'A' .BLKW 1 ; link to next QCB, or 0 Q.CRED:'A' .BLKW 1 ; QCB redirect pointer Q.CNAM:'A' .BLKW 2 ; Queue name in RAD50 Q.CPQC:'A' .BLKB 1 ; Current pending QIO packet count in queue Q.CMQC:'A' .BLKB 1 ; Max pending QIO packet count in queue Q.CWQL:'A' .BLKW 2 ; Write-QIO I/O packet list head Q.CRQL:'A' .BLKW 2 ; Read-QIO I/O packet list head Q.CWAL:'A' .BLKW 1 ; linked list of TCB's attached for write Q.CRAL:'A' .BLKW 1 ; linked list of TCB's attached for read Q.CTCB:'A' .BLKW 1 ; TCB address of task attached for AST Q.CAST:'A' .BLKW 1 ; AST routine address Q.CLGT:'A' ; length of QCB ; .ENDM ;QCBDF$ ; QCBDF$ ; .page .sbttl Queue Attachment Descriptor (QAD) Definition ; ; ; *** Queue Attachment Descriptor offsets. Linked to read/write attachment ; lists for QCB @ Q.CRAL/Q.CWAL, the attachment descriptor is ; allocated when task attaches to a queue and is deallocated when ; the task detaches from the queue. ; ; QAD Formatted as: ; ; +------------------------------+ ; ! Link to next QAD (or 0) ! = Q.ALNK ; +------------------------------+ ; ! TCB address of attached task ! = Q.ATCB ; +------------------------------+ ; .MACRO QADDF$ A ; .ASECT .=0 Q.ALNK:'A' .BLKW 1 ; link to next QAD, or 0 Q.ATCB:'A' .BLKW 1 ; TCB address of attached task Q.ALGT:'A' ; .ENDM ;QADDF$ ; QADDF$ ; ; .page .SBTTL MB: Driver Dispatch Table and I/O Initiation Entry Point .PSECT ; ; Define "DEBUG" to enable selected break point traps, define "TRACE" ; to enable display of PC at trace points to TT0: ;DEBUG=0 ;TRACE=0 ; LD$MB == 1 ; loadable driver MB$MRQ = 4 ; min # bytes allowed for read-QIO ASTPRM = 3 ; # AST parameters MAXBLX = 4 ; max number of 32 word blocks to transfer between checks ; for pending forks ; ; *** For PRSLMU system...there are a max of 64. lines each controlled ; by a task which has 1 default input mailbox. Main router ; is different in tht it has 64 incomming attachments to single ; mailbox. ; FREESZ == 64.*> ; # bytes in private buffer pool ; MBTOP: ; mark start of code/data ; DSPTBL: ; *** Local I/O function code dispatch table .word 0 ; no IO.KILL .word MBWLB ; IO.WLB .word MBRLB ; IO.RLB .word 0 ; no IO.ATT .word 0 ; no IO.DET .word MBACC ; IO.INL .word MBDEA ; IO.DEA .word MBABO ; IO.ABO .word MBDIAG ; diagnostic IO.RED N.IOFN = <.-DSPTBL>/2 ; # supported I/O functions $MBTBL:: ; *** Driver Dispatch Table .WORD $MBINI ; I/O initiation .WORD $MBCAN ; Cancel .WORD $MBTMO ; Timeout .WORD $MBPWF ; Powerfail/load ; ; *** $MBINI - I/O initiator... ; $MBINI:: CALL $GTPKT ; de-queue I/O packet BCC 4$ ; branch if got one RETURN ; else, exit 4$: ; ref label ; ; *** $GTPKT returns.... ; R1 = I/O packet address ; R3 = Controller index (always 0) ; R4 = SCB address ; R5 = UCB address ; .if df debug BPT .endc ;debug CLRB S.STS(R4) ; this driver NEVER goes busy BICB #US.BSY,U.STS(R5) MOV I.TCB(R1),R3 ; pickup issuing task's TCB address MOVB I.FCN+1(R1),R0 ; get I/O function CMP R0,#N.IOFN ; check for valid function BHI 16$ ; branch on bad function MOVB I.FCN(R1),R2 ; get subfunction bits ADD #I.PRM+4,R1 ; point R1 to QIO P3 argument CMPB R0,#IO.RLB/256. ; transfer function ?? BHI 8$ ; branch if no ADD #2,R1 ; align to P3 due to P1 relocation 8$: TST (R1) ; queue name given ?? BNE 12$ ; branch if yes TST 2(R1) ; maybe ?? BNE 12$ ; branch if yes MOV T.NAM(R3),(R1) ; Else, insert task name as default MOV T.NAM+2(R3),2(R1) 12$: ASL R0 ; set R0 to word index MOV DSPTBL(R0),R0 ; pickup routine address CALL (R0) ; call service routine BR $MBINI ; then try again 16$: MOV #IE.IFC&377,R0 ; setup return status CALL $IOALT ; finish packet BR $MBINI ; try again ; .SBTTL Access Mailbox ; ; *** MBACC - Establishes task's access to mailbox queue. If specified ; mailbox doesn't exist, it will be created. SF.AST sub-function is ; equivalent to IO.INL!SF.ACR with delivery of unsolicited input ASTs. ; Note that IO.INL with no sub-function implies IO.INL!SF.ACR. ; Specifying SF.AST with P1 = 0 is now allowed to give exclusive ; read access to task without requiring an AST routine. ; ; ; Registers on entry: ; R1 = points to P3/P4 QIO arguments = mailbox name ; R2 = subfunction bits ; R3 = TCB address of issuing task ; R4 = SCB address ; R5 = UCB address ; MBACC: ; *** Access mailbox ; .if df TRACE CALL TRACER ; display PC .endc ;TRACE .if df DEBUG BPT ; break to XDT .endc ;DEBUG ; BITB #^C,R2 ; any illegal bits ?? BNE 2$ ; branch if yes, invalid BITB #SF.ACW,R2 ; write access ?? BEQ 4$ ; branch if no, SF.ACR and/or SF.AST valid BITB #,R2 ; any other bits with SF.ACW ?? BEQ 4$ ; branch if no, valid 2$: MOV #IE.IFC&377,R0 ; set illegal function code BR 59$ ; and finish off packet 4$: CALL MBSQCB ; search for QCB by name BCC 12$ ; branch if QCB exists MOV R2,-(SP) ; save subfunction MOV -2(R1),R2 ; pickup max queue depth argument (P2) CALL MBIQCB ; allocate/initialize QCB MOV (SP)+,R2 ; recover subfunction BCS 49$ ; branch if can't allocate QCB BITB #SF.ACW,R2 ; access for write ?? BNE 40$ ; branch if yes BR 30$ ; else, must be access for read 12$: ; *** for existing mailbox, check validity of access MOV R3,R1 ; copy TCB address BITB #SF.ACW,R2 ; write access specified ?? BEQ 20$ ; branch if no, must be read CALL MBSWAD ; search write attachment list for TCB BCC 28$ ; branch if found, block multiple attaches BR 40$ ; else, access the mailbox 20$: TST Q.CTCB(R0) ; already accessed with read ASTs ?? BEQ 26$ ; branch if no, okay CMP R1,Q.CTCB(R0) ; attached by requestor ?? BEQ 28$ ; branch if yes, device already attached MOV #IE.DUN&377,R0 ; else, device not attachable BR 59$ ; and finish off packet 26$: CALL MBSRAD ; search read attachment list for TCB BCS 30$ ; branch if not found, okay 28$: MOV #IE.DAA&377,R0 ; setup error code, device already attached BR 59$ ; and finish off packet ; 30$: ; *** Valid access for read, link TCB to mailbox BITB #SF.AST,R2 ; access with unsolicited input AST ?? BEQ 35$ ; branch if no MOV S.PKT(R4),R1 ; recover I/O packet address MOV I.PRM(R1),Q.CAST(R0) ; save AST routine address (P1) MOV R3,Q.CTCB(R0) ; save task's TCB for AST delivery 35$: MOV R3,R1 ; copy TCB address CALL MBIRAD ; insert read attachment descriptor BCC 50$ ; branch if works BR 48$ ; else handle error ; 40$: ; *** Valid access for write, link TCB to mailbox MOV R3,R1 ; copy TCB address CALL MBIWAD ; insert write attachment descriptor BCC 50$ ; branch if works 48$: CALL MBRQCB ; remove QCB if last access 49$: MOV #IE.NOD&377,R0 ; setup error code, no dynamic space BR 59$ ; and finish I/O ; 50$: CMP #-1,(R0) ; check QCB link word BEQ 57$ ; branch if -1, QCB was just created CLR R1 ; pickup current pending I/O count BISB Q.CPQC(R0),R1 TST Q.CWQL(R0) ; pending operations = writes ?? BNE 56$ ; branch if yes, leave count +ve NEG R1 ; else, negate count for pending reads 56$: MOV #400!IS.SUC,R0 ; make top byte = 1 for existing mailbox CALLR $IODON ; complete I/O 57$: MOV MBQLHD,(R0) ; insert QCB in list MOV R0,MBQLHD MOV #IS.SUC,R0 ; setup standard success status 59$: CALLR $IOALT ; finish off packet ; .page .sbttl Deacces Mailbox ; ; *** MBDEA - Deaccesses issuing task from mailbox. All pending QIOs in the ; mailbox issued by the requesting task are aborted with IE.ABO ; status, but any AST service routines specified are NOT ; entered (same functioning as executive). With sub-function SF.ALL, ; all mailboxes attached by task are deaccessed, otherwise ; only the specified mailbox is deaccessed. ; ; Registers on entry: ; R1 = pointer to P3/P4 QIO arguments ; R2 = I/O subfunction bits ; R3 = TCB address of issuing task ; R4 = SCB address ; R5 = UCB address ; MBDEA: ; .if df TRACE CALL TRACER ; display PC .endc ;TRACE .if df DEBUG BPT ; break to XDT .endc ;DEBUG ; BIT #SF.ALL,R2 ; SF.ALL subfunction given ?? BEQ 10$ ; branch if no, pull only specific access MOV R3,R1 ; copy TCB address MOV MBQLHD,R0 ; get 1st QCB address BEQ 18$ ; branch if empty 4$: MOV (R0),R2 ; save link in case QCB deallocated CALL 20$ ; kill I/O and deaccess mailbox MOV R2,R0 ; make next QCB current BNE 4$ ; branch if more BR 18$ ; always successful ; 10$: ; *** deaccess specific mailbox CALL MBSQCB ; search for QCB by name BCS 16$ ; branch if no QCB found MOV R3,R1 ; copy TCB address CALL 20$ ; kill I/O and deaccess mailbox BCC 18$ ; branch if successful 16$: MOV #IE.DNA&377,R0 ; setup status, device not attached BR 19$ ; and exit 18$: MOV #IS.SUC&377,R0 ; setup status, success 19$: CALLR $IOALT ; jump to finish I/O ; ; *** local routine to kill I/O and deaccess mailbox 20$: CALL MBSWQL ; search write QIO list CALL MBSRQL ; search read QIO list BCS 24$ ; branch if nothing found CALL MBKQIO ; kill all QIOs found 24$: CALL MBDETQ ; detach task from queue RETURN ; .page .sbttl Abort QIOs from Selected Mailbox ; ; *** MBABO - Handles the IO.ABO function code to allow selective cancelling ; of QIOs issued by the task on a specific mailbox. A special ; sub-function bit is also allowed, SF.REJ, which forces ALL ; pending write QIOs on the specified mailbox to be rejected. ; Read QIOs may not be rejected. Issuing task may define a 16-bit ; value to be returned in the 2nd I/O status word of each ; QIO rejected by defining the value in QIO P1. ; ; Registers on entry: ; R1 = pointer to P3/P4 QIO arguments ; R2 = I/O subfunction bits ; R3 = TCB address of issuing task ; R4 = SCB address ; R5 = UCB address ; MBABO: ; .if df TRACE CALL TRACER ; display PC .endc ;TRACE .if df DEBUG BPT ; break to XDT .endc ;DEBUG ; CALL MBSQCB ; find specified QCB BCS 28$ ; branch if not defined BIT #SF.REJ,R2 ; reject subfunction set ?? BEQ 20$ ; branch if not ; ; *** Reject all pending writes on mailbox, allow ASTs to tasks ; MOV -4(R1),-(SP) ; save QIO P1 = reject reason MOV R3,R1 ; copy TCB address CALL MBSRAD ; issuing task have read access ?? MOV (SP)+,R1 ; recover reject status BCS 28$ ; branch if no read access, block QIO ADD #Q.CWQL,R0 ; point to write QIO list MOV (R0),R2 ; save address of 1st packet in list BEQ 26$ ; branch if empty, done CLR (R0) ; clear list head in QCB MOV R0,2(R0) CLRB (R0) ; clear pending QIO counter MOV #IE.REJ&377,R0 ; set I/O status to "transfer rejected" 18$: MOV PC,R3 ; set R3 non-zero, allow ASTs on rejection CALL MBKQIA ; kill all pending writes BR 26$ ; and quit ; ; *** Abort task's I/O on specific mailbox ; 20$: MOV R3,R1 ; copy TCB address CALL MBSWQL ; remove all write QIOs CALL MBSRQL ; and read QIOs BCS 26$ ; branch if nothing to kill CALL MBKQIO ; kill all I/O from task 26$: MOV #IS.SUC&377,R0 ; setup status for IO.ABO QIO BR 29$ ; and finish I/O 28$: MOV #IE.DNA&377,R0 ; setup status 29$: CALLR $IOALT ; finish off packet ; ; .page .sbttl Diagnostic Functions ; ; *** MBDIAG - Handles all diagnostic functions. These are all characterized ; by only being accepted from privileged tasks. Only one ; function/subfunction is currently allowed: IO.RED which will ; redirect all write QIOs from the mailbox specified in QIO ; parameters P1/P2 to the mailbox specified in P3/P4. Both ; mailboxes must exist prior to issuing the IO.RED QIO. ; ; Redirection of I/O stream is always broken if the destination ; mailbox is deleted from the system. Privileged applications ; may restore normal I/O flow by specifying the same mailbox name ; in both P1/P2 and P3/P4, or by leaving P1/P2 null which defaults ; to the P3/P4 mailbox. ; ; Registers on entry: ; R1 = pointer to P3/P4 QIO arguments ; R2 = I/O subfunction bits ; R3 = TCB address of issuing task ; R4 = SCB address ; R5 = UCB address ; MBDIAG: ; .if df TRACE CALL TRACER ; display PC .endc ;TRACE .if df DEBUG BPT ; break to XDT .endc ;DEBUG ; BIT #T3.PRV,T.ST3(R3) ; task privileged ?? BNE 5$ ; branch if yes MOV #IE.PRI&377,R0 ; setup error status BR 19$ ; and finish packet 5$: MOV R1,R2 ; Copy pointer to P3/P4 mailbox name CALL MBSQCB ; search for P3/P4 mailbox BCS 18$ ; branch if not found MOV R2,R1 ; recover address of P3 SUB #4,R1 ; point to P1/P2 mailbox name to be redirected MOV R0,R2 ; save P3/P4 mailbox QCB TST (R1) ; P1 defined ?? BNE 14$ ; branch if yes, search for mailbox TST 2(R1) ; P2 defined ?? BEQ 16$ ; branch if not, default P1/P2 to P3/P4 14$: CALL MBSQCB ; search for redirect mailbox BCS 18$ ; branch if not found 16$: MOV R2,Q.CRED(R0) ; redirect P1/P2 mailbox to P3/P4 mailbox MOV #IS.SUC&377,R0 ; setup success BR 19$ ; and exit 18$: MOV #IE.NSF&377,R0 ; use "no-such-file" for return code 19$: CALLR $IOALT ; finish I/O ; .page .sbttl Write Logical Block ; ; *** MBWLB - Handles the IO.WLB function by either linking the I/O packet ; into the write-QIO list for the specified queue, or transfers ; data to task with pending read-QIO. For special case of a task ; attached with unsolicited input AST, an AST is delivered to ; the task with 3 parameters on the stack: write-QIO byte count, ; sending task's name in RAD50. ; ; A new function is allowed, IO.RED, as a diagnostic function ; to redirect all write QIOs to an alternate mailbox. The redirect ; chain is similar to that used for standard device UCBs but ; is only followed for write QIOs AND is only one-level deep. ; The task must be privileged to issue IO.RED, and its behavior ; is expressly as a diagnostic function, ie. use at the risk of ; crashing the system. ; ; Registers on entry: ; R1 = points to P3/P4 QIO arguments ; R2 = I/O subfunction bits ; R3 = TCB address of issuing task ; R4 = SCB address ; R5 = UCB address ; MBWLB: ; .if df TRACE CALL TRACER ; display PC .endc ;TRACE .if df DEBUG BPT ; break to XDT .endc ;DEBUG ; CALL MBSQCB ; search for QCB by name BCS 8$ ; branch if QCB not found MOV R3,R1 ; copy TCB address CALL MBSWAD ; task attached for write ?? BCC 10$ ; branch if yes, proceed 8$: MOV #IE.DNA&377,R0 ; else, return error BR 20$ 10$: MOV Q.CRED(R0),R0 ; re-direct I/O once !! TST Q.CRQL(R0) ; read pending ?? BEQ 17$ ; branch if no DECB Q.CPQC(R0) ; decrement pending QIO count ADD #Q.CRQL,R0 ; point R0 to read QIO list head CALL $QRMVF ; remove next entry from read queue ; (R2/R3 destroyed) MOV S.PKT(R4),R0 ; get write QIO packet address CLR R2 ; set # pending writes for read I/O status CALLR MBCOPY ; jump to transfer data (@R0 -> @R1) 17$: CMPB #-1,Q.CPQC(R0) ; will this I/O overflow queue ?? BEQ 19$ ; branch if yes MOVB Q.CMQC(R0),R2 ; pickup max queue limit BEQ 21$ ; branch if 0, unlimited queue CMPB Q.CPQC(R0),R2 ; 1 more QIO exceed queue depth ?? BLO 21$ ; branch if no, queue read 19$: MOV #IE.DFU&377,R0 ; setup status 20$: CALLR $IOALT ; jump to finish I/O packet 21$: INCB Q.CPQC(R0) ; increment queue depth/pending QIO count ADD #Q.CWQL,R0 ; point R0 to write QIO list head MOV S.PKT(R4),R1 ; pickup I/O packet address again CALL $QINSF ; insert packet in FIFO list (R2 destroyed) ADD #,R0 ; point to AST control words MOV SP,R4 ; mark current stack MOV (R0)+,-(SP) ; save TCB of task attached for AST BEQ 49$ ; branch if none MOV I.PRM+4(R1),-(SP) ; save QIO byte count BIT #SF.NTN,I.FCN(R1) ; write with no task name ?? BNE 32$ ; branch if yes ADD #4,(SP) ; include task name bytes in count 32$: MOV (R0),-(SP) ; save AST routine address BEQ 49$ ; branch if none MOV #C.LGTH,R1 ; setup size for AST packet CALL $ALOCB ; allocate AST control block in system pool BCS 49$ ; branch if fails, skip AST delivery MOV R0,R1 ; copy packet address CLR (R0)+ ; skip link MOV #C.LGTH,(R0)+ ; insert packet size MOV #<<7.+ASTPRM>*2>,(R0)+ ; reserve space on stack MOV (SP)+,(R0)+ ; insert AST routine address MOV #ASTPRM,(R0)+ ; insert # AST parameters MOV (SP)+,(R0)+ ; (SP) = I/O byte count MOV T.NAM(R3),(R0)+ ; 2(SP) = 1st half of sender's name in RAD50 MOV T.NAM+2(R3),(R0)+ ; 4(SP) = 2nd half of sender's name MOV (SP)+,R0 ; recover target task's TCB address CALLR $QASTT ; jump to queue AST to task ; 49$: MOV R4,SP ; re-align stack RETURN ; and back to start I/O ; .page .sbttl Read Logical Block ; ; *** MBRLB - Handles the IO.RLB functions. If a write-QIO is pending ; on the specified queue, the data is copied to the requesting ; task and both QIOs are completed. If no write-QIO is pending, ; the read-QIO is either queued for completion when next write-QIO ; recieved, or will be finished with error status when ; the SF.NOW sub-function is specified. ; ; Registers on entry: ; R1 = points to P3/P4 QIO arguments ; R2 = I/O subfunction bits ; R3 = TCB address of issuing task ; R4 = SCB address ; R5 = UCB address ; MBRLB: ; .if df TRACE CALL TRACER ; display PC .endc ;TRACE .if df DEBUG BPT ; break to XDT .endc ;DEBUG ; CALL MBSQCB ; find target QCB BCS 8$ ; branch if not there MOV R3,R1 ; copy TCB CALL MBSRAD ; search read attachment descriptors BCC 10$ ; branch if found, okay 8$: MOV #IE.DNA&377,R0 ; setup status, device not attached BR 28$ ; and finish I/O 10$: MOV S.PKT(R4),R1 ; recover I/O packet address BITB #SF.NTN,I.FCN(R1) ; read with no task name ?? BNE 15$ ; branch if yes, no min packet CMP I.PRM+4(R1),#MB$MRQ ; check minimum read QIO size BHIS 15$ ; branch if okay MOV #IE.SPC&377,R0 ; setup status, illegal user buffer BR 28$ ; and finish I/O 15$: TST Q.CWQL(R0) ; write-QIO pending ?? BEQ 20$ ; branch if no DECB Q.CPQC(R0) ; show 1 less QIO pending MOVB Q.CPQC(R0),-(SP) ; save # pending writes ADD #Q.CWQL,R0 ; point to write-QIO list CALL $QRMVF ; remove packet from list ; (R2/R3 destroyed) MOV R1,R0 ; copy write packet address MOV S.PKT(R4),R1 ; recover read-QIO packet address MOVB (SP)+,R2 ; recover # pending writes for read ; I/O status CALLR MBCOPY ; jump to copy data 20$: BIT #SF.NOW,R2 ; "read now" function ?? BEQ 25$ ; branch if not, queue read MOV #IE.NDA&377,R0 ; show no data avaliable BR 28$ ; and finish I/O 25$: CMPB #-1,Q.CPQC(R0) ; will next I/O overflow ?? BEQ 27$ ; branch if yes MOVB Q.CMQC(R0),R2 ; pickup max queue depth BEQ 30$ ; branch if 0, unlimited queue CMPB Q.CPQC(R0),R2 ; 1 more QIO exceed queue depth ?? BLO 30$ ; branch if no, queue read 27$: MOV #IE.DFU&377,R0 ; set status, device full 28$: CALLR $IOALT ; jump to finish I/O 30$: INCB Q.CPQC(R0) ; increment pending QIO count ADD #Q.CRQL,R0 ; point to read-QIO list head CALLR $QINSF ; jump to queue packet ; .page .sbttl Copy Data from Sender to Receiver ; ; *** MBCOPY - Copies data between two tasks using executive's "$BLXIO" ; routine to re-map APR5/6 to perform transfer. Two additional ; words are reserved to hold the sender's task name and are returned ; preceeding the actual data transfered in the reader's data ; buffer. In the event that the read-QIO buffer is too small to ; hold all the data, the IE.DAO error status is returned. ; The 2nd I/O status word always contains ; the actual # of bytes transfered (eg. the read-QIO P2 argument) ; Due to the two word I/O status block size and support for large ; byte count QIOs, no indication of the write-QIO size will be given. ; ; Revision on 4/8/87 includes a check of the executive fork queue and ; creation of a fork process to avoid hogging system time. This ; facility requires that MBCOPY be entered with no arguments on the ; system stack. All references to MBCOPY are "CALLR" or "JMP" ; instructions so the major modification required is to avoid ; use of the system stack within MBCOPY. In particular, the following ; UCB offsets are used for special pointers while executing the ; memory-memory data transfer: ; ; U.BUF = Address of source (write-QIO) I/O packet ; I.PRM = source APR6 bias (KISAR6 contents) ; I.PRM+2 = source APR6 offset ; U.BUF+2 = Address of destination (read-QIO) I/O packet ; I.PRM = source APR5 bias (KISAR5 contents) ; I.PRM+2 = source APR5 offset ; U.CNT = Transfer size in bytes ; ; Registers on entry: ; ; R0 = Address of I/O packet describing sender's data buffer ; R1 = Address of I/O packet describing receiver's data buffer ; R2 = # write QIOs still pending in mailbox; returned to ; receiving task in top byte of 1st I/O status word ; R4 = SCB address ; R5 = UCB address ; ; returns: ; I/O status returned to both tasks; R0-R4 destroyed !! ; MBCOPY: ; .if df TRACE CALL TRACER ; display PC .endc ;TRACE .if df DEBUG BPT ; break to XDT .endc ;DEBUG ; MOV R1,U.BUF+2(R5) ; save destination I/O packet in UCB MOVB #IS.SUC&377,(R1) ; use link word for temp status MOVB R2,1(R1) ; for receiver, set # QIOs pending ; limited to byte value in this scheme MOV R0,U.BUF(R5) ; save source I/O packet in UCB MOV R0,S.PKT(R4) ; and as "current" packet in SCB ; MOV #IS.SUC&377,(R0) ; use link word for temp status MOV I.PRM+4(R0),R2 ; pickup # bytes from write QIO BITB #SF.NTN,I.FCN(R1) ; read with no task name ?? BNE 6$ ; branch if yes ADD #4,R2 ; adjust count for sender's task name BCS 8$ ; branch if overflowed...huge QIO that ; can't possibly be read completely 6$: CMP R2,I.PRM+4(R1) ; check against read-QIO buffer size BLOS 10$ ; branch if fits 8$: MOV I.PRM+4(R1),R2 ; truncate transfer to read buffer size MOVB #IE.DAO&377,(R0) ; reset write status MOVB #IE.DAO&377,(R1) ; reset read status 10$: MOV R2,I.PRM+4(R1) ; set read-QIO size to transfer size MOV I.PRM(R1),R3 ; pickup destination APR bias MOV I.PRM+2(R1),R4 ; and APR6 offset BITB #SF.NTN,I.FCN(R1) ; read with no task name ?? BNE 25$ ; branch if yes ADD #4,I.PRM+2(R1) ; update offset to account for name ; MOV I.TCB(R0),R1 ; get sender's TCB address ADD #T.NAM,R1 ; point to sender's task name BITB #SF.ETN,I.FCN(R0) ; write with explicit task name ?? BEQ 20$ ; branch if no, use installed name MOV R0,R1 ; else, copy I/O packet address ADD #I.PRM+12,R1 ; point to P5/P6 = explicit task name 20$: MOV KISAR6,-(SP) ; save APR6 mapping MOV R3,KISAR6 ; re-map to destination buffer MOVB (R1)+,(R4)+ ; copy sender's name to destination MOVB (R1)+,(R4)+ MOVB (R1)+,(R4)+ MOVB (R1)+,(R4)+ MOV (SP)+,KISAR6 ; restore APR6 mapping SUB #4,R2 ; adjust # bytes left BEQ 40$ ; branch if done 25$: MOV R2,U.CNT(R5) ; save remaining transfer count in UCB MOV I.PRM(R0),R1 ; get source APR bias MOV I.PRM+2(R0),R2 ; and source APR6 offset SUB #20000,R2 ; convert to APR5 offset MOV R2,I.PRM+2(R0) ; save it in source I/O packet 30$: MOV U.CNT(R5),R0 ; recover remaining byte count CMP R0,#MAXBLX*64. ; check count against max block size BLOS 34$ ; branch if in range MOV #MAXBLX*64.,R0 ; else limit transfer to max 34$: SUB R0,U.CNT(R5) ; adjust count left CALL $BLXIO ; call exec routine to copy data TST U.CNT(R5) ; anything left to copy ?? BEQ 40$ ; branch if done ; ; *** Update 4/8/87...if there are other drivers sitting on the fork queue, ; let them have the system for awhile. This requires that the stack ; be empty and necessitates saving and restoring R0-R4 before continuing. ; .if df TRACE CALL TRACER ; display PC .endc ;TRACE .if df DEBUG BPT ; break to XDT .endc ;DEBUG ; ADD #MAXBLX,R3 ; adjust destination APR bias ADD #MAXBLX,R1 ; adjust source APR bias MOV U.BUF+2(R5),R2 ; recover destination I/O packet address MOV U.BUF(R5),R0 ; recover source I/O packet address TST $FRKHD ; anyone waiting to fork ?? BEQ 38$ ; branch if not, keep copying MOV R3,I.PRM(R2) ; save destination APR bias MOV R1,I.PRM(R0) ; save source APR bias CALL $FORK ; create fork process ; .if df TRACE CALL TRACER ; display PC .endc ;TRACE .if df DEBUG BPT ; break to XDT .endc ;DEBUG ; MOV U.BUF+2(R5),R2 ; recover destination I/O packet address MOV U.BUF(R5),R0 ; recover source I/O packet address MOV I.PRM(R2),R3 ; recover destination APR bias MOV I.PRM(R0),R1 ; recover source APR bias 38$: MOV I.PRM+2(R2),R4 ; recover destination APR6 offset MOV I.PRM+2(R0),R2 ; recover source APR5 offset BR 30$ ; and loop til done 40$: MOV U.BUF(R5),R2 ; get source QIO packet address MOV I.PRM+4(R2),R1 ; recover byte count MOV (R2),R0 ; recover status MOV U.SCB(R5),R4 ; recover SCB address ; (packet address also @S.PKT(R4)) CALL $IODON ; finish write-QIO MOV U.BUF+2(R5),R2 ; get destination packet address MOV I.PRM+4(R2),R1 ; recover byte count MOV (R2),R0 ; recover status MOV U.SCB(R5),R4 ; recover SCB address MOV R2,S.PKT(R4) ; make destination packet = current CALLR $IODON ; finish read-QIO ; .page .sbttl Cancel All I/O for Task ; ; *** $MBCAN - Cancels all pending I/O from specified task. All currently ; active queues are scanned for QIO packets from the specified task. ; Any I/O packets found are linked into a temporary list of packets ; to be killed, and control is passed to the "FLUSH QIO LIST" ; routine. If I/O rundown is being performed for the task, the ; task is detached from all queues. ; ; Registers on call: ; R1 = TCB address of task ; R4 = SCB address ; R5 = UCB address ; PS = Priority set to device priority (in SCB = 0) ; $MBCAN: ; .if df TRACE CALL TRACER ; display PC .endc ;TRACE .if df DEBUG BPT ; break to XDT .endc ;DEBUG ; MOV MBQLHD,R0 ; get 1st QCB address BEQ 12$ ; branch if empty 4$: MOV (R0),R2 ; save link in case QCB deallocated CALL MBSWQL ; search read QIO list CALL MBSRQL ; search write QIO list BCS 8$ ; branch if none found CALL MBKQIO ; kill all QIOs found 8$: BIT #T2.ABO,T.ST2(R1) ; task being aborted ?? BEQ 10$ ; branch if no CALL MBDETQ ; detach task from queue 10$: MOV R2,R0 ; make next QCB current BNE 4$ ; branch if more 12$: RETURN ; .page .sbttl QIO Timeout ; ; *** $MBTMO - Timeout entry point; no-op for current release ; $MBTMO: RETURN ; .page .sbttl Powerfail/Load Entry Point ; ; *** $MBPWF - Powerfail/load entry point is called, for loadable drivers, ; when driver is initially loaded into system. This routine ; initializes all local data structures required for operation. ; VS: driver self-sized local buffer pool by checking driver's ; PCB; this is not implemented in this release. ; ; $MBPWF: ; .if df TRACE CALL TRACER ; display PC .endc ;TRACE .if df DEBUG BPT ; break to XDT .endc ;DEBUG ; CLR MBKLHD ; init kill QIO list MOV #MBKLHD,MBKLHD+2 CLR MBQLHD ; init QCB list MOV #MBPOOL,MBFLHD+2 ; link free list to first packet CLR MBPOOL ; show first packet as last MOV #FREESZ,MBPOOL+2 ; and set size of free pool RETURN ; .page .sbttl Detach task from mailbox queue control block ; ; *** MBDETQ - Accessory routine to detach a task from a queue and ; deallocate the QCB if this is the last task attached to the queue. ; ; Registers on call (both entry points). ; ; R0 = QCB address to detach task from ; R1 = TCB address of task ; R4 = SCB address ; R5 = UCB address ; ; returns: ; ; Carry bit set if task is not attached to queue ; Carry bit clear if task successfully detached from queue ; R0 may be destroyed if QCB deallocated !!! ; MBDETQ: ; detach task from queue MOV PC,-(SP) ; set status, assumes failure CMP R1,Q.CTCB(R0) ; TCB attached for unsolicited input AST ?? BNE 4$ ; branch if not CLR Q.CTCB(R0) ; else, disable AST delivery 4$: CALL MBRWAD ; remove any write attachment BCS 8$ ; branch if not attached CLR (SP) ; set success status 8$: CALL MBRRAD ; remove any read attachment BCS 12$ ; branch if not attached CLR (SP) ; set success status 12$: TST (SP)+ ; check status BEQ 16$ ; branch on success SEC ; else, setup return for caller BR 18$ ; and return 16$: CALL MBRQCB ; try to remove QCB CLC ; removed or not, show success 18$: RETURN ; .PAGE .SBTTL Queue Control Block Support Routines ; ; *** MBSQCB - Searches linked list for QCB by name. ; ; Registers on call: ; ; R1 = address of RAD50 queue name to match ; ; returns: ; Carry bit set on failure ; Carry bit clear on success, R0 contains address of ; QCB matching name ; MBSQCB: MOV #MBQLHD,R0 ; pickup list head address 2$: MOV (R0),R0 ; point to next QCB BEQ 8$ ; branch if none CMP Q.CNAM(R0),(R1) ; names match ?? BNE 2$ ; branch if no CMP Q.CNAM+2(R0),2(R1) ; maybe ?? BNE 2$ ; branch if no CLC ; show success BR 9$ ; and exit 8$: SEC ; show failure 9$: RETURN ; back to caller ; ; *** MBIQCB - Allocate and initialize QCB ; ; Registers on Call: ; ; R1 = Address of queue name in RAD50 ; R2 = Maximum queue depth (0-255.) ; ; returns: ; Carry bit set on failure; insufficient pool ; Carry bit clear on success; R0 = address of QCB ; created, 1st word of QCB = -1 as flag that ; QCB is not linked to mailbox QCB list ; ; NOTE: It is the caller's responsibility to ; re-link the QCB list by executing: ; MOV MBQLHD,(R0) ; MOV R0,MBQLHD ; MBIQCB: ; .if df TRACE CALL TRACER ; display PC .endc ;TRACE .if df DEBUG BPT ; break to XDT .endc ;DEBUG ; MOV R1,-(SP) ; save R1 MOV #Q.CLGT,R1 ; set size of packet needed CALL MBALOC ; allocate block MOV (SP)+,R1 ; recover R1 BCS 19$ ; branch if allocation failed MOV R0,-(SP) ; save QCB address ; ; *** NOTE: next coding forces positional dependencies in QCB fields !!! ; MOV #-1,(R0) ; flag it's a new QCB MOV R0,2(R0) ; re-direct to itself ADD #4,R0 ; skip over link/redirect pointers MOV (R1),(R0)+ ; define queue name MOV 2(R1),(R0)+ CLRB (R0)+ ; clear current queue depth MOVB R2,(R0)+ ; set max queue depth CLR (R0) ; clear write-QIO list head MOV R0,2(R0) ADD #4,R0 ; step over list head CLR (R0) ; clear write-QIO list head MOV R0,2(R0) ADD #4,R0 ; step over list head CLR (R0)+ ; clear attached-for-write TCB list CLR (R0)+ ; clear attached-for-read TCB list CLR (R0)+ ; clear TCB attached for AST CLR (R0) ; clear AST address MOV (SP)+,R0 ; recover QCB start address CLC ; show success 19$: RETURN ; back to caller ; ; *** MBRQCB - Removes QCB from active queue list. QCB's are only ; released when no tasks are attached to the queue. A search is ; made for any other QCB's redirected to the QCB about to be ; deleted and these redirections are broken ; ; Registers on call: ; ; R0 = Address of QCB to be released ; ; returns: ; ; Carry bit set on failure; task's still attached to queue ; R0 unaltered ; Carry bit clear on success, R0 cleared ; MBRQCB: MOV R1,-(SP) ; free R1-R3 MOV R2,-(SP) MOV R3,-(SP) CMP #-1,(R0) ; is this a new QCB ?? BEQ 26$ ; branch if yes, deallocate unconditionally TST Q.CWAL(R0) ; any attachements in write list BNE 30$ ; branch if yes, quit TST Q.CRAL(R0) ; any attachments in read list BNE 30$ ; branch if yes, quit MOV #MBQLHD,R1 ; pickup QCB list head 5$: CMP (R1),R0 ; pointing to this QCB ?? BEQ 10$ ; branch if yes MOV (R1),R1 ; step to next QCB BEQ 30$ ; bad QCB address from caller CMP Q.CRED(R1),R0 ; next QCB re-directed to QCB being deleted ?? BNE 5$ ; branch if no MOV R1,Q.CRED(R1) ; yes, re-direct to itself BR 5$ ; branch to check again 10$: MOV (R0),(R1) ; un-link QCB from list CLR (R0) MOV R1,-(SP) ; save preceeding QCB MOV R0,-(SP) ; save QCB address MOV Q.CWQL(R0),R2 ; get next write QIO packet BNE 16$ ; branch if entry in list ; (read list must be empty) MOV Q.CRQL(R0),R2 ; get next read QIO packet BEQ 18$ ; branch if none 16$: MOV #IE.ABO&377,R0 ; set status for I/O CLR R1 CLR R3 ; disable ASTs on I/O completion CALL MBKQIA ; flush pending QIOs 18$: MOV (SP)+,R0 ; recover QCB address MOV (SP)+,R1 ; and preceeding packet address 20$: ; *** remove any redirects left in other QCB's MOV (R1),R1 ; step to next QCB BEQ 26$ ; branch if done CMP Q.CRED(R1),R0 ; QCB redirected to packet being deleted ?? BNE 20$ ; branch if not MOV R1,Q.CRED(R1) ; un-do redirection BR 20$ ; loop through rest of packets 26$: MOV #Q.CLGT,R1 ; set size of packet CALL MBDEAC ; deallocate QCB CLR R0 ; wipe out QCB address BR 31$ ; and exit 30$: SEC ; show failure 31$: MOV (SP)+,R3 ; recover R1-R3 MOV (SP)+,R2 MOV (SP)+,R1 RETURN ; return to caller ; .page .sbttl Queue Attachment Descriptor Support Routines ; ; *** MBSWAD/MBSRAD - Searches write/read attachment descriptor list for ; matching TCB. ; ; Registers on call: ; ; R0 = QCB address ; R1 = TCB of task to look for ; ; returns: ; ; Carry bit set on failure ; Carry bit clear on success ; .enabl lsb MBSWAD: ; *** Search for TCB match in write list MOV R0,-(SP) ; save QCB address ADD #Q.CWAL,R0 ; point to write attachment list head BR 10$ ; branch to common code MBSRAD: ; *** Search for TCB match in read list MOV R0,-(SP) ; save QCB address ADD #Q.CRAL,R0 ; point to read attachment list head 10$: MOV (R0),R0 ; step to next QAD BEQ 18$ ; branch if none CMP Q.ATCB(R0),R1 ; TCB's match ?? BNE 10$ ; branch if no CLC ; show success BR 19$ ; and exit 18$: SEC ; show failure 19$: MOV (SP)+,R0 ; recover QCB address RETURN ; back to caller .dsabl lsb ; ; *** MBIWAD/MBIRAD - Insert write/read attachment descriptor into ; appropriate attachment list in QCB. As a task becomes attached ; to a queue, some means must be provided to detach the task ; in the event the task aborts abnormally. The mechanism chosen ; uses the buffered I/O count for the task. Each attachment ; descriptor increments the buffered I/O count for the task. ; When attachment descriptors are removed, these counts are ; decremented. If the task aborts, the executive attempts ; to perform I/O rundown which forces entry @$MBCAN which in turn ; should release all attachments....VERY IMPORTANT FOR ATTACH ; WITH ASTs ; ; Registers on call: ; ; R0 = QCB address ; R1 = TCB of task to attach ; ; returns: ; ; Carry bit set on failure; insufficient pool space ; Carry bit clear on success ; .enabl lsb MBIWAD: ; *** Insert attachment descriptor in write list MOV R0,-(SP) ; save QCB address ADD #Q.CWAL,R0 ; point to write attachment list head BR 10$ ; branch to common code MBIRAD: ; *** Insert attachment descriptor in read list MOV R0,-(SP) ; save QCB address ADD #Q.CRAL,R0 ; point to read attachment list head 10$: MOV R1,-(SP) ; save TCB address MOV R0,-(SP) ; save attachment list head MOV #Q.ALGT,R1 ; get length of QAD block to allocate CALL MBALOC ; allocate packet BCS 20$ ; branch if fails MOV (SP)+,R1 ; recover attachment list head address MOV (R1),(R0) ; link attachment descriptor into list MOV R0,(R1) MOV (SP)+,R1 ; recover TCB address MOV R1,Q.ATCB(R0) ; insert TCB in attachment descriptor INCB T.TIO(R1) ; bump buffered I/O count CLC ; show success BR 22$ ; branch to exit 20$: MOV (SP)+,R0 ; align stack preserving C-bit MOV (SP)+,R1 ; recover TCB address 22$: MOV (SP)+,R0 ; recover QCB address RETURN .dsabl lsb ; ; *** MBRWAD/MBRRAD - Removes write/read attachment descriptor for ; specified task (by TCB). ; ; Registers on call: ; ; R0 = QCB address ; R1 = TCB of detaching task ; ; returns: ; ; Carry bit set on failure ; Carry bit clear on success ; .enabl lsb MBRWAD: ; *** Remove QAD from write list MOV R0,-(SP) ; save QCB address ADD #Q.CWAL,R0 ; point to write attachment list head BR 6$ ; branch to common code MBRRAD: ; *** Remove QAD from read list MOV R0,-(SP) ; save QCB address ADD #Q.CRAL,R0 ; point to read attachment list head 6$: MOV R1,-(SP) ; save TCB address MOV R2,-(SP) ; save R2 7$: MOV R0,R2 ; save current packet address MOV (R2),R0 ; step to next QAD BEQ 16$ ; branch if none CMP Q.ATCB(R0),R1 ; TCB's match ?? BNE 7$ ; branch if no DECB T.TIO(R1) ; adjust task's buffered I/O count MOV (R0),(R2) ; un-link QAD MOV #Q.ALGT,R1 ; get QAD packet size CALL MBDEAC ; deallocate the packet, clear c-bit BR 17$ ; branch to return 16$: SEC ; show failure 17$: MOV (SP)+,R2 ; recover R2 MOV (SP)+,R1 ; recover TCB address MOV (SP)+,R0 ; recover QCB address RETURN ; back to caller .dsabl lsb ; .page .sbttl I/O Packet Support Routines ; ; *** MBKQIO/MBKQIA - "Kills" a list of QIO packets. MBKQIA is alternate entry ; to kill linked list of packets pointed to by caller in R2. ; ; Registers on call (MBKQIO entry): ; ; R4 = SCB address ; R5 = UCB address ; MBKLHD = list head for QIO packets to kill ; ; returns: ; IE.ABO status code to task issuing QIO, R0-R5 preserved ; ; Registers on call (MBKQIA entry): ; ; R0 = 1st status word to return for I/O ; R1 = 2nd status word to return for I/O ; R2 = Address of 1st I/O packet in linked list to be aborted ; R3 = AST flag; if 0, AST's are disabled on I/O completion ; R4 = SCB address ; R5 = UCB address ; ; returns: ; IE.ABO status code to task issuing QIO, R3-R5 preserved ; ; MBKQIO: MOV R0,-(SP) ; Free R0-R3 MOV R1,-(SP) MOV R2,-(SP) MOV R3,-(SP) MOV MBKLHD,R2 ; pickup 1st packet address BEQ 10$ ; quit if nothing there CLR MBKLHD ; clean up kill QIO list head MOV #MBKLHD,MBKLHD+2 MOV #IE.ABO&377,R0 ; Pickup standard I/O status CLR R1 CLR R3 ; disable ASTs on I/O completion CALL MBKQIA 10$: MOV (SP)+,R3 ; recover R0-R3 MOV (SP)+,R2 MOV (SP)+,R1 MOV (SP)+,R0 RETURN ; MBKQIA: ; alternate entry point ; .if df TRACE CALL TRACER ; display PC .endc ;TRACE .if df DEBUG BPT ; break to XDT .endc ;DEBUG ; MOV U.SCB(R5),R4 ; recover SCB address MOV S.PKT(R4),-(SP) ; save current packet address MOV R3,-(SP) ; save AST flag 4$: MOV R2,S.PKT(R4) ; make packet current TST (SP) ; I/O completion ASTs disabled ?? BNE 10$ ; branch if no CLR I.AST(R2) ; block completion ASTs 10$: MOV (R2),-(SP) ; save link to next packet MOV R0,-(SP) ; save status MOV R1,-(SP) CALL $IODON ; release packet MOV (SP)+,R1 ; recover status MOV (SP)+,R0 MOV U.SCB(R5),R4 ; recover SCB address MOV (SP)+,R2 ; get next packet address BNE 4$ ; branch if more MOV (SP)+,R3 ; restore R3 MOV (SP)+,S.PKT(R4) ; recover current packet 20$: CLC ; always show success RETURN ; and return to caller ; ; *** MBSRQL/MBSWQL - Searches the pending read/write QIO list in a queue ; for matching TCB and optionally matching LUN QIO requests. All ; I/O packets matching the specified criteria are re-linked into ; the "KILL" QIO list headed @MBKLHD. ; ; Registers on call: ; ; R0 = Address of QCB for queue to be searched ; R1 = TCB address of task to search lists for ; ; returns: ; Carry bit set indicates no QIOs in list ; Carry bit clear indicates QIO list scanned and all packets ; meeting criteria (TCB match) are linked to FIFO ; list @MBKLHD ; ; NOTE: Due to the nature of this driver, QIOs are either pending ; in the read queue or write queue, but never in both. A return ; with carry clear can be used to indicate there are no QIOs ; in the other list. ; ; .enabl lsb MBSWQL: ; search write QIO list MOV R2,-(SP) ; free R2 MOV #Q.CWQL,R2 ; setup offset BR 5$ ; branch to common code MBSRQL: ; search read QIO list MOV R2,-(SP) ; free R2 MOV #Q.CRQL,R2 ; setup offset 5$: MOV R3,-(SP) ; free R3 ADD R0,R2 ; point R2 to QIO list head MOV R2,-(SP) ; Save list head 8$: MOV (R2),R3 ; get next packet address BEQ 20$ ; branch if done CMP I.TCB(R3),R1 ; TCBs match ?? BEQ 14$ ; branch if yes MOV R3,R2 ; step over packet BR 8$ ; and look again 14$: MOV (R3),(R2) ; un-link packet CLR (R3) ; it will be last in kill list MOV R3,@MBKLHD+2 ; re-link packet to new list MOV R3,MBKLHD+2 DECB Q.CPQC(R0) ; show 1 less pending QIO BR 8$ ; look again 20$: MOV (SP)+,R3 ; recover list head MOV R2,2(R3) ; close list head TST MBKLHD ; anything linked to kill list ?? BNE 25$ ; branch if yes, return c-bit clear SEC ; show failure, nothing in list 25$: MOV (SP)+,R3 ; recover R3 MOV (SP)+,R2 ; recover R2 RETURN ; and return to caller .dsabl lsb ; ; .page .sbttl Private Buffer Pool Support Routines ; ; *** MBALOC - Allocate block from free pool list ; ; Registers on call: ; ; R1 = Size of packet required in bytes; must be >/= 4 ; and word aligned...NO CHECKING IS DONE !!! ; ; returns: ; ; Carry bit set on failure ; Carry bit clear on success; R0 = address of pool ; block allocated ; MBALOC: MOV R2,-(SP) ; free R2 MOV #MBFLHD,R0 ; get address of free memory list CALL $ALOC1 ; call exec routine to do it MOV (SP)+,R2 ; restore R2 RETURN ; and return to caller ; ; *** MBDEAC - Deallocate block to free memory pool ; ; Registers on call: ; ; R0 = Address of packet to deallocate ; R1 = Size of packet to deallocate ; ; returns: ; ; Carry bit clear; no failure possible ; MBDEAC: MOV R2,-(SP) ; free R2-R3 MOV R3,-(SP) MOV #MBFLHD,R3 ; get pointer to free memory list head CALL $DEAC1 ; call exec routine to do it CLC ; alwyas successful MOV (SP)+,R3 ; restore R2-R3 MOV (SP)+,R2 RETURN ; return to caller ; .IF DF TRACE .PAGE .sbttl Trace Debugger; print current PC on TT0: ; ; *** TRACER - Called to output current PC by directly accessing ; TT0: terminal hardware. ; ; TRACER:: ; MOV R0,-(SP) ; free R0-R2 MOV R1,-(SP) MOV R2,-(SP) MOV 6(SP),R1 ; get return PC MOV #5015,-(SP) ; insert vertical format MOV SP,R0 ; point to place to build ASCII PC SUB #10,SP ; reserve space to build rest of string MOV #6,R2 ; get # bytes to convert 10$: MOV R1,(SP) ; save current value BIC #177770,R1 ; grab lo 3 bits BIS #60,R1 ; force to ASCII MOVB R1,-(R0) ; insert character MOV (SP),R1 ; recover value ASR R1 ; align next bits ASR R1 ASR R1 SOB R2,10$ ; loop til done BICB #6,(R0) ; make 1st digit = 1/0 MOV (PC)+,(SP) ; identify output with "TR:" .ASCII /R:/ MOV (PC)+,-(SP) .ASCII <12>/T/ MOV SP,R0 ; replicate stack pointer MOV #14,R1 ; get # characters 25$: TSTB @#177564 ; output ready ?? BPL 25$ ; loop til yes MOVB (R0)+,@#177566 ; send character SOB R1,25$ ; loop til done MOV R0,SP ; align stack MOV (SP)+,R2 ; restore R0-R2 MOV (SP)+,R1 MOV (SP)+,R0 RETURN ; back to caller .ENDC ;TRACE ; ; .page .sbttl MB: Driver Private Buffer Pool ; ; *** Allocate required local data...private buffer pool placed at ; end of module so it may be dynamically sized to installed ; partition at load time (not supported yet) ; MBKLHD: .word 0 ; Kill QIO packet list head for cancel I/O .word MBKLHD MBQLHD: .word 0 ; queue control block (QCB) list head MBFLHD: .word 3,MBPOOL,0 ; MB: free pool list head MBPOOL: .word 0,FREESZ ; 1st free packet link/size .BLKB FREESZ-4 ; allocate pool space MBBOT: ; mark end of data/code .end MBTOP