	title	'MP/M II V2.0 Queue Management'
	name	'queue'
	dseg
@@queue:
	public	@@queue
	cseg
;queue:
@queue:
	public	@queue
;do;

;$include (copyrt.lit)
;/*
;  Copyright (C) 1979,1980,1981
;  Digital Research
;  P.O. Box 579
;  Pacific Grove, CA 93950
;
;  Revised:
;    14 Sept 81  by Thomas Rolander
;*/
;$include (common.lit)
;$nolist
;$include (proces.lit)
;$nolist
;$include (queue.lit)
;$nolist
;$include (datapg.ext)
;$nolist
;$include (proces.ext)
;$nolist


;  declare insert$process literally 'inspr';
;  insert$process:
	extrn	inspr
;    procedure (pdladr,pdadr) external;
;      declare (pdladr,pdadr) address;
;    end insert$process;

;  exitr:
	extrn	exitr
;    procedure external;
;    end exitr;

;  dispat:
	extrn	dispat
;    procedure external;
;    end dispat;

;  declare qlr address external;
	extrn	qlr

;  declare drl address external;
	extrn	drl

;  declare rlr address external;
	extrn	rlr

;  declare dparam address external;
	extrn	dparam

; queue offsets
bufos	equ	24

;/*
;  makeq:  The purpose of the make queue procedure is to setup
;         a queue control block.  A queue is configured as either
;         circular or linked depending upon the message size.
;         Message sizes 0 to 2 use circular queues while > 2 use
;         linked queues.  Note that the make queue does not
;         automatically open the queue, this must be done in
;         another explicit operation.

;  Entry Conditions:
;          BC = address of queue control block

;  Exit Conditions:
;          A  = return code,
;                where   0 = success,
;                      FFH = failure
;*/
;  makeq:
makeq:
	public	makeq
;    procedure (qcbadr) byte reentrant public;
;      declare qcbadr address;
;      declare qcb based qcbadr queuehead;
;      declare cqcb based qcbadr circularqueue;
;      declare lqcb based qcbadr linkedqueue;
;      declare (i,j,nxtmsgadr,bufadr,offset) address;
;      declare buf based bufadr (1) byte;
;      declare nxtmsg based nxtmsgadr address;

;      if qcb.msglen < 3 then
	LXI	H,0000BH
	DAD	B		; HL = .qcb.msglen+1
	MOV	A,M
	ORA	A
	JNZ	@1
	DCX	H
	MOV	A,M
	CPI	3H
	JNC	@1
;      do;  /* setup circular queue */
;        cqcb.msgin,
;        cqcb.msgout,
;        cqcb.msgcnt = 0;
	LXI	H,0012H
	DAD	B
	MVI	E,6
	XRA	A
@1A:
	MOV	M,A
	INX	H
	DCR	E
	JNZ	@1A
;      end;
	JMP	@2
@1:
;      else
;      do;  /* setup linked queue */
;        lqcb.mh = 0;
	LXI	H,0012H
	DAD	B
	XRA	A
	MOV	M,A
	INX	H
	MOV	M,A
;        lqcb.mt = .lqcb.mh;
	MOV	D,H
	MOV	E,L
	DCX	D
	INX	H
	MOV	M,E
	INX	H
	MOV	M,D
;        bufadr,
;        lqcb.bh = .lqcb.buf;
	INX	H
	MOV	D,H
	MOV	E,L
	INX	D
	INX	D
	MOV	M,E
	INX	H
	MOV	M,D
;        offset = lqcb.msglen + 2;
	LXI	H,000AH
	DAD	B
	PUSH	B
	MOV	C,M
	INX	H
	MOV	B,M
	INX	B
	INX	B
;        i = lqcb.nmbmsgs - 1;
	INX	H
	MOV	A,M
	INX	H
	MOV	H,M
	MOV	L,A
	PUSH	H
;        j = 0;
;        do while i <> 0;
@32:
	POP	H
	DCX	H
	MOV	A,H
	ORA	L
	JZ	@33
	PUSH	H
