/******************************** -*- C -*- ****************************
 *
 *	Byte Code Interpreter Module.
 *	This interprets the compiled bytecodes of a method.
 *
 *	$Revision: 1.7.5$
 *	$Date: 2000/05/28 16:56:52$
 *	$Author: pb$
 *
 ***********************************************************************/


/***********************************************************************
 *
 * Copyright 1988-92, 1994-95, 1999, 2000 Free Software Foundation, Inc.
 * Written by Steve Byrne.
 *
 * This file is part of GNU Smalltalk.
 *
 * GNU Smalltalk is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2, or (at your option) any later
 * version.
 *
 * GNU Smalltalk 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 General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * GNU Smalltalk; see the file COPYING.	 If not, write to the Free Software
 * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 ***********************************************************************/

/*
 *
 * This is basically how the interpreter works:
 *  1) The interpreter expects to be called in an environment where there
 *     already exists a well-defined method context.  The instruction pointer,
 *     stored in the global variable "ip", and the stack pointer, stored in the
 *     global variable "sp", should be set up to point into the current
 *     method and MethodContext.  Other global variables, such as "thisMethod",
 *     "self", "temporaries", etc. should also be setup.  See the routine
 *     prepareExecutionEnvironment for details.
 *  2) The interpreter checks to see if any change in its state is required,
 *     such as switching to a new process, dealing with an asynchronous signal
 *     and printing out the byte codes that are being executed, if that was
 *     requested by the user.
 *  3) After that, the byte code that ip points to is fetched and decoded.
 *     Some byte codes perform jumps, which are performed by merely adjusting
 *     the value of ip.	 Some are message sends, which are described in
 *     more detail below.  Some instructions require more than one byte code
 *     to perform their work; ip is advanced as needed and the extension
 *     byte codes are fetched.	Some byte codes, which push booleans,
 *     are usually followed by jump byte codes; in this case, and if
 *     JUMP_LOOKAHEAD is defined, the two byte codes are merged for speed.
 *  4) After dispatching the byte code, the interpreter loops around to
 *     execute another byte code.  If ip has changed to point to nil, it is
 *     a signal that the execution of the method is over, and the interpreter
 *     returns to its caller.
 *
 * Note that the interpreter is not called recursively to implement message
 * sends.  Rather the state of the interpreter is saved away in the currently
 * executing context, and a new context is created and the global variables
 * such as ip, sp, and temporaries are initialized accordingly.
 *
 * When a message send occurs, the sendMessage routine is invoked.  It
 * determines the class of the receiver, and checks to see if it already has
 * cached the method definition for the given selector and receiver class.
 * If so, that method is used, and if not, the receiver's method dictionary
 * is searched for a method with the proper selector.  If it's not found in
 * that method dictionary, the method dictionary of the classes parent is
 * examined, and on up the hierarchy, until a matching selector is found.
 *
 * If no selector is found, the receiver is sent a #doesNotUnderstand: message
 * to indicate that a matching method could not be found.
 *
 * If a method is found, it is examined for some special cases.	 The special
 * cases are primitive return of self, return of an instance variable, return
 * of a literal object, or execution of a primitive method definition.	This
 * latter operation is performed by the executePrimitiveOperation routine.  If
 * the execution of this primitive interpreter fails, the normal message send
 * operation is performed.
 *
 * If the found method is not one of the special cases, or if it is a
 * primitive that failed to execute, a "normal" message send is performed.
 * This basically entails saving away what state the interpreter has, such as
 * the values of ip, and sp, being careful to save their relative locations
 * and not their physical addresses, because one or more garbage collections
 * could occur before the method context is returned to, and the absolute
 * pointers would be invalid.
 *
 * The sendMessage routine then creates a new MethodContext object, makes
 * its parent be the currently executing MethodContext, and sets up
 * the interpreters global variables to reference the new method and
 * new MethodContext.  Once those variables are set, sendMessage returns
 * to the interpreter, which cheerfully begins executing the new method,
 * totally unaware that the method that it was executing has changed.
 *
 * When a method returns, the method that called it is used to restore the
 * interpreter's global variables to the state that they were in before
 * the called method was called.  The values of ip and sp are restored to
 * their absolute address values, and the other global state variables
 * are restored accordingly.  When after the state has been restored, the
 * interpreter continues execution, again totally oblivious to the fact
 * that it's not running the same method it was on its previous byte code.
 *
 * Global state
 * The following variables constitute the interpreter's state:
 * ip -- the real memory address of the next byte code to be executed.
 * sp -- the real memory address of the stack that's stored in the currently
 *	 executing block or method context.
 * thisMethod -- a CompiledMethod that is the currently executing method.
 * thisContextOOP -- a BlockContext or MethodContext that indicates the
 *		     context that the interpreter is currently running in.
 * temporaries -- physical address of the base of the method temporary
 *		  variables.  Typically a small number of bytes (multiple of 4
 *		  since it points to OOPs) lower than sp.
 * literals -- physical address of the base of the method literals
 * self -- an OOP that is the current receiver of the current message.
 *
 * Structure of the stack:
 *
 *    +-----------------------------------+	HERE END THE CONTEXT'S
 *    | receiver (self)			  |	FIXED INSTANCE VARIABLES
 *    +-----------------------------------+-------------------------------
 *    | args				  |	HERE BEGIN THE CONTEXT'S
 *    +-----------------------------------+	INDEXED INSTANCE VARIABLES
 *    | ...				  |
 *    +-----------------------------------+
 *    | temps				  |
 *    +-----------------------------------+
 *    | ...				  |
 *    +-----------------------------------+
 *
 */


#ifdef USE_GCC_DISPATCH
static void		**globalMonitoredByteCodes, **globalNormalByteCodes;
static void		**dispatchVec;
#endif

void
interpret()
{
/******************** REGISTER DECLARATIONS *************************/

#ifdef LOCAL_REGS
  #undef  sp
  #undef  ip
  #define exportRegs()	{ outerSP = sp; outerIP = ip; }
  #define importRegs()	{ sp = outerSP; ip = outerIP; }
  REGISTER(1, InstructionType	*ip);
  REGISTER(2, OOP		*sp);
#else
  #define exportRegs()
  #define importRegs()
#endif /* LOCAL_REGS */


#define setThisMethod(method, ipOffset) {				\
  register Method __method = (Method) oopToObj(thisMethod = method);	\
  methodBase = __method->bytecodes;					\
  literals = oopToObj(__method->literals)->data;			\
  ip = methodBase + ipOffset;						\
}

#ifndef USE_GCC_DISPATCH

  /* Most of the difference in speed between switch-based dispatch and
   * label-based dispatch comes from the fact that all bytecodes pass
   * through an if in the former case, while when using labels we can
   * use dispatchVec instead of an if statement.
   *
   * Note that the `goto lab' in BC is completely gratuitous and
   * it's only there to shut up the compiler's warnings. */

#define INTERPRETER		for(;;)
#define MONITOR_CODE		  if (exceptFlag)
#define BYTECODES		  switch(*ip)
#define BC(num, lab)		    goto lab; case num: lab: BC_HEADER {
#define FALL(lab, newLab)		goto newLab; }
#define END(lab)			NEXT_BYTECODE; }
#define NEXT_BYTECODE		      break

#define LOAD
#define PREFETCH(lab)			END(lab)

