$title('MP/M 1.1 Dispatcher')
	name	dsptch
;dsptch:
;do;


;/*
;  Copyright (C) 1979,1980
;  Digital Research
;  P.O. Box 579
;  Pacific Grove, CA 93950
;
;  Revised:
;    18 Jan 80 by Thomas Rolander
;*/

;/*
;    Common Literals
;*/

;  declare true literally '0FFFFH';
;  declare false literally '0';
;  declare forever literally 'while true';
;  declare boolean literally 'byte';

;  declare enter$region literally
;    'disable';

;/*
;    Proces Literals
;*/

;  declare process$header literally
;    'structure (pl address,
;                status byte,
;                priority byte,
;                stkptr address';
;  declare bdos$save literally
;               'disk$set$dma address,
;                disk$slct byte,
;                dcnt byte,
;                searchl byte,
;                searcha address,
;                thread address,
;                scratch (14) byte)';
;  declare process$descriptor literally
;               'process$header,
;                name (8) byte,
;                console byte,
;                memseg byte,
;                 b address,
;                bdos$save';

;  declare rtr$status       literally '0',
;          dq$status        literally '1',
;          nq$status        literally '2',
;          poll$status      literally '3',
;          FlgWt$status     literally '4',
;          Delay$status     literally '5',
;          Swap$status      literally '6',
;          Terminate$status literally '7',
;          Set$Prior$status literally '8',
;          Dispatch$status  literally '9',
;          Attach$status    literally '10',
;          Detach$status    literally '11',
;          Set$cns$status   literally '12';

;/*
;    Memmgr Literals
;*/

;  declare memory$descriptor literally
;    'structure (base byte,
;                size byte,
;                attrib byte)';

;/*
;    Th Literals
;*/

;  declare nmb$consoles literally '2';
	extrn	maxcns

;/*
;    Bdosi Externals
;*/

;  xios$pl:
	extrn	xiospl
;    procedure (device) byte external;
;      declare device byte;
;    end xios$pl;

;  xios$ms:
	extrn	xiosms
;    procedure (memsegadr) external;
;      declare memsegadr address;
;    end xios$ms;

;  str$clk:
	extrn	strclk
;    procedure external;
;    end str$clk;

;/*
;    Flag Externals
;*/

;  declare sys$flag (1) address external;
	extrn	sysflag

;/*
;    Memmgr Externals
;*/

;  declare memsegtbl (1) memory$descriptor external;
	extrn	memsegtbl

;  user$process:
	extrn	userprocess
;    procedure (pdadr) byte external;
;      declare pdadr$address;
;    end user$process;

;  mem$fr:
	extrn	memfr
;    procedure (mdadr) external;
;      declare mdadr address;
;    end mem$fr;

;/*
;    Th Externals
;*/

;  attach:
	extrn	attach
;    procedure (pdadr) external;
;      declare pdadr address;
;     end attach;

;  detach:
	extrn	detach
;    procedure (pdadr) external;
;      declare pdadr address;
;    end detach;


	dseg

;  declare rlr address external;
	extrn	rlr

;  declare rlrpd based rlr process$descriptor;

;  declare thread$root address external;
	extrn	thrdrt

;  declare drl address external;
	extrn	drl

;  declare drlpd based drl process$descriptor;

;  declare plr address external;
	extrn	plr

;  declare dlr address external;
	extrn	dlr

;  declare slr address external;
	extrn	slr

;  declare indisp byte public initial (0);
	public	indisp
indisp:	db	0

;  declare dsptch$param address public;
	public dparam
dparam:
dsptchparam:
	dw	0

;  declare pdadr address;
pdadr:
	dw	0

;  declare pd based pdadr process$descriptor;

;  declare pollpointer address;
pollpointer:
	dw	0

;  declare oldpollptr address;
oldpollptr:
nextthreadptr:
	dw	0

;  declare oldpoll based oldpollptr address;

;  declare next$thread$ptr address at (.oldpollptr);

;  declare next$thread based next$thread$ptr address;

;  declare pdladr address;
pdladr:
	dw	0

;  declare pdl based pdladr process$descriptor;

;  declare first$time boolean;


;/*
;  delay$list$insert:
;          The purpose of the delay list insert procedure is to
;        place a process descriptor on the delay list.  The delay
;        list is maintained in such a manner that the tick counts
;        in the process descriptor represent the value required to
;        produce the specified delay, by adding together all the
;        tick counts of the preceding processes on the delay list.

;  Entry Conditions:
;        Dispatch$param = ticks

;  Exit Conditions:
;        Note

;        *** NOTE *** this procedure assumes that
;                      a critical region has been entered.
;*/

	cseg