;          i = i - 1;
;          nxtmsgadr = .buf(j);
;          nxtmsg = .lqcb.buf(j+offset);
;          j = j + offset;
;        end;
	MOV	H,D
	MOV	L,E
	DAD	B
	XCHG
	MOV	M,E
	INX	H
	MOV	M,D
	JMP	@32
@33:
;        buf(j),
;        buf(j+1) = 0;
	STAX	D
	INX	D
	STAX	D
	POP	B
;      end;
@2:
;      qcb.dqph,
;      qcb.nqph = 0;
	LXI	H,00EH
	DAD	B
	XRA	A
	MOV	M,A
	INX	H
	MOV	M,A
	INX	H
	MOV	M,A
	INX	H
	MOV	M,A
;      enter$region;
	DI
;        qcb.ql = qlr;
	LHLD	QLR
	XCHG
	MOV	H,B
	MOV	L,C
	MOV	M,E
	INX	H
	MOV	M,D
;        qlr = .qcb.ql;
	DCX	H
	SHLD	QLR
;      exit$region;
	CALL	EXITR
;      return 0;
	XRA	A
	RET
;    end makeq;

;/*
;  openq:  The purpose of the open queue procedure is to place the
;         actual queue control block address into the user queue
;         control block.  The QCB address is obtained by searching
;         the queue list from the QLR to match the user QCB name
;         with the actual QCB name.

;  Entry Conditions:
;         BC = address of user queue control block

;  Exit Conditions:
;         A  = return code,
;               where   0 = success,
;                     FFH = failure
;*/
;  openq:
openq:
	public	openq
;    procedure (uqcbadr) byte reentrant public;
;      declare uqcbadr address;
;      declare uqcb based uqcbadr userqcb;
;      declare qcbadr address;
;      declare qcb based qcbadr queuehead;
;      declare i byte;

;      disable;
	di
;      qcbadr = qlr;
	LHLD	QLR
	XCHG
;      do while qcbadr <> 0;
	PUSH	B
@34:
	POP	B
	MOV	A,D
	ORA	E
	MVI	A,0FFH
	jnz	@35
	ei
	ret
@35:
;        i = 0;
	PUSH	B
	INX	B
	INX	B
	INX	B
	INX	B	; BC = .UQCB.NAME(0)
	LXI	H,0002H
	DAD	D
	PUSH	H	; TOS = .QCB.NAME(0)
	MVI	L,8
;        do while (i <> 8) and (qcb.name(i) = uqcb.name(i));
@36:
	XTHL
	LDAX	B
	CMP	M
	JZ	@36A
	POP	H
	XCHG
	MOV	E,M
	INX	H
	MOV	D,M
	JMP	@34
;          i = i + 1;
@36A:
	INX	B
	INX	H
	XTHL
	DCR	L
;        end;
	JNZ	@36
;        if i = 8 then
;        do;
;          uqcb.pointer = qcbadr;
	POP	B
	POP	H
	MOV	M,E
	INX	H
	MOV	M,D
;          return 0;
	XRA	A
	ei
	RET
;        end;
@3:
;        qcbadr = qcb.ql;
;      end;
;      return 0FFH;
;    end openq;

;/*
;  deletq:
;          The purpose of the delete queue procedure is to remove
;        the specified queue from the queue list.  Before this can
;        be done tests must be made to determine if there are any
;        processes which are on the queues NQ or DQ lists.

;  Entry Conditions:
;          BC = address of queue control block

;  Exit Conditions:
;          A  = return code,
;                where   0 = success,
;                      FFH = failure
;*/
;  deletq:
deletq:
	public	deletq
;    procedure (qcbadr) byte reentrant public;
;      declare qcbadr address;
;      declare qcb based qcbadr queuehead;
;      declare nxtqcbadr address;
;      declare nxtqcb based nxtqcbadr queuehead;

;      enter$region;
	DI
;        nxtqcbadr = qlr;
;	LHLD	QLR
;        if (qcb.dqph = 0) and
	LXI	H,000EH
	DAD	B
	MOV	A,M
	INX	H
	ORA	M
	INX	H
	ORA	M
	INX	H
	ORA	M
	JNZ	@4
;           (qcb.nqph = 0) then
	LXI	H,QLR
