/*
 * Copyright (C) 2018-2020 Western Digital Corporation or its affiliates
 * Copyright (C) 2017-2018 Wearable, Inc.
 * Copyright (C) 2000-2012 leJOS Contributors
 * Copyright (C) 2000 Jose H. Solorzano and TinyVM Contributors
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/.
 * 
 * This Source Code Form is “Incompatible With Secondary Licenses”,
 * as defined by the Mozilla Public License, v. 2.0.
 */

#ifndef _STACK_H
#define _STACK_H

#include "configure.h"
#include "threads.h"
#include "interpreter.h"
#include "memory.h"
#include "language.h"

#define get_local_word(IDX_)       (localsBase[(IDX_)])
#define get_local_ref(IDX_)        (stackword2ref(localsBase[(IDX_)]))
#define inc_local_word(IDX_,NUM_)  (localsBase[(IDX_)] += (NUM_))
#define just_set_top_word(WRD_)    (stackTop[0] = (WRD_))
#define get_top_word()             (stackTop[0])
#define get_top_ref()              (stackword2ref(stackTop[0]))
#define get_word_at(DOWN_)         (*(stackTop-(DOWN_)))
#define get_ref_at(DOWN_)          (stackword2ref(*(stackTop-(DOWN_))))
#define get_stack_ptr()            (stackTop)
#define get_stack_ptr_cur(state)        (state->program_stack_top)
#define get_stack_ptr_at(DOWN_)    (stackTop-(DOWN_))
#define get_stack_ptr_at_cur(state, DOWN_)(state->program_stack_top-(DOWN_))


void update_stack_frame (nvm_state_t* pState, StackFrame *stackFrame);
void update_registers (nvm_state_t* pState, StackFrame *stackFrame);

/**
 * Clears the operand stack for the given stack frame.
 */
static inline STACKWORD * init_sp (nvm_state_t* pState, StackFrame *stackFrame, MethodRecord *methodRecord)
{
  return ((STACKWORD*) ref2ptr(stackFrame->localsBase)) + methodRecord->numLocals - 1;
}

/**
 * Clears/initializes the operand stack at the bottom-most stack frame,
 * and pushes a void (unitialized) element, which should be overriden
 * immediately with set_top_word or set_top_ref.
 */
static inline void init_sp_pv (nvm_state_t* pState)
{
  pState->program_stack_top = stack_array(pState);
}

/**
 * With stack cleared, checks for stack overflow in given method.
 */
static inline boolean is_stack_overflow (nvm_state_t* pState, STACKWORD *stackTop, MethodRecord *methodRecord)
{
    return (stackTop + methodRecord->maxOperands) >= (stack_array(pState) + nvm_array_size((Object*)ref2ptr(pState->current_thread->stackArray)));
}

static inline void update_constant_registers (nvm_state_t* pState, StackFrame *stackFrame)
{
  pState->program_locals_base = (STACKWORD*) ref2ptr(stackFrame->localsBase);
}

#define push_word(word)     (*(++stackTop) = word)
#define push_word_cur(state, word) (*(++(state->program_stack_top)) = word)
#define push_ref(ref)      (*(++stackTop) = ref2stackword(ref))
#define push_ref_cur(state, ref)  (*(++(state->program_stack_top)) = ref2stackword(ref))

#define pop_word()          (*stackTop--)
#define pop_ref()           (stackword2ref(*stackTop--))

#define pop_jint()          ((JINT)word2jint(*stackTop--))
//#define pop_word_or_ref()   (*stackTop--)

#define pop_jlong(lword)     (*lword = ((uint64_t) *(stackTop--)), *lword |= ((uint64_t) *(stackTop--)) << UINT64_C(32))
#define push_jlong(lword)    (*(++stackTop) = (STACKWORD) ((*lword) >> UINT64_C(32)), *(++stackTop) = (STACKWORD) ((*lword) & UINT32_C(-1)))

#define pop_jdouble(jd)     (stackTop--, memcpy(jd, stackTop, sizeof(JDOUBLE)), stackTop--)
#define push_jdouble(jd)    (stackTop++, memcpy(stackTop, jd, sizeof(JDOUBLE)), stackTop++)

#define pop_words(aNum)     (stackTop -= aNum)
#define pop_words_cur(state, aNum) (state->program_stack_top -= aNum)

#define just_pop_word()     (--stackTop)
#define just_pop_ref()      (--stackTop)

#define push_void()         (++stackTop)

#define set_top_ref(aRef)   (*stackTop = ref2stackword(aRef))
#define set_top_ref_cur(state, aRef)(*(state->program_stack_top) = ref2stackword(aRef))
#define set_top_word(aWord) (*stackTop = aWord)
#define set_top_word_cur(state, aWord)(*(state->program_stack_top) = (aWord))

#define dup() \
{ \
  stackTop++; \
  *stackTop = *(stackTop-1); \
}

#define dup2() \
{ \
  *(stackTop+1) = *(stackTop-1); \
  *(stackTop+2) = *stackTop; \
  stackTop += 2; \
}

#define dup_x1() \
{ \
  stackTop++; \
  *stackTop = *(stackTop-1); \
  *(stackTop-1) = *(stackTop-2); \
  *(stackTop-2) = *stackTop; \
}

#define dup2_x1() \
{ \
  stackTop += 2; \
  *stackTop = *(stackTop-2); \
  *(stackTop-1) = *(stackTop-3); \
  *(stackTop-2) = *(stackTop-4); \
  *(stackTop-3) = *stackTop; \
  *(stackTop-4) = *(stackTop-1); \
}

#define dup_x2() \
{ \
  stackTop++; \
  *stackTop = *(stackTop-1); \
  *(stackTop-1) = *(stackTop-2); \
  *(stackTop-2) = *(stackTop-3); \
  *(stackTop-3) = *stackTop; \
}

#define dup2_x2() \
{ \
  stackTop += 2; \
  *stackTop = *(stackTop-2); \
  *(stackTop-1) = *(stackTop-3); \
  *(stackTop-2) = *(stackTop-4); \
  *(stackTop-3) = *(stackTop-5); \
  *(stackTop-4) = *stackTop; \
  *(stackTop-5) = *(stackTop-1); \
}

#define swap() \
{ \
  STACKWORD tempStackWord = *stackTop; \
  *stackTop = *(stackTop-1); \
  *(stackTop-1) = tempStackWord; \
}

#define set_local_word(aIndex,aWord)    (localsBase[aIndex] = aWord)
#define set_local_ref(aIndex, aWord)    (localsBase[aIndex] = aWord)

static inline void write_stackword(byte* ptr, STACKWORD val)
{
    memcpy(ptr, &val, sizeof(val));
}

static inline STACKWORD read_stackword(byte* ptr)
{
    STACKWORD res;
    memcpy(&res, ptr, sizeof(res));
    return res;
}

#endif

