#include <bases.h>

asm("
| the stack consists of a single linked linear list like this:
|
| struct stack
| { struct stack *next;			points to the next underlying structure
|   struct StackSwapStruct sss;		holds previous stackframe when used,
|					itself when unused, the current stackborders
|					are in the task structure ;-)
|   APTR top_of_stackframe; }		previous variable value
|					Note: stack->sss.stk_Lower is not reliable
|
| For better performance stackframes are cached in a 'unused' list when
| not in use. (AllocMem() is really a performance killer).
|

|
| Caution:
| Race condition ahead! exec might preempt our task at any time storing
| the current register set on top of the free stack. NEVER set a sp higher
| than the location of important data.
|
	.comm	___used_stack,8		| pointer to used stackframes,
					| pointer to unused stackframes
	.text
	.even
	.globl	___stkrst

___stkrst:
	movel	a2,sp@-
	moveml	#0x40f2,sp@-
	movel	sp,a2
	movel	d0,a3
l0:	lea	"A4(___used_stack)",a1
	tstl	a1@
	beq	l1			| No previous stackframe
	movel	"A4(___stackborders)",a0
	cmpl	a0@,a3
	jcs	l2
	cmpl	a0@(4:W),a3
	jhi	l2			| Stackpointer points to current frame
l1:	movel	a2@(28:W),a3@-		| Store returnaddress
	movel	a3,d0
	moveml	a2@+,#0x4f02		| Restore registers
	movel	a2@,a2
	movel	d0,sp			| Set stackpointer AFTER restoring registers!
	addql	#4,d0			| This function may return on the same
	rts				| stackframe it was called on.
l2:	movel	a1@,a0			| Go to previous stack
	movel	a0@,a1@+
	movel	a1@,a0@
	movel	a0,a1@
	addqw	#4,a0
	movel	a0@(12:W),"A4(___stk_limit)"
	movel	"A4(_SysBase)",a6
	jsr	a6@(-0x2dc)		| StackSwap(sss:a0)
	subqw	#4,sp			| Avoid overwriting returnaddress
	jra	l0
");
