/*******************************************************************
 *
 *  ttinterp.c                                              3.0
 *
 *  TrueType bytecode intepreter.
 *
 *  Copyright 1996-1998 by
 *  David Turner, Robert Wilhelm, and Werner Lemberg
 *
 *  This file is part of the FreeType project, and may only be used
 *  modified and distributed under the terms of the FreeType project
 *  license, LICENSE.TXT.  By continuing to use, modify, or distribute
 *  this file you indicate that you have read the license and
 *  understand and accept it fully.
 *
 *
 *  Changes between 3.0 and 2.3:
 *
 *  - Introduced the *big* switch statement to improve performance
 *    and code size, depending highly on the compiler you're using
 *
 *  - added an opcode counter in RunIns to detect infinite loop
 *    the function will return TT_Err_Execution_Too_Long
 *
 *  - some bug fixes and new obscure "features"
 *
 *
 *  Changes between 2.3 and 2.2:
 *
 *  - added support for rotation, stretching, instruction control
 *
 *  - added support for non-square pixels.  However, this doesn't
 *    work perfectly yet...
 *
 ******************************************************************/

#include "freetype.h"
#include "tttypes.h"
#include "ttdebug.h"
#include "ttcalc.h"
#include "ttmemory.h"
#include "ttinterp.h"

#ifdef DEBUG_INTERPRETER
#include <memory.h>
#include "ttdebug.h"

#ifdef HAVE_CONIO_H
#include <conio.h>
#endif

#endif /* DEBUG_INTEPRETER */


/* required by the tracing mode */
#undef  TT_COMPONENT
#define TT_COMPONENT      trace_interp


/* In order to detect infinite loops in the code, we set-up         */
/* a counter within the run loop. a singly stroke of interpretation */
/* is now limited to a maximum number of opcodes defined below..    */
/*                                                                  */
#define MAX_RUNNABLE_OPCODES  1000000


/* There are two kinds of implementations there:              */
/*                                                            */
/* a. static implementation:                                  */
/*                                                            */
/*    The current execution context is a static variable,     */
/*    which fields are accessed directly by the interpreter   */
/*    during execution.  The context is named 'cur'.          */
/*                                                            */
/*    This version is non-reentrant, of course.               */
/*                                                            */
/*                                                            */
/* b. indirect implementation:                                */
/*                                                            */
/*    The current execution context is passed to _each_       */
/*    function as its first argument, and each field is       */
/*    thus accessed indirectly.                               */
/*                                                            */
/*    This version is, however, fully re-entrant.             */
/*                                                            */
/*                                                            */
/*  The idea is that an indirect implementation may be        */
/*  slower to execute on the low-end processors that are      */
/*  used in some systems (like 386s or even 486s).            */
/*                                                            */
/*  When the interpreter started, we had no idea of the       */
/*  time that glyph hinting (i.e. executing instructions)     */
/*  could take in the whole process of rendering a glyph,     */
/*  and a 10 to 30% performance penalty on low-end systems    */
/*  didn't seem much of a good idea.  This question led us    */
/*  to provide two distinct builds of the C version from      */
/*  a single source, with the use of macros (again).          */
/*                                                            */
/*  Now that the engine is working (and working really        */
/*  well!), it seems that the greatest time-consuming         */
/*  factors are: file i/o, glyph loading, rasterizing and     */
/*  _then_ glyph hinting!                                     */
/*                                                            */
/*  Tests performed with two versions of the 'fttimer'        */
/*  program seem to indicate that hinting takes less than 5%  */
/*  of the rendering process, which is dominated by glyph     */
/*  loading and scan-line conversion by an high order of      */
/*  magnitude.                                                */
/*                                                            */
/*  As a consequence, the indirect implementation is now the  */
/*  default, as its performance costs can be considered       */
/*  negligible in our context. Note, however, that we         */
/*  kept the same source with macros because:                 */
/*                                                            */
/*    - the code is kept very close in design to the          */
/*      Pascal one used for development.                      */
/*                                                            */
/*    - it's much more readable that way!                     */
/*                                                            */
/*    - it's still open to later experimentation and tuning   */



#ifndef TT_STATIC_INTERPRETER      /* indirect implementation */

#define CUR (*exc)                 /* see ttobjs.h */

#else                              /* static implementation */

#define CUR cur

  static TExecution_Context  cur;  /* static exec. context variable */

  /* apparently, we have a _lot_ of direct indexing when accessing  */
  /* the static 'cur', which makes the code bigger (due to all the  */
  /* four bytes addresses).                                         */

#endif


#define INS_ARG         EXEC_OPS PStorage args  /* see ttexec.h */

#define SKIP_Code()     SkipCode( EXEC_ARG )

#define GET_ShortIns()  GetShortIns( EXEC_ARG )

#define COMPUTE_Funcs() Compute_Funcs( EXEC_ARG )

#define NORMalize( x, y, v )  Normalize( EXEC_ARGS x, y, v )

#define SET_SuperRound( scale, flags ) \
                        SetSuperRound( EXEC_ARGS scale, flags )

#define INS_Goto_CodeRange( range, ip ) \
                        Ins_Goto_CodeRange( EXEC_ARGS range, ip )

#define CUR_Func_project( x, y )   CUR.func_project( EXEC_ARGS x, y )
#define CUR_Func_move( z, p, d )   CUR.func_move( EXEC_ARGS z, p, d )
#define CUR_Func_dualproj( x, y )  CUR.func_dualproj( EXEC_ARGS x, y )
#define CUR_Func_freeProj( x, y )  CUR.func_freeProj( EXEC_ARGS x, y )
#define CUR_Func_round( d, c )     CUR.func_round( EXEC_ARGS d, c )

#define CUR_Func_read_cvt( index )       \
          CUR.func_read_cvt( EXEC_ARGS index )

#define CUR_Func_write_cvt( index, val ) \
          CUR.func_write_cvt( EXEC_ARGS index, val )

#define CUR_Func_move_cvt( index, val )  \
          CUR.func_move_cvt( EXEC_ARGS index, val )

#define CURRENT_Ratio()  Current_Ratio( EXEC_ARG )
#define CURRENT_Ppem()   Current_Ppem( EXEC_ARG )

#define CALC_Length()  Calc_Length( EXEC_ARG )

#define INS_SxVTL( a, b, c, d ) Ins_SxVTL( EXEC_ARGS a, b, c, d )

#define COMPUTE_Point_Displacement( a, b, c, d ) \
           Compute_Point_Displacement( EXEC_ARGS a, b, c, d )

#define MOVE_Zp2_Point( a, b, c, t )  Move_Zp2_Point( EXEC_ARGS a, b, c, t )

#define CUR_Ppem()  Cur_PPEM( EXEC_ARG )

  /* Instruction dispatch function, as used by the interpreter */
  typedef void  (*TInstruction_Function)( INS_ARG );

#define BOUNDS( x, n )  ( (x) >= (n) )



/*********************************************************************/
/*                                                                   */
/*  Before an opcode is executed, the interpreter verifies that      */
/*  there are enough arguments on the stack, with the help of        */
/*  the Pop_Push_Count table.                                        */
/*                                                                   */
/*  For each opcode, the first column gives the number of arguments  */
/*  that are popped from the stack; the second one gives the number  */
/*  of those that are pushed in result.                              */
/*                                                                   */
/*  Note that for opcodes with a varying number of parameters,       */
/*  either 0 or 1 arg is verified before execution, depending        */
/*  on the nature of the instruction:                                */
/*                                                                   */
/*   - if the number of arguments is given by the bytecode           */
/*     stream or the loop variable, 0 is chosen.                     */
/*                                                                   */
/*   - if the first argument is a count n that is followed           */
/*     by arguments a1..an, then 1 is chosen.                        */
/*                                                                   */
/*********************************************************************/

#undef  PACK
#define PACK( x, y )  ((x << 4) | y)

  static const Byte  Pop_Push_Count[256] =
  {
    /* opcodes are gathered in groups of 16 */
    /* please keep the spaces as they are   */

    /*  SVTCA  y  */  PACK( 0, 0 ),
    /*  SVTCA  x  */  PACK( 0, 0 ),
    /*  SPvTCA y  */  PACK( 0, 0 ),
    /*  SPvTCA x  */  PACK( 0, 0 ),
    /*  SFvTCA y  */  PACK( 0, 0 ),
    /*  SFvTCA x  */  PACK( 0, 0 ),
    /*  SPvTL //  */  PACK( 2, 0 ),
    /*  SPvTL +   */  PACK( 2, 0 ),
    /*  SFvTL //  */  PACK( 2, 0 ),
    /*  SFvTL +   */  PACK( 2, 0 ),
    /*  SPvFS     */  PACK( 2, 0 ),
    /*  SFvFS     */  PACK( 2, 0 ),
    /*  GPV       */  PACK( 0, 2 ),
    /*  GFV       */  PACK( 0, 2 ),
    /*  SFvTPv    */  PACK( 0, 0 ),
    /*  ISECT     */  PACK( 5, 0 ),

    /*  SRP0      */  PACK( 1, 0 ),
    /*  SRP1      */  PACK( 1, 0 ),
    /*  SRP2      */  PACK( 1, 0 ),
    /*  SZP0      */  PACK( 1, 0 ),
    /*  SZP1      */  PACK( 1, 0 ),
    /*  SZP2      */  PACK( 1, 0 ),
    /*  SZPS      */  PACK( 1, 0 ),
    /*  SLOOP     */  PACK( 1, 0 ),
    /*  RTG       */  PACK( 0, 0 ),
    /*  RTHG      */  PACK( 0, 0 ),
    /*  SMD       */  PACK( 1, 0 ),
    /*  ELSE      */  PACK( 0, 0 ),
    /*  JMPR      */  PACK( 1, 0 ),
    /*  SCvTCi    */  PACK( 1, 0 ),
    /*  SSwCi     */  PACK( 1, 0 ),
    /*  SSW       */  PACK( 1, 0 ),

    /*  DUP       */  PACK( 1, 2 ),
    /*  POP       */  PACK( 1, 0 ),
    /*  CLEAR     */  PACK( 0, 0 ),
    /*  SWAP      */  PACK( 2, 2 ),
    /*  DEPTH     */  PACK( 0, 1 ),
    /*  CINDEX    */  PACK( 1, 1 ),
    /*  MINDEX    */  PACK( 1, 0 ),
    /*  AlignPTS  */  PACK( 2, 0 ),
    /*  INS_$28   */  PACK( 0, 0 ),
    /*  UTP       */  PACK( 1, 0 ),
    /*  LOOPCALL  */  PACK( 2, 0 ),
    /*  CALL      */  PACK( 1, 0 ),
    /*  FDEF      */  PACK( 1, 0 ),
    /*  ENDF      */  PACK( 0, 0 ),
    /*  MDAP[0]   */  PACK( 1, 0 ),
    /*  MDAP[1]   */  PACK( 1, 0 ),

    /*  IUP[0]    */  PACK( 0, 0 ),
    /*  IUP[1]    */  PACK( 0, 0 ),
    /*  SHP[0]    */  PACK( 0, 0 ),
    /*  SHP[1]    */  PACK( 0, 0 ),
    /*  SHC[0]    */  PACK( 1, 0 ),
    /*  SHC[1]    */  PACK( 1, 0 ),
    /*  SHZ[0]    */  PACK( 1, 0 ),
    /*  SHZ[1]    */  PACK( 1, 0 ),
    /*  SHPIX     */  PACK( 1, 0 ),
    /*  IP        */  PACK( 0, 0 ),
    /*  MSIRP[0]  */  PACK( 2, 0 ),
    /*  MSIRP[1]  */  PACK( 2, 0 ),
    /*  AlignRP   */  PACK( 0, 0 ),
    /*  RTDG      */  PACK( 0, 0 ),
    /*  MIAP[0]   */  PACK( 2, 0 ),
    /*  MIAP[1]   */  PACK( 2, 0 ),

    /*  NPushB    */  PACK( 0, 0 ),
    /*  NPushW    */  PACK( 0, 0 ),
    /*  WS        */  PACK( 2, 0 ),
    /*  RS        */  PACK( 1, 1 ),
    /*  WCvtP     */  PACK( 2, 0 ),
    /*  RCvt      */  PACK( 1, 1 ),
    /*  GC[0]     */  PACK( 1, 1 ),
    /*  GC[1]     */  PACK( 1, 1 ),
    /*  SCFS      */  PACK( 2, 0 ),
    /*  MD[0]     */  PACK( 2, 1 ),
    /*  MD[1]     */  PACK( 2, 1 ),
    /*  MPPEM     */  PACK( 0, 1 ),
    /*  MPS       */  PACK( 0, 1 ),
    /*  FlipON    */  PACK( 0, 0 ),
    /*  FlipOFF   */  PACK( 0, 0 ),
    /*  DEBUG     */  PACK( 1, 0 ),

    /*  LT        */  PACK( 2, 1 ),
    /*  LTEQ      */  PACK( 2, 1 ),
    /*  GT        */  PACK( 2, 1 ),
    /*  GTEQ      */  PACK( 2, 1 ),
    /*  EQ        */  PACK( 2, 1 ),
    /*  NEQ       */  PACK( 2, 1 ),
    /*  ODD       */  PACK( 1, 1 ),
    /*  EVEN      */  PACK( 1, 1 ),
    /*  IF        */  PACK( 1, 0 ),
    /*  EIF       */  PACK( 0, 0 ),
    /*  AND       */  PACK( 2, 1 ),
    /*  OR        */  PACK( 2, 1 ),
    /*  NOT       */  PACK( 1, 1 ),
    /*  DeltaP1   */  PACK( 1, 0 ),
    /*  SDB       */  PACK( 1, 0 ),
    /*  SDS       */  PACK( 1, 0 ),

    /*  ADD       */  PACK( 2, 1 ),
    /*  SUB       */  PACK( 2, 1 ),
    /*  DIV       */  PACK( 2, 1 ),
    /*  MUL       */  PACK( 2, 1 ),
    /*  ABS       */  PACK( 1, 1 ),
    /*  NEG       */  PACK( 1, 1 ),
    /*  FLOOR     */  PACK( 1, 1 ),
    /*  CEILING   */  PACK( 1, 1 ),
    /*  ROUND[0]  */  PACK( 1, 1 ),
    /*  ROUND[1]  */  PACK( 1, 1 ),
    /*  ROUND[2]  */  PACK( 1, 1 ),
    /*  ROUND[3]  */  PACK( 1, 1 ),
    /*  NROUND[0] */  PACK( 1, 1 ),
    /*  NROUND[1] */  PACK( 1, 1 ),
    /*  NROUND[2] */  PACK( 1, 1 ),
    /*  NROUND[3] */  PACK( 1, 1 ),

    /*  WCvtF     */  PACK( 2, 0 ),
    /*  DeltaP2   */  PACK( 1, 0 ),
    /*  DeltaP3   */  PACK( 1, 0 ),
    /*  DeltaCn[0] */ PACK( 1, 0 ),
    /*  DeltaCn[1] */ PACK( 1, 0 ),
    /*  DeltaCn[2] */ PACK( 1, 0 ),
    /*  SROUND    */  PACK( 1, 0 ),
    /*  S45Round  */  PACK( 1, 0 ),
    /*  JROT      */  PACK( 2, 0 ),
    /*  JROF      */  PACK( 2, 0 ),
    /*  ROFF      */  PACK( 0, 0 ),
    /*  INS_$7B   */  PACK( 0, 0 ),
    /*  RUTG      */  PACK( 0, 0 ),
    /*  RDTG      */  PACK( 0, 0 ),
    /*  SANGW     */  PACK( 1, 0 ),
    /*  AA        */  PACK( 1, 0 ),

    /*  FlipPT    */  PACK( 0, 0 ),
    /*  FlipRgON  */  PACK( 2, 0 ),
    /*  FlipRgOFF */  PACK( 2, 0 ),
    /*  INS_$83   */  PACK( 0, 0 ),
    /*  INS_$84   */  PACK( 0, 0 ),
    /*  ScanCTRL  */  PACK( 1, 0 ),
    /*  SDVPTL[0] */  PACK( 2, 0 ),
    /*  SDVPTL[1] */  PACK( 2, 0 ),
    /*  GetINFO   */  PACK( 1, 1 ),
    /*  IDEF      */  PACK( 1, 0 ),
    /*  ROLL      */  PACK( 3, 3 ),
    /*  MAX       */  PACK( 2, 1 ),
    /*  MIN       */  PACK( 2, 1 ),
    /*  ScanTYPE  */  PACK( 1, 0 ),
    /*  InstCTRL  */  PACK( 2, 0 ),
    /*  INS_$8F   */  PACK( 0, 0 ),

    /*  INS_$90  */   PACK( 0, 0 ),
    /*  INS_$91  */   PACK( 0, 0 ),
    /*  INS_$92  */   PACK( 0, 0 ),
    /*  INS_$93  */   PACK( 0, 0 ),
    /*  INS_$94  */   PACK( 0, 0 ),
    /*  INS_$95  */   PACK( 0, 0 ),
    /*  INS_$96  */   PACK( 0, 0 ),
    /*  INS_$97  */   PACK( 0, 0 ),
    /*  INS_$98  */   PACK( 0, 0 ),
    /*  INS_$99  */   PACK( 0, 0 ),
    /*  INS_$9A  */   PACK( 0, 0 ),
    /*  INS_$9B  */   PACK( 0, 0 ),
    /*  INS_$9C  */   PACK( 0, 0 ),
    /*  INS_$9D  */   PACK( 0, 0 ),
    /*  INS_$9E  */   PACK( 0, 0 ),
    /*  INS_$9F  */   PACK( 0, 0 ),

    /*  INS_$A0  */   PACK( 0, 0 ),
    /*  INS_$A1  */   PACK( 0, 0 ),
    /*  INS_$A2  */   PACK( 0, 0 ),
    /*  INS_$A3  */   PACK( 0, 0 ),
    /*  INS_$A4  */   PACK( 0, 0 ),
    /*  INS_$A5  */   PACK( 0, 0 ),
    /*  INS_$A6  */   PACK( 0, 0 ),
    /*  INS_$A7  */   PACK( 0, 0 ),
    /*  INS_$A8  */   PACK( 0, 0 ),
    /*  INS_$A9  */   PACK( 0, 0 ),
    /*  INS_$AA  */   PACK( 0, 0 ),
    /*  INS_$AB  */   PACK( 0, 0 ),
    /*  INS_$AC  */   PACK( 0, 0 ),
    /*  INS_$AD  */   PACK( 0, 0 ),
    /*  INS_$AE  */   PACK( 0, 0 ),
    /*  INS_$AF  */   PACK( 0, 0 ),

    /*  PushB[0]  */  PACK( 0, 1 ),
    /*  PushB[1]  */  PACK( 0, 2 ),
    /*  PushB[2]  */  PACK( 0, 3 ),
    /*  PushB[3]  */  PACK( 0, 4 ),
    /*  PushB[4]  */  PACK( 0, 5 ),
    /*  PushB[5]  */  PACK( 0, 6 ),
    /*  PushB[6]  */  PACK( 0, 7 ),
    /*  PushB[7]  */  PACK( 0, 8 ),
    /*  PushW[0]  */  PACK( 0, 1 ),
    /*  PushW[1]  */  PACK( 0, 2 ),
    /*  PushW[2]  */  PACK( 0, 3 ),
    /*  PushW[3]  */  PACK( 0, 4 ),
    /*  PushW[4]  */  PACK( 0, 5 ),
    /*  PushW[5]  */  PACK( 0, 6 ),
    /*  PushW[6]  */  PACK( 0, 7 ),
    /*  PushW[7]  */  PACK( 0, 8 ),

    /*  MDRP[00]  */  PACK( 1, 0 ),
    /*  MDRP[01]  */  PACK( 1, 0 ),
    /*  MDRP[02]  */  PACK( 1, 0 ),
    /*  MDRP[03]  */  PACK( 1, 0 ),
    /*  MDRP[04]  */  PACK( 1, 0 ),
    /*  MDRP[05]  */  PACK( 1, 0 ),
    /*  MDRP[06]  */  PACK( 1, 0 ),
    /*  MDRP[07]  */  PACK( 1, 0 ),
    /*  MDRP[08]  */  PACK( 1, 0 ),
    /*  MDRP[09]  */  PACK( 1, 0 ),
    /*  MDRP[10]  */  PACK( 1, 0 ),
    /*  MDRP[11]  */  PACK( 1, 0 ),
    /*  MDRP[12]  */  PACK( 1, 0 ),
    /*  MDRP[13]  */  PACK( 1, 0 ),
    /*  MDRP[14]  */  PACK( 1, 0 ),
    /*  MDRP[15]  */  PACK( 1, 0 ),

    /*  MDRP[16]  */  PACK( 1, 0 ),
    /*  MDRP[17]  */  PACK( 1, 0 ),
    /*  MDRP[18]  */  PACK( 1, 0 ),
    /*  MDRP[19]  */  PACK( 1, 0 ),
    /*  MDRP[20]  */  PACK( 1, 0 ),
    /*  MDRP[21]  */  PACK( 1, 0 ),
    /*  MDRP[22]  */  PACK( 1, 0 ),
    /*  MDRP[23]  */  PACK( 1, 0 ),
    /*  MDRP[24]  */  PACK( 1, 0 ),
    /*  MDRP[25]  */  PACK( 1, 0 ),
    /*  MDRP[26]  */  PACK( 1, 0 ),
    /*  MDRP[27]  */  PACK( 1, 0 ),
    /*  MDRP[28]  */  PACK( 1, 0 ),
    /*  MDRP[29]  */  PACK( 1, 0 ),
    /*  MDRP[30]  */  PACK( 1, 0 ),
    /*  MDRP[31]  */  PACK( 1, 0 ),

    /*  MIRP[00]  */  PACK( 2, 0 ),
    /*  MIRP[01]  */  PACK( 2, 0 ),
    /*  MIRP[02]  */  PACK( 2, 0 ),
    /*  MIRP[03]  */  PACK( 2, 0 ),
    /*  MIRP[04]  */  PACK( 2, 0 ),
    /*  MIRP[05]  */  PACK( 2, 0 ),
    /*  MIRP[06]  */  PACK( 2, 0 ),
    /*  MIRP[07]  */  PACK( 2, 0 ),
    /*  MIRP[08]  */  PACK( 2, 0 ),
    /*  MIRP[09]  */  PACK( 2, 0 ),
    /*  MIRP[10]  */  PACK( 2, 0 ),
    /*  MIRP[11]  */  PACK( 2, 0 ),
    /*  MIRP[12]  */  PACK( 2, 0 ),
    /*  MIRP[13]  */  PACK( 2, 0 ),
    /*  MIRP[14]  */  PACK( 2, 0 ),
    /*  MIRP[15]  */  PACK( 2, 0 ),

    /*  MIRP[16]  */  PACK( 2, 0 ),
    /*  MIRP[17]  */  PACK( 2, 0 ),
    /*  MIRP[18]  */  PACK( 2, 0 ),
    /*  MIRP[19]  */  PACK( 2, 0 ),
    /*  MIRP[20]  */  PACK( 2, 0 ),
    /*  MIRP[21]  */  PACK( 2, 0 ),
    /*  MIRP[22]  */  PACK( 2, 0 ),
    /*  MIRP[23]  */  PACK( 2, 0 ),
    /*  MIRP[24]  */  PACK( 2, 0 ),
    /*  MIRP[25]  */  PACK( 2, 0 ),
    /*  MIRP[26]  */  PACK( 2, 0 ),
    /*  MIRP[27]  */  PACK( 2, 0 ),
    /*  MIRP[28]  */  PACK( 2, 0 ),
    /*  MIRP[29]  */  PACK( 2, 0 ),
    /*  MIRP[30]  */  PACK( 2, 0 ),
    /*  MIRP[31]  */  PACK( 2, 0 )
  };

  static  const  TT_Vector  Null_Vector = {0,0};

