/* -*- Mode: C; c-file-style: "gnu"; indent-tabs-mode:nil -*-
   interpfunc.c -- opcode functions (this file is included into interploop.c).
   Created: Chris Toshok <toshok@hungry.com>
 */
/*
  This file is part of Japhar, the GNU Virtual Machine for Java Bytecodes.
  Japhar is a project of The Hungry Programmers, GNU, and OryxSoft.

  Copyright (C) 1997, 1998, 1999 The Hungry Programmers

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Library General Public
  License as published by the Free Software Foundation; either
  version 2 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
  Library General Public License for more details.

  You should have received a copy of the GNU Library General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/


OPCODE_START(nop, 0)
{
}

OPCODE(wide, 196)
{
  PRUint8 oc = get_next_u1(f);
  PRUint16 vindex = get_next_u2(f);

  if (oc >= 21 /*iload*/ && oc <= 25 /*aload*/)
    {
      if (oc == 22 /*lload*/ || oc == 24 /*dload*/)
        {
          /* doubleword */
          op_stack_push_doubleword(f->_env->op_stack,
                                   f->vars[vindex].value.i,
                                   f->vars[vindex + 1].value.i);
        }
      else
        {
          /* singleword */
          op_stack_push_any(f->_env->op_stack,
                            f->vars[vindex].value.l);
        }
    }
  else if (oc >= 54 /*istore*/ && oc <= 58 /*astore*/)
    {
      if (oc == 55 /*lstore*/ || oc == 57 /*dstore*/)
        {
          /* doubleword */
          op_stack_pop_doubleword (f->_env->op_stack,
                                   f->vars[ vindex ].value.i,
                                   f->vars[ vindex+1 ].value.i);
        }
      else
        {
          /* singleword */
          op_stack_pop_any(f->_env->op_stack,
                           f->vars[vindex].value.l);
        }
    }
  else if (oc == 169 /*ret*/)
    {
      f->pc = (int)f->vars[vindex].value.i;
    }
  else /* oc == iinc */
    {
      PRInt16 c = get_next_u2(f);
      f->vars[vindex].value.i += c;
    }
}

OPCODE(breakpoint, 202) /* reserved opcode */
{
#ifdef WITH_JVMDI
  /* the bytecode verifier has already checked to make sure this
     opcode doesn't show up in the bytestream, so we must be running
     in a debugger.  generate a JVMDI event. */

  jvmdi_BreakpointEvent(f->_env, f->_env->java_thread,
                        clazzfile_to_jclass(f->_env, f->method->clazz), f->method,
                        f->pc - 1);
#endif
}

OPCODE(bipush, 16)
{
  op_stack_push_int(f->_env->op_stack, (PRInt8)get_next_u1(f));
}

OPCODE(sipush, 17)
{
  op_stack_push_int(f->_env->op_stack, (PRInt16)get_next_u2(f));
}

OPCODE(ldc, 18)
{
  push_item_from_constant_pool (f, get_next_u1(f));
}

OPCODE(ldc_w, 19)
{
  push_item_from_constant_pool (f, get_next_u2(f));
}

OPCODE(ldc2_w, 20)
{
  push_item_from_constant_pool (f, get_next_u2(f));
}

OPCODE(aconst_null, 1)
{
  op_stack_push_object(f->_env->op_stack, 0);
}

OPCODE(iconst_m1, 2)
{
  op_stack_push_int(f->_env->op_stack, -1);
}

OPCODE(iconst_0, 3)
{
  op_stack_push_int(f->_env->op_stack, 0);
}

OPCODE(iconst_1, 4)
{
  op_stack_push_int(f->_env->op_stack, 1);
}

OPCODE(iconst_2, 5)
{
  op_stack_push_int(f->_env->op_stack, 2);
}

OPCODE(iconst_3, 6)
{
  op_stack_push_int(f->_env->op_stack, 3);
}

OPCODE(iconst_4, 7)
{
  op_stack_push_int(f->_env->op_stack, 4);
}

OPCODE(iconst_5, 8)
{
  op_stack_push_int(f->_env->op_stack, 5);
}

OPCODE(lconst_0, 9)
{
  op_stack_push_long(f->_env->op_stack, 0);
}

OPCODE(lconst_1, 10)
{
  op_stack_push_long(f->_env->op_stack, 1l);
}

OPCODE(fconst_0, 11)
{
  op_stack_push_float(f->_env->op_stack, 0.0);
}

OPCODE(fconst_1, 12)
{
  op_stack_push_float(f->_env->op_stack, 1.0);
}

OPCODE(fconst_2, 13)
{
  op_stack_push_float(f->_env->op_stack, 2.0);
}

OPCODE(dconst_0, 14)
{
  op_stack_push_double(f->_env->op_stack, 0.0);
}

OPCODE(dconst_1, 15)
{
  op_stack_push_double(f->_env->op_stack, 1.0);
}

OPCODE(iload, 21)
{
  PRUint16 vindex = get_next_u1(f);

  op_stack_push_int(f->_env->op_stack,
                    f->vars[vindex].value.i);
}

OPCODE(iload_0, 26)
{
  op_stack_push_int(f->_env->op_stack, f->vars[0].value.i);
}

OPCODE(iload_1, 27)
{
  op_stack_push_int(f->_env->op_stack, f->vars[1].value.i);
}

OPCODE(iload_2, 28)
{
  op_stack_push_int(f->_env->op_stack, f->vars[2].value.i);
}

OPCODE(iload_3, 29)
{
  op_stack_push_int(f->_env->op_stack, f->vars[3].value.i);
}

OPCODE(lload, 22)
{
  PRUint16 vindex = get_next_u1(f);
  
  op_stack_push_doubleword(f->_env->op_stack,
                           f->vars[vindex].value.i,
                           f->vars[vindex + 1].value.i);
}

OPCODE(lload_0, 30)
{
  op_stack_push_doubleword(f->_env->op_stack,
                           f->vars[0].value.i,
                           f->vars[1].value.i);
}

OPCODE(lload_1, 31)
{
  op_stack_push_doubleword(f->_env->op_stack,
                           f->vars[1].value.i,
                           f->vars[2].value.i);
}

OPCODE(lload_2, 32)
{
  op_stack_push_doubleword(f->_env->op_stack,
                           f->vars[2].value.i,
                           f->vars[3].value.i);
}

OPCODE(lload_3, 33)
{
  op_stack_push_doubleword(f->_env->op_stack,
                           f->vars[3].value.i,
                           f->vars[4].value.i);
}

OPCODE(fload, 23)
{
  PRUint16 vindex = get_next_u1(f);

  op_stack_push_float(f->_env->op_stack, f->vars[vindex].value.f);
}

OPCODE(fload_0, 34)
{
  op_stack_push_float(f->_env->op_stack, f->vars[0].value.f);
}

OPCODE(fload_1, 35)
{
  op_stack_push_float(f->_env->op_stack, f->vars[1].value.f);
}

OPCODE(fload_2, 36)
{
  op_stack_push_float(f->_env->op_stack, f->vars[2].value.f);
}

OPCODE(fload_3, 37)
{
  op_stack_push_float(f->_env->op_stack, f->vars[3].value.f);
}

OPCODE(dload, 24)
{
  PRUint16 vindex = get_next_u1(f);
  
  op_stack_push_doubleword(f->_env->op_stack,
                           f->vars[vindex].value.i,
                           f->vars[vindex + 1].value.i);
}

OPCODE(dload_0, 38)
{
  op_stack_push_doubleword(f->_env->op_stack,
                           f->vars[0].value.i,
                           f->vars[1].value.i);
}

OPCODE(dload_1, 39)
{
  op_stack_push_doubleword(f->_env->op_stack,
                           f->vars[1].value.i,
                           f->vars[2].value.i);
}

OPCODE(dload_2, 40)
{
  op_stack_push_doubleword(f->_env->op_stack,
                           f->vars[2].value.i,
                           f->vars[3].value.i);
}

OPCODE(dload_3, 41)
{
  op_stack_push_doubleword(f->_env->op_stack,
                           f->vars[3].value.i,
                           f->vars[4].value.i);
}

OPCODE(aload, 25)
{
  PRUint16 vindex = get_next_u1(f);

  op_stack_push_object(f->_env->op_stack,
                       f->vars[vindex].value.l);
}

OPCODE(aload_0, 42)
{
  op_stack_push_object(f->_env->op_stack, f->vars[0].value.l);
}

OPCODE(aload_1, 43)
{
  op_stack_push_object(f->_env->op_stack, f->vars[1].value.l);
}

OPCODE(aload_2, 44)
{
  op_stack_push_object(f->_env->op_stack, f->vars[2].value.l);
}

OPCODE(aload_3, 45)
{
  op_stack_push_object(f->_env->op_stack, f->vars[3].value.l);
}

OPCODE(ishl, 120)
{
  PRInt32 value1;
  PRInt32 value2;

  op_stack_pop_int (f->_env->op_stack, PRInt32, value2);
  op_stack_pop_int (f->_env->op_stack, PRInt32, value1);  

  value1 <<= (value2 & 0x1F); /* value2 & low 5 bits */

  op_stack_push_int(f->_env->op_stack, value1);
}

OPCODE(ishr, 122)
{
  PRInt32 value1, value2;

  op_stack_pop_int (f->_env->op_stack, PRInt32, value2);
  op_stack_pop_int (f->_env->op_stack, PRInt32, value1);  

  value1 = value1 >> (value2 & 0x1F); /* value2 & low 5 bits */

  /* sign extension ???
     leave the top bit alone, do the shift, and then put the sign back */

  op_stack_push_int(f->_env->op_stack, value1);
}

OPCODE(iushr, 124)
{
  PRInt32 value1, value2;
  PRUint8 s;

  op_stack_pop_int (f->_env->op_stack, PRInt32, value2);
  op_stack_pop_int (f->_env->op_stack, PRInt32, value1);

  s = (PRUint8)(value2 & 0x1F);

  if (value1 >= 0)
    value1 >>= s;
  else
    value1 = (value1 >> s) + (2 << ~s);

  op_stack_push_int(f->_env->op_stack, value1);
}

