$title	 ('MP/M 1.1 Extended Disk Operating System')
	name	xdos
	cseg
;xdos:
;do;

;/*
;  Copyright (C) 1979,1980
;  Digital Research
;  P.O. Box 579
;  Pacific Grove, CA 93950
;
;  Revised:
;    18 Jan 80 by Thomas Rolander
;*/

;$include (common.lit)
;$nolist
;$include (queue.lit)
;$nolist
;$include (proces.lit)
;$nolist
;$include (memmgr.lit)
;$nolist
;$include (datapg.ext)
;$nolist
;$include (proces.ext)
;$nolist
;$include (queue.ext)
;$nolist
;$include (flag.ext)
;$nolist
;$include (memmgr.ext)
;$nolist
;$include (th.ext)
;$nolist
;  parse$filename:
	extrn	parsefilename
;    procedure (pcb$address) address external;
;      declare pcb$address address;
;    end parse$filename;

;  exitr:
	extrn	exitr
;    procedure external;
;    end exitr;

;  flgwt:
	extrn	flgwt
;    procedure (flgnmb) byte external;
;      declare flgnmb byte;
;    end flgwt;

;  flgset:
	extrn	flgset
;    procedure (flgnmb) byte external;
;      declare flgnmb byte;
;    end flgset;

;  absrq:
	extrn	absrq
;    procedure (mdadr) byte external;
;      declare mdadr address;
;    end absrq;

;  relrq:
	extrn	relrq
;    procedure (mdadr) byte external;
;      declare mdadr address;
;    end relrq;

;  memfr:
	extrn	memfr
;    procedure (mdadr) byte external;
;      declare mdadr address;
;    end memfr;

;  dispat:
	extrn	dispat
;    procedure external;
;    end dispat;

;  makeq:
	extrn	makeq
;    procedure (qcbadr) byte external;
;      declare qcbadr address;
;    end makeq;

;  openq:
	extrn	openq
;    procedure (uqcbadr) byte external;
;      declare uqcbadr address;
;    end openq;

;  deletq:
	extrn	deletq
;    procedure (uqcbadr) byte external;
;      declare uqcbadr address;
;    end deletq;

;  readq:
	extrn	readq
;    procedure (uqcbadr) byte external;
;      declare uqcbadr address;
;    end readq;

;  creadq:
	extrn	creadq
;    procedure (uqcbadr) byte external;
;      declare uqcbadr address;
;    end creadq;

;  writeq:
	extrn	writeq
;    procedure (uqcbadr) byte external;
;      declare uqcbadr address;
;    end writeq;

;  cwriteq:
	extrn	cwriteq
;    procedure (uqcbadr) byte external;
;      declare uqcbadr address;
;    end cwriteq;

;  assign:
	extrn	assign
;    procedure (pname) byte external;
;      declare pname address;
;    end assign;

;  declare dparam address external;
	extrn	dparam

;  declare rlr address external;
	extrn	rlr

;  declare drl address external;
	extrn	drl

;  declare dlr address external;
	extrn	dlr

;  declare plr address external;
	extrn	plr

;  declare slr address external;
	extrn	slr

;  declare qlr address external;
	extrn	qlr

;  declare thrdrt address external
	extrn	thrdrt

;  declare nmbcns byte external;
	extrn	nmbcns

;  declare cnsque (1) address external;
	extrn	cnsque

;  declare tod structure (
	extrn	tod
;    day address,
;    hr byte,
;    min byte,
;    sec byte ) external;

;  declare nmbflags byte external;
	extrn	nmbflags

;  declare sysfla (1) address external;
	extrn	sysfla

;  declare sysdat address external;
	extrn	sysdat

;  declare cli$lqcb address external;
	extrn	clilqcb

	dseg
;  declare cli$uqcb userqcbhead
;    initial (.cli$lqcb,0);
cliuqcb:
	dw	clilqcb	; pointer
	dw	$-$	; msgadr

;  declare rsp$adr address;
rspadr:	ds	2

;  declare rsp$uqcb userqcb
;    initial (0,.rsp$adr,'$$$$$$$$');
rspuqcb:
	dw	$-$	; pointer
	dw	rspadr	; msgadr
	db	'$$$$$$$$'	; name

;  declare cpb$adr address;
cpbadr:	ds	2
;  declare cpb based cpb$adr structure (
;    rsp$name$adr address,
;    rsp$param address);