#undef  NULL_Vector
#define NULL_Vector (TT_Vector*)&Null_Vector

/*******************************************************************
 *
 *  Function    :  Norm
 *
 *  Description :  Returns the norm (length) of a vector.
 *
 *  Input  :  X, Y   vector
 *
 *  Output :  Returns length in F26dot6.
 *
 *****************************************************************/

  static TT_F26Dot6  Norm( TT_F26Dot6  X, TT_F26Dot6  Y )
  {
    TT_Int64       T1, T2;


    MUL_64( X, X, T1 );
    MUL_64( Y, Y, T2 );

    ADD_64( T1, T2, T1 );

    return (TT_F26Dot6)SQRT_64( T1 );
  }


/*******************************************************************
 *
 *  Function    :  FUnits_To_Pixels
 *
 *  Description :  Scale a distance in FUnits to pixel coordinates.
 *
 *  Input  :  Distance in FUnits
 *
 *  Output :  Distance in 26.6 format.
 *
 *****************************************************************/

  static TT_F26Dot6  FUnits_To_Pixels( EXEC_OPS Short  distance )
  {
    return TT_MulDiv( distance,
                      CUR.metrics.scale1,
                      CUR.metrics.scale2 );
  }


/*******************************************************************
 *
 *  Function    :  Current_Ratio
 *
 *  Description :  Return the current aspect ratio scaling factor
 *                 depending on the projection vector's state and
 *                 device resolutions.
 *
 *  Input  :  None
 *
 *  Output :  Aspect ratio in 16.16 format, always <= 1.0 .
 *
 *****************************************************************/

  static Long  Current_Ratio( EXEC_OP )
  {
    if ( CUR.metrics.ratio )
      return CUR.metrics.ratio;

    if ( CUR.GS.projVector.y == 0 )
      CUR.metrics.ratio = CUR.metrics.x_ratio;

    else if ( CUR.GS.projVector.x == 0 )
      CUR.metrics.ratio = CUR.metrics.y_ratio;

    else
    {
      Long  x, y;


      x = TT_MulDiv( CUR.GS.projVector.x, CUR.metrics.x_ratio, 0x4000 );
      y = TT_MulDiv( CUR.GS.projVector.y, CUR.metrics.y_ratio, 0x4000 );
      CUR.metrics.ratio = Norm( x, y );
    }

    return CUR.metrics.ratio;
  }


  static Long  Current_Ppem( EXEC_OP )
  {
    return TT_MulFix( CUR.metrics.ppem, CURRENT_Ratio() );
  }


  static TT_F26Dot6  Read_CVT( EXEC_OPS ULong  index )
  {
    return CUR.cvt[index];
  }


  static TT_F26Dot6  Read_CVT_Stretched( EXEC_OPS ULong  index )
  {
    return TT_MulFix( CUR.cvt[index], CURRENT_Ratio() );
  }


  static void  Write_CVT( EXEC_OPS ULong  index, TT_F26Dot6  value )
  {
    CUR.cvt[index] = value;
  }

  static void  Write_CVT_Stretched( EXEC_OPS ULong  index, TT_F26Dot6  value )
  {
    CUR.cvt[index] = TT_MulDiv( value, 0x10000, CURRENT_Ratio() );
  }


  static void  Move_CVT( EXEC_OPS  ULong  index, TT_F26Dot6  value )
  {
    CUR.cvt[index] += value;
  }

  static void  Move_CVT_Stretched( EXEC_OPS ULong  index, TT_F26Dot6  value )
  {
    CUR.cvt[index] += TT_MulDiv( value, 0x10000, CURRENT_Ratio() );
  }


/******************************************************************
 *
 *  Function    :  Calc_Length
 *
 *  Description :  Computes the length in bytes of current opcode.
 *
 *****************************************************************/

  static Bool  Calc_Length( EXEC_OP )
  {
    CUR.opcode = CUR.code[CUR.IP];

    switch ( CUR.opcode )
    {
    case 0x40:
      if ( CUR.IP + 1 >= CUR.codeSize )
        return FAILURE;

      CUR.length = CUR.code[CUR.IP + 1] + 2;
      break;

    case 0x41:
      if ( CUR.IP + 1 >= CUR.codeSize )
        return FAILURE;

      CUR.length = CUR.code[CUR.IP + 1] * 2 + 2;
      break;

    case 0xB0:
    case 0xB1:
    case 0xB2:
    case 0xB3:
    case 0xB4:
    case 0xB5:
    case 0xB6:
    case 0xB7:
      CUR.length = CUR.opcode - 0xB0 + 2;
      break;

    case 0xB8:
    case 0xB9:
    case 0xBA:
    case 0xBB:
    case 0xBC:
    case 0xBD:
    case 0xBE:
    case 0xBF:
      CUR.length = (CUR.opcode - 0xB8) * 2 + 3;
      break;

    default:
      CUR.length = 1;
      break;
    }

    /* make sure result is in range */

    if ( CUR.IP + CUR.length > CUR.codeSize )
      return FAILURE;

    return SUCCESS;
  }


/*******************************************************************
 *
 *  Function    :  GetShortIns
 *
 *  Description :  Returns a short integer taken from the instruction
 *                 stream at address IP.
 *
 *  Input  :  None
 *
 *  Output :  Short read at Code^[IP..IP+1]
 *
 *  Notes  :  This one could become a Macro in the C version.
 *
 *****************************************************************/

  static Short  GetShortIns( EXEC_OP )
  {
    /* Reading a byte stream so there is no endianess (DaveP) */
    CUR.IP += 2;
    return (Short)((CUR.code[CUR.IP - 2] << 8) + CUR.code[CUR.IP - 1]);
  }


/*******************************************************************
 *
 *  Function    :  Ins_Goto_CodeRange
 *
 *  Description :  Goes to a certain code range in the instruction
 *                 stream.
 *
 *
 *  Input  :  aRange
 *            aIP
 *
 *  Output :  SUCCESS or FAILURE.
 *
 *****************************************************************/

  static Bool  Ins_Goto_CodeRange( EXEC_OPS Int  aRange, ULong  aIP )
  {
    TCodeRange*  WITH;


    if ( aRange < 1 || aRange > 3 )
    {
      CUR.error = TT_Err_Bad_Argument;
      return FAILURE;
    }

    WITH = &CUR.codeRangeTable[aRange - 1];

    if ( WITH->Base == NULL )     /* invalid coderange */
    {
      CUR.error = TT_Err_Invalid_CodeRange;
      return FAILURE;
    }

    /* NOTE: Because the last instruction of a program may be a CALL */
    /*       which will return to the first byte *after* the code    */
    /*       range, we test for AIP <= Size, instead of AIP < Size.  */

    if ( aIP > WITH->Size )
    {
      CUR.error = TT_Err_Code_Overflow;
      return FAILURE;
    }

    CUR.code     = WITH->Base;
    CUR.codeSize = WITH->Size;
    CUR.IP       = aIP;
    CUR.curRange = aRange;

    return SUCCESS;
  }


/*******************************************************************
 *
 *  Function    :  Direct_Move
 *
 *  Description :  Moves a point by a given distance along the
 *                 freedom vector.
 *
 *  Input  : Vx, Vy      point coordinates to move
 *           touch       touch flag to modify
 *           distance
 *
 *  Output :  None
 *
 *****************************************************************/

  static void  Direct_Move( EXEC_OPS PGlyph_Zone zone,
                                     UShort      point,
                                     TT_F26Dot6  distance )
  {
    TT_F26Dot6 v;


    v = CUR.GS.freeVector.x;

    if ( v != 0 )
    {
      zone->cur[point].x += TT_MulDiv( distance,
                                       v * 0x10000L,
                                       CUR.F_dot_P );

      zone->touch[point] |= TT_Flag_Touched_X;
    }

    v = CUR.GS.freeVector.y;

    if ( v != 0 )
    {
      zone->cur[point].y += TT_MulDiv( distance,
                                       v * 0x10000L,
                                       CUR.F_dot_P );

      zone->touch[point] |= TT_Flag_Touched_Y;
    }
  }


/******************************************************************/
/*                                                                */
/* The following versions are used whenever both vectors are both */
/* along one of the coordinate unit vectors, i.e. in 90% cases.   */
/*                                                                */
/******************************************************************/

/*******************************************************************
 * Direct_Move_X
 *
 *******************************************************************/

  static void  Direct_Move_X( EXEC_OPS PGlyph_Zone  zone,
                                       UShort       point,
                                       TT_F26Dot6   distance )
  {
    zone->cur[point].x += distance;
    zone->touch[point] |= TT_Flag_Touched_X;
  }


/*******************************************************************
 * Direct_Move_Y
 *
 *******************************************************************/

  static void  Direct_Move_Y( EXEC_OPS PGlyph_Zone  zone,
                                       UShort       point,
                                       TT_F26Dot6   distance )
  {
    zone->cur[point].y += distance;
    zone->touch[point] |= TT_Flag_Touched_Y;
  }


/*******************************************************************
 *
 *  Function    :  Round_None
 *
 *  Description :  Does not round, but adds engine compensation.
 *
 *  Input  :  distance      : distance to round
 *            compensation  : engine compensation
 *
 *  Output :  rounded distance.
 *
 *  NOTE : The spec says very few about the relationship between
 *         rounding and engine compensation.  However, it seems
 *         from the description of super round that we should
 *         should add the compensation before rounding.
 *
 ******************************************************************/

  static TT_F26Dot6  Round_None( EXEC_OPS TT_F26Dot6  distance,
                                          TT_F26Dot6  compensation )
  {
    TT_F26Dot6  val;


    if ( distance >= 0 )
    {
      val = distance + compensation;
      if ( val < 0 )
        val = 0;
    }
    else {
      val = distance - compensation;
      if ( val > 0 )
        val = 0;
    }

    return val;
  }


/*******************************************************************
 *
 *  Function    :  Round_To_Grid
 *
 *  Description :  Rounds value to grid after adding engine
 *                 compensation
 *
 *  Input  :  distance      : distance to round
 *            compensation  : engine compensation
 *
 *  Output :  Rounded distance.
 *
 *****************************************************************/

  static TT_F26Dot6  Round_To_Grid( EXEC_OPS TT_F26Dot6  distance,
                                             TT_F26Dot6  compensation )
  {
    TT_F26Dot6  val;


    if ( distance >= 0 )
    {
      val = distance + compensation + 32;
      if ( val > 0 )
        val &= ~63;
      else
        val = 0;
    }
    else
    {
      val = -( (compensation - distance + 32) & (-64) );
      if ( val > 0 )
        val = 0;
    }

    return  val;
  }


/*******************************************************************
 *
 *  Function    :  Round_To_Half_Grid
 *
 *  Description :  Rounds value to half grid after adding engine
 *                 compensation.
 *
 *  Input  :  distance      : distance to round
 *            compensation  : engine compensation
 *
 *  Output :  Rounded distance.
 *
 *****************************************************************/

  static TT_F26Dot6  Round_To_Half_Grid( EXEC_OPS TT_F26Dot6  distance,
                                                  TT_F26Dot6  compensation )
  {
    TT_F26Dot6  val;


    if ( distance >= 0 )
    {
      val = ((distance + compensation) & (-64)) + 32;
      if ( val < 0 )
        val = 0;
    }
    else
    {
      val = -( ((compensation - distance) & (-64)) + 32 );
      if ( val > 0 )
        val = 0;
    }

    return val;
  }


/*******************************************************************
 *
 *  Function    :  Round_Down_To_Grid
 *
 *  Description :  Rounds value down to grid after adding engine
 *                 compensation.
 *
 *  Input  :  distance      : distance to round
 *            compensation  : engine compensation
 *
 *  Output :  Rounded distance.
 *
 *****************************************************************/

  static TT_F26Dot6  Round_Down_To_Grid( EXEC_OPS TT_F26Dot6  distance,
                                                  TT_F26Dot6  compensation )
  {
    TT_F26Dot6  val;


    if ( distance >= 0 )
    {
      val = distance + compensation;
      if ( val > 0 )
        val &= ~63;
      else
        val = 0;
    }
    else
    {
      val = -( (compensation - distance) & (-64) );
      if ( val > 0 )
        val = 0;
    }

    return val;
  }


/*******************************************************************
 *
 *  Function    :  Round_Up_To_Grid
 *
 *  Description :  Rounds value up to grid after adding engine
 *                 compensation.
 *
 *  Input  :  distance      : distance to round
 *            compensation  : engine compensation
 *
 *  Output :  Rounded distance.
 *
 *****************************************************************/

  static TT_F26Dot6  Round_Up_To_Grid( EXEC_OPS TT_F26Dot6  distance,
                                                TT_F26Dot6  compensation )
  {
    TT_F26Dot6  val;


    if ( distance >= 0 )
    {
      val = distance + compensation + 63;
      if ( val > 0 )
        val &= ~63;
      else
        val = 0;
    }
    else
    {
      val = -( (compensation - distance + 63) & (-64) );
      if ( val > 0 )
        val = 0;
    }

    return val;
  }


/*******************************************************************
 *
 *  Function    :  Round_To_Double_Grid
 *
 *  Description :  Rounds value to double grid after adding engine
 *                 compensation.
 *
 *  Input  :  distance      : distance to round
 *            compensation  : engine compensation
 *
 *  Output :  Rounded distance.
 *
 *****************************************************************/

  static TT_F26Dot6  Round_To_Double_Grid( EXEC_OPS TT_F26Dot6  distance,
                                                    TT_F26Dot6  compensation )
  {
    TT_F26Dot6 val;


    if ( distance >= 0 )
    {
      val = distance + compensation + 16;
      if ( val > 0 )
        val &= ~31;
      else
        val = 0;
    }
    else
    {
      val = -( (compensation - distance + 16) & (-32) );
      if ( val > 0 )
        val = 0;
    }

    return val;
  }


/*******************************************************************
 *
 *  Function    :  Round_Super
 *
 *  Description :  Super-rounds value to grid after adding engine
 *                 compensation.
 *
 *  Input  :  distance      : distance to round
 *            compensation  : engine compensation
 *
 *  Output :  Rounded distance.
 *
 *  NOTE : The spec says very few about the relationship between
 *         rounding and engine compensation.  However, it seems
 *         from the description of super round that we should
 *         should add the compensation before rounding.
 *
 *****************************************************************/

  static TT_F26Dot6  Round_Super( EXEC_OPS TT_F26Dot6  distance,
                                           TT_F26Dot6  compensation )
  {
    TT_F26Dot6  val;


    if ( distance >= 0 )
    {
      val = (distance - CUR.phase + CUR.threshold + compensation) &
              (-CUR.period);
      if ( val < 0 )
        val = 0;
      val += CUR.phase;
    }
    else
    {
      val = -( (CUR.threshold - CUR.phase - distance + compensation) &
               (-CUR.period) );
      if ( val > 0 )
        val = 0;
      val -= CUR.phase;
    }

    return val;
  }


/*******************************************************************
 *
 *  Function    :  Round_Super_45
 *
 *  Description :  Super-rounds value to grid after adding engine
 *                 compensation.
 *
 *  Input  :  distance      : distance to round
 *            compensation  : engine compensation
 *
 *  Output :  Rounded distance.
 *
 *  NOTE : There is a separate function for Round_Super_45 as we
 *         may need a greater precision.
 *
 *****************************************************************/

  static TT_F26Dot6  Round_Super_45( EXEC_OPS TT_F26Dot6  distance,
                                              TT_F26Dot6  compensation )
  {
    TT_F26Dot6  val;


    if ( distance >= 0 )
    {
      val = ( (distance - CUR.phase + CUR.threshold + compensation) /
                CUR.period ) * CUR.period;
      if ( val < 0 )
        val = 0;
      val += CUR.phase;
    }
    else
    {
      val = -( ( (CUR.threshold - CUR.phase - distance + compensation) /
                   CUR.period ) * CUR.period );
      if ( val > 0 )
        val = 0;
      val -= CUR.phase;
    }

    return val;
  }


/*******************************************************************
 * Compute_Round
 *
 *****************************************************************/

  static void  Compute_Round( EXEC_OPS Byte  round_mode )
  {
    switch ( round_mode )
    {
    case TT_Round_Off:
      CUR.func_round = (TRound_Function)Round_None;
      break;

    case TT_Round_To_Grid:
      CUR.func_round = (TRound_Function)Round_To_Grid;
      break;

    case TT_Round_Up_To_Grid:
      CUR.func_round = (TRound_Function)Round_Up_To_Grid;
      break;

    case TT_Round_Down_To_Grid:
      CUR.func_round = (TRound_Function)Round_Down_To_Grid;
      break;

    case TT_Round_To_Half_Grid:
      CUR.func_round = (TRound_Function)Round_To_Half_Grid;
      break;

    case TT_Round_To_Double_Grid:
      CUR.func_round = (TRound_Function)Round_To_Double_Grid;
      break;

    case TT_Round_Super:
      CUR.func_round = (TRound_Function)Round_Super;
      break;

    case TT_Round_Super_45:
      CUR.func_round = (TRound_Function)Round_Super_45;
      break;
    }
  }


/*******************************************************************
 *
 *  Function    :  SetSuperRound
 *
 *  Description :  Sets Super Round parameters.
 *
 *  Input  :  GridPeriod   Grid period
 *            OpCode       SROUND opcode
 *
 *  Output :  None.
 *
 *****************************************************************/

  static void  SetSuperRound( EXEC_OPS TT_F26Dot6  GridPeriod,
                                       Long        selector )
  {
    switch ( selector & 0xC0 )
    {
      case 0:
        CUR.period = GridPeriod / 2;
        break;

      case 0x40:
        CUR.period = GridPeriod;
        break;

      case 0x80:
        CUR.period = GridPeriod * 2;
        break;

      /* This opcode is reserved, but... */

      case 0xC0:
        CUR.period = GridPeriod;
        break;
    }

    switch ( selector & 0x30 )
    {
    case 0:
      CUR.phase = 0;
      break;

    case 0x10:
      CUR.phase = CUR.period / 4;
      break;

    case 0x20:
      CUR.phase = CUR.period / 2;
      break;

    case 0x30:
      CUR.phase = GridPeriod * 3 / 4;
      break;
    }

    if ( (selector & 0x0F) == 0 )
      CUR.threshold = CUR.period - 1;
    else
      CUR.threshold = ( (Int)(selector & 0x0F) - 4L ) * CUR.period / 8;

    CUR.period    /= 256;
    CUR.phase     /= 256;
    CUR.threshold /= 256;
  }


/*******************************************************************
 *
 *  Function    :  Project
 *
 *  Description :  Computes the projection of (Vx,Vy) along the
 *                 current projection vector.
 *
 *  Input  :  Vx, Vy    input vector
 *
 *  Output :  Returns distance in F26dot6.
 *
 *****************************************************************/

  static TT_F26Dot6  Project( EXEC_OPS TT_Vector*  v1,
                                       TT_Vector*  v2 )
  {
    TT_Int64  T1, T2;


    MUL_64( v1->x - v2->x, CUR.GS.projVector.x, T1 );
    MUL_64( v1->y - v2->y, CUR.GS.projVector.y, T2 );

    ADD_64( T1, T2, T1 );

    return (TT_F26Dot6)DIV_64( T1, 0x4000L );
  }


/*******************************************************************
 *
 *  Function    :  Dual_Project
 *
 *  Description :  Computes the projection of (Vx,Vy) along the
 *                 current dual vector.
 *
 *  Input  :  Vx, Vy    input vector
 *
 *  Output :  Returns distance in F26dot6.
 *
 *****************************************************************/

  static TT_F26Dot6  Dual_Project( EXEC_OPS TT_Vector*  v1,
                                            TT_Vector*  v2 )
  {
    TT_Int64  T1, T2;


    MUL_64( v1->x - v2->x, CUR.GS.dualVector.x, T1 );
    MUL_64( v1->y - v2->y, CUR.GS.dualVector.y, T2 );

    ADD_64( T1, T2, T1 );

    return (TT_F26Dot6)DIV_64( T1, 0x4000L );
  }


/*******************************************************************
 *
 *  Function    :  Free_Project
 *
 *  Description :  Computes the projection of (Vx,Vy) along the
 *                 current freedom vector.
 *
 *  Input  :  Vx, Vy    input vector
 *
 *  Output :  Returns distance in F26dot6.
 *
 *****************************************************************/

  static TT_F26Dot6  Free_Project( EXEC_OPS TT_Vector*  v1,
                                            TT_Vector*  v2 )
  {
    TT_Int64  T1, T2;


    MUL_64( v1->x - v2->x, CUR.GS.freeVector.x, T1 );
    MUL_64( v1->y - v2->y, CUR.GS.freeVector.y, T2 );

    ADD_64( T1, T2, T1 );

    return (TT_F26Dot6)DIV_64( T1, 0x4000L );
  }


/*******************************************************************
 *
 *  Function    :  Project_x
 *
 *  Input  :  Vx, Vy    input vector
 *
 *  Output :  Returns Vx.
 *
 *  Note :    Used as a dummy function.
 *
 *****************************************************************/

  static TT_F26Dot6  Project_x( EXEC_OPS TT_Vector*  v1,
                                         TT_Vector*  v2 )
  {
    return (v1->x - v2->x);
  }