OPCODE(iand, 126)
{
  PRInt32 value1, value2;

  op_stack_pop_int (f->_env->op_stack, PRInt32, value2);
  op_stack_pop_int (f->_env->op_stack, PRInt32, value1);  

  op_stack_push_int(f->_env->op_stack, value1 & value2);
}

OPCODE(land, 127)
{
  PRInt64 value1, value2, result;

  op_stack_pop_long (f->_env->op_stack, value2);
  op_stack_pop_long (f->_env->op_stack, value1);

  LL_AND(result, value1, value2);

  op_stack_push_long(f->_env->op_stack, result);
}

OPCODE(ior, 128)
{
  PRInt32 value1, value2;

  op_stack_pop_int (f->_env->op_stack, PRInt32, value2);
  op_stack_pop_int (f->_env->op_stack, PRInt32, value1);  

  op_stack_push_int(f->_env->op_stack, value1 | value2); 
}

OPCODE(lor, 129)
{
  PRInt64 value1, value2, result;

  op_stack_pop_long (f->_env->op_stack, value1);
  op_stack_pop_long (f->_env->op_stack, value2);

  LL_OR(result, value1, value2);

  op_stack_push_long (f->_env->op_stack, result);
}

OPCODE(ixor, 130)
{
  PRInt32 value1, value2;

  op_stack_pop_int (f->_env->op_stack, PRInt32, value2);
  op_stack_pop_int (f->_env->op_stack, PRInt32, value1);  

  op_stack_push_int (f->_env->op_stack, value1 ^ value2); 
}

OPCODE(lxor, 131)
{
  PRInt64 value1, value2, result;

  op_stack_pop_long (f->_env->op_stack, value1);
  op_stack_pop_long (f->_env->op_stack, value2);

  LL_XOR(result, value1, value2);

  op_stack_push_long (f->_env->op_stack, result);
}

OPCODE(lshl, 121)
{
  PRInt64 value1, result;
  PRInt32 value2;

  op_stack_pop_int (f->_env->op_stack, PRInt32, value2);
  op_stack_pop_long (f->_env->op_stack, value1);

  result = value1 << (value2 & 0x3F); /* value2 & low 6 bits */

  op_stack_push_long(f->_env->op_stack, result);
}

OPCODE(lshr, 123)
{
  jlong value1, result;
  PRInt32 value2;

  op_stack_pop_int (f->_env->op_stack, PRInt32, value2);
  op_stack_pop_long (f->_env->op_stack, value1);

  result = value1 >> (value2 & 0x3F); /* value2 & low 6 bits */

  op_stack_push_long(f->_env->op_stack, result);
}

OPCODE(lushr, 125)
{
  jlong value1, result;
  PRInt32 value2;
  PRUint8 s;

  op_stack_pop_int (f->_env->op_stack, PRInt32, value2);
  op_stack_pop_long (f->_env->op_stack, value1);

  s = value2 & 0x3F;

  if (value1 >= 0)
    result = value1 >> s;
  else
    result = (value1 >> s) + ((jlong)2 << ~s);

  op_stack_push_long(f->_env->op_stack, result);
}

OPCODE(istore, 54)
{
  PRUint16 vindex = get_next_u1(f);

  op_stack_pop_int (f->_env->op_stack,
                    PRInt32, f->vars[ vindex ].value.i);
#ifdef WITH_JVMDI
  f->vars[ vindex ].tag = JSIG_INT;
#endif
}

OPCODE(lstore, 55)
{
  PRUint16 vindex = get_next_u1(f);

  op_stack_pop_doubleword (f->_env->op_stack,
                           f->vars[ vindex ].value.i,
                           f->vars[ vindex+1 ].value.i);
#ifdef WITH_JVMDI
  f->vars[ vindex ].tag = JSIG_LONG;
  f->vars[ vindex ].tag = JSIG_LONG2;
#endif
}

OPCODE(lstore_0, 63)
{
  op_stack_pop_doubleword (f->_env->op_stack,
                           f->vars[ 0 ].value.i,
                           f->vars[ 1 ].value.i);
#ifdef WITH_JVMDI
  f->vars[ 0 ].tag = JSIG_LONG;
  f->vars[ 1 ].tag = JSIG_LONG2;
#endif
}

OPCODE(lstore_1, 64)
{
  op_stack_pop_doubleword (f->_env->op_stack,
                           f->vars[ 1 ].value.i,
                           f->vars[ 2 ].value.i);
#ifdef WITH_JVMDI
  f->vars[ 1 ].tag = JSIG_LONG;
  f->vars[ 2 ].tag = JSIG_LONG2;
#endif
}

OPCODE(lstore_2, 65)
{
  op_stack_pop_doubleword (f->_env->op_stack,
                           f->vars[ 2 ].value.i,
                           f->vars[ 3 ].value.i);
#ifdef WITH_JVMDI
  f->vars[ 2 ].tag = JSIG_LONG;
  f->vars[ 3 ].tag = JSIG_LONG2;
#endif
}

OPCODE(lstore_3, 66)
{
  op_stack_pop_doubleword (f->_env->op_stack,
                           f->vars[ 3 ].value.i,
                           f->vars[ 4 ].value.i);
#ifdef WITH_JVMDI
  f->vars[ 3 ].tag = JSIG_LONG;
  f->vars[ 4 ].tag = JSIG_LONG2;
#endif
}

OPCODE(fstore, 56)
{
  PRUint16 vindex = get_next_u1(f);

  op_stack_pop_float (f->_env->op_stack,
                      f->vars[ vindex ].value.f);
#ifdef WITH_JVMDI
  f->vars[ vindex ].tag = JSIG_FLOAT;
#endif
}

OPCODE(fstore_0, 67)
{
  op_stack_pop_float (f->_env->op_stack,
                      f->vars[ 0 ].value.f);
  f->vars[ 0 ].tag = JSIG_FLOAT;
}

OPCODE(fstore_1, 68)
{
  op_stack_pop_float (f->_env->op_stack,
                      f->vars[ 1 ].value.f);
#ifdef WITH_JVMDI
  f->vars[ 1 ].tag = JSIG_FLOAT;
#endif
}

OPCODE(fstore_2, 69)
{
  op_stack_pop_float (f->_env->op_stack,
                      f->vars[ 2 ].value.f);
#ifdef WITH_JVMDI
  f->vars[ 2 ].tag = JSIG_FLOAT;
#endif
}

OPCODE(fstore_3, 70)
{
  op_stack_pop_float (f->_env->op_stack,
                      f->vars[ 3 ].value.f);
#ifdef WITH_JVMDI
  f->vars[ 3 ].tag = JSIG_FLOAT;
#endif
}

OPCODE(dstore, 57)
{
  PRUint16 vindex = get_next_u1(f);

  op_stack_pop_doubleword (f->_env->op_stack,
                           f->vars[ vindex ].value.i,
                           f->vars[ vindex+1 ].value.i);
#ifdef WITH_JVMDI
  f->vars[ vindex ].tag = JSIG_DOUBLE;
  f->vars[ vindex + 1 ].tag = JSIG_DOUBLE2;
#endif
}

OPCODE(dstore_0, 71)
{
  op_stack_pop_doubleword (f->_env->op_stack,
                           f->vars[ 0 ].value.i,
                           f->vars[ 1 ].value.i);
#ifdef WITH_JVMDI
  f->vars[ 0 ].tag = JSIG_DOUBLE;
  f->vars[ 1 ].tag = JSIG_DOUBLE2;
#endif
}

OPCODE(dstore_1, 72)
{
  op_stack_pop_doubleword (f->_env->op_stack,
                           f->vars[ 1 ].value.i,
                           f->vars[ 2 ].value.i);
#ifdef WITH_JVMDI
  f->vars[ 1 ].tag = JSIG_DOUBLE;
  f->vars[ 2 ].tag = JSIG_DOUBLE2;
#endif
}

OPCODE(dstore_2, 73)
{
  op_stack_pop_doubleword (f->_env->op_stack,
                           f->vars[ 2 ].value.i,
                           f->vars[ 3 ].value.i);
#ifdef WITH_JVMDI
  f->vars[ 2 ].tag = JSIG_DOUBLE;
  f->vars[ 3 ].tag = JSIG_DOUBLE2;
#endif
}

OPCODE(dstore_3, 74)
{
  op_stack_pop_doubleword (f->_env->op_stack,
                           f->vars[ 3 ].value.i,
                           f->vars[ 4 ].value.i);
#ifdef WITH_JVMDI
  f->vars[ 3 ].tag = JSIG_DOUBLE;
  f->vars[ 4 ].tag = JSIG_DOUBLE2;
#endif
}

OPCODE(astore, 58)
{
  PRUint16 vindex = get_next_u1(f);

  op_stack_pop_object (f->_env->op_stack,
                       f->vars[ vindex ].value.l);
#ifdef WITH_JVMDI
  f->vars[ vindex ].tag = JSIG_OBJECT;
#endif
}

OPCODE(istore_0, 59)
{
  op_stack_pop_int (f->_env->op_stack,
                    PRInt32, 
                    f->vars[ 0 ].value.i);
#ifdef WITH_JVMDI
  f->vars[ 0 ].tag = JSIG_INT;
#endif
}

OPCODE(istore_1, 60)
{
  op_stack_pop_int (f->_env->op_stack,
                    PRInt32, 
                    f->vars[ 1 ].value.i);
#ifdef WITH_JVMDI
  f->vars[ 1 ].tag = JSIG_INT;
#endif
}

OPCODE(istore_2, 61)
{
  op_stack_pop_int (f->_env->op_stack,
                    PRInt32, 
                    f->vars[ 2 ].value.i);
#ifdef WITH_JVMDI
  f->vars[ 2 ].tag = JSIG_INT;
#endif
}

OPCODE(istore_3, 62)
{
  op_stack_pop_int (f->_env->op_stack,
                    PRInt32, 
                    f->vars[ 3 ].value.i);
#ifdef WITH_JVMDI
  f->vars[ 3 ].tag = JSIG_INT;
#endif
}

OPCODE(astore_0, 75)
{
  op_stack_pop_object (f->_env->op_stack,
                       f->vars[ 0 ].value.l);
#ifdef WITH_JVMDI
  f->vars[ 0 ].tag = JSIG_OBJECT;
#endif
}