;  delay$list$insert:
delaylistinsert:
;    procedure;

	dseg
;      declare accum address;
accum:
	dw	0
;      declare nxtpdladr address;
nxtpdladr:
	dw	0
;      declare nxtpdl based nxtpdladr address;
;

	cseg
;      dsptch$param = dsptch$param+1;
	lxi	h,DSPTCHPARAM
	inr	m
;        if dlr = 0 then call strclk;
	LHLD	DLR
	MOV	A,H
	ORA	L
	CZ	STRCLK
;      accum = 0;
	LXI	H,0H
	SHLD	ACCUM
;      nxtpdladr = .dlr;
	LXI	H,DLR
	SHLD	NXTPDLADR
;        do forever;
;
@25:
;          pdladr = nxtpdl;
	MOV	E,M
	INX	H
	MOV	D,M
	XCHG
	SHLD	PDLADR
;          if (nxtpdl = 0) or
	XCHG
	DCX	H
	MOV	A,M
	INX	H
	ORA	M
	JZ	@25A
	LXI	H,10H
	DAD	D
	XCHG
	LHLD	ACCUM
	CALL	PADD
	LXI	D,DSPTCHPARAM
	CALL	PSUB
	JNC	@3
;             ((accum + pdl.b) > dsptch$param) then
;          do;
@25A:
;            pd.pl = nxtpdl;
	LHLD	NXTPDLADR
	MOV	C,M
	INX	H
	MOV	B,M
	DCX	H
	XCHG
	LHLD	PDADR
	MOV	M,C
	INX	H
	MOV	M,B
;            nxtpdl = pdadr;
	DCX	H
	XCHG
	MOV	M,E
	INX	H
	MOV	M,D
;            accum = dsptch$param - accum;
	LHLD	ACCUM
	LXI	D,DSPTCHPARAM
	CALL	PADD
	SHLD	ACCUM
;            pd.b = accum;
	XCHG
	LXI	B,10H
	LHLD	PDADR
	DAD	B
	PUSH	H
	MOV	M,E
	INX	H
	MOV	M,D
;            if pd.pl <> 0 then pdl.b = pdl.b - accum;
	LHLD	PDADR
	MOV	A,M
	INX	H
	ORA	M
	POP	D
	RZ
	LHLD	ACCUM
	CALL	PSUB
	XCHG
	DCX	H
	MOV	M,E
	INX	H
	MOV	M,D
;            return;
	RET
;          end;
@3:
;          accum = accum + pdl.b;
	LXI	B,10H
	LHLD	PDLADR
	DAD	B
	XCHG
	LHLD	ACCUM
	CALL	PADD
	SHLD	ACCUM
;          nxtpdladr = nxtpdl;
	LHLD	NXTPDLADR
	MOV	E,M
	INX	H
	MOV	D,M
	XCHG
	SHLD	NXTPDLADR
;        end;
	JMP	@25
;      end;
;    end delay$list$insert;

PADD:
	; HL = [DE] + HL
	LDAX	D
	ADD	L
	MOV	L,A
	INX	D
	LDAX	D
	ADC	H
	MOV	H,A
	RET

PSUB:
	; HL = [DE] - HL
	LDAX	D
	SUB	L
	MOV	L,A
	INX	D
	LDAX	D
	SBB	H
	MOV	H,A
	RET

;/*
;  insert$process:
;          The purpose of the insert process procedure is to
;        place a process desciptor into a list pointed to by a
;        passed parameter in priority order.  The algorithm
;        searches for a process in the list with a lower priority
;        (lower priorities have high priority numbers) to make
;        the insertion.  This ensures that processes of the same
;        priority are round-robin scheduled.

;  Entry Conditions:
;        BC = list root
;        DE = process descriptor

;        *** NOTE *** this procedure assumes that
;                      a critical region has been entered.

;  Exit Conditions:
;        None
;*/

	cseg
@5:
	MOV	B,H
	MOV	C,L
;  insert$process:
inspr:
insertprocess:
;    procedure (nxtpdladr,pdadr) public;
	public	inspr

;      declare (nxtpdladr,pdadr) address;
;      declare nxtpdl based nxtpdladr address;
;      declare pd based pdadr process$descriptor;


;      do forever;
;        pdladr = nxtpdl;
	; BC = NXTPDLADR, DE = PDADR
@27:
	LDAX	B
	MOV	L,A
	INX	B
	LDAX	B
	MOV	H,A
