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


#include "CoreSys.h"


#define CONT_STACKGUARD 0xFEEFEFFE


extern "C" {


// Placed in .noinit section because it is initialized before the .bss is zero-filled and to preserve its value
ContData* g_ContData __ECXX_ATTR_SEC_NOINIT;

// Alias of the above variable for compatibility with the Arduino library part
extern ContData* g_pcont __ECXX_ATTR_ALIAS("g_ContData");


extern void contInit(ContData* cont)
{
    memset( cont, 0, sizeof(ContData) );

    cont->stackGuard1  = CONT_STACKGUARD;
    cont->stackGuard2  = CONT_STACKGUARD;
    cont->stackEnd     = cont->stack + (sizeof(cont->stack) / 4);
    cont->structStart  = reinterpret_cast<unsigned*>(cont);

    // Fill stack with magic values to check high water mark
    for(eCxx::uint32_t pos = 0; pos < sizeof(cont->stack) / 4; ++pos) {
        cont->stack[pos] = CONT_STACKGUARD;
    }
}


extern void contRepaintStack(ContData *cont)
{
    register eCxx::uint32_t* sp asm("a1");

    // Fill 64 bytes adjacent to the current SP with magic values
    for(eCxx::uint32_t* pos = sp - 16; pos >= &cont->stack[0]; --pos) {
       *pos = CONT_STACKGUARD;
    }
}


extern eCxx::uint32_t contGetFreeStack(ContData* cont)
{
    eCxx::uint32_t* head      = cont->stack;
    eCxx::uint32_t  freeWords = 0;

    while(*head == CONT_STACKGUARD) {
        ++head;
        ++freeWords;
    }

    return freeWords * 4;
}


extern bool ICACHE_RAM_ATTR contCanYield(ContData* cont)
{ return !ETS_INTR_WITHINISR() && cont->pcRet != 0 && cont->pcYield == 0; }


extern bool ICACHE_RAM_ATTR contCheck(ContData* cont)
{ return (cont->stackGuard1 != CONT_STACKGUARD) || (cont->stackGuard2 != CONT_STACKGUARD); }


} // extern "C"