OPCODE(astore_1, 76)
{
  op_stack_pop_object (f->_env->op_stack,
                       f->vars[ 1 ].value.l);
#ifdef WITH_JVMDI
  f->vars[ 1 ].tag = JSIG_OBJECT;
#endif
}

OPCODE(astore_2, 77)
{
  op_stack_pop_object (f->_env->op_stack,
                       f->vars[ 2 ].value.l);
#ifdef WITH_JVMDI
  f->vars[ 2 ].tag = JSIG_OBJECT;
#endif
}

OPCODE(astore_3, 78)
{
  op_stack_pop_object (f->_env->op_stack,
                       f->vars[ 3 ].value.l);
#ifdef WITH_JVMDI
  f->vars[ 3 ].tag = JSIG_OBJECT;
#endif
}

OPCODE(putfield, 181)
{
  PRUint16 index;
  InterpValue value;
  japhar_object* objectref;
  FieldStruct *field;

  index = get_next_u2(f);
  
  field = ResolveFieldRef(f->_env, f->method->clazz,
                          &f->method->clazz->constants[index]);

  if (field->access_flags & ACC_STATIC)
    {
      HVM_ExceptionThrow(f->_env, java_lang_IncompatibleClassChangeError, NULL);
      break;
    }

  op_stack_pop_value(f->_env->op_stack, *field->sig_str, value);

  op_stack_pop_object (f->_env->op_stack, objectref);

  if (objectref == 0)
    {
      HVM_ExceptionThrow(f->_env, java_lang_NullPointerException, NULL);
      break;
    }

  HVM_FieldSet(objectref, field, value);
}

OPCODE(getfield, 180)
{
  PRUint16 index;
  japhar_object* objectref;
  FieldStruct *field;
  InterpValue value;

  index = get_next_u2(f);

  field = ResolveFieldRef(f->_env, f->method->clazz,
                          &f->method->clazz->constants[index]);

  if (field->access_flags & ACC_STATIC)
    {
      HVM_ExceptionThrow(f->_env, java_lang_IncompatibleClassChangeError, NULL);
      break;
    }

  op_stack_pop_object (f->_env->op_stack, objectref);

  if (objectref == 0)
    {
      HVM_ExceptionThrow(f->_env, java_lang_NullPointerException, NULL);
      break;
    }

  HVM_FieldGet(objectref, field, &value);
  op_stack_push_value(f->_env->op_stack,
                      *field->sig_str,
                      value);
}

OPCODE(putstatic, 179)
{
  PRUint16 index;
  InterpValue value;
  FieldStruct *field;

  index = get_next_u2(f);
  
  field = ResolveFieldRef(f->_env, f->method->clazz,
                          &f->method->clazz->constants[ index ]);

  if (!(field->access_flags & ACC_STATIC))
    {
      HVM_ExceptionThrow(f->_env, java_lang_IncompatibleClassChangeError, NULL);
      break;
    }

  initialize_class(f->_env, field->clazz);

  op_stack_pop_value(f->_env->op_stack,
                     *field->sig_str,
                     value);
  HVM_FieldSetStatic(field->clazz, field, value);
}

OPCODE(getstatic, 178)
{
  PRUint16 index;
  FieldStruct *field;
  InterpValue value;

  index = get_next_u2(f);

  field = ResolveFieldRef(f->_env, f->method->clazz,
                          &f->method->clazz->constants[ index ]);

  if (!(field->access_flags & ACC_STATIC))
    {
      HVM_ExceptionThrow(f->_env, java_lang_IncompatibleClassChangeError, NULL);
      break;
    }

  initialize_class(f->_env, field->clazz);

  HVM_FieldGetStatic(field->clazz, field, &value);
  op_stack_push_value(f->_env->op_stack,
                      *field->sig_str,
                      value);
}

OPCODE(new, 187)
{
  PRUint16 index;
  ClazzFile *cf;
  japhar_object* object_reference;

  index = get_next_u2(f);

  cf = ResolveClass(f->_env, f->method->clazz,
                    &f->method->clazz->constants[ index ]);
  PR_LOG(interpLm, PR_LOG_DEBUG, 
         ("new '%s'\n", getClassName(f->_env, cf)));
  object_reference = HVM_ObjectNew (f->_env, cf);

  if (object_reference == NULL)
    break; /* HVM_ObjectNew or one of its callees has already thrown
              an exception */

  op_stack_push_object (f->_env->op_stack, object_reference);
}

OPCODE(checkcast, 192)
{
  PRUint16 index;
  japhar_object* object_ref;
  ClazzFile *cf;

  index = get_next_u2(f);

  cf = ResolveClass(f->_env, f->method->clazz,
                    &f->method->clazz->constants[ index ]);

  op_stack_pop_object(f->_env->op_stack, object_ref);

  /* trivial case -- thunk is NULL, which is castable to anything */
  if (!object_ref)
    {
      op_stack_push_object(f->_env->op_stack, object_ref);
      break;
    }
  else
    {
      if (HVM_ObjectIsInstanceOf(f->_env, object_ref, cf))
        {
          op_stack_push_object(f->_env->op_stack, object_ref);
          break;
        }
      else
        {
          char buffer[200];
          PR_snprintf(buffer, sizeof(buffer), "can't cast '%s' to '%s'",
                      getClassName(f->_env, object_ref->clazz),
                      getClassName(f->_env, cf));
          HVM_ExceptionThrow(f->_env, java_lang_ClassCastException, buffer);
          break;
        }
    }
}

OPCODE(instanceof, 193)
{
  PRUint16 index;
  japhar_object* objectref;

  index = get_next_u2(f);

  op_stack_pop_object (f->_env->op_stack, objectref);

  /* null object is not instance of anything */
  if (objectref == 0)
    {
      op_stack_push_int (f->_env->op_stack, PR_FALSE);
    }
  else
    {
      ClazzFile *cf = ResolveClass(f->_env, f->method->clazz,
                                   &f->method->clazz->constants[ index ]);

      op_stack_push_int (f->_env->op_stack, 
                         HVM_ObjectIsInstanceOf(f->_env, objectref, cf));
    }
}

OPCODE(pop, 87)
{
  f->_env->op_stack->stack_top -= 1;
}

OPCODE(pop2, 88)
{
  f->_env->op_stack->stack_top -= 2;
}

OPCODE(dup, 89)
{
  op_stack_push_any(f->_env->op_stack,
                    *(f->_env->op_stack->stack_top - 1));
}

OPCODE(dupx1, 90)
{
  void *any1, *any2;

  op_stack_pop_any(f->_env->op_stack, any1);
  op_stack_pop_any(f->_env->op_stack, any2);

  op_stack_push_any(f->_env->op_stack, any1);
  op_stack_push_any(f->_env->op_stack, any2);

  op_stack_push_any(f->_env->op_stack, any1);
}

OPCODE(dupx2, 91)
{
  void *any1, *any2, *any3;

  op_stack_pop_any(f->_env->op_stack, any1);
  op_stack_pop_any(f->_env->op_stack, any2);
  op_stack_pop_any(f->_env->op_stack, any3);

  op_stack_push_any(f->_env->op_stack, any1);
  op_stack_push_any(f->_env->op_stack, any3);
  op_stack_push_any(f->_env->op_stack, any2);

  op_stack_push_any(f->_env->op_stack, any1);
}

OPCODE(dup2, 92)
{
  void *any1, *any2;

  op_stack_pop_any(f->_env->op_stack, any1);
  op_stack_pop_any(f->_env->op_stack, any2);

  op_stack_push_any(f->_env->op_stack, any2);
  op_stack_push_any(f->_env->op_stack, any1);

  op_stack_push_any(f->_env->op_stack, any2);
  op_stack_push_any(f->_env->op_stack, any1);
}

OPCODE(dup2x1, 93)
{
  void *any1, *any2, *any3;

  op_stack_pop_any(f->_env->op_stack, any1);
  op_stack_pop_any(f->_env->op_stack, any2);
  op_stack_pop_any(f->_env->op_stack, any3);

  op_stack_push_any(f->_env->op_stack, any2);
  op_stack_push_any(f->_env->op_stack, any1);
  op_stack_push_any(f->_env->op_stack, any3);

  op_stack_push_any(f->_env->op_stack, any2);
  op_stack_push_any(f->_env->op_stack, any1);
}

OPCODE(dup2x2, 94)
{
  void *any1, *any2, *any3, *any4;

  op_stack_pop_any(f->_env->op_stack, any1);
  op_stack_pop_any(f->_env->op_stack, any2);
  op_stack_pop_any(f->_env->op_stack, any3);
  op_stack_pop_any(f->_env->op_stack, any4);

  op_stack_push_any(f->_env->op_stack, any2);
  op_stack_push_any(f->_env->op_stack, any1);
  op_stack_push_any(f->_env->op_stack, any4);
  op_stack_push_any(f->_env->op_stack, any3);

  op_stack_push_any(f->_env->op_stack, any2);
  op_stack_push_any(f->_env->op_stack, any1);
}

OPCODE(swap, 95)
{
  void *any1, *any2;

  op_stack_pop_any(f->_env->op_stack, any1);
  op_stack_pop_any(f->_env->op_stack, any2);

  op_stack_push_any(f->_env->op_stack, any1);
  op_stack_push_any(f->_env->op_stack, any2);
}

OPCODE(invokevirtual, 182)
{
  PRUint16 index;
  MethodStruct *method;
  char *method_name;
  japhar_object* objectref;
  HungryEnv *henv = f->_env;

  index = get_next_u2(f);

  method = ResolveVirtualMethod(f->_env, f->method->clazz,
                                &f->method->clazz->constants[index],
                                &method_name);

  if (!method)
    {
      HVM_ExceptionThrow(henv, java_lang_NoSuchMethodError, method_name);
      break;
    }
  
  objectref = *(japhar_object**)(henv->op_stack->stack_top - method->num_param_words - 1);

  if (objectref == 0)
    {
      char msg[110];
      PR_snprintf(msg, sizeof(msg), "in call to virtual method '%s'",
                  method_name);
      HVM_ExceptionThrow(henv, java_lang_NullPointerException, msg);
      break;
    }

  method = GetMethodForVirtualMethod(henv, objectref->clazz, method);
  
  do_method_call (f, method);
}

