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

/**
 * This is included inside a switch statement.
 */

OPCODE(OP_IF_ICMPEQ)
OPCODE(OP_IF_ACMPEQ)
  // Arguments: 2
  // Stack: -2
  {
    STACKWORD tempStackWord = pop_word();
    set_top_word ((UJINT) word2jint(get_top_word()) - (UJINT) word2jint(tempStackWord));
  }
  // Fall through!
OPCODE(OP_IFEQ)
OPCODE(OP_IFNULL)
  // Arguments: 2
  // Stack: -1
  pc = do_goto (pc, pop_word() == 0);
  DISPATCH_CHECKED;

OPCODE(OP_IF_ICMPNE)
OPCODE(OP_IF_ACMPNE)
  {
    STACKWORD tempStackWord = pop_word();
    set_top_word ((UJINT) word2jint(get_top_word()) - (UJINT) word2jint(tempStackWord));
  }
  // Fall through!
OPCODE(OP_IFNE)
OPCODE(OP_IFNONNULL)
  pc = do_goto (pc, pop_word() != 0);
  DISPATCH_CHECKED;

OPCODE(OP_IFLT)
  pc = do_goto (pc, pop_jint() < 0);
  DISPATCH_CHECKED;

OPCODE(OP_IF_ICMPLT)
  {
    STACKWORD tempStackWord = pop_word();
    pc = do_goto (pc, pop_jint() < word2jint(tempStackWord));
  }
  DISPATCH_CHECKED;

OPCODE(OP_IFLE)
  pc = do_goto (pc, pop_jint() <= 0);
  DISPATCH_CHECKED;

OPCODE(OP_IF_ICMPLE)
  {
    STACKWORD tempStackWord = pop_word();
    pc = do_goto (pc, pop_jint() <= word2jint(tempStackWord));
  }
  DISPATCH_CHECKED;

OPCODE(OP_IFGE)
  pc = do_goto (pc, pop_jint() >= 0);
  DISPATCH_CHECKED;

OPCODE(OP_IF_ICMPGE)
  {
    STACKWORD tempStackWord = pop_word();
    pc = do_goto (pc, pop_jint() >= word2jint(tempStackWord));
  }
  DISPATCH_CHECKED;

OPCODE(OP_IFGT)
  pc = do_goto (pc, pop_jint() > 0);
  DISPATCH_CHECKED;

OPCODE(OP_IF_ICMPGT)
  {
    STACKWORD tempStackWord = pop_word();
    pc = do_goto (pc, pop_jint() > word2jint(tempStackWord));
  }
  DISPATCH_CHECKED;

OPCODE(OP_JSR)
  // Arguments: 2
  // Stack: +1
  push_ref(ptr2ref(pc + 2));
  // Fall through!
OPCODE(OP_GOTO)
  // Arguments: 2
  // Stack: +0
  pc = do_goto (pc, true);
  // No pc increment!
  DISPATCH_CHECKED;

OPCODE(OP_RET)
  // Arguments: 1
  // Stack: +0
  pc = ref2ptr(get_local_ref(pc[0]));
  #if NVM_DEBUG_BYTECODE
  printf ("\n  OP_RET: returning to %d\n", (int) pc);
  #endif
  // No pc increment!
  DISPATCH_CHECKED;

#if FP_DOUBLE_ARITHMETIC

OPCODE(OP_DCMPL)
  {
    JDOUBLE d1, d2;
    pop_jdouble(&d2);
    pop_jdouble(&d1);
    push_word( do_dcmp (d1, d2, -1));
  }
  DISPATCH;

OPCODE(OP_DCMPG)
  {
    JDOUBLE d1, d2;
    pop_jdouble(&d2);
    pop_jdouble(&d1);
    push_word( do_dcmp (d1, d2, 1));
  }
  DISPATCH;

#endif // FP_DOUBLE_ARITHMETIC

#if FP_ARITHMETIC

OPCODE(OP_FCMPL)
  {
    STACKWORD tempStackWord = pop_word();
    just_set_top_word( do_fcmp (word2jfloat(get_top_word()), word2jfloat(tempStackWord), -1));
  }
  DISPATCH;

OPCODE(OP_FCMPG)
  // TBD: no distinction between opcodes
  {
    STACKWORD tempStackWord = pop_word();
    just_set_top_word( do_fcmp (word2jfloat(get_top_word()), word2jfloat(tempStackWord), 1));
  }
  DISPATCH;
  
#endif // FP_ARITHMETIC
  
OPCODE(OP_LCMP)
  // Arguments: 0
  // Stack: -4 + 1
  {
    JLONG l1, l2;
    pop_jlong (&l2);
    pop_jlong (&l1);
    push_word (do_lcmp(l1, l2, 0));
  }
  DISPATCH;

OPCODE(OP_LOOKUPSWITCH)
  {
    // padding removed while linking
    int off, npairs, idx8;
    const byte *from, *to;

    off = get_word_4_swp( pc);
    npairs = get_word_4_swp( pc + 4);

    STACKWORD idx = pop_word();
    idx8 = (byte) idx;

    for( from = pc + 8, to = from + npairs * 8; from < to; from += 8) {
       if( from[ 3] == idx8) { // fast compare of low byte of match value
           if( get_word_4_swp( from) == idx) {
              off = get_word_4_swp( from + 4);
              break;
           }
       }
    }
    pc += off - 1;
  }
  DISPATCH_CHECKED;    

OPCODE(OP_TABLESWITCH)
  {
    // padding removed while linking
    int off, low, hig, idx;

    off = get_word_4_swp( pc);
    low = get_word_4_swp( pc + 4);
    hig = get_word_4_swp( pc + 8);

    idx = pop_word();
    if( idx >= low && idx <= hig) {
      off = get_word_4_swp( pc + 12 + ((idx - low) << 2));
    }

    pc += off - 1;
  }
  DISPATCH_CHECKED;    

// Notes:
// - Not supported: GOTO_W, JSR_W, LCMP

/*end*/
