; This source file is part of the LynxLib miscellaneous library by
; Robert Fischer, and is Copyright 1990 by Robert Fischer.  It costs no
; money, and you may not make money off of it, but you may redistribute
; it.  It comes with ABSOLUTELY NO WARRANTY.  See the file LYNXLIB.DOC
; for more details.
; To contact the author:
; 	Robert Fischer \\80 Killdeer Rd \\Hamden, CT   06517   USA
; 	(203) 288-9599     fischer-robert@cs.yale.edu

* Coroutine master
* Uses C calling conventions

* Process control block structure
* typedef enum {P_NEW, P_BLOCKED, P_READY, P_RUNNING, P_DEAD} procstate;
*
* typedef struct {
*	proc_state state;		process state
*	char *stack;			current stack pointer
*	message *msg;			message value
* } pcb

* PCB offsets
stack		= 0

; -------------------------------------------
; A PROCESS IS IDENTIFIED BY ITS STACK POINTER
; -------------------------------------------

* invoke(f, x, p)
* Calls f(x) in process p.  Returns when p blocks or terminates.
*	func *f;	Function to call
*	LONG x;		argument to give it
*	pcb *p;		process control block for that function
*

_init_co_s::
*	Pop parameters from master stack
	move.l	4(sp), a2	; function f
	move.l	8(sp), d1	; argument x
	move.l	12(sp), a1	; pcb address p

	move.l	stack(a1), a0

*	Almost call function
	move.l	d1, -(a0)		; push argument x
	move.l	#term, -(a0)	; Push termination address
	move.l	a2, -(a0)		; Push resume() address
	lea		-36(a0), a0		; Dummy-save the registers

	move.l	a0, stack(a1)
	rts

term:
* Switch to master stack
	move.l	resume_sp, a0
	move.l	(a0)+, sp
	move.l	a0, resume_sp
	movem.l	(sp)+, a3-a6/d3-d7

*	Don't need to save slave's stack pointer because slave is DEAD

*	Return to master for good
	move.l	#0, d0		; Put a FALSE in d0 to indicate f has terminated
	rts

* ----------------------------------------------------------------------

* BOOLEAN resume(p, m, ret_msg)
*	pcb *p;
*	message m;
*	message *ret_msg;	Return message from slave
* returns FALSE if p terminates, TRUE if it's still going
*
* Called by master.
* Resumes process p, which should be in state P_READY.
* Passes p.msg to the resumed process as a returned value.

_resume_co::
*	Pick up argument
	move.l	4(sp), a1	; pointer to slave's stack pointer
	move.l	8(sp), d0	; Message for resumed process

*	Prepare for eventual return to master
	movem.l	a3-a6/d3-d7, -(sp)

*	Push master's sp onto resume_stack
	move.l	resume_sp, a0
	move.l	sp, -(a0)
	move.l	a0, resume_sp

*	Switch stacks to slave stack
	move.l	stack(a1), sp

*	Return to slave from rendezvous
	; return message in d0 (above)
	movem.l	(sp)+, a3-a6/d3-d7	; restore registers
	rts			; return

* ----------------------------------------------------------------------

* message to_master(to_msg)
*	message to_msg
*
* Called by slave function.
* Blocks current process and passes control back to master.
* When process is resumed, the message in the pcb is passed as
* the value of the function.

_to_master::

*	Save things about slave's stack
	move.l	4(sp), d1	; slave's message
	movem.l	a3-a6/d3-d7, -(sp)	; save registers
	move.l	sp, d0		; save slave's stack pointer

*	Switch to master's stack
	move.l	resume_sp, a0
	move.l	(a0)+, sp
	move.l	a0, resume_sp
	movem.l	(sp)+, a3-a6/d3-d7

*	Find stack** variable on master's stack & store slave's stack * in it
	move.l	4(sp), a0
	move.l	d0, (a0)	; Save into pcb

* Store away slave's message
	move.l	12(sp), a0
	move.l	d1, (a0)	; Stuff slave's message into *ret_msg

	move.l	#-1, d0		; Put a TRUE in d0 to indicate f has not terminated
	rts

* ----------------------------------------------------------------------
	.bss

; In this stack, every time resume() is called, the pcb * of the process
; being resumed is pushed on the stack.  At tell_master, this is popped
; off the stack so tell_master() can tell which process is returning
resume_stack : ds.l	100
resume_end :			; End of resume stack
	.data
resume_sp : dc.l resume_end		; Resume stack pointer