OPCODE(invokenonvirtual, 183)
{
  PRUint16 index;
  MethodStruct *method;

  index = get_next_u2(f);

  method = ResolveNonVirtualMethodRef(f->_env, f->method->clazz,
                                      &f->method->clazz->constants[ index ]);

  if (NULL == method)
    {
      char buf[500];

      PR_snprintf(buf, 500, "unknown method '%s' in invokenonvirtual", method->name);
      /* XXX Is this the correct exception? [pere 1998-05-09] */
      HVM_ExceptionThrow(f->_env, java_lang_RuntimeException, buf);
      break;
    }

  do_method_call (f, method);
}

OPCODE(invokestatic, 184)
{
  PRUint16 index;
  ClazzFile *cf;
  MethodStruct *method;

  index = get_next_u2(f);

  method = ResolveStaticMethodRef(f->_env, f->method->clazz,
                                  &f->method->clazz->constants[ index ]);

  if (NULL == method)
    {
      char buf[500];

      PR_snprintf(buf, 500, "unknown method '%s' in invokestatic", method->name);
      /* XXX Is this the correct exception? [pere 1998-05-09] */
      HVM_ExceptionThrow(f->_env, java_lang_RuntimeException, buf);
      break;
    }

  cf = method->clazz;
  if (cf == NULL)
    {
      HVM_ExceptionThrow(f->_env, java_lang_RuntimeException,
                      "null class for method");
      break;
    }

  do_method_call(f, method);
}

OPCODE(invokeinterface, 185)
{
  PRUint16 index;
  PRUint32 nargs;
  PRUint32 reserved;
  japhar_object* objectref;
  char *interface_methodname;
  MethodStruct *method;
  MethodStruct *interface_method;

  index = get_next_u2(f);

  nargs = get_next_u1(f);
  reserved = get_next_u1(f);

  interface_method = ResolveInterfaceMethod(f->_env, f->method->clazz,
                                            &f->method->clazz->constants[ index ],
                                            &interface_methodname);

  if (!interface_method)
    {
      HVM_ExceptionThrow(f->_env, java_lang_NoSuchMethodError, interface_methodname);
      break;
    }

  objectref = *(japhar_object**)(f->_env->op_stack->stack_top - nargs);

  method = GetMethodForInterfaceMethod(f->_env,
                                       objectref->clazz,
                                       interface_method);

  if (!method)
    {
      HVM_ExceptionThrow(f->_env, java_lang_NoSuchMethodError, interface_methodname);
      break;
    }

  do_method_call(f, method);
}

OPCODE(ireturn, 172)
{
  op_stack_pop_int(f->_env->op_stack,
                   PRInt32, 
                   f->_env->return_value.value.i);

  {
    StackFrame *parent = f->parent;
    
    if (parent < f->_env->stack_highwater)
      {
        if ( (parent->flags & FRAME_NATIVE) )
          f->_env->return_value.tag = JSIG_INT;
        else
          {
            /* restore the old stack top -- this must use f->opstack_top!! */
            f->_env->op_stack->stack_top = f->opstack_top;

            op_stack_push_int(f->_env->op_stack,
                              f->_env->return_value.value.i);
          }
      }
  }
  
  pop_frame(f->_env);
}

OPCODE(lreturn, 173)
{
  StackFrame *parent = f->parent;
  
  if (parent < f->_env->stack_highwater)
    {
      if ( (parent->flags & FRAME_NATIVE) )
        {
          f->_env->return_value.tag = JSIG_LONG;
          op_stack_pop_long(f->_env->op_stack,
                            f->_env->return_value.value.j);
        }
      else
        {
          PRInt32 word1, word2;
          
          op_stack_pop_doubleword (f->_env->op_stack,
                                   word1, word2);
          
          /* restore the old stack top -- this must use f->opstack_top!! */
          f->_env->op_stack->stack_top = f->opstack_top;
          
          op_stack_push_doubleword(f->_env->op_stack,
                                   word1, word2);
        }
    }

  pop_frame (f->_env);
}

OPCODE(freturn, 174)
{
  op_stack_pop_float(f->_env->op_stack,
                     f->_env->return_value.value.f);
  {
    StackFrame *parent = f->parent;
    
    if (parent < f->_env->stack_highwater)
      {
        if ( (parent->flags & FRAME_NATIVE) )
          f->_env->return_value.tag = JSIG_FLOAT;
        else
          {
            /* restore the old stack top -- this must use f->opstack_top!! */
            f->_env->op_stack->stack_top = f->opstack_top;

            op_stack_push_float(f->_env->op_stack,
                                f->_env->return_value.value.f);
          }
      }
  }

  pop_frame (f->_env);
}

OPCODE(dreturn, 175)
{
  StackFrame *parent = f->parent;
  
  if (parent < f->_env->stack_highwater)
    {
      if ( (parent->flags & FRAME_NATIVE) )
        {
          f->_env->return_value.tag = JSIG_DOUBLE;
          op_stack_pop_double(f->_env->op_stack,
                              f->_env->return_value.value.d);
        }
      else
        {
          PRInt32 word1, word2;
          
          op_stack_pop_doubleword(f->_env->op_stack,
                                  word1, word2);
          
          /* restore the old stack top -- this must use f->opstack_top!! */
          f->_env->op_stack->stack_top = f->opstack_top;
          
          op_stack_push_doubleword(f->_env->op_stack,
                                   word1, word2);
        }
    }

  pop_frame (f->_env);
}

OPCODE(areturn, 176)
{
  StackFrame *parent = f->parent;

  op_stack_pop_object(f->_env->op_stack,
                      f->_env->return_value.value.l);

  /* XXX object must refer to an object of a type that is assignment
     compatible with the method. */

  if (parent < f->_env->stack_highwater)
    {
      if ( (parent->flags & FRAME_NATIVE) )
        f->_env->return_value.tag = JSIG_OBJECT;
      else
        {
          /* restore the old stack top -- this must use f->opstack_top!! */
          f->_env->op_stack->stack_top = f->opstack_top;
          
          op_stack_push_object(f->_env->op_stack,
                               f->_env->return_value.value.l);
        }
  }

  pop_frame (f->_env);
}

OPCODE(return, 177)
{
  StackFrame *parent = f->parent;

  if ( (parent->flags & FRAME_NATIVE) )
    f->_env->return_value.tag = JSIG_VOID;
  else
    /* restore the old stack top -- this must use f->opstack_top!! */
    f->_env->op_stack->stack_top = f->opstack_top;

  pop_frame (f->_env);
}

OPCODE(ifeq, 153)
{
  PRInt16 branch_add;
  PRInt32 value;
  int current_pc = f->pc - 1;

  branch_add = (PRInt16) get_next_u2(f);
  op_stack_pop_int(f->_env->op_stack,
                   PRInt32, 
                   value);

  if (value == 0)
    f->pc = current_pc + branch_add;
}

OPCODE(ifnull, 198)
{
  PRInt16 branch_add;
  japhar_object* value;
  int current_pc = f->pc - 1;

  branch_add = (PRInt16) get_next_u2(f);
  op_stack_pop_object(f->_env->op_stack, value);

  if (value == NULL)
    f->pc = current_pc + branch_add;
}

OPCODE(iflt, 155)
{
  PRInt16 branch_add;
  PRInt32 value;
  int current_pc = f->pc - 1;

  branch_add = (PRInt16) get_next_u2(f);
  op_stack_pop_int(f->_env->op_stack,
                   PRInt32, 
                   value);

  if (value < 0)
    f->pc = current_pc + branch_add;
}

OPCODE(ifle, 158)
{
  PRInt16 branch_add;
  PRInt32 value;
  int current_pc = f->pc - 1;

  branch_add = (PRInt16) get_next_u2(f);
  op_stack_pop_int(f->_env->op_stack,
                   PRInt32, 
                   value);

  if (value <= 0)
    f->pc = current_pc + branch_add;
}

OPCODE(ifne, 154)
{
  PRInt16 branch_add;
  PRInt32 value;
  int current_pc = f->pc - 1;

  branch_add = (PRInt16) get_next_u2(f);
  op_stack_pop_int(f->_env->op_stack,
                   PRInt32, 
                   value);

  if (value != 0)
    f->pc = current_pc + branch_add;
}

OPCODE(ifnonnull, 199)
{
  PRInt16 branch_add;
  japhar_object* value;
  int current_pc = f->pc - 1;

  branch_add = (PRInt16) get_next_u2(f);
  op_stack_pop_object(f->_env->op_stack, value);

  if (value != NULL)
    f->pc = current_pc + branch_add;
}

OPCODE(ifgt, 157)
{
  PRInt16 branch_add;
  PRInt32 value;
  int current_pc = f->pc - 1;

  branch_add = (PRInt16) get_next_u2(f);
  op_stack_pop_int(f->_env->op_stack,
                   PRInt32, 
                   value);

  if (value > 0)
    f->pc = current_pc + branch_add;
}

OPCODE(ifge, 156)
{
  PRInt16 branch_add;
  PRInt32 value;
  int current_pc = f->pc - 1;

  branch_add = (PRInt16) get_next_u2(f);
  op_stack_pop_int(f->_env->op_stack,
                   PRInt32, 
                   value);

  if (value >= 0)
    f->pc = current_pc + branch_add;
}

OPCODE(if_icmpeq, 159)
{
  PRInt16 branch_add;
  PRInt32 value1, value2;
  int current_pc = f->pc - 1;

  branch_add = (PRInt16) get_next_u2(f);
  op_stack_pop_int(f->_env->op_stack, PRInt32, value2);
  op_stack_pop_int(f->_env->op_stack, PRInt32, value1);

  if (value1 == value2)
    f->pc = current_pc + branch_add;
}

OPCODE(if_icmpne, 160)
{
  PRInt16 branch_add;
  PRInt32 value1, value2;
  int current_pc = f->pc - 1;

  branch_add = (PRInt16) get_next_u2(f);
  op_stack_pop_int(f->_env->op_stack, PRInt32, value2);
  op_stack_pop_int(f->_env->op_stack, PRInt32, value1);

  if (value1 != value2)
    f->pc = current_pc + branch_add;
}