#else /* defined(USE_GCC_DISPATCH) */
  /* Indirect threaded bytecode interpretation when using GCC.	NormalBytecodes
   * is indexed by bytecode and holds onto the address of the label to jump to
   * to execute that byte code.	 MonitoredByteCodes has all byte codes jump
   * to a common place to check for exceptional conditions, and then jump thru
   * normalByteCodes.  DispatchVec points normally at normalByteCodes, but when
   * there is an exceptional condition, it points at monitoredByteCodes.
   * TrueByteCodes and falseByteCodes are used (if JUMP_LOOKAHEAD is defined)
   * to dispatch conditional jumps immediately following comparisons (see above)
   * without pushing and popping the result of the conditional.
   */

  static void *normalByteCodes[] = {
    /* 1-byte Push bytecodes */
    &&pushRecVar0,    &&pushRecVar1,	&&pushRecVar2,	  &&pushRecVar3,	/* 0 */
    &&pushRecVar4,    &&pushRecVar5,	&&pushRecVar6,	  &&pushRecVar7,	/* 4 */
    &&pushRecVar8,    &&pushRecVar9,	&&pushRecVar10,	  &&pushRecVar11,	/* 8 */
    &&pushRecVar12,   &&pushRecVar13,	&&pushRecVar14,	  &&pushRecVar15,	/* 12 */
    &&pushTemp0,      &&pushTemp1,	&&pushTemp2,	  &&pushTemp3,		/* 16 */
    &&pushTemp4,      &&pushTemp5,	&&pushTemp6,	  &&pushTemp7,		/* 20 */
    &&pushTemp8,      &&pushTemp9,	&&pushTemp10,	  &&pushTemp11,		/* 24 */
    &&pushTemp12,     &&pushTemp13,	&&pushTemp14,	  &&pushTemp15,		/* 28 */
    &&pushLit0,	      &&pushLit1,	&&pushLit2,	  &&pushLit3,		/* 32 */
    &&pushLit4,	      &&pushLit5,	&&pushLit6,	  &&pushLit7,		/* 36 */
    &&pushLit8,	      &&pushLit9,	&&pushLit10,	  &&pushLit11,		/* 40 */
    &&pushLit12,      &&pushLit13,	&&pushLit14,	  &&pushLit15,		/* 44 */
    &&pushLit16,      &&pushLit17,	&&pushLit18,	  &&pushLit19,		/* 48 */
    &&pushLit20,      &&pushLit21,	&&pushLit22,	  &&pushLit23,		/* 52 */
    &&pushLit24,      &&pushLit25,	&&pushLit26,	  &&pushLit27,		/* 56 */
    &&pushLit28,      &&pushLit29,	&&pushLit30,	  &&pushLit31,		/* 60 */
    &&pushVar0,	      &&pushVar1,	&&pushVar2,	  &&pushVar3,		/* 64 */
    &&pushVar4,	      &&pushVar5,	&&pushVar6,	  &&pushVar7,		/* 68 */
    &&pushVar8,	      &&pushVar9,	&&pushVar10,	  &&pushVar11,		/* 72 */
    &&pushVar12,      &&pushVar13,	&&pushVar14,	  &&pushVar15,		/* 76 */
    &&pushVar16,      &&pushVar17,	&&pushVar18,	  &&pushVar19,		/* 80 */
    &&pushVar20,      &&pushVar21,	&&pushVar22,	  &&pushVar23,		/* 84 */
    &&pushVar24,      &&pushVar25,	&&pushVar26,	  &&pushVar27,		/* 88 */
    &&pushVar28,      &&pushVar29,	&&pushVar30,	  &&pushVar31,		/* 92 */

    /* 1-byte Pop/Store bytecodes */
    &&stRecVar0,      &&stRecVar1,	&&stRecVar2,	  &&stRecVar3,		/* 96 */
    &&stRecVar4,      &&stRecVar5,	&&stRecVar6,	  &&stRecVar7,		/* 100 */
    &&stTemp0,	      &&stTemp1,	&&stTemp2,	  &&stTemp3,		/* 104 */
    &&stTemp4,	      &&stTemp5,	&&stTemp6,	  &&stTemp7,		/* 108 */

    /* Special push bytecodes */
    &&pushSelf,	      &&pushTrue,	&&pushFalse,	  &&pushNil,		/* 112 */
    &&pushNeg1,	      &&push0,		&&push1,	  &&push2,		/* 116 */

    /* Return bytecodes */
    &&retSelf,	      &&retTrue,	&&retFalse,	  &&retNil,		/* 120 */
    &&explicitRet,    &&retStackTop,						/* 124 */

    /* All sorts of bytecodes */
    &&bigLiteralOp,   &&breakpoint,						/* 126 */

    /* Long bytecodes */
    &&pushIndexedVal, &&storeStackTop,	&&popAndStoreTop,			/* 128 */
    &&sendShort,      &&sendLong,	&&supSendShort,	  &&unused134,		/* 131 */

    /* More stack bytecodes */
    &&popStack,	      &&dupStack,	&&pushThisContext,			/* 135 */
    &&outerTempOp,    &&nop,		&&topSelf,				/* 138 */
    &&topOne,	      &&topIndexedVal,						/* 141 */

    /* Exit interpreter bytecode */
    &&endExecution,								/* 143 */

    /* Jump bytecodes */
    &&shJmp1,	      &&shJmp2,		&&shJmp3,	  &&shJmp4,		/* 144 */
    &&shJmp5,	      &&shJmp6,		&&shJmp7,	  &&shJmp8,		/* 148 */
    &&shJmp1False,    &&shJmp2False,	&&shJmp3False,	  &&shJmp4False,	/* 152 */
    &&shJmp5False,    &&shJmp6False,	&&shJmp7False,	  &&shJmp8False,	/* 156 */
    &&longJmpNeg4,    &&longJmpNeg3,	&&longJmpNeg2,	  &&longJmpNeg1,	/* 160 */
    &&longJmp0,	      &&longJmp1,	&&longJmp2,	  &&longJmp3,		/* 164 */
    &&popJmpTrue0,    &&popJmpTrue1,	&&popJmpTrue2,	  &&popJmpTrue3,	/* 168 */
    &&popJmpFalse0,   &&popJmpFalse1,	&&popJmpFalse2,	  &&popJmpFalse3,	/* 172 */

    /* Special 1-byte send bytecodes */
    &&sendPlus,	      &&sendMinus,	&&sendLess,	  &&sendGreater,	/* 176 */
    &&sendLessEq,     &&sendGreaterEq,	&&sendEqual,	  &&sendNotEqual,	/* 180 */
    &&sendTimes,      &&sendDivide,	&&sendRemainder,  &&sendAtSign,		/* 184 */
    &&sendBitShift,   &&sendIntDivide,	&&sendBitAnd,	  &&sendBitOr,		/* 188 */
    &&sendAt,	      &&sendAtPut,	&&sendSize,	  &&sendNext,		/* 192 */
    &&sendNextPut,    &&sendAtEnd,	&&sendSameObject, &&sendClass,		/* 196 */
    &&sendBlockCopy,  &&sendValue,	&&sendValueColon, &&sendDoColon,	/* 200 */
    &&sendNew,	      &&sendNewColon,	&&sendIsNil,	  &&sendNotNil,		/* 204 */

    /* General 1-byte send bytecodes */
    &&sendNoArg0,     &&sendNoArg1,	&&sendNoArg2,	  &&sendNoArg3,		/* 208 */
    &&sendNoArg4,     &&sendNoArg5,	&&sendNoArg6,	  &&sendNoArg7,		/* 212 */
    &&sendNoArg8,     &&sendNoArg9,	&&sendNoArg10,	  &&sendNoArg11,	/* 216 */
    &&sendNoArg12,    &&sendNoArg13,	&&sendNoArg14,	  &&sendNoArg15,	/* 220 */
    &&sendOneArg0,    &&sendOneArg1,	&&sendOneArg2,	  &&sendOneArg3,	/* 224 */
    &&sendOneArg4,    &&sendOneArg5,	&&sendOneArg6,	  &&sendOneArg7,	/* 228 */
    &&sendOneArg8,    &&sendOneArg9,	&&sendOneArg10,	  &&sendOneArg11,	/* 232 */
    &&sendOneArg12,   &&sendOneArg13,	&&sendOneArg14,	  &&sendOneArg15,	/* 236 */
    &&sendTwoArg0,    &&sendTwoArg1,	&&sendTwoArg2,	  &&sendTwoArg3,	/* 240 */
    &&sendTwoArg4,    &&sendTwoArg5,	&&sendTwoArg6,	  &&sendTwoArg7,	/* 244 */
    &&sendTwoArg8,    &&sendTwoArg9,	&&sendTwoArg10,	  &&sendTwoArg11,	/* 248 */
    &&sendTwoArg12,   &&sendTwoArg13,	&&sendTwoArg14,	  &&sendTwoArg15	/* 252 */
  };

  static void *monitoredByteCodes[] = {
    [0 ... 255] = &&monitorByteCodes
  };

