**
**	StackCall.a
**
**	Stack swap routines
**
**	Copyright © 1990-1994 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

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

	csect	text,0,0,1,2

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

	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 address

	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

	move.l	d0,d1
	add.l	d1,d1				; One long word per argument
	add.l	d1,d1
	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

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

	dc.l	_BSSBAS
	dc.l	_BSSLEN

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

	csect	__MERGED,data

	xref	SysBase

	xref	_BSSBAS
	xref	_BSSLEN

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

	end
