/*
 * 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_NEW)
  // Stack: +1
  // Arguments: 2
  // Hi byte unused
  {
    Object *newObj;
    SAVE_REGS(pState);
    nvm_class_index_t index = (pc[0] << 8) | pc[1];
    newObj = new_object_checked (pState, index, pc - 1);
    LOAD_REGS(pState);
    if (newObj != NULL) {
      push_ref (obj2ref(newObj));
      pc += 2;
    }
  }
  DISPATCH_CHECKED;

OPCODE(OP_GETSTATIC)
  // Optimized version, for 4 byte values only
  // Stack + 1
  // Arguments 2
  {
    nvm_class_index_t index = (pc[0] << 8) | pc[1];
    IndirectIndexRecord* index_record = get_indirect_index_record(pState, index);
    if (!is_initialized_idx (pState, index_record->class_index)) {
      SAVE_REGS(pState);
      int tempInt = dispatch_static_initializer (pState, get_class_record (pState, index_record->class_index), pc - 1);
      LOAD_REGS(pState);
      if (tempInt) {
        DISPATCH_CHECKED;
      }
    }
    push_word(read_stackword(get_static_state_base(pState) + index_record->method_index * sizeof(STACKWORD)));
    pc += 2;
  }
  DISPATCH;

OPCODE(OP_PUTSTATIC)
  // Optimized version, for 4 byte values only
  // Stack + 1
  // Arguments 2
  {
    nvm_class_index_t index = (pc[0] << 8) | pc[1];
    IndirectIndexRecord* index_record = get_indirect_index_record(pState, index);
    if (!is_initialized_idx (pState, index_record->class_index)) {
      SAVE_REGS(pState);
      int tempInt = dispatch_static_initializer (pState, get_class_record (pState, index_record->class_index), pc - 1);
      LOAD_REGS(pState);
      if (tempInt) {
        DISPATCH_CHECKED;
      }
    }
    write_stackword(get_static_state_base(pState) + index_record->method_index * sizeof(STACKWORD), pop_word());
    pc += 2;
  }
  DISPATCH;

OPCODE(OP_GETSTATIC_1)
MULTI_OPCODE(OP_GETSTATIC_2)
MULTI_OPCODE(OP_GETSTATIC_3)
MULTI_OPCODE(OP_GETSTATIC_4)
  // Stack: +1 or +2 for GETSTATIC
  {
    nvm_class_index_t index = (pc[0] << 8) | pc[1];
    IndirectIndexRecord* index_record = get_indirect_index_record(pState, index);
    if (!is_initialized_idx (pState, index_record->class_index)) {
      SAVE_REGS(pState);
      int tempInt = dispatch_static_initializer (pState, get_class_record (pState, index_record->class_index), pc - 1);
      LOAD_REGS(pState);
      if (tempInt) {
        DISPATCH_CHECKED;
      }
    }
    STACKWORD tempStackWord = ((STATICFIELD *) get_static_fields_base(pState))[((*(pc-1) - OP_GETSTATIC_1)*256 + index_record->method_index)];
    STACKWORD* tempWordPtr = (STACKWORD *)(get_static_state_base(pState) + get_static_field_offset (tempStackWord));
    tempStackWord >>= 12;
    if (tempStackWord == T_LONG || tempStackWord == T_DOUBLE) {
      push_word(get_word_ns((byte *)(tempWordPtr), T_LONG));
      push_word(get_word_ns((byte *)(tempWordPtr+1), T_LONG));
    } else {
      push_word(get_word_ns((byte *) tempWordPtr, tempStackWord));
    }
    pc += 2;
  }
  DISPATCH;

OPCODE(OP_PUTSTATIC_1)
MULTI_OPCODE(OP_PUTSTATIC_2)
MULTI_OPCODE(OP_PUTSTATIC_3)
MULTI_OPCODE(OP_PUTSTATIC_4)
  // Stack: -1 or -2 for GETSTATIC
  {
    nvm_class_index_t index = (pc[0] << 8) | pc[1];
    IndirectIndexRecord* index_record = get_indirect_index_record(pState, index);
    if (!is_initialized_idx (pState, index_record->class_index)) {
      SAVE_REGS(pState);
      int tempInt = dispatch_static_initializer (pState, get_class_record (pState, index_record->class_index), pc - 1);
      LOAD_REGS(pState);
      if (tempInt) {
        DISPATCH_CHECKED;
      }
    }
    STACKWORD tempStackWord = ((STATICFIELD *) get_static_fields_base(pState))[((*(pc-1) - OP_PUTSTATIC_1)*256 + index_record->method_index)];
    STACKWORD* tempWordPtr = (STACKWORD *)(get_static_state_base(pState) + get_static_field_offset (tempStackWord));
    tempStackWord >>= 12;
    if (tempStackWord  == T_LONG || tempStackWord == T_DOUBLE) {
      store_word_ns((byte *)(tempWordPtr + 1), T_LONG, pop_word());
      store_word_ns((byte *)(tempWordPtr), T_LONG, pop_word());
    } else {
      store_word_ns((byte *) tempWordPtr, tempStackWord, pop_word());
    }
    pc += 2;
  }
  DISPATCH;;

OPCODE(OP_GETFIELD)
  {
    // Optimized version for int/float/reference
    REFERENCE tempStackWord = get_top_ref();
    if (ref_null_p(tempStackWord)) {
      goto LABEL_THROW_NULLPTR_EXCEPTION;
    }

    STACKWORD* tempWordPtr = (STACKWORD *)(((byte *) ref2ptr (tempStackWord)) +
                get_pgfield_offset(pc[0], pc[1]));
    set_top_word(*tempWordPtr);
    pc += 2;
  }
  DISPATCH;

OPCODE(OP_PUTFIELD)
  {
    // Optimized version for int/float/reference
    REFERENCE tempStackWord = get_ref_at(1);
    if (ref_null_p(tempStackWord)) {
       goto LABEL_THROW_NULLPTR_EXCEPTION;
    }

    STACKWORD* tempWordPtr = (STACKWORD *)(((byte *) ref2ptr (tempStackWord)) +
                get_pgfield_offset(pc[0], pc[1]));
    if (get_pgfield_type(pc[0]) == T_REFERENCE) {
       update_object(pState, ref2obj(tempStackWord));
    }
    *tempWordPtr = pop_word();
    just_pop_ref();
    pc += 2;
  }
  DISPATCH;

OPCODE(OP_GETFIELD_1)
  {
    REFERENCE tempStackWord = get_top_ref();
    if (ref_null_p(tempStackWord)) {
       goto LABEL_THROW_NULLPTR_EXCEPTION;
    }

    STACKWORD* tempWordPtr = (STACKWORD *)(((byte *) ref2ptr (tempStackWord)) + 
                get_pgfield_offset(pc[0], pc[1]));
    uint8_t fieldType = get_pgfield_type(pc[0]);
    if (fieldType == T_LONG || fieldType == T_DOUBLE) {
      set_top_word(get_word_ns((byte *)tempWordPtr, T_LONG));
      push_word(get_word_ns((byte *)(tempWordPtr+1), T_LONG));
    } else {
      set_top_word(get_word_ns((byte *)tempWordPtr, fieldType));
    }
    pc += 2;
  }
  DISPATCH;

OPCODE(OP_PUTFIELD_1)
  {
    unsigned int fieldType;
    unsigned int offset;
    REFERENCE tempStackWord;
    STACKWORD* tempWordPtr;

    offset = get_pgfield_offset(pc[0], pc[1]);
    fieldType = get_pgfield_type(pc[0]);
    if (fieldType == T_LONG || fieldType == T_DOUBLE) {
      tempStackWord = get_ref_at (2);
      if (ref_null_p(tempStackWord)) {
         goto LABEL_THROW_NULLPTR_EXCEPTION;
      }
      tempWordPtr = (STACKWORD *)(((byte *) ref2ptr (tempStackWord)) + offset);
      store_word_ns((byte *)(tempWordPtr + 1), T_LONG, pop_word());
      store_word_ns((byte *)(tempWordPtr), T_LONG, pop_word());
    } else {
      tempStackWord = get_ref_at (1);
      if (ref_null_p(tempStackWord)) {
        goto LABEL_THROW_NULLPTR_EXCEPTION;
      }
      tempWordPtr = (STACKWORD *)(((byte *) ref2ptr (tempStackWord)) + offset);
      if (fieldType == T_REFERENCE) {
         update_object(pState, ref2ptr(tempStackWord));
      }
      store_word_ns((byte *)tempWordPtr, fieldType, pop_word());
    }
    just_pop_ref();
    pc += 2;
  }
  DISPATCH;

OPCODE(OP_INSTANCEOF)
  // Stack: unchanged
  // Arguments: 2
  set_top_word (instance_of (pState, ref2obj(get_top_ref()), (pc[0] << 8) | pc[1]));
  pc += 2;
  DISPATCH;

OPCODE(OP_CHECKCAST)
  // Stack: -1 +1 (same)
  // Arguments: 2
  {
    REFERENCE tempStackWord = get_top_ref();
    if (!ref_null_p(tempStackWord) && !instance_of (pState, ref2obj (tempStackWord), (pc[0] << 8) | pc[1])) {
      pState->thrown_exception = JAVA_LANG_CLASSCASTEXCEPTION;
      goto LABEL_THROW_EXCEPTION;
    }
    pc += 2;
  }
  DISPATCH;

// Notes:
// - GETFIELD and PUTFIELD: 4-bit field type, 12-bit field data offset

/*end*/
