Files
Sepp J Morris 31738079c4 Upload
Digital Research
2020-11-06 18:50:37 +01:00

459 lines
13 KiB
Plaintext

;*****************************************************
;*
;* Queue Routines
;*
;*****************************************************
; Each queue system entry point is mutually exclusive
; of the other queue system entry points.
; All the routines called in QUE2.RTM are
; subroutines of the entry points in QUE1.RTM and
; thus are called only when the process has ownership
; of the q system.
; If a process is already in the queue system another process
; will wait until the first process calls Q_UNSYNC.
; This allows us to keep interrupts on while using shared variables.
; To protect against terminating and potentially losing a QD,
; a message or neglecting to wake up a DQing or NQing
; process; a "no abort region" is entered whenever
; we obtain the queue system. In addition if we assign
; the queue system to a DQing or NQing process, the new owner
; of the q system is forced into a "no abort region". This
; makes the environment of the waking NQing or DQing process appear
; as if it never went to sleep.
; A note for the uninitiated: DQing is short for "Decoding from a Queue"
; or reading from a queue. NQing is short for "eNcoding to a Queue"
; or writing to a queue.
;=========== =======================
makeq_entry: ; Create a System Queue
;=========== =======================
; input : U_WRKSEG:DX = address of QD to create
; output: BX = 0 if okay , 0ffffh if error
; CX = error Code
call q_sync ;obtain q system, BX,DX preserved
call getqdaddr ;get QD and q buffer
jcxz qm_gotqd
jmp qm_err
qm_gotqd: ;DI->QD in SYSDAT
;make sure this queue doesn't
;already exist
qm_chk:
push es ;save UDA
mov es,sysdat
mov si,(offset qlr)-q_link
qm_nxt:
mov si,q_link[si] ;go down QLR to see if QD already
test si,si ! jz qm_go ;exists
push di ! push si ;save new QD and QLR ptr
mov cx,qnamsiz/2
add di,q_name ;ES:DI->QPB.NAME
add si,q_name ;DS:SI->QPB.NAME
repe cmpsw
pop si ! pop di ;restore QLR ptr and new QD
jne qm_nxt ;no match try next QD
pop es ;names match, restore UDA
call remqd ;QD pointed to by DS:DI
mov cx,e_q_inuse
jmps qm_err
qm_go: ;no match - alright to make this q
pop es ;ES=UDA
xor dx,dx ;initialize the QD
mov q_dq[di],dx
mov q_nq[di],dx
mov q_msgcnt[di],dx
mov q_msgout[di],dx
mov ax,qlr ;put QD on QLR
mov q_link[di],ax
mov qlr,di
xor bx,bx ;return success
jmps qm_ret
qm_err:
mov bx,0ffffh
qm_ret:
jmp q_unsync ;release q system; BX,CX preserved
;=========== ======================
openq_entry: ; Find an active Queue
;=========== ======================
; input: U_WRKSEG:DX = address of QPB
;
; output: sets QPB.QADDR to QD offset in SYSDAT
; BX = 0 if okay, 0ffffh if not
; CX = error Code
call q_sync ;obtain q system; BX,DX preserved
push es ;save UDA
mov es,u_wrkseg
mov si,(offset qlr)-q_link
mov di,dx ;ES:DI->QPB
qo_nqd:
mov si,q_link[si] ;go down QLR until QD name
test si,si ! jnz qo_cmp ;matches QPB name or end of QLR
pop es ;ES=UDA
mov cx,e_no_queue ;found end of QLR - can't open
jmps qo_err
qo_cmp:
push di ! push si ;compare names
mov cx,qnamsiz/2
add si,q_name ;DS:SI->QD.NAME
add di,qpb_name ;ES:DI->QPB.NAME
repe cmpsw
pop si ! pop di ;restore QD,QPB
jne qo_nqd ;try next QD if no match
test q_flags[si],qf_hide ;names match
jz noprot ;check for protection:
mov bx,rlr ;must be system process to
test p_flag[bx],pf_sys ;open q with QF_HIDE set
jnz noprot
pop es ;ES=UDA
mov cx,e_q_protected ;QF_HIDE and not SYS PD
jmps qo_err
noprot:
mov es:qpb_qaddr[di],si ;write the QD offset into
pop es ;ES=UDA
xor bx,bx ;the QPB_QADDR field
jmps qo_ret ;to open the q
qo_err: ;CX=error code
mov bx,0ffffh
qo_ret:
call q_unsync ;release q system; BX,CX preserved
ret
;============= =======================
deleteq_entry: ; Delete a System Queue
;============= =======================
; Takes QD off the QLR and place it in the QUL
; input: U_WRKSEG:DX = offset of QPB
; output: BX = 0 if ok, 0ffffh if error
; CX = error code
mov ax,es ;save UDA
mov es,u_wrkseg ;get QD address from user's
mov di,dx ;QPB
mov di,es:qpb_qaddr[di] ;DS:DI->QD (unverified)
mov es,ax ;ES=UDA
;Check for KEEP, SYS Flags
;DS:DI->QD
test q_flags[di],qf_keep
jnz qd_err1
test q_flags[di],qf_hide
jz qd_ok1
mov bx,rlr ;if hide flag then
test p_flag[bx],pf_sys ;SYS flag must be on in PD
jnz qd_ok1
qd_err1: mov cx,e_q_protected
mov bx,0ffffh
ret
qd_ok1:
push di ;DI->QD
call q_sync ;obtain q system; BX,DX preserved
pop di
mov ax,q_dq[di] ;if any process is NQing or
mov dx,q_nq[di] ;DQing we can't delete
or dx,ax ! mov cx,e_q_inuse
jnz qd_err2
mov bx,(offset qlr)-q_link
qd_nqd: ;look for QD on QLR
mov si,q_link[bx] ;DI=QD to delete
test si,si ! jz qd_noq
cmp si,di ! je qd_found
mov bx,si ! jmps qd_nqd ;try next queue
qd_found:
mov ax,q_link[di] ;found the queue, remove
mov q_link[bx],ax ;it from QLR
call remqd
xor bx,bx ;return success
jmps qd_ret
qd_noq:
mov cx,e_no_queue
qd_err2:
mov bx,0ffffh
qd_ret:
jmp q_unsync ;release q system
;ret ;BX,CX preserved
;=========== ============
readq_entry: ; Read Queue
;=========== ============
xor al,al ! jmps readq
;============
creadq_entry: ; Conditional Read Queue
;============
mov al,0ffh
; jmps readq
readq: ; Read message from queue
;----- -------------------------
; If no buffer is available the process
; making an unconditional READQ is placed into the DQ list.
;
; input: U_WRKSEG:DX = QPB
; AL = 0 if unconditional
; <> 0 if not
; output: BX = 0 if okay
; 0ffffh if error
; CX = Error Code
push ax ;save cond code
call q_sync ;get q system; BX,DX preserved
call queverify ;is the QPB valid ?
pop ax ;AL=cond code
mov si,dx ;U_WRKSEG:SI->QPB
jcxz qr_ver ;CX=0 QPB is ok
jmp qr_err ;CX=error code from
qr_ver: ;queverify
push es ;save UDA
mov es,u_wrkseg ;ES:SI->QPB
mov bx,es:qpb_qaddr[si] ;BX->QD
cmp q_msgcnt[bx],0 ;is there a msg to read ?
jne qr_readit
qr_wait:
pop es ;ES=UDA
test al,al ! jz qr_wait1 ;conditional read if AL <> 0
mov cx,e_q_empty
jmps qr_err
qr_wait1: ;ES=UDA
push bx ! push si ;QD, QPB
lea dx,q_dq[bx] ;DX=addr of DQ List
mov bl,ps_dq ;sleep status=DQ
call q_wait ;wait for a DQ
;we now own the q system
;and cannot be aborted
pop si ! pop bx ;QPB, QD
push es ;save UDA
mov es,u_wrkseg
qr_readit:
mov di,es:qpb_buffptr[si] ;ES:[DI]->user's queue buffer
mov cx,q_msglen[bx] ;ES=U_WRKSEG
test cx,cx ! jnz qr_lmsg ;check message length
xor ax,ax
test q_flags[bx],qf_mx ;msglen=0, check for MX q
jz qr_end
mov ax,rlr ;its a MX queue
mov q_buf[bx],ax ;BUF = PD addr of owner
xor ax,ax
jmps qr_end
qr_lmsg: ;msglen > 0
mov ax,q_msgout[bx] ! push ax ;MSGOUT is # of message to read
mul cx ! add ax,q_buf[bx] ;compute its start in buffer
mov si,ax
rep movsb ;move to QPB buffer
pop ax ! inc ax ;set MSGOUT to next message
cmp ax,q_nmsgs[bx] ;circular buffers, so
jne qr_end ;check for wrap around
xor ax,ax
qr_end:
pop es ;ES=UDA
mov q_msgout[bx],ax
dec q_msgcnt[bx]
lea dx,q_nq[bx]
call q_assign_sync ;give the queue system to
;first NQing process if any,
call q_unsync ;release q system if we still
;own it, exit no abort region
xor bx,bx ;indicate success
ret
qr_err:
call q_unsync ;release q system, exit no abort
;region, BX,CX preserved
mov bx,0ffffh ;indicate error
ret
;============ =============
writeq_entry: ; Write Queue
;============ =============
xor al,al ! jmps writeq
;============= =========================
cwriteq_entry: ; conditional Write Queue
;============= =========================
mov al,0ffh
;jmps writeq
writeq: ; Write message to queue
;------ ------------------------
; If no buffer is available when making an unconditional
; WRITEQ call, the calling process is placed into the NQ list.
;
; input: U_WRKSEG:DX = QPB
; AL = 0 if unconditional
; <> 0 if not
; output: BX = 0 if okay
; 0ffffh if error
; CX = Error Code
push ax ;save cond code
call q_sync ;get queue system
;BX,DX preserved
call queverify ;is the QPB valid ?
pop ax ;AL = cond code
mov di,dx ;U_WRKSEG:DI->QPB
jcxz qw_ver ;CX=0 QPB is ok
jmp qw_err ;CX=error code from
qw_ver: ;queverify
push es ;save UDA
mov es,u_wrkseg ;ES:DI->QPB
mov bx,es:qpb_qaddr[di] ;BX->QD
mov cx,q_msgcnt[bx]
cmp cx,q_nmsgs[bx] ;is there a buffer to write ?
jne qw_writeit
qw_wait:
pop es ;ES=UDA
test al,al ! jz qw_wait1 ;conditional read if AL <> 0
mov cx,e_q_full
jmps qw_err
qw_wait1: ;ES=UDA
push bx ! push di ;save QD, QPB
lea dx,q_nq[bx] ;DX=addr of NQ List
mov bl,ps_nq ;sleep status=NQ
call q_wait ;sleep on NQ List
;q system is assigned to us,
;we are in no abort region
;by DQing process that woke us
pop di ! pop bx ;QPB,QD
push es ;save UDA
mov es,u_wrkseg
qw_writeit:
mov si,es:qpb_buffptr[di] ;U_WRKSEG:[SI]->user's queue buffer
mov cx,q_msglen[bx] ;ES=U_WRKSEG
test cx,cx ! jnz qw_lmsg ;check message length
test q_flags[bx],qf_mx ;msglen=0, check for MX q
jz qw_end
xor ax,ax
mov q_buf[bx],ax ;its a MX queue
jmps qw_end ;BUF = 0
qw_lmsg: ;msglen > 0
mov ax,q_msgout[bx]
add ax,q_msgcnt[bx] ;AX = # of message to write
cmp ax,q_nmsgs[bx] ;check for wrap around
jb qw_move
sub ax,q_nmsgs[bx]
qw_move:
mul cx ! add ax,q_buf[bx] ;compute its start in buffer
mov di,ax ;DI offset of new msg in buffer
mov ax,ds ! mov dx,es
mov es,ax ! mov ds,dx ;ES=SYSDAT, DS=U_WRKSEG
rep movsb ;move to QPB buffer
mov ax,es ! mov ds,ax ;DS=SYSDAT
qw_end:
pop es ;ES=UDA
inc q_msgcnt[bx] ;one more message in the queue
lea dx,q_dq[bx] ;wake DQing process if any
call q_assign_sync
call q_unsync ;release q system if we still
xor bx,bx ;own it, exit no abort region
ret ;BX=0: success
qw_err:
call q_unsync ;release q system; exit no abort
;region BX,CX preserved
mov bx,0ffffh ;indicate error
ret
q_wait: ;wait on DQ or NQ list
;------
; entry: DX = list to wait on
; BL = sleep status
; calling process owns queue system through
; a call to q_sync
; Queue message or buffer space is not available.
; Give up queue system and sleep on DQ or NQ list, but
; do not allow any other process to read or write to a queue
; until we get on the sleep list.
pushf ! cli ;keep abort spec from running ...
push bx ! push dx ;save sleep status, list address
call q_unsync ;does not go to dispatcher, we can be
pop dx ! pop bx ;aborted once on sleep list
call sleep_entry ;go to dispatcher
popf ;come back after q_assign call
ret ;Q_ASSIGN: wakes us up when resource
;is ready
q_sync: ;obtain ownership of q system
;------
; entry: interrupts should be on
; ES=UDA
; exit: we own the queue system for make,open,read,write,delete
; queue operations, interrupts unchanged
; BX,DX preserved - usually entry parameters
push bx ! push dx
call no_abort_entry ;we cannot abort while in the queue system
mov bx,offset q_spb
call sync_entry
pop dx ! pop bx
ret
q_unsync: ;release the queue system
;-------- ;to other processes
; entry: interrupts can be or off
; exit: interrupts unchanged,
; have not gone to dispatcher,
; BX,CX preserved - usually return codes
push bx ! push cx
mov bx,offset q_spb
call unsync_entry ;wait for queue system
call ok_abort_entry ;allow calling PD to be terminated
pop cx ! pop bx
ret
q_assign_sync:
;-------------
; entry: DX = address of DQ or NQ list to give the queue
; exit: none
; Assign is used so a process is forced to read or write
; after DQing or NQing. Otherwise an awakening DQing or NQing
; process could be deleted, leaving a message or buffer space
; available with other processes still DQing or NQing.
; This message or buffer space would never be reclaimed.
; To prevent this situation, the waking DQing or NQing process
; is kept from aborting and assigned the QSPB. Note
; it is ok for a process to be aborted while it is on the NQ or
; DQ list before this routine is called.
pushf ! cli ;keep abort spec from terminating
mov bx,dx ;waking process
mov dx,[bx] ;first process on list
test dx,dx ;is there a process to wake?
jz qa_ret
push bx ;save list root
call no_abort_spec_entry ;can not abort while in queue
pop dx ;system
popf ;interrupts are ok now -
mov bx,offset q_spb ;present owner and next owner
jmp assign_sync_entry ;have TEMPKEEP on
qa_ret:
popf
ret