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

#include "trace.h"
#include "types.h"
#include "constants.h"
#include "classes.h"
#include "interpreter.h"
#include "platform_hooks.h"
#include "threads.h"
#include "opcodes.h"
#include "configure.h"
#include "memory.h"
#include "language.h"
#include "exceptions.h"
#include "specialclasses.h"
#include "stack.h"
#include "poll.h"
#include <inttypes.h>
#include <string.h>

#define get_pgfield_type(HIBYTE_)            ((HIBYTE_) >> 4)
#define get_pgfield_offset(HIBYTE_,LOBYTE_)  (((uint16_t) ((HIBYTE_) & 0x0F) << 8) | (LOBYTE_))

/**
 * Assumes pc points to 2-byte offset, and jumps.
 */
#define do_goto(pc, aCond) \
    ((aCond) \
     ? ((pc) + (JSHORT) (((uint16_t) *(pc) << 8) | *((pc)+1)) - 1) \
     : ((pc) + 2))

#if FP_ARITHMETIC


STACKWORD do_fcmp (JFLOAT f1, JFLOAT f2, STACKWORD def)
{
    STACKWORD res;
    
    if (f1 > f2)
        res = 1;
    else if (fcmp(f1, f2))
        res = 0;
    else if (f1 < f2)
        res = -1;
    else
        res = def;
    
    return res;
}

#endif

#if FP_DOUBLE_ARITHMETIC

STACKWORD do_dcmp (double f1, double f2, STACKWORD def)
{
    STACKWORD res;
    
    if (f1 > f2)
        res = 1;
    else if (dcmp(f1, f2))
        res = 0;
    else if (f1 < f2)
        res = -1;
    else
        res = def;
    
    return res;
}

#endif

STACKWORD do_lcmp (LLONG l1, LLONG l2, STACKWORD def)
{
    STACKWORD res;
    
    if (l1 > l2)
        res = 1;
    else if (l1 == l2)
        res = 0;
    else if (l1 < l2)
        res = -1;
    else
        res = def;
    
    return res;
}

/**
 * Pops the array index off the stack, checks
 * bounds and null reference. The array reference
 * is the top word on the stack after this operation.
 * Sets arrayStart to start of the array data area.
 * @return array index if successful, -1 if an exception has been scheduled.
 */
static void* array_helper(nvm_state_t* pState, unsigned int idx, Object *obj, int sz)
{
    if (obj == NULL) {
        pState->thrown_exception = JAVA_LANG_NULLPOINTEREXCEPTION;
        return NULL;
    }
    
    if (idx >= nvm_array_size(obj)) {
        pState->thrown_exception = JAVA_LANG_ARRAYINDEXOUTOFBOUNDSEXCEPTION;
        return NULL;
    }

    return (void*)(array_start(obj) + idx * sz);
}

#define SAVE_REGS(state) (state->program_counter = pc, state->program_stack_top = stackTop, state->program_locals_base = localsBase)
#define LOAD_REGS(state) (localsBase = state->program_locals_base, stackTop = state->program_stack_top, pc = state->program_counter)
#define SAVE_REGS2(state) (state->program_counter = (pc), state->program_stack_top = stackTop, state->program_locals_base = localsBase, state->program_offset = 0)
#define LOAD_REGS2(state) (localsBase = state->program_locals_base, stackTop = state->program_stack_top, pc = (state->program_counter), state->program_offset = -1)

/**
 * Everything runs inside here, essentially.
 *
 * To be able use only a single fast test on each instruction
 * several assumptions are made:
 * - pState->current_thread is initialized and non-null and
 *   it is not set to null by any bytecode instruction.
 * - Thus it is not allowed to call schedule_thread() in instructions,
 *   use schedule_request( REQUEST_SWITCH_THREAD) instead.
 * - Whenever scheduler_make_request is false, scheduler_request_code is REQUEST_TICK.
 * - Thus anybody who sets scheduler_request_code must also set scheduler_make_request to true
 *   (using schedule_request assures this).
 * - Only the request handler may set scheduler_make_request to false.
 * - The millisecond timer interrupt must set scheduler_make_request to true
 *   for time slices to work.
 * When executing instructions the value of pc does not point to the current
 * instruction, it begins by pointing at pc+1 byte. However it may have a value
 * of pc+1 to pc+n where n is the max size of an instruction. When not actually
 * executing instructions pc will point to the next instruction. All of this
 * presents a problem for operations like invoking methods and throwing
 * exceptions, because they need a consistant state. So we define a set of rules
 * to make things easier. We use the macro SAVE_REG/LOAD_REG to define a safe
 * point and we must ensure that at these points the value of program_counter is in a
 * predictable state. In particular we require that...
 * 1. A VM function can use the getPC macro to obtain a pointer to the
 *    currently executing instruction. If the instruction is complete (during
 *    a thread switch for example then getPC will return the instruction about
 *    to be executed.
 * 2. If a VM function wishes to perform a jump operation, then it can do so
 *    by assigning directly to program_counter. Note that in some cases (allocations and
 *    exceptions) it may also need to take additional actions (returning NULL)
 *    to ensure that the current instruction is aborted.
 * In particular the above rules ensure that the following sequence...
 *   pState->program_counter = getPC(pState);
 * will result in the current instruction being re-started.
 * The macros above enforce this condition. They use the additional state variable
 * program_offset to allow correction of the value of program_counter.
 *
 * We have a similar issue with the stack. In some cases (primarily method calls
 * and memory allocation), we may need to be able to restart the instruction.
 * to allow this the stack should be left unchanged until after the call to
 * LOAD_REGS.
 *
 * The following macros allow two types of dispatch to be defined. One using
 * a conventional switch statement, the other using a dispatch table. Note
 * that the dispatch table uses a relative offset to allow the table to be
 * stored in flash memory.
 */