;        do while nxtqcbadr <> 0;
@38:
	MOV	A,L
	ORA	H
	JZ	@4
;          if nxtqcb.ql = qcbadr then
	MOV	A,M
	CMP	C
	JNZ	@5
	INX	H
	MOV	A,M
	DCX	H
	CMP	B
	JNZ	@5
;          do;
;            nxtqcb.ql = qcb.ql;
	LDAX	B
	MOV	M,A
	INX	B
	INX	H
	LDAX	B
	MOV	M,A
;            exit$region;
	CALL	EXITR
;            return 0;
	XRA	A
	RET
;          end;
@5:
;          nxtqcbadr = nxtqcb.ql;
	MOV	E,M
	INX	H
	MOV	D,M
	XCHG
;        end;
	JMP	@38
@4:
;      exit$region;
	CALL	EXITR
;      return 0FFH;
	MVI	A,0FFH
	RET
;    end deletq;

;/*
;  rdynqph:
;          The ready NQ process head procedure is called when a
;        buffer is freed by the action of readq. Rdynqph determines
;        if a process has been placed in the NQ state waiting for
;        an available buffer at that queue.  If such a process
;        exists the process is placed on the dispatcher ready list
;        and the NQ process head list is updated.

;  Entry Conditions:
;        BC = address of queue descriptor

;  Exit Conditions:
;        None
;*/

;  rdynqph:
rdynqph:
;    procedure (qcbadr) reentrant;
;      declare qcbadr address;
;      declare qcb based qcbadr queuehead;
;      declare pdadr address;
;      declare pd based pdadr process$descriptor;

;      if qcb.nqph <> 0 then
	LXI	H,0010H
RNQDQPH:
	DAD	B
	MOV	E,M
	INX	H
	MOV	D,M
	MOV	A,D
	ORA	E
	JZ	EXITR
;      do;
;        pdadr = qcb.nqph;
	; DE = PDADR, BC = QCBADR
;        qcb.nqph = pd.pl;
	DCX	H
	LDAX	D
	MOV	M,A
	INX	D
	INX	H
	LDAX	D
	MOV	M,A
;        pd.status = rtr$status;
	INX	D
	XRA	A
	STAX	D
;        call insert$process (.drl,pdadr);
	DCX	D
	DCX	D
	LXI	B,DRL
	CALL	INSPR
;      end;
;      exit$region;
	JMP	EXITR
;    end rdynqph;

;/*
;  readq:
;          The purpose of the read queue procedure is to read
;        a message from the specified queue.  If no message is
;        available the calling process is placed into the DQ
;        status, relinquishing the processor, until a message
;        is posted at the queue.

;  Entry Conditions:
;        BC = address of user queue control block

;  Exit Conditions:
;        None
;*/

;  readq:
readq:
	public	readq
;    procedure (uqcbadr) byte reentrant public;
;      declare uqcbadr address;
;      declare uqcb based uqcbadr userqcb;
;      declare qcbadr address;
;      declare qcb based qcbadr queuehead;
;      declare cqcb based qcbadr circularqueue;
;      declare lqcb based qcbadr linkedqueue;
;      declare msglnkadr address;
;      declare msglnk based msglnkadr address;
;      declare i address;
;      declare i$cont based i address;

;      qcbadr = uqcb.pointer;
	LDAX	B
	MOV	E,A
	INX	B
	LDAX	B
	MOV	D,A
	INX	B
	LDAX	B
	MOV	H,A
	INX	B
	LDAX	B
	MOV	B,A
	MOV	C,H
;      do forever;
@40:
	; BC = UQCB.MSGADR, DE = QCBADR
;        if qcb.msglen < 3 then
	LXI	H,000BH
	DAD	D
	MOV	A,M
	ORA	A
	JNZ	@7
	DCX	H
	MOV	A,M
	CPI	3H
	JNC	@7
;        do;  /* reading message from a circular queue */
;          enter$region;
	DI
;          if cqcb.msgcnt <> 0 then
	LXI	H,0016H
	DAD	D
	MOV	A,M
	INX	H
	ORA	M
	JZ	@14
;          do;
;            if cqcb.msglen <> 0 then
	LXI	H,000AH
	DAD	D
	MOV	A,M
	ORA	M
	JZ	@9