;  declare flag$wait$function literally '4';
;  declare flag$set$function literally '5';

;  declare (svstk,old$drl,old$thread$root) address;
svstk:	ds	2
olddrl:	ds	2
oldthreadroot:
	ds	2
	cseg

;  xdos:
xdos:
	public	xdos
;    procedure (function,parameter) address reentrant public;
;      declare function byte;
;      declare parameter address;
;      declare ret address;
;      declare pd based parameter process$descriptor;

;      ret = 0000H;
	MOV	A,C
;      function = function - 128;
	SUI	80H
;      if function > max$xdos$function then
	CPI	maxfunc
	LXI	H,0FFFFH
	RNC
;      do;
;        ret = 0FFFFH;
;      end;
;      else
;      do case function;
	CPI	4H
	JZ	@1	; SETUP RETURN TO DISPATCH
	CPI	5H	;  FOR ALL BUT FLAG OP'S
	JZ	@1
	LXI	H,DISPAT
	PUSH	H
@1:
	LXI	H,EXDOS
	PUSH	H
	MOV	B,D
	MOV	C,E
	MOV	E,A
	MVI	D,0
	LXI	H,@37
	DAD	D
	DAD	D
	MOV	E,M
	INX	H
	MOV	D,M
	LHLD	RLR
	INX	H
	INX	H	; HL = .RLR.STATUS
	XCHG
	PCHL

;        /* function = 128, Absolute Memory Request */
;        ret = abs$rq (parameter);
;	CALL	ABSRQ

;        /* function = 129, Relocatable Memory Request */
;        ret = rel$rq (parameter);
;	CALL	RELRQ

;        /* function = 130, Memory Free */
;        call mem$fr (parameter);
;	CALL	MEMFR

;        /* function = 131, Poll Device */
;        do;
@7:
;          enter$region;
	DI
;          dsptch$param = parameter;
	MOV	L,C
	MOV	H,B
	SHLD	DPARAM
;          rlrpd.status = poll$status;
	XCHG
	MVI	M,3H
;        end;
	RET

;        /* function = 132, Flag Wait */
;        ret = flag$wait (parameter);
;	CALL	FLGWT

;        /* function = 133, Flag Set */
;        ret = flag$set (parameter);
;	CALL	FLGSET

;        /* function = 134, Make Queue */
;        ret = makeq (parameter);
;	CALL	MAKEQ

;        /* function = 135, Open Queue */
;        ret = openq (parameter);
;	CALL	OPENQ

;        /* function = 136, Delete Queue */
;        ret = deletq (parameter);
;	CALL	DELETQ

;        /* function = 137, Read Queue */
;        ret = readq (parameter);
;	CALL	READQ

;        /* function = 138, Conditional Read Queue */
;        ret = creadq (parameter);
;	CALL	CREADQ

;        /* function = 139, Write Queue */
;        ret = writeq (parameter);
;	CALL	WRITEQ

;        /* function = 140, Conditional Write Queue */
;        ret = cwriteq (parameter);
;	CALL	CWRITEQ

;        /* function = 141, Delay */
;        do;
@17:
;          enter$region;
	DI
;          dsptch$param = parameter;
	MOV	L,C
	MOV	H,B
	SHLD	DPARAM
;          rlrpd.status = delay$status;
	XCHG
	MVI	M,5H
;        end;
	RET

;        /* function = 142, Dispatch */
;        do;
@18:
;          enter$region;
	DI
;          rlrpd.status = dispatch$status;
	XCHG
	MVI	M,9H
;        end;
	RET

;        /* function = 143, Terminate */
;        do;
@19:
;          enter$region;
	DI
;          dsptch$param = parameter;
	MOV	L,C
	MOV	H,B
	SHLD	DPARAM
;          rlrpd.status = terminate$status;
	XCHG
	MVI	M,7H
;        end;
	RET

;        /* function = 144, Create */
;        do;
@20:
;          enter$region;
	DI
;          old$drl = drl;
	LHLD	DRL
	SHLD	OLDDRL
;          old$thread$root = thread$root;
	LHLD	THRDRT
	SHLD	OLDTHREADROOT
;          drl,
;          thread$root = parameter;
	MOV	L,C
	MOV	H,B
	SHLD	DRL
	SHLD	THRDRT
;          do while pd.pl <> 0;
@38:
	MOV	E,M
	INX	H
	MOV	D,M
	MOV	A,E
	ORA	D
	JZ	@39
