/*
 * Copyright (C) 2020-2021 Aloysius Indrayanto
 *
 * This file is part of the eCxx library, see LICENSE file for the license details.
 */

/*
 * ----------------------------------------------------------------------------------
 * The code in this file is copied (with a slight modifications) from:
 *     cont.S - continuations support for Xtensa call0 ABI
 * ----------------------------------------------------------------------------------
 * Copyright (C) 2014 Ivan Grokhotkov. All rights reserved.
 *
 * This file is part of the esp8266 core for Arduino environment.
 *
 * This library is free software; you can redistribute it and/or modify it under the
 * terms of the GNU Lesser General Public License as published by the Free Software
 * Foundation; either version 2.1 of the License, or (at your option) any later
 * version.
 *
 * This library 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License along
 * with this library; if not, write to the Free Software Foundation, Inc., 51
 * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
 * ----------------------------------------------------------------------------------
 */


/* Function aliases for compatibility with the Arduino library part */
    .global cont_yield
    .set    cont_yield, contYield

    .global cont_run
    .set    cont_run, contRun

/////////////////////////////////////////////////////////////////////////////////////

    .section .irom0.text
    .align    4
    .literal_position
    .global   contYield
    .type     contYield, @function

/* a1: sp         */
/* a2: void* cont */
contYield:
    /* Adjust stack and save registers */
    addi    a1,  a1, -24
    s32i    a12, a1, 0
    s32i    a13, a1, 4
    s32i    a14, a1, 8
    s32i    a15, a1, 12
    s32i    a0,  a1, 16
    s32i    a2,  a1, 20

    /* &contContinue -> cont.pcYield */
    movi    a3, contContinue
    s32i    a3, a2, 8
    /* sp -> cont.spYield */
    s32i    a1, a2, 12
    /* a0 <- cont.pcRet */
    l32i    a0, a2, 0
    /* sp <- cont.spRet */
    l32i    a1, a2, 4
    jx      a0

contContinue:
    l32i    a12, a1, 0
    l32i    a13, a1, 4
    l32i    a14, a1, 8
    l32i    a15, a1, 12
    l32i    a0,  a1, 16
    l32i    a2,  a1, 20
    addi    a1,  a1, 24
    ret
    .size    contYield, . - contYield

/////////////////////////////////////////////////////////////////////////////////////

/*
 *  Signal xtensa-gdb to treat this function as the outermost one.
 *
 *  https://github.com/jcmvbkbc/binutils-gdb-xtensa/blob/407e1140841f7fc337e90a7ba4482fb5a10c1701/gdb/xtensa-tdep.c#L2677-L2679
 *
 *  """ Special case for terminating backtrace at a function that wants to be seen as the outermost one.
 *      Such a function will clear it's RA (A0) register to 0 in the prologue instead of saving its
 *      original value. """
 */
    .text
    .align   4
    .literal_position
    .global  contWrapper
    .type    contWrapper, @function

contWrapper:
    movi    a0, 0
    callx0  a3
    movi    a2, contNorm
    jx      a2
    .size   contWrapper, . - contWrapper

/////////////////////////////////////////////////////////////////////////////////////

    .section .irom0.text
    .align   4
    .literal_position
    .global  contRun
    .type    contRun, @function

/* a1: sp           */
/* a2: void* cont   */
/* a3: void (*func) */
contRun:
    /* Adjust stack and save registers */
    addi    a1,  a1, -20
    s32i    a12, a1, 0
    s32i    a13, a1, 4
    s32i    a14, a1, 8
    s32i    a15, a1, 12
    s32i    a0,  a1, 16
    /* &contRet -> cont.pcRet */
    movi    a4, contRet
    s32i    a4, a2, 0
    /* sp -> cont.spRet */
    s32i    a1, a2, 4
    /* if(cont.pcYield != 0) goto contResume */
    l32i    a4, a2, 8
    bnez    a4, contResume
    /* else */
    /* set new stack */
    l32i    a1, a2, 16;
    /* goto func */
    movi    a2, contWrapper
    jx      a2

contResume:
    /* a1 <- cont.spYield */
    l32i    a1, a2, 12
    /* Reset yield flag (0 -> cont.pcYield) */
    movi    a3, 0
    s32i    a3, a2, 8
    /* Jump to saved cont.pcYield */
    movi    a0, contRet
    jx      a4

contNorm:
    /* Calculate pointer to cont.structStart from sp */
    l32i    a2,    a1, 4
    /* sp <- cont.spRet */
    l32i    a1, a2, 4
    /* 0 -> cont.pcRet */
    movi    a4, 0
    s32i    a4, a2, 0

contRet:
    /* Restore registers */
    l32i    a12, a1, 0
    l32i    a13, a1, 4
    l32i    a14, a1, 8
    l32i    a15, a1, 12
    l32i    a0,  a1, 16
    /* Adjust stack and return */
    addi    a1,  a1, 20
    ret
    .size   contRun, . - contRun