#if FAST_DISPATCH
// Fast byte code dispatch. Uses the GCC labels as values extension.
#define OPCODE(op) L_##op:
#define UNUSED_OPCODE(op)
#define MULTI_OPCODE(op)
#define DISPATCH1 goto *(pState->dispatch_table[*pc++])
#define DISPATCH goto *(dispatch[*pc++])
#define DISPATCH_CHECKED {instruction_hook(pState); DISPATCH1;}
#define START_DISPATCH DISPATCH1;
#define END_DISPATCH
#define DISPATCH_EVENTS CHECK_EVENT: pc--;
#define INIT_DISPATCH (pc++, pState->dispatch_check_event = forceCheck, pState->dispatch_normal = pState->dispatch_table = dispatch);
#else
// Standard dispatch code uses a switch statement
#define OPCODE(op) case op:
#define UNUSED_OPCODE(op) case op:
#define MULTI_OPCODE(op) case op:
#define DISPATCH_CHECKED goto CHECK_EVENT
#define DISPATCH goto DISPATCH_NEXT
#define START_DISPATCH DISPATCH_NEXT: switch(*pc++) {
#define END_DISPATCH }
#define DISPATCH_EVENTS CHECK_EVENT: instruction_hook(pState);
#define INIT_DISPATCH
#endif

static boolean engine_check_events(nvm_state_t* pState)
{
    while (SCHEDULER_REQUESTED(pState))
    {
        byte requestCode;
        TIMESTAMP now;
        boolean force_gc = false;
        
        if(pState->scheduler_request_code == REQUEST_EXIT) {
            exit_hook(pState);
            return false;
        }
        requestCode = pState->scheduler_request_code;
        pState->scheduler_request_code = REQUEST_TICK;
#if FAST_DISPATCH
        pState->dispatch_table = pState->dispatch_normal;
#else
        pState->scheduler_make_request = false;
#endif
        check_events(pState);
        now = atomic_time_get(pState->sys_time);
        
        if (requestCode == REQUEST_SWITCH_THREAD
#if TIME_SCHEDULING
            || now >= pState->switch_time
#endif
            || now >= pState->wake_time
            ) {
#if NVM_DEBUG_THREADS
            printf("switching thread: %lu\n", (int)pState->switch_time - now);
#endif
#if TIME_SCHEDULING
            pState->switch_time = now + TICKS_PER_TIME_SLICE;
#endif
            force_gc = switch_thread(pState, now);
            if (force_gc) {
                run_collector(pState, !force_gc, false);
#if TIME_SCHEDULING
                pState->gc_time = now + (TICKS_PER_TIME_SLICE*GC_INTERVAL);
#endif
                force_gc = switch_thread(pState, now);
            }
#if NVM_DEBUG_THREADS
            printf ("done switching thread\n");
#endif
            switch_thread_hook();
#if TIME_SCHEDULING
        } else if (now >= pState->gc_time) {
            run_collector(pState, true, false);
            pState->gc_time = now + (TICKS_PER_TIME_SLICE*GC_INTERVAL);
#endif
        }
        if (pState->current_thread == NULL   /* no runnable thread */
            && pState->scheduler_request_code == REQUEST_TICK) { /* no important request */
            do {
#if TIME_SCHEDULING
                force_gc = force_gc || (now >= pState->force_gc_time);
#endif
                wait_garbage_collect(pState, false, force_gc);
#if TIME_SCHEDULING
                now = atomic_time_get(pState->sys_time);
                if (force_gc) {
                    pState->force_gc_time = now + (TICKS_PER_TIME_SLICE*GC_INTERVAL);
                }
#endif
                check_events(pState);
                force_gc = switch_thread(pState, now);
            } while (force_gc);
            if (pState->current_thread == NULL) { // still nothing to run, go to sleep
#if TIME_SCHEDULING
                if (pState->wake_time >= pState->force_gc_time) {
                    pState->wake_time = pState->force_gc_time;
                }
#endif
                idle_hook(pState);
                pState->wake_time = APOCALYPSE;
                schedule_request(pState, REQUEST_SWITCH_THREAD);
            }
        }
    }
    
    assert(pState->scheduler_request_code == REQUEST_TICK, INTERPRETER2);
    assert(pState->current_thread != NULL, INTERPRETER3);

    return true;
}