OPCODE(if_icmplt, 161)
{
  PRInt16 branch_add;
  PRInt32 value1, value2;
  int current_pc = f->pc - 1;

  branch_add = (PRInt16) get_next_u2(f);
  op_stack_pop_int(f->_env->op_stack, PRInt32, value2);
  op_stack_pop_int(f->_env->op_stack, PRInt32, value1);

  if (value1 < value2)
    f->pc = current_pc + branch_add;
}

OPCODE(if_icmpgt, 163)
{
  PRInt16 branch_add;
  PRInt32 value1, value2;
  int current_pc = f->pc - 1;

  branch_add = (PRInt16) get_next_u2(f);
  op_stack_pop_int(f->_env->op_stack, PRInt32, value2);
  op_stack_pop_int(f->_env->op_stack, PRInt32, value1);

  if (value1 > value2)
    f->pc = current_pc + branch_add;
}

OPCODE(if_icmple, 164)
{
  PRInt16 branch_add;
  PRInt32 value1, value2;
  int current_pc = f->pc - 1;

  branch_add = (PRInt16) get_next_u2(f);
  op_stack_pop_int(f->_env->op_stack, PRInt32, value2);
  op_stack_pop_int(f->_env->op_stack, PRInt32, value1);

  if (value1 <= value2)
    f->pc = current_pc + branch_add;
}

OPCODE(if_icmpge, 162)
{
  PRInt16 branch_add;
  PRInt32 value1, value2;
  int current_pc = f->pc - 1;

  branch_add = (PRInt16) get_next_u2(f);
  op_stack_pop_int(f->_env->op_stack, PRInt32, value2);
  op_stack_pop_int(f->_env->op_stack, PRInt32, value1);

  if (value1 >= value2)
    f->pc = current_pc + branch_add;
}

OPCODE(lcmp, 148)
{
  PRInt64 value1, value2;

  op_stack_pop_long(f->_env->op_stack, value2);
  op_stack_pop_long(f->_env->op_stack, value1);

  if (LL_CMP(value1, >, value2))
    op_stack_push_int(f->_env->op_stack, 1);
  else if (LL_EQ(value1, value2))
    op_stack_push_int(f->_env->op_stack, 0);
  else
    op_stack_push_int(f->_env->op_stack, -1);
}

OPCODE(fcmpl, 149)
{
  float value1, value2;

  op_stack_pop_float(f->_env->op_stack, value2);
  op_stack_pop_float(f->_env->op_stack, value1);

  if (isnan(value1) || isnan(value2))
    op_stack_push_int(f->_env->op_stack, -1);
  else if (value1 > value2)
    op_stack_push_int(f->_env->op_stack, 1);
  else if (value1 == value2)
    op_stack_push_int(f->_env->op_stack, 0);
  else /* (value1 < value2) */
    op_stack_push_int(f->_env->op_stack, -1);
}

OPCODE(fcmpg, 150)
{
  float value1, value2;

  op_stack_pop_float(f->_env->op_stack, value2);
  op_stack_pop_float(f->_env->op_stack, value1);

  if (isnanf(value1) || isnanf(value2))
    op_stack_push_int(f->_env->op_stack, 1);
  else if (value1 > value2)
    op_stack_push_int(f->_env->op_stack, 1);
  else if (value1 == value2)
    op_stack_push_int(f->_env->op_stack, 0);
  else /* (value1 < value2) */
    op_stack_push_int(f->_env->op_stack, -1);
}

OPCODE(dcmpl, 151)
{
  PRFloat64 value1, value2;

  op_stack_pop_double(f->_env->op_stack, value2);
  op_stack_pop_double(f->_env->op_stack, value1);

  if (isnan(value1) || isnan(value2))
    op_stack_push_int(f->_env->op_stack, -1);
  else if (value1 > value2)
    op_stack_push_int(f->_env->op_stack, 1);
  else if (value1 == value2)
    op_stack_push_int(f->_env->op_stack, 0);
  else /* (value1 < value2) */
    op_stack_push_int(f->_env->op_stack, -1);
}

OPCODE(dcmpg, 152)
{
  PRFloat64 value1, value2;

  op_stack_pop_double(f->_env->op_stack, value2);
  op_stack_pop_double(f->_env->op_stack, value1);

  if (isnan(value1) || isnan(value2))
    op_stack_push_int(f->_env->op_stack, 1);
  else if (value1 > value2)
    op_stack_push_int(f->_env->op_stack, 1);
  else if (value1 == value2)
    op_stack_push_int(f->_env->op_stack, 0);
  else /* (value1 < value2) */
    op_stack_push_int(f->_env->op_stack, -1);
}

OPCODE(if_acmpeq, 165)
{
  PRInt16 branch_add;
  japhar_object *value1, *value2;
  int current_pc = f->pc - 1;

  branch_add = (PRInt16) get_next_u2(f);
  op_stack_pop_object(f->_env->op_stack, value2);
  op_stack_pop_object(f->_env->op_stack, value1);

  if (value1 == value2)
    f->pc = current_pc + branch_add;
}

OPCODE(if_acmpne, 166)
{
  PRInt16 branch_add;
  japhar_object *value1, *value2;
  int current_pc = f->pc - 1;

  branch_add = (PRInt16) get_next_u2(f);
  op_stack_pop_object(f->_env->op_stack, value2);
  op_stack_pop_object(f->_env->op_stack, value1);

  if (value1 != value2)
    f->pc = current_pc + branch_add;
}

OPCODE(goto, 167)
{
  PRInt16 branch_add;
  int current_pc = f->pc - 1;

  branch_add = (PRInt16) get_next_u2(f);

  f->pc = current_pc + branch_add;
}

OPCODE(goto_w, 200)
{
  PRInt32 branch_add;
  int current_pc = f->pc - 1;

  branch_add = get_next_u4(f);

  f->pc = current_pc + branch_add;
}

OPCODE(jsr, 168)
{
  PRInt16 branch_add;
  int current_pc = f->pc - 1;

  branch_add = (PRInt16) get_next_u2(f);

  op_stack_push_int(f->_env->op_stack, f->pc);

  f->pc = current_pc + branch_add;
}

OPCODE(jsr_w, 201)
{
  HVM_ExceptionThrow(f->_env, java_lang_RuntimeException,
                  "unfinished opcode:  jsr_w");
}

OPCODE(ret, 169)
{
  PRUint16 vindex = get_next_u1(f);

  f->pc = (int)f->vars[vindex].value.i;
}

OPCODE(iadd, 96)
{
  PRInt32 value1, value2;

  op_stack_pop_int(f->_env->op_stack, PRInt32, value2);
  op_stack_pop_int(f->_env->op_stack, PRInt32, value1);

  op_stack_push_int(f->_env->op_stack, value1 + value2);
}

OPCODE(ladd, 97)
{
  PRInt64 value1, value2, result;

  op_stack_pop_long(f->_env->op_stack, value1);
  op_stack_pop_long(f->_env->op_stack, value2);

  LL_ADD(result, value1, value2);

  op_stack_push_long(f->_env->op_stack, result);
}

OPCODE(fadd, 98)
{
  float value1, value2;

  op_stack_pop_float(f->_env->op_stack, value2);
  op_stack_pop_float(f->_env->op_stack, value1);

  op_stack_push_float(f->_env->op_stack, value1 + value2);
}

OPCODE(dadd, 99)
{
  PRFloat64 value1, value2;

  op_stack_pop_double(f->_env->op_stack, value2);
  op_stack_pop_double(f->_env->op_stack, value1);

  op_stack_push_double(f->_env->op_stack, value1 + value2);
}

OPCODE(isub, 100)
{
  PRInt32 value1, value2;

  op_stack_pop_int(f->_env->op_stack, PRInt32, value2);
  op_stack_pop_int(f->_env->op_stack, PRInt32, value1);

  op_stack_push_int(f->_env->op_stack, value1 - value2);
}

OPCODE(lsub, 101)
{
  PRInt64 value1, value2, result;
  
  op_stack_pop_long(f->_env->op_stack, value2);
  op_stack_pop_long(f->_env->op_stack, value1);

  LL_SUB(result, value1, value2);

  op_stack_push_long(f->_env->op_stack, result);
}

OPCODE(fsub, 102)
{
  float value1, value2;

  op_stack_pop_float(f->_env->op_stack, value2);
  op_stack_pop_float(f->_env->op_stack, value1);

  op_stack_push_float(f->_env->op_stack, value1 - value2);
}

OPCODE(dsub, 103)
{
  PRFloat64 value1, value2;

  op_stack_pop_double(f->_env->op_stack, value2);
  op_stack_pop_double(f->_env->op_stack, value1);

  op_stack_push_double(f->_env->op_stack, value1 - value2);
}

OPCODE(imul, 104)
{
  PRInt32 value1, value2;

  op_stack_pop_int(f->_env->op_stack, PRInt32, value2);
  op_stack_pop_int(f->_env->op_stack, PRInt32, value1);
 
  op_stack_push_int(f->_env->op_stack, value1 * value2);
}

OPCODE(lmul, 105)
{
  PRInt64 value1, value2, result;
  
  op_stack_pop_long(f->_env->op_stack, value2);
  op_stack_pop_long(f->_env->op_stack, value1);

  LL_MUL(result, value1, value2);

  op_stack_push_long(f->_env->op_stack, result);
}

OPCODE(fmul, 106)
{
  float value1, value2;

  op_stack_pop_float(f->_env->op_stack, value2);
  op_stack_pop_float(f->_env->op_stack, value1);

  op_stack_push_float(f->_env->op_stack, value1 * value2);
}

OPCODE(dmul, 107)
{ 
  PRFloat64 value1, value2;

  op_stack_pop_double(f->_env->op_stack, value2);
  op_stack_pop_double(f->_env->op_stack, value1);

  op_stack_push_double(f->_env->op_stack, value1 * value2);
}

OPCODE(idiv, 108)
{
  PRInt32 value1, value2;

  op_stack_pop_int(f->_env->op_stack, PRInt32, value2);
  op_stack_pop_int(f->_env->op_stack, PRInt32, value1);

  if (value2 == 0)
    {
      HVM_ExceptionThrow(f->_env, java_lang_ArithmeticException, "divisor is 0 in integer division.");
      break;
    }
  else
    op_stack_push_int(f->_env->op_stack, value1 / value2);
}