;            do;
	; A = MSGLEN, BC = UQCB.MSGADR, DE = QCBADR
	PUSH	B
	LXI	H,0014H
	DAD	D
	MOV	C,M
	INX	H
	MOV	B,M	; BC = CQCB.MSGOUT
	LXI	H,0018H
	DAD	D
	DAD	B
;              if cqcb.msglen = 2 then i = i + i;
	DCR	A
	JZ	@10A
	DAD	B
@10A:
	POP	B
;              call move (cqcb.msglen,.cqcb.buf(i),uqcb.msgadr);
	MOV	A,M
	STAX	B
	JZ	@10B
	INX	B
	INX	H
	MOV	A,M
	STAX	B
@10B:
;              cqcb.msgout = (cqcb.msgout+1) mod cqcb.nmbmsgs;
	LXI	H,000CH
	DAD	D
	MOV	C,M
	INX	H
	MOV	B,M
	PUSH	B	; TOS,BC = CQCB.NMBMSGS
	LXI	H,0014H
	DAD	D
	MOV	C,M
	INX	H
	MOV	B,M
	INX	B
	XTHL
	MOV	A,C
	CMP	L
	JNZ	@10C
	MOV	A,B
	CMP	H
	JNZ	@10C
	LXI	B,0000H
@10C:
	POP	H
	MOV	M,B
	DCX	H
	MOV	M,C
;            end;
	JMP	@11
@9:
;            else
;            do;
;              if cqcb.name(0) = 'M' then
	LXI	H,0002H
	DAD	D
	MOV	A,M
	CPI	'M'
	JNZ	@12
;              do;
;                if cqcb.name(1) = 'X' then
	INX	H
	MOV	A,M
	CPI	'X'
	JNZ	@13
;                do;
;                  i = qcbadr + 24;
;                  /* put pdadr into MX queue */
;                  icont = rlr;
	LHLD	RLR
	MOV	B,H
	MOV	C,L
	LXI	H,0018H
	DAD	D
	MOV	M,C
	INX	H
	MOV	M,B
;                end;
@13:
;              end;
@12:
;            end;
@11:
;            cqcb.msgcnt = cqcb.msgcnt - 1;
	LXI	H,0016H
	DAD	D
	MOV	C,M
	INX	H
	MOV	B,M
	DCX	B
	MOV	M,B
	DCX	H
	MOV	M,C
;            call rdynqph (qcbadr);
	MOV	B,D
	MOV	C,E
	CALL	RDYNQPH
;            return 0;
	XRA	A
	RET
;          end;
;        end;
@7:	; BC = uqcb.msgadr, DE = qcbadr
;        else
;        do;  /* reading message from a linked queue */
;          enter$region;
	DI
;          if lqcb.mh <> 0 then
	LXI	H,0012H
	DAD	D
	PUSH	D	; TOS = QCBADR
	MOV	E,M
	INX	H
	MOV	D,M
	MOV	A,E
	ORA	D
	JZ	@15
;          do;
;            msglnkadr = lqcb.mh;
	PUSH	D
;            lqcb.mh = msglnk;
	LDAX	D
	DCX	H
	MOV	M,A
	INX	D
	INX	H
	LDAX	D
	MOV	M,A
;            if msglnk = 0 then lqcb.mt = .lqcb.mh;
	DCX	H
	ORA	M
	JNZ	@16
	MOV	D,H
	MOV	E,L
	INX	H
	INX	H
	MOV	M,E
	INX	H
	MOV	M,D
@16:
;            exit$region;
	PUSH	B
	CALL	EXITR
	POP	D
;            call move (lqcb.msglen,msglnkadr+2,uqcb.msgadr);
	POP	B
	POP	H
	PUSH	H
	PUSH	B
	INX	B
	INX	B
	; BC = MSGLNKADR+2, DE = UQCB.MSGADR, HL = QCBADR
	MOV	A,L
	ADI	0AH
	MOV	L,A
	MOV	A,H
	ACI	00H
	MOV	H,A
	MOV	A,M
	INX	H
	MOV	H,M
	MOV	L,A	; HL = LQCB.MSGLEN
	LDAX	B
	STAX	D
	INX	B
	INX	D
	DCX	H
	MOV	A,H
	ORA	L
	JNZ	$-7H
