.TITLE VXDRV -- Variable Size Pipe Driver .IDENT /082285/ ;+ ; ; VXDRV VERSION 1 ; ; DRIVER CODE VERSION 1.00 ; ; 1.00 22-AUG-1985 Initial version. Eddy Fey ; ; Note: This driver originated from the VSDRV code ; written by OSADAR and would not have been posible ; with outit. ; ; Functions supported: ; ; IO.KIL (0012) CANCEL ALL WAITING READ REQUESTS FOR THIS TASK ; IO.WLB (0400) PUT A MESSAGE INTO ANY EXISTING QUEUE ; IO.RLB (1000) GET A MESSAGE FROM A QUEUE ; IO.RLB!SF.WAI (1200) WAIT FOR A MESSAGE TO BE PUT INTO A QUEUE, ; AND GET IT ; IO.ATT (1400) (NO-OP -- CANNOT ATTACH DEVICE) ; IO.DET (2000) (NO-OP) ; IO.CRQ (2400) CREATE A MESSAGE QUEUE ; IO.CRQ!SF.FLU (2500) CREATE A MESSAGE QUEUE; IF IT ALREADY EXISTS, ; FLUSH IT (DELETE ALL MESSAGES) ; IO.DLQ (3000) DELETE A MESSAGE QUEUE AND ALL QUEUED MESSAGES ; IO.DLQ!SF.ALL (3001) DELETE ALL MESSAGE QUEUES AND ALL QUEUED MESSAGES ; IO.CLN (3400) CLOSE LUN (USED BY EXEC); CLEAN UP AST'S ; IO.DMP (4000) DUMP A SNAPSHOT OF THE PRIVATE POOL ; IO.AST (4400) SET/CLEAR AST FOR A QUEUE ; IO.EXM (5000) EXAMINE SELECTED MESSAGE ; IO.DLM (5400) DELETE SELECTED MESSAGE ; IO.CQL (6000) CHANGE ENTRY LIMIT FOR A QUEUE ; IO.CQL!SF.ALL (6001) CHANGE ENTRY LIMIT FOR ALL QUEUES ; ; Each message queue has a name, which is a six-character rad50 string, ; which must begin with something other than a space. when no queue ; name is given in a function, the task's name is used as a default. ; If privilege checking is enabled in this assembly, then non-privileged ; tasks are not allowed to create, delete, or read queues other ; than their default queue. All tasks may send messages to any queue. ; (if privilege checking is disabled, any task can do anything.) ; ; To disable privilege checking, comment out the following line: ; ;VX$PRV = 0 ; check privileges ; ; The six-word parameter list supplied to qio is used as follows: ; word description ; 1 Buffer address (with IO.WLB, IO.RLB, IO.DMP, IO.EXM). ; 2 Buffer length in bytes (with IO.WLB, IO.RLB, IO.DMP, IO.EXM). ; 3 First word of rad50 queue name (with IO.WLB; for privileged ; tasks, also with IO.RLB, IO.CRQ, IO.DLQ, IO.EXM, IO.DLM). ; 4 Second word of rad50 queue name (same functions as word 3). ; 5 Requested message id (with IO.EXM, IO.DLM). ; 6 Address of ast routine, or 0 to clear (with IO.AST). ; ; It is recommended that words 3 and 4 be set to zero if the default ; queue name is desired. ; NOTE: IO.RLB returns the sender task name in the first two words of ; the buffer, so four extra bytes should be supplied in the user buffer. ; io.exm returns the entire message, including the four overhead words ; (next-message id pointer, text length of current message, sender task ; NAME) but, does allow a partial transfer, unlike IO.RLB. ; ;- .MCALL HWDDF$,PKTDF$,DCBDF$,UCBDF$,SCBDF$,TCBDF$,PCBDF$ .MCALL DDT$,GTPKT$ HWDDF$ PKTDF$ DCBDF$ UCBDF$ SCBDF$ TCBDF$ PCBDF$ ; LD$VX == 1 ; Loadable driver V$$X11 == 1 ; number of controllers NUMPRM = 3 ; number of ast parameters on stack MXENSZ = 4000. ; maximum queue entry size. VXNENT = 50. ; default max number of entries ; ; MESSAGE QUEUE HEADER OFFSETS ; QH.LNK = 0 QH.FMS = 2 QH.LMS = 4 QH.QNM = 6 QH.RWP = 12 QH.TCB = 14 QH.AST = 16 QH.MAX = 20 QH.CUR = 22 QHDSIZ = 24 ; ; MESSAGE OFFSETS ; MS.LNK = 0 MS.LEN = 2 MS.STN = 4 MS.DAT = 10 ; ; I/O subfunctions. ; SF.FLU = 100 ; flush queue SF.WAI = 200 ; read and wait for entry SF.ALL = 1 ; set default # of entries - all ; ; The following are possible status returns from the driver: ; IS.SUC = 1 ; successful completion IE.ABO = -15. ; operation aborted IE.PRI = -16. ; privilege violation IE.QNF = -101. ; queue not found IE.QEX = -102. ; queue already exists IE.NMS = -103. ; no more space in partition pool IE.RAW = -104. ; read already waiting in queue IE.NOM = -105. ; no message available IE.UBS = -106. ; user buffer is too small for data transfer IE.ATS = -107. ; ast previously set for queue IE.ATC = -108. ; no ast presently set for queue IE.MXS = -109. ; attempt to queue entry larger then maximum ; ; Offsets to queue headers in partition via APR6 ; APR5 = 120000 APR6 = 140000 V.FQHD = APR5 + 0 ; Pointer to first queue header V.LQHD = APR5 + 2 ; Pointer to last queue header V.PHDR = APR5 + 4 ; Pool header V.POOL = APR5 + 12 ; Start of pool VHDSIZ = 20 ; Size of pool headers DDT$ VX,V$$X11,NONE,,,NEW ; ; VXINI -- I/O INITIATOR ENTRY: GET A PACKET TO PROCESS ; ; INITIAL CONDITIONS: ; R5 = UCB ADDRESS ; ; RESULTS: ; IF CY SET, NO PACKET OR CONTROLLER BUSY ; IF CY CLEAR: ; R1 = PACKET ADDRESS ; R4 = SCB ADDRESS ; R5 = UCB ADDRESS ; VXINI: GTPKT$ VX,V$$X11 ; get a packet or return ; ; VXPROC -- Process i/o packet. ; ; Initial conditions: ; R1 = packet address ; R4 = SCB address ; R5 = UCB address ; ; This routine will map APR 5 and 6 to the unit's partition and ; dispatch the request to the function routine. ; VXPROC: MOVB I.FCN+1(R1),R2 ; get function code BIC #177760,R2 ; only 16 allowed ASL R2 ; make it a word offset MOV I.TCB(R1),R0 ; get requestor TCB address MOV T.NAM(R0),U.QNAM(R5) ; get task name for default queue name MOV T.NAM+2(R0),U.QNAM+2(R5) ; MOV INITBL(R2),R2 ; get routine's address CALL MAPD ; map APR 5 and 6 to partitition JMP (R2) ; go to proper routine. ; ; Enter function routines with the following conditions: ; ; R0 = REQUESTOR'S TCB ADDRESS ; R1 = I/O PACKET ADDRESS ; R2 = FUNCTION CODE TIMES 2 ; R3 = ; R4 = SCB ADDRESS ; R5 = UCB ADDRESS ; U.QNAM(R5), U.QNAM+2(R5) ARE SET UP ; INITBL: .WORD VXDONZ ;IO.KIL .WORD VXWLB ;IO.WLB .WORD VXRLB ;IO.RLB .WORD VXDONZ ;IO.ATT IS NO-OP'ED .WORD VXDONZ ;IO.DET IS NO-OP'ED .WORD VXCRQ ;IO.CRQ .WORD VXDLQ ;IO.DLQ .WORD VXCLN ;IO.CLN .WORD VXDMP ;IO.DMP .WORD VXAST ;IO.AST .WORD VXEXM ;IO.EXM .WORD VXDLM ;IO.DLM .WORD VXCQL ;IO.CQL .WORD VXIFC ; illegal function code .WORD VXIFC ; illegal function code .WORD VXIFC ; illegal function code ; VXKRB: VXPWF: VXOUT: RETURN ; timeout just returns ; ; Unit online entry. ; find the partition and size; ; initialize queue pointers. ; VXUCB: BCS 5$ ; going off-line? MOV U.DCB(R5),R3 ; TEMP *** BIS #10000,D.MSK(R3) ; *** BIS #10000,D.MSK+2(R3) ; *** MOV R5,R3 ADD #U.PAR,R3 ; partition name in UCB CALL $SRNAM ; search for it BCC 10$ ; branch if found MOV #T.NDNR,R0 ; print device not ready message CALL $DVMSG MOVB #IE.DNR,$SCERR ; indicate an error 5$: CLR U.PSZ(R5) ; clear partition size parameter CLR U.PRB(R5) ; and relocation bias RETURN ; ; Found the partition (PCB in R2), ; get the size and address. ; 10$: MOV P.SIZE(R2),R1 ; get partition size (64 byte segments) ASH #6,R1 ; * 64 = # bytes CMP R1,#<16384.-64.> ; can't be > 16K - 64 BLE 20$ MOV #<16384.-64.>,R1 ; too big, set to default 20$: SUB #VHDSIZ,R1 ; minus queue header size MOV R1,U.PSZ(R5) ; save free pool size MOV P.REL(R2),U.PRB(R5) ; relocation bias MOV #VXNENT,U.CW4(R5) ; set default number of entries CALL MAPD ; map APR 5 and 6 to partition CALL IPOOL ; init queue headers CALL UNMAPD ; restore mapping RETURN ; ; VXDONZ -- DONE WITH A PACKET, ZERO BYTES TRANSFERRED ; VXDON -- DONE WITH A PACKET, TRANSFER COUNT IN R1 ; VXERR -- ERROR WITH PACKET, STATUS AND COUNT SUPPLIED ; VXERRZ -- ERROR WITH PACKET, STATUS SUPPLIED, ZERO COUNT ; ; INITIAL CONDITIONS: ; R5 = UCB ADDRESS ; VXIFC: MOV #IE.IFC,R0 ; illegal function code BR VXERRZ VXDONZ: MOV #IS.SUC,R0 ; show successful completion VXERRZ: CALL UNMAPD ; restore APR 5 and 6 CALL $IOALT ; finish up, zero bytes transferred JMP VXINI ; loop back to get next packet VXDON: MOV #IS.SUC,R0 ; show successful completion VXERR: CALL UNMAPD ; restore APR 5 and 6 CALL $IODON ; finish it up JMP VXINI ; loop back to initiator entry .PAGE ; ; The structure of the message queues: ; ; First, there is a queue-header header, at the beginning of the ; partition which looks like this: ; ; offset description label ; ---------------------------------- ; 0 | Pointer to first queue header | V.FQHD (queue header listhead) ; ---------------------------------- ; 2 | Pointer to last queue header | V.LQHD ; ---------------------------------- ; 4 | Block allocation mask (3) | V.PHDR (private pool header) ; ---------------------------------- ; 6 | Pointer to free pool | V.FREE ; ---------------------------------- ; 10 | (0) | ; ---------------------------------- ; ; This heads a queue of queue headers, each of which looks like this: ; ; +--------------------------------+ ; | POINTER TO NEXT QUEUE HEADER | QH.LNK ; +--------------------------------+ ; | POINTER TO FIRST MESSAGE | QH.FMS ; +--------------------------------+ ; | POINTER TO LAST MESSAGE | QH.LMS ; +--------------------------------+ ; | | QH.QNM ; | RAD50 NAME OF QUEUE | ; | | ; +--------------------------------+ ; | ADDRESS OF READ WAIT PACKET | QH.RWP ; +--------------------------------+ ; | TCB ADDRESS OF AST TASK | QH.TCB ; +--------------------------------+ ; | AST ADDRESS | QH.AST ; +--------------------------------+ ; | Maximum number of entries | QH.MAX ; ef 102686 ; +--------------------------------+ ; | Current number of entries | QH.CUR ; ef 102686 ; +--------------------------------+ ; ; ; A MESSAGE IN A QUEUE LOOKS LIKE THIS: ; ; +--------------------------------+ ; | POINTER TO NEXT MESSAGE | MS.LNK ; +--------------------------------+ ; | MESSAGE LENGTH IN BYTES | MS.LEN ; +--------------------------------+ ; | . | ; - RAD50 SENDER TASK NAME - MS.STN ; | . | ; +--------------------------------+ ; | . | MS.DAT ; - . - ; | . | ; - MESSAGE TEXT - ; | . | ; - . - ; | . | ; ---------------------------------- ; ; MESSAGE TEXT MAY BE PADDED WITH 0 TO 3 BYTES OF EXTRA SPACE, ; TO BRING THE ENTIRE MESSAGE TO A MULTIPLE OF 4 BYTES IN LENGTH ; ; ; ; VXCRQ -- create a queue ; VXCRQ: CMP #^RA ,I.PRM+4(R1) ; was a queue name specified? BHI 11$ ; no, use default .IF DF VX$PRV BIT #T3.PRV,T.ST3(R0) ; is requestor a privileged task? BNE 10$ ; if so, ok MOV #IE.PRI,R0 ; else it's a privilege violation JMP VXERRZ ; so exit .ENDC ;DF VX$PRV 10$: MOV I.PRM+4(R1),U.QNAM(R5) ; copy requested queue name MOV I.PRM+6(R1),U.QNAM+2(R5) ; 11$: MOV R1,-(SP) ; save packet address CALL FQHBQN ; find queue header by queue name BEQ 40$ ; not found--ok MOV (SP)+,R1 ; restore packet address CMPB #SF.FLU,I.FCN(R1) ; flush set? BNE 30$ ; if not, error TST QH.AST(R0) ; ast set? BEQ 20$ ; if not, proceed with flush MOV #IE.ATS,R0 ; else error "ast is set" JMP VXERRZ ; error exit 20$: MOV R0,U.QHD(R5) ; save queue header address CALL FLUSHQ ; flush it JMP VXDONZ ; done 30$: MOV #IE.QEX,R0 ; set error code JMP VXERRZ ; error exit 40$: MOV #QHDSIZ,R1 ; queue header size CALL ALOCAT ; allocate it BCC 50$ ; got it--ok MOV (SP)+,R1 ; restore packet address MOV #IE.NMS,R0 ; no more space JMP VXERRZ ; error exit 50$: TST (SP)+ ; adjust sp MOV R0,U.QHD(R5) ; save queue header address MOV #,R3 ; queue size CLR (R0)+ ; link is null MOV R0,R1 ; save message queue header CLR (R0)+ ; pointer to first message is null MOV R1,(R0)+ ; end pointer to itself MOV U.QNAM(R5),(R0)+ ; put in the queue name MOV U.QNAM+2(R5),(R0)+ CLR (R0)+ ; no read wait packet CLR (R0)+ ; clear ast task tcb address CLR (R0)+ ; clear ast address CLR (R0)+ ; clear max entries ; ef 102686 CLR (R0)+ ; clear current # entries ; ef 102686 MOV U.QHD(R5),R1 ; get address of new header MOV U.CW4(R5),QH.MAX(R1) ; set the limit ; ef 102686 MOV #V.FQHD,R0 ; get address of queue-header listhead CALL $QINSF ; insert into queue JMP VXDONZ ; done .SBTTL VXDLQ -- Delete a queue and any queued messages ; ; VXDLQ -- Delete a queue and any queued messages ; VXDLQ: CMP #^RA ,I.PRM+4(R1) ; was a queue name specified? BHI 11$ ; no, use default .IF DF VX$PRV BIT #T3.PRV,T.ST3(R0) ; is requestor a privileged task? BNE 10$ ; if so, ok MOV #IE.PRI,R0 ; else it's a privilege violation JMP VXERRZ ; so exit .ENDC ;DF VX$PRV 10$: MOV I.PRM+4(R1),U.QNAM(R5) ; copy requested queue name MOV I.PRM+6(R1),U.QNAM+2(R5) ; 11$: BIT #SF.ALL,I.FCN(R1) ; delete all queues? BNE VXKILQ CALL FQHBQN ; find queue header by queue name BNE 1$ ; found--ok MOV #IE.QNF,R0 ; queue not found JMP VXERRZ ; error exit 1$: MOV R0,U.QHD(R5) ; save queue address TST QH.AST(R0) ; is ast set in this queue? BEQ 3$ ; if not, ok MOV #IE.ATS,R0 ; else error "ast is set" JMP VXERRZ ; error exit 3$: MOV (R0),(R1) ; copy our link to predecessor's BNE 2$ ; if non-null, unlink is complete MOV R1,V.LQHD ; else predecessor is new last entry 2$: CALL FLUSHQ ; flush that queue! MOV #QHDSIZ,R1 ; prepare to deallocate queue header MOV U.QHD(R5),R0 ; get its address CALL DEALOC ; deallocate it JMP VXDONZ ; done .SBTTL VXKILQ - Initialize pool area ; ; VXKILQ -- Initialize pool area ; ; This section will initialize the free pool to be empty ; VXKILQ: CALL IPOOL ; initialize the pool JMP VXDONZ ; done .SBTTL VXRLB - Read a message from the queue ; ; VXRLB -- Read a message from the queue ; VXRLB: CMP #^RA ,I.PRM+6(R1) ;WAS A QUEUE NAME SPECIFIED? BHI 11$ ;NO, USE DEFAULT .IF DF VX$PRV BIT #T3.PRV,T.ST3(R0) ;IS REQUESTOR A PRIVILEGED TASK? BNE 10$ ;IF SO, OK MOV #IE.PRI,R0 ;ELSE IT'S A PRIVILEGE VIOLATION JMP VXERRZ ;SO EXIT .ENDC ;DF VX$PRV 10$: MOV I.PRM+6(R1),U.QNAM(R5) ;COPY REQUESTED QUEUE NAME MOV I.PRM+10(R1),U.QNAM+2(R5) ; 11$: MOV R1,-(SP) ; save packet address CALL FQHBQN ; find queue header by queue name BNE 1$ ; got it--ok TST (SP)+ ; adjust sp MOV #IE.QNF,R0 ; queue not found JMP VXERRZ ; error exit 1$: MOV (SP)+,R1 ; restore packet address MOV R0,U.QHD(R5) ; save queue header address TSTB I.FCN(R1) ; wait specified in function? BPL 4$ ; no, ok TST QH.RWP(R0) ; already waiting? BEQ 3$ ; no, ok CMP R1,QH.RWP(R0) ; is this the packet that was waiting? BNE 22$ ; no, it's another one CLR QH.RWP(R0) ; clear waiting packet address BR 4$ ; and allow the read to proceed 22$: MOV #IE.RAW,R0 ; show queue has read already waiting JMP VXERRZ ; error exit 3$: TST QH.AST(R0) ; is an ast set for this queue? BEQ 32$ ; no, it's ok MOV #IE.ATS,R0 ; else error "ast is set" JMP VXERRZ ; error exit 32$: TST QH.FMS(R0) ; is a message queued already? BNE 4$ ; if it is, don't wait MOV R1,QH.RWP(R0) ; else set read wait packet address MOV I.TCB(R1),QH.TCB(R0) ; set TCB address on read wait BICB #US.BSY,U.STS(R5) ; clear unit busy CLRB S.STS(R4) ; clear controller busy CALL UNMAPD ; restore APR 5 and 6 JMP VXINI ; get next packet 4$: MOV QH.FMS(R0),R1 ; get address of first message BNE 5$ ; z clear if got one MOV #IE.NOM,R0 ; else indicate no message available JMP VXERRZ ; and error exit 5$: MOV R1,U.MSG(R5) ; save message address MOV MS.LEN(R1),U.MSGL(R5) ; get length of message received MOV U.MSGL(R5),R0 ; get length of message ADD #4,R0 ; add four for sender task name CMP R0,U.CNT(R5) ; compare to user buffer size BLOS 6$ ; if le, user buffer is big enough MOV #IE.UBS,R0 ; else user buffer is too small JMP VXERRZ ; so error exit 6$: MOV R0,-(SP) ; save transfer count CALL GETMSG ; actually get it now ADD #MS.STN,R1 ; point to name of sender task MOV (SP)+,R0 ; get transfer count again INC R0 ; increment to round up ASR R0 ; get word count (>0 due to task name) CALL PUTBLK ; call tranfer routine MOV U.MSG(R5),R0 ; get message address again MOV U.MSGL(R5),R1 ; get length ADD #MS.DAT,R1 ; add overhead size CALL DEALOC ; deallocate the message block MOV U.MSGL(R5),R1 ; calculate transfer count ADD #4,R1 MOV U.QHD(R5),R0 ; get queue header address ; ef 102686 DEC QH.CUR(R0) ; decrement # of entries ; ef 102686 JMP VXDON ; done with this one .SBTTL VXWLB - Write a message to the queue ; ; VXWLB -- Write a message to the queue. ; VXWLB: CMP #^RA ,I.PRM+6(R1) ; was a queue name specified? BHI 11$ ; no, use default MOV I.PRM+6(R1),U.QNAM(R5) ; copy requested queue name MOV I.PRM+10(R1),U.QNAM+2(R5) ; 11$: CALL FQHBQN ; find queue header by queue name BNE 3$ ; if found, ok MOV #IE.QNF,R0 ; queue not found JMP VXERRZ ; error exit 3$: MOV R0,U.QHD(R5) ; save queue header address TST QH.MAX(R0) ; maximum entry limit set? ; ef 102686 BEQ 30$ ; no CMP QH.MAX(R0),QH.CUR(R0) ; yes, already at the end ? BEQ 41$ 30$: MOV U.CNT(R5),R1 ; get message length CMP #,R1 ; see if too big BGE 4$ ; branch if ok 31$: MOV #IE.MXS,R0 ; entry size error JMP VXERRZ ; error exit 4$: MOV R1,U.MSGL(R5) ; save it ADD #MS.DAT,R1 ; determine required storage CALL ALOCAT ; allocate it BCC 5$ ; if it succeeded, ok 41$: MOV #IE.NMS,R0 ; no more space JMP VXERRZ ; error exit 5$: MOV R0,U.MSG(R5) ; save message address CLR (R0)+ ; clear link to next message MOV U.MSGL(R5),(R0)+ ; set message length MOV U.SCB(R5),R1 ; get packet address MOV S.PKT(R1),R1 MOV I.TCB(R1),R1 ; now get requestor's tcb address MOV T.NAM(R1),(R0)+ ; move sender task name into block MOV T.NAM+2(R1),(R0)+ ; MOV U.MSGL(R5),R1 ; get message length INC R1 ; increment to round up ASR R1 ; get word count BEQ 7$ ; if zero, skip transfer MOV R0,-(SP) ; save current queue address MOV R1,-(SP) ; and wordcount MOV U.BUF(R5),@#KDSAR6 ; map user buffer MOV U.BUF+2(R5),R2 ; get buffer virtual address BIT #40000,R0 ; queue in APR 6 ? BEQ 20$ ; no 10$: ADD #200,@#KDSAR5 ; yes, bias to second 4k BIC #40000,R0 ; clear bit 14 in queue address BIS #120000,R0 ; adjust to apr 5 BR 50$ ; ; Queue doesn't start in 2nd 4K. ; see if count will cause it to overflow. ; 20$: ADD R1,R0 ; add count to queue address ADD R1,R0 BIT #40000,R0 BEQ 40$ ; branch if all in 1st 4k MOV 2(SP),R0 ; get queue address again MOV #140000,R4 ; calculate amount in 1st 4k SUB R0,R4 ; ASR R4 ; divide by 2 INC R4 ; +1 MOV R4,R1 CALL BLKMOV ; transfer to queue MOV (SP),R1 ; get original count SUB R4,R1 ; minus 1st 4k wc CLR R0 BR 10$ 40$: MOV 2(SP),R0 ; restore queue address 50$: CALL BLKMOV ; transfer to queue TST (SP)+ ; adjust SP TST (SP)+ MOV U.PRB(R5),@#KDSAR5 ; restore APR 5 MOV U.PRB(R5),@#KDSAR6 ; restore APR 6 ADD #200,@#KDSAR6 MOV R2,U.BUF+2(R5) ; update pointer 7$: MOV U.QHD(R5),R0 ; get receiver queue header address TST (R0)+ ; point to message queue header MOV U.MSG(R5),R1 ; get message address CALL $QINSF ; put in the queue TST -(R0) ; get back receiver queue header addr TST QH.AST(R0) ; ast specified for this queue? BEQ 77$ ; if not, skip ast code MOV #<5+NUMPRM>*2,R1 ; allocate ast block of proper length CALL $ALOCB ; (allocation from exec pool) BCC 71$ ; if successful, continue IOT ; crash the system (out of pool anyway!) 71$: MOV U.QHD(R5),R2 ; get queue header address MOV R0,-(SP) ; save ast block address CLR (R0)+ ; clear link MOV R1,(R0)+ ; store length of ast block MOV #<7+NUMPRM>*2,(R0)+ ; store bytes to allocate on stack MOV QH.AST(R2),(R0)+ ; set ast address MOV #NUMPRM,(R0)+ ; set number of parameters MOV QH.QNM(R2),(R0)+ ; store first word of queue name MOV QH.QNM+2(R2),(R0)+ ; store second word of queue name MOV U.MSGL(R5),(R0) ; store message length ADD #4,(R0) ; add four for sender task name space MOV QH.TCB(R2),R0 ; get task's tcb address MOV (SP)+,R1 ; get ast block address CALL $QASTT ; queue the ast MOV U.QHD(R5),R0 ; get the queue header address 77$: INC QH.CUR(R0) ; increment # of entries ; ef 102686 TST QH.RWP(R0) ; was there a packet waiting on read? BNE 8$ ; yes, have to process read as well MOV U.MSGL(R5),R1 ; else finish up JMP VXDON ; by jumping out 8$: MOV QH.RWP(R0),U.MSG(R5) ; save read-wait's i/o packet address MOV #IS.SUC,R0 ; indicate success MOV U.MSGL(R5),R1 ; get length CALL UNMAPD ; restore APR 5 and 6 CALL $IODON ; finish our packet BISB #US.BSY,U.STS(R5) ; set unit busy MOV U.SCB(R5),R4 ; get scb address INCB S.STS(R4) ; set controller busy MOV U.MSG(R5),R1 ; restore old packet address MOV R1,S.PKT(R4) ; set new packet address MOV I.PRM(R1),U.BUF(R5) ; set up ucb for transfers MOV I.PRM+2(R1),U.BUF+2(R5) ; MOV I.PRM+4(R1),U.CNT(R5) ; JMP VXPROC ; process the old packet .SBTTL VXCLN - Close lun ; ; VXCLN -- CLOSE LUN (CLEAN UP AST'S) ; ; Close lun (clean up ast's) is initiated by the exec when a task is ; exiting, and one of the task's luns has a nonzero second lun word. ; VXDRV sets the second lun word nonzero when a task sets an ast, just ; so that the exec will do this. This function will then clear *all* ; ast's set by the task. Presumably, if a task wanted to clear all ; the ast's that it set, it could do this call itself. That doesn't ; save anything, though, since all the second lun words that need to ; be cleared will not be affected. (If one lun is used to set all ast's, ; and the same lun is used to clear them all with this function, then ; the exec will not be forced to do this.) ; VXCLN: CLR @I.LN2(R1) ; clear second lun word for this lun MOV #V.FQHD,R3 ; get queue header header address 1$: MOV (R3),R3 ; get next queue header address BEQ 9$ ; if no more, done CMP QH.TCB(R3),R0 ; ast set by this task in this queue? BNE 1$ ; if not, loop CLR QH.TCB(R3) ; else clear the ast task tcb address CLR QH.AST(R3) ; clear the ast address too BR 1$ ; and loop 9$: JMP VXDONZ .SBTTL VXAST - Set/clear ast ; ; VXAST -- SET/CLEAR AST ; ; This function is used to set or clear an ast for a particular queue. ; only the task that sets the ast can clear it. (the exec does this ; for the task at exit, if necessary, with IO.CLN; see above.) ; VXAST: CMP #^RA ,I.PRM+4(R1) ; was a queue name specified? BHI 11$ ; no, use default .IF DF VX$PRV BIT #T3.PRV,T.ST3(R0) ; is requestor a privileged task? BNE 10$ ; if so, ok MOV #IE.PRI,R0 ; else it's a privilege violation JMP VXERRZ ; so exit .ENDC ;DF VX$PRV 10$: MOV I.PRM+4(R1),U.QNAM(R5) ; copy requested queue name MOV I.PRM+6(R1),U.QNAM+2(R5) ; 11$: MOV R1,-(SP) ; save packet address CALL FQHBQN ; find queue header by queue name BNE 1$ ; found--ok TST (SP)+ ; adjust sp MOV #IE.QNF,R0 ; queue not found JMP VXERRZ ; error exit 1$: MOV (SP)+,R1 ; restore packet address MOV R0,U.QHD(R5) ; save queue address TST QH.RWP(R0) ; is there a read waiting in this queue? BEQ 2$ ; if not, ok MOV #IE.RAW,R0 ; else error "read waiting" JMP VXERRZ ; error exit 2$: MOV I.PRM+12(R1),R3 ; get ast address; are we clearing ast? BEQ 3$ ; yes, clearing TST QH.AST(R0) ; if setting ast, see if one already set BEQ 22$ ; not already set; it's ok to set one MOV #IE.ATS,R0 ; else error "ast already set" JMP VXERRZ ; error exit 22$: MOV I.TCB(R1),QH.TCB(R0) ; set tcb address of ast task MOV R3,QH.AST(R0) ; set ast address MOV #2,@I.LN2(R1) ; set second lun word nonzero JMP VXDONZ ; done 3$: TST QH.AST(R0) ; if clearing ast, see if one is set BNE 33$ ; if one set, ok MOV #IE.ATC,R0 ; else error "no ast set" JMP VXERRZ ; error exit 33$: CMP I.TCB(R1),QH.TCB(R0) ; same task clearing as set it? BEQ 35$ ; yes, it's ok to clear MOV #IE.ATS,R0 ; else error "ast set by other task" JMP VXERRZ ; error exit 35$: CLR QH.TCB(R0) ; clear ast task tcb address CLR QH.AST(R0) ; clear ast address too JMP VXDONZ ; done .SBTTL VXCAN - Cancel I/O ; ; VXCAN -- CANCEL I/O ; ; Cancel I/O does only one thing: It kills any read-wait packets ; that belong to the task doing the cancel. No queues or messages ; are deleted. ; VXCAN: CALL MAPD ; map APR 5 and 6 to partition MOV R1,R2 ; save TCB address MOV #V.FQHD,R0 ; get address of queue-header listhead 1$: MOV (R0),R0 ; follow link to next queue header BEQ 9$ ; if null, we're done TST QH.RWP(R0) ; else see if this queue has a wait BEQ 1$ ; if not, skip this header MOV QH.RWP(R0),R1 ; else get packet address CMP #16.,I.TCB(R1) ; packet already deallocated ? BNE 2$ CMP R2,QH.TCB(R0) ; did it belong to this task ? BNE 1$ ; no CLR QH.TCB(R0) ; clear TCB CLR QH.RWP(R0) ; clear read wait address BR 1$ ; go to next queue 2$: CMP R2,I.TCB(R1) ; does it belong to this task? BNE 1$ ; if not, skip this header CLR QH.RWP(R0) ; else we want to clear this one CLR QH.TCB(R0) ; clear TCB address MOV R1,S.PKT(R4) ; set packet address into SCB MOV R0,U.QHD(R5) ; save header address MOV #IE.ABO,R0 ; set abort status CALL UNMAPD ; restore APR 5 and 6 CALL $IOALT ; kill the packet CALL MAPD ; map again MOV U.SCB(R5),R4 ; restore SCB MOV U.QHD(R5),R0 ; restore queue header address BR 1$ ; loop for all the rest 9$: CALL UNMAPD ; restore APR 5 and 6 RETURN .SBTTL VXDMP - Dump driver pool snapshot ; ; VXDMP -- DUMP DRIVER POOL SNAPSHOT ; VXDMP: .IF DF VX$PRV BIT #T3.PRV,T.ST3(R0) ; is requestor a privileged task? BNE 10$ ; if so, ok MOV #IE.PRI,R0 ; else it's a privilege violation JMP VXERRZ ; so exit .ENDC ;DF VX$PRV 10$: MOV U.PSZ(R5),R0 ; get size of queue area ADD #VHDSIZ,R0 ; plus pool queue header size CMP R0,U.CNT(R5) ; task buffer big enough? BLOS 1$ ; yes, dump it MOV #IE.UBS,R0 ; else user buffer too small JMP VXERRZ ; error exit 1$: MOV R0,-(SP) ; save count for i/o status blk ASR R0 ; get word count MOV #160000,R2 ; SUB U.BUF+2(R5),R2 ; 160000 - buffer start ASR R2 ; = maximum MOV R0,R3 ; clear remainder SUB R2,R3 ; calculate 2nd chunk BLE 5$ ; branch if no MOV R2,R0 ; get first block's wordcount 5$: MOV #V.FQHD,R1 ; get starting address CALL PUTBLK ; call transfer routine TST R3 ; any remainder ? BLE 9$ MOV #V.FQHD,R1 ; where we left off in the queue ADD R0,R1 ADD R0,R1 MOV R3,R0 ; get remainder count MOV #140000,U.BUF+2(R5) ; reset buffer address ADD #200,U.BUF(R5) ; bump to next 4k in user space CALL PUTBLK ; do last chunk 9$: MOV (SP)+,R1 ; restore count JMP VXDON .SBTTL VXEXM -- Examine Selected Message ; ; VXEXM -- Examine selected message ; VXEXM: CMP #^RA ,I.PRM+6(R1) ; was a queue name specified? BHI 11$ ; no, use default .IF DF VX$PRV BIT #T3.PRV,T.ST3(R0) ; is requestor a privileged task? BNE 10$ ; if so, ok MOV #IE.PRI,R0 ; else it's a privilege violation JMP VXERRZ ; so exit .ENDC ;DF VX$PRV 10$: MOV I.PRM+6(R1),U.QNAM(R5) ; copy requested queue name MOV I.PRM+10(R1),U.QNAM+2(R5) ; 11$: MOV R1,-(SP) ; save packet address CALL FQHBQN ; find queue header by queue name BNE 1$ ; got it--ok TST (SP)+ ; adjust sp MOV #IE.QNF,R0 ; queue not found JMP VXERRZ ; error exit 1$: MOV R0,U.QHD(R5) ; save queue header address MOV (SP)+,R1 ; restore packet address MOV I.PRM+12(R1),R3 ; get requested message id BNE 12$ ; if nonzero, not first message default MOV QH.FMS(R0),R3 ; if default, set to first message ASSUME QH.FMS,2 ; make sure next instruction works! 12$: TST (R0)+ ; point to first message address 2$: MOV (R0),R0 ; get next message address BEQ 3$ ; if zero, reached end -- not found CMP R0,R3 ; compare to requested message id BNE 2$ ; not the one we want -- keep looking MOV MS.LEN(R0),R3 ; found it! now get message length ADD #MS.DAT+1,R3 ; compute total size and round up ... BIC #1,R3 ; to an even number CMP R3,U.CNT(R5) ; compare to user buffer size BLOS 22$ ; if not > user buffer, ok MOV U.CNT(R5),R3 ; else use user buffer size 22$: MOV R0,R1 ; copy pointer MOV R3,R0 ; copy count ASR R0 ; and turn it into word count CALL PUTBLK ; call transfer routine MOV R3,R1 ; get the count again JMP VXDON ; and finish up 3$: MOV #IE.NOM,R0 ; if not found, return "no message" error JMP VXERRZ .SBTTL VXDLM - Delete Selected Message ; ; VXDLM -- Delete Selected Message ; VXDLM: CMP #^RA ,I.PRM+4(R1) ; was a queue name specified? BHI 11$ ; no, use default .IF DF VX$PRV BIT #T3.PRV,T.ST3(R0) ; is requestor a privileged task? BNE 10$ ; if so, ok MOV #IE.PRI,R0 ; else it's a privilege violation JMP VXERRZ ; so exit .ENDC ;DF VX$PRV 10$: MOV I.PRM+4(R1),U.QNAM(R5) ; copy requested queue name MOV I.PRM+6(R1),U.QNAM+2(R5) 11$: MOV R1,-(SP) ; save packet address CALL FQHBQN ; find queue header by queue name BNE 1$ ; found--ok TST (SP)+ ; adjust sp MOV #IE.QNF,R0 ; queue not found JMP VXERRZ ; error exit 1$: MOV R0,U.QHD(R5) ; save queue address MOV (SP)+,R1 ; restore packet address MOV I.PRM+10(R1),R3 ; get requested message id BNE 12$ ; if not zero, isn't default to first MOV QH.FMS(R0),R3 ; else get first message address 12$: TST QH.AST(R0) ; does queue have an ast? BEQ 14$ ; if not, ok CMP I.TCB(R1),QH.TCB(R0) ; else see if same task as this one BEQ 14$ ; if so, ok MOV #IE.ATS,R0 ; indicate ast set JMP VXERRZ ; and error exit ASSUME QH.FMS,2 ; make sure next instruction works! 14$: TST (R0)+ ; point to first message address 2$: MOV R0,R2 ; save previous address MOV (R0),R0 ; get next address BEQ 3$ ; if zero, not found CMP R0,R3 ; else see if we want this one BNE 2$ ; no, looking for another one MOV (R0),(R2) ; got it, now delete it: close up list BNE 22$ ; if not zero link, no new end MOV U.QHD(R5),R3 ; new end: get queue header address MOV R2,QH.LMS(R3) ; store new end DEC QH.CUR(R3) ; decrement # of entries ; ef 102686 22$: MOV MS.LEN(R0),R1 ; get message text length ADD #MS.DAT,R1 ; add length of overhead stuff CALL DEALOC ; deallocate it JMP VXDONZ ; done! 3$: MOV #IE.NOM,R0 ; not found -- "no such message" JMP VXERRZ ; ; VXCQL -- Change Queue Entry Limit ; VXCQL: .IF DF VX$PRV BIT #T3.PRV,T.ST3(R0) ; is requestor a privileged task? BNE 10$ ; if so, ok MOV #IE.PRI,R0 ; else it's a privilege violation JMP VXERRZ ; so exit .ENDC ;DF VX$PRV CMP #^RA ,I.PRM+4(R1) ; was a queue name specified? BHI 11$ ; no, use default 10$: MOV I.PRM+4(R1),U.QNAM(R5) ; copy requested queue name MOV I.PRM+6(R1),U.QNAM+2(R5) 11$: BITB #SF.ALL,I.FCN(R1) ; change default for all? BEQ 12$ ; no MOV I.PRM+10(R1),U.CW4(R5) ; change default limit. BR 50$ 12$: MOV R1,-(SP) ; save packet address CALL FQHBQN ; find queue header by queue name BNE 40$ ; found--ok TST (SP)+ ; adjust sp MOV #IE.QNF,R0 ; queue not found JMP VXERRZ ; error exit 40$: MOV (SP)+,R1 ; restore packet address MOV I.PRM+10(R1),QH.MAX(R0) ; Set specified limit 50$: JMP VXDONZ .SBTTL Mapping Routines ; ; MAPD -- Map APR's 5 and 6 (D space) to partition assigned to this ; unit. ; Inputs: ; U.PRB(R5) = Partition reloction bias. ; Outputs: ; KDSAR5 = Partition relocation bias ; KDSAR6 = Partition relocation bias + 200 ; (sp) = Previous KDSAR5 (for UNMAPD) ; 2(sp) = Previous KDSAR6 " MAPD: TST -(SP) ; allocate some stack MOV 2(SP),-(SP) ; put return address on top MOV @#KDSAR6,4(SP) ; save APR 6 D mapping MOV @#KDSAR5,2(SP) ; save APR 5 D mapping MOV U.PRB(R5),@#KDSAR5 ; map to partition MOV U.PRB(R5),@#KDSAR6 ADD #200,@#KDSAR6 ; 2nd 4K RETURN ; ; UNMAPD -- Map APR's 5 and 6 (D space) back to original assignment. ; ; Inputs: ; 2(sp) = Previous KDSAR5 (for UNMAPD) ; 4(sp) = Previous KDSAR6 " ; Outputs: ; KDSAR5 = Previous relocation bias ; KDSAR6 = Previous relocation bias UNMAPD: MOV 4(SP),@#KDSAR6 ; restore APR 6 MOV (SP)+,2(SP) ; adjust return address MOV (SP)+,@#KDSAR5 ; restore APR 5 RETURN .SBTTL Locate Queue and Pool Space Management Routines ; ; FQHBQN -- Find Queue Header By Queue Name ; ; Initial conditions: ; U.QNAM(R5), U.QNAM+2(R5) = rad50 queue name ; ; Results: ; R0 = Address of queue header (if found) ; = 0 (if not found) ; R1 = Previous queue header ; Z status clear if found, set if not found ; FQHBQN: MOV #V.FQHD,R0 ; get queue-header header address 1$: MOV R0,R1 ; save predecessor address for unlink MOV (R0),R0 ; follow link to next header BEQ 9$ ; if null, queue not found CMP U.QNAM(R5),QH.QNM(R0) ; see if first words match BNE 1$ ; if not, follow link CMP U.QNAM+2(R5),QH.QNM+2(R0) ; see if second words match BNE 1$ ; if not, follow link 9$: TST R0 ; set z status according to result RETURN ; return when equal, or at end of queue ; ; ALOCAT -- Allocate a private pool block. ; ; Initial conditions: ; APR 5 and 6 mapped to partition ; ; RESULTS: ; IF CY SET, ALLOCATION FAILED ; IF CY CLEAR, R0 = ADDRESS, R1 = LENGTH OF BLOCK ALLOCATED ; ALOCAT: MOV #V.PHDR,R0 ; get private pool header address CALL $ALOC1 ; allocate RETURN ; ; DEALOC -- Deallocate a private pool block. ; ; Initial conditions: ; APR 5 and 6 mapped to partition ; R0 = address of block to deallocate ; DEALOC: MOV #V.PHDR,R3 ; get private pool header address CALL $DEAC1 ; deallocate RETURN ; ; GETMSG -- GET NEXT MESSAGE IN QUEUE ; ; INITIAL CONDITIONS: ; APR 5 and 6 mapped to partition ; U.QHD(R5) = Address of queue from which to get message ; ; RESULTS: ; R1 = Address of message if one is dequeued ; CY status clear if message dequeued, set if no message found. ; GETMSG: MOV U.QHD(R5),R0 ; get address of queue header ASSUME QH.FMS,2 ; required if next instruction is to work! TST (R0)+ ; point to message queue header CALLR $QRMVF ; remove from queue and return ; ; FLUSHQ -- FLUSH QUEUE ; ; INITIAL CONDITIONS: ; APR 5 and 6 mapped to partition ; U.QHD(R5) = Address of queue to flush ; ; RESULTS: ; R0, R1 AND R3 ARE DESTROYED ; The queue is flushed of all messages, and a waiting read ; packet is aborted if present. ; FLUSHQ: CALL GETMSG ; get next message in queue BCS 9$ ; if cy, no more messages MOV R1,R0 ; copy message address MOV MS.LEN(R0),R1 ; get length ADD #MS.DAT,R1 ; add overhead CALL DEALOC ; deallocate the block BR FLUSHQ ; try it again 9$: MOV U.QHD(R5),R0 ; get queue header address CLR QH.CUR(R0) ; clear entry counter ; ef 102686 TST QH.RWP(R0) ; waiting packet present? BEQ 8$ ; no, skip it MOV QH.RWP(R0),R3 ; Get packet address CLR QH.RWP(R0) ; clear read i/o packet address MOV QH.TCB(R0),R1 ; get waiting TCB CLR QH.TCB(R0) ; clear TCB address CMP R1,I.TCB(R3) ; already deallocated ? BNE 8$ ; yes MOV #IE.ABO,R0 ; set abort status CLR R1 ; zero the count CALL $IOFIN ; finish read packet 8$: RETURN ; ; Initialize pool in private partition. ; APR 5 and 6 must be mapped to the partition. ; IPOOL: MOV R1,-(SP) ; save r1 MOV R2,-(SP) ; save r2 CLR V.FQHD ; initialize queue header link MOV #V.FQHD,V.LQHD ; link to itself MOV #3,V.PHDR ; set up private pool MOV #V.POOL,V.PHDR+2 ; pool address CLR V.PHDR+4 CLR V.POOL ; clear private pool link MOV U.PSZ(R5),V.POOL+2 ; set private pool size MOV U.PSZ(R5),R1 ASR R1 ; pool size in words MOV #V.POOL+4,R2 ; address start 10$: CLR (R2)+ ; zero the pool SOB R1,10$ MOV (SP)+,R2 MOV (SP)+,R1 RETURN .SBTTL Data Transfer Subroutines ; ; ; PUTBLK -- Transfer data from queue to user buffer routine. ; ; INITIAL CONDITIONS: ; APR 5 and 6 mapped to partition ; R0 = number of words to transfer ; R1 = driver virtual address of data to be transferred ; U.BUF,U.BUF+2 in ucb contain buffer pointer from $RELOC ; RESULTS: ; Data has been transferred to user buffer ; U.BUF,U.BUF+2 in ucb have been updated ; R4 destroyed ; PUTBLK: MOV R1,-(SP) ; save current queue address MOV R0,-(SP) ; save wordcount MOV R1,R2 ; put queue address in R2 (source) MOV R0,R1 ; count in R1 MOV U.BUF(R5),@#KDSAR6 ; map user buffer MOV U.BUF+2(R5),R0 ; get buffer virtual address (destination) BIT #40000,R2 ; queue in APR 6 ? BEQ 20$ ; no 10$: ADD #200,@#KDSAR5 ; yes, bias to second 4k BIC #40000,R2 ; clear bit 14 in queue address BIS #120000,R2 ; adjust to apr 5 BR 50$ ; ; Queue doesn't start in 2nd 4K. ; see if count will cause it to overflow. ; 20$: ADD R1,R2 ; add count to queue address ADD R1,R2 BIT #40000,R2 BEQ 40$ ; branch if all in 1st 4k MOV 2(SP),R2 ; get queue address again MOV #140000,R4 ; calculate amount in 1st 4k SUB R2,R4 ; ASR R4 ; divide by 2 MOV R4,R1 CALL BLKMOV ; transfer to queue MOV (SP),R1 ; get original count SUB R4,R1 ; minus 1st 4k wc CLR R2 BR 10$ 40$: MOV 2(SP),R2 ; restore queue address 50$: CALL BLKMOV ; transfer from queue MOV R0,U.BUF+2(R5) ; get buffer virtual address MOV (SP)+,R0 ; restore R0 MOV (SP)+,R1 ; restore R1 MOV U.PRB(R5),@#KDSAR5 ; restore APR 5 MOV U.PRB(R5),@#KDSAR6 ; restore APR 6 ADD #200,@#KDSAR6 RETURN ; ; BLKMOV -- BlocK Move Subroutine. ; Moves data in blocks of 32. words. ; R2 - source address ; R0 - destination address ; R1 - wordcount ; BLKMOV: MOV R4,-(SP) MOV R1,R4 ; calculate number of blocks BLE BMXIT ; <= 0, exit. ASH #-5,R4 ; divide by 32. BIC #177740,R1 ; remainder TST R4 ; count < 1 block ? BNE DOMOV ; branch if not REMAIN: ADD R1,R1 ; finished with blocks NEG R1 ; now do remainder JMP MOVE(R1) ; vector to correct number of moves DOMOV: ; block of 32 MOV instructions .REPT 32. MOV (R2)+,(R0)+ .ENDR MOVE: DEC R4 ; anymore blocks ? BGT DOMOV ; branch if yes TST R1 BGT REMAIN ; no, remainder ? BMXIT: MOV (SP)+,R4 RETURN .END