/*******************************************************************
 *
 *  Function    :  Project_y
 *
 *  Input  :  Vx, Vy    input vector
 *
 *  Output :  Returns Vy.
 *
 *  Note :    Used as a dummy function.
 *
 *****************************************************************/

  static TT_F26Dot6  Project_y( EXEC_OPS TT_Vector*  v1,
                                         TT_Vector*  v2 )
  {
    return (v1->y - v2->y);
  }


/*******************************************************************
 *
 *  Function    :  Compute_Funcs
 *
 *  Description :  Computes the projections and movement function
 *                 pointers according to the current graphics state.
 *
 *  Input  :  None
 *
 *****************************************************************/

  static void  Compute_Funcs( EXEC_OP )
  {
    if ( CUR.GS.freeVector.x == 0x4000 )
    {
      CUR.func_freeProj = (TProject_Function)Project_x;
      CUR.F_dot_P       = CUR.GS.projVector.x * 0x10000L;
    }
    else
    {
      if ( CUR.GS.freeVector.y == 0x4000 )
      {
        CUR.func_freeProj = (TProject_Function)Project_y;
        CUR.F_dot_P       = CUR.GS.projVector.y * 0x10000L;
      }
      else
      {
        CUR.func_freeProj = (TProject_Function)Free_Project;
        CUR.F_dot_P = (Long)CUR.GS.projVector.x * CUR.GS.freeVector.x * 4 +
                      (Long)CUR.GS.projVector.y * CUR.GS.freeVector.y * 4;
      }
    }

    CUR.cached_metrics = FALSE;

    if ( CUR.GS.projVector.x == 0x4000 )
      CUR.func_project = (TProject_Function)Project_x;
    else
    {
      if ( CUR.GS.projVector.y == 0x4000 )
        CUR.func_project = (TProject_Function)Project_y;
      else
        CUR.func_project = (TProject_Function)Project;
    }

    if ( CUR.GS.dualVector.x == 0x4000 )
      CUR.func_dualproj = (TProject_Function)Project_x;
    else
    {
      if ( CUR.GS.dualVector.y == 0x4000 )
        CUR.func_dualproj = (TProject_Function)Project_y;
      else
        CUR.func_dualproj = (TProject_Function)Dual_Project;
    }

    CUR.func_move = (TMove_Function)Direct_Move;

    if ( CUR.F_dot_P == 0x40000000L )
    {
      if ( CUR.GS.freeVector.x == 0x4000 )
        CUR.func_move = (TMove_Function)Direct_Move_X;
      else
      {
        if ( CUR.GS.freeVector.y == 0x4000 )
          CUR.func_move = (TMove_Function)Direct_Move_Y;
      }
    }

    /* at small sizes, F_dot_P can become too small, resulting   */
    /* in overflows and 'spikes' in a number of glyphs like 'w'. */

    if ( ABS( CUR.F_dot_P ) < 0x4000000L )
      CUR.F_dot_P = 0x40000000L;

    /* Disable cached aspect ratio */
    CUR.metrics.ratio = 0;
  }


/*******************************************************************
 *
 *  Function    :  Normalize
 *
 *  Description :  Norms a vector
 *
 *  Input  :  Vx, Vy    input vector
 *            R         unit vector
 *
 *  Output :  Returns FAILURE if a vector parameter is zero.
 *
 *****************************************************************/

  static Bool  Normalize( EXEC_OPS TT_F26Dot6      Vx,
                                   TT_F26Dot6      Vy,
                                   TT_UnitVector*  R )
  {
    TT_F26Dot6  W;
    Bool        S1, S2;


    if ( ABS( Vx ) < 0x10000L && ABS( Vy ) < 0x10000L )
    {
      Vx *= 0x100;
      Vy *= 0x100;

      W = Norm( Vx, Vy );

      if ( W == 0 )
      {
        /* XXX : UNDOCUMENTED! It seems that it's possible to try  */
        /*       to normalize the vector (0,0). Return immediately */
        return SUCCESS;
      }

      R->x = (TT_F2Dot14)TT_MulDiv( Vx, 0x4000L, W );
      R->y = (TT_F2Dot14)TT_MulDiv( Vy, 0x4000L, W );

      return SUCCESS;
    }

    W = Norm( Vx, Vy );

    Vx = TT_MulDiv( Vx, 0x4000L, W );
    Vy = TT_MulDiv( Vy, 0x4000L, W );

    W = Vx * Vx + Vy * Vy;

    /* Now, we want that Sqrt( W ) = 0x4000 */
    /* Or 0x1000000 <= W < 0x1004000        */

    if ( Vx < 0 )
    {
      Vx = -Vx;
      S1 = TRUE;
    }
    else
      S1 = FALSE;

    if ( Vy < 0 )
    {
      Vy = -Vy;
      S2 = TRUE;
    }
    else
      S2 = FALSE;

    while ( W < 0x1000000L )
    {
      /* We need to increase W, by a minimal amount */
      if ( Vx < Vy )
        Vx++;
      else
        Vy++;

      W = Vx * Vx + Vy * Vy;
    }

    while ( W >= 0x1004000L )
    {
      /* We need to decrease W, by a minimal amount */
      if ( Vx < Vy )
        Vx--;
      else
        Vy--;

      W = Vx * Vx + Vy * Vy;
    }

    /* Note that in various cases, we can only  */
    /* compute a Sqrt(W) of 0x3FFF, eg. Vx = Vy */

    if ( S1 )
      Vx = -Vx;

    if ( S2 )
      Vy = -Vy;

    R->x = (TT_F2Dot14)Vx;   /* Type conversion */
    R->y = (TT_F2Dot14)Vy;   /* Type conversion */

    return SUCCESS;
  }





  static Bool  Ins_SxVTL( EXEC_OPS  UShort          aIdx1,
                                    UShort          aIdx2,
                                    Int             aOpc,
                                    TT_UnitVector*  Vec )
  {
    Long       A, B, C;
    TT_Vector* p1;
    TT_Vector* p2;


    if ( BOUNDS( aIdx1, CUR.zp2.n_points ) ||
         BOUNDS( aIdx2, CUR.zp1.n_points ) )
    {
      CUR.error = TT_Err_Invalid_Reference;
      return FAILURE;
    }

    p1 = CUR.zp1.cur + aIdx2;
    p2 = CUR.zp2.cur + aIdx1;

    A = p1->x - p2->x;
    B = p1->y - p2->y;

    if ( (aOpc & 1) != 0 )
    {
      C =  B;   /* CounterClockwise rotation */
      B =  A;
      A = -C;
    }

    NORMalize( A, B, Vec );
    return SUCCESS;
  }


/* When not using the big switch statements, the interpreter uses a */
/* call table defined later below in this source.  Each opcode must */
/* thus have a corresponding function, even trivial ones.           */
/*                                                                  */
/* They're all defined there.                                       */

#define DO_SVTCA                       \
  {                                    \
    Short  A, B;                       \
                                       \
                                       \
    A = (Short)(CUR.opcode & 1) << 14; \
    B = A ^ (Short)0x4000;             \
                                       \
    CUR.GS.freeVector.x = A;           \
    CUR.GS.projVector.x = A;           \
    CUR.GS.dualVector.x = A;           \
                                       \
    CUR.GS.freeVector.y = B;           \
    CUR.GS.projVector.y = B;           \
    CUR.GS.dualVector.y = B;           \
                                       \
    COMPUTE_Funcs();                   \
  }


#define DO_SPVTCA                      \
  {                                    \
    Short  A, B;                       \
                                       \
                                       \
    A = (Short)(CUR.opcode & 1) << 14; \
    B = A ^ (Short)0x4000;             \
                                       \
    CUR.GS.projVector.x = A;           \
    CUR.GS.dualVector.x = A;           \
                                       \
    CUR.GS.projVector.y = B;           \
    CUR.GS.dualVector.y = B;           \
                                       \
    COMPUTE_Funcs();                   \
  }


#define DO_SFVTCA                      \
  {                                    \
    Short  A, B;                       \
                                       \
                                       \
    A = (Short)(CUR.opcode & 1) << 14; \
    B = A ^ (Short)0x4000;             \
                                       \
    CUR.GS.freeVector.x = A;           \
    CUR.GS.freeVector.y = B;           \
                                       \
    COMPUTE_Funcs();                   \
  }


#define DO_SPVTL                                     \
    if ( INS_SxVTL( args[1],                         \
                    args[0],                         \
                    CUR.opcode,                      \
                    &CUR.GS.projVector) == SUCCESS ) \
    {                                                \
      CUR.GS.dualVector = CUR.GS.projVector;         \
      COMPUTE_Funcs();                               \
    }

#define DO_SFVTL                                     \
    if ( INS_SxVTL( (Int)(args[1]),                  \
                    (Int)(args[0]),                  \
                    CUR.opcode,                      \
                    &CUR.GS.freeVector) == SUCCESS ) \
      COMPUTE_Funcs();


#define DO_SFVTPV                          \
    CUR.GS.freeVector = CUR.GS.projVector; \
    COMPUTE_Funcs();


#define DO_SPVFS                                \
  {                                             \
    Short  S;                                   \
    Long   X, Y;                                \
                                                \
                                                \
    /* Only use low 16bits, then sign extend */ \
    S = (Short)args[1];                         \
    Y = (Long)S;                                \
    S = (Short)args[0];                         \
    X = (Long)S;                                \
                                                \
    NORMalize( X, Y, &CUR.GS.projVector );      \
                                                \
    CUR.GS.dualVector = CUR.GS.projVector;      \
    COMPUTE_Funcs();                            \
  }


#define DO_SFVFS                                \
  {                                             \
    Short  S;                                   \
    Long   X, Y;                                \
                                                \
                                                \
    /* Only use low 16bits, then sign extend */ \
    S = (Short)args[1];                         \
    Y = (Long)S;                                \
    S = (Short)args[0];                         \
    X = S;                                      \
                                                \
    NORMalize( X, Y, &CUR.GS.freeVector );      \
    COMPUTE_Funcs();                            \
  }


#define DO_GPV                     \
    args[0] = CUR.GS.projVector.x; \
    args[1] = CUR.GS.projVector.y;


#define DO_GFV                     \
    args[0] = CUR.GS.freeVector.x; \
    args[1] = CUR.GS.freeVector.y;


#define DO_SRP0  \
    CUR.GS.rp0 = (UShort)(args[0]);


#define DO_SRP1  \
    CUR.GS.rp1 = (UShort)(args[0]);


#define DO_SRP2  \
    CUR.GS.rp2 = (UShort)(args[0]);


#define DO_RTHG                                           \
    CUR.GS.round_state = TT_Round_To_Half_Grid;           \
    CUR.func_round = (TRound_Function)Round_To_Half_Grid;


#define DO_RTG                                       \
    CUR.GS.round_state = TT_Round_To_Grid;           \
    CUR.func_round = (TRound_Function)Round_To_Grid;


#define DO_RTDG                                             \
    CUR.GS.round_state = TT_Round_To_Double_Grid;           \
    CUR.func_round = (TRound_Function)Round_To_Double_Grid;


#define DO_RUTG                                         \
    CUR.GS.round_state = TT_Round_Up_To_Grid;           \
    CUR.func_round = (TRound_Function)Round_Up_To_Grid;


#define DO_RDTG                                           \
    CUR.GS.round_state = TT_Round_Down_To_Grid;           \
    CUR.func_round = (TRound_Function)Round_Down_To_Grid;


#define DO_ROFF                                   \
    CUR.GS.round_state = TT_Round_Off;            \
    CUR.func_round = (TRound_Function)Round_None;


#define DO_SROUND                                  \
    SET_SuperRound( 0x4000L, args[0] );            \
    CUR.GS.round_state = TT_Round_Super;           \
    CUR.func_round = (TRound_Function)Round_Super;


#define DO_S45ROUND                                   \
    SET_SuperRound( 0x2D41L, args[0] );               \
    CUR.GS.round_state = TT_Round_Super_45;           \
    CUR.func_round = (TRound_Function)Round_Super_45;


#define DO_SLOOP                       \
    if ( args[0] < 0 )                 \
      CUR.error = TT_Err_Bad_Argument; \
    else                               \
      CUR.GS.loop = args[0];


#define DO_SMD  \
    CUR.GS.minimum_distance = args[0];


#define DO_SCVTCI  \
    CUR.GS.control_value_cutin = (TT_F26Dot6)args[0];


#define DO_SSWCI  \
    CUR.GS.single_width_cutin = (TT_F26Dot6)args[0];


    /* XXX : UNDOCUMENTED! or bug in the Windows engine?  */
    /*                                                    */
    /* It seems that the value that is read here is       */
    /* expressed in 16.16 format, rather than in          */
    /* font units..                                       */
    /*                                                    */
#define DO_SSW  \
    CUR.GS.single_width_value = (TT_F26Dot6)(args[0] >> 10);


#define DO_FLIPON  \
    CUR.GS.auto_flip = TRUE;


#define DO_FLIPOFF  \
    CUR.GS.auto_flip = FALSE;


#define DO_SDB  \
    CUR.GS.delta_base = (Short)args[0];


#define DO_SDS  \
    CUR.GS.delta_shift = (Short)args[0];


#define DO_MD  /* nothing */


#define DO_MPPEM  \
    args[0] = CURRENT_Ppem();


#define DO_MPS  \
    args[0] = CUR.metrics.pointSize;


#define DO_DUP  \
    args[1] = args[0];


#define DO_CLEAR  \
    CUR.new_top = 0;


#define DO_SWAP        \
  {                    \
    Long  L;           \
                       \
                       \
    L       = args[0]; \
    args[0] = args[1]; \
    args[1] = L;       \
  }


#define DO_DEPTH  \
    args[0] = CUR.top;


#define DO_CINDEX                           \
  {                                         \
    Long  L;                                \
                                            \
                                            \
    L = args[0];                            \
                                            \
    if ( L <= 0 || L > CUR.args )           \
      CUR.error = TT_Err_Invalid_Reference; \
    else                                    \
      args[0] = CUR.stack[CUR.args - L];    \
  }


#define DO_JROT                      \
    if ( args[1] != 0 )              \
    {                                \
      CUR.IP      += (Int)(args[0]); \
      CUR.step_ins = FALSE;          \
    }


#define DO_JMPR                    \
    CUR.IP      += (Int)(args[0]); \
    CUR.step_ins = FALSE;


#define DO_JROF                      \
    if ( args[1] == 0 )              \
    {                                \
      CUR.IP      += (Int)(args[0]); \
      CUR.step_ins = FALSE;          \
    }


#define DO_LT  \
    args[0] = (args[0] < args[1]);


#define DO_LTEQ  \
    args[0] = (args[0] <= args[1]);


#define DO_GT  \
    args[0] = (args[0] > args[1]);


#define DO_GTEQ  \
    args[0] = (args[0] >= args[1]);


#define DO_EQ  \
    args[0] = (args[0] == args[1]);


#define DO_NEQ  \
    args[0] = (args[0] != args[1]);


#define DO_ODD  \
    args[0] = ( (CUR_Func_round( args[0], 0 ) & 127) == 64 );


#define DO_EVEN  \
    args[0] = ( (CUR_Func_round( args[0], 0 ) & 127) == 0 );


#define DO_AND  \
    args[0] = ( args[0] && args[1] );


#define DO_OR  \
    args[0] = ( args[0] || args[1] );


#define DO_NOT  \
    args[0] = !args[0];


#define DO_ADD  \
    args[0] += args[1];


#define DO_SUB  \
    args[0] -= args[1];


#define DO_DIV                                      \
    if ( args[1] == 0 )                             \
      CUR.error = TT_Err_Divide_By_Zero;            \
    else                                            \
      args[0] = TT_MulDiv( args[0], 64L, args[1] );


#define DO_MUL  \
    args[0] = TT_MulDiv( args[0], args[1], 64L );


#define DO_ABS  \
    args[0] = ABS( args[0] );


#define DO_NEG  \
    args[0] = -args[0];


#define DO_FLOOR  \
    args[0] &= -64;


#define DO_CEILING  \
    args[0] = (args[0] + 63) & (-64);


/* Utility macro for DO_{RS,WS} and DO_{RCVT,WCVTP,WCVTF} */

#define CHECKED_INDEX( arg, lim ) \
    ULong  I = (ULong) arg;       \
    CHECK_BOUNDS( I, lim )


#define DO_RS                                  \
    {                                          \
      CHECKED_INDEX( args[0], CUR.storeSize ); \
      args[0] = CUR.storage[I];                \
    }


#define DO_WS                                  \
    {                                          \
      CHECKED_INDEX( args[0], CUR.storeSize ); \
      CUR.storage[I] = args[1];                \
    }


#define DO_RCVT                              \
    {                                        \
      CHECKED_INDEX( args[0], CUR.cvtSize ); \
      args[0] = CUR_Func_read_cvt( I );      \
    }


#define DO_WCVTP                             \
    {                                        \
      CHECKED_INDEX( args[0], CUR.cvtSize ); \
      CUR_Func_write_cvt( I, args[1] );      \
    }


#define DO_WCVTF                                          \
    {                                                     \
      CHECKED_INDEX( args[0], CUR.cvtSize );              \
      CUR.cvt[I] = FUnits_To_Pixels( EXEC_ARGS args[1] ); \
    }


#define DO_DEBUG  \
    CUR.error = TT_Err_Debug_OpCode;


#define DO_ROUND                                                            \
    args[0] = CUR_Func_round( args[0],                                      \
                              CUR.metrics.compensations[CUR.opcode-0x68] );


#define DO_NROUND                                                         \
    args[0] = Round_None( EXEC_ARGS                                       \
                          args[0],                                        \
                          CUR.metrics.compensations[CUR.opcode - 0x6C] );

#define DO_MAX               \
    if ( args[1] > args[0] ) \
      args[0] = args[1];


#define DO_MIN               \
    if ( args[1] < args[0] ) \
      args[0] = args[1];


#ifndef TT_CONFIG_OPTION_INTERPRETER_SWITCH

#undef CHECK_BOUNDS
#define CHECK_BOUNDS( val, limit )           \
     if ( BOUNDS( val, limit ) )             \
     {                                       \
       CUR.error = TT_Err_Invalid_Reference; \
       return;                               \
     }


/*******************************************/
/* SVTCA[a]  : Set F and P vectors to axis */
/* CodeRange : $00-$01                     */
/* Stack     : -->                         */

  static void  Ins_SVTCA( INS_ARG )
  {
    DO_SVTCA
  }


/*******************************************/
/* SPVTCA[a] : Set PVector to Axis         */
/* CodeRange : $02-$03                     */
/* Stack     : -->                         */

  static void  Ins_SPVTCA( INS_ARG )
  {
    DO_SPVTCA
  }


/*******************************************/
/* SFVTCA[a] : Set FVector to Axis         */
/* CodeRange : $04-$05                     */
/* Stack     : -->                         */

  static void  Ins_SFVTCA( INS_ARG )
  {
    DO_SFVTCA
  }

/*******************************************/
/* SPVTL[a]  : Set PVector to Line         */
/* CodeRange : $06-$07                     */
/* Stack     : uint32 uint32 -->           */

  static void  Ins_SPVTL( INS_ARG )
  {
    DO_SPVTL
  }


/*******************************************/
/* SFVTL[a]  : Set FVector to Line         */
/* CodeRange : $08-$09                     */
/* Stack     : uint32 uint32 -->           */

  static void  Ins_SFVTL( INS_ARG )
  {
    DO_SFVTL
  }


/*******************************************/
/* SFVTPV[]  : Set FVector to PVector      */
/* CodeRange : $0E                         */
/* Stack     : -->                         */

  static void  Ins_SFVTPV( INS_ARG )
  {
    DO_SFVTPV
  }


/*******************************************/
/* SPVFS[]   : Set PVector From Stack      */
/* CodeRange : $0A                         */
/* Stack     : f2.14 f2.14 -->             */

  static void  Ins_SPVFS( INS_ARG )
  {
    DO_SPVFS
  }


/*******************************************/
/* SFVFS[]   : Set FVector From Stack      */
/* CodeRange : $0B                         */
/* Stack     : f2.14 f2.14 -->             */

  static void  Ins_SFVFS( INS_ARG )
  {
    DO_SFVFS
  }


/*******************************************/
/* GPV[]     : Get Projection Vector       */
/* CodeRange : $0C                         */
/* Stack     : ef2.14 --> ef2.14           */

  static void  Ins_GPV( INS_ARG )
  {
    DO_GPV
  }


/*******************************************/
/* GFV[]     : Get Freedom Vector          */
/* CodeRange : $0D                         */
/* Stack     : ef2.14 --> ef2.14           */

  static void  Ins_GFV( INS_ARG )
  {
    DO_GFV
  }


/*******************************************/
/* SRP0[]    : Set Reference Point 0       */
/* CodeRange : $10                         */
/* Stack     : uint32 -->                  */

  static void  Ins_SRP0( INS_ARG )
  {
    DO_SRP0
  }


/*******************************************/
/* SRP1[]    : Set Reference Point 1       */
/* CodeRange : $11                         */
/* Stack     : uint32 -->                  */

  static void  Ins_SRP1( INS_ARG )
  {
    DO_SRP1
  }


/*******************************************/
/* SRP2[]    : Set Reference Point 2       */
/* CodeRange : $12                         */
/* Stack     : uint32 -->                  */

  static void  Ins_SRP2( INS_ARG )
  {
    DO_SRP2
  }


/*******************************************/
/* RTHG[]    : Round To Half Grid          */
/* CodeRange : $19                         */
/* Stack     : -->                         */

  static void  Ins_RTHG( INS_ARG )
  {
    DO_RTHG
  }


/*******************************************/
/* RTG[]     : Round To Grid               */
/* CodeRange : $18                         */
/* Stack     : -->                         */

  static void  Ins_RTG( INS_ARG )
  {
    DO_RTG
  }


/*******************************************/
/* RTDG[]    : Round To Double Grid        */
/* CodeRange : $3D                         */
/* Stack     : -->                         */

  static void  Ins_RTDG( INS_ARG )
  {
    DO_RTDG
  }


/*******************************************/
/* RUTG[]    : Round Up To Grid            */
/* CodeRange : $7C                         */
/* Stack     : -->                         */

  static void  Ins_RUTG( INS_ARG )
  {
    DO_RUTG
  }


/*******************************************/
/* RDTG[]    : Round Down To Grid          */
/* CodeRange : $7D                         */
/* Stack     : -->                         */

  static void  Ins_RDTG( INS_ARG )
  {
    DO_RDTG
  }