void engine(nvm_state_t* pState)
{
    const byte *pc = pState->program_counter;
    STACKWORD *stackTop = pState->program_stack_top;
    STACKWORD *localsBase = pState->program_locals_base;
#if FAST_DISPATCH
    // The following table provides the main opcode dispatch table.
    // One entry per opcode, in opcode order. The subtraction makes
    // the value a relative offset allowing a smaller table and
    // allowing the table to be stored in ROM
    //
    // For some odd reason with some versions of gcc having an none multiple of 4
    // unique label entries in this table, results in approximately 1.5K more
    // code! To avoid this we tune the unique entry count by doubling up the
    // entries for opcodes that have multiple labels for the same code.
    


    static const DISPATCH_LABEL dispatch[] =
    {
        &&L_OP_NOP,
        &&L_OP_ACONST_NULL,
        &&L_OP_ICONST_M1,
        &&L_OP_ICONST_0,
        &&L_OP_ICONST_1,
        &&L_OP_ICONST_2,
        &&L_OP_ICONST_3,
        &&L_OP_ICONST_4,
        &&L_OP_ICONST_5,
        &&L_OP_LCONST_0,
        &&L_OP_LCONST_1,
        &&L_OP_FCONST_0,
        &&L_OP_FCONST_1,
        &&L_OP_FCONST_2,
        &&L_OP_DCONST_0,
        &&L_OP_DCONST_1,
        &&L_OP_BIPUSH,
        &&L_OP_SIPUSH,
        &&L_OP_LDC,
        &&L_OP_XXXUNUSEDXXX, //OP_LDC_W
        &&L_OP_LDC2_W,
        &&L_OP_ILOAD,
        &&L_OP_LLOAD,
        &&L_OP_FLOAD,
        &&L_OP_DLOAD,
        &&L_OP_ALOAD,
        &&L_OP_ILOAD_0,
        &&L_OP_ILOAD_1,
        &&L_OP_ILOAD_2,
        &&L_OP_ILOAD_3,
        &&L_OP_LLOAD_0,
        &&L_OP_LLOAD_1,
        &&L_OP_LLOAD_2,
        &&L_OP_LLOAD_3,
        &&L_OP_FLOAD_0,
        &&L_OP_FLOAD_1,
        &&L_OP_FLOAD_2,
        &&L_OP_FLOAD_3,
        &&L_OP_DLOAD_0,
        &&L_OP_DLOAD_1,
        &&L_OP_DLOAD_2,
        &&L_OP_DLOAD_3,
        &&L_OP_ALOAD_0,
        &&L_OP_ALOAD_1,
        &&L_OP_ALOAD_2,
        &&L_OP_ALOAD_3,
        &&L_OP_IALOAD,
        &&L_OP_LALOAD,
        &&L_OP_FALOAD,
        &&L_OP_DALOAD,
        &&L_OP_AALOAD,
        &&L_OP_BALOAD,
        &&L_OP_CALOAD,
        &&L_OP_SALOAD,
        &&L_OP_ISTORE,
        &&L_OP_LSTORE,
        &&L_OP_FSTORE,
        &&L_OP_DSTORE,
        &&L_OP_ASTORE,
        &&L_OP_ISTORE_0,
        &&L_OP_ISTORE_1,
        &&L_OP_ISTORE_2,
        &&L_OP_ISTORE_3,
        &&L_OP_LSTORE_0,
        &&L_OP_LSTORE_1,
        &&L_OP_LSTORE_2,
        &&L_OP_LSTORE_3,
        &&L_OP_FSTORE_0,
        &&L_OP_FSTORE_1,
        &&L_OP_FSTORE_2,
        &&L_OP_FSTORE_3,
        &&L_OP_DSTORE_0,
        &&L_OP_DSTORE_1,
        &&L_OP_DSTORE_2,
        &&L_OP_DSTORE_3,
        &&L_OP_ASTORE_0,
        &&L_OP_ASTORE_1,
        &&L_OP_ASTORE_2,
        &&L_OP_ASTORE_3,
        &&L_OP_IASTORE,
        &&L_OP_LASTORE,
        &&L_OP_FASTORE,
        &&L_OP_DASTORE,
        &&L_OP_AASTORE,
        &&L_OP_BASTORE,
        &&L_OP_CASTORE,
        &&L_OP_SASTORE,
        &&L_OP_POP,
        &&L_OP_POP2,
        &&L_OP_DUP,
        &&L_OP_DUP_X1,
        &&L_OP_DUP_X2,
        &&L_OP_DUP2,
        &&L_OP_DUP2_X1,
        &&L_OP_DUP2_X2,
        &&L_OP_SWAP,
        &&L_OP_IADD,
        &&L_OP_LADD,
        &&L_OP_FADD,
        &&L_OP_DADD,
        &&L_OP_ISUB,
        &&L_OP_LSUB,
        &&L_OP_FSUB,
        &&L_OP_DSUB,
        &&L_OP_IMUL,
        &&L_OP_LMUL,
        &&L_OP_FMUL,
        &&L_OP_DMUL,
        &&L_OP_IDIV,
        &&L_OP_LDIV,
        &&L_OP_FDIV,
        &&L_OP_DDIV,
        &&L_OP_IREM,
        &&L_OP_LREM,
        &&L_OP_FREM,
        &&L_OP_DREM,
        &&L_OP_INEG,
        &&L_OP_LNEG,
        &&L_OP_FNEG,
        &&L_OP_DNEG,
        &&L_OP_ISHL,
        &&L_OP_LSHL,
        &&L_OP_ISHR,
        &&L_OP_LSHR,
        &&L_OP_IUSHR,
        &&L_OP_LUSHR,
        &&L_OP_IAND,
        &&L_OP_LAND,
        &&L_OP_IOR,
        &&L_OP_LOR,
        &&L_OP_IXOR,
        &&L_OP_LXOR,
        &&L_OP_IINC,
        &&L_OP_I2L,
        &&L_OP_I2F,
        &&L_OP_I2D,
        &&L_OP_L2I,
        &&L_OP_L2F,
        &&L_OP_L2D,
        &&L_OP_F2I,
        &&L_OP_F2L,
        &&L_OP_F2D,
        &&L_OP_D2I,
        &&L_OP_D2L,
        &&L_OP_D2F,
        &&L_OP_I2B,
        &&L_OP_I2C,
        &&L_OP_I2S,
        &&L_OP_LCMP,
        &&L_OP_FCMPL,
        &&L_OP_FCMPG,
        &&L_OP_DCMPL,
        &&L_OP_DCMPG,
        &&L_OP_IFEQ,
        &&L_OP_IFNE,
        &&L_OP_IFLT,
        &&L_OP_IFGE,
        &&L_OP_IFGT,
        &&L_OP_IFLE,
        &&L_OP_IF_ICMPEQ,
        &&L_OP_IF_ICMPNE,
        &&L_OP_IF_ICMPLT,
        &&L_OP_IF_ICMPGE,
        &&L_OP_IF_ICMPGT,
        &&L_OP_IF_ICMPLE,
        &&L_OP_IF_ACMPEQ,
        &&L_OP_IF_ACMPNE,
        &&L_OP_GOTO,
        &&L_OP_JSR,
        &&L_OP_RET,
        &&L_OP_TABLESWITCH,
        &&L_OP_LOOKUPSWITCH,
        &&L_OP_IRETURN,
        &&L_OP_LRETURN,
        &&L_OP_FRETURN,
        &&L_OP_DRETURN,
        &&L_OP_ARETURN,
        &&L_OP_RETURN,
        &&L_OP_GETSTATIC,
        &&L_OP_PUTSTATIC,
        &&L_OP_GETFIELD,
        &&L_OP_PUTFIELD,
        &&L_OP_INVOKEVIRTUAL,
        &&L_OP_INVOKESPECIAL,
        &&L_OP_INVOKESTATIC,
        &&L_OP_INVOKEVIRTUAL2,
        &&L_OP_XXXUNUSEDXXX,
        &&L_OP_NEW,
        &&L_OP_NEWARRAY,
        &&L_OP_ANEWARRAY,
        &&L_OP_ARRAYLENGTH,
        &&L_OP_ATHROW,
        &&L_OP_CHECKCAST,
        &&L_OP_INSTANCEOF,
        &&L_OP_MONITORENTER,
        &&L_OP_MONITOREXIT,
        &&L_OP_WIDE,
        &&L_OP_MULTIANEWARRAY,
        &&L_OP_IFNULL,
        &&L_OP_IFNONNULL,
        &&L_OP_XXXUNUSEDXXX, //OP_GOTO_W
        &&L_OP_XXXUNUSEDXXX, //OP_JSR_W
        &&L_OP_XXXUNUSEDXXX, //OP_BREAKPOINT
        &&L_OP_GETSTATIC_1, // Note use of duplicate entries
        &&L_OP_GETSTATIC_1, // see the comment above for details
        &&L_OP_GETSTATIC_1,
        &&L_OP_GETSTATIC_1,
        &&L_OP_PUTSTATIC_1,
        &&L_OP_PUTSTATIC_1,
        &&L_OP_PUTSTATIC_1,
        &&L_OP_PUTSTATIC_1,
        &&L_OP_LDC_1,
        &&L_OP_LDC_2,
        &&L_OP_LDC_2,
        &&L_OP_LDC_2,
        &&L_OP_LDC_2,
        &&L_OP_LDC_2,
        &&L_OP_LDC_2,
        &&L_OP_LDC_2,
        &&L_OP_LDC_2,
        &&L_OP_LDC_2,
        &&L_OP_LDC_2,
        &&L_OP_LDC_2,
        &&L_OP_LDC_2,
        &&L_OP_LDC_2,
        &&L_OP_LDC_2,
        &&L_OP_LDC_2, // Duplicate entries. See above
        &&L_OP_GETFIELD_1,
        &&L_OP_PUTFIELD_1,
        &&L_OP_INVOKEJAVA,
    };
    
    // The following table is used to force the interpreter to jump to the
    // check event code rather than the next instruction. Basically causes a
    // jump to the check event code.
    static const DISPATCH_LABEL forceCheck[] =
    {
        &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT,
        &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT,
        &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT,
        &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT,
        &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT,
        &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT,
        &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT,
        &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT,
        &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT,
        &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT,
        &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT,
        &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT,
        &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT,
        &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT,
        &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT,
        &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT,
        &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT, &&CHECK_EVENT
    };

#endif

#if TIME_SCHEDULING
    pState->switch_time = atomic_time_get(pState->sys_time) + TICKS_PER_TIME_SLICE;
    pState->gc_time = pState->switch_time;
    pState->force_gc_time = pState->gc_time;
#endif
    
    assert( pState->current_thread != NULL, INTERPRETER0);
    INIT_DISPATCH
    schedule_request(pState, REQUEST_SWITCH_THREAD);
    
    assert( pState->current_thread != NULL, INTERPRETER1);
    
    if(!pState->current_thread) {
        return;
    }

    DISPATCH_EVENTS
    SAVE_REGS2(pState);
    if (!engine_check_events(pState)) {
        return;
    }
    LOAD_REGS2(pState);

    //-----------------------------------------------
    // SWITCH BEGINS HERE
    //-----------------------------------------------

#if NVM_DEBUG_BYTECODE
    printf ("PC: 0x%X: ", (int) pc);
    printf ("OPCODE (0x%X)\n", (int) *pc);
#endif

    START_DISPATCH
    
#include "op_skip.h"
#include "op_stack.h"
#include "op_locals.h"
#include "op_arrays.h"
#include "op_objects.h"
#include "op_control.h"
#include "op_other.h"
#include "op_conversions.h"
#include "op_logical.h"
#include "op_arithmetic.h"
#include "op_methods.h"
#include "op_unused.h"
    
    END_DISPATCH
    
    //-----------------------------------------------
    // SWITCH ENDS HERE
    //-----------------------------------------------

LABEL_THROW_VIRTUAL_MACHINE_ERROR:
    pState->thrown_exception = JAVA_LANG_VIRTUALMACHINEERROR;
    goto LABEL_THROW_EXCEPTION;
    
LABEL_THROW_ARITHMETIC_EXCEPTION:
    pState->thrown_exception = JAVA_LANG_ARITHMETICEXCEPTION;
    goto LABEL_THROW_EXCEPTION;
    
LABEL_THROW_NULLPTR_EXCEPTION:
    pState->thrown_exception = JAVA_LANG_NULLPOINTEREXCEPTION;
    goto LABEL_THROW_EXCEPTION;
    
LABEL_THROW_EXCEPTION:
    SAVE_REGS(pState);
    nvm_throw_new_exception(pState, pState->thrown_exception);
    LOAD_REGS(pState);
    DISPATCH_CHECKED;
}