;        if (nxtpdl = 0) or
	ORA	L
	INX	D
	INX	D
	INX	D
	LDAX	D
	DCX	D
	DCX	D
	DCX	D
	JZ	@27A
	; HL = PDLADR
	INX	H
	INX	H
	INX	H
	SUB	M
	DCX	H
	DCX	H
	DCX	H
	JNC	@5
;           (pd.priority < pdl.priority) then
;        do;
;          pd.pl = nxtpdl;
@27A:
	MOV	A,L
	STAX	D
	INX	D
	MOV	A,H
	STAX	D
	DCX	D
;          nxtpdl = pdadr;
	MOV	A,D
	STAX	B
	DCX	B
	MOV	A,E
	STAX B
;          return;
	RET
;        end;
;        nxtpdladr = nxtpdl;
;      end;
;    end insert$process;

;/*
;  dispatch:
;          The purpose of the dispatch procedure is to perform the
;        action required by the status of the current running
;        process, to determine the highest priority ready process,
;        and then to give it the processor resource.  The procedure
;        takes all the processes linked to the dispatcher
;        ready list and inserts them into the ready list before
;        executing the highest priority ready process.

;  Entry Conditions:
;        None

;  Exit Conditions:
;        None
;*/

	dseg
svhl:	ds	2	; saved HL
svde:	ds	2	; saved DE
svret:	ds	2	; saved return
svsp:	ds	2	; saved SP

dspstk:
	dw	0c7c7h,0c7c7h,0c7c7h,0c7c7h,0c7c7h,0c7c7h
	dw	0c7c7h,0c7c7h,0c7c7h,0c7c7h,0c7c7h,0c7c7h

	cseg
;  dispatch:
pdisp:
	public	pdisp
;    Note:  This entry point should only be used from an
;           interrupt handler, i.e. interrupts disabled.
;    rlrpd.status = dispatch
	di
	shld	svhl
	lhld	rlr
	inx	h
	inx	h
	mvi	m,9
	lhld	svhl

dispatch:
;    procedure public;
	public	dispatch

;      enter$region;
	DI
;
; save registers in the Process Desciptor
;
	shld	svhl
	pop	h
	shld	svret
	push	psw
	lxi	h,0
	dad	sp
	shld	svsp
	xchg
	shld	svde
	lhld	rlr
	lxi	d,50
	dad	d
	xchg
	pop	psw
	lhld	svret
	push	h
	xchg
	sphl
	lhld	svde
	xchg
	lhld	svhl

	PUSH	PSW
	PUSH	B
	PUSH	D
	PUSH	H
;        call svz80;

	public	dspsvz80
dspsvz80:
	jmp	noz80save
	db	0ddh,0e5h	;Push IX
	db	0fdh,0e5h	;Push IY
	db	008h		;Ex AF,AF'
	db	0d9h		;Exx
	push	psw
	push	b
	push	d
	push	h
noz80save:

	mvi	a,0ffh
	sta	indisp		;set in dipatcher flag

;        pdadr = stackptr;
	lhld	svsp
	XCHG
;        rlrpd.stkptr = pdadr;
	LHLD	RLR
;        pdadr = rlr;
	SHLD	PDADR
	MOV	B,H
	MOV	C,L
	inx	h
	inx	h
	inx	h
	inx	h
	MOV	M,E
	INX	H
	MOV	M,D
;
	LXI	SP,DSPSTK+24
;        if rlrpd.status <> rtr$status then rlr = rlrpd.pl;
	dcx	h
	dcx	h
	dcx	h
	MOV	A,M
	ORA	A
	JZ	@7
	DCX	H
	DCX	H
	MOV	E,M
	INX	H
	MOV	D,M
	XCHG
	SHLD	RLR
;        do case pd.status;
	MOV	E,A
	MVI	D,0
	LXI	H,@29
	DAD	D
	DAD	D
	MOV	E,M
	INX	H
	MOV	D,M
	LHLD	DSPTCHPARAM
	XCHG
	PCHL

;          /* status = 0, Ready to Run */
;           do;
;          end;

;          /* status = 1, DQ */
;          do;
@9:
;            call insert$process (dsptch$param,pdadr);
;          end;

;          /* status = 2, NQ */
;          do;
@10:
;            call insert$process (dsptch$param,pdadr);
	MOV	H,B
	MOV	L,C
	MOV	B,D
	MOV	C,E
	XCHG
;          end;
	JMP	@6

;          /* status = 3, Poll */
;          do;
@11:
;            pd.b = dsptch$param;
	LXI	H,10H
	DAD	B
	MOV	M,E
	INX	H
	MOV	M,D