;            pd.thread = pd.pl;
	LXI	H,12H
	DAD	B
	MOV	M,E
	INX	H
	MOV	M,D
	CALL @39A
;            parameter = pd.pl;
	MOV	B,D
	MOV	C,E
	XCHG
;          end;
	JMP	@38
@39:
;          pd.pl = old$drl;
	XCHG
	LHLD	OLDDRL
	XCHG
	MOV	M,D
	DCX	H
	MOV	M,E
;          pd.thread = old$thread$root;
	LHLD	OLDTHREADROOT
	XCHG
	LXI	H,12H
	DAD	B
	MOV	M,E
	INX	H
	MOV	M,D
@39A:
;            pd.drvacc = 0;
	lxi	h,1ch
	dad	b
	xra	a
	mov	m,a
	inx	h
	mov	m,a
;        end;
	RET

;        /* function = 145, Set Priority */
;        do;
@21:
;          enter$region;
	DI
;          dsptch$param = parameter;
	MOV	L,C
	MOV	H,B
	SHLD	DPARAM
;          rlrpd.status = set$prior$status;
	XCHG
	MVI	M,8H
;        end;
	RET

;        /* function = 146, Attach */
;        do;
@22:
;          enter$region;
	DI
;          rlrpd.status = attach$status;
	XCHG
	MVI	M,0AH
;        end;
	RET

;        /* function = 147, Detach */
;        do;
@23:
;          enter$region;
	DI
;          rlrpd.status = detach$status;
	XCHG
	MVI	M,0BH
;        end;
	RET

;        /* function = 148, Set Console */
;        do;
@24:
	lxi	h,nmbcns
	mov	a,c
	cmp	m
	mvi	a,0ffh
	rnc			;** return if bad console #
;          enter$region;
	DI
;          dsptch$param = parameter;
	MOV	L,C
	MOV	H,B
	SHLD	DPARAM
;          rlrpd.status = set$cns$status;
	XCHG
	MVI	M,0CH
;        end;
	RET

;        /* function = 149, Assign Console */
;        ret = assign (parameter);
;	CALL	ASSIGN

;        /* function = 150, Send CLI Command */
;        do;
@26:
;          cli$uqcb.msgadr = parameter;
	MOV	L,C
	MOV	H,B
	SHLD	CLIUQCB+2H
;          ret = writeq (.cli$uqcb);
	LXI	B,CLIUQCB
	JMP	WRITEQ
;        end;

;        /* function = 151, Call Resident System Process */
;        do;
@27:
;          enter$region;
	DI
	POP	H	; DISCARD EXDOS RETURN
;            cpb$adr = parameter;
	MOV	L,C
	MOV	H,B
	SHLD	CPBADR
;            call move (8,cpb.rsp$name$adr,.rsp$uqcb.name);
	MOV	C,M
	INX	H
	MOV	B,M
	LXI	D,RSPUQCB+4H
	MVI	L,8H
	LDAX	B
	STAX	D
	INX	B
	INX	D
	DCR	L
	JNZ	$-5H
;            /* open queue having passed procedure name */
;            if low(ret := xdos (135,.rsp$uqcb)) <> 0ffh then
	LXI	B,RSPUQCB
	CALL	OPENQ
	INR	A
	LXI	H,0001H
	JZ	@30
;            do;
;              /* read queue to get procedure entry point address */
;              ret = xdos (137,.rsp$uqcb);
;              /* execute the procedure (function) */
	LXI	B,RSPUQCB
	CALL	READQ
;              ret = xfunc (cpb.rsp$param,rsp$adr);
;              /* write queue to put message back on queue, this
;                 mechanism makes the procedure a serially
;                 resuseable resource                            */
	LHLD	CPBADR
	INX	H
	INX	H
	MOV	C,M
	INX	H
	MOV	B,M
	LHLD	RSPADR
	LXI	D,XFUNC
	PUSH	D
	PCHL
XFUNC:
;              if low(xdos (139,.rsp$uqcb)) = 0ffh
	PUSH	H
	LXI	B,RSPUQCB
	CALL	WRITEQ
	INR	A
	POP	H
	JNZ	@30
;                then ret = 0ffffh;
	LXI	H,0FFFFH
