**
**      StackCall.asm
**
**      Stack swap & stack size check routines
**
**      Copyright © 1990-1994 by Olaf `Olsen' Barthel
**              All Rights Reserved
**
**      :ts=8
**

        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

SysBase		equ	4

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

        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,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,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

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

        end