;            enter$region;
	POP	D	; DE = MSGLNKADR
	POP	B	; BC = QCBADR
	DI
;            msglnk = lqcb.bh;
	LXI	H,16H
	DAD	B
	MOV	A,M
	STAX	D
	INX	D
	INX	H
	MOV	A,M
	STAX	D
;            lqcb.bh = msglnkadr;
	DCX	D
	MOV	M,D
	DCX	H
	MOV	M,E
;            call rdynqph (qcbadr);
	CALL	RDYNQPH
;            return 0;
	XRA	A
	RET
;          end;
@15:
	POP	D
;        end;
@14:
;        rlrpd.status = dq$status;
	LHLD	RLR
	INX	H
	INX	H
	MVI	M,1H
;        dsptch$param = .qcb.dqph;
	LXI	H,000EH
	DAD	D
	SHLD	DPARAM
;        call dispatch;
	CALL	DISPAT
;      end; /* forever */
	JMP	@40
;    end readq;

;/*
;  creadq:
;          The purpose of the conditional read queue procedure
;        is to read a message from the specified queue.  If
;        no message is available a status of FFH is returned.

;  Entry Conditions:
;        BC = address of user queue control block

;  Exit Conditions:
;        A  = return code,
;              where   0 = success,
;                    FFH = failure
;*/

;  creadq:
creadq:
	public	creadq
;    procedure (uqcbadr) byte reentrant public;
;      declare uqcbadr address;
;      declare uqcb based uqcbadr userqcb;
;      declare qcbadr address;
;      declare qcb based qcbadr queuehead;
;      declare cqcb based qcbadr circularqueue;
;      declare lqcb based qcbadr linkedqueue;

;      qcbadr = uqcb.pointer;
	LDAX	B
	MOV	E,A
	INX	B
	LDAX	B
	DCX	B
	MOV	D,A	; BC = UQCBADR, DE = QCBADR
;      if qcb.msglen < 3 then
	LXI	H,000BH
	DAD	D
	MOV	A,M
	ORA	A
	JNZ	@17
	DCX	H
	MOV	A,M
	CPI	3H
	JNC	@17
;      do;  /* reading message from a circular queue */
;        enter$region;
	DI
;        if cqcb.msgcnt <> 0 then return readq (uqcbadr);
	LXI	H,0016H
	JMP	@20
;      end;
@17:
;      else
;      do;  /* reading message from a linked queue */
;        enter$region;
	DI
;        if lqcb.mh <> 0 then return readq (uqcbadr);
	LXI	H,0012H
@20:
	DAD	D
	MOV	A,M
	INX	H
	ORA	M
	JNZ	READQ
;      end;
;      exit$region;
	CALL	EXITR
;      return 0FFH;
	MVI	A,0FFH
	RET
;    end creadq;

;/*
;  rdydqph:
;          The ready DQ process head procedure is called when a
;        message is posted by the action of writeq. Rdydqph
;        determines if a process has been placed in the DQ state
;        waiting for a message at that queue.  If such a process
;        exists the process is placed on the dispatcher ready list
;        and the DQ process head list is updated.

;  Entry Conditions:
;        BC = address of queue descriptor

;  Exit Conditions:
;        None
;*/

;  rdydqph:
rdydqph:
;    procedure (qcbadr) reentrant;
;      declare qcbadr address;
;      declare qcb based qcbadr queuehead;
;      declare pdadr address;
;      declare pd based pdadr process$descriptor;

;      if qcb.dqph <> 0 then
	LXI	H,0000EH
	JMP	RNQDQPH
;      do;
;        pdadr = qcb.dqph;
;        qcb.dqph = pd.pl;
;        pd.status = rtr$status;
;        call insert$process (.drl,pdadr);
;      end;
;      exit$region;
;    end rdydqph;