#ifdef JUMP_LOOKAHEAD
  static void *trueByteCodes[] = {
    [  0 ... 124] = &&lookaheadFailedTrue,		/* pot-pourri	 */

    &&retTrue,						/* return top	 */

    [126 ... 151] = &&lookaheadFailedTrue,		/* pot-pourri	 */
    [152 ... 159] = &&nop,				/* jump if false */
    [160 ... 167] = &&lookaheadFailedTrue,		/* unconditional */

    &&longJmp0,	      &&longJmp1,	&&longJmp2,	  &&longJmp3,

    [172 ... 175] = &&lookaheadLongJumpNotTaken,	/* jump if false */
    [176 ... 255] = &&lookaheadFailedTrue		/* pot-pourri */
  };

  static void *falseByteCodes[] = {
    [  0 ... 124] = &&lookaheadFailedFalse,		/* pot-pourri	 */

    &&retFalse,						/* return top	 */

    [126 ... 151] = &&lookaheadFailedFalse,		/* pot-pourri	 */

    &&shJmp1,	    &&shJmp2,		&&shJmp3,	  &&shJmp4,
    &&shJmp5,	    &&shJmp6,		&&shJmp7,	  &&shJmp8,

    [160 ... 167] = &&lookaheadFailedFalse,		/* unconditional */
    [168 ... 171] = &&lookaheadLongJumpNotTaken,	/* jump if true */

    &&longJmp0,	    &&longJmp1,		&&longJmp2,	  &&longJmp3,

    [176 ... 255] = &&lookaheadFailedFalse		/* pot-pourri */
  };
#endif

#define SET_EXCEPT_FLAG(x)						\
  dispatchVec = x ? globalMonitoredByteCodes : globalNormalByteCodes

#define INTERPRETER					\
  globalNormalByteCodes = normalByteCodes;		\
  globalMonitoredByteCodes = monitoredByteCodes;	\
  dispatchVec = normalByteCodes;

#define MONITOR_CODE			monitorByteCodes:		\
					  if(exceptFlag)

#define BYTECODES			  goto *normalByteCodes[*ip];
#define NEXT_BYTECODE			  goto *dispatchVec[*ip]
#define BC(num, lab)			lab: BC_HEADER {
#define LOAD				  REGISTER(3, void **prefetch); \
					  prefetch = &dispatchVec[*ip]
#define FALL(lab, newLab)		  goto newLab; }
#define PREFETCH(lab)			  goto **prefetch; }
#define END(lab)			  NEXT_BYTECODE; }

#ifdef JUMP_LOOKAHEAD
#define DO_JUMP_LOOKAHEAD(x) {		\
  if (x) {				\
    popNOOPs(1);			\
    goto *trueByteCodes[*ip];		\
  } else {				\
    popNOOPs(1);			\
    goto *falseByteCodes[*ip];		\
  }					\
}
#endif
#endif /* defined(USE_GCC_DISPATCH) */

/******************** COMMON DEFINITIONS *********************************/

#ifdef OPTIMIZE
# define ILLEGAL(errMsg)
#else
# define ILLEGAL(errMsg)		errorf(errMsg)
#endif

#define EXIT_IF(what)		\
  if (what) {			\
    inInterpreter = false;	\
    ip = nil;			\
    return;			\
  }

#define BRANCH_LABEL(lab)	lab:

#ifdef PROFBLOCK
#define BC_HEADER		ip++; byteCodeCounter++; bytecodes[*ip]++;
#else
#define BC_HEADER		ip++; byteCodeCounter++;
#endif /* PROFBLOCK */

/******************** DEFAULT DEFINITIONS ********************************/

#ifndef DO_JUMP_LOOKAHEAD
#define DO_JUMP_LOOKAHEAD(x) {		\
  setStackTopBoolean(x);		\
  NEXT_BYTECODE;			\
}
#endif

#ifndef SET_EXCEPT_FLAG
#endif

