258 lines
		
	
	
	
		
			6 KiB
		
	
	
	
		
			Text
		
	
	
	
	
	
			
		
		
	
	
			258 lines
		
	
	
	
		
			6 KiB
		
	
	
	
		
			Text
		
	
	
	
	
	
| #
 | |
| ;
 | |
| ; (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands.
 | |
| ; See the copyright notice in the ACK home directory, in the file "Copyright".
 | |
| ;
 | |
| ;
 | |
| ; Module:	implementation of coroutines
 | |
| ; Author:	Ceriel J.H. Jacobs
 | |
| ; Version:	$Header$
 | |
| ;
 | |
| ; This version works for EM implementations that have a contiguous downwards
 | |
| ; growing stack, on which data below the stack-pointer is not destroyed.
 | |
| ;
 | |
| #include <em_mes.h>
 | |
| #include <m2_traps.h>
 | |
| 
 | |
|  mes 2, EM_WSIZE, EM_PSIZE
 | |
| 
 | |
|  ; This file contains the implementation of the following routines from
 | |
|  ; the SYSTEM module:
 | |
|  ;	TRANSFER, NEWPROCESS
 | |
|  ; The NEWPROCESS routine creates a new coroutine stack frame.
 | |
|  ; The TRANSFER routine implements transfers from one coroutine to another.
 | |
|  ; The memory organization for coroutines is rather complicated.
 | |
|  ; One problem is caused by the fact that the user must allocate the
 | |
|  ; stackspace. So, this stackspace can be located anywhere, including on
 | |
|  ; the heap. This means that we cannot use this space as a stack, because
 | |
|  ; in EM, the stack-pointer may never point below the heap-pointer.
 | |
|  ; So, this space is only used to save the stack when the coroutine isn't
 | |
|  ; running.
 | |
|  ; It also contains information about the size of the frame, the
 | |
|  ; address of the procedure that forms the coroutine body, the offset
 | |
|  ; of the LB from the start of the frame, and the offset of the SP from
 | |
|  ; the start of the frame.
 | |
|  ; So, is looks like this:
 | |
|  ;			|-----------------------------|
 | |
|  ;                      |                             |
 | |
|  ;                      |                             |
 | |
|  ;                      |                             |
 | |
|  ;					.
 | |
|  ;					.
 | |
|  ;					.
 | |
|  ;                      |                             |
 | |
|  ;                      |                             |
 | |
|  ;                      |                             |	<--- coroutine ident
 | |
|  ;			|-----------------------------|
 | |
|  ;			|    saved SP                 |
 | |
|  ;			|-----------------------------|
 | |
|  ;			|    saved LB		      |
 | |
|  ;			|-----------------------------|
 | |
|  ;			|    procedure address or 0   |
 | |
|  ;			|-----------------------------|
 | |
|  ;			|	size		      |
 | |
|  ;			|-----------------------------|
 | |
|  ;
 | |
|  ; Another problem is that the coroutines must always run at the same
 | |
|  ; place in the stack. Therefore, in the runtime startoff a piece of the
 | |
|  ; stack is allocated for coroutines.
 | |
| 
 | |
|  exp $_SYSTEM__NEWPROCESS
 | |
|  exp $_SYSTEM__TRANSFER
 | |
|  inp $ChkSize
 | |
| 
 | |
|  pro $_SYSTEM__NEWPROCESS, 0
 | |
| 
 | |
|  ; This procedure only initializes the area used for saving the stack.
 | |
|  ; Its definition is:
 | |
|  ;	PROCEDURE NEWPROCESS(P:PROC; A:ADDRESS; n:CARDINAL; VAR p1:ADDRESS);
 | |
| 
 | |
|  lol 2*EM_PSIZE		; size of frame (n)
 | |
|  cal $ChkSize
 | |
|  asp EM_WSIZE
 | |
|  lfr EM_WSIZE
 | |
|  sil EM_PSIZE		; store size in area (indicated by A)
 | |
|  lal EM_PSIZE
 | |
|  loi EM_PSIZE		; address of area (A)
 | |
|  lal 0
 | |
|  loi EM_PSIZE		; address of coroutine body (P)
 | |
|  lal EM_PSIZE
 | |
|  loi EM_PSIZE
 | |
|  adp EM_WSIZE
 | |
|  sti EM_PSIZE		; store it in area
 | |
|  lal EM_PSIZE
 | |
|  loi EM_PSIZE
 | |
|  adp 3*EM_PSIZE + EM_WSIZE	; this becomes the coroutine identifier
 | |
|  lal 2*EM_PSIZE+EM_WSIZE
 | |
|  loi EM_PSIZE
 | |
|  sti EM_PSIZE
 | |
|  ret 0
 | |
|  end 0
 | |
| 
 | |
| target
 | |
|  bss EM_PSIZE, 0, 0
 | |
| 
 | |
|  pro $_SYSTEM__TRANSFER, 0
 | |
| 
 | |
|  ; This procedure does all the hard work.
 | |
|  ; It must save the current environment, and restore the one to which the
 | |
|  ; transfer is done. It must also make it look like the return is done
 | |
|  ; from ITS invocation of transfer.
 | |
|  ; Definition is:
 | |
|  ;	PROCEDURE TRANSFER(VAR p1, p2 : ADDRESS);
 | |
| 
 | |
|  mes ms_gto	; This is a dangerous procedure
 | |
| 
 | |
|  lal EM_PSIZE
 | |
|  loi EM_PSIZE
 | |
|  loi EM_PSIZE	; address of target coroutine
 | |
|  dup EM_PSIZE
 | |
|  lae CurrentProcess
 | |
|  loi EM_PSIZE
 | |
|  dup EM_PSIZE
 | |
|  lal 0
 | |
|  loi EM_PSIZE	; address of place where to store address of current coroutine
 | |
|  sti EM_PSIZE	; store
 | |
|  cmp		; compare with current process
 | |
|  zne *1
 | |
|  ; Here, no real transfer needs to be done
 | |
|  asp EM_PSIZE
 | |
|  ret 0		; just return
 | |
| 1
 | |
|  lae target
 | |
|  sti EM_PSIZE	; store it in target
 | |
| 
 | |
| 		; Now, we save the current stack
 | |
| 		; Use local base from main program
 | |
| 
 | |
|  lor 0		; load LB
 | |
|  lae CurrentProcess
 | |
|  loi EM_PSIZE
 | |
|  adp -2*EM_PSIZE
 | |
|  sti EM_PSIZE	; save it
 | |
|  lor 1		; load SP
 | |
|  lae CurrentProcess
 | |
|  loi EM_PSIZE
 | |
|  adp -EM_PSIZE
 | |
|  sti EM_PSIZE	; save it
 | |
| 		; Now, we must find a stack we can temporarily use.
 | |
| 		; Just take the one from the main program.
 | |
|  lae MainProcess
 | |
|  loi EM_PSIZE
 | |
|  adp -EM_PSIZE
 | |
|  loi EM_PSIZE
 | |
|  str 1		; temporary stackpointer
 | |
| 
 | |
|  lae MainLB
 | |
|  loi EM_PSIZE
 | |
|  str 0
 | |
| 
 | |
|  lae CurrentProcess
 | |
|  loi EM_PSIZE
 | |
|  lae MainProcess
 | |
|  loi EM_PSIZE
 | |
|  cmp
 | |
|  zeq *2
 | |
| 
 | |
|  lae StackBase
 | |
|  loi EM_PSIZE
 | |
|  lae CurrentProcess
 | |
|  loi EM_PSIZE
 | |
|  adp -3*EM_PSIZE-EM_WSIZE
 | |
|  loi EM_WSIZE	; get size
 | |
|  ngi EM_WSIZE
 | |
|  ads EM_WSIZE	; gives source address
 | |
|  lae CurrentProcess
 | |
|  loi EM_PSIZE	; destination address
 | |
|  lae CurrentProcess
 | |
|  loi EM_PSIZE
 | |
|  adp -3*EM_PSIZE-EM_WSIZE
 | |
|  loi EM_WSIZE
 | |
|  bls EM_WSIZE	; copy
 | |
| 
 | |
| 2
 | |
|  lae target
 | |
|  loi EM_PSIZE
 | |
|  dup EM_PSIZE
 | |
|  lae CurrentProcess
 | |
|  sti EM_PSIZE	; store target process descriptor in _CurrentProcess
 | |
|  lae MainProcess
 | |
|  loi EM_PSIZE
 | |
|  cmp
 | |
|  zeq *4
 | |
| 		; Now check if the coroutine was called before
 | |
|  lae target
 | |
|  loi EM_PSIZE
 | |
|  adp -3*EM_PSIZE
 | |
|  loi EM_PSIZE
 | |
|  zer EM_PSIZE
 | |
|  cmp
 | |
|  zeq *5
 | |
| 		; No, it was'nt
 | |
|  lae StackBase
 | |
|  loi EM_PSIZE
 | |
|  str 1		; new stack pointer
 | |
|  lae target
 | |
|  loi EM_PSIZE
 | |
|  adp -3*EM_PSIZE
 | |
|  loi EM_PSIZE
 | |
|  zer EM_PSIZE
 | |
|  lae target
 | |
|  loi EM_PSIZE
 | |
|  adp -3*EM_PSIZE
 | |
|  sti EM_PSIZE
 | |
|  cai
 | |
|  loc 0
 | |
|  cal $exit
 | |
|  ret 0
 | |
| 5
 | |
|  lae target
 | |
|  loi EM_PSIZE	; push source address
 | |
|  lae StackBase
 | |
|  loi EM_PSIZE	; subtract size from this and we have the destination address
 | |
|  lae target
 | |
|  loi EM_PSIZE
 | |
|  adp -3*EM_PSIZE-EM_WSIZE
 | |
|  loi EM_WSIZE
 | |
|  ngi EM_WSIZE
 | |
|  ads EM_WSIZE	; got it
 | |
|  lae target
 | |
|  loi EM_PSIZE
 | |
|  adp -3*EM_PSIZE-EM_WSIZE
 | |
|  loi EM_WSIZE
 | |
|  bls EM_WSIZE
 | |
| 4
 | |
|  lae target
 | |
|  loi EM_PSIZE
 | |
|  adp -2*EM_PSIZE
 | |
|  loi EM_PSIZE
 | |
|  str 0		; restore LB
 | |
|  lae target
 | |
|  loi EM_PSIZE
 | |
|  adp -EM_PSIZE
 | |
|  loi EM_PSIZE
 | |
|  str 1		; restore SP
 | |
|  ret 0
 | |
|  end 0
 | |
| 
 | |
|  pro $ChkSize, 0
 | |
|  lol 0
 | |
|  loc 3*EM_PSIZE+EM_WSIZE
 | |
|  sbi EM_WSIZE
 | |
|  dup EM_WSIZE
 | |
|  stl 0
 | |
|  loe StackSize
 | |
|  cmu EM_WSIZE
 | |
|  zle *1
 | |
|  loc M2_TOOLARGE	; trap number for "stack size too large"
 | |
|  trp
 | |
| 1
 | |
|  lol 0
 | |
|  loc EM_WSIZE-1
 | |
|  adi EM_WSIZE
 | |
|  loc EM_WSIZE
 | |
|  dvi EM_WSIZE
 | |
|  loc EM_WSIZE
 | |
|  mli EM_WSIZE
 | |
|  ret EM_WSIZE
 | |
|  end 0
 |