;            end;
;            else
;            do;
;              /* procedure not resident */
;              ret = 1;
;            end;
;          exit$region;
;        end;
@30:
	PUSH	H
	CALL	EXITR
	POP	H
	RET

;        /* function = 152, Parse Filename */
;        ret = parse$filename (parameter);
@31:
	POP	H	; DISCARD EXDOS RETURN
	JMP	PARSEFILENAME

;        /* function = 153, Get Console Number */
;        ret = rlrpd.console;
@32:
	LXI	B,0CH
	XCHG
	DAD	B
	MOV	A,M
	RET

;        /* function = 154, System Data Address */
;        ret = sysdat;
@33:
	POP	H	; DISCARD EXDOS RETURN
	LHLD	SYSDAT
	RET

;        /* function = 155, Get Time & Date */
;        do;
@34:
;          call move (5,.tod,parameter);
	LXI	D,TOD
	MVI	L,5H
	LDAX	D
	STAX	B
	INX	B
	INX	D
	DCR	L
	JNZ	$-5H
;        end;
	RET

;        /* function = 156, Return Process Descriptor Address */
;	 do;
rtnpdadr:
;          return rlr;
	pop	h	; discard EXDOS return
	lhld	rlr
;        end;
	ret

;        /* function = 157, Abort Specified Process */
;        do;
abort:
;
; BC -> Abort$parameter$control$block
; declare apcb structure (
;   pdadr address,
;   param address,
;   pname (8) byte );
	ldax	b
	inx	b
	mov	e,a
	ldax	b
	inx	b
	mov	d,a
	ora	e	;test for PD address present
	ldax	b
	inx	b
	mov	l,a
	ldax	b
	inx	b
	mov	h,a	;DE = apcb.pdadr, HL = apcb.param
	di
	shld	dparam
	shld	tparam
	jnz	@15	;jump if already have pdadr
;      pdadr = thread$root;
	LHLD	THRDRT
	XCHG
;      do while pdadr <> 0;
@9:
;        i = 1;
	PUSH	B
	LXI	H,6
	DAD	D
	PUSH	H
	MVI	L,9	;** this includes the pd.console byte !
;        do while (i <> 9) and (pd.name(i-1) = pname(i));
@11:
	XTHL
	LDAX	B
	sub	M
	INX	B
	INX	H
	JZ	@11A
	ani	7fh
	jz	@11a	;also don't care on high order bit
	POP	H
	POP	B
	JMP	@6
@11A:
	XTHL
	DCR	L
	JNZ	@11
;          i = i + 1;
;        end;
	pop	h
	pop	b
	jmp	@15
@6:
;        pdadr = pd.thread;
	LXI	H,12H
	DAD	D
	MOV	E,M
	INX	H
	MOV	D,M
	MOV	A,D
	ORA	E
	JNZ	@9
;      end;
exitabort:
;      return 0FFFFH;
	call	exitr
	mvi	a,0ffh
	RET
@15:
	lxi	h,6
	dad	d
	mov	a,m
	cpi	'@'
	jz	exitabort	;cannot abort @pdname
	lhld	rlr
	mov	a,e
	cmp	l
	jnz	@16
	mov	a,d
	cmp	h
	jnz	@16
;
; aborting the running process
	inx	h
	inx	h
	mvi	m,7	;set pd.status to terminate
	ret
@16:
	lxi	b,setupabort
	push	b		;setup return address
	lxi	h,2
	dad	d
	mov	c,m
	mvi	b,0
	lxi	h,abtbl
	dad	b
	dad	b
	mov	a,m
	inx	h
	mov	h,m
	mov	l,a
	pchl

abtbl:
	dw	abtrun	; 0 = Ready to run
	dw	abtque	; 1 = DQ
	dw	abtque	; 2 = NQ
	dw	abtpol	; 3 = Poll
	dw	abtflg	; 4 = Flag Wait
	dw	abtdly	; 5 = Delay
	dw	abtswp	; 6 = Swap
	dw	abtrun	; 7 = Terminate
	dw	abtrun	; 8 = Set Priority
	dw	abtrun	; 9 = Dispatch
	dw	abtcns	;10 = Attach
	dw	abtrun	;11 = Detach
	dw	abtcns	;12 = Set Console

;
; 0 Ready to run
; 7 Terminate
; 8 Set Priority
; 9 Dispatch
;11 Detach
;
abtrun:
	pop	h	;discard return addr
	jmp	procrdy	;no action simply setup abort

