#****************************************************************************
#
# module      : RTE_SystemLinux.s
#
# -------------------------------------------------------------------------
#
# responsible : Ivan Schreter
#
# special area: RTE System Interface
# description : Coroutine switching for ia32 Linux
#
# -------------------------------------------------------------------------
#
#
#*****************************************************************************/
#
#**************************************************************************
#
#************************************************************************
#
#
#
#    ========== licence begin  GPL
#    Copyright (c) 2003-2005 SAP AG
#
#    This program is free software; you can redistribute it and/or
#    modify it under the terms of the GNU General Public License
#    as published by the Free Software Foundation; either version 2
#    of the License, or (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program; if not, write to the Free Software
#    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
#    ========== licence end

&if $MACH = I386

.text

#-------------------------------------------------------------------------------------
# Implementation of own context setup interface
#
#   struct RTE_OwnCoroutineContext
#   {
#       void *sp ;          Stack pointer
#       void (*addr)() ;	Return pointer
#   };
#
#   void RTE_OwnMakeContext(struct RTE_OwnCoroutineContext *pContext)
#
#   pContext must point to an already filled context
#     pContext->sp   set to first byte of stack array,
#     pContext->addr point to initial function
#
.align 4
.globl RTE_OwnMakeContext
RTE_OwnMakeContext:
    pushl %esi              # save registers
    pushl %edi              # TODO: which registers are to be saved???
    pushl %ecx              # TODO: do we need to save flags? (direction)
    pushl %eax

    movl 20(%esp), %esi     # load pointer to new context
    movl (%esi), %edi       # load stack pointer to prepare
    sub $4, %edi            # point to last word on coroutine stack
    std                     # set direction flag to decrement

    # Here we push return address of the main coroutine function. Since
    # there is nowhere to return, we simply push null.

    xorl %eax, %eax         # zero eax
    stosl                   # set return address of main function to null

    # Here we build a pseudo stack frame for RTE_OwnSwapContext. First, we
    # push to the stack the return address, which is actually entry point
    # into the main function for the new context. Then, we push 8 dummy words
    # which will be later restored with popa in RTE_OwnSwapContext.

    movl 4(%esi), %eax      # load main function pointer (pushed as return address)
    stosl                   # store return address (and decrement edi)

    mov $8, %ecx            # we need to store 8 dummy words
    xor %eax, %eax          # zero eax
    rep stosl               # store 8 dummy words

    # The stack frame for RTE_OwnSwapContext is now built. Update the stack
    # pointer in context so that it is properly restored in RTE_OwnSwapContext.

    add $4, %edi            # point to the first dummy word
    movl %edi, (%esi)       # store new stack pointer

    popl %eax               # restore registers
    popl %ecx
    popl %edi
    popl %esi
    ret                     # done

#-------------------------------------------------------------------------------------
# Implementation of own context switch interface
#
#   struct RTE_OwnCoroutineContext
#   {
#       void *sp ;          Stack pointer
#       void (*addr)() ;	Return pointer
#   };
#
#   void RTE_OwnSwapContext( struct RTE_OwnCoroutineContext *pCaller,
#                            struct RTE_OwnCoroutineContext *pNew )
#
# This call replaces swapcontext() but does not check for stack overflow
#
.align 4
.globl RTE_OwnSwapContext
RTE_OwnSwapContext:
    pusha                   # push all general registers
    movl 36(%esp), %eax     # load pointer to pCaller
    movl %esp, (%eax)       # store current stack pointer

    # Now we have stored the registers on the stack and the current stack
    # pointer into the old context. We are now prepared to load new stack
    # pointer from the new context. This points to either a stack frame
    # generated by a call to RTE_OwnSwapContext or to a pseudo stack frame
    # generated by a call to RTE_OwnMakeContext. To switch to the other
    # coroutine we just need to restore stack pointer and pop the registers
    # from the stack.

    movl 40(%esp), %eax     # load pointer to pNew
    movl (%eax), %esp       # restore current stack pointer
    popa                    # restore all general registers
    ret                     # return to new stack frame (in pNew)

&endif
