**
**	StackCall.a
**
**	Stack swap & stack size check routines for SAS/C
**	using the small data model
**
**	Copyright © 1990-1996 by Olaf `Olsen' Barthel
**		All Rights Reserved
**

	include	"exec/types.i"
	include	"exec/tasks.i"
	include	"exec/memory.i"
	include	"exec/execbase.i"

	include	"dos/dos.i"
	include	"dos/dosextens.i"

*----------------------------------------------------------------------

CALL	macro
	xref	LVO\1
	jsr	LVO\1(a6)
	endm

*----------------------------------------------------------------------

ARG_SUCCESS	equ	4+5*4
ARG_STACKSIZE	equ	ARG_SUCCESS+4
ARG_ARGCOUNT	equ	ARG_STACKSIZE+4
ARG_ROUTINE	equ	ARG_ARGCOUNT+4
ARG_ARGS	equ	ARG_ROUTINE+4

TRUE		equ	1
FALSE		equ	0

*----------------------------------------------------------------------

	section	text,code

*----------------------------------------------------------------------

	xdef	StackCall

	;	LONG __stdargs StackCall(	LONG *Success,
	;					LONG StackSize,
	;					LONG ArgCount,
	;					LONG (* __stdargs Function)(...),
	;					...);
	;
	;	Calls a function with parameters on a newly-allocated
	;	stack. Caution: assumes all arguments are 32 bits wide.

StackCall:

	movem.l	d2/d3/a2/a3/a6,-(sp)		; Save registers

	move.l	ARG_STACKSIZE(sp),d0		; Get desired stack size
	add.l	#15+StackSwapStruct_SIZEOF,d0	; Round to next...
	and.l	#-16,d0				; ...quad long word
	move.l	d0,d2				; Save it for later
	move.l	#MEMF_PUBLIC,d1			; Well?
	move.l	SysBase(a4),a6			; Get SysBase

	CALL	AllocMem			; Allocate the memory
	tst.l	d0				; Success?
	beq.b	1$				; Exit if not

	move.l	d0,a2				; Remember base adress

	add.l	#StackSwapStruct_SIZEOF,d0	; Skip the header
	move.l	d0,stk_Lower(a2)		; Store the stack lower bound

	move.l	a2,a3				; Get the base address
	add.l	d2,a3				; Add the allocation size
	move.l	a3,stk_Upper(a2)		; Store the stack upper bound

	move.l	ARG_ARGCOUNT(sp),d0		; Get the number of arguments
	beq.b	3$				; Skip if none

	moveq	#4,d1				; One long word per argument
	mulu	d0,d1				; Nasty
	ext.l	d1				; Forget the upper word
	addq.l	#4,d1				; One for predecrement
	sub.l	d1,a3				; Pull it down

	subq.l	#1,d0				; Subtract one for DBRA
	lea.l	ARG_ARGS(sp),a0			; Get the source argument list
	move.l	a3,a1				; Get the destination argument list

2$	move.l	(a0)+,(a1)+			; Copy the parameters
	dbra	d0,2$				; Loop until all is copied

3$	move.l	a3,stk_Pointer(a2)		; Store the new stack pointer

	move.l	ARG_ROUTINE(sp),a3		; Get address of the routine to call

	move.l	a2,a0				; Swap the stack
	CALL	StackSwap

	jsr	(a3)				; Call the routine
	move.l	d0,d3				; Save the result

	move.l	a2,a0				; Swap the stack
	CALL	StackSwap

	move.l	a2,a1				; Get the base address
	move.l	d2,d0				; Get the allocation length
	CALL	FreeMem				; Free the memory

	move.l	ARG_SUCCESS(sp),a0		; Flag as success
	move.l	#TRUE,(a0)

	move.l	d3,d0				; Return the result

	movem.l	(sp)+,d2/d3/a2/a3/a6		; Restore registers
	rts

1$	move.l	ARG_SUCCESS(sp),a0		; Flag as failure
	move.l	#FALSE,(a0)

	movem.l	(sp)+,d2/d3/a2/a3/a6		; Restore registers
	rts

*----------------------------------------------------------------------

	xdef	@StackSize
	xdef	StackSize

	;	LONG StackSize(struct Task *Task);
	;
	;	Determines the amount of stack still available to a task.
	;	Pass in NULL as the task address to return the amount
	;	of stack available to the calling task.

StackSize:
	move.l	4(sp),a0			; Get task address

@StackSize:
	move.l	a6,-(sp)			; Save registers

	move.l	SysBase(a4),a6			; Get SysBase

	move.l	a0,d0				; Valid task address?
	beq.b	4$

	move.l	TC_SPREG(a0),d0			; Remember stack pointer
	bra.b	3$

4$	move.l	a0,a1
	CALL	FindTask			; Who am I?
	move.l	d0,a0				; That's me
	move.l	sp,d0				; Remember stack pointer

3$	cmp.b	#NT_PROCESS,LN_TYPE(a0)		; Is this a process?
	bne.b	1$

	tst.l	pr_CLI(a0)			; Is it running as a CLI process?
	beq.b	1$

	move.l	pr_ReturnAddr(a0),d1		; Get upper bound
	addq.l	#4,d1
	move.l	pr_ReturnAddr(a0),a0
	sub.l	(a0),d1				; Subtract total stack size

	bra.b	2$

1$	move.l	TC_SPLOWER(a0),d1		; Get lower bound

2$	sub.l	d1,d0				; Subtract from stack pointer

	move.l	(sp)+,a6			; Restore registers

	rts

*----------------------------------------------------------------------

	section	__MERGED,data

	xref	SysBase

	end