;/*
;  writeq:
;          The purpose of the write queue procedure is to write
;        a message to the specified queue.  If no buffer is
;        available the calling process is placed into the NQ
;        status, relinquishing the processor, until a buffer
;        is returned to the queue.

;  Entry Conditions:
;        BC = address of user queue control block

;  Exit Conditions:
;        None
;*/

;  writeq:
writeq:
	public	writeq
;    procedure (uqcbadr) byte reentrant public;
;      declare uqcbadr address;
;      declare uqcb based uqcbadr userqcb;
;      declare qcbadr address;
;      declare qcb based qcbadr queuehead;
;      declare cqcb based qcbadr circularqueue;
;      declare lqcb based qcbadr linkedqueue;
;      declare i address;
;      declare msglnkadr address;
;      declare msglnk based msglnkadr address;
;      declare mtadr address;
;      declare mtcont based mtadr address;

;      qcbadr = uqcb.pointer;
	LDAX	B
	MOV	E,A
	INX	B
	LDAX	B
	MOV	D,A
	INX	B
	LDAX	B
	MOV	H,A
	INX	B
	LDAX	B
	MOV	B,A
	MOV	C,H
;      do forever;
@42:	; BC = uqcb.nsgadr, DE = qcbadr
;        if qcb.msglen < 3 then
	LXI	H,000BH
	DAD	D
	MOV	A,M
	ORA	A
	JNZ	@22
	DCX	H
	MOV	A,M
	CPI	3H
	JNC	@22
;        do;  /* writing message to a circular queue */
;          enter$region;
	DI
;          if cqcb.msgcnt <> cqcb.nmbmsgs then
	LXI	H,0017H
	DAD	D
	MOV	A,M
	DCX	H
	PUSH	H
	LXI	H,000DH
	DAD	D
	CMP	M
	DCX	H
	MOV	A,M
	XTHL
	JNZ	@42A
	CMP	M
@42A:
	POP	H
	JZ	@26
;          do;
;            if cqcb.msglen <> 0 then
	DCX	H
	DCX	H
	MOV	A,M
	ORA	A
	JZ	@24
;            do;
;              i = cqcb.msgin;
	PUSH	B
	LXI	H,0012H
	DAD	D
	MOV	C,M
	INX	H
	MOV	B,M
	LXI	H,0018H
	DAD	D
	DAD	B
;              if cqcb.msglen = 2 then i = i + i;
	DCR	A
	JZ	@25A
	DAD	B
@25A:
	POP	B
;              call move (cqcb.msglen,uqcb.msgadr,.cqcb.buf(i));
	LDAX	B
	MOV	M,A
	JZ	@25B
	INX	B
	INX	H
	LDAX	B
	MOV	M,A
@25B:
;              cqcb.msgin = (cqcb.msgin+1) mod cqcb.nmbmsgs;
	LXI	H,000CH
	DAD	D
	MOV	C,M
	INX	H
	MOV	B,M
	PUSH	B
	LXI	H,0012H
	DAD	D
	MOV	C,M
	INX	H
	MOV	B,M
	INX	B
	XTHL
	MOV	A,C
	CMP	L
	JNZ	@25C
	MOV	A,B
	CMP	H
	JNZ	@25C
	LXI	B,0000H
@25C:
	POP	H
	MOV	M,B
	DCX	H
	MOV	M,C
;            end;
@24:
;            cqcb.msgcnt = cqcb.msgcnt + 1;
	LXI	H,0016H
	DAD	D
	MOV	C,M
	INX	H
	MOV	B,M
	INX	B
	MOV	M,B
	DCX	H
	MOV	M,C
;            call rdydqph (qcbadr);
	MOV	B,D
	MOV	C,E
	CALL	RDYDQPH
;            return 0;
	XRA	A
	RET
;          end;
;        end;
@22:
;        else
;        do;  /* writing message to a linked queue */
;          enter$region;
	DI
;          if lqcb.bh <> 0 then
	LXI	H,0016H
	DAD	D
	PUSH	D
	MOV	E,M
	INX	H
	MOV	D,M
	MOV	A,E
	ORA	D
	JZ	@27
;          do;
;            msglnkadr = lqcb.bh;
	PUSH	D
;            lqcb.bh = msglnk;
	LDAX	D
	DCX	H
	MOV	M,A
	INX	D
	INX	H
	LDAX	D
	MOV	M,A