/*******************************************/
/* ROFF[]    : Round OFF                   */
/* CodeRange : $7A                         */
/* Stack     : -->                         */

  static void  Ins_ROFF( INS_ARG )
  {
    DO_ROFF
  }


/*******************************************/
/* SROUND[]  : Super ROUND                 */
/* CodeRange : $76                         */
/* Stack     : Eint8 -->                   */

  static void  Ins_SROUND( INS_ARG )
  {
    DO_SROUND
  }


/*******************************************/
/* S45ROUND[]: Super ROUND 45 degrees      */
/* CodeRange : $77                         */
/* Stack     : uint32 -->                  */

  static void  Ins_S45ROUND( INS_ARG )
  {
    DO_S45ROUND
  }


/*******************************************/
/* SLOOP[]   : Set LOOP variable           */
/* CodeRange : $17                         */
/* Stack     : int32? -->                  */

  static void  Ins_SLOOP( INS_ARG )
  {
    DO_SLOOP
  }


/*******************************************/
/* SMD[]     : Set Minimum Distance        */
/* CodeRange : $1A                         */
/* Stack     : f26.6 -->                   */

  static void  Ins_SMD( INS_ARG )
  {
    DO_SMD
  }


/**********************************************/
/* SCVTCI[]  : Set Control Value Table Cut In */
/* CodeRange : $1D                            */
/* Stack     : f26.6 -->                      */

  static void  Ins_SCVTCI( INS_ARG )
  {
    DO_SCVTCI
  }


/**********************************************/
/* SSWCI[]   : Set Single Width Cut In        */
/* CodeRange : $1E                            */
/* Stack     : f26.6 -->                      */

  static void  Ins_SSWCI( INS_ARG )
  {
    DO_SSWCI
  }


/**********************************************/
/* SSW[]     : Set Single Width               */
/* CodeRange : $1F                            */
/* Stack     : int32? -->                     */

  static void  Ins_SSW( INS_ARG )
  {
    DO_SSW
  }


/**********************************************/
/* FLIPON[]  : Set Auto_flip to On            */
/* CodeRange : $4D                            */
/* Stack     : -->                            */

  static void  Ins_FLIPON( INS_ARG )
  {
    DO_FLIPON
  }


/**********************************************/
/* FLIPOFF[] : Set Auto_flip to Off           */
/* CodeRange : $4E                            */
/* Stack     : -->                            */

  static void  Ins_FLIPOFF( INS_ARG )
  {
    DO_FLIPOFF
  }


/**********************************************/
/* SANGW[]   : Set Angle Weight               */
/* CodeRange : $7E                            */
/* Stack     : uint32 -->                     */

  static void  Ins_SANGW( INS_ARG )
  {
    /* instruction not supported anymore */
  }


/**********************************************/
/* SDB[]     : Set Delta Base                 */
/* CodeRange : $5E                            */
/* Stack     : uint32 -->                     */

  static void  Ins_SDB( INS_ARG )
  {
    DO_SDB
  }


/**********************************************/
/* SDS[]     : Set Delta Shift                */
/* CodeRange : $5F                            */
/* Stack     : uint32 -->                     */

  static void  Ins_SDS( INS_ARG )
  {
    DO_SDS
  }


/**********************************************/
/* MPPEM[]   : Measure Pixel Per EM           */
/* CodeRange : $4B                            */
/* Stack     : --> Euint16                    */

  static void  Ins_MPPEM( INS_ARG )
  {
    DO_MPPEM
  }


/**********************************************/
/* MPS[]     : Measure PointSize              */
/* CodeRange : $4C                            */
/* Stack     : --> Euint16                    */

  static void  Ins_MPS( INS_ARG )
  {
    DO_MPS
  }

/*******************************************/
/* DUP[]     : Duplicate top stack element */
/* CodeRange : $20                         */
/* Stack     : StkElt --> StkElt StkElt    */

  static void  Ins_DUP( INS_ARG )
  {
    DO_DUP
  }


/*******************************************/
/* POP[]     : POPs the stack's top elt.   */
/* CodeRange : $21                         */
/* Stack     : StkElt -->                  */

  static void  Ins_POP( INS_ARG )
  {
    /* nothing to do */
  }


/*******************************************/
/* CLEAR[]   : Clear the entire stack      */
/* CodeRange : $22                         */
/* Stack     : StkElt... -->               */

  static void  Ins_CLEAR( INS_ARG )
  {
    DO_CLEAR
  }


/*******************************************/
/* SWAP[]    : Swap the top two elements   */
/* CodeRange : $23                         */
/* Stack     : 2 * StkElt --> 2 * StkElt   */

  static void  Ins_SWAP( INS_ARG )
  {
    DO_SWAP
  }


/*******************************************/
/* DEPTH[]   : return the stack depth      */
/* CodeRange : $24                         */
/* Stack     : --> uint32                  */

  static void  Ins_DEPTH( INS_ARG )
  {
    DO_DEPTH
  }


/*******************************************/
/* CINDEX[]  : copy indexed element        */
/* CodeRange : $25                         */
/* Stack     : int32 --> StkElt            */

  static void  Ins_CINDEX( INS_ARG )
  {
    DO_CINDEX
  }


/*******************************************/
/* EIF[]     : End IF                      */
/* CodeRange : $59                         */
/* Stack     : -->                         */

  static void  Ins_EIF( INS_ARG )
  {
    /* nothing to do */
  }


/*******************************************/
/* JROT[]    : Jump Relative On True       */
/* CodeRange : $78                         */
/* Stack     : StkElt int32 -->            */

  static void  Ins_JROT( INS_ARG )
  {
    DO_JROT
  }


/*******************************************/
/* JMPR[]    : JuMP Relative               */
/* CodeRange : $1C                         */
/* Stack     : int32 -->                   */

  static void  Ins_JMPR( INS_ARG )
  {
    DO_JMPR
  }


/*******************************************/
/* JROF[]    : Jump Relative On False      */
/* CodeRange : $79                         */
/* Stack     : StkElt int32 -->            */

  static void  Ins_JROF( INS_ARG )
  {
    DO_JROF
  }


/*******************************************/
/* LT[]      : Less Than                   */
/* CodeRange : $50                         */
/* Stack     : int32? int32? --> bool      */

  static void  Ins_LT( INS_ARG )
  {
    DO_LT
  }


/*******************************************/
/* LTEQ[]    : Less Than or EQual          */
/* CodeRange : $51                         */
/* Stack     : int32? int32? --> bool      */

  static void  Ins_LTEQ( INS_ARG )
  {
    DO_LTEQ
  }


/*******************************************/
/* GT[]      : Greater Than                */
/* CodeRange : $52                         */
/* Stack     : int32? int32? --> bool      */

  static void  Ins_GT( INS_ARG )
  {
    DO_GT
  }


/*******************************************/
/* GTEQ[]    : Greater Than or EQual       */
/* CodeRange : $53                         */
/* Stack     : int32? int32? --> bool      */

  static void  Ins_GTEQ( INS_ARG )
  {
    DO_GTEQ
  }


/*******************************************/
/* EQ[]      : EQual                       */
/* CodeRange : $54                         */
/* Stack     : StkElt StkElt --> bool      */

  static void  Ins_EQ( INS_ARG )
  {
    DO_EQ
  }


/*******************************************/
/* NEQ[]     : Not EQual                   */
/* CodeRange : $55                         */
/* Stack     : StkElt StkElt --> bool      */

  static void  Ins_NEQ( INS_ARG )
  {
    DO_NEQ
  }


/*******************************************/
/* ODD[]     : Odd                         */
/* CodeRange : $56                         */
/* Stack     : f26.6 --> bool              */

  static void  Ins_ODD( INS_ARG )
  {
    DO_ODD
  }


/*******************************************/
/* EVEN[]    : Even                        */
/* CodeRange : $57                         */
/* Stack     : f26.6 --> bool              */

  static void  Ins_EVEN( INS_ARG )
  {
    DO_EVEN
  }


/*******************************************/
/* AND[]     : logical AND                 */
/* CodeRange : $5A                         */
/* Stack     : uint32 uint32 --> uint32    */

  static void  Ins_AND( INS_ARG )
  {
    DO_AND
  }


/*******************************************/
/* OR[]      : logical OR                  */
/* CodeRange : $5B                         */
/* Stack     : uint32 uint32 --> uint32    */

  static void  Ins_OR( INS_ARG )
  {
    DO_OR
  }


/*******************************************/
/* NOT[]     : logical NOT                 */
/* CodeRange : $5C                         */
/* Stack     : StkElt --> uint32           */

  static void  Ins_NOT( INS_ARG )
  {
    DO_NOT
  }


/*******************************************/
/* ADD[]     : ADD                         */
/* CodeRange : $60                         */
/* Stack     : f26.6 f26.6 --> f26.6       */

  static void  Ins_ADD( INS_ARG )
  {
    DO_ADD
  }


/*******************************************/
/* SUB[]     : SUBstract                   */
/* CodeRange : $61                         */
/* Stack     : f26.6 f26.6 --> f26.6       */

  static void  Ins_SUB( INS_ARG )
  {
    DO_SUB
  }


/*******************************************/
/* DIV[]     : DIVide                      */
/* CodeRange : $62                         */
/* Stack     : f26.6 f26.6 --> f26.6       */

  static void  Ins_DIV( INS_ARG )
  {
    DO_DIV
  }


/*******************************************/
/* MUL[]     : MULtiply                    */
/* CodeRange : $63                         */
/* Stack     : f26.6 f26.6 --> f26.6       */

  static void  Ins_MUL( INS_ARG )
  {
    DO_MUL
  }


/*******************************************/
/* ABS[]     : ABSolute value              */
/* CodeRange : $64                         */
/* Stack     : f26.6 --> f26.6             */

  static void  Ins_ABS( INS_ARG )
  {
    DO_ABS
  }


/*******************************************/
/* NEG[]     : NEGate                      */
/* CodeRange : $65                         */
/* Stack     : f26.6 --> f26.6             */

  static void  Ins_NEG( INS_ARG )
  {
    DO_NEG
  }


/*******************************************/
/* FLOOR[]   : FLOOR                       */
/* CodeRange : $66                         */
/* Stack     : f26.6 --> f26.6             */

  static void  Ins_FLOOR( INS_ARG )
  {
    DO_FLOOR
  }


/*******************************************/
/* CEILING[] : CEILING                     */
/* CodeRange : $67                         */
/* f26.6 --> f26.6                         */

  static void  Ins_CEILING( INS_ARG )
  {
    DO_CEILING
  }

/*******************************************/
/* RS[]      : Read Store                  */
/* CodeRange : $43                         */
/* Stack     : uint32 --> uint32           */

  static void  Ins_RS( INS_ARG )
  {
    DO_RS
  }


/*******************************************/
/* WS[]      : Write Store                 */
/* CodeRange : $42                         */
/* Stack     : uint32 uint32 -->           */

  static void  Ins_WS( INS_ARG )
  {
    DO_WS
  }


/*******************************************/
/* WCVTP[]   : Write CVT in Pixel units    */
/* CodeRange : $44                         */
/* Stack     : f26.6 uint32 -->            */

  static void  Ins_WCVTP( INS_ARG )
  {
    DO_WCVTP
  }


/*******************************************/
/* WCVTF[]   : Write CVT in FUnits         */
/* CodeRange : $70                         */
/* Stack     : uint32 uint32 -->           */

  static void  Ins_WCVTF( INS_ARG )
  {
    DO_WCVTF
  }


/*******************************************/
/* RCVT[]    : Read CVT                    */
/* CodeRange : $45                         */
/* Stack     : uint32 --> f26.6            */

  static void  Ins_RCVT( INS_ARG )
  {
    DO_RCVT
  }


/********************************************/
/* AA[]        : Adjust Angle               */
/* CodeRange   : $7F                        */
/* Stack       : uint32 -->                 */

  static void  Ins_AA( INS_ARG )
  {
    /* Intentional - no longer supported */
  }


/********************************************/
/* DEBUG[]     : DEBUG. Unsupported         */
/* CodeRange   : $4F                        */
/* Stack       : uint32 -->                 */

/* NOTE : The original instruction pops a value from the stack */

  static void  Ins_DEBUG( INS_ARG )
  {
    DO_DEBUG
  }

/*******************************************/
/* ROUND[ab] : ROUND value                 */
/* CodeRange : $68-$6B                     */
/* Stack     : f26.6 --> f26.6             */

  static void  Ins_ROUND( INS_ARG )
  {
    DO_ROUND
  }

/*******************************************/
/* NROUND[ab]: No ROUNDing of value        */
/* CodeRange : $6C-$6F                     */
/* Stack     : f26.6 --> f26.6             */

  static void  Ins_NROUND( INS_ARG )
  {
    DO_NROUND
  }



/*******************************************/
/* MAX[]     : MAXimum                     */
/* CodeRange : $68                         */
/* Stack     : int32? int32? --> int32     */

  static void  Ins_MAX( INS_ARG )
  {
    DO_MAX
  }


/*******************************************/
/* MIN[]     : MINimum                     */
/* CodeRange : $69                         */
/* Stack     : int32? int32? --> int32     */

  static void  Ins_MIN( INS_ARG )
  {
    DO_MIN
  }


#endif  /* !TT_CONFIG_OPTION_INTERPRETER_SWITCH */


/* The following functions are called as is within the switch statement */

/*******************************************/
/* MINDEX[]  : move indexed element        */
/* CodeRange : $26                         */
/* Stack     : int32? --> StkElt           */

  static void  Ins_MINDEX( INS_ARG )
  {
    Long  L, K;


    L = args[0];

    if ( L <= 0 || L > CUR.args )
    {
      CUR.error = TT_Err_Invalid_Reference;
      return;
    }

    K = CUR.stack[CUR.args - L];

    MEM_Move( (&CUR.stack[CUR.args - L    ]),
              (&CUR.stack[CUR.args - L + 1]),
              (L - 1) * sizeof ( Long ) );

    CUR.stack[CUR.args - 1] = K;
  }


/*******************************************/
/* ROLL[]    : roll top three elements     */
/* CodeRange : $8A                         */
/* Stack     : 3 * StkElt --> 3 * StkElt   */

  static void  Ins_ROLL( INS_ARG )
  {
    Long  A, B, C;


    A = args[2];
    B = args[1];
    C = args[0];

    args[2] = C;
    args[1] = A;
    args[0] = B;
  }



/****************************************************************/
/*                                                              */
/* MANAGING THE FLOW OF CONTROL                                 */
/*                                                              */
/*  Instructions appear in the specs' order.                    */
/*                                                              */
/****************************************************************/

  static Bool  SkipCode( EXEC_OP )
  {
    CUR.IP += CUR.length;

    if ( CUR.IP < CUR.codeSize )
      if ( CALC_Length() == SUCCESS )
        return SUCCESS;

    CUR.error = TT_Err_Code_Overflow;
    return FAILURE;
  }


/*******************************************/
/* IF[]      : IF test                     */
/* CodeRange : $58                         */
/* Stack     : StkElt -->                  */

  static void  Ins_IF( INS_ARG )
  {
    Int   nIfs;
    Bool  Out;


    if ( args[0] != 0 )
      return;

    nIfs = 1;
    Out = 0;

    do
    {
      if ( SKIP_Code() == FAILURE )
        return;

      switch ( CUR.opcode )
      {
      case 0x58:      /* IF */
        nIfs++;
        break;

      case 0x1b:      /* ELSE */
        Out = (nIfs == 1);
        break;

      case 0x59:      /* EIF */
        nIfs--;
        Out = (nIfs == 0);
        break;
      }
    } while ( Out == 0 );
  }


/*******************************************/
/* ELSE[]    : ELSE                        */
/* CodeRange : $1B                         */
/* Stack     : -->                         */

  static void  Ins_ELSE( INS_ARG )
  {
    Int  nIfs;


    nIfs = 1;

    do
    {
      if ( SKIP_Code() == FAILURE )
        return;

      switch ( CUR.opcode )
      {
      case 0x58:    /* IF */
        nIfs++;
        break;

      case 0x59:    /* EIF */
        nIfs--;
        break;
      }
    } while ( nIfs != 0 );
  }


/****************************************************************/
/*                                                              */
/* DEFINING AND USING FUNCTIONS AND INSTRUCTIONS                */
/*                                                              */
/*  Instructions appear in the specs' order.                    */
/*                                                              */
/****************************************************************/

/*******************************************/
/* FDEF[]    : Function DEFinition         */
/* CodeRange : $2C                         */
/* Stack     : uint32 -->                  */

  static void  Ins_FDEF( INS_ARG )
  {
    ULong       n;
    PDefRecord  pRec;


    n = args[0];

    if ( BOUNDS( n, CUR.numFDefs ) )
    {
      CUR.error = TT_Err_Invalid_Reference;
      return;
    }

    pRec = &CUR.FDefs[n];

    pRec->Range  = CUR.curRange;
    pRec->Opc    = (Byte)n;
    pRec->Start  = CUR.IP + 1;
    pRec->Active = TRUE;

  /* Now skip the whole function definition. */
  /* We don't allow nested IDEFS & FDEFs.    */

    while ( SKIP_Code() == SUCCESS )
    {
      switch ( CUR.opcode )
      {
      case 0x89:    /* IDEF */
      case 0x2c:    /* FDEF */
        CUR.error = TT_Err_Nested_DEFS;
        return;

      case 0x2d:   /* ENDF */
        return;
      }
    }
  }


/*******************************************/
/* ENDF[]    : END Function definition     */
/* CodeRange : $2D                         */
/* Stack     : -->                         */

  static void  Ins_ENDF( INS_ARG )
  {
    PCallRecord  pRec;


    if ( CUR.callTop <= 0 )     /* We encountered an ENDF without a call */
    {
      CUR.error = TT_Err_ENDF_In_Exec_Stream;
      return;
    }

    CUR.callTop--;

    pRec = &CUR.callStack[CUR.callTop];

    pRec->Cur_Count--;

    CUR.step_ins = FALSE;

    if ( pRec->Cur_Count > 0 )
    {
      CUR.callTop++;
      CUR.IP = pRec->Cur_Restart;
    }
    else
      /* Loop through the current function */
      INS_Goto_CodeRange( pRec->Caller_Range,
                          pRec->Caller_IP );

    /* Exit the current call frame.                       */

    /* NOTE: When the last intruction of a program        */
    /*       is a CALL or LOOPCALL, the return address    */
    /*       is always out of the code range.  This is    */
    /*       a valid address, and it's why we do not test */
    /*       the result of Ins_Goto_CodeRange() here!     */
  }


/*******************************************/
/* CALL[]    : CALL function               */
/* CodeRange : $2B                         */
/* Stack     : uint32? -->                 */

  static void  Ins_CALL( INS_ARG )
  {
    ULong        F;
    PCallRecord  pCrec;


    F = (ULong) args[0];

    if ( BOUNDS( F, CUR.numFDefs ) || !CUR.FDefs[F].Active )
    {
      CUR.error = TT_Err_Invalid_Reference;
      return;
    }

    if ( CUR.callTop >= CUR.callSize )
    {
      CUR.error = TT_Err_Stack_Overflow;
      return;
    }

    pCrec = &CUR.callStack[CUR.callTop];

    pCrec->Caller_Range = CUR.curRange;
    pCrec->Caller_IP    = CUR.IP + 1;
    pCrec->Cur_Count    = 1;
    pCrec->Cur_Restart  = CUR.FDefs[F].Start;

    CUR.callTop++;

    INS_Goto_CodeRange( CUR.FDefs[F].Range,
                        CUR.FDefs[F].Start );

    CUR.step_ins = FALSE;
  }


/*******************************************/
/* LOOPCALL[]: LOOP and CALL function      */
/* CodeRange : $2A                         */
/* Stack     : uint32? Eint16? -->         */

  static void  Ins_LOOPCALL( INS_ARG )
  {
    ULong        F;
    PCallRecord  pTCR;


    F = (ULong) args[1];

    if ( BOUNDS( F, CUR.numFDefs ) || !CUR.FDefs[F].Active )
    {
      CUR.error = TT_Err_Invalid_Reference;
      return;
    }

    if ( CUR.callTop >= CUR.callSize )
    {
      CUR.error = TT_Err_Stack_Overflow;
      return;
    }

    if ( args[0] > 0 )
    {
      pTCR = &CUR.callStack[CUR.callTop];

      pTCR->Caller_Range = CUR.curRange;
      pTCR->Caller_IP    = CUR.IP + 1;
      pTCR->Cur_Count    = (Int)(args[0]);
      pTCR->Cur_Restart  = CUR.FDefs[F].Start;

      CUR.callTop++;

      INS_Goto_CodeRange( CUR.FDefs[F].Range,
                          CUR.FDefs[F].Start );

      CUR.step_ins = FALSE;
    }
  }


/*******************************************/
/* IDEF[]    : Instruction DEFinition      */
/* CodeRange : $89                         */
/* Stack     : Eint8 -->                   */

  static void Ins_IDEF( INS_ARG )
  {
    UShort      A;
    PDefRecord  pTDR;


    A = 0;

    while ( A < CUR.numIDefs )
    {
      pTDR = &CUR.IDefs[A];

      if ( pTDR->Active == 0 )
      {
        pTDR->Opc    = (Byte)(args[0]);
        pTDR->Start  = CUR.IP + 1;
        pTDR->Range  = CUR.curRange;
        pTDR->Active = TRUE;

        A = CUR.numIDefs;

        /* Now skip the whole function definition */
        /* We don't allow nested IDEFs & FDEFs.   */

        while ( SKIP_Code() == SUCCESS )
        {
          switch ( CUR.opcode )
          {
          case 0x89:   /* IDEF */
          case 0x2c:   /* FDEF */
            CUR.error = TT_Err_Nested_DEFS;
            return;
          case 0x2d:   /* ENDF */
            return;
          }
        }
      }
      else
        A++;
    }
  }



/****************************************************************/
/*                                                              */
/* PUSHING DATA ONTO THE INTERPRETER STACK                      */
/*                                                              */
/*  Instructions appear in the specs' order.                    */
/*                                                              */
/****************************************************************/

/*******************************************/
/* NPUSHB[]  : PUSH N Bytes                */
/* CodeRange : $40                         */
/* Stack     : --> uint32...               */

  static void  Ins_NPUSHB( INS_ARG )
  {
    Int  L, K;


    L = (Int)CUR.code[CUR.IP + 1];

    if ( BOUNDS( L, CUR.stackSize + 1 - CUR.top ) )
    {
      CUR.error = TT_Err_Stack_Overflow;
      return;
    }

    for ( K = 1; K <= L; K++ )
      args[K - 1] = CUR.code[CUR.IP + K + 1];

    CUR.new_top += L;
  }