;
; 1 DQ
; 2 NQ
;
abtque:
	;find queue link & remove
	lxi	b,qlr
abtq0:
	ldax	b
	mov	l,a
	inx	b
	ldax	b
	mov	h,a
	ora	l
	rz		;not DQing or NQing ?
	push	h
	lxi	b,14
	dad	b
	mov	b,h
	mov	c,l
	push	b
	call	delpr
	pop	b
	pop	h
	rc		;return if DQing & removed
	push	h
	inx	b
	inx	b
	call	delpr
	pop	b
	rc		;return if NQing & removed
	jmp	abtq0

;
; 3 Poll
;
abtpol:
	;remove PD from poll list
	lxi	b,plr
	jmp	delpr
	;ret

;
; 4 Flag Wait
;
abtflg:
	;remove PD from flag
	lda	nmbflags
	mov	c,a
	inr	c
	lxi	h,sysfla-1
abtfl0:
	inx	h
	dcr	c
	rz		;not waiting for a flag ?!
	mov	a,e
	cmp	m
	inx	h
	jnz	abtfl0
	mov	a,d
	cmp	m
	jnz	abtfl0
	mvi	m,0ffh
	dcx	h
	mvi	m,0ffh
	ret

;
; 5 Delay
;
abtdly:
	;remove PD from delay list
	lxi	b,dlr
	jmp	delpr
	;ret

;
; 6 Swap
;
abtswp:
	;remove PD from swap list
	lxi	b,slr
	jmp	delpr
	;ret

;
;10 Attach
;12 Set Console
;
abtcns:
	;remove PD from console queue
	lda	nmbcns
	inr	a
	mov	l,a
	lxi	b,cnsque
abct0:
	dcr	l
	rz		;not queued for any console ?
	push	b
	push	h
	call	delpr
	pop	h
	pop	b
	inx	b
	inx	b
	jnc	abct0
	ret

setupabort:
	;put PD on dispatcher ready list
	lhld	drl
	xchg
	mov	m,e
	inx	h
	mov	m,d
	dcx	h
	shld	drl
	xchg
procrdy:
	;compute process return address location in stack
	;and fill in with address of abort code.
	lxi	h,abortcode
	shld	abtcdadr
	lxi	h,4
	dad	d	;HL = .pd.stkptr
	lxi	d,abtstack
	mov	m,e
	inx	h
	mov	m,d
	call	exitr
	xra	a
	ret

abortcode:
	di
	mvi	c,143
	lxi	d,$-$	;Note: Potential critical parameter !
tparam	equ	$-2
	jmp	xdos

	dseg
abtstack:
abtcdadr:
	dw	abortcode

	cseg

;        end;

;      end; /* case */
@37:
	DW	ABSRQ
	DW	RELRQ
	DW	MEMFR
	DW      @7
	DW	FLGWT
	DW	FLGSET
	DW	MAKEQ
	DW	OPENQ
	DW	DELETQ
	DW	READQ
	DW	CREADQ
	DW	WRITEQ
	DW	CWRITEQ
	DW      @17
	DW      @18
	DW      @19
	DW      @20
	DW      @21
	DW      @22
	DW      @23
	DW      @24
	DW	ASSIGN
	DW      @26
	DW      @27
	DW	@31
	DW      @32
	DW      @33
	DW      @34
	dw	rtnpdadr
	dw	abort
maxfunc	equ	($-@37)/2

;      if function <> flag$set$function then
;      do;
;        if function <> flag$wait$function then
;        do;
;          call dispatch;
;        end;
;      end;
;      return ret;
EXDOS:
	MVI	H,0
	MOV	L,A
	RET
;    end xdos;

delpr0:
	mov	b,h
	mov	c,l
;  delete$process:
;    procedure (nxtpdladr,pdadr) public;
delpr:
	public	delpr
;      declare (nxtpdladr,pdadr) address;
;      declare pdladr based nxtpdladr address;
	ldax	b
	mov	l,a
	inx	b
	ldax	b
	dcx	b
	mov	h,a
	ora	l
	rz		;end of list with no match
	mov	a,l
	cmp	e
	jnz	delpr0
	mov	a,h
	cmp	d
	jnz	delpr0
	mov	a,m	;found match, update pointers
	stax	b
	inx	h
	inx	b
	mov	a,m
	stax	b
	stc		;indicate success
	ret
;end xdos;
	END
