/*
            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.
 */

/* modified by ruki
 *
 * - modify stack layout
 * - fix some bugs on mips32
 */

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

/* make context (refer to boost.context)
 *
 *
 *             --------------------------------------------------------------------------------------
 * stackdata: |                                                       |         context             ||
 *             --------------------------------------------------------------------------------------
 *                                                                                              (16-align)
 *
 *             ------------------------------------------------
 * context:   |  s0  |  s1  |  s2  |  s3  |  s4  |  s5  |  s6  |
 *             ------------------------------------------------
 *            0      4      8      12     16     20     24
 *
 *                                ------------------------------------
 *                               |                                   \|/
 *                               |       __end  func             retval(from)          function arguments
 *             -----------------------------------------------------------------------------------------------------
 *            |  s7  |  fp  |  retval  |  ra  |  pc  |  gp  | context |  priv  | padding |   (a0-a3)    |  padding  |
 *             -----------------------------------------------------------------------------------------------------
 *            28     32     36         40     44     48     52        56       60        64
 *                                                                                       |(16-align)
 *                                                                                       |
 *                                                                             sp when jump to function
 *
 *
 * @param stackdata     the stack data (a0)
 * @param stacksize     the stack size (a1)
 * @param func          the entry function (a2)
 *
 * @return              the context pointer (v0)
 */
function tb_context_make, export=1
#ifdef __PIC__
    .set    noreorder
    .cpload $t9
    .set    reorder
#endif

    // save the stack top to v0
    addu $v0, $a0, $a1

    // reserve space for arguments(a0-a3) of context-function
    addiu $v0, $v0, -32

    // 16-align of the stack top address
    move $v1, $v0
    li $v0, -16
    and $v0, $v1, $v0

    /* reserve space for context-data on context-stack
     *
     * 64 = align8(60)
     */
    addiu $v0, $v0, -64

    // context.pc = func
    sw $a2, 44($v0)

    // context.gp = global pointer
    sw $gp, 48($v0)

    /* init retval = a writeable space (context)
     *
     * it will write retval(context, priv) when jump to a new context function entry first
     */
    addiu $t0, $v0, 52
    sw $t0, 36($v0)

    // context.ra = address of label __end
    la $t9, __end
    sw $t9, 40($v0)

    // return pointer to context-data
    jr $ra

__end:

    // allocate stack frame space and save return address
    addiu $sp, $sp, -32
    sw $ra, 28($sp)

    // exit(0)
    move  $a0, $zero
    lw $t9, %call16(_exit)($gp)
    jalr $t9

endfunc

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

    # reserve stack space first
    addiu $sp, $sp, -64

    // save registers and construct the current context
    sw $s0, ($sp)
    sw $s1, 4($sp)
    sw $s2, 8($sp)
    sw $s3, 12($sp)
    sw $s4, 16($sp)
    sw $s5, 20($sp)
    sw $s6, 24($sp)
    sw $s7, 28($sp)
    sw $fp, 32($sp)
    sw $a0, 36($sp)     // save retval
    sw $ra, 40($sp)
    sw $ra, 44($sp)     // save ra as pc
    sw $gp, 48($sp)     // save gp

    // save the old context(sp) to a0
    move $a0, $sp

    // switch to the new context(sp) and stack
    move $sp, $a1

    // restore registers of the new context
    lw $s0, ($sp)
    lw $s1, 4($sp)
    lw $s2, 8($sp)
    lw $s3, 12($sp)
    lw $s4, 16($sp)
    lw $s5, 20($sp)
    lw $s6, 24($sp)
    lw $s7, 28($sp)
    lw $fp, 32($sp)
    lw $t0, 36($sp)     // load retval
    lw $ra, 40($sp)
    lw $t9, 44($sp)     // load t9 = pc
    lw $gp, 48($sp)     // load gp

    // restore stack space
    addiu $sp, $sp, 64

    // return from-context(context: a0, priv: a1) from jump
    sw $a0, ($t0)
    sw $a2, 4($t0)

    // pass old-context(context: a0, priv: a1) arguments to the context function
    move $a1, $a2

    /* jump to the return or entry address(pc)
     *
     *             ----------------------
     * context:   |   args   |  padding  |
     *             ----------------------
     *            0
     *            |
     *            sp
     */
    jr $t9

endfunc

