#include <stabs.h>
#include <bases.h>

asm("
	.text
	.even
	.globl	___stkext_f
	.globl	___stkext
	.globl	___exit_stk
	.globl	___stk_free

___stkext_f:
	movel	a0,sp@-
	movel	sp,a0			| memorize sp
	moveml	#0xc062,sp@-		| preserves all registers
	movel	sp,a2			| memorize sp
	addl	#28:W,d1		| Used by this function itself
	movel	"A4(___stackborders)",a1| get upper border of stack in a1
	movel	a1@(4:W),a1
	tstl	"A4(___used_stack)"
	bne	l0
	movel	"A4(___SaveSP)",a1
l0:	jbsr	___stkext		| change to a stackframe with d0 bytes
	movel	"A4(___used_stack)",a6	| fix stored sp
	addl	d1,a6@(12:W)
	movel	a2,d0			| calculate number of bytes to copy
	addl	"A4(___stk_argbytes)",d0| to new stackframe in d0
	addl	d1,d0
	cmpl	a1,d0			| never copy over the bottom of the stack
	jls	l1
	movel	a1,d0
l1:	subl	a0,d0			| copy
	subl	d0,sp
	movel	sp,a1
	movel	"A4(_SysBase)",a6
	jsr	a6@(-0x270)		| CopyMem(src:a0,dst:a1,siz:d0)
	movel	a2,a0
	moveml	a0@+,#0x4603
	movel	sp,a0			| Prepare cleanup
	addl	d1,a0
	movel	#___stkrst_f,a0@(8:W)
	movel	sp@+,a0
	rts

___stkext:
	movel	a0,sp@-
	moveml	#0xf072,sp@-		| preserves all registers
	movel	"A4(___stk_safezone)",d3
	lea	"A4(___stk_limit)",a2
	lea	"A4(___used_stack)",a3
	addqw	#4,a3
	movel	"A4(_SysBase)",a6
	movel	d0,d2
	addl	d3,d2
	addl	"A4(___stk_argbytes)",d2| required stack
l3:	movel	a3@,a0			| Look for a spare stackframe
	movel	a0,d0
	jne	l6
	addl	#20:W,d2		| no more stackframes - allocate a new one
	movel	"A4(___stk_minframe)",d1
	cmpl	d1,d2
	jge	l4
	movel	d1,d2
l4:	movel	d2,d0
	moveql	#1,d1			| MEMF_PUBLIC
	jsr	a6@(-0xc6)		| AllocMem(siz:d0,typ:d1)
	tstl	d0
	jeq	___stkovf		| allocation failed
l5:	movel	d0,a0			| prepare new frame for use
	addl	d0,d2
	movel	d2,a0@(8:W)
	addl	#20:w,d0
	movel	d0,a0@(4:W)
	jra	l7
l6:	movel	a0@,a3@			| Remove spare stackframe from list
	movel	a0@(8:W),d0		| Test spare stackframe
	subl	a0@(4:W),d0
	cmpl	d0,d2
	jls	l7
	addl	#20:W,d0		| Not big enough
	movel	a0,a1
	jsr	a6@(-0xd2)		| FreeMem(mem:a1,siz:d0)
	jra	l3
l7:	movel	a3@-,a0@		| Add it to the used list
	movel	a0,a3@
	addqw	#4,a0
	movel	a2@,a0@(12:W)
	movel	a0@,a2@			| Prepare ___top_of_stackframe variable
	addl	d3,a2@
	lea	a0@(4:W),a2
	movel	a2@+,a2@		| Prepare target sp
	jsr	a6@(-0x2dc)		| StackSwap(sss:a0)
	movel	a2@,a0
	addl	#40:W,a2@		| Fix stored sp
	moveml	a0@+,#0x4e0f
	movel	a0@(4:W),sp@-		| prepare returnaddress
	movel	a0@,a0
	rts

___exit_stk:
	tstl	"A4(___used_stack)"
	jeq	___stk_free
	lea	sp@(12:W),a0
	movel	"A4(___SaveSP)",d0	| Go back to first stackframe
	jbsr	___stkrst
	movel	a0@,sp@-		| restore argument of exit()
	movel	a0@-,sp@-		| returnaddress for exit()
	movel	a0@-,sp@-		| returnaddress for callfuncs()
	movel	a0@-,sp@-		| our returnaddress
|	jbsr	___stk_free
|	rts

___stk_free:
	movel	a2,sp@-
	movel	a6,sp@-			| Free all unused stackframes
	movel	"A4(_SysBase)",a6
	lea	"A4(___used_stack)",a2
	addqw	#4,a2
l8:	movel	a2@,a1			| Remove spare stackframe from list
	movel	a1,d0
	jeq	l9
	movel	a1@,a2@
	movel	a1@(8:W),d0		| Calculate size
	subl	a1@(4:W),d0
	addl	#20:W,d0
	jsr	a6@(-0xd2)		| FreeMem(mem:a1,siz:d0)
	jra	l8
l9:	movel	sp@+,a6
	movel	sp@+,a2
	rts
");

ADD2EXIT(__exit_stk,-50);