/*******************************************/
/* NPUSHW[]  : PUSH N Words                */
/* CodeRange : $41                         */
/* Stack     : --> int32...                */

  static void  Ins_NPUSHW( INS_ARG )
  {
    Int  L, K;


    L = (Int)CUR.code[CUR.IP + 1];

    if ( BOUNDS( L, CUR.stackSize + 1 - CUR.top ) )
    {
      CUR.error = TT_Err_Stack_Overflow;
      return;
    }

    CUR.IP += 2;

    for ( K = 0; K < L; K++ )
      args[K] = GET_ShortIns();

    CUR.step_ins = FALSE;
    CUR.new_top += L;
  }


/*******************************************/
/* PUSHB[abc]: PUSH Bytes                  */
/* CodeRange : $B0-$B7                     */
/* Stack     : --> uint32...               */

  static void  Ins_PUSHB( INS_ARG )
  {
    Int  L, K;


    L = ((Int)CUR.opcode - 0xB0 + 1);

    if ( BOUNDS( L, CUR.stackSize + 1 - CUR.top ) )
    {
      CUR.error = TT_Err_Stack_Overflow;
      return;
    }

    for ( K = 1; K <= L; K++ )
      args[K - 1] = CUR.code[CUR.IP + K];
  }


/*******************************************/
/* PUSHW[abc]: PUSH Words                  */
/* CodeRange : $B8-$BF                     */
/* Stack     : --> int32...                */

  static void  Ins_PUSHW( INS_ARG )
  {
    Int  L, K;


    L = CUR.opcode - 0xB8 + 1;

    if ( BOUNDS( L, CUR.stackSize + 1 - CUR.top ) )
    {
      CUR.error = TT_Err_Stack_Overflow;
      return;
    }

    CUR.IP++;

    for ( K = 0; K < L; K++ )
      args[K] = GET_ShortIns();

    CUR.step_ins = FALSE;
  }



/****************************************************************/
/*                                                              */
/* MANAGING THE GRAPHICS STATE                                  */
/*                                                              */
/*  Instructions appear in the specs' order.                    */
/*                                                              */
/****************************************************************/

/**********************************************/
/* GC[a]     : Get Coordinate projected onto  */
/* CodeRange : $46-$47                        */
/* Stack     : uint32 --> f26.6               */

/* BULLSHIT: Measures from the original glyph must be taken */
/*           along the dual projection vector!              */

  static void  Ins_GC( INS_ARG )
  {
    ULong       L;
    TT_F26Dot6  R;


    L = (ULong) args[0];

    if ( BOUNDS( L, CUR.zp2.n_points ) )
    {
      CUR.error = TT_Err_Invalid_Reference;
      return;
    }

    if ( CUR.opcode & 1 )
      R = CUR_Func_dualproj( CUR.zp2.org + L, NULL_Vector );
    else
      R = CUR_Func_project( CUR.zp2.cur + L, NULL_Vector );

    args[0] = R;
  }


/**********************************************/
/* SCFS[]    : Set Coordinate From Stack      */
/* CodeRange : $48                            */
/* Stack     : f26.6 uint32 -->               */
/*                                            */
/* Formula:                                   */
/*                                            */
/*   OA := OA + ( value - OA.p )/( f.p ) * f  */
/*                                            */

  static void  Ins_SCFS( INS_ARG )
  {
    Long   K;
    ULong  L;


    L = (ULong) args[0];

    if ( BOUNDS( L, CUR.zp2.n_points ) )
    {
      CUR.error = TT_Err_Invalid_Reference;
      return;
    }

    K = CUR_Func_project( CUR.zp2.cur+L, NULL_Vector );

    CUR_Func_move( &CUR.zp2, L, args[1] - K );

    /* not part of the specs, but here for safety */

    if ( CUR.GS.gep2 == 0 )
      CUR.zp2.org[L] = CUR.zp2.cur[L];
  }


/**********************************************/
/* MD[a]     : Measure Distance               */
/* CodeRange : $49-$4A                        */
/* Stack     : uint32 uint32 --> f26.6        */

/* BULLSHIT: Measure taken in the original glyph must be along */
/*           the dual projection vector.                       */

/* Second BULLSHIT: Flag attributes are inverted!                */
/*                  0 => measure distance in original outline    */
/*                  1 => measure distance in grid-fitted outline */

/* Third one !! : zp0 - zp1, and not "zp2 - zp1" !!!             */
/*                                                               */

  static void  Ins_MD( INS_ARG )
  {
    ULong      K, L;
    TT_F26Dot6 D;


    K = (ULong) args[1];
    L = (ULong) args[0];

    if( BOUNDS( L, CUR.zp0.n_points ) ||
        BOUNDS( K, CUR.zp1.n_points ) )
    {
      CUR.error = TT_Err_Invalid_Reference;
      return;
    }

    if ( CUR.opcode & 1 )
      D = CUR_Func_project( CUR.zp0.cur + L, CUR.zp1.cur + K );
    else
      D = CUR_Func_dualproj( CUR.zp0.org + L, CUR.zp1.org + K );

    args[0] = D;
  }


/*******************************************/
/* SDPVTL[a] : Set Dual PVector to Line    */
/* CodeRange : $86-$87                     */
/* Stack     : uint32 uint32 -->           */

  static void  Ins_SDPVTL( INS_ARG )
  {
    Long   A, B, C;
    ULong  p1, p2;   /* was Int in pas type ERROR */


    p1 = (ULong) args[1];
    p2 = (ULong) args[0];

    if ( BOUNDS( p2, CUR.zp1.n_points ) ||
         BOUNDS( p1, CUR.zp2.n_points ) )
    {
      CUR.error = TT_Err_Invalid_Reference;
      return;
    }

    {
      TT_Vector* v1 = CUR.zp1.org + p2;
      TT_Vector* v2 = CUR.zp2.org + p1;


      A = v1->x - v2->x;
      B = v1->y - v2->y;
    }

    if ( (CUR.opcode & 1) != 0 )
    {
      C =  B;   /* CounterClockwise rotation */
      B =  A;
      A = -C;
    }

    NORMalize( A, B, &CUR.GS.dualVector );

    {
      TT_Vector*  v1 = CUR.zp1.cur + p2;
      TT_Vector*  v2 = CUR.zp2.cur + p1;


      A = v1->x - v2->x;
      B = v1->y - v2->y;
    }

    if ( (CUR.opcode & 1) != 0 )
    {
      C =  B;   /* CounterClockwise rotation */
      B =  A;
      A = -C;
    }

    NORMalize( A, B, &CUR.GS.projVector );

    COMPUTE_Funcs();
  }


/*******************************************/
/* SZP0[]    : Set Zone Pointer 0          */
/* CodeRange : $13                         */
/* Stack     : uint32 -->                  */

  static void  Ins_SZP0( INS_ARG )
  {
    switch ( args[0] )
    {
    case 0:
      CUR.zp0 = CUR.twilight;
      break;

    case 1:
      CUR.zp0 = CUR.pts;
      break;

    default:
      CUR.error = TT_Err_Invalid_Reference;
      return;
    }

    CUR.GS.gep0 = (UShort)(args[0]);
  }


/*******************************************/
/* SZP1[]    : Set Zone Pointer 1          */
/* CodeRange : $14                         */
/* Stack     : uint32 -->                  */

  static void  Ins_SZP1( INS_ARG )
  {
    switch ( args[0] )
    {
    case 0:
      CUR.zp1 = CUR.twilight;
      break;

    case 1:
      CUR.zp1 = CUR.pts;
      break;

    default:
      CUR.error = TT_Err_Invalid_Reference;
      return;
    }

    CUR.GS.gep1 = (UShort)(args[0]);
  }


/*******************************************/
/* SZP2[]    : Set Zone Pointer 2          */
/* CodeRange : $15                         */
/* Stack     : uint32 -->                  */

  static void  Ins_SZP2( INS_ARG )
  {
    switch ( args[0] )
    {
    case 0:
      CUR.zp2 = CUR.twilight;
      break;

    case 1:
      CUR.zp2 = CUR.pts;
      break;

    default:
      CUR.error = TT_Err_Invalid_Reference;
      return;
    }

    CUR.GS.gep2 = (UShort)(args[0]);
  }


/*******************************************/
/* SZPS[]    : Set Zone Pointers           */
/* CodeRange : $16                         */
/* Stack     : uint32 -->                  */

  static void  Ins_SZPS( INS_ARG )
  {
    switch ( args[0] )
    {
    case 0:
      CUR.zp0 = CUR.twilight;
      break;

    case 1:
      CUR.zp0 = CUR.pts;
      break;

    default:
      CUR.error = TT_Err_Invalid_Reference;
      return;
    }

    CUR.zp1 = CUR.zp0;
    CUR.zp2 = CUR.zp0;

    CUR.GS.gep0 = (UShort)(args[0]);
    CUR.GS.gep1 = (UShort)(args[0]);
    CUR.GS.gep2 = (UShort)(args[0]);
  }


/*******************************************/
/* INSTCTRL[]: INSTruction ConTRol         */
/* CodeRange : $8e                         */
/* Stack     : int32 int32 -->             */

  static void  Ins_INSTCTRL( INS_ARG )
  {
    Long  K, L;


    K = args[1];
    L = args[0];

    if ( K < 1 || K > 2 )
    {
      CUR.error = TT_Err_Invalid_Reference;
      return;
    }

    if( L != 0 )
        L = K;

    CUR.GS.instruct_control = (Byte)
      ( CUR.GS.instruct_control & ~(Byte)K ) | (Byte) L;
  }


/*******************************************/
/* SCANCTRL[]: SCAN ConTRol                */
/* CodeRange : $85                         */
/* Stack     : uint32? -->                 */

  static void  Ins_SCANCTRL( INS_ARG )
  {
    Int  A;


    /* Get Threshold */
    A = (Int)(args[0] & 0xFF);

    if ( A == 0xFF )
    {
      CUR.GS.scan_control = TRUE;
      return;
    }
    else if ( A == 0 )
    {
      CUR.GS.scan_control = FALSE;
      return;
    }

    A *= 64;

    if ( (args[0] & 0x100) != 0 && CUR.metrics.pointSize <= A )
      CUR.GS.scan_control = TRUE;

    if ( (args[0] & 0x200) != 0 && CUR.metrics.rotated )
      CUR.GS.scan_control = TRUE;

    if ( (args[0] & 0x400) != 0 && CUR.metrics.stretched )
      CUR.GS.scan_control = TRUE;

    if ( (args[0] & 0x800) != 0 && CUR.metrics.pointSize > A )
      CUR.GS.scan_control = FALSE;

    if ( (args[0] & 0x1000) != 0 && CUR.metrics.rotated )
      CUR.GS.scan_control = FALSE;

    if ( (args[0] & 0x2000) != 0 && CUR.metrics.stretched )
      CUR.GS.scan_control = FALSE;
}


/*******************************************/
/* SCANTYPE[]: SCAN TYPE                   */
/* CodeRange : $8D                         */
/* Stack     : uint32? -->                 */

  static void  Ins_SCANTYPE( INS_ARG )
  {
    /* For compatibility with future enhancements, */
    /* we must ignore new modes                    */

    if ( args[0] >= 0 && args[0] <= 5 )
    {
      if ( args[0] == 3 )
        args[0] = 2;

      CUR.GS.scan_type = (Int)args[0];
    }
  }



/****************************************************************/
/*                                                              */
/* MANAGING OUTLINES                                            */
/*                                                              */
/*  Instructions appear in the specs' order.                    */
/*                                                              */
/****************************************************************/

/**********************************************/
/* FLIPPT[]  : FLIP PoinT                     */
/* CodeRange : $80                            */
/* Stack     : uint32... -->                  */

  static void  Ins_FLIPPT( INS_ARG )
  {
    UShort  point;


    if ( CUR.top < CUR.GS.loop )
    {
      CUR.error = TT_Err_Too_Few_Arguments;
      return;
    }

    while ( CUR.GS.loop > 0 )
    {
      CUR.args--;

      point = (UShort)CUR.stack[CUR.args];

      if ( BOUNDS( point, CUR.pts.n_points ) )
      {
        CUR.error = TT_Err_Invalid_Reference;
        return;
      }

      CUR.pts.touch[point] ^= TT_Flag_On_Curve;

      CUR.GS.loop--;
    }

    CUR.GS.loop = 1;
    CUR.new_top = CUR.args;
  }


/**********************************************/
/* FLIPRGON[]: FLIP RanGe ON                  */
/* CodeRange : $81                            */
/* Stack     : uint32 uint32 -->              */

  static void  Ins_FLIPRGON( INS_ARG )
  {
    ULong  I, K, L;


    K = (ULong) args[1];
    L = (ULong) args[0];

    if ( BOUNDS( K, CUR.pts.n_points ) ||
         BOUNDS( L, CUR.pts.n_points ) )
    {
      CUR.error = TT_Err_Invalid_Reference;
      return;
    }

    for ( I = L; I <= K; I++ )
      CUR.pts.touch[I] |= TT_Flag_On_Curve;
  }


/**********************************************/
/* FLIPRGOFF : FLIP RanGe OFF                 */
/* CodeRange : $82                            */
/* Stack     : uint32 uint32 -->              */

  static void  Ins_FLIPRGOFF( INS_ARG )
  {
    ULong  I, K, L;


    K = (ULong) args[1];
    L = (ULong) args[0];

    if ( BOUNDS( K, CUR.pts.n_points ) ||
         BOUNDS( L, CUR.pts.n_points ) )
    {
      CUR.error = TT_Err_Invalid_Reference;
      return;
    }

    for ( I = L; I <= K; I++ )
      CUR.pts.touch[I] &= ~TT_Flag_On_Curve;
  }


  static Bool  Compute_Point_Displacement( EXEC_OPS
                                           PCoordinates  x,
                                           PCoordinates  y,
                                           PGlyph_Zone   zone,
                                           UShort*       refp )
  {
    TGlyph_Zone  zp;
    UShort       p;
    TT_F26Dot6   d;


    if ( CUR.opcode & 1 )
    {
      zp = CUR.zp0;
      p  = CUR.GS.rp1;
    }
    else
    {
      zp = CUR.zp1;
      p  = CUR.GS.rp2;
    }

    if ( BOUNDS( p, zp.n_points ) )
    {
      CUR.error = TT_Err_Invalid_Displacement;
      return FAILURE;
    }

    *zone = zp;
    *refp = p;

    d = CUR_Func_project( zp.cur + p, zp.org + p );

    *x = TT_MulDiv(d, (Long)CUR.GS.freeVector.x * 0x10000L, CUR.F_dot_P );
    *y = TT_MulDiv(d, (Long)CUR.GS.freeVector.y * 0x10000L, CUR.F_dot_P );

    return SUCCESS;
  }


  static void  Move_Zp2_Point( EXEC_OPS
                               UShort      point,
                               TT_F26Dot6  dx,
                               TT_F26Dot6  dy,
                               Bool        touch )
  {
    if ( CUR.GS.freeVector.x != 0 )
    {
      CUR.zp2.cur[point].x += dx;
      if ( touch )
        CUR.zp2.touch[point] |= TT_Flag_Touched_X;
    }

    if ( CUR.GS.freeVector.y != 0 )
    {
      CUR.zp2.cur[point].y += dy;
      if ( touch )
        CUR.zp2.touch[point] |= TT_Flag_Touched_Y;
    }
  }


/**********************************************/
/* SHP[a]    : SHift Point by the last point  */
/* CodeRange : $32-33                         */
/* Stack     : uint32... -->                  */

  static void  Ins_SHP( INS_ARG )
  {
    TGlyph_Zone zp;
    UShort      refp;

    TT_F26Dot6  dx,
                dy;
    UShort      point;


    if ( CUR.top < CUR.GS.loop )
    {
      CUR.error = TT_Err_Invalid_Reference;
      return;
    }

    if ( COMPUTE_Point_Displacement( &dx, &dy, &zp, &refp ) )
      return;

    while ( CUR.GS.loop > 0 )
    {
      CUR.args--;
      point = (UShort)CUR.stack[CUR.args];

      if ( BOUNDS( point, CUR.zp2.n_points ) )
      {
        CUR.error = TT_Err_Invalid_Reference;
        return;
      }

      /* UNDOCUMENTED! SHP touches the points */
      MOVE_Zp2_Point( point, dx, dy, TRUE );

      CUR.GS.loop--;
    }

    CUR.GS.loop = 1;
    CUR.new_top = CUR.args;
  }


/**********************************************/
/* SHC[a]    : SHift Contour                  */
/* CodeRange : $34-35                         */
/* Stack     : uint32 -->                     */

  static void  Ins_SHC( INS_ARG )
  {
    TGlyph_Zone zp;
    UShort      refp;
    TT_F26Dot6  dx,
                dy;

    ULong       contour, i;
    UShort      first_point, last_point;


    contour = args[0];

    if ( BOUNDS( contour, CUR.pts.n_contours ) )
    {
      CUR.error = TT_Err_Invalid_Reference;
      return;
    }

    if ( COMPUTE_Point_Displacement( &dx, &dy, &zp, &refp ) )
      return;

    if ( contour == 0 )
      first_point = 0;
    else
      first_point = CUR.pts.contours[contour - 1] + 1;

    last_point = CUR.pts.contours[contour];

    /* XXX: this is probably wrong... at least it prevents memory */
    /*      corruption when zp2 is the twilight zone              */
    if ( last_point > CUR.zp2.n_points )
    {
      if ( CUR.zp2.n_points > 0 )
        last_point = CUR.zp2.n_points - 1;
      else
        last_point = 0;
    }

    /* UNDOCUMENTED! SHC doesn't touch the points */
    for ( i = first_point; i <= last_point; i++ )
    {
      if ( zp.cur != CUR.zp2.cur || refp != i )
        MOVE_Zp2_Point( i, dx, dy, FALSE );
    }
  }


/**********************************************/
/* SHZ[a]    : SHift Zone                     */
/* CodeRange : $36-37                         */
/* Stack     : uint32 -->                     */

  static void  Ins_SHZ( INS_ARG )
  {
    TGlyph_Zone zp;
    UShort      refp;
    TT_F26Dot6  dx,
                dy;

    UShort  last_point;
    Long    i;


    if ( BOUNDS( args[0], 2 ) )
    {
      CUR.error = TT_Err_Invalid_Reference;
      return;
    }

    if ( COMPUTE_Point_Displacement( &dx, &dy, &zp, &refp ) )
      return;

    if ( CUR.zp2.n_points > 0 )
      last_point = CUR.zp2.n_points - 1;
    else
      last_point = 0;

    /* UNDOCUMENTED! SHZ doesn't touch the points */
    for ( i = 0; i <= last_point; i++ )
    {
      if ( zp.cur != CUR.zp2.cur || refp != i )
        MOVE_Zp2_Point( i, dx, dy, FALSE );
    }
  }


/**********************************************/
/* SHPIX[]   : SHift points by a PIXel amount */
/* CodeRange : $38                            */
/* Stack     : f26.6 uint32... -->            */

  static void  Ins_SHPIX( INS_ARG )
  {
    TT_F26Dot6  dx, dy;
    UShort      point;


    if ( CUR.top < CUR.GS.loop + 1 )
    {
      CUR.error = TT_Err_Invalid_Reference;
      return;
    }

    dx = TT_MulDiv( args[0],
                    (Long)CUR.GS.freeVector.x,
                    0x4000 );
    dy = TT_MulDiv( args[0],
                    (Long)CUR.GS.freeVector.y,
                    0x4000 );

    while ( CUR.GS.loop > 0 )
    {
      CUR.args--;

      point = (UShort)CUR.stack[CUR.args];

      if ( BOUNDS( point, CUR.zp2.n_points ) )
      {
        CUR.error = TT_Err_Invalid_Reference;
        return;
      }

      MOVE_Zp2_Point( point, dx, dy, TRUE );

      CUR.GS.loop--;
    }

    CUR.GS.loop = 1;
    CUR.new_top = CUR.args;
  }


/**********************************************/
/* MSIRP[a]  : Move Stack Indirect Relative   */
/* CodeRange : $3A-$3B                        */
/* Stack     : f26.6 uint32 -->               */

  static void  Ins_MSIRP( INS_ARG )
  {
    UShort      point;
    TT_F26Dot6  distance;


    point = (UShort)args[0];

    if ( BOUNDS( point,      CUR.zp1.n_points ) ||
         BOUNDS( CUR.GS.rp0, CUR.zp0.n_points ) )
    {
      CUR.error = TT_Err_Invalid_Reference;
      return;
    }

    /* XXX: UNDOCUMENTED! behaviour */
    if ( CUR.GS.gep0 == 0 )   /* if in twilight zone */
    {
      CUR.zp1.org[point] = CUR.zp0.org[CUR.GS.rp0];
      CUR.zp1.cur[point] = CUR.zp1.org[point];
    }

    distance = CUR_Func_project( CUR.zp1.cur + point,
                                 CUR.zp0.cur + CUR.GS.rp0 );

    CUR_Func_move( &CUR.zp1, point, args[1] - distance );

    CUR.GS.rp1 = CUR.GS.rp0;
    CUR.GS.rp2 = point;

    if ( (CUR.opcode & 1) != 0 )
      CUR.GS.rp0 = point;
  }


/**********************************************/
/* MDAP[a]   : Move Direct Absolute Point     */
/* CodeRange : $2E-$2F                        */
/* Stack     : uint32 -->                     */

  static void  Ins_MDAP( INS_ARG )
  {
    UShort      point;
    TT_F26Dot6  cur_dist,
                distance;


    point = (UShort)args[0];

    if ( BOUNDS( point, CUR.zp0.n_points ) )
    {
      CUR.error = TT_Err_Invalid_Reference;
      return;
    }

    /* XXX: Is there some undocumented feature while in the */
    /*      twilight zone? ?                                */
    if ( (CUR.opcode & 1) != 0 )
    {
      cur_dist = CUR_Func_project( CUR.zp0.cur + point, NULL_Vector );
      distance = CUR_Func_round( cur_dist,
                                 CUR.metrics.compensations[0] ) - cur_dist;
    }
    else
      distance = 0;

    CUR_Func_move( &CUR.zp0, point, distance );

    CUR.GS.rp0 = point;
    CUR.GS.rp1 = point;
  }