/******************** BODY OF THE INTERPRETER ***********************/

  importRegs();
  EXIT_IF(!ip);

  inInterpreter = true;

  /* this is all we need to do -- since we only pay attention to the
   * execution tracing flag on entry to this routine, and since the first time
   * through we pass through the monitoring code, we can get away with just
   * exceptFlag here.
   */
  exceptFlag = executionTracing;

  INTERPRETER {
    OOP			returnedValue;

    MONITOR_CODE {
      exportRegs();
      if (asyncQueueIndex) {	/* deal with any async signals	*/
	register int	i;
	IntState	oldSigMask;
	oldSigMask = disableInterrupts(); /* block out everything! */
	for (i = 0; i < asyncQueueIndex; i++) {
	  syncSignal(queuedAsyncSignals[i]);
	}
	asyncQueueIndex = 0;
	enableInterrupts(oldSigMask);
      }
      if (!isNil(switchToProcess)) {
	/*exportRegs(); */
	changeProcessContext(switchToProcess);
	importRegs();
	EXIT_IF(!ip);
      }
      if (abortExecution) {
	OOP selectorOOP;
	selectorOOP = internString(abortExecution);
	abortExecution = nil;
	sendMessage(selectorOOP, 0, false);
      }
      if (executionTracing) {
	if (verboseExecTracing) {
	  printf("\t  --> ");
	  printObject(stackTop());
	  printf("\n");
	}
	printf(" %5d:\t", ip - methodBase);
	printByteCodeName(ip, ip - methodBase, literals);
	printf("\n");
      }
      setExceptFlag(executionTracing);
      importRegs();
    }

    BYTECODES {
    BC(0,  pushRecVar0) LOAD; pushOOP(receiverVariable(self, 0));  PREFETCH(pushRecVar0);
    BC(1,  pushRecVar1) LOAD; pushOOP(receiverVariable(self, 1));  PREFETCH(pushRecVar1);
    BC(2,  pushRecVar2) LOAD; pushOOP(receiverVariable(self, 2));  PREFETCH(pushRecVar2);
    BC(3,  pushRecVar3) LOAD; pushOOP(receiverVariable(self, 3));  PREFETCH(pushRecVar3);
    BC(4,  pushRecVar4) LOAD; pushOOP(receiverVariable(self, 4));  PREFETCH(pushRecVar4);
    BC(5,  pushRecVar5) LOAD; pushOOP(receiverVariable(self, 5));  PREFETCH(pushRecVar5);
    BC(6,  pushRecVar6) LOAD; pushOOP(receiverVariable(self, 6));  PREFETCH(pushRecVar6);
    BC(7,  pushRecVar7) LOAD; pushOOP(receiverVariable(self, 7));  PREFETCH(pushRecVar7);
    BC(8,  pushRecVar8) LOAD; pushOOP(receiverVariable(self, 8));  PREFETCH(pushRecVar8);
    BC(9,  pushRecVar9) LOAD; pushOOP(receiverVariable(self, 9));  PREFETCH(pushRecVar9);
    BC(10, pushRecVar10)LOAD; pushOOP(receiverVariable(self, 10)); PREFETCH(pushRecVar10);
    BC(11, pushRecVar11)LOAD; pushOOP(receiverVariable(self, 11)); PREFETCH(pushRecVar11);
    BC(12, pushRecVar12)LOAD; pushOOP(receiverVariable(self, 12)); PREFETCH(pushRecVar12);
    BC(13, pushRecVar13)LOAD; pushOOP(receiverVariable(self, 13)); PREFETCH(pushRecVar13);
    BC(14, pushRecVar14)LOAD; pushOOP(receiverVariable(self, 14)); PREFETCH(pushRecVar14);
    BC(15, pushRecVar15)LOAD; pushOOP(receiverVariable(self, 15)); PREFETCH(pushRecVar15);

    BC(16, pushTemp0) LOAD; pushOOP(methodTemporary(0));  PREFETCH(pushTemp0);
    BC(17, pushTemp1) LOAD; pushOOP(methodTemporary(1));  PREFETCH(pushTemp1);
    BC(18, pushTemp2) LOAD; pushOOP(methodTemporary(2));  PREFETCH(pushTemp2);
    BC(19, pushTemp3) LOAD; pushOOP(methodTemporary(3));  PREFETCH(pushTemp3);
    BC(20, pushTemp4) LOAD; pushOOP(methodTemporary(4));  PREFETCH(pushTemp4);
    BC(21, pushTemp5) LOAD; pushOOP(methodTemporary(5));  PREFETCH(pushTemp5);
    BC(22, pushTemp6) LOAD; pushOOP(methodTemporary(6));  PREFETCH(pushTemp6);
    BC(23, pushTemp7) LOAD; pushOOP(methodTemporary(7));  PREFETCH(pushTemp7);
    BC(24, pushTemp8) LOAD; pushOOP(methodTemporary(8));  PREFETCH(pushTemp8);
    BC(25, pushTemp9) LOAD; pushOOP(methodTemporary(9));  PREFETCH(pushTemp9);
    BC(26, pushTemp10)LOAD; pushOOP(methodTemporary(10)); PREFETCH(pushTemp10);
    BC(27, pushTemp11)LOAD; pushOOP(methodTemporary(11)); PREFETCH(pushTemp11);
    BC(28, pushTemp12)LOAD; pushOOP(methodTemporary(12)); PREFETCH(pushTemp12);
    BC(29, pushTemp13)LOAD; pushOOP(methodTemporary(13)); PREFETCH(pushTemp13);
    BC(30, pushTemp14)LOAD; pushOOP(methodTemporary(14)); PREFETCH(pushTemp14);
    BC(31, pushTemp15)LOAD; pushOOP(methodTemporary(15)); PREFETCH(pushTemp15);

    BC(32, pushLit0) LOAD; pushOOP(methodLiteral(0));  PREFETCH(pushLit0);
    BC(33, pushLit1) LOAD; pushOOP(methodLiteral(1));  PREFETCH(pushLit1);
    BC(34, pushLit2) LOAD; pushOOP(methodLiteral(2));  PREFETCH(pushLit2);
    BC(35, pushLit3) LOAD; pushOOP(methodLiteral(3));  PREFETCH(pushLit3);
    BC(36, pushLit4) LOAD; pushOOP(methodLiteral(4));  PREFETCH(pushLit4);
    BC(37, pushLit5) LOAD; pushOOP(methodLiteral(5));  PREFETCH(pushLit5);
    BC(38, pushLit6) LOAD; pushOOP(methodLiteral(6));  PREFETCH(pushLit6);
    BC(39, pushLit7) LOAD; pushOOP(methodLiteral(7));  PREFETCH(pushLit7);
    BC(40, pushLit8) LOAD; pushOOP(methodLiteral(8));  PREFETCH(pushLit8);
    BC(41, pushLit9) LOAD; pushOOP(methodLiteral(9));  PREFETCH(pushLit9);
    BC(42, pushLit10)LOAD; pushOOP(methodLiteral(10)); PREFETCH(pushLit10);
    BC(43, pushLit11)LOAD; pushOOP(methodLiteral(11)); PREFETCH(pushLit11);
    BC(44, pushLit12)LOAD; pushOOP(methodLiteral(12)); PREFETCH(pushLit12);
    BC(45, pushLit13)LOAD; pushOOP(methodLiteral(13)); PREFETCH(pushLit13);
    BC(46, pushLit14)LOAD; pushOOP(methodLiteral(14)); PREFETCH(pushLit14);
    BC(47, pushLit15)LOAD; pushOOP(methodLiteral(15)); PREFETCH(pushLit15);
    BC(48, pushLit16)LOAD; pushOOP(methodLiteral(16)); PREFETCH(pushLit16);
    BC(49, pushLit17)LOAD; pushOOP(methodLiteral(17)); PREFETCH(pushLit17);
    BC(50, pushLit18)LOAD; pushOOP(methodLiteral(18)); PREFETCH(pushLit18);
    BC(51, pushLit19)LOAD; pushOOP(methodLiteral(19)); PREFETCH(pushLit19);
    BC(52, pushLit20)LOAD; pushOOP(methodLiteral(20)); PREFETCH(pushLit20);
    BC(53, pushLit21)LOAD; pushOOP(methodLiteral(21)); PREFETCH(pushLit21);
    BC(54, pushLit22)LOAD; pushOOP(methodLiteral(22)); PREFETCH(pushLit22);
    BC(55, pushLit23)LOAD; pushOOP(methodLiteral(23)); PREFETCH(pushLit23);
    BC(56, pushLit24)LOAD; pushOOP(methodLiteral(24)); PREFETCH(pushLit24);
    BC(57, pushLit25)LOAD; pushOOP(methodLiteral(25)); PREFETCH(pushLit25);
    BC(58, pushLit26)LOAD; pushOOP(methodLiteral(26)); PREFETCH(pushLit26);
    BC(59, pushLit27)LOAD; pushOOP(methodLiteral(27)); PREFETCH(pushLit27);
    BC(60, pushLit28)LOAD; pushOOP(methodLiteral(28)); PREFETCH(pushLit28);
    BC(61, pushLit29)LOAD; pushOOP(methodLiteral(29)); PREFETCH(pushLit29);
    BC(62, pushLit30)LOAD; pushOOP(methodLiteral(30)); PREFETCH(pushLit30);
    BC(63, pushLit31)LOAD; pushOOP(methodLiteral(31)); PREFETCH(pushLit31);

    BC(64, pushVar0)  LOAD; pushOOP(methodVariable(0));	 PREFETCH(pushVar0);
    BC(65, pushVar1)  LOAD; pushOOP(methodVariable(1));	 PREFETCH(pushVar1);
    BC(66, pushVar2)  LOAD; pushOOP(methodVariable(2));	 PREFETCH(pushVar2);
    BC(67, pushVar3)  LOAD; pushOOP(methodVariable(3));	 PREFETCH(pushVar3);
    BC(68, pushVar4)  LOAD; pushOOP(methodVariable(4));	 PREFETCH(pushVar4);
    BC(69, pushVar5)  LOAD; pushOOP(methodVariable(5));	 PREFETCH(pushVar5);
    BC(70, pushVar6)  LOAD; pushOOP(methodVariable(6));	 PREFETCH(pushVar6);
    BC(71, pushVar7)  LOAD; pushOOP(methodVariable(7));	 PREFETCH(pushVar7);
    BC(72, pushVar8)  LOAD; pushOOP(methodVariable(8));	 PREFETCH(pushVar8);
    BC(73, pushVar9)  LOAD; pushOOP(methodVariable(9));	 PREFETCH(pushVar9);
    BC(74, pushVar10) LOAD; pushOOP(methodVariable(10)); PREFETCH(pushVar10);
    BC(75, pushVar11) LOAD; pushOOP(methodVariable(11)); PREFETCH(pushVar11);
    BC(76, pushVar12) LOAD; pushOOP(methodVariable(12)); PREFETCH(pushVar12);
    BC(77, pushVar13) LOAD; pushOOP(methodVariable(13)); PREFETCH(pushVar13);
    BC(78, pushVar14) LOAD; pushOOP(methodVariable(14)); PREFETCH(pushVar14);
    BC(79, pushVar15) LOAD; pushOOP(methodVariable(15)); PREFETCH(pushVar15);
    BC(80, pushVar16) LOAD; pushOOP(methodVariable(16)); PREFETCH(pushVar16);
    BC(81, pushVar17) LOAD; pushOOP(methodVariable(17)); PREFETCH(pushVar17);
    BC(82, pushVar18) LOAD; pushOOP(methodVariable(18)); PREFETCH(pushVar18);
    BC(83, pushVar19) LOAD; pushOOP(methodVariable(19)); PREFETCH(pushVar19);
    BC(84, pushVar20) LOAD; pushOOP(methodVariable(20)); PREFETCH(pushVar20);
    BC(85, pushVar21) LOAD; pushOOP(methodVariable(21)); PREFETCH(pushVar21);
    BC(86, pushVar22) LOAD; pushOOP(methodVariable(22)); PREFETCH(pushVar22);
    BC(87, pushVar23) LOAD; pushOOP(methodVariable(23)); PREFETCH(pushVar23);
    BC(88, pushVar24) LOAD; pushOOP(methodVariable(24)); PREFETCH(pushVar24);
    BC(89, pushVar25) LOAD; pushOOP(methodVariable(25)); PREFETCH(pushVar25);
    BC(90, pushVar26) LOAD; pushOOP(methodVariable(26)); PREFETCH(pushVar26);
    BC(91, pushVar27) LOAD; pushOOP(methodVariable(27)); PREFETCH(pushVar27);
    BC(92, pushVar28) LOAD; pushOOP(methodVariable(28)); PREFETCH(pushVar28);
    BC(93, pushVar29) LOAD; pushOOP(methodVariable(29)); PREFETCH(pushVar29);
    BC(94, pushVar30) LOAD; pushOOP(methodVariable(30)); PREFETCH(pushVar30);
    BC(95, pushVar31) LOAD; pushOOP(methodVariable(31)); PREFETCH(pushVar31);

    BC(96,  stRecVar0) LOAD; storeReceiverVariable(self, 0, popOOP()); PREFETCH(stRecVar0);
    BC(97,  stRecVar1) LOAD; storeReceiverVariable(self, 1, popOOP()); PREFETCH(stRecVar1);
    BC(98,  stRecVar2) LOAD; storeReceiverVariable(self, 2, popOOP()); PREFETCH(stRecVar2);
    BC(99,  stRecVar3) LOAD; storeReceiverVariable(self, 3, popOOP()); PREFETCH(stRecVar3);
    BC(100, stRecVar4) LOAD; storeReceiverVariable(self, 4, popOOP()); PREFETCH(stRecVar4);
    BC(101, stRecVar5) LOAD; storeReceiverVariable(self, 5, popOOP()); PREFETCH(stRecVar5);
    BC(102, stRecVar6) LOAD; storeReceiverVariable(self, 6, popOOP()); PREFETCH(stRecVar6);
    BC(103, stRecVar7) LOAD; storeReceiverVariable(self, 7, popOOP()); PREFETCH(stRecVar7);

    BC(104, stTemp0) LOAD; storeMethodTemporary(0, popOOP()); PREFETCH(stTemp0);
    BC(105, stTemp1) LOAD; storeMethodTemporary(1, popOOP()); PREFETCH(stTemp1);
    BC(106, stTemp2) LOAD; storeMethodTemporary(2, popOOP()); PREFETCH(stTemp2);
    BC(107, stTemp3) LOAD; storeMethodTemporary(3, popOOP()); PREFETCH(stTemp3);
    BC(108, stTemp4) LOAD; storeMethodTemporary(4, popOOP()); PREFETCH(stTemp4);
    BC(109, stTemp5) LOAD; storeMethodTemporary(5, popOOP()); PREFETCH(stTemp5);
    BC(110, stTemp6) LOAD; storeMethodTemporary(6, popOOP()); PREFETCH(stTemp6);
    BC(111, stTemp7) LOAD; storeMethodTemporary(7, popOOP()); PREFETCH(stTemp7);

    BC(112, pushSelf)	LOAD; uncheckedPushOOP(self);		PREFETCH(pushSelf);
    BC(113, pushTrue)	LOAD; uncheckedPushOOP(trueOOP);	PREFETCH(pushTrue);
    BC(114, pushFalse)	LOAD; uncheckedPushOOP(falseOOP);	PREFETCH(pushFalse);
    BC(115, pushNil)	LOAD; uncheckedPushOOP(nilOOP);		PREFETCH(pushNil);
    BC(116, pushNeg1)	LOAD; pushInt(-1);			PREFETCH(pushNeg1);
    BC(117, push0)	LOAD; pushInt(0);			PREFETCH(push0);
    BC(118, push1)	LOAD; pushInt(1);			PREFETCH(push1);
    BC(119, push2)	LOAD; pushInt(2);			PREFETCH(push2);

    BC(120, retSelf)  returnedValue = self;		FALL(retSelf, doRetStackTop);
    BC(121, retTrue)  returnedValue = trueOOP;		FALL(retTrue, doRetStackTop);
    BC(122, retFalse) returnedValue = falseOOP;		FALL(retFalse, doRetStackTop);
    BC(123, retNil)   returnedValue = nilOOP;		FALL(retNil, doRetStackTop);

    BC(124, explicitRet)		/* return stack top from method */
#ifdef old_code
/**/abortMethod:			/* here if ^C is seen to abort things */
#endif
      register OOP		methodContextOOP;
      register BlockContext	blockContext;
      returnedValue = stackTop();

      /*
       * We're executing in a block context and an explicit return is
       * encountered.  This means that we are to return from the caller of
       * the method that created the block context, no matter how many
       * levels of message sending are between where we currently are and
       * our parent method context.
       */
      blockContext = (BlockContext)oopToObj(thisContextOOP);
      do {
	methodContextOOP = blockContext->outerContext;
	blockContext = (BlockContext)oopToObj(methodContextOOP);
      } while (!isMethodContext(blockContext));

      blockContext = (BlockContext)oopToObj(thisContextOOP);
      if (noParentContext(methodContextOOP)) {
	OOP prevContextOOP;

	/* We are to create a reference to thisContext, so empty the stack. */
	emptyContextStack();
	prevContextOOP = thisContextOOP;

	/* Just unwind to the caller, and prepare to send a message to the
	 * context */
	exportRegs();
	unwindLastContext();
	importRegs();
	setStackTop(prevContextOOP);

	debug();
	exportRegs();
	sendMessage(internString("badReturnError"), 0, false);
	importRegs();
	NEXT_BYTECODE;
      } /* test for block context in a dead method */

      exportRegs();
      unwindToContext(methodContextOOP);
      unwindLastContext();
      importRegs();
      setStackTop(returnedValue);
    END(explicitRet);

    BC(125, retStackTop)
      returnedValue = stackTop();
    BRANCH_LABEL(doRetStackTop);
      exportRegs();
      unwindLastContext();
      importRegs();
      setStackTop(returnedValue);
    END(retStackTop);

    /* 126, not in the blue book, is used for big literals access as follows:
       01111110 yyxxxxxx xxxxxxxx
       on method literal xxx do
	    yy = 00 -> push constant
	    yy = 01 -> push variable
	    yy = 10 -> store variable
	    yy = 11 -> pop/store variable
    */

    BC(126, bigLiteralOp)
      REGISTER(3, unsigned int num);
      num = *ip++;
      num <<= 8;
      num |= *ip++;

      switch(num >> 14) {
	case 0:
	  pushOOP(methodLiteral(num & 16383));
	  break;
	case 1:
	  pushOOP(methodVariable(num & 16383));
	  break;
	case 2:
	  storeMethodVariable(num & 16383, popOOP());
	  break;
	case 3: default:
	  storeMethodVariable(num & 16383, stackTop());
	  break;
      }
    END(bigLiteralOp);

    BC(127, breakpoint)
      REGISTER(3, OOP		tempOOP);

      ip--; /* we reenter execution in the same bytecode. */
      emptyContextStack();

      tempOOP = popOOP();
      pushOOP(self);
      pushOOP(thisContextOOP);
      pushOOP(tempOOP);
      exportRegs();
      sendMessage(internString("breakpoint:return:"), 2, false);
      importRegs();
    END(breakpoint);

    BC(128, pushIndexedVal)
      REGISTER(3, Byte		ival2);
      ival2 = *ip++;
      switch (ival2 >> 6) {
      case 0:
	pushOOP(receiverVariable(self, ival2 & 63));
	break;
      case 1:
	pushOOP(methodTemporary(ival2 & 63));
	break;
      case 2:
	pushOOP(methodLiteral(ival2 & 63));
	break;
      case 3: default:
	pushOOP(methodVariable(ival2 & 63));
	break;
      }
    END(pushIndexedVal);

    BC(129, storeStackTop)
      REGISTER(3, Byte		ival2);
      ival2 = *ip++;
      switch (ival2 >> 6) {
      case 0:
	storeReceiverVariable(self, ival2 & 63, stackTop());
	break;
      case 1:
	storeMethodTemporary(ival2 & 63, stackTop());
	break;
      case 2:
	ILLEGAL("Attempt to store into a method constant");
	break;
      case 3: default:
	storeMethodVariable(ival2 & 63, stackTop());
      }
    END(storeStackTop);

    BC(130, popAndStoreTop)
      REGISTER(3, Byte		ival2);
      ival2 = *ip++;
      switch (ival2 >> 6) {
      case 0:
	storeReceiverVariable(self, ival2 & 63, popOOP());
	break;
      case 1:
	storeMethodTemporary(ival2 & 63, popOOP());
	break;
      case 2:
	ILLEGAL("Attempt to store into a method constant");
	break;
      case 3: default:
	storeMethodVariable(ival2 & 63, popOOP());
      }
    END(popAndStoreTop);

    BC(131, sendShort)	/* send selector y (xxxyyyyy), x args */
      REGISTER(3, Byte		ival2);
      ival2 = *ip++;
      exportRegs();
      sendMessage(methodLiteral(ival2 & 31), ival2 >> 5, false);
      importRegs();
    END(sendShort);

    BC(132, sendLong)		/* send selector y (yysxxxxx,yyyyyyyy) x args */
      REGISTER(3, Byte	ival2);		/*     \___to super if s=1    */
      register int	ival3;
      ival2 = *ip++;		/* the number of args */
      ival3 = *ip++;		/* the selector */
      ival3 += (ival2 & 192) << 2;
      exportRegs();
      if (ival2 & 32) {
	sendToSuper(methodLiteral(ival3), ival2 & 31, true);
      } else {
	sendMessage(methodLiteral(ival3), ival2 & 31, false);
      }
      importRegs();
    END(sendLong);

    BC(133, supSendShort)	/* send super selector y (xxxyyyyy), x args*/
      REGISTER(3, Byte		ival2);
      ival2 = *ip++;
      exportRegs();
      sendToSuper(methodLiteral(ival2 & 31), ival2 >> 5, true);
      importRegs();
    END(supSendShort);

    BC(134, unused134)
      errorf("Invalid bytecode 134 encountered!");
      exportRegs();
      showBacktrace();
      importRegs();
    END(supSendLong);

    BC(135, popStack)
      LOAD;
      popNOOPs(1);
    PREFETCH(popStack);

    BC(136, dupStack)
      register OOP tempOOP;
      LOAD;
      tempOOP = stackTop();
      pushOOP(tempOOP);
    PREFETCH(dupStack);

    BC(137, pushThisContext) /* push active context */
      emptyContextStack();
      pushOOP(thisContextOOP);
#ifdef PROFBLOCK
      ps.numThisContexts++;
#endif
    END(pushThisContext);

    BC(138, outerTempOp)
	unsigned int numScopes;
	unsigned int varIndex;
	OOP	contextOOP;
	BlockContext context;

	varIndex = *ip++;
	numScopes = *ip++;

	context = (BlockContext)oopToObj(thisContextOOP);
	do {
	  contextOOP = context->outerContext;
	  context = (BlockContext)oopToObj(contextOOP);
	} while (--numScopes);

#ifndef OPTIMIZE
	if (context != nil) {	/* we're on the right one */
#endif
	  switch(varIndex >> 6) {
	    case 0:
	      ILLEGAL("Invalid bytecode 138/0");
	      break;
	    case 1:
	      pushOOP(context->contextStack[varIndex & 63]);
	      break;
	    case 2:
	      context->contextStack[varIndex & 63] = popOOP();
	      break;
	    case 3: default:
	      context->contextStack[varIndex & 63] = stackTop();
	      break;
#ifndef OPTIMIZE
	  }
#endif
	}
    END(outerTempOp);

    /* :-( Sigh ... have to sacrifice 139 for nop... )-: */
    BC(139, nop);					END(nop);
    BC(140, topSelf) LOAD; uncheckedSetTop(self);	PREFETCH(topSelf);
    BC(141, topOne)  LOAD; uncheckedSetTop(fromInt(1)); PREFETCH(topOne);

    BC(142, topIndexedVal)
      REGISTER(3, Byte		ival2);
      ival2 = *ip++;
      switch (ival2 >> 6) {
      case 0:
	setStackTop(receiverVariable(self, ival2 & 63));
	break;
      case 1:
	setStackTop(methodTemporary(ival2 & 63));
	break;
      case 2:
	setStackTop(methodLiteral(ival2 & 63));
	break;
      case 3: default:
	setStackTop(methodVariable(ival2 & 63));
	break;
      }
    END(topIndexedVal);

    BC(143, endExecution) EXIT_IF(true); END(unused143);


    /* Forward jump by 1 to 8 */
    BC(144, shJmp1) ip += 1;  END(shJmp1);
    BC(145, shJmp2) ip += 2;  END(shJmp2);
    BC(146, shJmp3) ip += 3;  END(shJmp3);
    BC(147, shJmp4) ip += 4;  END(shJmp4);
    BC(148, shJmp5) ip += 5;  END(shJmp5);
    BC(149, shJmp6) ip += 6;  END(shJmp6);
    BC(150, shJmp7) ip += 7;  END(shJmp7);
    BC(151, shJmp8) ip += 8;  END(shJmp8);


#define jmpFalse(bytes) \
  REGISTER(3, OOP	tempOOP); \
  if ((tempOOP = popOOP()) == falseOOP) { /* jump forward if false */ \
    ip += (bytes); \
  } else if (tempOOP != trueOOP) { \
    ip += (bytes); \
    unPop(1); \
    exportRegs(); \
    sendMessage(internString("mustBeBoolean"), 0, false); \
    importRegs(); \
    NEXT_BYTECODE; \
  }

    BC(152, shJmp1False) jmpFalse(1); END(shJmp1False);
    BC(153, shJmp2False) jmpFalse(2); END(shJmp2False);
    BC(154, shJmp3False) jmpFalse(3); END(shJmp3False);
    BC(155, shJmp4False) jmpFalse(4); END(shJmp4False);
    BC(156, shJmp5False) jmpFalse(5); END(shJmp5False);
    BC(157, shJmp6False) jmpFalse(6); END(shJmp6False);
    BC(158, shJmp7False) jmpFalse(7); END(shJmp7False);
    BC(159, shJmp8False) jmpFalse(8); END(shJmp8False);

#undef jmpFalse

#define longJmpSigned(highDelta)				\
  REGISTER(3, Byte		ival2);				\
  ival2 = *ip++;		/* jump forward or back */	\
  ip += (long)((highDelta) << 8) + ival2

    BC(160, longJmpNeg4) longJmpSigned(-4); END(longJmpNeg4);
    BC(161, longJmpNeg3) longJmpSigned(-3); END(longJmpNeg3);
    BC(162, longJmpNeg2) longJmpSigned(-2); END(longJmpNeg2);
    BC(163, longJmpNeg1) longJmpSigned(-1); END(longJmpNeg1);
    BC(164, longJmp0)	 longJmpSigned(0);  END(longJmp0);
    BC(165, longJmp1)	 longJmpSigned(1);  END(longJmp1);
    BC(166, longJmp2)	 longJmpSigned(2);  END(longJmp2);
    BC(167, longJmp3)	 longJmpSigned(3);  END(longJmp3);

#undef longJmpSigned



#define boolJmp(highDelta, jmpCaseOOP, nonJmpCaseOOP)	\
  REGISTER(3, OOP	tempOOP);			\
  if ((tempOOP = popOOP()) == (jmpCaseOOP)) {		\
    REGISTER(3, Byte	ival2);				\
    ival2 = *ip++;					\
    ip += (highDelta << 8) + ival2;			\
  } else if (tempOOP != (nonJmpCaseOOP)) {		\
    REGISTER(3, Byte	ival2);				\
    ival2 = *ip++;					\
    ip += (highDelta << 8) + ival2;			\
    unPop(1);						\
    exportRegs();					\
    sendMessage(internString("mustBeBoolean"), 0, false); \
    importRegs();					\
  } else {						\
    REGISTER(3, Byte	ival2);				\
    ival2 = *ip++;					\
  }

#define trueJmp(highDelta) \
  boolJmp(highDelta, trueOOP, falseOOP)

#define falseJmp(highDelta) \
  boolJmp(highDelta, falseOOP, trueOOP)


    BC(168, popJmpTrue0)  trueJmp(0);  END(popJmpTrue0);
    BC(169, popJmpTrue1)  trueJmp(1);  END(popJmpTrue1);
    BC(170, popJmpTrue2)  trueJmp(2);  END(popJmpTrue2);
    BC(171, popJmpTrue3)  trueJmp(3);  END(popJmpTrue3);

    BC(172, popJmpFalse0) falseJmp(0); END(popJmpFalse0);
    BC(173, popJmpFalse1) falseJmp(1); END(popJmpFalse1);
    BC(174, popJmpFalse2) falseJmp(2); END(popJmpFalse2);
    BC(175, popJmpFalse3) falseJmp(3); END(popJmpFalse3);

#undef falseJmp
#undef trueJmp
#undef boolJmp


#ifndef OPEN_CODE_MATH

#define RAW_INT_OP(op, noOverflow)
#define RAW_FLOAT_OP(op)
#define INTERP_BASIC_OP(op, fop)
#define RAW_BOOL_OP(operator)
#define RAW_BOOL_FLOAT_OP(operator)
#define INTERP_BASIC_BOOL(operator)

#else

#define RAW_INT_OP(op, noOverflow)	\
{					\
  register long iarg1, iarg2;		\
  iarg1 = toInt(tempOOP);		\
  iarg2 = toInt(popOOP());		\
  op;					\
  if(noOverflow || !INT_OVERFLOW(iarg1)) { \
    setStackTopInt(iarg1);		\
    NEXT_BYTECODE;			\
  } else {				\
    unPop(1);				\
  }					\
}

#define RAW_FLOAT_OP(op)		\
{					\
  OOP	oop2,oopResult;			\
  double farg1, farg2;			\
  oop2 = stackTop();			\
  if (isClass(oop2, floatClass)) {	\
    farg1 = floatOOPValue(tempOOP);	\
    farg2 = floatOOPValue(oop2);	\
    popNOOPs(1);			\
    exportRegs();			\
    oopResult = floatNew(op);		\
    importRegs();			\
    setStackTop(oopResult);		\
    NEXT_BYTECODE;			\
  }					\
}


#define INTERP_BASIC_OP(op, fop)		\
  REGISTER(3, OOP	tempOOP);		\
  tempOOP = stackAt(1);				\
  if (areInts(tempOOP, stackTop())) {		\
    RAW_INT_OP(op, false);			\
  } else if (isClass(tempOOP, floatClass)) {	\
    RAW_FLOAT_OP(fop);				\
  }


#define RAW_BOOL_OP(operator)						\
  DO_JUMP_LOOKAHEAD(	((long)tempOOP) operator ((long)popOOP()) );

#define RAW_BOOL_FLOAT_OP(operator)	\
{					\
  OOP	oop2;				\
  oop2 = stackTop();			\
  if (isClass(oop2, floatClass)) {	\
    double farg1, farg2;		\
    farg1 = floatOOPValue(tempOOP);	\
    farg2 = floatOOPValue(oop2);	\
    popNOOPs(1);			\
    DO_JUMP_LOOKAHEAD(farg1 operator farg2); \
  }					\
}

#define INTERP_BASIC_BOOL(operator)	\
  REGISTER(3, OOP	tempOOP);	\
  tempOOP = stackAt(1);			\
  if (areInts(tempOOP, stackTop())) {	\
    RAW_BOOL_OP(operator);		\
  } else if (isClass(tempOOP, floatClass)) {	\
    RAW_BOOL_FLOAT_OP(operator);	\
  }

#endif

    BC(176, sendPlus)
      INTERP_BASIC_OP(iarg1 += iarg2, farg1 + farg2);
      exportRegs();
      sendMessage(plusSymbol, 1, false);
      importRegs();
    END(sendPlus);

    BC(177, sendMinus)
      INTERP_BASIC_OP(iarg1 -= iarg2, farg1 - farg2);
      exportRegs();
      sendMessage(minusSymbol, 1, false);
      importRegs();
    END(sendMinus);

    BC(178, sendLess)
      INTERP_BASIC_BOOL(<);
      exportRegs();
      sendMessage(lessThanSymbol, 1, false);
      importRegs();
    END(sendLess);

    BC(179, sendGreater)
      INTERP_BASIC_BOOL(>);
      exportRegs();
      sendMessage(greaterThanSymbol, 1, false);
      importRegs();
    END(sendGreater);

    BC(180, sendLessEq)
      INTERP_BASIC_BOOL(<=);
      exportRegs();
      sendMessage(lessEqualSymbol, 1, false);
      importRegs();
    END(sendLessEq);

    BC(181, sendGreaterEq)
      INTERP_BASIC_BOOL(>=);
      exportRegs();
      sendMessage(greaterEqualSymbol, 1, false);
      importRegs();
    END(sendGreaterEq);

    BC(182, sendEqual)
      INTERP_BASIC_BOOL(==);
      exportRegs();
      sendMessage(equalSymbol, 1, false);
      importRegs();
    END(sendEqual);

    BC(183, sendNotEqual)
      INTERP_BASIC_BOOL(!=);
      exportRegs();
      sendMessage(notEqualSymbol, 1, false);
      importRegs();
    END(sendNotEqual);

    BC(184, sendTimes)
      INTERP_BASIC_OP(iarg1 = mulWithCheck(iarg1, iarg2), farg1 * farg2);
      exportRegs();
      sendMessage(timesSymbol, 1, false);
      importRegs();
    END(sendTimes);

    BC(185, sendDivide)
      exportRegs();
      sendMessage(divideSymbol, 1, false);
      importRegs();
    END(sendDivide);

    BC(186, sendRemainder)
      exportRegs();
      sendMessage(remainderSymbol, 1, false);
      importRegs();
    END(sendRemainder);

    BC(187, sendAtSign)
      exportRegs();
      sendMessage(internString("@"), 1, false);
      importRegs();
    END(sendAtSign);

    BC(188, sendBitShift)
      REGISTER(3, OOP	tempOOP);
      tempOOP = stackAt(1);
      if (isInt(tempOOP)) {
	register long	iarg1, iarg2;
	if (isInt(stackTop())) {
	  iarg1 = toInt(tempOOP);
	  iarg2 = toInt(popOOP());
	  if (iarg2 < 0) {
	    setStackTopInt(iarg1 >> -iarg2);
	    NEXT_BYTECODE;
	  }
	  if (iarg2 < ST_INT_SIZE) {
	    register long result = iarg1 << iarg2;
	    if ((result >> iarg2) == iarg1) {
	      setStackTopInt(result);
	      NEXT_BYTECODE;
	    }
	  }
	}
	unPop(1);
      }
      exportRegs();
      sendMessage(bitShiftColonSymbol, 1, false);
      importRegs();
    END(sendBitShift);

    BC(189, sendIntDivide)
      exportRegs();
      sendMessage(integerDivideSymbol, 1, false);
      importRegs();
    END(sendIntDivide);

    BC(190, sendBitAnd)
      REGISTER(3, OOP	tempOOP);
      tempOOP = stackAt(1);
      if (areInts(tempOOP, stackTop())) {
	RAW_INT_OP(iarg1 &= iarg2, true);
      } else {
	exportRegs();
	sendMessage(bitAndColonSymbol, 1, false);
	importRegs();
      }
    END(sendBitAnd);

    BC(191, sendBitOr)
      REGISTER(3, OOP	tempOOP);
      tempOOP = stackAt(1);
      if (areInts(tempOOP, stackTop())) {
	RAW_INT_OP(iarg1 |= iarg2, true);
      } else {
	exportRegs();
	sendMessage(bitOrColonSymbol, 1, false);
	importRegs();
      }
    END(sendBitOr);

    BC(192, sendAt)
      REGISTER(3, OOP	tempOOP);
      exportRegs();
      tempOOP = stackAt(1);
      if(isOOP(tempOOP) &&
	 atCacheClass == (tempOOP = oopClass(tempOOP)) &&
	 !executePrimitiveOperation(atCachePrim, 1, nil)) {
	importRegs();
	NEXT_BYTECODE;
      }

      /* Not the same class that is in the cache, or the primitive failed --
       * send the message, and modify the cache if the send is resolved to
       * a primitive. */
      lastPrimitive = 0;
      sendMessage(atColonSymbol, 1, false);
      if (lastPrimitive) {
	atCachePrim = lastPrimitive;
	atCacheClass = tempOOP;
      }
      importRegs();
    END(sendAt);

    BC(193, sendAtPut)
      REGISTER(3, OOP	tempOOP);
      exportRegs();
      tempOOP = stackAt(2);
      if(isOOP(tempOOP) &&
	 atPutCacheClass == (tempOOP = oopClass(tempOOP)) &&
	 !executePrimitiveOperation(atPutCachePrim, 2, nil)) {
	importRegs();
	NEXT_BYTECODE;
      }

      /* Not the same class that is in the cache, or the primitive failed --
       * send the message, and modify the cache if the send is resolved to
       * a primitive. */
      lastPrimitive = 0;
      sendMessage(atColonPutColonSymbol, 2, false);
      if (lastPrimitive) {
	atPutCachePrim = lastPrimitive;
	atPutCacheClass = tempOOP;
      }
      importRegs();
    END(sendAtPut);

    BC(194, sendSize)
      REGISTER(3, OOP	tempOOP);
      exportRegs();
      tempOOP = stackTop();
      if(isOOP(tempOOP) &&
	 sizeCacheClass == (tempOOP = oopClass(tempOOP)) &&
	 !executePrimitiveOperation(sizeCachePrim, 0, nil)) {
	importRegs();
	NEXT_BYTECODE;
      }

      /* Not the same class that is in the cache, or the primitive failed --
       * send the message, and modify the cache if the send is resolved to
       * a primitive. */
      lastPrimitive = 0;
      sendMessage(sizeSymbol, 0, false);
      if (lastPrimitive) {
	sizeCachePrim = lastPrimitive;
	sizeCacheClass = tempOOP;
      }
      importRegs();
    END(sendSize);

    BC(195, sendNext)
      exportRegs();
      sendMessage(nextSymbol, 0, false);
      importRegs();
    END(sendNext);

    BC(196, sendNextPut)
      exportRegs();
      sendMessage(nextPutColonSymbol, 1, false);
      importRegs();
    END(sendNextPut);

    BC(197, sendAtEnd)
      exportRegs();
      sendMessage(atEndSymbol, 0, false);
      importRegs();
    END(sendAtEnd);

    BC(198, sendSameObject)
      REGISTER(3, OOP	tempOOP);
      register OOP oop1;
      tempOOP = popOOP();
      oop1 = stackTop();
      DO_JUMP_LOOKAHEAD(oop1 == tempOOP);
    END(sendSameObject);

    BC(199, sendClass)
      REGISTER(3, OOP	tempOOP);
      tempOOP = stackTop();
      setStackTop(isInt(tempOOP) ? smallIntegerClass : oopClass(tempOOP));
    END(sendClass);

    BC(200, sendBlockCopy)
      REGISTER(3, OOP	tempOOP);
      exportRegs();
      tempOOP = stackAt(1);
      if (isOOP(tempOOP)) {
	tempOOP = oopClass(tempOOP);
	if ((tempOOP == blockClosureClass) &&
	    !executePrimitiveOperation(80, 1, nil)) {
	  importRegs();
	  NEXT_BYTECODE;
	}
      }
      sendMessage(blockCopyColonSymbol, 0, false);
      importRegs();
    END(sendBlockCopy);

    BC(201, sendValue)
      exportRegs();
      if (!isClass(stackTop(), blockClosureClass) ||
	  sendBlockValue(0)) {
	sendMessage(valueSymbol, 0, false);
      }
      importRegs();
    END(sendValue);

    BC(202, sendValueColon)
      exportRegs();
      if (!isClass(stackAt(1), blockClosureClass) ||
	  sendBlockValue(1)) {
	sendMessage(valueColonSymbol, 1, false);
      }
      importRegs();
    END(sendValueColon);

    BC(203, sendDoColon)
      exportRegs();
      sendMessage(doColonSymbol, 1, false);
      importRegs();
    END(sendDoColon);

    BC(204, sendNew)
      exportRegs();
      sendMessage(newSymbol, 0, false);
      importRegs();
    END(sendNew);

    BC(205, sendNewColon)
      exportRegs();
      sendMessage(newColonSymbol, 1, false);
      importRegs();
    END(sendNewColon);

    BC(206, sendIsNil)
      DO_JUMP_LOOKAHEAD(stackTop() == nilOOP);
    END(sendIsNil);

    BC(207, sendNotNil)
      DO_JUMP_LOOKAHEAD(stackTop() != nilOOP);
    END(sendNotNil);


#define sendLit(index, numArgs) \
  exportRegs();			\
  sendMessage(methodLiteral((index)), (numArgs), false); \
  importRegs()


    BC(208, sendNoArg0)	 sendLit(0, 0);	  END(sendNoArg0);
    BC(209, sendNoArg1)	 sendLit(1, 0);	  END(sendNoArg1);
    BC(210, sendNoArg2)	 sendLit(2, 0);	  END(sendNoArg2);
    BC(211, sendNoArg3)	 sendLit(3, 0);	  END(sendNoArg3);
    BC(212, sendNoArg4)	 sendLit(4, 0);	  END(sendNoArg4);
    BC(213, sendNoArg5)	 sendLit(5, 0);	  END(sendNoArg5);
    BC(214, sendNoArg6)	 sendLit(6, 0);	  END(sendNoArg6);
    BC(215, sendNoArg7)	 sendLit(7, 0);	  END(sendNoArg7);
    BC(216, sendNoArg8)	 sendLit(8, 0);	  END(sendNoArg8);
    BC(217, sendNoArg9)	 sendLit(9, 0);	  END(sendNoArg9);
    BC(218, sendNoArg10) sendLit(10, 0);  END(sendNoArg10);
    BC(219, sendNoArg11) sendLit(11, 0);  END(sendNoArg11);
    BC(220, sendNoArg12) sendLit(12, 0);  END(sendNoArg12);
    BC(221, sendNoArg13) sendLit(13, 0);  END(sendNoArg13);
    BC(222, sendNoArg14) sendLit(14, 0);  END(sendNoArg14);
    BC(223, sendNoArg15) sendLit(15, 0);  END(sendNoArg15);

    BC(224, sendOneArg0)  sendLit(0, 1);  END(sendOneArg0);
    BC(225, sendOneArg1)  sendLit(1, 1);  END(sendOneArg1);
    BC(226, sendOneArg2)  sendLit(2, 1);  END(sendOneArg2);
    BC(227, sendOneArg3)  sendLit(3, 1);  END(sendOneArg3);
    BC(228, sendOneArg4)  sendLit(4, 1);  END(sendOneArg4);
    BC(229, sendOneArg5)  sendLit(5, 1);  END(sendOneArg5);
    BC(230, sendOneArg6)  sendLit(6, 1);  END(sendOneArg6);
    BC(231, sendOneArg7)  sendLit(7, 1);  END(sendOneArg7);
    BC(232, sendOneArg8)  sendLit(8, 1);  END(sendOneArg8);
    BC(233, sendOneArg9)  sendLit(9, 1);  END(sendOneArg9);
    BC(234, sendOneArg10) sendLit(10, 1); END(sendOneArg10);
    BC(235, sendOneArg11) sendLit(11, 1); END(sendOneArg11);
    BC(236, sendOneArg12) sendLit(12, 1); END(sendOneArg12);
    BC(237, sendOneArg13) sendLit(13, 1); END(sendOneArg13);
    BC(238, sendOneArg14) sendLit(14, 1); END(sendOneArg14);
    BC(239, sendOneArg15) sendLit(15, 1); END(sendOneArg15);

    BC(240, sendTwoArg0)  sendLit(0, 2);  END(sendTwoArg0);
    BC(241, sendTwoArg1)  sendLit(1, 2);  END(sendTwoArg1);
    BC(242, sendTwoArg2)  sendLit(2, 2);  END(sendTwoArg2);
    BC(243, sendTwoArg3)  sendLit(3, 2);  END(sendTwoArg3);
    BC(244, sendTwoArg4)  sendLit(4, 2);  END(sendTwoArg4);
    BC(245, sendTwoArg5)  sendLit(5, 2);  END(sendTwoArg5);
    BC(246, sendTwoArg6)  sendLit(6, 2);  END(sendTwoArg6);
    BC(247, sendTwoArg7)  sendLit(7, 2);  END(sendTwoArg7);
    BC(248, sendTwoArg8)  sendLit(8, 2);  END(sendTwoArg8);
    BC(249, sendTwoArg9)  sendLit(9, 2);  END(sendTwoArg9);
    BC(250, sendTwoArg10) sendLit(10, 2); END(sendTwoArg10);
    BC(251, sendTwoArg11) sendLit(11, 2); END(sendTwoArg11);
    BC(252, sendTwoArg12) sendLit(12, 2); END(sendTwoArg12);
    BC(253, sendTwoArg13) sendLit(13, 2); END(sendTwoArg13);
    BC(254, sendTwoArg14) sendLit(14, 2); END(sendTwoArg14);
    BC(255, sendTwoArg15) sendLit(15, 2); END(sendTwoArg15);
#undef sendLit

#ifdef JUMP_LOOKAHEAD
    BRANCH_LABEL(lookaheadFailedTrue)
      uncheckedPushOOP(trueOOP);
      NEXT_BYTECODE;

    BRANCH_LABEL(lookaheadFailedFalse)
      uncheckedPushOOP(falseOOP);
      NEXT_BYTECODE;

    BRANCH_LABEL(lookaheadLongJumpNotTaken)
      byteCodeCounter++;
      ip += 2;
      NEXT_BYTECODE;
#endif

    } /* BYTECODES */
  } /* INTERPRETER */
} /* interpret() */


/* Always use outer ip/sp outside interpret */
#ifdef LOCAL_REGS
  #define ip		outerIP
  #define sp		outerSP
#endif