OPCODE(ldiv, 109)
{
  PRInt64 value1, value2, result;
  
  op_stack_pop_long(f->_env->op_stack, value2);
  op_stack_pop_long(f->_env->op_stack, value1);

  if (LL_EQ(value2, LL_ZERO))
    {
      HVM_ExceptionThrow(f->_env, java_lang_ArithmeticException, "divisor is 0 in long division.");
      break;
    }
  else
    {
      LL_DIV(result, value1, value2);
      op_stack_push_long(f->_env->op_stack, result);
    }
}

OPCODE(fdiv, 110)
{
  float value1, value2;

  op_stack_pop_float(f->_env->op_stack, value2);
  op_stack_pop_float(f->_env->op_stack, value1);

  op_stack_push_float(f->_env->op_stack, value1 / value2);
}

OPCODE(ddiv, 111)
{
  PRFloat64 value1, value2;
  
  op_stack_pop_double(f->_env->op_stack, value2);
  op_stack_pop_double(f->_env->op_stack, value1);

  op_stack_push_double(f->_env->op_stack, value1 / value2);
}

OPCODE(irem, 112)
{
  PRInt32 value1, value2;

  op_stack_pop_int(f->_env->op_stack, PRInt32, value2);
  op_stack_pop_int(f->_env->op_stack, PRInt32, value1);

  if (value2 == 0)
    {
      HVM_ExceptionThrow(f->_env, java_lang_ArithmeticException, "divisor is 0 in integer remainder.");
      break;
    }
  else
    op_stack_push_int(f->_env->op_stack, value1 % value2);
}

OPCODE(lrem, 113)
{
  PRInt64 value1, value2, result;

  op_stack_pop_long(f->_env->op_stack, value2);
  op_stack_pop_long(f->_env->op_stack, value1);

  if (LL_EQ(value2, LL_ZERO))
    {
      HVM_ExceptionThrow(f->_env, java_lang_ArithmeticException, "divisor is 0 in long remainder.");
      break;
    }
  else
    {
      LL_MOD(result, value1, value2);
      op_stack_push_long(f->_env->op_stack, result);
    }
}

OPCODE(frem, 114)
{
  float value1, value2;
  
  op_stack_pop_float(f->_env->op_stack, value2);
  op_stack_pop_float(f->_env->op_stack, value1);
  
  op_stack_push_float(f->_env->op_stack, fmodf(value1,value2));
}

OPCODE(drem, 115)
{
  PRFloat64 value1, value2;
  
  op_stack_pop_double(f->_env->op_stack, value2);
  op_stack_pop_double(f->_env->op_stack, value1);
  
  op_stack_push_double(f->_env->op_stack, fmod(value1,value2));
}

OPCODE(ineg, 116)
{
  PRInt32 value;
  
  op_stack_pop_int(f->_env->op_stack, PRInt32, value);
  
  op_stack_push_int(f->_env->op_stack, -1 * value);
}

OPCODE(lneg, 117)
{
  PRInt64 value, result;
  
  op_stack_pop_long(f->_env->op_stack, value);
  
  LL_NEG(result, value);

  op_stack_push_long(f->_env->op_stack, result);
}

OPCODE(fneg, 118)
{
  float value;
  
  op_stack_pop_float(f->_env->op_stack, value);
  op_stack_push_float(f->_env->op_stack, -1.0f * value);
}

OPCODE(dneg, 119)
{
  PRFloat64 value;
  
  op_stack_pop_double(f->_env->op_stack, value);
  op_stack_push_double(f->_env->op_stack, -1.0 * value);
}

OPCODE(iinc, 132)
{
  PRUint8 vindex = get_next_u1(f);
  PRInt8 c = get_next_u1(f);
  
  f->vars[vindex].value.i += c;
}

OPCODE(i2l, 133)
{
  PRInt64 lvalue;
  PRInt32 ivalue;
  
  op_stack_pop_int (f->_env->op_stack, PRInt32, ivalue);
  
  LL_I2L(lvalue, ivalue);
  
  op_stack_push_long (f->_env->op_stack, lvalue);
}

OPCODE(i2f, 134)
{
  PRInt32 ivalue;
  
  op_stack_pop_int (f->_env->op_stack, PRInt32, ivalue);
  op_stack_push_float (f->_env->op_stack, (float)ivalue);
}

OPCODE(i2d, 135)
{
  PRInt32 ivalue;
  
  op_stack_pop_int (f->_env->op_stack, PRInt32, ivalue);
  op_stack_push_double (f->_env->op_stack, (PRFloat64)ivalue);
}

OPCODE(f2i, 139)
{
  float fvalue;
  
  op_stack_pop_float(f->_env->op_stack, fvalue);
  op_stack_push_int(f->_env->op_stack, (PRInt32)fvalue);
}

OPCODE(f2d, 141)
{
  float fvalue;

  op_stack_pop_float(f->_env->op_stack, fvalue);
  op_stack_push_double(f->_env->op_stack, (PRFloat64)fvalue);
}

OPCODE(int2byte, 145)
{
  PRInt32 ivalue;
  PRInt8 bvalue;

  op_stack_pop_int(f->_env->op_stack, PRInt32, ivalue);
  bvalue = (PRInt8) (ivalue & 0xFF);
  op_stack_push_int(f->_env->op_stack, (PRInt32)bvalue);
}

OPCODE(int2char, 146)
{
  PRInt32 ivalue;

  op_stack_pop_int(f->_env->op_stack, PRInt32, ivalue);
  op_stack_push_int(f->_env->op_stack, (PRUint16)(ivalue & 0xFFFF));
}

OPCODE(l2i, 136)
{
  PRInt64 lvalue;
  PRInt32 ivalue;

  op_stack_pop_long(f->_env->op_stack, lvalue);

  LL_L2I(ivalue, lvalue);

  op_stack_push_int(f->_env->op_stack, ivalue);
}

OPCODE(l2f, 137)
{
  PRInt64 lvalue;
  float fvalue;

  op_stack_pop_long(f->_env->op_stack, lvalue);

  LL_L2F(fvalue, lvalue);

  op_stack_push_float(f->_env->op_stack, fvalue);
}

OPCODE(l2d, 138)
{
  double dvalue;
  PRInt64 lvalue;

  op_stack_pop_long(f->_env->op_stack, lvalue);

  LL_L2D(dvalue, lvalue);

  op_stack_push_double(f->_env->op_stack, dvalue);
}

OPCODE(f2l, 140)
{
  float fvalue;
  PRInt64 lvalue;

  op_stack_pop_float(f->_env->op_stack, fvalue);

  LL_F2L(lvalue, fvalue);

  op_stack_push_long(f->_env->op_stack, lvalue);
}

OPCODE(d2i, 142)
{
  PRFloat64 dvalue;

  op_stack_pop_double(f->_env->op_stack, dvalue);
  op_stack_push_int(f->_env->op_stack, (PRInt32)dvalue);
}

OPCODE(d2l, 143)
{
  PRFloat64 dvalue;
  PRInt64 lvalue;

  op_stack_pop_double(f->_env->op_stack, dvalue);

  LL_D2L(lvalue, dvalue);

  op_stack_push_long(f->_env->op_stack, lvalue);
}

OPCODE(d2f, 144)
{
  PRFloat64 dvalue;

  op_stack_pop_double(f->_env->op_stack, dvalue);
  op_stack_push_float(f->_env->op_stack, (float)dvalue);
}

OPCODE(int2short, 147)
{
  PRInt32 ivalue;

  op_stack_pop_int(f->_env->op_stack, PRInt32, ivalue);
  op_stack_push_int(f->_env->op_stack, (PRInt16)ivalue);
}

OPCODE(newarray, 188)
{
  PRUint8 atype;
  PRInt32 size;
  japhar_object* array_reference;
  char *array_classname = NULL;

  atype = get_next_u1 (f);
  op_stack_pop_int (f->_env->op_stack, PRInt32, size);

  switch (atype)
    {
    case 4: /*T_BOOLEAN*/
      array_classname = "[Z";
      break;
    case 5: /*T_CHAR*/
      array_classname = "[C";
      break;
    case 6: /*T_FLOAT*/
      array_classname = "[F";
      break;
    case 7: /*T_DOUBLE*/
      array_classname = "[D";
      break;
    case 8: /*T_BYTE*/
      array_classname = "[B";
      break;
    case 9: /*T_SHORT*/
      array_classname = "[S";
      break;
    case 10: /*T_INT*/
      array_classname = "[I";
      break;
    case 11: /*T_LONG*/
      array_classname = "[J";
      break;
    default:
      PR_ASSERT(0);
    }
  
  array_reference = HVM_ArrayNew (f->_env, size,
                                  HVM_ClassFind(f->_env, array_classname));

  if (array_reference == NULL)
    {
      HVM_ExceptionThrow(f->_env, java_lang_OutOfMemoryError, "array allocation failed.");
      break;
    }

  op_stack_push_object (f->_env->op_stack, array_reference);
}

OPCODE(anewarray, 189)
{
  PRUint16 ob_type;
  PRInt32 length;
  japhar_object* array_reference;
  char *element_classname;

  ob_type = get_next_u2(f);

  element_classname = ResolveClassName(f->_env, f->method->clazz,
                                       &f->method->clazz->constants[ ob_type ]);

  op_stack_pop_int (f->_env->op_stack, PRInt32, length);

  array_reference = HVM_ArrayCreate(f->_env, length, element_classname);

  if (array_reference == NULL)
    {
      HVM_ExceptionThrow(f->_env, java_lang_OutOfMemoryError, "array allocation failed.");
      break;
    }

  op_stack_push_object (f->_env->op_stack, array_reference);
}