;            pd.pl = plr;
	LHLD	PLR
	XCHG
	LHLD	PDADR
	MOV	M,E
	INX	H
	MOV	M,D
;            plr = pdadr;
	DCX	H
	SHLD	PLR
;          end;
	JMP	@7

;          /* status = 4, Flag Wait */
;          do;
@12:
;            sys$flag(dsptch$param) = pdadr;
	LXI	H,SYSFLAG
	DAD	D
	DAD	D
	MOV	M,C
	INX	H
	MOV	M,B
;            pd.pl = 0;
	XRA	A
	STAX	B
	INX	B
	STAX	B
;          end;
	JMP	@7

;          /* status = 5, Delay */
;          do;
@13:
;            call delay$list$insert;
	CALL	DELAYLISTINSERT
;          end;
	JMP	@7

;          /* status = 6, Swap */
;          do;
@14:
;            call insert$process (.slr,pdadr);
	MOV	D,B
	MOV	E,C
	LXI	B,SLR
;          end;
	JMP	@6

;          /* status = 7, Terminate */
;          do;
@15:
;             if dsptch$param = 0ffh or /* sys pr. term */
;               user$process (pdadr) then
	PUSH	D
	CALL	USERPROCESS
	POP	D
	RAR
	JC	@15A
	INR	E
	JZ	@15C
	JMP	@16
;            do;
@15A:
;              call mem$fr (.memsegtbl(pd.memseg));
;	free memory segment only if BDOS system reset call
	LHLD	DSPTCHPARAM
	INR	H
	JZ	@15C
	LXI	B,0FH
	LHLD	PDADR
	DAD	B
	MOV	A,M
	LXI	H,MEMSEGTBL
	ADD	A
	ADD	A
	MOV	E,A
	MVI	D,0
	DAD	D
	MOV	B,H
	MOV	C,L
	CALL	MEMFR
@15c:
;              call rlsmx (pdadr);
	lhld	pdadr
	mov	b,h
	mov	c,l
	extrn	rlsmx
	call	rlsmx	; release all owned MX queues
;              call detach (pdadr);
	LHLD	PDADR
	MOV	B,H
	MOV	C,L
	CALL	DETACH
;              next$thread$ptr = .thread$root;
	LHLD	PDADR
	MOV	B,H
	MOV	C,L
	LXI	H,thrdrt
@30:
	; BC = PDADR, HL = NEXTTHREADPTR
;              do while (pdladr := next$thread) <> pdadr;
	MOV	E,M
	INX	H
	MOV	D,M
	; DE = PDLADR, NEXTTHREAD
	MOV	A,E
	CMP	C
	JNZ	@30A
	MOV	A,D
	CMP	B
	JZ	@31
@30A:
;                next$thread$ptr = .pdl.thread;
	LXI	H,12H
	DAD	D
;              end;
	JMP	@30
@31:
;              next$thread = pdl.thread;
	DCX	H
	XCHG
	LXI	B,12H
	DAD	B
	MOV	A,M
	STAX	D
	INX	D
	INX	H
	MOV	A,M
	STAX	D
;            end;

	JMP	@7
@16:
;            else
;            do;
;              pd.status = rtr$status;
	LHLD	PDADR
	INX	H
	INX	H
	MVI	M,0H
;              pd.pl = drl;
;              drl = pdadr;
;            end;
;          end;
	JMP	@19

;          /* status = 8, Set Priority */
;          do;
@18:
;            pd.priority = dsptch$param;
	INX	B
	INX	B
	INX	B
	MOV	A,L
	STAX	B
;            pd.pl = drl;
;            drl = pdadr;
;          end;
;	JMP	@19

;          /* status = 9, Dispatch */
;          do;
@19:
;            pd.pl = drl;
	LHLD	DRL
	XCHG
	LHLD	PDADR
	MOV	M,E
	INX	H
	MOV	M,D
;            drl = pdadr;
	DCX	H
	SHLD	DRL
;          end;
	JMP	@7

;          /* status = 10, Attach */
;          do;
;@20:  (see end of set console)
;            call attach (pdadr);
;          end;

;          /* status = 11, Detach */
;          do;
@21:
;            call detach (pdadr);
	CALL	DETACH
;            pd.pl = drl;
;            drl = pdadr;
;          end;
	JMP	@19

;          /* status = 12, Set Console */
;          do;
@22:
;            call detach (pdadr);
	CALL	DETACH