;            exit$region;
	PUSH	B
	CALL	EXITR
	POP	B
;            call move (lqcb.msglen,uqcb.msgadr,msglnkadr+2);
	POP	D
	POP	H
	PUSH	H
	PUSH	D
	INX	D
	INX	D
	; BC = UQCB.MSGADR, DE = MSGLNKADR+2, HL = QCBADR
	MOV	A,L
	ADI	0AH
	MOV	L,A
	MOV	A,H
	ACI	0
	MOV	H,A
	MOV	A,M
	INX	H
	MOV	H,M
	MOV	L,A
	; BC = UQCB.MSGADR, DE = MSGLNKADR+2, HL = LQCB.MSGLEN
	LDAX	B
	STAX	D
	INX	B
	INX	D
	DCX	H
	MOV	A,H
	ORA	L
	JNZ	$-7H
;            msglnk = 0;
	POP	D
	STAX	D
	INX	D
	STAX	D
	DCX	D
;            mtadr = lqcb.mt;
	LXI	H,0014H
	POP	B
	DAD	B
	PUSH	B
	MOV	C,M
	INX	H
	MOV	B,M
;            enter$region;
	DI
;            mtcont,
;            lqcb.mt = msglnkadr;
	MOV	A,E
	STAX	B
	INX	B
	MOV	A,D
	STAX	B
	MOV	M,D
	DCX	H
	MOV	M,E
;            call rdydqph (qcbadr);
	POP	B
	CALL	RDYDQPH
;            return 0;
	XRA	A
	RET
;          end;
@27:
	POP	D
;        end;
@26:
;        rlrpd.status = nq$status;
	LHLD	RLR
	INX	H
	INX	H
	MVI	M,2H
;        dsptch$param = .qcb.nqph;
	LXI	H,0010H
	DAD	D
	SHLD	DPARAM
;        call dispatch;
	CALL	DISPAT
;      end; /* forever */
	JMP	@42
;    end writeq;

;/*
;  cwriteq:
;          The purpose of the conditional write queue procedure
;        is to write a message to the specified queue.  If no
;        buffer is available the value FFH is returned.

;  Entry Conditions:
;        BC = address of user queue control block

;  Exit Conditions:
;        A  = return code,
;              where   0 = success,
;                    FFH = failure
;*/

;  cwriteq:
cwriteq:
	public	cwriteq
;    procedure (uqcbadr) byte reentrant public;
;      declare uqcbadr address;
;      declare uqcb based uqcbadr userqcb;
;      declare qcbadr address;
;      declare qcb based qcbadr queuehead;
;      declare cqcb based qcbadr circularqueue;
;      declare lqcb based qcbadr linkedqueue;

;      qcbadr = uqcb.pointer;
	LDAX	B
	MOV	E,A
	INX	B
	LDAX	B
	DCX	B
	MOV	D,A
;      if qcb.msglen < 3 then
	LXI	H,000BH
	DAD	D
	MOV	A,M
	ORA	A
	JNZ	@28
	DCX	H
	MOV	A,M
	CPI	3H
	JNC	@28
;      do;  /* writing message to a circular queue */
;        enter$region;
	DI
;        if cqcb.nmbmsgs <> cqcb.msgcnt
	LXI	H,000CH
	DAD	D
	MOV	A,M
	INX	H
	MOV	H,M
	MOV	L,A
	JMP	@29
;          then return writeq(uqcbadr);
;      end;
@28:
;      else
;      do;  /* writing message to a linked queue */
;        enter$region;
	DI
;        if lqcb.bh <> 0 then return writeq(uqcbadr);
	LXI	H,0000H
@29:
	PUSH	H
	LXI	H,0016H
	DAD	D
	MOV	E,M
	INX	H
	MOV	D,M
	POP	H
	MOV	A,E
	CMP	L
	JNZ	WRITEQ
	MOV	A,D
	CMP	H
	JNZ	WRITEQ
;      end;
;      exit$region;
	CALL	EXITR
;      return 0FFH;
	MVI	A,0FFH
	RET
;    end cwriteq;

;end queue;
	END