OPCODE(multianewarray, 197)
{
  PRInt32 *sizes = NULL;
  int i;
  PRUint16 index;
  PRUint8 dimensions;
  japhar_object* array_reference;
  char *array_classname;

  index = get_next_u2(f);

  dimensions = get_next_u1 (f);

  /* get space to store the list of array dimensions */

  sizes = (PRInt32 *) PR_MALLOC (sizeof (PRInt32) * dimensions);
  
  if (sizes == NULL)
    {
      HVM_ExceptionThrow(f->_env, java_lang_OutOfMemoryError,
                      "array allocation failed.");
      break;
    }

  /* pop the array sizes from the operand stack */

  for (i=0; i<dimensions; i++)
    op_stack_pop_int (f->_env->op_stack, PRInt32,
                      *(sizes + dimensions - i - 1));

  array_classname = ResolveClassName(f->_env, f->method->clazz,
                                     &f->method->clazz->constants[ index ]);

  array_reference = HVM_ArrayNewMulti (f->_env,
                                      sizes,
                                      dimensions,
                                      HVM_ClassFind(f->_env, array_classname));

  if (array_reference == NULL)
    {
      HVM_ExceptionThrow(f->_env, java_lang_OutOfMemoryError, "array allocation failed.");
      break;
    }

  op_stack_push_object (f->_env->op_stack, array_reference);
}

OPCODE(arraylength, 190)
{
  japhar_object* arrayref;
  PRInt32 val;

  op_stack_pop_object(f->_env->op_stack, arrayref);

  if (arrayref == 0)
    {
      HVM_ExceptionThrow(f->_env, java_lang_NullPointerException, NULL);
      break;
    }

  val = HVM_ArrayGetLength(arrayref);

  op_stack_push_int (f->_env->op_stack, val);
}

OPCODE(iaload, 46)
{
  japhar_object* arrayref;
  PRInt32 index;
  PRInt32 length_val;
  void* body_val;

  op_stack_pop_int (f->_env->op_stack, PRInt32, index);
  op_stack_pop_object (f->_env->op_stack, arrayref);

  if (arrayref == 0)
    {
      HVM_ExceptionThrow(f->_env, java_lang_NullPointerException, "op_iaload");
      break;
    }

  length_val = HVM_ArrayGetLength(arrayref);

  if (index >= length_val || index < 0)
    {
      char index_str[100];
      PR_snprintf(index_str, sizeof(index_str), "%d", index);
      HVM_ExceptionThrow(f->_env, java_lang_ArrayIndexOutOfBoundsException,
                         index_str);
      break;
    }

  body_val = HVM_ArrayGetBody(arrayref);

  op_stack_push_int(f->_env->op_stack,
                    ((PRInt32*)body_val)[ index ]);
}

OPCODE(laload, 47)
{
  japhar_object* arrayref;
  PRInt32 index;
  PRInt32 length_val;
  void* body_val;

  op_stack_pop_int (f->_env->op_stack, PRInt32, index);
  op_stack_pop_object (f->_env->op_stack, arrayref);

  if (arrayref == 0)
    {
      HVM_ExceptionThrow(f->_env, java_lang_NullPointerException, NULL);
      break;
    }

  length_val = HVM_ArrayGetLength(arrayref);

  if (index >= length_val || index < 0)
    {
      char index_str[100];
      PR_snprintf(index_str, sizeof(index_str), "%d", index);
      HVM_ExceptionThrow(f->_env, java_lang_ArrayIndexOutOfBoundsException,
                         index_str);
      break;
    }

  body_val = HVM_ArrayGetBody(arrayref);

  op_stack_push_long(f->_env->op_stack,
                     ((PRInt64*)body_val)[ index ]);
}

OPCODE(faload, 48)
{
  japhar_object* arrayref;
  PRInt32 index;
  PRInt32 length_val;
  void* body_val;

  op_stack_pop_int (f->_env->op_stack, PRInt32, index);
  op_stack_pop_object (f->_env->op_stack, arrayref);

  if (arrayref == 0)
    {
      HVM_ExceptionThrow(f->_env, java_lang_NullPointerException, NULL);
      break;
    }

  length_val = HVM_ArrayGetLength(arrayref);

  if (index >= length_val || index < 0)
    {
      char index_str[100];
      PR_snprintf(index_str, sizeof(index_str), "%d", index);
      HVM_ExceptionThrow(f->_env, java_lang_ArrayIndexOutOfBoundsException,
                         index_str);
      break;
    }

  body_val = HVM_ArrayGetBody(arrayref);

  op_stack_push_float(f->_env->op_stack,
                      ((float*)body_val)[ index ]);
}

OPCODE(daload, 49)
{
  japhar_object* arrayref;
  PRInt32 index;
  PRInt32 length_val;
  void* body_val;

  op_stack_pop_int (f->_env->op_stack, PRInt32, index);
  op_stack_pop_object (f->_env->op_stack, arrayref);

  if (arrayref == 0)
    {
      HVM_ExceptionThrow(f->_env, java_lang_NullPointerException, NULL);
      break;
    }

  length_val = HVM_ArrayGetLength(arrayref);

  if (index >= length_val || index < 0)
    {
      char index_str[100];
      PR_snprintf(index_str, sizeof(index_str), "%d", index);
      HVM_ExceptionThrow(f->_env, java_lang_ArrayIndexOutOfBoundsException,
                         index_str);
      break;
    }

  body_val = HVM_ArrayGetBody(arrayref);

  op_stack_push_double(f->_env->op_stack,
                       ((PRFloat64*)body_val)[ index ]);
}

OPCODE(aaload, 50)
{
  japhar_object* arrayref;
  PRInt32 index;
  PRInt32 length_val;
  void* body_val;

  op_stack_pop_int (f->_env->op_stack, PRInt32, index);
  op_stack_pop_object (f->_env->op_stack, arrayref);

  if (arrayref == 0)
    {
      HVM_ExceptionThrow(f->_env, java_lang_NullPointerException, NULL);
      break;
    }

  length_val = HVM_ArrayGetLength(arrayref);

  if (index >= length_val || index < 0)
    {
      char index_str[100];
      PR_snprintf(index_str, sizeof(index_str), "%d", index);
      HVM_ExceptionThrow(f->_env, java_lang_ArrayIndexOutOfBoundsException,
                         index_str);
      break;
    }

  body_val = HVM_ArrayGetBody(arrayref);

  op_stack_push_object(f->_env->op_stack,
                       ((japhar_object**)body_val)[ index ]);
}

OPCODE(baload, 51)
{
  japhar_object* arrayref;
  PRInt32 index;
  PRInt32 length_val;
  void* body_val;

  op_stack_pop_int (f->_env->op_stack, PRInt32, index);
  op_stack_pop_object (f->_env->op_stack, arrayref);

  if (arrayref == 0)
    {
      HVM_ExceptionThrow(f->_env, java_lang_NullPointerException, NULL);
      break;
    }

  length_val = HVM_ArrayGetLength(arrayref);

  if (index >= length_val || index < 0)
    {
      char index_str[100];
      PR_snprintf(index_str, sizeof(index_str), "%d", index);
      HVM_ExceptionThrow(f->_env, java_lang_ArrayIndexOutOfBoundsException, 
                         index_str);
      break;
    }

  body_val = HVM_ArrayGetBody(arrayref);

  op_stack_push_int(f->_env->op_stack,
                    ((PRInt8*)body_val)[ index ]);
}

OPCODE(caload, 52)
{
  japhar_object* arrayref;
  PRInt32 index;
  PRInt32 length_val;
  void* body_val;

  op_stack_pop_int (f->_env->op_stack, PRInt32, index);
  op_stack_pop_object (f->_env->op_stack, arrayref);

  if (arrayref == 0)
    {
      HVM_ExceptionThrow(f->_env, java_lang_NullPointerException, NULL);
      break;
    }

  length_val = HVM_ArrayGetLength(arrayref);

  if (index >= length_val || index < 0)
    {
      char index_str[100];
      PR_snprintf(index_str, sizeof(index_str), "%d", index);
      HVM_ExceptionThrow(f->_env, java_lang_ArrayIndexOutOfBoundsException,
                         index_str);
      break;
    }

  body_val = HVM_ArrayGetBody(arrayref);

  op_stack_push_int(f->_env->op_stack,
                    ((PRUint16*)body_val)[ index ]);
}

OPCODE(saload, 53)
{
  japhar_object* arrayref;
  PRInt32 index;
  PRInt32 length_val;
  void* body_val;

  op_stack_pop_int (f->_env->op_stack, PRInt32, index);
  op_stack_pop_object (f->_env->op_stack, arrayref);

  if (arrayref == 0)
    {
      HVM_ExceptionThrow(f->_env, java_lang_NullPointerException, NULL);
      break;
    }

  length_val = HVM_ArrayGetLength(arrayref);

  if (index >= length_val || index < 0)
    {
      char index_str[100];
      PR_snprintf(index_str, sizeof(index_str), "%d", index);
      HVM_ExceptionThrow(f->_env, java_lang_ArrayIndexOutOfBoundsException,
                         index_str);
      break;
    }

  body_val = HVM_ArrayGetBody(arrayref);
  
  op_stack_push_int(f->_env->op_stack,
                    ((PRInt16*)body_val)[ index ]);
}

OPCODE(iastore, 79)
{
  japhar_object* arrayref;
  PRInt32 index;
  PRInt32 value;
  PRInt32 length_val;
  void* body_val;

  op_stack_pop_int (f->_env->op_stack, PRInt32, value);
  op_stack_pop_int (f->_env->op_stack, PRInt32, index);
  op_stack_pop_object (f->_env->op_stack, arrayref);

  if (arrayref == 0)
    {
      HVM_ExceptionThrow(f->_env, java_lang_NullPointerException, NULL);
      break;
    }

  length_val = HVM_ArrayGetLength(arrayref);

  if (index >= length_val || index < 0)
    {
      char index_str[100];
      PR_snprintf(index_str, sizeof(index_str), "%d", index);
      HVM_ExceptionThrow(f->_env, java_lang_ArrayIndexOutOfBoundsException,
                         index_str);
      break;
    }

  body_val = HVM_ArrayGetBody(arrayref);

  ((PRInt32*)body_val)[ index ] = value;
}