/**********************************************/
/* MIAP[a]   : Move Indirect Absolute Point   */
/* CodeRange : $3E-$3F                        */
/* Stack     : uint32 uint32 -->              */

  static void  Ins_MIAP( INS_ARG )
  {
    ULong       cvtEntry;
    UShort      point;
    TT_F26Dot6  distance,
                org_dist;


    cvtEntry = (ULong)args[1];
    point    = (UShort)args[0];

    if ( BOUNDS( point,    CUR.zp0.n_points ) ||
         BOUNDS( cvtEntry, CUR.cvtSize )      )
    {
      CUR.error = TT_Err_Invalid_Reference;
      return;
    }

    /* UNDOCUMENTED!                                     */
    /*                                                   */
    /* The behaviour of an MIAP instruction is quite     */
    /* different when used in the twilight zone.         */
    /*                                                   */
    /* First, no control value cutin test is performed   */
    /* as it would fail anyway.  Second, the original    */
    /* point, i.e. (org_x,org_y) of zp0.point, is set    */
    /* to the absolute, unrounded distance found in      */
    /* the CVT.                                          */
    /*                                                   */
    /* This is used in the CVT programs of the Microsoft */
    /* fonts Arial, Times, etc., in order to re-adjust   */
    /* some key font heights.  It allows the use of the  */
    /* IP instruction in the twilight zone, which        */
    /* otherwise would be "illegal" according to the     */
    /* specs :)                                          */
    /*                                                   */
    /* We implement it with a special sequence for the   */
    /* twilight zone. This is a bad hack, but it seems   */
    /* to work.                                          */

    distance = CUR_Func_read_cvt( cvtEntry );

    if ( CUR.GS.gep0 == 0 )   /* If in twilight zone */
    {
      CUR.zp0.org[point].x = TT_MulDiv( CUR.GS.freeVector.x,
                                        distance, 0x4000L );
      CUR.zp0.org[point].y = TT_MulDiv( CUR.GS.freeVector.y,
                                        distance, 0x4000L );
      CUR.zp0.cur[point] = CUR.zp0.org[point];
    }

    org_dist = CUR_Func_project( CUR.zp0.cur + point, NULL_Vector );

    if ( (CUR.opcode & 1) != 0 )   /* rounding and control cutin flag */
    {
      if ( ABS(distance - org_dist) > CUR.GS.control_value_cutin )
        distance = org_dist;

      distance = CUR_Func_round( distance, CUR.metrics.compensations[0] );
    }

    CUR_Func_move( &CUR.zp0, point, distance - org_dist );

    CUR.GS.rp0 = point;
    CUR.GS.rp1 = point;
  }


/**********************************************/
/* MDRP[abcde] : Move Direct Relative Point   */
/* CodeRange   : $C0-$DF                      */
/* Stack       : uint32 -->                   */

  static void  Ins_MDRP( INS_ARG )
  {
    UShort      point;
    TT_F26Dot6  org_dist, distance;


    point = (UShort)args[0];

    if ( BOUNDS( point,      CUR.zp1.n_points ) ||
         BOUNDS( CUR.GS.rp0, CUR.zp0.n_points ) )
    {
      CUR.error = TT_Err_Invalid_Reference;
      return;
    }

    /* XXX: Is there some undocumented feature while in the */
    /*      twilight zone?                                  */

    org_dist = CUR_Func_dualproj( CUR.zp1.org + point,
                                  CUR.zp0.org + CUR.GS.rp0 );

    /* single width cutin test */

    if ( ABS( org_dist ) < CUR.GS.single_width_cutin )
    {
      if ( org_dist >= 0 )
        org_dist = CUR.GS.single_width_value;
      else
        org_dist = -CUR.GS.single_width_value;
    }

    /* round flag */

    if ( (CUR.opcode & 4) != 0 )
      distance = CUR_Func_round( org_dist,
                                 CUR.metrics.compensations[CUR.opcode & 3] );
    else
      distance = Round_None( EXEC_ARGS
                             org_dist,
                             CUR.metrics.compensations[CUR.opcode & 3]  );

    /* minimum distance flag */

    if ( (CUR.opcode & 8) != 0 )
    {
      if ( org_dist >= 0 )
      {
        if ( distance < CUR.GS.minimum_distance )
          distance = CUR.GS.minimum_distance;
      }
      else
      {
        if ( distance > -CUR.GS.minimum_distance )
          distance = -CUR.GS.minimum_distance;
      }
    }

    /* now move the point */

    org_dist = CUR_Func_project( CUR.zp1.cur + point,
                                 CUR.zp0.cur + CUR.GS.rp0 );

    CUR_Func_move( &CUR.zp1, point, distance - org_dist );

    CUR.GS.rp1 = CUR.GS.rp0;
    CUR.GS.rp2 = point;

    if ( (CUR.opcode & 16) != 0 )
      CUR.GS.rp0 = point;
  }


/**********************************************/
/* MIRP[abcde] : Move Indirect Relative Point */
/* CodeRange   : $E0-$FF                      */
/* Stack       : int32? uint32 -->            */

  static void  Ins_MIRP( INS_ARG )
  {
    UShort      point;
    ULong       cvtEntry;

    TT_F26Dot6  cvt_dist,
                distance,
                cur_dist,
                org_dist;


    point    = (UShort)args[0];
    cvtEntry = (ULong)(args[1] + 1);

    /* XXX: UNDOCUMENTED! cvt[-1] = 0 always */

    if ( BOUNDS( point,      CUR.zp1.n_points ) ||
         BOUNDS( cvtEntry,   CUR.cvtSize + 1 )  ||
         BOUNDS( CUR.GS.rp0, CUR.zp0.n_points ) )
    {
      CUR.error = TT_Err_Invalid_Reference;
      return;
    }

    if ( !cvtEntry )
      cvt_dist = 0;
    else
      cvt_dist = CUR_Func_read_cvt( cvtEntry - 1 );

    /* single width test */

    if ( ABS( cvt_dist ) < CUR.GS.single_width_cutin )
    {
      if ( cvt_dist >= 0 )
        cvt_dist =  CUR.GS.single_width_value;
      else
        cvt_dist = -CUR.GS.single_width_value;
    }

    /* XXX : UNDOCUMENTED! -- twilight zone */

    if ( CUR.GS.gep1 == 0 )
    {
      CUR.zp1.org[point].x = CUR.zp0.org[CUR.GS.rp0].x +
                             TT_MulDiv( cvt_dist,
                                        CUR.GS.freeVector.x,
                                        0x4000 );

      CUR.zp1.org[point].y = CUR.zp0.org[CUR.GS.rp0].y +
                             TT_MulDiv( cvt_dist,
                                        CUR.GS.freeVector.y,
                                        0x4000 );

      CUR.zp1.cur[point] = CUR.zp1.org[point];
    }

    org_dist = CUR_Func_dualproj( CUR.zp1.org + point,
                                  CUR.zp0.org + CUR.GS.rp0 );

    cur_dist = CUR_Func_project( CUR.zp1.cur + point,
                                 CUR.zp0.cur + CUR.GS.rp0 );

    /* auto-flip test */

    if ( CUR.GS.auto_flip )
    {
      if ( (org_dist ^ cvt_dist) < 0 )
        cvt_dist = -cvt_dist;
    }

    /* control value cutin and round */

    if ( (CUR.opcode & 4) != 0 )
    {
      /* XXX: UNDOCUMENTED!  Only perform cut-in test when both points */
      /*      refer to the same zone.                                  */

      if ( CUR.GS.gep0 == CUR.GS.gep1 )
        if ( ABS( cvt_dist - org_dist ) >= CUR.GS.control_value_cutin )
          cvt_dist = org_dist;

      distance = CUR_Func_round( cvt_dist,
                                 CUR.metrics.compensations[CUR.opcode & 3] );
    }
    else
      distance = Round_None( EXEC_ARGS
                             cvt_dist,
                             CUR.metrics.compensations[CUR.opcode & 3] );

    /* minimum distance test */

    if ( (CUR.opcode & 8) != 0 )
    {
      if ( org_dist >= 0 )
      {
        if ( distance < CUR.GS.minimum_distance )
          distance = CUR.GS.minimum_distance;
      }
      else
      {
        if ( distance > -CUR.GS.minimum_distance )
          distance = -CUR.GS.minimum_distance;
      }
    }

    CUR_Func_move( &CUR.zp1, point, distance - cur_dist );

    CUR.GS.rp1 = CUR.GS.rp0;

    if ( (CUR.opcode & 16) != 0 )
      CUR.GS.rp0 = point;

    /* UNDOCUMENTED! */

    CUR.GS.rp2 = point;
  }


/**********************************************/
/* ALIGNRP[]   : ALIGN Relative Point         */
/* CodeRange   : $3C                          */
/* Stack       : uint32 uint32... -->         */

  static void  Ins_ALIGNRP( INS_ARG )
  {
    ULong       point;
    TT_F26Dot6  distance;


    if ( CUR.top < CUR.GS.loop ||
         BOUNDS( CUR.GS.rp0, CUR.zp0.n_points ) )
    {
      CUR.error = TT_Err_Invalid_Reference;
      return;
    }

    while ( CUR.GS.loop > 0 )
    {
      CUR.args--;

      point = (ULong)CUR.stack[CUR.args];

      if ( BOUNDS( point, CUR.zp1.n_points ) )
      {
        CUR.error = TT_Err_Invalid_Reference;
        return;
      }

      distance = CUR_Func_project( CUR.zp1.cur + point,
                                   CUR.zp0.cur + CUR.GS.rp0 );

      CUR_Func_move( &CUR.zp1, point, -distance );
      CUR.GS.loop--;
    }

    CUR.GS.loop = 1;
    CUR.new_top = CUR.args;
  }


/**********************************************/
/* ISECT[]     : moves point to InterSECTion  */
/* CodeRange   : $0F                          */
/* Stack       : 5 * uint32 -->               */

  static void  Ins_ISECT( INS_ARG )
  {
    ULong  point,
           a0, a1,
           b0, b1;

    TT_F26Dot6  discriminant;

    TT_F26Dot6  dx,  dy,
                dax, day,
                dbx, dby;

    TT_F26Dot6  val;

    TT_Vector   R;


    point = (ULong) args[0];

    a0 = (ULong) args[1];
    a1 = (ULong) args[2];
    b0 = (ULong) args[3];
    b1 = (ULong) args[4];

    if ( BOUNDS( b0, CUR.zp0.n_points ) ||
         BOUNDS( b1, CUR.zp0.n_points ) ||
         BOUNDS( a0, CUR.zp1.n_points ) ||
         BOUNDS( a1, CUR.zp1.n_points ) ||
         BOUNDS( point, CUR.zp2.n_points ) )
    {
      CUR.error = TT_Err_Invalid_Reference;
      return;
    }

    dbx = CUR.zp0.cur[b1].x - CUR.zp0.cur[b0].x;
    dby = CUR.zp0.cur[b1].y - CUR.zp0.cur[b0].y;

    dax = CUR.zp1.cur[a1].x - CUR.zp1.cur[a0].x;
    day = CUR.zp1.cur[a1].y - CUR.zp1.cur[a0].y;

    dx = CUR.zp0.cur[b0].x - CUR.zp1.cur[a0].x;
    dy = CUR.zp0.cur[b0].y - CUR.zp1.cur[a0].y;

    CUR.zp2.touch[point] |= TT_Flag_Touched_Both;

    discriminant = TT_MulDiv( dax, -dby, 0x40L ) +
                   TT_MulDiv( day, dbx, 0x40L );

    if ( ABS( discriminant ) >= 0x40 )
    {
      val = TT_MulDiv( dx, -dby, 0x40L ) + TT_MulDiv( dy, dbx, 0x40L );

      R.x = TT_MulDiv( val, dax, discriminant );
      R.y = TT_MulDiv( val, day, discriminant );

      CUR.zp2.cur[point].x = CUR.zp1.cur[a0].x + R.x;
      CUR.zp2.cur[point].y = CUR.zp1.cur[a0].y + R.y;
    }
    else
    {
      /* else, take the middle of the middles of A and B */

      CUR.zp2.cur[point].x = ( CUR.zp1.cur[a0].x +
                               CUR.zp1.cur[a1].x +
                               CUR.zp0.cur[b0].x +
                               CUR.zp0.cur[b1].x ) / 4;
      CUR.zp2.cur[point].y = ( CUR.zp1.cur[a0].y +
                               CUR.zp1.cur[a1].y +
                               CUR.zp0.cur[b0].y +
                               CUR.zp0.cur[b1].y ) / 4;
    }
  }


/**********************************************/
/* ALIGNPTS[]  : ALIGN PoinTS                 */
/* CodeRange   : $27                          */
/* Stack       : uint32 uint32 -->            */

  static void  Ins_ALIGNPTS( INS_ARG )
  {
    ULong       p1, p2;
    TT_F26Dot6  distance;


    p1 = (ULong)args[0];
    p2 = (ULong)args[1];

    if ( BOUNDS( args[0], CUR.zp1.n_points ) ||
         BOUNDS( args[1], CUR.zp0.n_points ) )
    {
      CUR.error = TT_Err_Invalid_Reference;
      return;
    }

    distance = CUR_Func_project( CUR.zp0.cur + p2,
                                 CUR.zp1.cur + p1 ) / 2;

    CUR_Func_move( &CUR.zp1, p1, distance );
    CUR_Func_move( &CUR.zp0, p2, -distance );
  }


/**********************************************/
/* IP[]        : Interpolate Point            */
/* CodeRange   : $39                          */
/* Stack       : uint32... -->                */

  static void  Ins_IP( INS_ARG )
  {
    TT_F26Dot6  org_a, org_b, org_x,
                cur_a, cur_b, cur_x,
                distance;
    UShort      point;


    if ( CUR.top < CUR.GS.loop )
    {
      CUR.error = TT_Err_Invalid_Reference;
      return;
    }

    /* XXX: there are some glyphs in some braindead but popular  */
    /*      fonts out there (e.g. [aeu]grave in monotype.ttf)    */
    /*      calling IP[] with bad values of rp[12]               */
    /*      do something sane when this odd thing happens        */

    if ( BOUNDS( CUR.GS.rp1, CUR.zp0.n_points ) ||
         BOUNDS( CUR.GS.rp2, CUR.zp1.n_points ) )
    {
      org_a = cur_a = 0;
      org_b = cur_b = 0;
    }
    else
    {
      org_a = CUR_Func_dualproj( CUR.zp0.org + CUR.GS.rp1, NULL_Vector );
      org_b = CUR_Func_dualproj( CUR.zp1.org + CUR.GS.rp2, NULL_Vector );

      cur_a = CUR_Func_project( CUR.zp0.cur + CUR.GS.rp1, NULL_Vector );
      cur_b = CUR_Func_project( CUR.zp1.cur + CUR.GS.rp2, NULL_Vector );
    }

    while ( CUR.GS.loop > 0 )
    {
      CUR.args--;

      point = (UShort)CUR.stack[CUR.args];
      if ( BOUNDS( point, CUR.zp2.n_points ) )
      {
        CUR.error = TT_Err_Invalid_Reference;
        return;
      }

      org_x = CUR_Func_dualproj( CUR.zp2.org + point, NULL_Vector );
      cur_x = CUR_Func_project ( CUR.zp2.cur + point, NULL_Vector );

      if ( ( org_a <= org_b && org_x <= org_a ) ||
           ( org_a >  org_b && org_x >= org_a ) )

        distance = ( cur_a - org_a ) + ( org_x - cur_x );

      else if ( ( org_a <= org_b  &&  org_x >= org_b ) ||
                ( org_a >  org_b  &&  org_x <  org_b ) )

        distance = ( cur_b - org_b ) + ( org_x - cur_x );

      else
         /* note: it seems that rounding this value isn't a good */
         /*       idea (cf. width of capital 'S' in Times)       */

         distance = TT_MulDiv( cur_b - cur_a,
                               org_x - org_a,
                               org_b - org_a ) + ( cur_a - cur_x );

      CUR_Func_move( &CUR.zp2, point, distance );

      CUR.GS.loop--;
    }

    CUR.GS.loop = 1;
    CUR.new_top = CUR.args;
  }


/**********************************************/
/* UTP[a]      : UnTouch Point                */
/* CodeRange   : $29                          */
/* Stack       : uint32 -->                   */

  static void  Ins_UTP( INS_ARG )
  {
    ULong  point;
    Byte   mask;


    point = (ULong) args[0];

    if ( BOUNDS( point, CUR.zp0.n_points ) )
    {
      CUR.error = TT_Err_Invalid_Reference;
      return;
    }

    mask = 0xFF;

    if ( CUR.GS.freeVector.x != 0 )
      mask &= ~TT_Flag_Touched_X;

    if ( CUR.GS.freeVector.y != 0 )
      mask &= ~TT_Flag_Touched_Y;

    CUR.zp0.touch[point] &= mask;
  }


  /* Local variables for Ins_IUP: */
  struct LOC_Ins_IUP
  {
    TT_Vector*  orgs;   /* original and current coordinate */
    TT_Vector*  curs;   /* arrays                          */
  };


  static void  Shift( UShort  p1,
                      UShort  p2,
                      UShort  p,
                      struct LOC_Ins_IUP*  LINK )
  {
    UShort      i;
    TT_F26Dot6  x;


    x = LINK->curs[p].x - LINK->orgs[p].x;

    for ( i = p1; i < p; i++ )
      LINK->curs[i].x += x;

    for ( i = p + 1; i <= p2; i++ )
      LINK->curs[i].x += x;
  }


  static void  Interp( UShort  p1, UShort  p2,
                       UShort  ref1, UShort  ref2,
                       struct LOC_Ins_IUP*  LINK )
  {
    UShort      i;
    TT_F26Dot6  x, x1, x2, d1, d2;


    if ( p1 > p2 )
      return;

    x1 = LINK->orgs[ref1].x;
    d1 = LINK->curs[ref1].x - LINK->orgs[ref1].x;
    x2 = LINK->orgs[ref2].x;
    d2 = LINK->curs[ref2].x - LINK->orgs[ref2].x;

    if ( x1 == x2 )
    {
      for ( i = p1; i <= p2; i++ )
      {
        x = LINK->orgs[i].x;

        if ( x <= x1 )
          x += d1;
        else
          x += d2;

        LINK->curs[i].x = x;
      }
      return;
    }

    if ( x1 < x2 )
    {
      for ( i = p1; i <= p2; i++ )
      {
        x = LINK->orgs[i].x;

        if ( x <= x1 )
          x += d1;
        else
        {
          if ( x >= x2 )
            x += d2;
          else
            x = LINK->curs[ref1].x +
                  TT_MulDiv( x - x1,
                             LINK->curs[ref2].x - LINK->curs[ref1].x,
                             x2 - x1 );
        }
        LINK->curs[i].x = x;
      }
      return;
    }

    /* x2 < x1 */

    for ( i = p1; i <= p2; i++ )
    {
      x = LINK->orgs[i].x;
      if ( x <= x2 )
        x += d2;
      else
      {
        if ( x >= x1 )
          x += d1;
        else
          x = LINK->curs[ref1].x +
              TT_MulDiv( x - x1,
                         LINK->curs[ref2].x - LINK->curs[ref1].x,
                         x2 - x1 );
      }
      LINK->curs[i].x = x;
    }
  }


/**********************************************/
/* IUP[a]      : Interpolate Untouched Points */
/* CodeRange   : $30-$31                      */
/* Stack       : -->                          */

  static void  Ins_IUP( INS_ARG )
  {
    struct LOC_Ins_IUP  V;
    Byte                mask;

    UShort  first_point;   /* first point of contour        */
    UShort  end_point;     /* end point (last+1) of contour */

    UShort  first_touched; /* first touched point in contour   */
    UShort  cur_touched;   /* current touched point in contour */

    UShort  point;         /* current point   */
    UShort  contour;       /* current contour */


    if ( CUR.opcode & 1 )
    {
      mask   = TT_Flag_Touched_X;
      V.orgs = CUR.pts.org;
      V.curs = CUR.pts.cur;
    }
    else
    {
      mask   = TT_Flag_Touched_Y;
      V.orgs = (TT_Vector*)( ((TT_F26Dot6*)CUR.pts.org) + 1 );
      V.curs = (TT_Vector*)( ((TT_F26Dot6*)CUR.pts.cur) + 1 );
    }

    contour = 0;
    point   = 0;

    do
    {
      end_point   = CUR.pts.contours[contour];
      first_point = point;

      while ( point <= end_point && (CUR.pts.touch[point] & mask) == 0 )
        point++;

      if ( point <= end_point )
      {
        first_touched = point;
        cur_touched   = point;

        point++;

        while ( point <= end_point )
        {
          if ( (CUR.pts.touch[point] & mask) != 0 )
          {
            if ( point > 0 )
              Interp( cur_touched + 1,
                      point - 1,
                      cur_touched,
                      point,
                      &V );
            cur_touched = point;
          }

          point++;
        }

        if ( cur_touched == first_touched )
          Shift( (Int)first_point, (Int)end_point, (Int)cur_touched, &V );
        else
        {
          Interp( cur_touched + 1,
                  end_point,
                  cur_touched,
                  first_touched,
                  &V );

          if ( first_touched > 0 )
            Interp( first_point,
                    first_touched - 1,
                    cur_touched,
                    first_touched,
                    &V );
        }
      }
      contour++;
    } while ( contour < CUR.pts.n_contours );
  }


/**********************************************/
/* DELTAPn[]   : DELTA Exceptions P1, P2, P3  */
/* CodeRange   : $5D,$71,$72                  */
/* Stack       : uint32 (2 * uint32)... -->   */

  static void  Ins_DELTAP( INS_ARG )
  {
    UShort  k, nump;
    ULong   A, C;
    Long    B;


    nump = (UShort)args[0];

    for ( k = 1; k <= nump; k++ )
    {
      if ( CUR.args < 2 )
      {
        CUR.error = TT_Err_Too_Few_Arguments;
        return;
      }

      CUR.args -= 2;

      A = (ULong) CUR.stack[CUR.args + 1];
      B = CUR.stack[CUR.args];

      /* XXX : because some popular fonts contain some invalid DeltaP */
      /*       instructions, we simply ignore them when the stacked   */
      /*       point reference is off limit, rather than returning an */
      /*       error. As a delta instruction doesn't change a glyph   */
      /*       in great ways, this shouldn't be a problem..           */

      if ( !BOUNDS( A, CUR.zp0.n_points ) )
      {
        C = ((ULong)B & 0xF0) >> 4;

        switch ( CUR.opcode )
        {
        case 0x5d:
          break;

        case 0x71:
          C += 16;
          break;

        case 0x72:
          C += 32;
          break;
        }

        C += CUR.GS.delta_base;

        if ( CURRENT_Ppem() == C )
        {
          B = (B & 0xF) - 8;
          if ( B >= 0 )
            B++;
          B = B * 64 / (1L << CUR.GS.delta_shift);

          CUR_Func_move( &CUR.zp0, A, (Int)B );
        }
      }
    }

    CUR.new_top = CUR.args;
  }