;            if dsptch$param < nmb$consoles then
	CALL	MAXCNS
	MOV	B,A
	LDA	DSPTCHPARAM
	CMP	B
	JNC	@7
;            do;
;              pd.console = dsptch$param;
	LXI	B,0EH
	LHLD	PDADR
	DAD	B
	MOV	M,A
;              call attach (pdadr);
@20:
	LHLD	PDADR
	MOV	B,H
	MOV	C,L
	CALL	ATTACH
;            end;
;          end;
	JMP	@7

;        end; /* case */
@29:
	DW	@7
	DW	@9
	DW	@10
	DW	@11
	DW	@12
	DW	@13
	DW	@14
	DW	@15
	DW	@18
	DW	@19
	DW	@20
	DW	@21
	DW	@22
@6:
	CALL	INSERTPROCESS
@7:

;        /* Put ready processes on ready list and poll
;           devices required -> at least once then until
;           there is a process ready to run              */
;        do while first$time or (rlr = 0);
@32:
;          /* insert all processes on ready list from
;             the dispatcher ready list               */
;          do while drl <> 0;
@34:
	LHLD	DRL
@34A:
	MOV	A,H
	ORA	L
	JZ	@35
;            pdadr = drlpd.pl;
	MOV	E,M
	INX	H
	MOV	D,M
	PUSH	D
;            drlpd.status = rtr$status;
	INX	H
	MVI	M,0H
;            call insert$process (.rlr,drl);
	DCX	H
	DCX	H
	XCHG
	LXI	B,RLR
	CALL	INSERTPROCESS
;            drl = pdadr;
	POP	H
	SHLD	DRL
;          end;
	JMP	@34A
@35:

;          /* poll all required devices and place any
;             readied processes on the ready list      */
;          pdadr = .plr;
	LXI	D,PLR
;          pollpointer = plr;
	LHLD	PLR
;          do while pollpointer <> 0;
@36:	; BC = oldpollptr, DE = pdadr, HL = pollpointer
	MOV	A,H
	ORA	L
	JZ	@37
;            oldpollptr = pdadr;
	MOV	B,D
	MOV	C,E
;            pdadr = pollpointer;
	MOV	D,H
	MOV	E,L
;            pollpointer = pd.pl;
	MOV	A,M
	INX	H
	MOV	H,M
	MOV	L,A
;            if xios$pl (pd.b) then
	PUSH	B
	PUSH	D
	PUSH	H
	LXI	H,10H
	DAD	D
	MOV	C,M
	CALL	XIOSPL
	POP	H
	POP	D
	POP	B
	RAR
	JNC	@36
;            do;
;              oldpoll = pollpointer;
	MOV	A,L
	STAX	B
	INX	B
	MOV	A,H
	STAX	B
	DCX	B
	PUSH	B
	PUSH	H
;              pd.status = rtr$status;
	MOV	H,D
	MOV	L,E
	INX	H
	INX	H
	MVI	M,0
;              call insert$process (.rlr,pdadr);
	LXI	B,RLR
	CALL	INSERTPROCESS
	POP	H
	POP	B
;              pdadr = oldpollptr;
	MOV	D,B
	MOV	E,C
;            end;
;          end;
	JMP	@36
@37:
;          first$time = false;
;        end;

;        call xios$ms (.memsegtbl(rlrpd.memseg));
	lhld	rlr
	LXI	B,0FH
	DAD	B
	MOV	A,M
	add	a
	jm	@38	;system process, no xiosms
	add	a
	LXI	H,MEMSEGTBL
	mov	e,a
	mvi	d,0
	dad	d
	MOV	B,H
	MOV	C,L
	CALL	XIOSMS

;        stackptr = rlrpd.stkptr;
@38:
	LHLD	RLR
	LXI	B,4H
	DAD	B
	MOV	E,M
	INX	H
	MOV	D,M
	XCHG
	shld	svsp

	xra	a
	sta	indisp	;clear in dispatcher flag

	lhld	rlr
	lxi	d,50-8
	dad	d
	sphl

	public	dsprsz80
dsprsz80:
	jmp	noz80restore

	lxi	d,-12
	dad	d
	sphl

	pop	h
	pop	d
	pop	b
	pop	psw
	db	0d9h		;Exx
	db	008h		;Ex AF,AF'
	db	0fdh,0e1h	;Pop IY
	db	0ddh,0e1h	;Pop IX
noz80restore:

	POP	H
	POP	D
	POP	B
	POP	PSW
;      exit$region;

	shld	svhl
	lhld	svsp
	sphl
	lhld	svhl

	EI
	RET
;    end dispatch;

;end dsptch;

	end
