/*
            Copyright Oliver Kowalke 2009.
   Distributed under the Boost Software License, Version 1.0.
      (See accompanying file LICENSE_1_0.txt or copy at
            http://www.boost.org/LICENSE_1_0.txt)
*/

/*
 * Boost Software License - Version 1.0 - August 17th, 2003
 *
 * Permission is hereby granted, free of charge, to any person or organization
 * obtaining a copy of the software and accompanying documentation covered by
 * this license (the "Software") to use, reproduce, display, distribute,
 * execute, and transmit the Software, and to prepare derivative works of the
 * Software, and to permit third-parties to whom the Software is furnished to
 * do so, all subject to the following:
 *
 * The copyright notices in the Software and this entire statement, including
 * the above license grant, this restriction and the following disclaimer,
 * must be included in all copies of the Software, in whole or in part, and
 * all derivative works of the Software, unless such copies or derivative
 * works are solely in the form of machine-executable object code generated by
 * a source language processor.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
 * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
 * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */

/* //////////////////////////////////////////////////////////////////////////////////////
 * implementation
 */

/* make context (refer to boost.context)
 *
 *
 *             --------------------------------------------------------------------------------------
 * stackdata: |                                                       |         context             ||
 *             --------------------------------------------------------------------------------------
 *                                                                                              (16-align)
 *
 *             ---------------------------------------------------------------------
 * context:   |   x19   |   x20   |   x21   |   x22   |   x23   |   x24   |   x25   |
 *             ---------------------------------------------------------------------
 *            0         8         16        24        32        40        48                     
 *                           
 *                                                                         
 *                                                     __end     func      
 *             ---------------------------------------------------------------------
 *            |   x26   |   x27   |   x28   |   fp    |   lr    |   pc    | padding |
 *             ---------------------------------------------------------------------
 *            56        64        72        80        88        96        |                 
 *                                                                        |                  
 *                                                                    16-align
 *
 *
 * @param stackdata     the stack data (x0)
 * @param stacksize     the stack size (x1)
 * @param func          the entry function (x2)
 *
 * @return              the context pointer (x0)
 */
function tb_context_make, export=1

    // save the stack top to x0
    add x0, x0, x1

    // 16-align of the stack top address 
    and x0, x0, ~0xf

    /* reserve space for context-data on context-stack
     *
     * 112 = align16(13 * 8)
     */
    sub x0, x0, #112

    // context.pc = func
    str x2, [x0, #96]

    // get the address of label __end
#ifdef TB_CONFIG_OS_IOS
    /* numeric offset since llvm still does not support labels in adr
     *
     * 0x0c = 3 instructions * size (4) before label '__end'
     *
     * new version llvm have already fix this issues.
     */
    adr x1, 0x0c
#else
    adr x1, __end
#endif

    // context.lr = the address of label __end
    str x1, [x0, #88]

    // return pointer to context-data (x0)
#ifdef TB_CONFIG_OS_IOS
    ret lr
#else
    ret x30
#endif

__end:

    // exit(0)
    mov x0, #0
#ifdef __ELF__
    bl _exit
#else
    bl __exit
#endif

endfunc

/* jump context (refer to boost.context)
 *
 * @param context       the to-context (x0)
 * @param priv          the passed user private data (x1)
 *
 * @return              the from-context (context: x0, priv: x1)
 */
function tb_context_jump, export=1

    /* prepare stack space first
     *
     * 0x70 = align16(13 * 8)
     */
    sub sp, sp, #0x70

    // save x19 - x30
    stp x19, x20, [sp, #0x00]
    stp x21, x22, [sp, #0x10]
    stp x23, x24, [sp, #0x20]
    stp x25, x26, [sp, #0x30]
    stp x27, x28, [sp, #0x40]
#ifdef TB_CONFIG_OS_IOS
    stp fp,  lr,  [sp, #0x50]
#else
    stp x29, x30, [sp, #0x50]
#endif

    // save lr as pc
#ifdef TB_CONFIG_OS_IOS
    str lr, [sp, #0x60]
#else
    str x30, [sp, #0x60]
#endif

    // save the old context(sp) to x4
    mov x4, sp

    // switch to the new context(sp) and stack
    mov sp, x0

    // restore x19 - x30
    ldp x19, x20, [sp, #0x00]
    ldp x21, x22, [sp, #0x10]
    ldp x23, x24, [sp, #0x20]
    ldp x25, x26, [sp, #0x30]
    ldp x27, x28, [sp, #0x40]
#ifdef TB_CONFIG_OS_IOS
    ldp fp,  lr,  [sp, #0x50]
#else
    ldp x29, x30, [sp, #0x50]
#endif

    /* pass old-context(context: x0, priv: x1) arguments to the context function
     *
     * and return from-context: retval(context: x0, priv: x1) from jump 
     */
    mov x0, x4

    // load pc
    ldr x4, [sp, #0x60]

    // restore stack space
    add sp, sp, #0x70

    // jump to the return or entry address(pc)
    ret x4

endfunc