/**********************************************/
/* DELTACn[]   : DELTA Exceptions C1, C2, C3  */
/* CodeRange   : $73,$74,$75                  */
/* Stack       : uint32 (2 * uint32)... -->   */

  static void  Ins_DELTAC( INS_ARG )
  {
    UShort  nump, k;
    ULong   A, C;
    Long    B;


    nump = (UShort)args[0];

    for ( k = 1; k <= nump; k++ )
    {
      if ( CUR.args < 2 )
      {
        CUR.error = TT_Err_Too_Few_Arguments;
        return;
      }

      CUR.args -= 2;

      A = (ULong) CUR.stack[CUR.args + 1];
      B = CUR.stack[CUR.args];

      if ( BOUNDS( A, CUR.cvtSize ) )
      {
        CUR.error = TT_Err_Invalid_Reference;
        return;
      }

      C = ((ULong)B & 0xF0) >> 4;

      switch ( CUR.opcode )
      {
      case 0x73:
        break;

      case 0x74:
        C += 16;
        break;

      case 0x75:
        C += 32;
        break;
      }

      C += CUR.GS.delta_base;

      if ( CURRENT_Ppem() == C )
      {
        B = (B & 0xF) - 8;
        if ( B >= 0 )
          B++;
        B = B * 64 / (1L << CUR.GS.delta_shift);

        CUR_Func_move_cvt( A, B );
      }
    }

    CUR.new_top = CUR.args;
  }



/****************************************************************/
/*                                                              */
/* MISC. INSTRUCTIONS                                           */
/*                                                              */
/****************************************************************/


/**********************************************/
/* GETINFO[]   : GET INFOrmation              */
/* CodeRange   : $88                          */
/* Stack       : uint32 --> uint32            */

/* XXX According to Apple specs, bits 1 & 2 of the argument ought to be */
/*     consulted before rotated / stretched info is returned            */

  static void  Ins_GETINFO( INS_ARG )
  {
    Long  K;


    K = 0;

    /* We return then Windows 3.1 version number */
    /* for the font scaler                       */
    if ( (args[0] & 1) != 0 )
      K = 3;

    /* Has the glyph been rotated ? */
    if ( CUR.metrics.rotated )
      K |= 0x80;

    /* Has the glyph been stretched ? */
    if ( CUR.metrics.stretched )
      K |= 0x100;

    args[0] = K;
  }


  static void  Ins_UNKNOWN( INS_ARG )
  {
    CUR.error = TT_Err_Invalid_Opcode;
  }


#ifndef TT_CONFIG_OPTION_INTERPRETER_SWITCH
  static TInstruction_Function  Instruct_Dispatch[256] =
  {
    /* Opcodes are gathered in groups of 16. */
    /* Please keep the spaces as they are.   */

    /*  SVTCA  y  */  Ins_SVTCA,
    /*  SVTCA  x  */  Ins_SVTCA,
    /*  SPvTCA y  */  Ins_SPVTCA,
    /*  SPvTCA x  */  Ins_SPVTCA,
    /*  SFvTCA y  */  Ins_SFVTCA,
    /*  SFvTCA x  */  Ins_SFVTCA,
    /*  SPvTL //  */  Ins_SPVTL,
    /*  SPvTL +   */  Ins_SPVTL,
    /*  SFvTL //  */  Ins_SFVTL,
    /*  SFvTL +   */  Ins_SFVTL,
    /*  SPvFS     */  Ins_SPVFS,
    /*  SFvFS     */  Ins_SFVFS,
    /*  GPV       */  Ins_GPV,
    /*  GFV       */  Ins_GFV,
    /*  SFvTPv    */  Ins_SFVTPV,
    /*  ISECT     */  Ins_ISECT,

    /*  SRP0      */  Ins_SRP0,
    /*  SRP1      */  Ins_SRP1,
    /*  SRP2      */  Ins_SRP2,
    /*  SZP0      */  Ins_SZP0,
    /*  SZP1      */  Ins_SZP1,
    /*  SZP2      */  Ins_SZP2,
    /*  SZPS      */  Ins_SZPS,
    /*  SLOOP     */  Ins_SLOOP,
    /*  RTG       */  Ins_RTG,
    /*  RTHG      */  Ins_RTHG,
    /*  SMD       */  Ins_SMD,
    /*  ELSE      */  Ins_ELSE,
    /*  JMPR      */  Ins_JMPR,
    /*  SCvTCi    */  Ins_SCVTCI,
    /*  SSwCi     */  Ins_SSWCI,
    /*  SSW       */  Ins_SSW,

    /*  DUP       */  Ins_DUP,
    /*  POP       */  Ins_POP,
    /*  CLEAR     */  Ins_CLEAR,
    /*  SWAP      */  Ins_SWAP,
    /*  DEPTH     */  Ins_DEPTH,
    /*  CINDEX    */  Ins_CINDEX,
    /*  MINDEX    */  Ins_MINDEX,
    /*  AlignPTS  */  Ins_ALIGNPTS,
    /*  INS_$28   */  Ins_UNKNOWN,
    /*  UTP       */  Ins_UTP,
    /*  LOOPCALL  */  Ins_LOOPCALL,
    /*  CALL      */  Ins_CALL,
    /*  FDEF      */  Ins_FDEF,
    /*  ENDF      */  Ins_ENDF,
    /*  MDAP[0]   */  Ins_MDAP,
    /*  MDAP[1]   */  Ins_MDAP,

    /*  IUP[0]    */  Ins_IUP,
    /*  IUP[1]    */  Ins_IUP,
    /*  SHP[0]    */  Ins_SHP,
    /*  SHP[1]    */  Ins_SHP,
    /*  SHC[0]    */  Ins_SHC,
    /*  SHC[1]    */  Ins_SHC,
    /*  SHZ[0]    */  Ins_SHZ,
    /*  SHZ[1]    */  Ins_SHZ,
    /*  SHPIX     */  Ins_SHPIX,
    /*  IP        */  Ins_IP,
    /*  MSIRP[0]  */  Ins_MSIRP,
    /*  MSIRP[1]  */  Ins_MSIRP,
    /*  AlignRP   */  Ins_ALIGNRP,
    /*  RTDG      */  Ins_RTDG,
    /*  MIAP[0]   */  Ins_MIAP,
    /*  MIAP[1]   */  Ins_MIAP,

    /*  NPushB    */  Ins_NPUSHB,
    /*  NPushW    */  Ins_NPUSHW,
    /*  WS        */  Ins_WS,
    /*  RS        */  Ins_RS,
    /*  WCvtP     */  Ins_WCVTP,
    /*  RCvt      */  Ins_RCVT,
    /*  GC[0]     */  Ins_GC,
    /*  GC[1]     */  Ins_GC,
    /*  SCFS      */  Ins_SCFS,
    /*  MD[0]     */  Ins_MD,
    /*  MD[1]     */  Ins_MD,
    /*  MPPEM     */  Ins_MPPEM,
    /*  MPS       */  Ins_MPS,
    /*  FlipON    */  Ins_FLIPON,
    /*  FlipOFF   */  Ins_FLIPOFF,
    /*  DEBUG     */  Ins_DEBUG,

    /*  LT        */  Ins_LT,
    /*  LTEQ      */  Ins_LTEQ,
    /*  GT        */  Ins_GT,
    /*  GTEQ      */  Ins_GTEQ,
    /*  EQ        */  Ins_EQ,
    /*  NEQ       */  Ins_NEQ,
    /*  ODD       */  Ins_ODD,
    /*  EVEN      */  Ins_EVEN,
    /*  IF        */  Ins_IF,
    /*  EIF       */  Ins_EIF,
    /*  AND       */  Ins_AND,
    /*  OR        */  Ins_OR,
    /*  NOT       */  Ins_NOT,
    /*  DeltaP1   */  Ins_DELTAP,
    /*  SDB       */  Ins_SDB,
    /*  SDS       */  Ins_SDS,

    /*  ADD       */  Ins_ADD,
    /*  SUB       */  Ins_SUB,
    /*  DIV       */  Ins_DIV,
    /*  MUL       */  Ins_MUL,
    /*  ABS       */  Ins_ABS,
    /*  NEG       */  Ins_NEG,
    /*  FLOOR     */  Ins_FLOOR,
    /*  CEILING   */  Ins_CEILING,
    /*  ROUND[0]  */  Ins_ROUND,
    /*  ROUND[1]  */  Ins_ROUND,
    /*  ROUND[2]  */  Ins_ROUND,
    /*  ROUND[3]  */  Ins_ROUND,
    /*  NROUND[0] */  Ins_NROUND,
    /*  NROUND[1] */  Ins_NROUND,
    /*  NROUND[2] */  Ins_NROUND,
    /*  NROUND[3] */  Ins_NROUND,

    /*  WCvtF     */  Ins_WCVTF,
    /*  DeltaP2   */  Ins_DELTAP,
    /*  DeltaP3   */  Ins_DELTAP,
    /*  DeltaCn[0] */ Ins_DELTAC,
    /*  DeltaCn[1] */ Ins_DELTAC,
    /*  DeltaCn[2] */ Ins_DELTAC,
    /*  SROUND    */  Ins_SROUND,
    /*  S45Round  */  Ins_S45ROUND,
    /*  JROT      */  Ins_JROT,
    /*  JROF      */  Ins_JROF,
    /*  ROFF      */  Ins_ROFF,
    /*  INS_$7B   */  Ins_UNKNOWN,
    /*  RUTG      */  Ins_RUTG,
    /*  RDTG      */  Ins_RDTG,
    /*  SANGW     */  Ins_SANGW,
    /*  AA        */  Ins_AA,

    /*  FlipPT    */  Ins_FLIPPT,
    /*  FlipRgON  */  Ins_FLIPRGON,
    /*  FlipRgOFF */  Ins_FLIPRGOFF,
    /*  INS_$83   */  Ins_UNKNOWN,
    /*  INS_$84   */  Ins_UNKNOWN,
    /*  ScanCTRL  */  Ins_SCANCTRL,
    /*  SDPVTL[0] */  Ins_SDPVTL,
    /*  SDPVTL[1] */  Ins_SDPVTL,
    /*  GetINFO   */  Ins_GETINFO,
    /*  IDEF      */  Ins_IDEF,
    /*  ROLL      */  Ins_ROLL,
    /*  MAX       */  Ins_MAX,
    /*  MIN       */  Ins_MIN,
    /*  ScanTYPE  */  Ins_SCANTYPE,
    /*  InstCTRL  */  Ins_INSTCTRL,
    /*  INS_$8F   */  Ins_UNKNOWN,

    /*  INS_$90  */   Ins_UNKNOWN,
    /*  INS_$91  */   Ins_UNKNOWN,
    /*  INS_$92  */   Ins_UNKNOWN,
    /*  INS_$93  */   Ins_UNKNOWN,
    /*  INS_$94  */   Ins_UNKNOWN,
    /*  INS_$95  */   Ins_UNKNOWN,
    /*  INS_$96  */   Ins_UNKNOWN,
    /*  INS_$97  */   Ins_UNKNOWN,
    /*  INS_$98  */   Ins_UNKNOWN,
    /*  INS_$99  */   Ins_UNKNOWN,
    /*  INS_$9A  */   Ins_UNKNOWN,
    /*  INS_$9B  */   Ins_UNKNOWN,
    /*  INS_$9C  */   Ins_UNKNOWN,
    /*  INS_$9D  */   Ins_UNKNOWN,
    /*  INS_$9E  */   Ins_UNKNOWN,
    /*  INS_$9F  */   Ins_UNKNOWN,

    /*  INS_$A0  */   Ins_UNKNOWN,
    /*  INS_$A1  */   Ins_UNKNOWN,
    /*  INS_$A2  */   Ins_UNKNOWN,
    /*  INS_$A3  */   Ins_UNKNOWN,
    /*  INS_$A4  */   Ins_UNKNOWN,
    /*  INS_$A5  */   Ins_UNKNOWN,
    /*  INS_$A6  */   Ins_UNKNOWN,
    /*  INS_$A7  */   Ins_UNKNOWN,
    /*  INS_$A8  */   Ins_UNKNOWN,
    /*  INS_$A9  */   Ins_UNKNOWN,
    /*  INS_$AA  */   Ins_UNKNOWN,
    /*  INS_$AB  */   Ins_UNKNOWN,
    /*  INS_$AC  */   Ins_UNKNOWN,
    /*  INS_$AD  */   Ins_UNKNOWN,
    /*  INS_$AE  */   Ins_UNKNOWN,
    /*  INS_$AF  */   Ins_UNKNOWN,

    /*  PushB[0]  */  Ins_PUSHB,
    /*  PushB[1]  */  Ins_PUSHB,
    /*  PushB[2]  */  Ins_PUSHB,
    /*  PushB[3]  */  Ins_PUSHB,
    /*  PushB[4]  */  Ins_PUSHB,
    /*  PushB[5]  */  Ins_PUSHB,
    /*  PushB[6]  */  Ins_PUSHB,
    /*  PushB[7]  */  Ins_PUSHB,
    /*  PushW[0]  */  Ins_PUSHW,
    /*  PushW[1]  */  Ins_PUSHW,
    /*  PushW[2]  */  Ins_PUSHW,
    /*  PushW[3]  */  Ins_PUSHW,
    /*  PushW[4]  */  Ins_PUSHW,
    /*  PushW[5]  */  Ins_PUSHW,
    /*  PushW[6]  */  Ins_PUSHW,
    /*  PushW[7]  */  Ins_PUSHW,

    /*  MDRP[00]  */  Ins_MDRP,
    /*  MDRP[01]  */  Ins_MDRP,
    /*  MDRP[02]  */  Ins_MDRP,
    /*  MDRP[03]  */  Ins_MDRP,
    /*  MDRP[04]  */  Ins_MDRP,
    /*  MDRP[05]  */  Ins_MDRP,
    /*  MDRP[06]  */  Ins_MDRP,
    /*  MDRP[07]  */  Ins_MDRP,
    /*  MDRP[08]  */  Ins_MDRP,
    /*  MDRP[09]  */  Ins_MDRP,
    /*  MDRP[10]  */  Ins_MDRP,
    /*  MDRP[11]  */  Ins_MDRP,
    /*  MDRP[12]  */  Ins_MDRP,
    /*  MDRP[13]  */  Ins_MDRP,
    /*  MDRP[14]  */  Ins_MDRP,
    /*  MDRP[15]  */  Ins_MDRP,

    /*  MDRP[16]  */  Ins_MDRP,
    /*  MDRP[17]  */  Ins_MDRP,
    /*  MDRP[18]  */  Ins_MDRP,
    /*  MDRP[19]  */  Ins_MDRP,
    /*  MDRP[20]  */  Ins_MDRP,
    /*  MDRP[21]  */  Ins_MDRP,
    /*  MDRP[22]  */  Ins_MDRP,
    /*  MDRP[23]  */  Ins_MDRP,
    /*  MDRP[24]  */  Ins_MDRP,
    /*  MDRP[25]  */  Ins_MDRP,
    /*  MDRP[26]  */  Ins_MDRP,
    /*  MDRP[27]  */  Ins_MDRP,
    /*  MDRP[28]  */  Ins_MDRP,
    /*  MDRP[29]  */  Ins_MDRP,
    /*  MDRP[30]  */  Ins_MDRP,
    /*  MDRP[31]  */  Ins_MDRP,

    /*  MIRP[00]  */  Ins_MIRP,
    /*  MIRP[01]  */  Ins_MIRP,
    /*  MIRP[02]  */  Ins_MIRP,
    /*  MIRP[03]  */  Ins_MIRP,
    /*  MIRP[04]  */  Ins_MIRP,
    /*  MIRP[05]  */  Ins_MIRP,
    /*  MIRP[06]  */  Ins_MIRP,
    /*  MIRP[07]  */  Ins_MIRP,
    /*  MIRP[08]  */  Ins_MIRP,
    /*  MIRP[09]  */  Ins_MIRP,
    /*  MIRP[10]  */  Ins_MIRP,
    /*  MIRP[11]  */  Ins_MIRP,
    /*  MIRP[12]  */  Ins_MIRP,
    /*  MIRP[13]  */  Ins_MIRP,
    /*  MIRP[14]  */  Ins_MIRP,
    /*  MIRP[15]  */  Ins_MIRP,

    /*  MIRP[16]  */  Ins_MIRP,
    /*  MIRP[17]  */  Ins_MIRP,
    /*  MIRP[18]  */  Ins_MIRP,
    /*  MIRP[19]  */  Ins_MIRP,
    /*  MIRP[20]  */  Ins_MIRP,
    /*  MIRP[21]  */  Ins_MIRP,
    /*  MIRP[22]  */  Ins_MIRP,
    /*  MIRP[23]  */  Ins_MIRP,
    /*  MIRP[24]  */  Ins_MIRP,
    /*  MIRP[25]  */  Ins_MIRP,
    /*  MIRP[26]  */  Ins_MIRP,
    /*  MIRP[27]  */  Ins_MIRP,
    /*  MIRP[28]  */  Ins_MIRP,
    /*  MIRP[29]  */  Ins_MIRP,
    /*  MIRP[30]  */  Ins_MIRP,
    /*  MIRP[31]  */  Ins_MIRP
  };
#endif


/****************************************************************/
/*                                                              */
/*                    RUN                                       */
/*                                                              */
/*  This function executes a run of opcodes.  It will exit      */
/*  in the following cases:                                     */
/*                                                              */
/*   - Errors (in which case it returns FALSE)                  */
/*                                                              */
/*   - Reaching the end of the main code range (returns TRUE).  */
/*     Reaching the end of a code range within a function       */
/*     call is an error.                                        */
/*                                                              */
/*   - After executing one single opcode, if the flag           */
/*     'Instruction_Trap' is set to TRUE (returns TRUE).        */
/*                                                              */
/*  On exit whith TRUE, test IP < CodeSize to know wether it    */
/*  comes from a instruction trap or a normal termination.      */
/*                                                              */
/*                                                              */
/*     Note:  The documented DEBUG opcode pops a value from     */
/*            the stack.  This behaviour is unsupported, here   */
/*            a DEBUG opcode is always an error.                */
/*                                                              */
/*                                                              */
/* THIS IS THE INTERPRETER'S MAIN LOOP                          */
/*                                                              */
/*  Instructions appear in the specs' order.                    */
/*                                                              */
/****************************************************************/

  LOCAL_DEF
#ifndef DEBUG_INTERPRETER
  TT_Error  RunIns( PExecution_Context  exc )
#else
  TT_Error  RunIns2( PExecution_Context  exc )