OPCODE(lastore, 80)
{
  japhar_object* arrayref;
  PRInt32 index;
  PRInt64 value;
  PRInt32 length_val;
  void* body_val;

  op_stack_pop_long (f->_env->op_stack, value);
  op_stack_pop_int (f->_env->op_stack, PRInt32, index);
  op_stack_pop_object (f->_env->op_stack, arrayref);

  if (arrayref == 0)
    {
      HVM_ExceptionThrow(f->_env, java_lang_NullPointerException, NULL);
      break;
    }

  length_val = HVM_ArrayGetLength(arrayref);

  if (index >= length_val || index < 0)
    {
      char index_str[100];
      PR_snprintf(index_str, sizeof(index_str), "%d", index);
      HVM_ExceptionThrow(f->_env, java_lang_ArrayIndexOutOfBoundsException, 
                         index_str);
      break;
    }

  body_val = HVM_ArrayGetBody(arrayref);

  ((PRInt64*)body_val)[ index ] = value;
}

OPCODE(fastore, 81)
{
  japhar_object* arrayref;
  PRInt32 index;
  float value;
  PRInt32 length_val;
  void* body_val;

  op_stack_pop_float (f->_env->op_stack, value);
  op_stack_pop_int (f->_env->op_stack, PRInt32, index);
  op_stack_pop_object (f->_env->op_stack, arrayref);

  if (arrayref == 0)
    {
      HVM_ExceptionThrow(f->_env, java_lang_NullPointerException, NULL);
      break;
    }

  length_val = HVM_ArrayGetLength(arrayref);

  if (index >= length_val || index < 0)
    {
      char index_str[100];
      PR_snprintf(index_str, sizeof(index_str), "%d", index);
      HVM_ExceptionThrow(f->_env, java_lang_ArrayIndexOutOfBoundsException,
                         index_str);
      break;
    }

  body_val = HVM_ArrayGetBody(arrayref);

  ((float*)body_val)[ index ] = value;
}

OPCODE(dastore, 82)
{
  japhar_object* arrayref;
  PRInt32 index;
  PRFloat64 value;
  PRInt32 length_val;
  void* body_val;

  op_stack_pop_double (f->_env->op_stack, value);
  op_stack_pop_int (f->_env->op_stack, PRInt32, index);
  op_stack_pop_object (f->_env->op_stack, arrayref);

  if (arrayref == 0)
    {
      HVM_ExceptionThrow(f->_env, java_lang_NullPointerException, NULL);
      break;
    }

  length_val = HVM_ArrayGetLength(arrayref);

  if (index >= length_val || index < 0)
    {
      char index_str[100];
      PR_snprintf(index_str, sizeof(index_str), "%d", index);
      HVM_ExceptionThrow(f->_env, java_lang_ArrayIndexOutOfBoundsException,
                         index_str);
      break;
    }

  body_val = HVM_ArrayGetBody(arrayref);

  ((PRFloat64*)body_val)[ index ] = value;
}

OPCODE(aastore, 83)
{
  japhar_object* arrayref;
  PRInt32 index;
  japhar_object* value;
  PRInt32 length_val;
  void* body_val;

  op_stack_pop_object (f->_env->op_stack, value);
  op_stack_pop_int (f->_env->op_stack, PRInt32, index);
  op_stack_pop_object (f->_env->op_stack, arrayref);

  if (arrayref == 0)
    {
      HVM_ExceptionThrow(f->_env, java_lang_NullPointerException, NULL);
      break;
    }

  length_val = HVM_ArrayGetLength(arrayref);

  if (index >= length_val || index < 0)
    {
      char index_str[100];
      PR_snprintf(index_str, sizeof(index_str), "%d", index);
      HVM_ExceptionThrow(f->_env, java_lang_ArrayIndexOutOfBoundsException, 
                         index_str);
      break;
    }

  /* XXX need to check the type of the object being stored and throw
     ArrayStoreException if there's a mismatch. */

  body_val = HVM_ArrayGetBody(arrayref);

  ((japhar_object**)body_val)[ index ] = value;
}

OPCODE(bastore, 84)
{
  japhar_object* arrayref;
  PRInt32 index;
  PRInt32 value;
  PRInt32 length_val;
  void* body_val;

  op_stack_pop_int (f->_env->op_stack, PRInt32, value);
  op_stack_pop_int (f->_env->op_stack, PRInt32, index);
  op_stack_pop_object (f->_env->op_stack, arrayref);

  if (arrayref == 0)
    {
      HVM_ExceptionThrow(f->_env, java_lang_NullPointerException, NULL);
      break;
    }

  length_val = HVM_ArrayGetLength(arrayref);

  if (index >= length_val || index < 0)
    {
      char index_str[100];
      PR_snprintf(index_str, sizeof(index_str), "%d", index);
      HVM_ExceptionThrow(f->_env, java_lang_ArrayIndexOutOfBoundsException,
                         index_str);
      break;
    }

  body_val = HVM_ArrayGetBody(arrayref);

  ((PRInt8*)body_val)[ index ] = (PRInt8)value;
}

OPCODE(castore, 85)
{
  japhar_object* arrayref;
  PRInt32 index;
  PRInt32 value;
  PRInt32 length_val;
  void* body_val;

  op_stack_pop_int (f->_env->op_stack, PRInt32, value);
  op_stack_pop_int (f->_env->op_stack, PRInt32, index);
  op_stack_pop_object (f->_env->op_stack, arrayref);

  if (arrayref == 0)
    {
      HVM_ExceptionThrow(f->_env, java_lang_NullPointerException, NULL);
      break;
    }

  length_val = HVM_ArrayGetLength(arrayref);

  if (index >= length_val || index < 0)
    {
      char index_str[100];
      PR_snprintf(index_str, sizeof(index_str), "%d", index);
      HVM_ExceptionThrow(f->_env, java_lang_ArrayIndexOutOfBoundsException,
                         index_str);
      break;
    }

  body_val = HVM_ArrayGetBody(arrayref);

  ((PRUint16*)body_val)[ index ] = (PRUint16)value;
}

OPCODE(sastore, 86)
{
  japhar_object* arrayref;
  PRInt32 index;
  PRInt32 value;
  PRInt32 length_val;
  void* body_val;

  op_stack_pop_int (f->_env->op_stack, PRInt32, value);
  op_stack_pop_int (f->_env->op_stack, PRInt32, index);
  op_stack_pop_object (f->_env->op_stack, arrayref);

  if (arrayref == 0)
    {
      HVM_ExceptionThrow(f->_env, java_lang_NullPointerException, NULL);
      break;
    }

  length_val = HVM_ArrayGetLength(arrayref);

  if (index >= length_val || index < 0)
    {
      char index_str[100];
      PR_snprintf(index_str, sizeof(index_str), "%d", index);
      HVM_ExceptionThrow(f->_env, java_lang_ArrayIndexOutOfBoundsException,
                         index_str);
      break;
    }

  body_val = HVM_ArrayGetBody(arrayref);

  ((PRInt16*)body_val)[ index ] = (PRInt16)value;
}

OPCODE(monitorenter, 194)
{
  japhar_object* objectref;

  op_stack_pop_object(f->_env->op_stack, objectref);

  HVM_MonitorEnter(f->_env, objectref);
}

OPCODE(monitorexit, 195)
{
  japhar_object* objectref;

  op_stack_pop_object(f->_env->op_stack, objectref);

  if ( !HVM_MonitorExit(f->_env, objectref) )
    {
      HVM_ExceptionThrow(f->_env, java_lang_IllegalMonitorStateException,
                         "not monitor owner!");
      break;
    }
}

OPCODE(lookupswitch, 171)
{
  PRInt32 address_of_this_instruction = f->pc - 1 ;
  PRInt32 numpairs;
  int padding;
  PRInt32 the_offset;
  PRBool found;
  PRInt32 i;
  PRInt32 key;

  /* eat up the padding -- the interesting stuff is aligned
     to a 4 byte boundary. */
  padding = 3 - (address_of_this_instruction % 4);

  for (i = 0; i < padding; i ++)
    {
      get_next_u1(f);
    }

  op_stack_pop_int(f->_env->op_stack, PRInt32, key);

  /* get the default offset. */
  the_offset = get_next_u4(f);

  /* get the number of pairs. */
  numpairs = get_next_u4(f);

  found = PR_FALSE;
  for (i = 0; i < numpairs; i ++)
    {
      PRInt32 match = get_next_u4(f);
      PRInt32 offset = get_next_u4(f);

      if (!found && key == match)
        {
          the_offset = offset;
          found = PR_TRUE;
        }
    }

  f->pc = address_of_this_instruction + the_offset;
}

OPCODE(tableswitch, 170)
{
  PRInt32 index;
  PRInt32 high, low, default_off;
  PRInt32 offset_to_use, i;
  PRInt32 address_of_this_instruction = f->pc - 1 ;
  PRInt32 padding;

  /* eat up the padding -- the interesting stuff is aligned
     to a 4 byte boundary. */
  padding = 3 - (address_of_this_instruction % 4);

  for (i = 0; i < padding; i ++)
    {
      get_next_u1(f);
    }

  op_stack_pop_int(f->_env->op_stack, PRInt32, index);

  /* get the default offset. */
  default_off = get_next_u4(f);

  /* get the low index. */
  low = get_next_u4(f);

  /* get the high index. */
  high = get_next_u4(f);

  offset_to_use = default_off;

  for (i = 0 ; i < high - low + 1; i ++)
    {
      PRInt32 jump_offset = get_next_u4(f);

      /* just consume all the jump_offsets */
      if (index > high || index < low) continue;
      
      if (index - low == i)
        offset_to_use = jump_offset;
    }

  f->pc = address_of_this_instruction + offset_to_use;
}

OPCODE(athrow, 191)
{
  japhar_object* throwable_ref;
  HungryEnv *henv = f->_env;

  op_stack_pop_object(henv->op_stack, throwable_ref);

  if (throwable_ref)
    {
      HVM_ExceptionThrowWithStackFrame(henv, throwable_ref, henv->top_frame);
    }
  else
    {
      HVM_ExceptionThrow(f->_env, java_lang_NullPointerException,
                         "null exception object thrown");
    }

}

OPCODE(notimplemented, 186)
{
  printf ("Instruction %d not implemented yet :)\n", 
          f->method->code[ f->pc-1 ]);
  
  abort_with_message("Instruction not implemented\n");
}