#endif
  {
    UShort       A;
    PDefRecord   WITH;
    PCallRecord  WITH1;

    Long         ins_counter = 0;  /* executed instructions counter */

    /* set CVT functions */
    CUR.metrics.ratio = 0;
    if ( CUR.metrics.x_ppem != CUR.metrics.y_ppem )
    {
      /* non-square pixels, use the stretched routines */
      CUR.func_read_cvt  = Read_CVT_Stretched;
      CUR.func_write_cvt = Write_CVT_Stretched;
      CUR.func_move_cvt  = Move_CVT_Stretched;
    }
    else
    {
      /* square pixels, use normal routines */
      CUR.func_read_cvt  = Read_CVT;
      CUR.func_write_cvt = Write_CVT;
      CUR.func_move_cvt  = Move_CVT;
    }

    COMPUTE_Funcs();
    Compute_Round( EXEC_ARGS (Byte)exc->GS.round_state );

    do
    {
      if ( CALC_Length() != SUCCESS )
      {
        CUR.error = TT_Err_Code_Overflow;
        goto LErrorLabel_;
      }

      /* First, let's check for empty stack and overflow */

      CUR.args = CUR.top - (Pop_Push_Count[CUR.opcode] >> 4);

      /* `args' is the top of the stack once arguments have been popped. */
      /* One can also interpret it as the index of the last argument.    */

      if ( CUR.args < 0 )
      {
        CUR.error = TT_Err_Too_Few_Arguments;
        goto LErrorLabel_;
      }

      CUR.new_top = CUR.args + (Pop_Push_Count[CUR.opcode] & 15);

      /* `new_top' is the new top of the stack, after the instruction's */
      /* execution.  `top' will be set to `new_top' after the 'switch'  */
      /* statement.                                                     */

      if ( CUR.new_top > CUR.stackSize )
      {
        CUR.error = TT_Err_Stack_Overflow;
        goto LErrorLabel_;
      }

      CUR.step_ins = TRUE;
      CUR.error    = TT_Err_Ok;

#ifdef TT_CONFIG_OPTION_INTERPRETER_SWITCH
      {
        PStorage        args   = CUR.stack + CUR.args;
        Byte            opcode = CUR.opcode;

#undef   CHECK_BOUNDS
#define  CHECK_BOUNDS( a, b )   if ( BOUNDS( a, b ) ) goto Set_Invalid_Ref;

        switch (opcode)
        {
        case 0x00:  /* SVTCA y  */
        case 0x01:  /* SVTCA x  */
        case 0x02:  /* SPvTCA y */
        case 0x03:  /* SPvTCA x */
        case 0x04:  /* SFvTCA y */
        case 0x05:  /* SFvTCA x */
          {
            Short A, B;


            A = (Short)(opcode & 1) << 14;
            B = A ^ (Short)0x4000;

            if ( opcode < 4 )
            {
              CUR.GS.projVector.x = A;
              CUR.GS.projVector.y = B;

              CUR.GS.dualVector.x = A;
              CUR.GS.dualVector.y = B;
            }

            if ( (opcode & 2) == 0 )
            {
              CUR.GS.freeVector.x = A;
              CUR.GS.freeVector.y = B;
            }

            COMPUTE_Funcs();
          }
          break;

        case 0x06:  /* SPvTL // */
        case 0x07:  /* SPvTL +  */
          DO_SPVTL
          break;

        case 0x08:  /* SFvTL // */
        case 0x09:  /* SFvTL +  */
          DO_SFVTL
          break;

        case 0x0A:  /* SPvFS */
          DO_SPVFS
          break;

        case 0x0B:  /* SFvFS */
          DO_SFVFS
          break;


        case 0x0C:  /* GPV */
          DO_GPV
          break;

        case 0x0D:  /* GFV */
          DO_GFV
          break;

        case 0x0E:  /* SFvTPv */
          DO_SFVTPV
          break;

        case 0x0F:  /* ISECT  */
          Ins_ISECT( EXEC_ARGS  args );
          break;

        case 0x10:  /* SRP0 */
          DO_SRP0
          break;

        case 0x11:  /* SRP1 */
          DO_SRP1
          break;

        case 0x12:  /* SRP2 */
          DO_SRP2
          break;

        case 0x13:  /* SZP0 */
          Ins_SZP0( EXEC_ARGS  args );
          break;

        case 0x14:  /* SZP1 */
          Ins_SZP1( EXEC_ARGS  args );
          break;

        case 0x15:  /* SZP2 */
          Ins_SZP2( EXEC_ARGS  args );
          break;

        case 0x16:  /* SZPS */
          Ins_SZPS( EXEC_ARGS  args );
          break;

        case 0x17:  /* SLOOP */
          DO_SLOOP
          break;

        case 0x18:  /* RTG */
          DO_RTG
          break;

        case 0x19:  /* RTHG */
          DO_RTHG
          break;

        case 0x1A:  /* SMD */
          DO_SMD
          break;

        case 0x1B:  /* ELSE */
          Ins_ELSE( EXEC_ARGS  args );
          break;

        case 0x1C:  /* JMPR */
          DO_JMPR
          break;

        case 0x1D:  /* SCVTCI */
          DO_SCVTCI
          break;

        case 0x1E:  /* SSWCI */
          DO_SSWCI
          break;

        case 0x1F:  /* SSW */
          DO_SSW
          break;

        case 0x20:  /* DUP */
          DO_DUP
          break;

        case 0x21:  /* POP */
          /* nothing :-) !! */
          break;

        case 0x22:  /* CLEAR */
          DO_CLEAR
          break;

        case 0x23:  /* SWAP */
          DO_SWAP
          break;

        case 0x24:  /* DEPTH */
          DO_DEPTH
          break;

        case 0x25:  /* CINDEX */
          DO_CINDEX
          break;

        case 0x26:  /* MINDEX */
          Ins_MINDEX( EXEC_ARGS  args );
          break;

        case 0x27:  /* ALIGNPTS */
          Ins_ALIGNPTS( EXEC_ARGS  args );
          break;

        case 0x28:  /* ???? */
          Ins_UNKNOWN( EXEC_ARGS  args );
          break;

        case 0x29:  /* UTP */
          Ins_UTP( EXEC_ARGS  args );
          break;

        case 0x2A:  /* LOOPCALL */
          Ins_LOOPCALL( EXEC_ARGS  args );
          break;

        case 0x2B:  /* CALL */
          Ins_CALL( EXEC_ARGS  args );
          break;

        case 0x2C:  /* FDEF */
          Ins_FDEF( EXEC_ARGS  args );
          break;

        case 0x2D:  /* ENDF */
          Ins_ENDF( EXEC_ARGS  args );
          break;

        case 0x2E:  /* MDAP */
        case 0x2F:  /* MDAP */
          Ins_MDAP( EXEC_ARGS  args );
          break;


        case 0x30:  /* IUP */
        case 0x31:  /* IUP */
          Ins_IUP( EXEC_ARGS  args );
          break;

        case 0x32:  /* SHP */
        case 0x33:  /* SHP */
          Ins_SHP( EXEC_ARGS  args );
          break;

        case 0x34:  /* SHC */
        case 0x35:  /* SHC */
          Ins_SHC( EXEC_ARGS  args );
          break;

        case 0x36:  /* SHZ */
        case 0x37:  /* SHZ */
          Ins_SHZ( EXEC_ARGS  args );
          break;

        case 0x38:  /* SHPIX */
          Ins_SHPIX( EXEC_ARGS  args );
          break;

        case 0x39:  /* IP    */
          Ins_IP( EXEC_ARGS  args );
          break;

        case 0x3A:  /* MSIRP */
        case 0x3B:  /* MSIRP */
          Ins_MSIRP( EXEC_ARGS  args );
          break;

        case 0x3C:  /* AlignRP */
          Ins_ALIGNRP( EXEC_ARGS  args );
          break;

        case 0x3D:  /* RTDG */
          DO_RTDG
          break;

        case 0x3E:  /* MIAP */
        case 0x3F:  /* MIAP */
          Ins_MIAP( EXEC_ARGS  args );
          break;

        case 0x40:  /* NPUSHB */
          Ins_NPUSHB( EXEC_ARGS  args );
          break;

        case 0x41:  /* NPUSHW */
          Ins_NPUSHW( EXEC_ARGS  args );
          break;

        case 0x42:  /* WS */
          DO_WS
          break;

    Set_Invalid_Ref:
            CUR.error = TT_Err_Invalid_Reference;
          break;

        case 0x43:  /* RS */
          DO_RS
          break;

        case 0x44:  /* WCVTP */
          DO_WCVTP
          break;

        case 0x45:  /* RCVT */
          DO_RCVT
          break;

        case 0x46:  /* GC */
        case 0x47:  /* GC */
          Ins_GC( EXEC_ARGS  args );
          break;

        case 0x48:  /* SCFS */
          Ins_SCFS( EXEC_ARGS  args );
          break;

        case 0x49:  /* MD */
        case 0x4A:  /* MD */
          Ins_MD( EXEC_ARGS  args );
          break;

        case 0x4B:  /* MPPEM */
          DO_MPPEM
          break;

        case 0x4C:  /* MPS */
          DO_MPS
          break;

        case 0x4D:  /* FLIPON */
          DO_FLIPON
          break;

        case 0x4E:  /* FLIPOFF */
          DO_FLIPOFF
          break;

        case 0x4F:  /* DEBUG */
          DO_DEBUG
          break;

        case 0x50:  /* LT */
          DO_LT
          break;

        case 0x51:  /* LTEQ */
          DO_LTEQ
          break;

        case 0x52:  /* GT */
          DO_GT
          break;

        case 0x53:  /* GTEQ */
          DO_GTEQ
          break;

        case 0x54:  /* EQ */
          DO_EQ
          break;

        case 0x55:  /* NEQ */
          DO_NEQ
          break;

        case 0x56:  /* ODD */
          DO_ODD
          break;

        case 0x57:  /* EVEN */
          DO_EVEN
          break;

        case 0x58:  /* IF */
          Ins_IF( EXEC_ARGS  args );
          break;

        case 0x59:  /* EIF */
          /* do nothing */
          break;

        case 0x5A:  /* AND */
          DO_AND
          break;

        case 0x5B:  /* OR */
          DO_OR
          break;

        case 0x5C:  /* NOT */
          DO_NOT
          break;

        case 0x5D:  /* DELTAP1 */
          Ins_DELTAP( EXEC_ARGS  args );
          break;

        case 0x5E:  /* SDB */
          DO_SDB
          break;

        case 0x5F:  /* SDS */
          DO_SDS
          break;

        case 0x60:  /* ADD */
          DO_ADD
          break;

        case 0x61:  /* SUB */
          DO_SUB
          break;

        case 0x62:  /* DIV */
          DO_DIV
          break;

        case 0x63:  /* MUL */
          DO_MUL
          break;

        case 0x64:  /* ABS */
          DO_ABS
          break;

        case 0x65:  /* NEG */
          DO_NEG
          break;

        case 0x66:  /* FLOOR */
          DO_FLOOR
          break;

        case 0x67:  /* CEILING */
          DO_CEILING
          break;

        case 0x68:  /* ROUND */
        case 0x69:  /* ROUND */
        case 0x6A:  /* ROUND */
        case 0x6B:  /* ROUND */
          DO_ROUND
          break;

        case 0x6C:  /* NROUND */
        case 0x6D:  /* NROUND */
        case 0x6E:  /* NRRUND */
        case 0x6F:  /* NROUND */
          DO_NROUND
          break;

        case 0x70:  /* WCVTF */
          DO_WCVTF
          break;

        case 0x71:  /* DELTAP2 */
        case 0x72:  /* DELTAP3 */
          Ins_DELTAP( EXEC_ARGS  args );
          break;

        case 0x73:  /* DELTAC0 */
        case 0x74:  /* DELTAC1 */
        case 0x75:  /* DELTAC2 */
          Ins_DELTAC( EXEC_ARGS  args );
          break;

        case 0x76:  /* SROUND */
          DO_SROUND
          break;

        case 0x77:  /* S45Round */
          DO_S45ROUND
          break;

        case 0x78:  /* JROT */
          DO_JROT
          break;

        case 0x79:  /* JROF */
          DO_JROF
          break;

        case 0x7A:  /* ROFF */
          DO_ROFF
          break;

        case 0x7B:  /* ???? */
          Ins_UNKNOWN( EXEC_ARGS  args );
          break;

        case 0x7C:  /* RUTG */
          DO_RUTG
          break;

        case 0x7D:  /* RDTG */
          DO_RDTG
          break;

        case 0x7E:  /* SANGW */
        case 0x7F:  /* AA    */
          /* nothing - obsolete */
          break;

        case 0x80:  /* FLIPPT */
          Ins_FLIPPT( EXEC_ARGS  args );
          break;

        case 0x81:  /* FLIPRGON */
          Ins_FLIPRGON( EXEC_ARGS  args );
          break;

        case 0x82:  /* FLIPRGOFF */
          Ins_FLIPRGOFF( EXEC_ARGS  args );
          break;

        case 0x83:  /* UNKNOWN */
        case 0x84:  /* UNKNOWN */
          Ins_UNKNOWN( EXEC_ARGS  args );
          break;

        case 0x85:  /* SCANCTRL */
          Ins_SCANCTRL( EXEC_ARGS  args );
          break;

        case 0x86:  /* SDPVTL */
        case 0x87:  /* SDPVTL */
          Ins_SDPVTL( EXEC_ARGS  args );
          break;

        case 0x88:  /* GETINFO */
          Ins_GETINFO( EXEC_ARGS  args );
          break;

        case 0x89:  /* IDEF */
          Ins_IDEF( EXEC_ARGS  args );
          break;

        case 0x8A:  /* ROLL */
          Ins_ROLL( EXEC_ARGS  args );
          break;

        case 0x8B:  /* MAX */
          DO_MAX
          break;

        case 0x8C:  /* MIN */
          DO_MIN
          break;

        case 0x8D:  /* SCANTYPE */
          Ins_SCANTYPE( EXEC_ARGS  args );
          break;

        case 0x8E:  /* INSTCTRL */
          Ins_INSTCTRL( EXEC_ARGS  args );
          break;

        case 0x8F:
          Ins_UNKNOWN( EXEC_ARGS  args );
          break;

        default:
          if ( opcode >= 0xE0 )
            Ins_MIRP( EXEC_ARGS  args );
          else if ( opcode >= 0xC0 )
            Ins_MDRP( EXEC_ARGS  args );
          else if ( opcode >= 0xB8 )
            Ins_PUSHW( EXEC_ARGS  args );
          else if ( opcode >= 0xB0 )
            Ins_PUSHB( EXEC_ARGS  args );
          else
            Ins_UNKNOWN( EXEC_ARGS  args );
        }

      }
#else
      Instruct_Dispatch[CUR.opcode]( EXEC_ARGS &CUR.stack[CUR.args] );
#endif
      if ( CUR.error != TT_Err_Ok )
      {
        switch ( CUR.error )
        {
        case TT_Err_Invalid_Opcode: /* looking for redefined instructions */
          A = 0;

          while ( A < CUR.numIDefs )
          {
            WITH = &CUR.IDefs[A];

            if ( WITH->Active && CUR.opcode == WITH->Opc )
            {
              if ( CUR.callTop >= CUR.callSize )
              {
                CUR.error = TT_Err_Invalid_Reference;
                goto LErrorLabel_;
              }

              WITH1 = &CUR.callStack[CUR.callTop];

              WITH1->Caller_Range = CUR.curRange;
              WITH1->Caller_IP    = CUR.IP + 1;
              WITH1->Cur_Count    = 1;
              WITH1->Cur_Restart  = WITH->Start;

              if ( INS_Goto_CodeRange( WITH->Range, WITH->Start ) == FAILURE )
                goto LErrorLabel_;

              goto LSuiteLabel_;
            }
            else
            {
              A++;
              continue;
            }
          }

          CUR.error = TT_Err_Invalid_Opcode;
          goto LErrorLabel_;
/*        break;   Unreachable code warning suppress.  Leave in case a later
                   change to remind the editor to consider break; */

        default:
          goto LErrorLabel_;
/*        break; */
        }
      }

      CUR.top = CUR.new_top;

      if ( CUR.step_ins )
        CUR.IP += CUR.length;

      /* increment instruction counter and check if we didn't   */
      /* run this program for too long ?? (e.g. infinite loops) */
      if ( ++ins_counter > MAX_RUNNABLE_OPCODES )
        return TT_Err_Execution_Too_Long;


  LSuiteLabel_:

      if ( CUR.IP >= CUR.codeSize )
      {
        if ( CUR.callTop > 0 )
        {
          CUR.error = TT_Err_Code_Overflow;
          goto LErrorLabel_;
        }
        else
          goto LNo_Error_;
      }
    } while ( !CUR.instruction_trap );

  LNo_Error_:

    return TT_Err_Ok;

  LErrorLabel_:

    return CUR.error;
  }


#ifdef DEBUG_INTERPRETER

  LOCAL_DEF
  TT_Error  RunIns( PExecution_Context  exc )
  {
    Int    A, diff;
    ULong  next_IP;
    Char   ch, *temp;

    TT_Error  error;

    TGlyph_Zone  save;
    TGlyph_Zone  pts;

#define TT_Round_Off             5
#define TT_Round_To_Half_Grid    0
#define TT_Round_To_Grid         1
#define TT_Round_To_Double_Grid  2
#define TT_Round_Up_To_Grid      4
#define TT_Round_Down_To_Grid    3
#define TT_Round_Super           6
#define TT_Round_Super_45        7

    const String*  round_str[8] =
    {
      "to half-grid",
      "to grid",
      "to double grid",
      "down to grid",
      "up to grid",
      "off",
      "super",
      "super 45"
    };


#if 0
    if ( !Goto_CodeRange( exc, TT_CodeRange_Glyph, 0 ) )
      return FAILURE;

    exc->pts = ((PInstance)exc->owner)->pts;
    exc->numContours = ((PInstance)exc)->numContours;

    exc->zp0 = exc->pts;
    exc->zp1 = exc->pts;
    exc->zp2 = exc->pts;

    exc->GS  = exc->default_GS;

    exc->GS.gep0 = 1;
    exc->GS.gep1 = 1;
    exc->GS.gep2 = 1;

    exc->GS.projVector.x = 0x4000;
    exc->GS.projVector.y = 0x0000;
    exc->GS.freeVector.x = 0x4000;
    exc->GS.freeVector.y = 0x0000;
    exc->GS.dualVector.x = 0x4000;
    exc->GS.dualVector.y = 0x0000;

    exc->GS.round_state = 1;

    exc->top = 0;
    /* Some glyphs leave something on the stack! */
    /* We must empty it.                         */
#endif

    if ( exc->curRange != TT_CodeRange_Glyph )
      return RunIns2( exc );

    pts = exc->pts;

    save.n_points   = pts.n_points;
    save.n_contours = pts.n_contours;

    save.org   = (PCoordinates)malloc( 2 * sizeof( TT_F26Dot6 ) *
                                       save.n_points );
    save.cur   = (PCoordinates)malloc( 2 * sizeof( TT_F26Dot6 ) *
                                       save.n_points );
    save.touch = (PByte)malloc( save.n_points );

    exc->instruction_trap = 1;

    do
    {
      if ( CUR.IP < CUR.codeSize )
      {
        CALC_Length();

        CUR.args = CUR.top - (Pop_Push_Count[CUR.opcode] >> 4);

        /* `args' is the top of the stack once arguments have been popped. */
        /* One can also interpret it as the index of the last argument.    */

        /* First print the current stack */

        A = CUR.args - 4;

        if ( A < 0 )
          A = 0;

        while ( A < CUR.top )
        {
          PTRACE0(( "%04lx ", CUR.stack[A] ));
          A++;
          if ( A == CUR.args )
            PTRACE0(( "* " ));
        }
        PTRACE0(( "\n" ));

        /* Now print the current line */

#if 1
        PTRACE0(( "%s\n", (String*)Cur_U_Line( &CUR ) ));
#endif

        /* First, check for empty stack and overflow */

        if ( CUR.args < 0 )
        {
          PTRACE0(( "ERROR : Too Few Arguments\n" ));
          CUR.error = TT_Err_Too_Few_Arguments;
          goto LErrorLabel_;
        }

        CUR.new_top = CUR.args + (Pop_Push_Count[CUR.opcode] & 15);

      /* new_top  is the new top of the stack, after the instruction's */
      /* execution. top will be set to new_top after the 'case'        */

        if ( CUR.new_top > CUR.stackSize )
        {
          PTRACE0(( "ERROR : Stack overflow\n" ));
          CUR.error = TT_Err_Stack_Overflow;
          goto LErrorLabel_;
        }
      }
      else
        PTRACE0(( "End of program reached.\n" ));

      do
      {
#ifdef OS2
        ch = getch();
        if ( ch > ' ' )
          PTRACE0(( "%c\n", ch ));
#else
        ch = getchar();
#endif
        switch ( ch )
        {
        case '\n':
          ch = '\0';
          break;

        case '?':
          PTRACE0(( "Help\n\n" ));
          PTRACE0(( "?   Show this page\n" ));
          PTRACE0(( "q   Quit debugger\n" ));
          PTRACE0(( "n   Next instruction\n" ));
          PTRACE0(( "s   Step into\n" ));
          PTRACE0(( "v   Show vector info\n" ));
          PTRACE0(( "p   Show points zone\n\n" ));
          ch = '\0';
          break;

        case 'v':
          PTRACE0(( "freedom    (%04hx,%04hx)\n",
                       exc->GS.freeVector.x,
                       exc->GS.freeVector.y ));
          PTRACE0(( "projection (%04hx,%04hx)\n",
                       exc->GS.projVector.x,
                       exc->GS.projVector.y ));
          PTRACE0(( "dual       (%04hx,%04hx)\n\n",
                       exc->GS.dualVector.x,
                       exc->GS.dualVector.y ));
          ch = '\0';
          break;

        case 'g':
          PTRACE0(( "rounding   %s\n", round_str[exc->GS.round_state] ));
          PTRACE0(( "min dist   %04lx\n", exc->GS.minimum_distance ));
          PTRACE0(( "cvt_cutin  %04lx\n", exc->GS.control_value_cutin ));
          ch = '\0';
          break;

        case 'p':
          for ( A = 0; A < exc->pts.n_points; A++ )
          {
            PTRACE0(( "%02hx  ", A ));

            PTRACE0(( "%08lx,%08lx - ",
                         pts.org[A].x,
                         pts.org[A].y ));

            PTRACE0(( "%08lx,%08lx\n",
                         pts.cur[A].x,
                         pts.cur[A].y ));
          }
          PTRACE0(( "\n" ));
          ch = '\0';
          break;
        }
      } while ( ch == '\0' );

      MEM_Copy( save.org,   pts.org, 2 * pts.n_points *
                                     sizeof ( TT_F26Dot6 ) );
      MEM_Copy( save.cur,   pts.cur, 2 * pts.n_points *
                                     sizeof ( TT_F26Dot6 ) );
      MEM_Copy( save.touch, pts.touch, pts.n_points );

      switch ( ch )
      {
      case 'q':
        goto LErrorLabel_;

      case 's':
        if ( CUR.IP < CUR.codeSize )
          if ( ( error = RunIns2( exc ) ) )
            goto LErrorLabel_;
        break;

      case 'n':
        if ( CUR.IP < CUR.codeSize )
        {
          next_IP = CUR.IP + CUR.length;
          while ( CUR.IP != next_IP )
          {
            if ( ( error = RunIns2( exc ) ) )
              goto LErrorLabel_;
          }
        }
        break;

      default:
        PTRACE0(( "unknown command. Press ? for help\n" ));
      }

      for ( A = 0; A < pts.n_points; A++ )
      {
        diff = 0;
        if ( save.org[A].x != pts.org[A].x ) diff |= 1;
        if ( save.org[A].y != pts.org[A].y ) diff |= 2;
        if ( save.cur[A].x != pts.cur[A].x ) diff |= 4;
        if ( save.cur[A].y != pts.cur[A].y ) diff |= 8;
        if ( save.touch[A] != pts.touch[A] ) diff |= 16;

        if ( diff )
        {
          PTRACE0(( "%02hx  ", A ));

          if ( diff & 16 ) temp = "(%01hx)"; else temp = " %01hx ";
          PTRACE0(( temp, save.touch[A] & 7 ));

          if ( diff & 1 ) temp = "(%08lx)"; else temp = " %08lx ";
          PTRACE0(( temp, save.org[A].x ));

          if ( diff & 2 ) temp = "(%08lx)"; else temp = " %08lx ";
          PTRACE0(( temp, save.org[A].y ));

          if ( diff & 4 ) temp = "(%08lx)"; else temp = " %08lx ";
          PTRACE0(( temp, save.cur[A].x ));

          if ( diff & 8 ) temp = "(%08lx)"; else temp = " %08lx ";
          PTRACE0(( temp, save.cur[A].y ));

          PTRACE0(( "\n" ));

          PTRACE0(( "%02hx  ", A ));

          if ( diff & 16 ) temp = "[%01hx]"; else temp = " %01hx ";
          PTRACE0(( temp, pts.touch[A] & 7 ));

          if ( diff & 1 ) temp = "[%08lx]"; else temp = " %08lx ";
          PTRACE0(( temp, pts.org[A].x ));

          if ( diff & 2 ) temp = "[%08lx]"; else temp = " %08lx ";
          PTRACE0(( temp, pts.org[A].y ));

          if ( diff & 4 ) temp = "[%08lx]"; else temp = " %08lx ";
          PTRACE0(( temp, pts.cur[A].x ));

          if ( diff & 8 ) temp = "[%08lx]"; else temp = " %08lx ";
          PTRACE0(( temp, pts.cur[A].y ));

          PTRACE0(( "\n\n" ));
        }
      }
    } while ( TRUE );

  LErrorLabel_:

    /* Load_Instance_Context(aIns); */

    return error;
  }

#endif /* DEBUG_INTERPRETER */


/* END */
