Edit

kc3-lang/freetype/src/truetype/ttinterp.c

Branch :

  • Show log

    Commit

  • Author : Werner Lemberg
    Date : 2020-06-13 21:15:45
    Hash : 16586859
    Message : Remove redundant inclusion of `ft2build.h'. * */*: Remove `#include <ft2build.h>' where possible. * include/freetype/freetype.h: Remove cpp error about missing inclusion of `ft2build.h'.

  • src/truetype/ttinterp.c
  • /****************************************************************************
     *
     * ttinterp.c
     *
     *   TrueType bytecode interpreter (body).
     *
     * Copyright (C) 1996-2020 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.
     *
     */
    
    
    /* Greg Hitchcock from Microsoft has helped a lot in resolving unclear */
    /* issues; many thanks!                                                */
    
    
    #include <freetype/internal/ftdebug.h>
    #include <freetype/internal/ftcalc.h>
    #include <freetype/fttrigon.h>
    #include <freetype/ftsystem.h>
    #include <freetype/ftdriver.h>
    #include <freetype/ftmm.h>
    
    #include "ttinterp.h"
    #include "tterrors.h"
    #include "ttsubpix.h"
    #ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
    #include "ttgxvar.h"
    #endif
    
    
    #ifdef TT_USE_BYTECODE_INTERPRETER
    
    
      /**************************************************************************
       *
       * The macro FT_COMPONENT is used in trace mode.  It is an implicit
       * parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log
       * messages during execution.
       */
    #undef  FT_COMPONENT
    #define FT_COMPONENT  ttinterp
    
    
    #define NO_SUBPIXEL_HINTING                                                  \
              ( ((TT_Driver)FT_FACE_DRIVER( exc->face ))->interpreter_version == \
                TT_INTERPRETER_VERSION_35 )
    
    #ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY
    #define SUBPIXEL_HINTING_INFINALITY                                          \
              ( ((TT_Driver)FT_FACE_DRIVER( exc->face ))->interpreter_version == \
                TT_INTERPRETER_VERSION_38 )
    #endif
    
    #ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL
    #define SUBPIXEL_HINTING_MINIMAL                                             \
              ( ((TT_Driver)FT_FACE_DRIVER( exc->face ))->interpreter_version == \
                TT_INTERPRETER_VERSION_40 )
    #endif
    
    #define PROJECT( v1, v2 )                                   \
              exc->func_project( exc,                           \
                                 SUB_LONG( (v1)->x, (v2)->x ),  \
                                 SUB_LONG( (v1)->y, (v2)->y ) )
    
    #define DUALPROJ( v1, v2 )                                   \
              exc->func_dualproj( exc,                           \
                                  SUB_LONG( (v1)->x, (v2)->x ),  \
                                  SUB_LONG( (v1)->y, (v2)->y ) )
    
    #define FAST_PROJECT( v )                          \
              exc->func_project( exc, (v)->x, (v)->y )
    
    #define FAST_DUALPROJ( v )                          \
              exc->func_dualproj( exc, (v)->x, (v)->y )
    
    
      /**************************************************************************
       *
       * Two simple bounds-checking macros.
       */
    #define BOUNDS( x, n )   ( (FT_UInt)(x)  >= (FT_UInt)(n)  )
    #define BOUNDSL( x, n )  ( (FT_ULong)(x) >= (FT_ULong)(n) )
    
    
    #undef  SUCCESS
    #define SUCCESS  0
    
    #undef  FAILURE
    #define FAILURE  1
    
    
      /**************************************************************************
       *
       *                       CODERANGE FUNCTIONS
       *
       */
    
    
      /**************************************************************************
       *
       * @Function:
       *   TT_Goto_CodeRange
       *
       * @Description:
       *   Switches to a new code range (updates the code related elements in
       *   `exec', and `IP').
       *
       * @Input:
       *   range ::
       *     The new execution code range.
       *
       *   IP ::
       *     The new IP in the new code range.
       *
       * @InOut:
       *   exec ::
       *     The target execution context.
       */
      FT_LOCAL_DEF( void )
      TT_Goto_CodeRange( TT_ExecContext  exec,
                         FT_Int          range,
                         FT_Long         IP )
      {
        TT_CodeRange*  coderange;
    
    
        FT_ASSERT( range >= 1 && range <= 3 );
    
        coderange = &exec->codeRangeTable[range - 1];
    
        FT_ASSERT( coderange->base );
    
        /* 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 IP <= Size instead of IP < Size.     */
        /*                                                               */
        FT_ASSERT( IP <= coderange->size );
    
        exec->code     = coderange->base;
        exec->codeSize = coderange->size;
        exec->IP       = IP;
        exec->curRange = range;
      }
    
    
      /**************************************************************************
       *
       * @Function:
       *   TT_Set_CodeRange
       *
       * @Description:
       *   Sets a code range.
       *
       * @Input:
       *   range ::
       *     The code range index.
       *
       *   base ::
       *     The new code base.
       *
       *   length ::
       *     The range size in bytes.
       *
       * @InOut:
       *   exec ::
       *     The target execution context.
       */
      FT_LOCAL_DEF( void )
      TT_Set_CodeRange( TT_ExecContext  exec,
                        FT_Int          range,
                        void*           base,
                        FT_Long         length )
      {
        FT_ASSERT( range >= 1 && range <= 3 );
    
        exec->codeRangeTable[range - 1].base = (FT_Byte*)base;
        exec->codeRangeTable[range - 1].size = length;
      }
    
    
      /**************************************************************************
       *
       * @Function:
       *   TT_Clear_CodeRange
       *
       * @Description:
       *   Clears a code range.
       *
       * @Input:
       *   range ::
       *     The code range index.
       *
       * @InOut:
       *   exec ::
       *     The target execution context.
       */
      FT_LOCAL_DEF( void )
      TT_Clear_CodeRange( TT_ExecContext  exec,
                          FT_Int          range )
      {
        FT_ASSERT( range >= 1 && range <= 3 );
    
        exec->codeRangeTable[range - 1].base = NULL;
        exec->codeRangeTable[range - 1].size = 0;
      }
    
    
      /**************************************************************************
       *
       *                  EXECUTION CONTEXT ROUTINES
       *
       */
    
    
      /**************************************************************************
       *
       * @Function:
       *   TT_Done_Context
       *
       * @Description:
       *   Destroys a given context.
       *
       * @Input:
       *   exec ::
       *     A handle to the target execution context.
       *
       *   memory ::
       *     A handle to the parent memory object.
       *
       * @Note:
       *   Only the glyph loader and debugger should call this function.
       */
      FT_LOCAL_DEF( void )
      TT_Done_Context( TT_ExecContext  exec )
      {
        FT_Memory  memory = exec->memory;
    
    
        /* points zone */
        exec->maxPoints   = 0;
        exec->maxContours = 0;
    
        /* free stack */
        FT_FREE( exec->stack );
        exec->stackSize = 0;
    
        /* free call stack */
        FT_FREE( exec->callStack );
        exec->callSize = 0;
        exec->callTop  = 0;
    
        /* free glyph code range */
        FT_FREE( exec->glyphIns );
        exec->glyphSize = 0;
    
        exec->size = NULL;
        exec->face = NULL;
    
        FT_FREE( exec );
      }
    
    
      /**************************************************************************
       *
       * @Function:
       *   Init_Context
       *
       * @Description:
       *   Initializes a context object.
       *
       * @Input:
       *   memory ::
       *     A handle to the parent memory object.
       *
       * @InOut:
       *   exec ::
       *     A handle to the target execution context.
       *
       * @Return:
       *   FreeType error code.  0 means success.
       */
      static FT_Error
      Init_Context( TT_ExecContext  exec,
                    FT_Memory       memory )
      {
        FT_Error  error;
    
    
        FT_TRACE1(( "Init_Context: new object at 0x%08p\n", exec ));
    
        exec->memory   = memory;
        exec->callSize = 32;
    
        if ( FT_NEW_ARRAY( exec->callStack, exec->callSize ) )
          goto Fail_Memory;
    
        /* all values in the context are set to 0 already, but this is */
        /* here as a remainder                                         */
        exec->maxPoints   = 0;
        exec->maxContours = 0;
    
        exec->stackSize = 0;
        exec->glyphSize = 0;
    
        exec->stack    = NULL;
        exec->glyphIns = NULL;
    
        exec->face = NULL;
        exec->size = NULL;
    
        return FT_Err_Ok;
    
      Fail_Memory:
        FT_ERROR(( "Init_Context: not enough memory for %p\n", exec ));
        TT_Done_Context( exec );
    
        return error;
     }
    
    
      /**************************************************************************
       *
       * @Function:
       *   Update_Max
       *
       * @Description:
       *   Checks the size of a buffer and reallocates it if necessary.
       *
       * @Input:
       *   memory ::
       *     A handle to the parent memory object.
       *
       *   multiplier ::
       *     The size in bytes of each element in the buffer.
       *
       *   new_max ::
       *     The new capacity (size) of the buffer.
       *
       * @InOut:
       *   size ::
       *     The address of the buffer's current size expressed
       *     in elements.
       *
       *   buff ::
       *     The address of the buffer base pointer.
       *
       * @Return:
       *   FreeType error code.  0 means success.
       */
      FT_LOCAL_DEF( FT_Error )
      Update_Max( FT_Memory  memory,
                  FT_ULong*  size,
                  FT_ULong   multiplier,
                  void*      _pbuff,
                  FT_ULong   new_max )
      {
        FT_Error  error;
        void**    pbuff = (void**)_pbuff;
    
    
        if ( *size < new_max )
        {
          if ( FT_REALLOC( *pbuff, *size * multiplier, new_max * multiplier ) )
            return error;
          *size = new_max;
        }
    
        return FT_Err_Ok;
      }
    
    
      /**************************************************************************
       *
       * @Function:
       *   TT_Load_Context
       *
       * @Description:
       *   Prepare an execution context for glyph hinting.
       *
       * @Input:
       *   face ::
       *     A handle to the source face object.
       *
       *   size ::
       *     A handle to the source size object.
       *
       * @InOut:
       *   exec ::
       *     A handle to the target execution context.
       *
       * @Return:
       *   FreeType error code.  0 means success.
       *
       * @Note:
       *   Only the glyph loader and debugger should call this function.
       */
      FT_LOCAL_DEF( FT_Error )
      TT_Load_Context( TT_ExecContext  exec,
                       TT_Face         face,
                       TT_Size         size )
      {
        FT_Int          i;
        FT_ULong        tmp;
        TT_MaxProfile*  maxp;
        FT_Error        error;
    
    
        exec->face = face;
        maxp       = &face->max_profile;
        exec->size = size;
    
        if ( size )
        {
          exec->numFDefs   = size->num_function_defs;
          exec->maxFDefs   = size->max_function_defs;
          exec->numIDefs   = size->num_instruction_defs;
          exec->maxIDefs   = size->max_instruction_defs;
          exec->FDefs      = size->function_defs;
          exec->IDefs      = size->instruction_defs;
          exec->pointSize  = size->point_size;
          exec->tt_metrics = size->ttmetrics;
          exec->metrics    = *size->metrics;
    
          exec->maxFunc    = size->max_func;
          exec->maxIns     = size->max_ins;
    
          for ( i = 0; i < TT_MAX_CODE_RANGES; i++ )
            exec->codeRangeTable[i] = size->codeRangeTable[i];
    
          /* set graphics state */
          exec->GS = size->GS;
    
          exec->cvtSize = size->cvt_size;
          exec->cvt     = size->cvt;
    
          exec->storeSize = size->storage_size;
          exec->storage   = size->storage;
    
          exec->twilight  = size->twilight;
    
          /* In case of multi-threading it can happen that the old size object */
          /* no longer exists, thus we must clear all glyph zone references.   */
          FT_ZERO( &exec->zp0 );
          exec->zp1 = exec->zp0;
          exec->zp2 = exec->zp0;
        }
    
        /* XXX: We reserve a little more elements on the stack to deal safely */
        /*      with broken fonts like arialbs, courbs, timesbs, etc.         */
        tmp = (FT_ULong)exec->stackSize;
        error = Update_Max( exec->memory,
                            &tmp,
                            sizeof ( FT_F26Dot6 ),
                            (void*)&exec->stack,
                            maxp->maxStackElements + 32 );
        exec->stackSize = (FT_Long)tmp;
        if ( error )
          return error;
    
        tmp = exec->glyphSize;
        error = Update_Max( exec->memory,
                            &tmp,
                            sizeof ( FT_Byte ),
                            (void*)&exec->glyphIns,
                            maxp->maxSizeOfInstructions );
        exec->glyphSize = (FT_UShort)tmp;
        if ( error )
          return error;
    
        exec->pts.n_points   = 0;
        exec->pts.n_contours = 0;
    
        exec->zp1 = exec->pts;
        exec->zp2 = exec->pts;
        exec->zp0 = exec->pts;
    
        exec->instruction_trap = FALSE;
    
        return FT_Err_Ok;
      }
    
    
      /**************************************************************************
       *
       * @Function:
       *   TT_Save_Context
       *
       * @Description:
       *   Saves the code ranges in a `size' object.
       *
       * @Input:
       *   exec ::
       *     A handle to the source execution context.
       *
       * @InOut:
       *   size ::
       *     A handle to the target size object.
       *
       * @Note:
       *   Only the glyph loader and debugger should call this function.
       */
      FT_LOCAL_DEF( void )
      TT_Save_Context( TT_ExecContext  exec,
                       TT_Size         size )
      {
        FT_Int  i;
    
    
        /* XXX: Will probably disappear soon with all the code range */
        /*      management, which is now rather obsolete.            */
        /*                                                           */
        size->num_function_defs    = exec->numFDefs;
        size->num_instruction_defs = exec->numIDefs;
    
        size->max_func = exec->maxFunc;
        size->max_ins  = exec->maxIns;
    
        for ( i = 0; i < TT_MAX_CODE_RANGES; i++ )
          size->codeRangeTable[i] = exec->codeRangeTable[i];
      }
    
    
      /**************************************************************************
       *
       * @Function:
       *   TT_Run_Context
       *
       * @Description:
       *   Executes one or more instructions in the execution context.
       *
       * @Input:
       *   exec ::
       *     A handle to the target execution context.
       *
       * @Return:
       *   TrueType error code.  0 means success.
       */
      FT_LOCAL_DEF( FT_Error )
      TT_Run_Context( TT_ExecContext  exec )
      {
        TT_Goto_CodeRange( exec, tt_coderange_glyph, 0 );
    
        exec->zp0 = exec->pts;
        exec->zp1 = exec->pts;
        exec->zp2 = exec->pts;
    
        exec->GS.gep0 = 1;
        exec->GS.gep1 = 1;
        exec->GS.gep2 = 1;
    
        exec->GS.projVector.x = 0x4000;
        exec->GS.projVector.y = 0x0000;
    
        exec->GS.freeVector = exec->GS.projVector;
        exec->GS.dualVector = exec->GS.projVector;
    
        exec->GS.round_state = 1;
        exec->GS.loop        = 1;
    
        /* some glyphs leave something on the stack. so we clean it */
        /* before a new execution.                                  */
        exec->top     = 0;
        exec->callTop = 0;
    
        return exec->face->interpreter( exec );
      }
    
    
      /* The default value for `scan_control' is documented as FALSE in the */
      /* TrueType specification.  This is confusing since it implies a      */
      /* Boolean value.  However, this is not the case, thus both the       */
      /* default values of our `scan_type' and `scan_control' fields (which */
      /* the documentation's `scan_control' variable is split into) are     */
      /* zero.                                                              */
    
      const TT_GraphicsState  tt_default_graphics_state =
      {
        0, 0, 0,
        { 0x4000, 0 },
        { 0x4000, 0 },
        { 0x4000, 0 },
    
        1, 64, 1,
        TRUE, 68, 0, 0, 9, 3,
        0, FALSE, 0, 1, 1, 1
      };
    
    
      /* documentation is in ttinterp.h */
    
      FT_EXPORT_DEF( TT_ExecContext )
      TT_New_Context( TT_Driver  driver )
      {
        FT_Memory  memory;
        FT_Error   error;
    
        TT_ExecContext  exec = NULL;
    
    
        if ( !driver )
          goto Fail;
    
        memory = driver->root.root.memory;
    
        /* allocate object */
        if ( FT_NEW( exec ) )
          goto Fail;
    
        /* initialize it; in case of error this deallocates `exec' too */
        error = Init_Context( exec, memory );
        if ( error )
          goto Fail;
    
        return exec;
    
      Fail:
        return NULL;
      }
    
    
      /**************************************************************************
       *
       * 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.
       *
       * Opcodes which have a varying number of parameters in the data stream
       * (NPUSHB, NPUSHW) are handled specially; they have a negative value in
       * the `opcode_length' table, and the value in `Pop_Push_Count' is set
       * to zero.
       *
       */
    
    
    #undef  PACK
    #define PACK( x, y )  ( ( x << 4 ) | y )
    
    
      static
      const FT_Byte  Pop_Push_Count[256] =
      {
        /* opcodes are gathered in groups of 16 */
        /* please keep the spaces as they are   */
    
        /* 0x00 */
        /*  SVTCA[0]  */  PACK( 0, 0 ),
        /*  SVTCA[1]  */  PACK( 0, 0 ),
        /*  SPVTCA[0] */  PACK( 0, 0 ),
        /*  SPVTCA[1] */  PACK( 0, 0 ),
        /*  SFVTCA[0] */  PACK( 0, 0 ),
        /*  SFVTCA[1] */  PACK( 0, 0 ),
        /*  SPVTL[0]  */  PACK( 2, 0 ),
        /*  SPVTL[1]  */  PACK( 2, 0 ),
        /*  SFVTL[0]  */  PACK( 2, 0 ),
        /*  SFVTL[1]  */  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 ),
    
        /* 0x10 */
        /*  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 ),
    
        /* 0x20 */
        /*  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 ),
    
        /* 0x30 */
        /*  IUP[0]    */  PACK( 0, 0 ),
        /*  IUP[1]    */  PACK( 0, 0 ),
        /*  SHP[0]    */  PACK( 0, 0 ), /* loops */
        /*  SHP[1]    */  PACK( 0, 0 ), /* loops */
        /*  SHC[0]    */  PACK( 1, 0 ),
        /*  SHC[1]    */  PACK( 1, 0 ),
        /*  SHZ[0]    */  PACK( 1, 0 ),
        /*  SHZ[1]    */  PACK( 1, 0 ),
        /*  SHPIX     */  PACK( 1, 0 ), /* loops */
        /*  IP        */  PACK( 0, 0 ), /* loops */
        /*  MSIRP[0]  */  PACK( 2, 0 ),
        /*  MSIRP[1]  */  PACK( 2, 0 ),
        /*  ALIGNRP   */  PACK( 0, 0 ), /* loops */
        /*  RTDG      */  PACK( 0, 0 ),
        /*  MIAP[0]   */  PACK( 2, 0 ),
        /*  MIAP[1]   */  PACK( 2, 0 ),
    
        /* 0x40 */
        /*  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 ),
    
        /* 0x50 */
        /*  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 ),
    
        /* 0x60 */
        /*  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 ),
    
        /* 0x70 */
        /*  WCVTF     */  PACK( 2, 0 ),
        /*  DELTAP2   */  PACK( 1, 0 ),
        /*  DELTAP3   */  PACK( 1, 0 ),
        /*  DELTAC1   */  PACK( 1, 0 ),
        /*  DELTAC2   */  PACK( 1, 0 ),
        /*  DELTAC3   */  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 ),
    
        /* 0x80 */
        /*  FLIPPT    */  PACK( 0, 0 ), /* loops */
        /*  FLIPRGON  */  PACK( 2, 0 ),
        /*  FLIPRGOFF */  PACK( 2, 0 ),
        /*  INS_$83   */  PACK( 0, 0 ),
        /*  INS_$84   */  PACK( 0, 0 ),
        /*  SCANCTRL  */  PACK( 1, 0 ),
        /*  SDPVTL[0] */  PACK( 2, 0 ),
        /*  SDPVTL[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 ),
    
        /* 0x90 */
        /*  INS_$90  */   PACK( 0, 0 ),
        /*  GETVAR   */   PACK( 0, 0 ), /* will be handled specially */
        /*  GETDATA  */   PACK( 0, 1 ),
        /*  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 ),
    
        /* 0xA0 */
        /*  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 ),
    
        /* 0xB0 */
        /*  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 ),
    
        /* 0xC0 */
        /*  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 ),
    
        /* 0xD0 */
        /*  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 ),
    
        /* 0xE0 */
        /*  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 ),
    
        /* 0xF0 */
        /*  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 )
      };
    
    
    #ifdef FT_DEBUG_LEVEL_TRACE
    
      /* the first hex digit gives the length of the opcode name; the space */
      /* after the digit is here just to increase readability of the source */
      /* code                                                               */
    
      static
      const char*  const opcode_name[256] =
      {
        /* 0x00 */
        "8 SVTCA[y]",
        "8 SVTCA[x]",
        "9 SPVTCA[y]",
        "9 SPVTCA[x]",
        "9 SFVTCA[y]",
        "9 SFVTCA[x]",
        "9 SPVTL[||]",
        "8 SPVTL[+]",
        "9 SFVTL[||]",
        "8 SFVTL[+]",
        "5 SPVFS",
        "5 SFVFS",
        "3 GPV",
        "3 GFV",
        "6 SFVTPV",
        "5 ISECT",
    
        /* 0x10 */
        "4 SRP0",
        "4 SRP1",
        "4 SRP2",
        "4 SZP0",
        "4 SZP1",
        "4 SZP2",
        "4 SZPS",
        "5 SLOOP",
        "3 RTG",
        "4 RTHG",
        "3 SMD",
        "4 ELSE",
        "4 JMPR",
        "6 SCVTCI",
        "5 SSWCI",
        "3 SSW",
    
        /* 0x20 */
        "3 DUP",
        "3 POP",
        "5 CLEAR",
        "4 SWAP",
        "5 DEPTH",
        "6 CINDEX",
        "6 MINDEX",
        "8 ALIGNPTS",
        "7 INS_$28",
        "3 UTP",
        "8 LOOPCALL",
        "4 CALL",
        "4 FDEF",
        "4 ENDF",
        "6 MDAP[]",
        "9 MDAP[rnd]",
    
        /* 0x30 */
        "6 IUP[y]",
        "6 IUP[x]",
        "8 SHP[rp2]",
        "8 SHP[rp1]",
        "8 SHC[rp2]",
        "8 SHC[rp1]",
        "8 SHZ[rp2]",
        "8 SHZ[rp1]",
        "5 SHPIX",
        "2 IP",
        "7 MSIRP[]",
        "A MSIRP[rp0]",
        "7 ALIGNRP",
        "4 RTDG",
        "6 MIAP[]",
        "9 MIAP[rnd]",
    
        /* 0x40 */
        "6 NPUSHB",
        "6 NPUSHW",
        "2 WS",
        "2 RS",
        "5 WCVTP",
        "4 RCVT",
        "8 GC[curr]",
        "8 GC[orig]",
        "4 SCFS",
        "8 MD[curr]",
        "8 MD[orig]",
        "5 MPPEM",
        "3 MPS",
        "6 FLIPON",
        "7 FLIPOFF",
        "5 DEBUG",
    
        /* 0x50 */
        "2 LT",
        "4 LTEQ",
        "2 GT",
        "4 GTEQ",
        "2 EQ",
        "3 NEQ",
        "3 ODD",
        "4 EVEN",
        "2 IF",
        "3 EIF",
        "3 AND",
        "2 OR",
        "3 NOT",
        "7 DELTAP1",
        "3 SDB",
        "3 SDS",
    
        /* 0x60 */
        "3 ADD",
        "3 SUB",
        "3 DIV",
        "3 MUL",
        "3 ABS",
        "3 NEG",
        "5 FLOOR",
        "7 CEILING",
        "8 ROUND[G]",
        "8 ROUND[B]",
        "8 ROUND[W]",
        "7 ROUND[]",
        "9 NROUND[G]",
        "9 NROUND[B]",
        "9 NROUND[W]",
        "8 NROUND[]",
    
        /* 0x70 */
        "5 WCVTF",
        "7 DELTAP2",
        "7 DELTAP3",
        "7 DELTAC1",
        "7 DELTAC2",
        "7 DELTAC3",
        "6 SROUND",
        "8 S45ROUND",
        "4 JROT",
        "4 JROF",
        "4 ROFF",
        "7 INS_$7B",
        "4 RUTG",
        "4 RDTG",
        "5 SANGW",
        "2 AA",
    
        /* 0x80 */
        "6 FLIPPT",
        "8 FLIPRGON",
        "9 FLIPRGOFF",
        "7 INS_$83",
        "7 INS_$84",
        "8 SCANCTRL",
        "A SDPVTL[||]",
        "9 SDPVTL[+]",
        "7 GETINFO",
        "4 IDEF",
        "4 ROLL",
        "3 MAX",
        "3 MIN",
        "8 SCANTYPE",
        "8 INSTCTRL",
        "7 INS_$8F",
    
        /* 0x90 */
        "7 INS_$90",
    #ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
        "C GETVARIATION",
        "7 GETDATA",
    #else
        "7 INS_$91",
        "7 INS_$92",
    #endif
        "7 INS_$93",
        "7 INS_$94",
        "7 INS_$95",
        "7 INS_$96",
        "7 INS_$97",
        "7 INS_$98",
        "7 INS_$99",
        "7 INS_$9A",
        "7 INS_$9B",
        "7 INS_$9C",
        "7 INS_$9D",
        "7 INS_$9E",
        "7 INS_$9F",
    
        /* 0xA0 */
        "7 INS_$A0",
        "7 INS_$A1",
        "7 INS_$A2",
        "7 INS_$A3",
        "7 INS_$A4",
        "7 INS_$A5",
        "7 INS_$A6",
        "7 INS_$A7",
        "7 INS_$A8",
        "7 INS_$A9",
        "7 INS_$AA",
        "7 INS_$AB",
        "7 INS_$AC",
        "7 INS_$AD",
        "7 INS_$AE",
        "7 INS_$AF",
    
        /* 0xB0 */
        "8 PUSHB[0]",
        "8 PUSHB[1]",
        "8 PUSHB[2]",
        "8 PUSHB[3]",
        "8 PUSHB[4]",
        "8 PUSHB[5]",
        "8 PUSHB[6]",
        "8 PUSHB[7]",
        "8 PUSHW[0]",
        "8 PUSHW[1]",
        "8 PUSHW[2]",
        "8 PUSHW[3]",
        "8 PUSHW[4]",
        "8 PUSHW[5]",
        "8 PUSHW[6]",
        "8 PUSHW[7]",
    
        /* 0xC0 */
        "7 MDRP[G]",
        "7 MDRP[B]",
        "7 MDRP[W]",
        "6 MDRP[]",
        "8 MDRP[rG]",
        "8 MDRP[rB]",
        "8 MDRP[rW]",
        "7 MDRP[r]",
        "8 MDRP[mG]",
        "8 MDRP[mB]",
        "8 MDRP[mW]",
        "7 MDRP[m]",
        "9 MDRP[mrG]",
        "9 MDRP[mrB]",
        "9 MDRP[mrW]",
        "8 MDRP[mr]",
    
        /* 0xD0 */
        "8 MDRP[pG]",
        "8 MDRP[pB]",
        "8 MDRP[pW]",
        "7 MDRP[p]",
        "9 MDRP[prG]",
        "9 MDRP[prB]",
        "9 MDRP[prW]",
        "8 MDRP[pr]",
        "9 MDRP[pmG]",
        "9 MDRP[pmB]",
        "9 MDRP[pmW]",
        "8 MDRP[pm]",
        "A MDRP[pmrG]",
        "A MDRP[pmrB]",
        "A MDRP[pmrW]",
        "9 MDRP[pmr]",
    
        /* 0xE0 */
        "7 MIRP[G]",
        "7 MIRP[B]",
        "7 MIRP[W]",
        "6 MIRP[]",
        "8 MIRP[rG]",
        "8 MIRP[rB]",
        "8 MIRP[rW]",
        "7 MIRP[r]",
        "8 MIRP[mG]",
        "8 MIRP[mB]",
        "8 MIRP[mW]",
        "7 MIRP[m]",
        "9 MIRP[mrG]",
        "9 MIRP[mrB]",
        "9 MIRP[mrW]",
        "8 MIRP[mr]",
    
        /* 0xF0 */
        "8 MIRP[pG]",
        "8 MIRP[pB]",
        "8 MIRP[pW]",
        "7 MIRP[p]",
        "9 MIRP[prG]",
        "9 MIRP[prB]",
        "9 MIRP[prW]",
        "8 MIRP[pr]",
        "9 MIRP[pmG]",
        "9 MIRP[pmB]",
        "9 MIRP[pmW]",
        "8 MIRP[pm]",
        "A MIRP[pmrG]",
        "A MIRP[pmrB]",
        "A MIRP[pmrW]",
        "9 MIRP[pmr]"
      };
    
    #endif /* FT_DEBUG_LEVEL_TRACE */
    
    
      static
      const FT_Char  opcode_length[256] =
      {
        1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,
        1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,
        1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,
        1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,
    
       -1,-2, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,
        1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,
        1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,
        1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,
    
        1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,
        1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,
        1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,
        2, 3, 4, 5,  6, 7, 8, 9,  3, 5, 7, 9, 11,13,15,17,
    
        1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,
        1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,
        1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,
        1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1
      };
    
    #undef PACK
    
    
    #ifndef FT_CONFIG_OPTION_NO_ASSEMBLER
    
    #if defined( __arm__ )                                 && \
        ( defined( __thumb2__ ) || !defined( __thumb__ ) )
    
    #define TT_MulFix14  TT_MulFix14_arm
    
      static FT_Int32
      TT_MulFix14_arm( FT_Int32  a,
                       FT_Int    b )
      {
        FT_Int32  t, t2;
    
    
    #if defined( __CC_ARM ) || defined( __ARMCC__ )
    
        __asm
        {
          smull t2, t,  b,  a           /* (lo=t2,hi=t) = a*b */
          mov   a,  t,  asr #31         /* a   = (hi >> 31) */
          add   a,  a,  #0x2000         /* a  += 0x2000 */
          adds  t2, t2, a               /* t2 += a */
          adc   t,  t,  #0              /* t  += carry */
          mov   a,  t2, lsr #14         /* a   = t2 >> 14 */
          orr   a,  a,  t,  lsl #18     /* a  |= t << 18 */
        }
    
    #elif defined( __GNUC__ )
    
        __asm__ __volatile__ (
          "smull  %1, %2, %4, %3\n\t"       /* (lo=%1,hi=%2) = a*b */
          "mov    %0, %2, asr #31\n\t"      /* %0  = (hi >> 31) */
    #if defined( __clang__ ) && defined( __thumb2__ )
          "add.w  %0, %0, #0x2000\n\t"      /* %0 += 0x2000 */
    #else
          "add    %0, %0, #0x2000\n\t"      /* %0 += 0x2000 */
    #endif
          "adds   %1, %1, %0\n\t"           /* %1 += %0 */
          "adc    %2, %2, #0\n\t"           /* %2 += carry */
          "mov    %0, %1, lsr #14\n\t"      /* %0  = %1 >> 16 */
          "orr    %0, %0, %2, lsl #18\n\t"  /* %0 |= %2 << 16 */
          : "=r"(a), "=&r"(t2), "=&r"(t)
          : "r"(a), "r"(b)
          : "cc" );
    
    #endif
    
        return a;
      }
    
    #endif /* __arm__ && ( __thumb2__ || !__thumb__ ) */
    
    #endif /* !FT_CONFIG_OPTION_NO_ASSEMBLER */
    
    
    #if defined( __GNUC__ )                              && \
        ( defined( __i386__ ) || defined( __x86_64__ ) )
    
    #define TT_MulFix14  TT_MulFix14_long_long
    
      /* Temporarily disable the warning that C90 doesn't support `long long'. */
    #if ( __GNUC__ * 100 + __GNUC_MINOR__ ) >= 406
    #pragma GCC diagnostic push
    #endif
    #pragma GCC diagnostic ignored "-Wlong-long"
    
      /* This is declared `noinline' because inlining the function results */
      /* in slower code.  The `pure' attribute indicates that the result   */
      /* only depends on the parameters.                                   */
      static __attribute__(( noinline ))
             __attribute__(( pure )) FT_Int32
      TT_MulFix14_long_long( FT_Int32  a,
                             FT_Int    b )
      {
    
        long long  ret = (long long)a * b;
    
        /* The following line assumes that right shifting of signed values */
        /* will actually preserve the sign bit.  The exact behaviour is    */
        /* undefined, but this is true on x86 and x86_64.                  */
        long long  tmp = ret >> 63;
    
    
        ret += 0x2000 + tmp;
    
        return (FT_Int32)( ret >> 14 );
      }
    
    #if ( __GNUC__ * 100 + __GNUC_MINOR__ ) >= 406
    #pragma GCC diagnostic pop
    #endif
    
    #endif /* __GNUC__ && ( __i386__ || __x86_64__ ) */
    
    
    #ifndef TT_MulFix14
    
      /* Compute (a*b)/2^14 with maximum accuracy and rounding.  */
      /* This is optimized to be faster than calling FT_MulFix() */
      /* for platforms where sizeof(int) == 2.                   */
      static FT_Int32
      TT_MulFix14( FT_Int32  a,
                   FT_Int    b )
      {
        FT_Int32   sign;
        FT_UInt32  ah, al, mid, lo, hi;
    
    
        sign = a ^ b;
    
        if ( a < 0 )
          a = -a;
        if ( b < 0 )
          b = -b;
    
        ah = (FT_UInt32)( ( a >> 16 ) & 0xFFFFU );
        al = (FT_UInt32)( a & 0xFFFFU );
    
        lo    = al * b;
        mid   = ah * b;
        hi    = mid >> 16;
        mid   = ( mid << 16 ) + ( 1 << 13 ); /* rounding */
        lo   += mid;
        if ( lo < mid )
          hi += 1;
    
        mid = ( lo >> 14 ) | ( hi << 18 );
    
        return sign >= 0 ? (FT_Int32)mid : -(FT_Int32)mid;
      }
    
    #endif  /* !TT_MulFix14 */
    
    
    #if defined( __GNUC__ )        && \
        ( defined( __i386__ )   ||    \
          defined( __x86_64__ ) ||    \
          defined( __arm__ )    )
    
    #define TT_DotFix14  TT_DotFix14_long_long
    
    #if ( __GNUC__ * 100 + __GNUC_MINOR__ ) >= 406
    #pragma GCC diagnostic push
    #endif
    #pragma GCC diagnostic ignored "-Wlong-long"
    
      static __attribute__(( pure )) FT_Int32
      TT_DotFix14_long_long( FT_Int32  ax,
                             FT_Int32  ay,
                             FT_Int    bx,
                             FT_Int    by )
      {
        /* Temporarily disable the warning that C90 doesn't support */
        /* `long long'.                                             */
    
        long long  temp1 = (long long)ax * bx;
        long long  temp2 = (long long)ay * by;
    
    
        temp1 += temp2;
        temp2  = temp1 >> 63;
        temp1 += 0x2000 + temp2;
    
        return (FT_Int32)( temp1 >> 14 );
    
      }
    
    #if ( __GNUC__ * 100 + __GNUC_MINOR__ ) >= 406
    #pragma GCC diagnostic pop
    #endif
    
    #endif /* __GNUC__ && (__arm__ || __i386__ || __x86_64__) */
    
    
    #ifndef TT_DotFix14
    
      /* compute (ax*bx+ay*by)/2^14 with maximum accuracy and rounding */
      static FT_Int32
      TT_DotFix14( FT_Int32  ax,
                   FT_Int32  ay,
                   FT_Int    bx,
                   FT_Int    by )
      {
        FT_Int32   m, s, hi1, hi2, hi;
        FT_UInt32  l, lo1, lo2, lo;
    
    
        /* compute ax*bx as 64-bit value */
        l = (FT_UInt32)( ( ax & 0xFFFFU ) * bx );
        m = ( ax >> 16 ) * bx;
    
        lo1 = l + ( (FT_UInt32)m << 16 );
        hi1 = ( m >> 16 ) + ( (FT_Int32)l >> 31 ) + ( lo1 < l );
    
        /* compute ay*by as 64-bit value */
        l = (FT_UInt32)( ( ay & 0xFFFFU ) * by );
        m = ( ay >> 16 ) * by;
    
        lo2 = l + ( (FT_UInt32)m << 16 );
        hi2 = ( m >> 16 ) + ( (FT_Int32)l >> 31 ) + ( lo2 < l );
    
        /* add them */
        lo = lo1 + lo2;
        hi = hi1 + hi2 + ( lo < lo1 );
    
        /* divide the result by 2^14 with rounding */
        s   = hi >> 31;
        l   = lo + (FT_UInt32)s;
        hi += s + ( l < lo );
        lo  = l;
    
        l   = lo + 0x2000U;
        hi += ( l < lo );
    
        return (FT_Int32)( ( (FT_UInt32)hi << 18 ) | ( l >> 14 ) );
      }
    
    #endif /* TT_DotFix14 */
    
    
      /**************************************************************************
       *
       * @Function:
       *   Current_Ratio
       *
       * @Description:
       *   Returns the current aspect ratio scaling factor depending on the
       *   projection vector's state and device resolutions.
       *
       * @Return:
       *   The aspect ratio in 16.16 format, always <= 1.0 .
       */
      static FT_Long
      Current_Ratio( TT_ExecContext  exc )
      {
        if ( !exc->tt_metrics.ratio )
        {
          if ( exc->GS.projVector.y == 0 )
            exc->tt_metrics.ratio = exc->tt_metrics.x_ratio;
    
          else if ( exc->GS.projVector.x == 0 )
            exc->tt_metrics.ratio = exc->tt_metrics.y_ratio;
    
          else
          {
            FT_F26Dot6  x, y;
    
    
            x = TT_MulFix14( exc->tt_metrics.x_ratio,
                             exc->GS.projVector.x );
            y = TT_MulFix14( exc->tt_metrics.y_ratio,
                             exc->GS.projVector.y );
            exc->tt_metrics.ratio = FT_Hypot( x, y );
          }
        }
        return exc->tt_metrics.ratio;
      }
    
    
      FT_CALLBACK_DEF( FT_Long )
      Current_Ppem( TT_ExecContext  exc )
      {
        return exc->tt_metrics.ppem;
      }
    
    
      FT_CALLBACK_DEF( FT_Long )
      Current_Ppem_Stretched( TT_ExecContext  exc )
      {
        return FT_MulFix( exc->tt_metrics.ppem, Current_Ratio( exc ) );
      }
    
    
      /**************************************************************************
       *
       * Functions related to the control value table (CVT).
       *
       */
    
    
      FT_CALLBACK_DEF( FT_F26Dot6 )
      Read_CVT( TT_ExecContext  exc,
                FT_ULong        idx )
      {
        return exc->cvt[idx];
      }
    
    
      FT_CALLBACK_DEF( FT_F26Dot6 )
      Read_CVT_Stretched( TT_ExecContext  exc,
                          FT_ULong        idx )
      {
        return FT_MulFix( exc->cvt[idx], Current_Ratio( exc ) );
      }
    
    
      FT_CALLBACK_DEF( void )
      Write_CVT( TT_ExecContext  exc,
                 FT_ULong        idx,
                 FT_F26Dot6      value )
      {
        exc->cvt[idx] = value;
      }
    
    
      FT_CALLBACK_DEF( void )
      Write_CVT_Stretched( TT_ExecContext  exc,
                           FT_ULong        idx,
                           FT_F26Dot6      value )
      {
        exc->cvt[idx] = FT_DivFix( value, Current_Ratio( exc ) );
      }
    
    
      FT_CALLBACK_DEF( void )
      Move_CVT( TT_ExecContext  exc,
                FT_ULong        idx,
                FT_F26Dot6      value )
      {
        exc->cvt[idx] = ADD_LONG( exc->cvt[idx], value );
      }
    
    
      FT_CALLBACK_DEF( void )
      Move_CVT_Stretched( TT_ExecContext  exc,
                          FT_ULong        idx,
                          FT_F26Dot6      value )
      {
        exc->cvt[idx] = ADD_LONG( exc->cvt[idx],
                                  FT_DivFix( value, Current_Ratio( exc ) ) );
      }
    
    
      /**************************************************************************
       *
       * @Function:
       *   GetShortIns
       *
       * @Description:
       *   Returns a short integer taken from the instruction stream at
       *   address IP.
       *
       * @Return:
       *   Short read at code[IP].
       *
       * @Note:
       *   This one could become a macro.
       */
      static FT_Short
      GetShortIns( TT_ExecContext  exc )
      {
        /* Reading a byte stream so there is no endianness (DaveP) */
        exc->IP += 2;
        return (FT_Short)( ( exc->code[exc->IP - 2] << 8 ) +
                             exc->code[exc->IP - 1]      );
      }
    
    
      /**************************************************************************
       *
       * @Function:
       *   Ins_Goto_CodeRange
       *
       * @Description:
       *   Goes to a certain code range in the instruction stream.
       *
       * @Input:
       *   aRange ::
       *     The index of the code range.
       *
       *   aIP ::
       *     The new IP address in the code range.
       *
       * @Return:
       *   SUCCESS or FAILURE.
       */
      static FT_Bool
      Ins_Goto_CodeRange( TT_ExecContext  exc,
                          FT_Int          aRange,
                          FT_Long         aIP )
      {
        TT_CodeRange*  range;
    
    
        if ( aRange < 1 || aRange > 3 )
        {
          exc->error = FT_THROW( Bad_Argument );
          return FAILURE;
        }
    
        range = &exc->codeRangeTable[aRange - 1];
    
        if ( !range->base )     /* invalid coderange */
        {
          exc->error = FT_THROW( 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 > range->size )
        {
          exc->error = FT_THROW( Code_Overflow );
          return FAILURE;
        }
    
        exc->code     = range->base;
        exc->codeSize = range->size;
        exc->IP       = aIP;
        exc->curRange = aRange;
    
        return SUCCESS;
      }
    
    
      /*
       *
       * Apple's TrueType specification at
       *
       *   https://developer.apple.com/fonts/TrueType-Reference-Manual/RM02/Chap2.html#order
       *
       * gives the following order of operations in instructions that move
       * points.
       *
       *   - check single width cut-in (MIRP, MDRP)
       *
       *   - check control value cut-in (MIRP, MIAP)
       *
       *   - apply engine compensation (MIRP, MDRP)
       *
       *   - round distance (MIRP, MDRP) or value (MIAP, MDAP)
       *
       *   - check minimum distance (MIRP,MDRP)
       *
       *   - move point (MIRP, MDRP, MIAP, MSIRP, MDAP)
       *
       * For rounding instructions, engine compensation happens before rounding.
       *
       */
    
    
      /**************************************************************************
       *
       * @Function:
       *   Direct_Move
       *
       * @Description:
       *   Moves a point by a given distance along the freedom vector.  The
       *   point will be `touched'.
       *
       * @Input:
       *   point ::
       *     The index of the point to move.
       *
       *   distance ::
       *     The distance to apply.
       *
       * @InOut:
       *   zone ::
       *     The affected glyph zone.
       *
       * @Note:
       *   See `ttinterp.h' for details on backward compatibility mode.
       *   `Touches' the point.
       */
      static void
      Direct_Move( TT_ExecContext  exc,
                   TT_GlyphZone    zone,
                   FT_UShort       point,
                   FT_F26Dot6      distance )
      {
        FT_F26Dot6  v;
    
    
        v = exc->GS.freeVector.x;
    
        if ( v != 0 )
        {
    #ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY
          if ( SUBPIXEL_HINTING_INFINALITY                            &&
               ( !exc->ignore_x_mode                                ||
                 ( exc->sph_tweak_flags & SPH_TWEAK_ALLOW_X_DMOVE ) ) )
            zone->cur[point].x = ADD_LONG( zone->cur[point].x,
                                           FT_MulDiv( distance,
                                                      v,
                                                      exc->F_dot_P ) );
          else
    #endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */
    
    #ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL
          /* Exception to the post-IUP curfew: Allow the x component of */
          /* diagonal moves, but only post-IUP.  DejaVu tries to adjust */
          /* diagonal stems like on `Z' and `z' post-IUP.               */
          if ( SUBPIXEL_HINTING_MINIMAL && !exc->backward_compatibility )
            zone->cur[point].x = ADD_LONG( zone->cur[point].x,
                                           FT_MulDiv( distance,
                                                      v,
                                                      exc->F_dot_P ) );
          else
    #endif
    
          if ( NO_SUBPIXEL_HINTING )
            zone->cur[point].x = ADD_LONG( zone->cur[point].x,
                                           FT_MulDiv( distance,
                                                      v,
                                                      exc->F_dot_P ) );
    
          zone->tags[point] |= FT_CURVE_TAG_TOUCH_X;
        }
    
        v = exc->GS.freeVector.y;
    
        if ( v != 0 )
        {
    #ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL
          if ( !( SUBPIXEL_HINTING_MINIMAL    &&
                  exc->backward_compatibility &&
                  exc->iupx_called            &&
                  exc->iupy_called            ) )
    #endif
            zone->cur[point].y = ADD_LONG( zone->cur[point].y,
                                           FT_MulDiv( distance,
                                                      v,
                                                      exc->F_dot_P ) );
    
          zone->tags[point] |= FT_CURVE_TAG_TOUCH_Y;
        }
      }
    
    
      /**************************************************************************
       *
       * @Function:
       *   Direct_Move_Orig
       *
       * @Description:
       *   Moves the *original* position of a point by a given distance along
       *   the freedom vector.  Obviously, the point will not be `touched'.
       *
       * @Input:
       *   point ::
       *     The index of the point to move.
       *
       *   distance ::
       *     The distance to apply.
       *
       * @InOut:
       *   zone ::
       *     The affected glyph zone.
       */
      static void
      Direct_Move_Orig( TT_ExecContext  exc,
                        TT_GlyphZone    zone,
                        FT_UShort       point,
                        FT_F26Dot6      distance )
      {
        FT_F26Dot6  v;
    
    
        v = exc->GS.freeVector.x;
    
        if ( v != 0 )
          zone->org[point].x = ADD_LONG( zone->org[point].x,
                                         FT_MulDiv( distance,
                                                    v,
                                                    exc->F_dot_P ) );
    
        v = exc->GS.freeVector.y;
    
        if ( v != 0 )
          zone->org[point].y = ADD_LONG( zone->org[point].y,
                                         FT_MulDiv( distance,
                                                    v,
                                                    exc->F_dot_P ) );
      }
    
    
      /**************************************************************************
       *
       * Special versions of Direct_Move()
       *
       *   The following versions are used whenever both vectors are both
       *   along one of the coordinate unit vectors, i.e. in 90% of the cases.
       *   See `ttinterp.h' for details on backward compatibility mode.
       *
       */
    
    
      static void
      Direct_Move_X( TT_ExecContext  exc,
                     TT_GlyphZone    zone,
                     FT_UShort       point,
                     FT_F26Dot6      distance )
      {
    #ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY
        if ( SUBPIXEL_HINTING_INFINALITY && !exc->ignore_x_mode )
          zone->cur[point].x = ADD_LONG( zone->cur[point].x, distance );
        else
    #endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */
    
    #ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL
        if ( SUBPIXEL_HINTING_MINIMAL && !exc->backward_compatibility )
          zone->cur[point].x = ADD_LONG( zone->cur[point].x, distance );
        else
    #endif
    
        if ( NO_SUBPIXEL_HINTING )
          zone->cur[point].x = ADD_LONG( zone->cur[point].x, distance );
    
        zone->tags[point]  |= FT_CURVE_TAG_TOUCH_X;
      }
    
    
      static void
      Direct_Move_Y( TT_ExecContext  exc,
                     TT_GlyphZone    zone,
                     FT_UShort       point,
                     FT_F26Dot6      distance )
      {
        FT_UNUSED( exc );
    
    #ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL
        if ( !( SUBPIXEL_HINTING_MINIMAL             &&
                exc->backward_compatibility          &&
                exc->iupx_called && exc->iupy_called ) )
    #endif
          zone->cur[point].y = ADD_LONG( zone->cur[point].y, distance );
    
        zone->tags[point] |= FT_CURVE_TAG_TOUCH_Y;
      }
    
    
      /**************************************************************************
       *
       * Special versions of Direct_Move_Orig()
       *
       *   The following versions are used whenever both vectors are both
       *   along one of the coordinate unit vectors, i.e. in 90% of the cases.
       *
       */
    
    
      static void
      Direct_Move_Orig_X( TT_ExecContext  exc,
                          TT_GlyphZone    zone,
                          FT_UShort       point,
                          FT_F26Dot6      distance )
      {
        FT_UNUSED( exc );
    
        zone->org[point].x = ADD_LONG( zone->org[point].x, distance );
      }
    
    
      static void
      Direct_Move_Orig_Y( TT_ExecContext  exc,
                          TT_GlyphZone    zone,
                          FT_UShort       point,
                          FT_F26Dot6      distance )
      {
        FT_UNUSED( exc );
    
        zone->org[point].y = ADD_LONG( zone->org[point].y, distance );
      }
    
      /**************************************************************************
       *
       * @Function:
       *   Round_None
       *
       * @Description:
       *   Does not round, but adds engine compensation.
       *
       * @Input:
       *   distance ::
       *     The distance (not) to round.
       *
       *   compensation ::
       *     The engine compensation.
       *
       * @Return:
       *   The compensated distance.
       */
      static FT_F26Dot6
      Round_None( TT_ExecContext  exc,
                  FT_F26Dot6      distance,
                  FT_F26Dot6      compensation )
      {
        FT_F26Dot6  val;
    
        FT_UNUSED( exc );
    
    
        if ( distance >= 0 )
        {
          val = ADD_LONG( distance, compensation );
          if ( val < 0 )
            val = 0;
        }
        else
        {
          val = SUB_LONG( distance, compensation );
          if ( val > 0 )
            val = 0;
        }
        return val;
      }
    
    
      /**************************************************************************
       *
       * @Function:
       *   Round_To_Grid
       *
       * @Description:
       *   Rounds value to grid after adding engine compensation.
       *
       * @Input:
       *   distance ::
       *     The distance to round.
       *
       *   compensation ::
       *     The engine compensation.
       *
       * @Return:
       *   Rounded distance.
       */
      static FT_F26Dot6
      Round_To_Grid( TT_ExecContext  exc,
                     FT_F26Dot6      distance,
                     FT_F26Dot6      compensation )
      {
        FT_F26Dot6  val;
    
        FT_UNUSED( exc );
    
    
        if ( distance >= 0 )
        {
          val = FT_PIX_ROUND_LONG( ADD_LONG( distance, compensation ) );
          if ( val < 0 )
            val = 0;
        }
        else
        {
          val = NEG_LONG( FT_PIX_ROUND_LONG( SUB_LONG( compensation,
                                                       distance ) ) );
          if ( val > 0 )
            val = 0;
        }
    
        return val;
      }
    
    
      /**************************************************************************
       *
       * @Function:
       *   Round_To_Half_Grid
       *
       * @Description:
       *   Rounds value to half grid after adding engine compensation.
       *
       * @Input:
       *   distance ::
       *     The distance to round.
       *
       *   compensation ::
       *     The engine compensation.
       *
       * @Return:
       *   Rounded distance.
       */
      static FT_F26Dot6
      Round_To_Half_Grid( TT_ExecContext  exc,
                          FT_F26Dot6      distance,
                          FT_F26Dot6      compensation )
      {
        FT_F26Dot6  val;
    
        FT_UNUSED( exc );
    
    
        if ( distance >= 0 )
        {
          val = ADD_LONG( FT_PIX_FLOOR( ADD_LONG( distance, compensation ) ),
                          32 );
          if ( val < 0 )
            val = 32;
        }
        else
        {
          val = NEG_LONG( ADD_LONG( FT_PIX_FLOOR( SUB_LONG( compensation,
                                                            distance ) ),
                                    32 ) );
          if ( val > 0 )
            val = -32;
        }
    
        return val;
      }
    
    
      /**************************************************************************
       *
       * @Function:
       *   Round_Down_To_Grid
       *
       * @Description:
       *   Rounds value down to grid after adding engine compensation.
       *
       * @Input:
       *   distance ::
       *     The distance to round.
       *
       *   compensation ::
       *     The engine compensation.
       *
       * @Return:
       *   Rounded distance.
       */
      static FT_F26Dot6
      Round_Down_To_Grid( TT_ExecContext  exc,
                          FT_F26Dot6      distance,
                          FT_F26Dot6      compensation )
      {
        FT_F26Dot6  val;
    
        FT_UNUSED( exc );
    
    
        if ( distance >= 0 )
        {
          val = FT_PIX_FLOOR( ADD_LONG( distance, compensation ) );
          if ( val < 0 )
            val = 0;
        }
        else
        {
          val = NEG_LONG( FT_PIX_FLOOR( SUB_LONG( compensation, distance ) ) );
          if ( val > 0 )
            val = 0;
        }
    
        return val;
      }
    
    
      /**************************************************************************
       *
       * @Function:
       *   Round_Up_To_Grid
       *
       * @Description:
       *   Rounds value up to grid after adding engine compensation.
       *
       * @Input:
       *   distance ::
       *     The distance to round.
       *
       *   compensation ::
       *     The engine compensation.
       *
       * @Return:
       *   Rounded distance.
       */
      static FT_F26Dot6
      Round_Up_To_Grid( TT_ExecContext  exc,
                        FT_F26Dot6      distance,
                        FT_F26Dot6      compensation )
      {
        FT_F26Dot6  val;
    
        FT_UNUSED( exc );
    
    
        if ( distance >= 0 )
        {
          val = FT_PIX_CEIL_LONG( ADD_LONG( distance, compensation ) );
          if ( val < 0 )
            val = 0;
        }
        else
        {
          val = NEG_LONG( FT_PIX_CEIL_LONG( SUB_LONG( compensation,
                                                      distance ) ) );
          if ( val > 0 )
            val = 0;
        }
    
        return val;
      }
    
    
      /**************************************************************************
       *
       * @Function:
       *   Round_To_Double_Grid
       *
       * @Description:
       *   Rounds value to double grid after adding engine compensation.
       *
       * @Input:
       *   distance ::
       *     The distance to round.
       *
       *   compensation ::
       *     The engine compensation.
       *
       * @Return:
       *   Rounded distance.
       */
      static FT_F26Dot6
      Round_To_Double_Grid( TT_ExecContext  exc,
                            FT_F26Dot6      distance,
                            FT_F26Dot6      compensation )
      {
        FT_F26Dot6  val;
    
        FT_UNUSED( exc );
    
    
        if ( distance >= 0 )
        {
          val = FT_PAD_ROUND_LONG( ADD_LONG( distance, compensation ), 32 );
          if ( val < 0 )
            val = 0;
        }
        else
        {
          val = NEG_LONG( FT_PAD_ROUND_LONG( SUB_LONG( compensation, distance ),
                                             32 ) );
          if ( val > 0 )
            val = 0;
        }
    
        return val;
      }
    
    
      /**************************************************************************
       *
       * @Function:
       *   Round_Super
       *
       * @Description:
       *   Super-rounds value to grid after adding engine compensation.
       *
       * @Input:
       *   distance ::
       *     The distance to round.
       *
       *   compensation ::
       *     The engine compensation.
       *
       * @Return:
       *   Rounded distance.
       *
       * @Note:
       *   The TrueType specification says very little about the relationship
       *   between rounding and engine compensation.  However, it seems from
       *   the description of super round that we should add the compensation
       *   before rounding.
       */
      static FT_F26Dot6
      Round_Super( TT_ExecContext  exc,
                   FT_F26Dot6      distance,
                   FT_F26Dot6      compensation )
      {
        FT_F26Dot6  val;
    
    
        if ( distance >= 0 )
        {
          val = ADD_LONG( distance,
                          exc->threshold - exc->phase + compensation ) &
                  -exc->period;
          val = ADD_LONG( val, exc->phase );
          if ( val < 0 )
            val = exc->phase;
        }
        else
        {
          val = NEG_LONG( SUB_LONG( exc->threshold - exc->phase + compensation,
                                    distance ) &
                            -exc->period );
          val = SUB_LONG( val, exc->phase );
          if ( val > 0 )
            val = -exc->phase;
        }
    
        return val;
      }
    
    
      /**************************************************************************
       *
       * @Function:
       *   Round_Super_45
       *
       * @Description:
       *   Super-rounds value to grid after adding engine compensation.
       *
       * @Input:
       *   distance ::
       *     The distance to round.
       *
       *   compensation ::
       *     The engine compensation.
       *
       * @Return:
       *   Rounded distance.
       *
       * @Note:
       *   There is a separate function for Round_Super_45() as we may need
       *   greater precision.
       */
      static FT_F26Dot6
      Round_Super_45( TT_ExecContext  exc,
                      FT_F26Dot6      distance,
                      FT_F26Dot6      compensation )
      {
        FT_F26Dot6  val;
    
    
        if ( distance >= 0 )
        {
          val = ( ADD_LONG( distance,
                            exc->threshold - exc->phase + compensation ) /
                    exc->period ) * exc->period;
          val = ADD_LONG( val, exc->phase );
          if ( val < 0 )
            val = exc->phase;
        }
        else
        {
          val = NEG_LONG( ( SUB_LONG( exc->threshold - exc->phase + compensation,
                                      distance ) /
                              exc->period ) * exc->period );
          val = SUB_LONG( val, exc->phase );
          if ( val > 0 )
            val = -exc->phase;
        }
    
        return val;
      }
    
    
      /**************************************************************************
       *
       * @Function:
       *   Compute_Round
       *
       * @Description:
       *   Sets the rounding mode.
       *
       * @Input:
       *   round_mode ::
       *     The rounding mode to be used.
       */
      static void
      Compute_Round( TT_ExecContext  exc,
                     FT_Byte         round_mode )
      {
        switch ( round_mode )
        {
        case TT_Round_Off:
          exc->func_round = (TT_Round_Func)Round_None;
          break;
    
        case TT_Round_To_Grid:
          exc->func_round = (TT_Round_Func)Round_To_Grid;
          break;
    
        case TT_Round_Up_To_Grid:
          exc->func_round = (TT_Round_Func)Round_Up_To_Grid;
          break;
    
        case TT_Round_Down_To_Grid:
          exc->func_round = (TT_Round_Func)Round_Down_To_Grid;
          break;
    
        case TT_Round_To_Half_Grid:
          exc->func_round = (TT_Round_Func)Round_To_Half_Grid;
          break;
    
        case TT_Round_To_Double_Grid:
          exc->func_round = (TT_Round_Func)Round_To_Double_Grid;
          break;
    
        case TT_Round_Super:
          exc->func_round = (TT_Round_Func)Round_Super;
          break;
    
        case TT_Round_Super_45:
          exc->func_round = (TT_Round_Func)Round_Super_45;
          break;
        }
      }
    
    
      /**************************************************************************
       *
       * @Function:
       *   SetSuperRound
       *
       * @Description:
       *   Sets Super Round parameters.
       *
       * @Input:
       *   GridPeriod ::
       *     The grid period.
       *
       *   selector ::
       *     The SROUND opcode.
       */
      static void
      SetSuperRound( TT_ExecContext  exc,
                     FT_F2Dot14      GridPeriod,
                     FT_Long         selector )
      {
        switch ( (FT_Int)( selector & 0xC0 ) )
        {
          case 0:
            exc->period = GridPeriod / 2;
            break;
    
          case 0x40:
            exc->period = GridPeriod;
            break;
    
          case 0x80:
            exc->period = GridPeriod * 2;
            break;
    
          /* This opcode is reserved, but... */
          case 0xC0:
            exc->period = GridPeriod;
            break;
        }
    
        switch ( (FT_Int)( selector & 0x30 ) )
        {
        case 0:
          exc->phase = 0;
          break;
    
        case 0x10:
          exc->phase = exc->period / 4;
          break;
    
        case 0x20:
          exc->phase = exc->period / 2;
          break;
    
        case 0x30:
          exc->phase = exc->period * 3 / 4;
          break;
        }
    
        if ( ( selector & 0x0F ) == 0 )
          exc->threshold = exc->period - 1;
        else
          exc->threshold = ( (FT_Int)( selector & 0x0F ) - 4 ) * exc->period / 8;
    
        /* convert to F26Dot6 format */
        exc->period    >>= 8;
        exc->phase     >>= 8;
        exc->threshold >>= 8;
      }
    
    
      /**************************************************************************
       *
       * @Function:
       *   Project
       *
       * @Description:
       *   Computes the projection of vector given by (v2-v1) along the
       *   current projection vector.
       *
       * @Input:
       *   v1 ::
       *     First input vector.
       *   v2 ::
       *     Second input vector.
       *
       * @Return:
       *   The distance in F26dot6 format.
       */
      static FT_F26Dot6
      Project( TT_ExecContext  exc,
               FT_Pos          dx,
               FT_Pos          dy )
      {
        return TT_DotFix14( dx, dy,
                            exc->GS.projVector.x,
                            exc->GS.projVector.y );
      }
    
    
      /**************************************************************************
       *
       * @Function:
       *   Dual_Project
       *
       * @Description:
       *   Computes the projection of the vector given by (v2-v1) along the
       *   current dual vector.
       *
       * @Input:
       *   v1 ::
       *     First input vector.
       *   v2 ::
       *     Second input vector.
       *
       * @Return:
       *   The distance in F26dot6 format.
       */
      static FT_F26Dot6
      Dual_Project( TT_ExecContext  exc,
                    FT_Pos          dx,
                    FT_Pos          dy )
      {
        return TT_DotFix14( dx, dy,
                            exc->GS.dualVector.x,
                            exc->GS.dualVector.y );
      }
    
    
      /**************************************************************************
       *
       * @Function:
       *   Project_x
       *
       * @Description:
       *   Computes the projection of the vector given by (v2-v1) along the
       *   horizontal axis.
       *
       * @Input:
       *   v1 ::
       *     First input vector.
       *   v2 ::
       *     Second input vector.
       *
       * @Return:
       *   The distance in F26dot6 format.
       */
      static FT_F26Dot6
      Project_x( TT_ExecContext  exc,
                 FT_Pos          dx,
                 FT_Pos          dy )
      {
        FT_UNUSED( exc );
        FT_UNUSED( dy );
    
        return dx;
      }
    
    
      /**************************************************************************
       *
       * @Function:
       *   Project_y
       *
       * @Description:
       *   Computes the projection of the vector given by (v2-v1) along the
       *   vertical axis.
       *
       * @Input:
       *   v1 ::
       *     First input vector.
       *   v2 ::
       *     Second input vector.
       *
       * @Return:
       *   The distance in F26dot6 format.
       */
      static FT_F26Dot6
      Project_y( TT_ExecContext  exc,
                 FT_Pos          dx,
                 FT_Pos          dy )
      {
        FT_UNUSED( exc );
        FT_UNUSED( dx );
    
        return dy;
      }
    
    
      /**************************************************************************
       *
       * @Function:
       *   Compute_Funcs
       *
       * @Description:
       *   Computes the projection and movement function pointers according
       *   to the current graphics state.
       */
      static void
      Compute_Funcs( TT_ExecContext  exc )
      {
        if ( exc->GS.freeVector.x == 0x4000 )
          exc->F_dot_P = exc->GS.projVector.x;
        else if ( exc->GS.freeVector.y == 0x4000 )
          exc->F_dot_P = exc->GS.projVector.y;
        else
          exc->F_dot_P =
            ( (FT_Long)exc->GS.projVector.x * exc->GS.freeVector.x +
              (FT_Long)exc->GS.projVector.y * exc->GS.freeVector.y ) >> 14;
    
        if ( exc->GS.projVector.x == 0x4000 )
          exc->func_project = (TT_Project_Func)Project_x;
        else if ( exc->GS.projVector.y == 0x4000 )
          exc->func_project = (TT_Project_Func)Project_y;
        else
          exc->func_project = (TT_Project_Func)Project;
    
        if ( exc->GS.dualVector.x == 0x4000 )
          exc->func_dualproj = (TT_Project_Func)Project_x;
        else if ( exc->GS.dualVector.y == 0x4000 )
          exc->func_dualproj = (TT_Project_Func)Project_y;
        else
          exc->func_dualproj = (TT_Project_Func)Dual_Project;
    
        exc->func_move      = (TT_Move_Func)Direct_Move;
        exc->func_move_orig = (TT_Move_Func)Direct_Move_Orig;
    
        if ( exc->F_dot_P == 0x4000L )
        {
          if ( exc->GS.freeVector.x == 0x4000 )
          {
            exc->func_move      = (TT_Move_Func)Direct_Move_X;
            exc->func_move_orig = (TT_Move_Func)Direct_Move_Orig_X;
          }
          else if ( exc->GS.freeVector.y == 0x4000 )
          {
            exc->func_move      = (TT_Move_Func)Direct_Move_Y;
            exc->func_move_orig = (TT_Move_Func)Direct_Move_Orig_Y;
          }
        }
    
        /* at small sizes, F_dot_P can become too small, resulting   */
        /* in overflows and `spikes' in a number of glyphs like `w'. */
    
        if ( FT_ABS( exc->F_dot_P ) < 0x400L )
          exc->F_dot_P = 0x4000L;
    
        /* Disable cached aspect ratio */
        exc->tt_metrics.ratio = 0;
      }
    
    
      /**************************************************************************
       *
       * @Function:
       *   Normalize
       *
       * @Description:
       *   Norms a vector.
       *
       * @Input:
       *   Vx ::
       *     The horizontal input vector coordinate.
       *   Vy ::
       *     The vertical input vector coordinate.
       *
       * @Output:
       *   R ::
       *     The normed unit vector.
       *
       * @Return:
       *   Returns FAILURE if a vector parameter is zero.
       *
       * @Note:
       *   In case Vx and Vy are both zero, `Normalize' returns SUCCESS, and
       *   R is undefined.
       */
      static FT_Bool
      Normalize( FT_F26Dot6      Vx,
                 FT_F26Dot6      Vy,
                 FT_UnitVector*  R )
      {
        FT_Vector V;
    
    
        if ( Vx == 0 && Vy == 0 )
        {
          /* XXX: UNDOCUMENTED! It seems that it is possible to try   */
          /*      to normalize the vector (0,0).  Return immediately. */
          return SUCCESS;
        }
    
        V.x = Vx;
        V.y = Vy;
    
        FT_Vector_NormLen( &V );
    
        R->x = (FT_F2Dot14)( V.x / 4 );
        R->y = (FT_F2Dot14)( V.y / 4 );
    
        return SUCCESS;
      }
    
    
      /**************************************************************************
       *
       * Here we start with the implementation of the various opcodes.
       *
       */
    
    
    #define ARRAY_BOUND_ERROR                         \
        do                                            \
        {                                             \
          exc->error = FT_THROW( Invalid_Reference ); \
          return;                                     \
        } while (0)
    
    
      /**************************************************************************
       *
       * MPPEM[]:      Measure Pixel Per EM
       * Opcode range: 0x4B
       * Stack:        --> Euint16
       */
      static void
      Ins_MPPEM( TT_ExecContext  exc,
                 FT_Long*        args )
      {
        args[0] = exc->func_cur_ppem( exc );
      }
    
    
      /**************************************************************************
       *
       * MPS[]:        Measure Point Size
       * Opcode range: 0x4C
       * Stack:        --> Euint16
       */
      static void
      Ins_MPS( TT_ExecContext  exc,
               FT_Long*        args )
      {
        if ( NO_SUBPIXEL_HINTING )
        {
          /* Microsoft's GDI bytecode interpreter always returns value 12; */
          /* we return the current PPEM value instead.                     */
          args[0] = exc->func_cur_ppem( exc );
        }
        else
        {
          /* A possible practical application of the MPS instruction is to   */
          /* implement optical scaling and similar features, which should be */
          /* based on perceptual attributes, thus independent of the         */
          /* resolution.                                                     */
          args[0] = exc->pointSize;
        }
      }
    
    
      /**************************************************************************
       *
       * DUP[]:        DUPlicate the stack's top element
       * Opcode range: 0x20
       * Stack:        StkElt --> StkElt StkElt
       */
      static void
      Ins_DUP( FT_Long*  args )
      {
        args[1] = args[0];
      }
    
    
      /**************************************************************************
       *
       * POP[]:        POP the stack's top element
       * Opcode range: 0x21
       * Stack:        StkElt -->
       */
      static void
      Ins_POP( void )
      {
        /* nothing to do */
      }
    
    
      /**************************************************************************
       *
       * CLEAR[]:      CLEAR the entire stack
       * Opcode range: 0x22
       * Stack:        StkElt... -->
       */
      static void
      Ins_CLEAR( TT_ExecContext  exc )
      {
        exc->new_top = 0;
      }
    
    
      /**************************************************************************
       *
       * SWAP[]:       SWAP the stack's top two elements
       * Opcode range: 0x23
       * Stack:        2 * StkElt --> 2 * StkElt
       */
      static void
      Ins_SWAP( FT_Long*  args )
      {
        FT_Long  L;
    
    
        L       = args[0];
        args[0] = args[1];
        args[1] = L;
      }
    
    
      /**************************************************************************
       *
       * DEPTH[]:      return the stack DEPTH
       * Opcode range: 0x24
       * Stack:        --> uint32
       */
      static void
      Ins_DEPTH( TT_ExecContext  exc,
                 FT_Long*        args )
      {
        args[0] = exc->top;
      }
    
    
      /**************************************************************************
       *
       * LT[]:         Less Than
       * Opcode range: 0x50
       * Stack:        int32? int32? --> bool
       */
      static void
      Ins_LT( FT_Long*  args )
      {
        args[0] = ( args[0] < args[1] );
      }
    
    
      /**************************************************************************
       *
       * LTEQ[]:       Less Than or EQual
       * Opcode range: 0x51
       * Stack:        int32? int32? --> bool
       */
      static void
      Ins_LTEQ( FT_Long*  args )
      {
        args[0] = ( args[0] <= args[1] );
      }
    
    
      /**************************************************************************
       *
       * GT[]:         Greater Than
       * Opcode range: 0x52
       * Stack:        int32? int32? --> bool
       */
      static void
      Ins_GT( FT_Long*  args )
      {
        args[0] = ( args[0] > args[1] );
      }
    
    
      /**************************************************************************
       *
       * GTEQ[]:       Greater Than or EQual
       * Opcode range: 0x53
       * Stack:        int32? int32? --> bool
       */
      static void
      Ins_GTEQ( FT_Long*  args )
      {
        args[0] = ( args[0] >= args[1] );
      }
    
    
      /**************************************************************************
       *
       * EQ[]:         EQual
       * Opcode range: 0x54
       * Stack:        StkElt StkElt --> bool
       */
      static void
      Ins_EQ( FT_Long*  args )
      {
        args[0] = ( args[0] == args[1] );
      }
    
    
      /**************************************************************************
       *
       * NEQ[]:        Not EQual
       * Opcode range: 0x55
       * Stack:        StkElt StkElt --> bool
       */
      static void
      Ins_NEQ( FT_Long*  args )
      {
        args[0] = ( args[0] != args[1] );
      }
    
    
      /**************************************************************************
       *
       * ODD[]:        Is ODD
       * Opcode range: 0x56
       * Stack:        f26.6 --> bool
       */
      static void
      Ins_ODD( TT_ExecContext  exc,
               FT_Long*        args )
      {
        args[0] = ( ( exc->func_round( exc, args[0], 0 ) & 127 ) == 64 );
      }
    
    
      /**************************************************************************
       *
       * EVEN[]:       Is EVEN
       * Opcode range: 0x57
       * Stack:        f26.6 --> bool
       */
      static void
      Ins_EVEN( TT_ExecContext  exc,
                FT_Long*        args )
      {
        args[0] = ( ( exc->func_round( exc, args[0], 0 ) & 127 ) == 0 );
      }
    
    
      /**************************************************************************
       *
       * AND[]:        logical AND
       * Opcode range: 0x5A
       * Stack:        uint32 uint32 --> uint32
       */
      static void
      Ins_AND( FT_Long*  args )
      {
        args[0] = ( args[0] && args[1] );
      }
    
    
      /**************************************************************************
       *
       * OR[]:         logical OR
       * Opcode range: 0x5B
       * Stack:        uint32 uint32 --> uint32
       */
      static void
      Ins_OR( FT_Long*  args )
      {
        args[0] = ( args[0] || args[1] );
      }
    
    
      /**************************************************************************
       *
       * NOT[]:        logical NOT
       * Opcode range: 0x5C
       * Stack:        StkElt --> uint32
       */
      static void
      Ins_NOT( FT_Long*  args )
      {
        args[0] = !args[0];
      }
    
    
      /**************************************************************************
       *
       * ADD[]:        ADD
       * Opcode range: 0x60
       * Stack:        f26.6 f26.6 --> f26.6
       */
      static void
      Ins_ADD( FT_Long*  args )
      {
        args[0] = ADD_LONG( args[0], args[1] );
      }
    
    
      /**************************************************************************
       *
       * SUB[]:        SUBtract
       * Opcode range: 0x61
       * Stack:        f26.6 f26.6 --> f26.6
       */
      static void
      Ins_SUB( FT_Long*  args )
      {
        args[0] = SUB_LONG( args[0], args[1] );
      }
    
    
      /**************************************************************************
       *
       * DIV[]:        DIVide
       * Opcode range: 0x62
       * Stack:        f26.6 f26.6 --> f26.6
       */
      static void
      Ins_DIV( TT_ExecContext  exc,
               FT_Long*        args )
      {
        if ( args[1] == 0 )
          exc->error = FT_THROW( Divide_By_Zero );
        else
          args[0] = FT_MulDiv_No_Round( args[0], 64L, args[1] );
      }
    
    
      /**************************************************************************
       *
       * MUL[]:        MULtiply
       * Opcode range: 0x63
       * Stack:        f26.6 f26.6 --> f26.6
       */
      static void
      Ins_MUL( FT_Long*  args )
      {
        args[0] = FT_MulDiv( args[0], args[1], 64L );
      }
    
    
      /**************************************************************************
       *
       * ABS[]:        ABSolute value
       * Opcode range: 0x64
       * Stack:        f26.6 --> f26.6
       */
      static void
      Ins_ABS( FT_Long*  args )
      {
        if ( args[0] < 0 )
          args[0] = NEG_LONG( args[0] );
      }
    
    
      /**************************************************************************
       *
       * NEG[]:        NEGate
       * Opcode range: 0x65
       * Stack:        f26.6 --> f26.6
       */
      static void
      Ins_NEG( FT_Long*  args )
      {
        args[0] = NEG_LONG( args[0] );
      }
    
    
      /**************************************************************************
       *
       * FLOOR[]:      FLOOR
       * Opcode range: 0x66
       * Stack:        f26.6 --> f26.6
       */
      static void
      Ins_FLOOR( FT_Long*  args )
      {
        args[0] = FT_PIX_FLOOR( args[0] );
      }
    
    
      /**************************************************************************
       *
       * CEILING[]:    CEILING
       * Opcode range: 0x67
       * Stack:        f26.6 --> f26.6
       */
      static void
      Ins_CEILING( FT_Long*  args )
      {
        args[0] = FT_PIX_CEIL_LONG( args[0] );
      }
    
    
      /**************************************************************************
       *
       * RS[]:         Read Store
       * Opcode range: 0x43
       * Stack:        uint32 --> uint32
       */
      static void
      Ins_RS( TT_ExecContext  exc,
              FT_Long*        args )
      {
        FT_ULong  I = (FT_ULong)args[0];
    
    
        if ( BOUNDSL( I, exc->storeSize ) )
        {
          if ( exc->pedantic_hinting )
            ARRAY_BOUND_ERROR;
          else
            args[0] = 0;
        }
        else
        {
    #ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY
          /* subpixel hinting - avoid Typeman Dstroke and */
          /* IStroke and Vacuform rounds                  */
          if ( SUBPIXEL_HINTING_INFINALITY                 &&
               exc->ignore_x_mode                          &&
               ( ( I == 24                             &&
                   ( exc->face->sph_found_func_flags &
                     ( SPH_FDEF_SPACING_1 |
                       SPH_FDEF_SPACING_2 )          ) ) ||
                 ( I == 22                      &&
                   ( exc->sph_in_func_flags   &
                     SPH_FDEF_TYPEMAN_STROKES ) )        ||
                 ( I == 8                              &&
                   ( exc->face->sph_found_func_flags &
                     SPH_FDEF_VACUFORM_ROUND_1       ) &&
                   exc->iup_called                     ) ) )
            args[0] = 0;
          else
    #endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */
            args[0] = exc->storage[I];
        }
      }
    
    
      /**************************************************************************
       *
       * WS[]:         Write Store
       * Opcode range: 0x42
       * Stack:        uint32 uint32 -->
       */
      static void
      Ins_WS( TT_ExecContext  exc,
              FT_Long*        args )
      {
        FT_ULong  I = (FT_ULong)args[0];
    
    
        if ( BOUNDSL( I, exc->storeSize ) )
        {
          if ( exc->pedantic_hinting )
            ARRAY_BOUND_ERROR;
        }
        else
          exc->storage[I] = args[1];
      }
    
    
      /**************************************************************************
       *
       * WCVTP[]:      Write CVT in Pixel units
       * Opcode range: 0x44
       * Stack:        f26.6 uint32 -->
       */
      static void
      Ins_WCVTP( TT_ExecContext  exc,
                 FT_Long*        args )
      {
        FT_ULong  I = (FT_ULong)args[0];
    
    
        if ( BOUNDSL( I, exc->cvtSize ) )
        {
          if ( exc->pedantic_hinting )
            ARRAY_BOUND_ERROR;
        }
        else
          exc->func_write_cvt( exc, I, args[1] );
      }
    
    
      /**************************************************************************
       *
       * WCVTF[]:      Write CVT in Funits
       * Opcode range: 0x70
       * Stack:        uint32 uint32 -->
       */
      static void
      Ins_WCVTF( TT_ExecContext  exc,
                 FT_Long*        args )
      {
        FT_ULong  I = (FT_ULong)args[0];
    
    
        if ( BOUNDSL( I, exc->cvtSize ) )
        {
          if ( exc->pedantic_hinting )
            ARRAY_BOUND_ERROR;
        }
        else
          exc->cvt[I] = FT_MulFix( args[1], exc->tt_metrics.scale );
      }
    
    
      /**************************************************************************
       *
       * RCVT[]:       Read CVT
       * Opcode range: 0x45
       * Stack:        uint32 --> f26.6
       */
      static void
      Ins_RCVT( TT_ExecContext  exc,
                FT_Long*        args )
      {
        FT_ULong  I = (FT_ULong)args[0];
    
    
        if ( BOUNDSL( I, exc->cvtSize ) )
        {
          if ( exc->pedantic_hinting )
            ARRAY_BOUND_ERROR;
          else
            args[0] = 0;
        }
        else
          args[0] = exc->func_read_cvt( exc, I );
      }
    
    
      /**************************************************************************
       *
       * AA[]:         Adjust Angle
       * Opcode range: 0x7F
       * Stack:        uint32 -->
       */
      static void
      Ins_AA( void )
      {
        /* intentionally no longer supported */
      }
    
    
      /**************************************************************************
       *
       * DEBUG[]:      DEBUG.  Unsupported.
       * Opcode range: 0x4F
       * Stack:        uint32 -->
       *
       * Note: The original instruction pops a value from the stack.
       */
      static void
      Ins_DEBUG( TT_ExecContext  exc )
      {
        exc->error = FT_THROW( Debug_OpCode );
      }
    
    
      /**************************************************************************
       *
       * ROUND[ab]:    ROUND value
       * Opcode range: 0x68-0x6B
       * Stack:        f26.6 --> f26.6
       */
      static void
      Ins_ROUND( TT_ExecContext  exc,
                 FT_Long*        args )
      {
        args[0] = exc->func_round(
                    exc,
                    args[0],
                    exc->tt_metrics.compensations[exc->opcode - 0x68] );
      }
    
    
      /**************************************************************************
       *
       * NROUND[ab]:   No ROUNDing of value
       * Opcode range: 0x6C-0x6F
       * Stack:        f26.6 --> f26.6
       */
      static void
      Ins_NROUND( TT_ExecContext  exc,
                  FT_Long*        args )
      {
        args[0] = Round_None(
                    exc,
                    args[0],
                    exc->tt_metrics.compensations[exc->opcode - 0x6C] );
      }
    
    
      /**************************************************************************
       *
       * MAX[]:        MAXimum
       * Opcode range: 0x8B
       * Stack:        int32? int32? --> int32
       */
      static void
      Ins_MAX( FT_Long*  args )
      {
        if ( args[1] > args[0] )
          args[0] = args[1];
      }
    
    
      /**************************************************************************
       *
       * MIN[]:        MINimum
       * Opcode range: 0x8C
       * Stack:        int32? int32? --> int32
       */
      static void
      Ins_MIN( FT_Long*  args )
      {
        if ( args[1] < args[0] )
          args[0] = args[1];
      }
    
    
      /**************************************************************************
       *
       * MINDEX[]:     Move INDEXed element
       * Opcode range: 0x26
       * Stack:        int32? --> StkElt
       */
      static void
      Ins_MINDEX( TT_ExecContext  exc,
                  FT_Long*        args )
      {
        FT_Long  L, K;
    
    
        L = args[0];
    
        if ( L <= 0 || L > exc->args )
        {
          if ( exc->pedantic_hinting )
            exc->error = FT_THROW( Invalid_Reference );
        }
        else
        {
          K = exc->stack[exc->args - L];
    
          FT_ARRAY_MOVE( &exc->stack[exc->args - L    ],
                         &exc->stack[exc->args - L + 1],
                         ( L - 1 ) );
    
          exc->stack[exc->args - 1] = K;
        }
      }
    
    
      /**************************************************************************
       *
       * CINDEX[]:     Copy INDEXed element
       * Opcode range: 0x25
       * Stack:        int32 --> StkElt
       */
      static void
      Ins_CINDEX( TT_ExecContext  exc,
                  FT_Long*        args )
      {
        FT_Long  L;
    
    
        L = args[0];
    
        if ( L <= 0 || L > exc->args )
        {
          if ( exc->pedantic_hinting )
            exc->error = FT_THROW( Invalid_Reference );
          args[0] = 0;
        }
        else
          args[0] = exc->stack[exc->args - L];
      }
    
    
      /**************************************************************************
       *
       * ROLL[]:       ROLL top three elements
       * Opcode range: 0x8A
       * Stack:        3 * StkElt --> 3 * StkElt
       */
      static void
      Ins_ROLL( FT_Long*  args )
      {
        FT_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
       *
       */
    
    
      /**************************************************************************
       *
       * SLOOP[]:      Set LOOP variable
       * Opcode range: 0x17
       * Stack:        int32? -->
       */
      static void
      Ins_SLOOP( TT_ExecContext  exc,
                 FT_Long*        args )
      {
        if ( args[0] < 0 )
          exc->error = FT_THROW( Bad_Argument );
        else
        {
          /* we heuristically limit the number of loops to 16 bits */
          exc->GS.loop = args[0] > 0xFFFFL ? 0xFFFFL : args[0];
        }
      }
    
    
      static FT_Bool
      SkipCode( TT_ExecContext  exc )
      {
        exc->IP += exc->length;
    
        if ( exc->IP < exc->codeSize )
        {
          exc->opcode = exc->code[exc->IP];
    
          exc->length = opcode_length[exc->opcode];
          if ( exc->length < 0 )
          {
            if ( exc->IP + 1 >= exc->codeSize )
              goto Fail_Overflow;
            exc->length = 2 - exc->length * exc->code[exc->IP + 1];
          }
    
          if ( exc->IP + exc->length <= exc->codeSize )
            return SUCCESS;
        }
    
      Fail_Overflow:
        exc->error = FT_THROW( Code_Overflow );
        return FAILURE;
      }
    
    
      /**************************************************************************
       *
       * IF[]:         IF test
       * Opcode range: 0x58
       * Stack:        StkElt -->
       */
      static void
      Ins_IF( TT_ExecContext  exc,
              FT_Long*        args )
      {
        FT_Int   nIfs;
        FT_Bool  Out;
    
    
        if ( args[0] != 0 )
          return;
    
        nIfs = 1;
        Out = 0;
    
        do
        {
          if ( SkipCode( exc ) == FAILURE )
            return;
    
          switch ( exc->opcode )
          {
          case 0x58:      /* IF */
            nIfs++;
            break;
    
          case 0x1B:      /* ELSE */
            Out = FT_BOOL( nIfs == 1 );
            break;
    
          case 0x59:      /* EIF */
            nIfs--;
            Out = FT_BOOL( nIfs == 0 );
            break;
          }
        } while ( Out == 0 );
      }
    
    
      /**************************************************************************
       *
       * ELSE[]:       ELSE
       * Opcode range: 0x1B
       * Stack:        -->
       */
      static void
      Ins_ELSE( TT_ExecContext  exc )
      {
        FT_Int  nIfs;
    
    
        nIfs = 1;
    
        do
        {
          if ( SkipCode( exc ) == FAILURE )
            return;
    
          switch ( exc->opcode )
          {
          case 0x58:    /* IF */
            nIfs++;
            break;
    
          case 0x59:    /* EIF */
            nIfs--;
            break;
          }
        } while ( nIfs != 0 );
      }
    
    
      /**************************************************************************
       *
       * EIF[]:        End IF
       * Opcode range: 0x59
       * Stack:        -->
       */
      static void
      Ins_EIF( void )
      {
        /* nothing to do */
      }
    
    
      /**************************************************************************
       *
       * JMPR[]:       JuMP Relative
       * Opcode range: 0x1C
       * Stack:        int32 -->
       */
      static void
      Ins_JMPR( TT_ExecContext  exc,
                FT_Long*        args )
      {
        if ( args[0] == 0 && exc->args == 0 )
        {
          exc->error = FT_THROW( Bad_Argument );
          return;
        }
    
        exc->IP += args[0];
        if ( exc->IP < 0                                             ||
             ( exc->callTop > 0                                    &&
               exc->IP > exc->callStack[exc->callTop - 1].Def->end ) )
        {
          exc->error = FT_THROW( Bad_Argument );
          return;
        }
    
        exc->step_ins = FALSE;
    
        if ( args[0] < 0 )
        {
          if ( ++exc->neg_jump_counter > exc->neg_jump_counter_max )
            exc->error = FT_THROW( Execution_Too_Long );
        }
      }
    
    
      /**************************************************************************
       *
       * JROT[]:       Jump Relative On True
       * Opcode range: 0x78
       * Stack:        StkElt int32 -->
       */
      static void
      Ins_JROT( TT_ExecContext  exc,
                FT_Long*        args )
      {
        if ( args[1] != 0 )
          Ins_JMPR( exc, args );
      }
    
    
      /**************************************************************************
       *
       * JROF[]:       Jump Relative On False
       * Opcode range: 0x79
       * Stack:        StkElt int32 -->
       */
      static void
      Ins_JROF( TT_ExecContext  exc,
                FT_Long*        args )
      {
        if ( args[1] == 0 )
          Ins_JMPR( exc, args );
      }
    
    
      /**************************************************************************
       *
       * DEFINING AND USING FUNCTIONS AND INSTRUCTIONS
       *
       */
    
    
      /**************************************************************************
       *
       * FDEF[]:       Function DEFinition
       * Opcode range: 0x2C
       * Stack:        uint32 -->
       */
      static void
      Ins_FDEF( TT_ExecContext  exc,
                FT_Long*        args )
      {
        FT_ULong       n;
        TT_DefRecord*  rec;
        TT_DefRecord*  limit;
    
    #ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY
        /* arguments to opcodes are skipped by `SKIP_Code' */
        FT_Byte    opcode_pattern[9][12] = {
                     /* #0 inline delta function 1 */
                     {
                       0x4B, /* PPEM    */
                       0x53, /* GTEQ    */
                       0x23, /* SWAP    */
                       0x4B, /* PPEM    */
                       0x51, /* LTEQ    */
                       0x5A, /* AND     */
                       0x58, /* IF      */
                       0x38, /*   SHPIX */
                       0x1B, /* ELSE    */
                       0x21, /*   POP   */
                       0x21, /*   POP   */
                       0x59  /* EIF     */
                     },
                     /* #1 inline delta function 2 */
                     {
                       0x4B, /* PPEM    */
                       0x54, /* EQ      */
                       0x58, /* IF      */
                       0x38, /*   SHPIX */
                       0x1B, /* ELSE    */
                       0x21, /*   POP   */
                       0x21, /*   POP   */
                       0x59  /* EIF     */
                     },
                     /* #2 diagonal stroke function */
                     {
                       0x20, /* DUP     */
                       0x20, /* DUP     */
                       0xB0, /* PUSHB_1 */
                             /*   1     */
                       0x60, /* ADD     */
                       0x46, /* GC_cur  */
                       0xB0, /* PUSHB_1 */
                             /*   64    */
                       0x23, /* SWAP    */
                       0x42  /* WS      */
                     },
                     /* #3 VacuFormRound function */
                     {
                       0x45, /* RCVT    */
                       0x23, /* SWAP    */
                       0x46, /* GC_cur  */
                       0x60, /* ADD     */
                       0x20, /* DUP     */
                       0xB0  /* PUSHB_1 */
                             /*   38    */
                     },
                     /* #4 TTFautohint bytecode (old) */
                     {
                       0x20, /* DUP     */
                       0x64, /* ABS     */
                       0xB0, /* PUSHB_1 */
                             /*   32    */
                       0x60, /* ADD     */
                       0x66, /* FLOOR   */
                       0x23, /* SWAP    */
                       0xB0  /* PUSHB_1 */
                     },
                     /* #5 spacing function 1 */
                     {
                       0x01, /* SVTCA_x */
                       0xB0, /* PUSHB_1 */
                             /*   24    */
                       0x43, /* RS      */
                       0x58  /* IF      */
                     },
                     /* #6 spacing function 2 */
                     {
                       0x01, /* SVTCA_x */
                       0x18, /* RTG     */
                       0xB0, /* PUSHB_1 */
                             /*   24    */
                       0x43, /* RS      */
                       0x58  /* IF      */
                     },
                     /* #7 TypeMan Talk DiagEndCtrl function */
                     {
                       0x01, /* SVTCA_x */
                       0x20, /* DUP     */
                       0xB0, /* PUSHB_1 */
                             /*   3     */
                       0x25, /* CINDEX  */
                     },
                     /* #8 TypeMan Talk Align */
                     {
                       0x06, /* SPVTL   */
                       0x7D, /* RDTG    */
                     },
                   };
        FT_UShort  opcode_patterns   = 9;
        FT_UShort  opcode_pointer[9] = {  0, 0, 0, 0, 0, 0, 0, 0, 0 };
        FT_UShort  opcode_size[9]    = { 12, 8, 8, 6, 7, 4, 5, 4, 2 };
        FT_UShort  i;
    #endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */
    
    
        /* FDEF is only allowed in `prep' or `fpgm' */
        if ( exc->curRange == tt_coderange_glyph )
        {
          exc->error = FT_THROW( DEF_In_Glyf_Bytecode );
          return;
        }
    
        /* some font programs are broken enough to redefine functions! */
        /* We will then parse the current table.                       */
    
        rec   = exc->FDefs;
        limit = FT_OFFSET( rec, exc->numFDefs );
        n     = (FT_ULong)args[0];
    
        for ( ; rec < limit; rec++ )
        {
          if ( rec->opc == n )
            break;
        }
    
        if ( rec == limit )
        {
          /* check that there is enough room for new functions */
          if ( exc->numFDefs >= exc->maxFDefs )
          {
            exc->error = FT_THROW( Too_Many_Function_Defs );
            return;
          }
          exc->numFDefs++;
        }
    
        /* Although FDEF takes unsigned 32-bit integer,  */
        /* func # must be within unsigned 16-bit integer */
        if ( n > 0xFFFFU )
        {
          exc->error = FT_THROW( Too_Many_Function_Defs );
          return;
        }
    
        rec->range          = exc->curRange;
        rec->opc            = (FT_UInt16)n;
        rec->start          = exc->IP + 1;
        rec->active         = TRUE;
        rec->inline_delta   = FALSE;
        rec->sph_fdef_flags = 0x0000;
    
        if ( n > exc->maxFunc )
          exc->maxFunc = (FT_UInt16)n;
    
    #ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY
        /* We don't know for sure these are typeman functions, */
        /* however they are only active when RS 22 is called   */
        if ( n >= 64 && n <= 66 )
          rec->sph_fdef_flags |= SPH_FDEF_TYPEMAN_STROKES;
    #endif
    
        /* Now skip the whole function definition. */
        /* We don't allow nested IDEFS & FDEFs.    */
    
        while ( SkipCode( exc ) == SUCCESS )
        {
    
    #ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY
    
          if ( SUBPIXEL_HINTING_INFINALITY )
          {
            for ( i = 0; i < opcode_patterns; i++ )
            {
              if ( opcode_pointer[i] < opcode_size[i]                  &&
                   exc->opcode == opcode_pattern[i][opcode_pointer[i]] )
              {
                opcode_pointer[i] += 1;
    
                if ( opcode_pointer[i] == opcode_size[i] )
                {
                  FT_TRACE6(( "sph: Function %d, opcode ptrn: %d, %s %s\n",
                              i, n,
                              exc->face->root.family_name,
                              exc->face->root.style_name ));
    
                  switch ( i )
                  {
                  case 0:
                    rec->sph_fdef_flags             |= SPH_FDEF_INLINE_DELTA_1;
                    exc->face->sph_found_func_flags |= SPH_FDEF_INLINE_DELTA_1;
                    break;
    
                  case 1:
                    rec->sph_fdef_flags             |= SPH_FDEF_INLINE_DELTA_2;
                    exc->face->sph_found_func_flags |= SPH_FDEF_INLINE_DELTA_2;
                    break;
    
                  case 2:
                    switch ( n )
                    {
                      /* needs to be implemented still */
                    case 58:
                      rec->sph_fdef_flags             |= SPH_FDEF_DIAGONAL_STROKE;
                      exc->face->sph_found_func_flags |= SPH_FDEF_DIAGONAL_STROKE;
                    }
                    break;
    
                  case 3:
                    switch ( n )
                    {
                    case 0:
                      rec->sph_fdef_flags             |= SPH_FDEF_VACUFORM_ROUND_1;
                      exc->face->sph_found_func_flags |= SPH_FDEF_VACUFORM_ROUND_1;
                    }
                    break;
    
                  case 4:
                    /* probably not necessary to detect anymore */
                    rec->sph_fdef_flags             |= SPH_FDEF_TTFAUTOHINT_1;
                    exc->face->sph_found_func_flags |= SPH_FDEF_TTFAUTOHINT_1;
                    break;
    
                  case 5:
                    switch ( n )
                    {
                    case 0:
                    case 1:
                    case 2:
                    case 4:
                    case 7:
                    case 8:
                      rec->sph_fdef_flags             |= SPH_FDEF_SPACING_1;
                      exc->face->sph_found_func_flags |= SPH_FDEF_SPACING_1;
                    }
                    break;
    
                  case 6:
                    switch ( n )
                    {
                    case 0:
                    case 1:
                    case 2:
                    case 4:
                    case 7:
                    case 8:
                      rec->sph_fdef_flags             |= SPH_FDEF_SPACING_2;
                      exc->face->sph_found_func_flags |= SPH_FDEF_SPACING_2;
                    }
                    break;
    
                   case 7:
                     rec->sph_fdef_flags             |= SPH_FDEF_TYPEMAN_DIAGENDCTRL;
                     exc->face->sph_found_func_flags |= SPH_FDEF_TYPEMAN_DIAGENDCTRL;
                     break;
    
                   case 8:
    #if 0
                     rec->sph_fdef_flags             |= SPH_FDEF_TYPEMAN_DIAGENDCTRL;
                     exc->face->sph_found_func_flags |= SPH_FDEF_TYPEMAN_DIAGENDCTRL;
    #endif
                     break;
                  }
                  opcode_pointer[i] = 0;
                }
              }
    
              else
                opcode_pointer[i] = 0;
            }
    
            /* Set sph_compatibility_mode only when deltas are detected */
            exc->face->sph_compatibility_mode =
              ( ( exc->face->sph_found_func_flags & SPH_FDEF_INLINE_DELTA_1 ) |
                ( exc->face->sph_found_func_flags & SPH_FDEF_INLINE_DELTA_2 ) );
          }
    
    #endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */
    
          switch ( exc->opcode )
          {
          case 0x89:    /* IDEF */
          case 0x2C:    /* FDEF */
            exc->error = FT_THROW( Nested_DEFS );
            return;
    
          case 0x2D:   /* ENDF */
            rec->end = exc->IP;
            return;
          }
        }
      }
    
    
      /**************************************************************************
       *
       * ENDF[]:       END Function definition
       * Opcode range: 0x2D
       * Stack:        -->
       */
      static void
      Ins_ENDF( TT_ExecContext  exc )
      {
        TT_CallRec*  pRec;
    
    
    #ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY
        exc->sph_in_func_flags = 0x0000;
    #endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */
    
        if ( exc->callTop <= 0 )     /* We encountered an ENDF without a call */
        {
          exc->error = FT_THROW( ENDF_In_Exec_Stream );
          return;
        }
    
        exc->callTop--;
    
        pRec = &exc->callStack[exc->callTop];
    
        pRec->Cur_Count--;
    
        exc->step_ins = FALSE;
    
        if ( pRec->Cur_Count > 0 )
        {
          exc->callTop++;
          exc->IP = pRec->Def->start;
        }
        else
          /* Loop through the current function */
          Ins_Goto_CodeRange( exc, pRec->Caller_Range, pRec->Caller_IP );
    
        /* Exit the current call frame.                      */
    
        /* NOTE: If the last instruction 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 is why we do not test */
        /*       the result of Ins_Goto_CodeRange() here!    */
      }
    
    
      /**************************************************************************
       *
       * CALL[]:       CALL function
       * Opcode range: 0x2B
       * Stack:        uint32? -->
       */
      static void
      Ins_CALL( TT_ExecContext  exc,
                FT_Long*        args )
      {
        FT_ULong       F;
        TT_CallRec*    pCrec;
        TT_DefRecord*  def;
    
    
        /* first of all, check the index */
    
        F = (FT_ULong)args[0];
        if ( BOUNDSL( F, exc->maxFunc + 1 ) )
          goto Fail;
    
        if ( !exc->FDefs )
          goto Fail;
    
        /* Except for some old Apple fonts, all functions in a TrueType */
        /* font are defined in increasing order, starting from 0.  This */
        /* means that we normally have                                  */
        /*                                                              */
        /*    exc->maxFunc+1 == exc->numFDefs                           */
        /*    exc->FDefs[n].opc == n for n in 0..exc->maxFunc           */
        /*                                                              */
        /* If this isn't true, we need to look up the function table.   */
    
        def = exc->FDefs + F;
        if ( exc->maxFunc + 1 != exc->numFDefs || def->opc != F )
        {
          /* look up the FDefs table */
          TT_DefRecord*  limit;
    
    
          def   = exc->FDefs;
          limit = def + exc->numFDefs;
    
          while ( def < limit && def->opc != F )
            def++;
    
          if ( def == limit )
            goto Fail;
        }
    
        /* check that the function is active */
        if ( !def->active )
          goto Fail;
    
    #ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY
        if ( SUBPIXEL_HINTING_INFINALITY                                    &&
             exc->ignore_x_mode                                             &&
             ( ( exc->iup_called                                        &&
                 ( exc->sph_tweak_flags & SPH_TWEAK_NO_CALL_AFTER_IUP ) ) ||
               ( def->sph_fdef_flags & SPH_FDEF_VACUFORM_ROUND_1 )        ) )
          goto Fail;
        else
          exc->sph_in_func_flags = def->sph_fdef_flags;
    #endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */
    
        /* check the call stack */
        if ( exc->callTop >= exc->callSize )
        {
          exc->error = FT_THROW( Stack_Overflow );
          return;
        }
    
        pCrec = exc->callStack + exc->callTop;
    
        pCrec->Caller_Range = exc->curRange;
        pCrec->Caller_IP    = exc->IP + 1;
        pCrec->Cur_Count    = 1;
        pCrec->Def          = def;
    
        exc->callTop++;
    
        Ins_Goto_CodeRange( exc, def->range, def->start );
    
        exc->step_ins = FALSE;
    
        return;
    
      Fail:
        exc->error = FT_THROW( Invalid_Reference );
      }
    
    
      /**************************************************************************
       *
       * LOOPCALL[]:   LOOP and CALL function
       * Opcode range: 0x2A
       * Stack:        uint32? Eint16? -->
       */
      static void
      Ins_LOOPCALL( TT_ExecContext  exc,
                    FT_Long*        args )
      {
        FT_ULong       F;
        TT_CallRec*    pCrec;
        TT_DefRecord*  def;
    
    
        /* first of all, check the index */
        F = (FT_ULong)args[1];
        if ( BOUNDSL( F, exc->maxFunc + 1 ) )
          goto Fail;
    
        /* Except for some old Apple fonts, all functions in a TrueType */
        /* font are defined in increasing order, starting from 0.  This */
        /* means that we normally have                                  */
        /*                                                              */
        /*    exc->maxFunc+1 == exc->numFDefs                           */
        /*    exc->FDefs[n].opc == n for n in 0..exc->maxFunc           */
        /*                                                              */
        /* If this isn't true, we need to look up the function table.   */
    
        def = FT_OFFSET( exc->FDefs, F );
        if ( exc->maxFunc + 1 != exc->numFDefs || def->opc != F )
        {
          /* look up the FDefs table */
          TT_DefRecord*  limit;
    
    
          def   = exc->FDefs;
          limit = FT_OFFSET( def, exc->numFDefs );
    
          while ( def < limit && def->opc != F )
            def++;
    
          if ( def == limit )
            goto Fail;
        }
    
        /* check that the function is active */
        if ( !def->active )
          goto Fail;
    
    #ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY
        if ( SUBPIXEL_HINTING_INFINALITY                         &&
             exc->ignore_x_mode                                  &&
             ( def->sph_fdef_flags & SPH_FDEF_VACUFORM_ROUND_1 ) )
          goto Fail;
        else
          exc->sph_in_func_flags = def->sph_fdef_flags;
    #endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */
    
        /* check stack */
        if ( exc->callTop >= exc->callSize )
        {
          exc->error = FT_THROW( Stack_Overflow );
          return;
        }
    
        if ( args[0] > 0 )
        {
          pCrec = exc->callStack + exc->callTop;
    
          pCrec->Caller_Range = exc->curRange;
          pCrec->Caller_IP    = exc->IP + 1;
          pCrec->Cur_Count    = (FT_Int)args[0];
          pCrec->Def          = def;
    
          exc->callTop++;
    
          Ins_Goto_CodeRange( exc, def->range, def->start );
    
          exc->step_ins = FALSE;
    
          exc->loopcall_counter += (FT_ULong)args[0];
          if ( exc->loopcall_counter > exc->loopcall_counter_max )
            exc->error = FT_THROW( Execution_Too_Long );
        }
    
        return;
    
      Fail:
        exc->error = FT_THROW( Invalid_Reference );
      }
    
    
      /**************************************************************************
       *
       * IDEF[]:       Instruction DEFinition
       * Opcode range: 0x89
       * Stack:        Eint8 -->
       */
      static void
      Ins_IDEF( TT_ExecContext  exc,
                FT_Long*        args )
      {
        TT_DefRecord*  def;
        TT_DefRecord*  limit;
    
    
        /* we enable IDEF only in `prep' or `fpgm' */
        if ( exc->curRange == tt_coderange_glyph )
        {
          exc->error = FT_THROW( DEF_In_Glyf_Bytecode );
          return;
        }
    
        /*  First of all, look for the same function in our table */
    
        def   = exc->IDefs;
        limit = FT_OFFSET( def, exc->numIDefs );
    
        for ( ; def < limit; def++ )
          if ( def->opc == (FT_ULong)args[0] )
            break;
    
        if ( def == limit )
        {
          /* check that there is enough room for a new instruction */
          if ( exc->numIDefs >= exc->maxIDefs )
          {
            exc->error = FT_THROW( Too_Many_Instruction_Defs );
            return;
          }
          exc->numIDefs++;
        }
    
        /* opcode must be unsigned 8-bit integer */
        if ( 0 > args[0] || args[0] > 0x00FF )
        {
          exc->error = FT_THROW( Too_Many_Instruction_Defs );
          return;
        }
    
        def->opc    = (FT_Byte)args[0];
        def->start  = exc->IP + 1;
        def->range  = exc->curRange;
        def->active = TRUE;
    
        if ( (FT_ULong)args[0] > exc->maxIns )
          exc->maxIns = (FT_Byte)args[0];
    
        /* Now skip the whole function definition. */
        /* We don't allow nested IDEFs & FDEFs.    */
    
        while ( SkipCode( exc ) == SUCCESS )
        {
          switch ( exc->opcode )
          {
          case 0x89:   /* IDEF */
          case 0x2C:   /* FDEF */
            exc->error = FT_THROW( Nested_DEFS );
            return;
          case 0x2D:   /* ENDF */
            def->end = exc->IP;
            return;
          }
        }
      }
    
    
      /**************************************************************************
       *
       * PUSHING DATA ONTO THE INTERPRETER STACK
       *
       */
    
    
      /**************************************************************************
       *
       * NPUSHB[]:     PUSH N Bytes
       * Opcode range: 0x40
       * Stack:        --> uint32...
       */
      static void
      Ins_NPUSHB( TT_ExecContext  exc,
                  FT_Long*        args )
      {
        FT_UShort  L, K;
    
    
        L = (FT_UShort)exc->code[exc->IP + 1];
    
        if ( BOUNDS( L, exc->stackSize + 1 - exc->top ) )
        {
          exc->error = FT_THROW( Stack_Overflow );
          return;
        }
    
        for ( K = 1; K <= L; K++ )
          args[K - 1] = exc->code[exc->IP + K + 1];
    
        exc->new_top += L;
      }
    
    
      /**************************************************************************
       *
       * NPUSHW[]:     PUSH N Words
       * Opcode range: 0x41
       * Stack:        --> int32...
       */
      static void
      Ins_NPUSHW( TT_ExecContext  exc,
                  FT_Long*        args )
      {
        FT_UShort  L, K;
    
    
        L = (FT_UShort)exc->code[exc->IP + 1];
    
        if ( BOUNDS( L, exc->stackSize + 1 - exc->top ) )
        {
          exc->error = FT_THROW( Stack_Overflow );
          return;
        }
    
        exc->IP += 2;
    
        for ( K = 0; K < L; K++ )
          args[K] = GetShortIns( exc );
    
        exc->step_ins = FALSE;
        exc->new_top += L;
      }
    
    
      /**************************************************************************
       *
       * PUSHB[abc]:   PUSH Bytes
       * Opcode range: 0xB0-0xB7
       * Stack:        --> uint32...
       */
      static void
      Ins_PUSHB( TT_ExecContext  exc,
                 FT_Long*        args )
      {
        FT_UShort  L, K;
    
    
        L = (FT_UShort)( exc->opcode - 0xB0 + 1 );
    
        if ( BOUNDS( L, exc->stackSize + 1 - exc->top ) )
        {
          exc->error = FT_THROW( Stack_Overflow );
          return;
        }
    
        for ( K = 1; K <= L; K++ )
          args[K - 1] = exc->code[exc->IP + K];
      }
    
    
      /**************************************************************************
       *
       * PUSHW[abc]:   PUSH Words
       * Opcode range: 0xB8-0xBF
       * Stack:        --> int32...
       */
      static void
      Ins_PUSHW( TT_ExecContext  exc,
                 FT_Long*        args )
      {
        FT_UShort  L, K;
    
    
        L = (FT_UShort)( exc->opcode - 0xB8 + 1 );
    
        if ( BOUNDS( L, exc->stackSize + 1 - exc->top ) )
        {
          exc->error = FT_THROW( Stack_Overflow );
          return;
        }
    
        exc->IP++;
    
        for ( K = 0; K < L; K++ )
          args[K] = GetShortIns( exc );
    
        exc->step_ins = FALSE;
      }
    
    
      /**************************************************************************
       *
       * MANAGING THE GRAPHICS STATE
       *
       */
    
    
      static FT_Bool
      Ins_SxVTL( TT_ExecContext  exc,
                 FT_UShort       aIdx1,
                 FT_UShort       aIdx2,
                 FT_UnitVector*  Vec )
      {
        FT_Long     A, B, C;
        FT_Vector*  p1;
        FT_Vector*  p2;
    
        FT_Byte  opcode = exc->opcode;
    
    
        if ( BOUNDS( aIdx1, exc->zp2.n_points ) ||
             BOUNDS( aIdx2, exc->zp1.n_points ) )
        {
          if ( exc->pedantic_hinting )
            exc->error = FT_THROW( Invalid_Reference );
          return FAILURE;
        }
    
        p1 = exc->zp1.cur + aIdx2;
        p2 = exc->zp2.cur + aIdx1;
    
        A = SUB_LONG( p1->x, p2->x );
        B = SUB_LONG( p1->y, p2->y );
    
        /* If p1 == p2, SPvTL and SFvTL behave the same as */
        /* SPvTCA[X] and SFvTCA[X], respectively.          */
        /*                                                 */
        /* Confirmed by Greg Hitchcock.                    */
    
        if ( A == 0 && B == 0 )
        {
          A      = 0x4000;
          opcode = 0;
        }
    
        if ( ( opcode & 1 ) != 0 )
        {
          C = B;   /* counter clockwise rotation */
          B = A;
          A = NEG_LONG( C );
        }
    
        Normalize( A, B, Vec );
    
        return SUCCESS;
      }
    
    
      /**************************************************************************
       *
       * SVTCA[a]:     Set (F and P) Vectors to Coordinate Axis
       * Opcode range: 0x00-0x01
       * Stack:        -->
       *
       * SPvTCA[a]:    Set PVector to Coordinate Axis
       * Opcode range: 0x02-0x03
       * Stack:        -->
       *
       * SFvTCA[a]:    Set FVector to Coordinate Axis
       * Opcode range: 0x04-0x05
       * Stack:        -->
       */
      static void
      Ins_SxyTCA( TT_ExecContext  exc )
      {
        FT_Short  AA, BB;
    
        FT_Byte  opcode = exc->opcode;
    
    
        AA = (FT_Short)( ( opcode & 1 ) << 14 );
        BB = (FT_Short)( AA ^ 0x4000 );
    
        if ( opcode < 4 )
        {
          exc->GS.projVector.x = AA;
          exc->GS.projVector.y = BB;
    
          exc->GS.dualVector.x = AA;
          exc->GS.dualVector.y = BB;
        }
    
        if ( ( opcode & 2 ) == 0 )
        {
          exc->GS.freeVector.x = AA;
          exc->GS.freeVector.y = BB;
        }
    
        Compute_Funcs( exc );
      }
    
    
      /**************************************************************************
       *
       * SPvTL[a]:     Set PVector To Line
       * Opcode range: 0x06-0x07
       * Stack:        uint32 uint32 -->
       */
      static void
      Ins_SPVTL( TT_ExecContext  exc,
                 FT_Long*        args )
      {
        if ( Ins_SxVTL( exc,
                        (FT_UShort)args[1],
                        (FT_UShort)args[0],
                        &exc->GS.projVector ) == SUCCESS )
        {
          exc->GS.dualVector = exc->GS.projVector;
          Compute_Funcs( exc );
        }
      }
    
    
      /**************************************************************************
       *
       * SFvTL[a]:     Set FVector To Line
       * Opcode range: 0x08-0x09
       * Stack:        uint32 uint32 -->
       */
      static void
      Ins_SFVTL( TT_ExecContext  exc,
                 FT_Long*        args )
      {
        if ( Ins_SxVTL( exc,
                        (FT_UShort)args[1],
                        (FT_UShort)args[0],
                        &exc->GS.freeVector ) == SUCCESS )
        {
          Compute_Funcs( exc );
        }
      }
    
    
      /**************************************************************************
       *
       * SFvTPv[]:     Set FVector To PVector
       * Opcode range: 0x0E
       * Stack:        -->
       */
      static void
      Ins_SFVTPV( TT_ExecContext  exc )
      {
        exc->GS.freeVector = exc->GS.projVector;
        Compute_Funcs( exc );
      }
    
    
      /**************************************************************************
       *
       * SPvFS[]:      Set PVector From Stack
       * Opcode range: 0x0A
       * Stack:        f2.14 f2.14 -->
       */
      static void
      Ins_SPVFS( TT_ExecContext  exc,
                 FT_Long*        args )
      {
        FT_Short  S;
        FT_Long   X, Y;
    
    
        /* Only use low 16bits, then sign extend */
        S = (FT_Short)args[1];
        Y = (FT_Long)S;
        S = (FT_Short)args[0];
        X = (FT_Long)S;
    
        Normalize( X, Y, &exc->GS.projVector );
    
        exc->GS.dualVector = exc->GS.projVector;
        Compute_Funcs( exc );
      }
    
    
      /**************************************************************************
       *
       * SFvFS[]:      Set FVector From Stack
       * Opcode range: 0x0B
       * Stack:        f2.14 f2.14 -->
       */
      static void
      Ins_SFVFS( TT_ExecContext  exc,
                 FT_Long*        args )
      {
        FT_Short  S;
        FT_Long   X, Y;
    
    
        /* Only use low 16bits, then sign extend */
        S = (FT_Short)args[1];
        Y = (FT_Long)S;
        S = (FT_Short)args[0];
        X = S;
    
        Normalize( X, Y, &exc->GS.freeVector );
        Compute_Funcs( exc );
      }
    
    
      /**************************************************************************
       *
       * GPv[]:        Get Projection Vector
       * Opcode range: 0x0C
       * Stack:        ef2.14 --> ef2.14
       */
      static void
      Ins_GPV( TT_ExecContext  exc,
               FT_Long*        args )
      {
        args[0] = exc->GS.projVector.x;
        args[1] = exc->GS.projVector.y;
      }
    
    
      /**************************************************************************
       *
       * GFv[]:        Get Freedom Vector
       * Opcode range: 0x0D
       * Stack:        ef2.14 --> ef2.14
       */
      static void
      Ins_GFV( TT_ExecContext  exc,
               FT_Long*        args )
      {
        args[0] = exc->GS.freeVector.x;
        args[1] = exc->GS.freeVector.y;
      }
    
    
      /**************************************************************************
       *
       * SRP0[]:       Set Reference Point 0
       * Opcode range: 0x10
       * Stack:        uint32 -->
       */
      static void
      Ins_SRP0( TT_ExecContext  exc,
                FT_Long*        args )
      {
        exc->GS.rp0 = (FT_UShort)args[0];
      }
    
    
      /**************************************************************************
       *
       * SRP1[]:       Set Reference Point 1
       * Opcode range: 0x11
       * Stack:        uint32 -->
       */
      static void
      Ins_SRP1( TT_ExecContext  exc,
                FT_Long*        args )
      {
        exc->GS.rp1 = (FT_UShort)args[0];
      }
    
    
      /**************************************************************************
       *
       * SRP2[]:       Set Reference Point 2
       * Opcode range: 0x12
       * Stack:        uint32 -->
       */
      static void
      Ins_SRP2( TT_ExecContext  exc,
                FT_Long*        args )
      {
        exc->GS.rp2 = (FT_UShort)args[0];
      }
    
    
      /**************************************************************************
       *
       * SMD[]:        Set Minimum Distance
       * Opcode range: 0x1A
       * Stack:        f26.6 -->
       */
      static void
      Ins_SMD( TT_ExecContext  exc,
               FT_Long*        args )
      {
        exc->GS.minimum_distance = args[0];
      }
    
    
      /**************************************************************************
       *
       * SCVTCI[]:     Set Control Value Table Cut In
       * Opcode range: 0x1D
       * Stack:        f26.6 -->
       */
      static void
      Ins_SCVTCI( TT_ExecContext  exc,
                  FT_Long*        args )
      {
        exc->GS.control_value_cutin = (FT_F26Dot6)args[0];
      }
    
    
      /**************************************************************************
       *
       * SSWCI[]:      Set Single Width Cut In
       * Opcode range: 0x1E
       * Stack:        f26.6 -->
       */
      static void
      Ins_SSWCI( TT_ExecContext  exc,
                 FT_Long*        args )
      {
        exc->GS.single_width_cutin = (FT_F26Dot6)args[0];
      }
    
    
      /**************************************************************************
       *
       * SSW[]:        Set Single Width
       * Opcode range: 0x1F
       * Stack:        int32? -->
       */
      static void
      Ins_SSW( TT_ExecContext  exc,
               FT_Long*        args )
      {
        exc->GS.single_width_value = FT_MulFix( args[0],
                                                exc->tt_metrics.scale );
      }
    
    
      /**************************************************************************
       *
       * FLIPON[]:     Set auto-FLIP to ON
       * Opcode range: 0x4D
       * Stack:        -->
       */
      static void
      Ins_FLIPON( TT_ExecContext  exc )
      {
        exc->GS.auto_flip = TRUE;
      }
    
    
      /**************************************************************************
       *
       * FLIPOFF[]:    Set auto-FLIP to OFF
       * Opcode range: 0x4E
       * Stack:        -->
       */
      static void
      Ins_FLIPOFF( TT_ExecContext  exc )
      {
        exc->GS.auto_flip = FALSE;
      }
    
    
      /**************************************************************************
       *
       * SANGW[]:      Set ANGle Weight
       * Opcode range: 0x7E
       * Stack:        uint32 -->
       */
      static void
      Ins_SANGW( void )
      {
        /* instruction not supported anymore */
      }
    
    
      /**************************************************************************
       *
       * SDB[]:        Set Delta Base
       * Opcode range: 0x5E
       * Stack:        uint32 -->
       */
      static void
      Ins_SDB( TT_ExecContext  exc,
               FT_Long*        args )
      {
        exc->GS.delta_base = (FT_UShort)args[0];
      }
    
    
      /**************************************************************************
       *
       * SDS[]:        Set Delta Shift
       * Opcode range: 0x5F
       * Stack:        uint32 -->
       */
      static void
      Ins_SDS( TT_ExecContext  exc,
               FT_Long*        args )
      {
        if ( (FT_ULong)args[0] > 6UL )
          exc->error = FT_THROW( Bad_Argument );
        else
          exc->GS.delta_shift = (FT_UShort)args[0];
      }
    
    
      /**************************************************************************
       *
       * RTHG[]:       Round To Half Grid
       * Opcode range: 0x19
       * Stack:        -->
       */
      static void
      Ins_RTHG( TT_ExecContext  exc )
      {
        exc->GS.round_state = TT_Round_To_Half_Grid;
        exc->func_round     = (TT_Round_Func)Round_To_Half_Grid;
      }
    
    
      /**************************************************************************
       *
       * RTG[]:        Round To Grid
       * Opcode range: 0x18
       * Stack:        -->
       */
      static void
      Ins_RTG( TT_ExecContext  exc )
      {
        exc->GS.round_state = TT_Round_To_Grid;
        exc->func_round     = (TT_Round_Func)Round_To_Grid;
      }
    
    
      /**************************************************************************
       * RTDG[]:       Round To Double Grid
       * Opcode range: 0x3D
       * Stack:        -->
       */
      static void
      Ins_RTDG( TT_ExecContext  exc )
      {
        exc->GS.round_state = TT_Round_To_Double_Grid;
        exc->func_round     = (TT_Round_Func)Round_To_Double_Grid;
      }
    
    
      /**************************************************************************
       * RUTG[]:       Round Up To Grid
       * Opcode range: 0x7C
       * Stack:        -->
       */
      static void
      Ins_RUTG( TT_ExecContext  exc )
      {
        exc->GS.round_state = TT_Round_Up_To_Grid;
        exc->func_round     = (TT_Round_Func)Round_Up_To_Grid;
      }
    
    
      /**************************************************************************
       *
       * RDTG[]:       Round Down To Grid
       * Opcode range: 0x7D
       * Stack:        -->
       */
      static void
      Ins_RDTG( TT_ExecContext  exc )
      {
        exc->GS.round_state = TT_Round_Down_To_Grid;
        exc->func_round     = (TT_Round_Func)Round_Down_To_Grid;
      }
    
    
      /**************************************************************************
       *
       * ROFF[]:       Round OFF
       * Opcode range: 0x7A
       * Stack:        -->
       */
      static void
      Ins_ROFF( TT_ExecContext  exc )
      {
        exc->GS.round_state = TT_Round_Off;
        exc->func_round     = (TT_Round_Func)Round_None;
      }
    
    
      /**************************************************************************
       *
       * SROUND[]:     Super ROUND
       * Opcode range: 0x76
       * Stack:        Eint8 -->
       */
      static void
      Ins_SROUND( TT_ExecContext  exc,
                  FT_Long*        args )
      {
        SetSuperRound( exc, 0x4000, args[0] );
    
        exc->GS.round_state = TT_Round_Super;
        exc->func_round     = (TT_Round_Func)Round_Super;
      }
    
    
      /**************************************************************************
       *
       * S45ROUND[]:   Super ROUND 45 degrees
       * Opcode range: 0x77
       * Stack:        uint32 -->
       */
      static void
      Ins_S45ROUND( TT_ExecContext  exc,
                    FT_Long*        args )
      {
        SetSuperRound( exc, 0x2D41, args[0] );
    
        exc->GS.round_state = TT_Round_Super_45;
        exc->func_round     = (TT_Round_Func)Round_Super_45;
      }
    
    
      /**************************************************************************
       *
       * GC[a]:        Get Coordinate projected onto
       * Opcode range: 0x46-0x47
       * Stack:        uint32 --> f26.6
       *
       * XXX: UNDOCUMENTED: Measures from the original glyph must be taken
       *      along the dual projection vector!
       */
      static void
      Ins_GC( TT_ExecContext  exc,
              FT_Long*        args )
      {
        FT_ULong    L;
        FT_F26Dot6  R;
    
    
        L = (FT_ULong)args[0];
    
        if ( BOUNDSL( L, exc->zp2.n_points ) )
        {
          if ( exc->pedantic_hinting )
            exc->error = FT_THROW( Invalid_Reference );
          R = 0;
        }
        else
        {
          if ( exc->opcode & 1 )
            R = FAST_DUALPROJ( &exc->zp2.org[L] );
          else
            R = FAST_PROJECT( &exc->zp2.cur[L] );
        }
    
        args[0] = R;
      }
    
    
      /**************************************************************************
       *
       * SCFS[]:       Set Coordinate From Stack
       * Opcode range: 0x48
       * Stack:        f26.6 uint32 -->
       *
       * Formula:
       *
       *   OA := OA + ( value - OA.p )/( f.p ) * f
       */
      static void
      Ins_SCFS( TT_ExecContext  exc,
                FT_Long*        args )
      {
        FT_Long    K;
        FT_UShort  L;
    
    
        L = (FT_UShort)args[0];
    
        if ( BOUNDS( L, exc->zp2.n_points ) )
        {
          if ( exc->pedantic_hinting )
            exc->error = FT_THROW( Invalid_Reference );
          return;
        }
    
        K = FAST_PROJECT( &exc->zp2.cur[L] );
    
        exc->func_move( exc, &exc->zp2, L, SUB_LONG( args[1], K ) );
    
        /* UNDOCUMENTED!  The MS rasterizer does that with */
        /* twilight points (confirmed by Greg Hitchcock)   */
        if ( exc->GS.gep2 == 0 )
          exc->zp2.org[L] = exc->zp2.cur[L];
      }
    
    
      /**************************************************************************
       *
       * MD[a]:        Measure Distance
       * Opcode range: 0x49-0x4A
       * Stack:        uint32 uint32 --> f26.6
       *
       * XXX: UNDOCUMENTED: Measure taken in the original glyph must be along
       *                    the dual projection vector.
       *
       * XXX: UNDOCUMENTED: Flag attributes are inverted!
       *                      0 => measure distance in original outline
       *                      1 => measure distance in grid-fitted outline
       *
       * XXX: UNDOCUMENTED: `zp0 - zp1', and not `zp2 - zp1!
       */
      static void
      Ins_MD( TT_ExecContext  exc,
              FT_Long*        args )
      {
        FT_UShort   K, L;
        FT_F26Dot6  D;
    
    
        K = (FT_UShort)args[1];
        L = (FT_UShort)args[0];
    
        if ( BOUNDS( L, exc->zp0.n_points ) ||
             BOUNDS( K, exc->zp1.n_points ) )
        {
          if ( exc->pedantic_hinting )
            exc->error = FT_THROW( Invalid_Reference );
          D = 0;
        }
        else
        {
          if ( exc->opcode & 1 )
            D = PROJECT( exc->zp0.cur + L, exc->zp1.cur + K );
          else
          {
            /* XXX: UNDOCUMENTED: twilight zone special case */
    
            if ( exc->GS.gep0 == 0 || exc->GS.gep1 == 0 )
            {
              FT_Vector*  vec1 = exc->zp0.org + L;
              FT_Vector*  vec2 = exc->zp1.org + K;
    
    
              D = DUALPROJ( vec1, vec2 );
            }
            else
            {
              FT_Vector*  vec1 = exc->zp0.orus + L;
              FT_Vector*  vec2 = exc->zp1.orus + K;
    
    
              if ( exc->metrics.x_scale == exc->metrics.y_scale )
              {
                /* this should be faster */
                D = DUALPROJ( vec1, vec2 );
                D = FT_MulFix( D, exc->metrics.x_scale );
              }
              else
              {
                FT_Vector  vec;
    
    
                vec.x = FT_MulFix( vec1->x - vec2->x, exc->metrics.x_scale );
                vec.y = FT_MulFix( vec1->y - vec2->y, exc->metrics.y_scale );
    
                D = FAST_DUALPROJ( &vec );
              }
            }
          }
        }
    
    #ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY
        /* Disable Type 2 Vacuform Rounds - e.g. Arial Narrow */
        if ( SUBPIXEL_HINTING_INFINALITY &&
             exc->ignore_x_mode          &&
             FT_ABS( D ) == 64           )
          D += 1;
    #endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */
    
        args[0] = D;
      }
    
    
      /**************************************************************************
       *
       * SDPvTL[a]:    Set Dual PVector to Line
       * Opcode range: 0x86-0x87
       * Stack:        uint32 uint32 -->
       */
      static void
      Ins_SDPVTL( TT_ExecContext  exc,
                  FT_Long*        args )
      {
        FT_Long    A, B, C;
        FT_UShort  p1, p2;            /* was FT_Int in pas type ERROR */
    
        FT_Byte  opcode = exc->opcode;
    
    
        p1 = (FT_UShort)args[1];
        p2 = (FT_UShort)args[0];
    
        if ( BOUNDS( p2, exc->zp1.n_points ) ||
             BOUNDS( p1, exc->zp2.n_points ) )
        {
          if ( exc->pedantic_hinting )
            exc->error = FT_THROW( Invalid_Reference );
          return;
        }
    
        {
          FT_Vector*  v1 = exc->zp1.org + p2;
          FT_Vector*  v2 = exc->zp2.org + p1;
    
    
          A = SUB_LONG( v1->x, v2->x );
          B = SUB_LONG( v1->y, v2->y );
    
          /* If v1 == v2, SDPvTL behaves the same as */
          /* SVTCA[X], respectively.                 */
          /*                                         */
          /* Confirmed by Greg Hitchcock.            */
    
          if ( A == 0 && B == 0 )
          {
            A      = 0x4000;
            opcode = 0;
          }
        }
    
        if ( ( opcode & 1 ) != 0 )
        {
          C = B;   /* counter clockwise rotation */
          B = A;
          A = NEG_LONG( C );
        }
    
        Normalize( A, B, &exc->GS.dualVector );
    
        {
          FT_Vector*  v1 = exc->zp1.cur + p2;
          FT_Vector*  v2 = exc->zp2.cur + p1;
    
    
          A = SUB_LONG( v1->x, v2->x );
          B = SUB_LONG( v1->y, v2->y );
    
          if ( A == 0 && B == 0 )
          {
            A      = 0x4000;
            opcode = 0;
          }
        }
    
        if ( ( opcode & 1 ) != 0 )
        {
          C = B;   /* counter clockwise rotation */
          B = A;
          A = NEG_LONG( C );
        }
    
        Normalize( A, B, &exc->GS.projVector );
        Compute_Funcs( exc );
      }
    
    
      /**************************************************************************
       *
       * SZP0[]:       Set Zone Pointer 0
       * Opcode range: 0x13
       * Stack:        uint32 -->
       */
      static void
      Ins_SZP0( TT_ExecContext  exc,
                FT_Long*        args )
      {
        switch ( (FT_Int)args[0] )
        {
        case 0:
          exc->zp0 = exc->twilight;
          break;
    
        case 1:
          exc->zp0 = exc->pts;
          break;
    
        default:
          if ( exc->pedantic_hinting )
            exc->error = FT_THROW( Invalid_Reference );
          return;
        }
    
        exc->GS.gep0 = (FT_UShort)args[0];
      }
    
    
      /**************************************************************************
       *
       * SZP1[]:       Set Zone Pointer 1
       * Opcode range: 0x14
       * Stack:        uint32 -->
       */
      static void
      Ins_SZP1( TT_ExecContext  exc,
                FT_Long*        args )
      {
        switch ( (FT_Int)args[0] )
        {
        case 0:
          exc->zp1 = exc->twilight;
          break;
    
        case 1:
          exc->zp1 = exc->pts;
          break;
    
        default:
          if ( exc->pedantic_hinting )
            exc->error = FT_THROW( Invalid_Reference );
          return;
        }
    
        exc->GS.gep1 = (FT_UShort)args[0];
      }
    
    
      /**************************************************************************
       *
       * SZP2[]:       Set Zone Pointer 2
       * Opcode range: 0x15
       * Stack:        uint32 -->
       */
      static void
      Ins_SZP2( TT_ExecContext  exc,
                FT_Long*        args )
      {
        switch ( (FT_Int)args[0] )
        {
        case 0:
          exc->zp2 = exc->twilight;
          break;
    
        case 1:
          exc->zp2 = exc->pts;
          break;
    
        default:
          if ( exc->pedantic_hinting )
            exc->error = FT_THROW( Invalid_Reference );
          return;
        }
    
        exc->GS.gep2 = (FT_UShort)args[0];
      }
    
    
      /**************************************************************************
       *
       * SZPS[]:       Set Zone PointerS
       * Opcode range: 0x16
       * Stack:        uint32 -->
       */
      static void
      Ins_SZPS( TT_ExecContext  exc,
                FT_Long*        args )
      {
        switch ( (FT_Int)args[0] )
        {
        case 0:
          exc->zp0 = exc->twilight;
          break;
    
        case 1:
          exc->zp0 = exc->pts;
          break;
    
        default:
          if ( exc->pedantic_hinting )
            exc->error = FT_THROW( Invalid_Reference );
          return;
        }
    
        exc->zp1 = exc->zp0;
        exc->zp2 = exc->zp0;
    
        exc->GS.gep0 = (FT_UShort)args[0];
        exc->GS.gep1 = (FT_UShort)args[0];
        exc->GS.gep2 = (FT_UShort)args[0];
      }
    
    
      /**************************************************************************
       *
       * INSTCTRL[]:   INSTruction ConTRoL
       * Opcode range: 0x8E
       * Stack:        int32 int32 -->
       */
      static void
      Ins_INSTCTRL( TT_ExecContext  exc,
                    FT_Long*        args )
      {
        FT_ULong  K, L, Kf;
    
    
        K = (FT_ULong)args[1];
        L = (FT_ULong)args[0];
    
        /* selector values cannot be `OR'ed;                 */
        /* they are indices starting with index 1, not flags */
        if ( K < 1 || K > 3 )
        {
          if ( exc->pedantic_hinting )
            exc->error = FT_THROW( Invalid_Reference );
          return;
        }
    
        /* convert index to flag value */
        Kf = 1 << ( K - 1 );
    
        if ( L != 0 )
        {
          /* arguments to selectors look like flag values */
          if ( L != Kf )
          {
            if ( exc->pedantic_hinting )
              exc->error = FT_THROW( Invalid_Reference );
            return;
          }
        }
    
        exc->GS.instruct_control &= ~(FT_Byte)Kf;
        exc->GS.instruct_control |= (FT_Byte)L;
    
        if ( K == 3 )
        {
    #ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY
          /* INSTCTRL modifying flag 3 also has an effect */
          /* outside of the CVT program                   */
          if ( SUBPIXEL_HINTING_INFINALITY )
            exc->ignore_x_mode = FT_BOOL( L == 4 );
    #endif
    
    #ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL
          /* Native ClearType fonts sign a waiver that turns off all backward  */
          /* compatibility hacks and lets them program points to the grid like */
          /* it's 1996.  They might sign a waiver for just one glyph, though.  */
          if ( SUBPIXEL_HINTING_MINIMAL )
            exc->backward_compatibility = !FT_BOOL( L == 4 );
    #endif
        }
      }
    
    
      /**************************************************************************
       *
       * SCANCTRL[]:   SCAN ConTRoL
       * Opcode range: 0x85
       * Stack:        uint32? -->
       */
      static void
      Ins_SCANCTRL( TT_ExecContext  exc,
                    FT_Long*        args )
      {
        FT_Int  A;
    
    
        /* Get Threshold */
        A = (FT_Int)( args[0] & 0xFF );
    
        if ( A == 0xFF )
        {
          exc->GS.scan_control = TRUE;
          return;
        }
        else if ( A == 0 )
        {
          exc->GS.scan_control = FALSE;
          return;
        }
    
        if ( ( args[0] & 0x100 ) != 0 && exc->tt_metrics.ppem <= A )
          exc->GS.scan_control = TRUE;
    
        if ( ( args[0] & 0x200 ) != 0 && exc->tt_metrics.rotated )
          exc->GS.scan_control = TRUE;
    
        if ( ( args[0] & 0x400 ) != 0 && exc->tt_metrics.stretched )
          exc->GS.scan_control = TRUE;
    
        if ( ( args[0] & 0x800 ) != 0 && exc->tt_metrics.ppem > A )
          exc->GS.scan_control = FALSE;
    
        if ( ( args[0] & 0x1000 ) != 0 && exc->tt_metrics.rotated )
          exc->GS.scan_control = FALSE;
    
        if ( ( args[0] & 0x2000 ) != 0 && exc->tt_metrics.stretched )
          exc->GS.scan_control = FALSE;
      }
    
    
      /**************************************************************************
       *
       * SCANTYPE[]:   SCAN TYPE
       * Opcode range: 0x8D
       * Stack:        uint16 -->
       */
      static void
      Ins_SCANTYPE( TT_ExecContext  exc,
                    FT_Long*        args )
      {
        if ( args[0] >= 0 )
          exc->GS.scan_type = (FT_Int)args[0] & 0xFFFF;
      }
    
    
      /**************************************************************************
       *
       * MANAGING OUTLINES
       *
       */
    
    
      /**************************************************************************
       *
       * FLIPPT[]:     FLIP PoinT
       * Opcode range: 0x80
       * Stack:        uint32... -->
       */
      static void
      Ins_FLIPPT( TT_ExecContext  exc )
      {
        FT_UShort  point;
    
    
    #ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL
        /* See `ttinterp.h' for details on backward compatibility mode. */
        if ( SUBPIXEL_HINTING_MINIMAL    &&
             exc->backward_compatibility &&
             exc->iupx_called            &&
             exc->iupy_called            )
          goto Fail;
    #endif
    
        if ( exc->top < exc->GS.loop )
        {
          if ( exc->pedantic_hinting )
            exc->error = FT_THROW( Too_Few_Arguments );
          goto Fail;
        }
    
        while ( exc->GS.loop > 0 )
        {
          exc->args--;
    
          point = (FT_UShort)exc->stack[exc->args];
    
          if ( BOUNDS( point, exc->pts.n_points ) )
          {
            if ( exc->pedantic_hinting )
            {
              exc->error = FT_THROW( Invalid_Reference );
              return;
            }
          }
          else
            exc->pts.tags[point] ^= FT_CURVE_TAG_ON;
    
          exc->GS.loop--;
        }
    
      Fail:
        exc->GS.loop = 1;
        exc->new_top = exc->args;
      }
    
    
      /**************************************************************************
       *
       * FLIPRGON[]:   FLIP RanGe ON
       * Opcode range: 0x81
       * Stack:        uint32 uint32 -->
       */
      static void
      Ins_FLIPRGON( TT_ExecContext  exc,
                    FT_Long*        args )
      {
        FT_UShort  I, K, L;
    
    
    #ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL
        /* See `ttinterp.h' for details on backward compatibility mode. */
        if ( SUBPIXEL_HINTING_MINIMAL    &&
             exc->backward_compatibility &&
             exc->iupx_called            &&
             exc->iupy_called            )
          return;
    #endif
    
        K = (FT_UShort)args[1];
        L = (FT_UShort)args[0];
    
        if ( BOUNDS( K, exc->pts.n_points ) ||
             BOUNDS( L, exc->pts.n_points ) )
        {
          if ( exc->pedantic_hinting )
            exc->error = FT_THROW( Invalid_Reference );
          return;
        }
    
        for ( I = L; I <= K; I++ )
          exc->pts.tags[I] |= FT_CURVE_TAG_ON;
      }
    
    
      /**************************************************************************
       *
       * FLIPRGOFF:    FLIP RanGe OFF
       * Opcode range: 0x82
       * Stack:        uint32 uint32 -->
       */
      static void
      Ins_FLIPRGOFF( TT_ExecContext  exc,
                     FT_Long*        args )
      {
        FT_UShort  I, K, L;
    
    
    #ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL
        /* See `ttinterp.h' for details on backward compatibility mode. */
        if ( SUBPIXEL_HINTING_MINIMAL    &&
             exc->backward_compatibility &&
             exc->iupx_called            &&
             exc->iupy_called            )
          return;
    #endif
    
        K = (FT_UShort)args[1];
        L = (FT_UShort)args[0];
    
        if ( BOUNDS( K, exc->pts.n_points ) ||
             BOUNDS( L, exc->pts.n_points ) )
        {
          if ( exc->pedantic_hinting )
            exc->error = FT_THROW( Invalid_Reference );
          return;
        }
    
        for ( I = L; I <= K; I++ )
          exc->pts.tags[I] &= ~FT_CURVE_TAG_ON;
      }
    
    
      static FT_Bool
      Compute_Point_Displacement( TT_ExecContext  exc,
                                  FT_F26Dot6*     x,
                                  FT_F26Dot6*     y,
                                  TT_GlyphZone    zone,
                                  FT_UShort*      refp )
      {
        TT_GlyphZoneRec  zp;
        FT_UShort        p;
        FT_F26Dot6       d;
    
    
        if ( exc->opcode & 1 )
        {
          zp = exc->zp0;
          p  = exc->GS.rp1;
        }
        else
        {
          zp = exc->zp1;
          p  = exc->GS.rp2;
        }
    
        if ( BOUNDS( p, zp.n_points ) )
        {
          if ( exc->pedantic_hinting )
            exc->error = FT_THROW( Invalid_Reference );
          *refp = 0;
          return FAILURE;
        }
    
        *zone = zp;
        *refp = p;
    
        d = PROJECT( zp.cur + p, zp.org + p );
    
        *x = FT_MulDiv( d, (FT_Long)exc->GS.freeVector.x, exc->F_dot_P );
        *y = FT_MulDiv( d, (FT_Long)exc->GS.freeVector.y, exc->F_dot_P );
    
        return SUCCESS;
      }
    
    
      /* See `ttinterp.h' for details on backward compatibility mode. */
      static void
      Move_Zp2_Point( TT_ExecContext  exc,
                      FT_UShort       point,
                      FT_F26Dot6      dx,
                      FT_F26Dot6      dy,
                      FT_Bool         touch )
      {
        if ( exc->GS.freeVector.x != 0 )
        {
    #ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL
          if ( !( SUBPIXEL_HINTING_MINIMAL    &&
                  exc->backward_compatibility ) )
    #endif
            exc->zp2.cur[point].x = ADD_LONG( exc->zp2.cur[point].x, dx );
    
          if ( touch )
            exc->zp2.tags[point] |= FT_CURVE_TAG_TOUCH_X;
        }
    
        if ( exc->GS.freeVector.y != 0 )
        {
    #ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL
          if ( !( SUBPIXEL_HINTING_MINIMAL    &&
                  exc->backward_compatibility &&
                  exc->iupx_called            &&
                  exc->iupy_called            ) )
    #endif
            exc->zp2.cur[point].y = ADD_LONG( exc->zp2.cur[point].y, dy );
    
          if ( touch )
            exc->zp2.tags[point] |= FT_CURVE_TAG_TOUCH_Y;
        }
      }
    
    
      /**************************************************************************
       *
       * SHP[a]:       SHift Point by the last point
       * Opcode range: 0x32-0x33
       * Stack:        uint32... -->
       */
      static void
      Ins_SHP( TT_ExecContext  exc )
      {
        TT_GlyphZoneRec  zp;
        FT_UShort        refp;
    
        FT_F26Dot6       dx, dy;
        FT_UShort        point;
    
    
        if ( exc->top < exc->GS.loop )
        {
          if ( exc->pedantic_hinting )
            exc->error = FT_THROW( Invalid_Reference );
          goto Fail;
        }
    
        if ( Compute_Point_Displacement( exc, &dx, &dy, &zp, &refp ) )
          return;
    
        while ( exc->GS.loop > 0 )
        {
          exc->args--;
          point = (FT_UShort)exc->stack[exc->args];
    
          if ( BOUNDS( point, exc->zp2.n_points ) )
          {
            if ( exc->pedantic_hinting )
            {
              exc->error = FT_THROW( Invalid_Reference );
              return;
            }
          }
          else
    #ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY
          /* doesn't follow Cleartype spec but produces better result */
          if ( SUBPIXEL_HINTING_INFINALITY && exc->ignore_x_mode )
            Move_Zp2_Point( exc, point, 0, dy, TRUE );
          else
    #endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */
            Move_Zp2_Point( exc, point, dx, dy, TRUE );
    
          exc->GS.loop--;
        }
    
      Fail:
        exc->GS.loop = 1;
        exc->new_top = exc->args;
      }
    
    
      /**************************************************************************
       *
       * SHC[a]:       SHift Contour
       * Opcode range: 0x34-35
       * Stack:        uint32 -->
       *
       * UNDOCUMENTED: According to Greg Hitchcock, there is one (virtual)
       *               contour in the twilight zone, namely contour number
       *               zero which includes all points of it.
       */
      static void
      Ins_SHC( TT_ExecContext  exc,
               FT_Long*        args )
      {
        TT_GlyphZoneRec  zp;
        FT_UShort        refp;
        FT_F26Dot6       dx, dy;
    
        FT_Short         contour, bounds;
        FT_UShort        start, limit, i;
    
    
        contour = (FT_Short)args[0];
        bounds  = ( exc->GS.gep2 == 0 ) ? 1 : exc->zp2.n_contours;
    
        if ( BOUNDS( contour, bounds ) )
        {
          if ( exc->pedantic_hinting )
            exc->error = FT_THROW( Invalid_Reference );
          return;
        }
    
        if ( Compute_Point_Displacement( exc, &dx, &dy, &zp, &refp ) )
          return;
    
        if ( contour == 0 )
          start = 0;
        else
          start = (FT_UShort)( exc->zp2.contours[contour - 1] + 1 -
                               exc->zp2.first_point );
    
        /* we use the number of points if in the twilight zone */
        if ( exc->GS.gep2 == 0 )
          limit = exc->zp2.n_points;
        else
          limit = (FT_UShort)( exc->zp2.contours[contour] -
                               exc->zp2.first_point + 1 );
    
        for ( i = start; i < limit; i++ )
        {
          if ( zp.cur != exc->zp2.cur || refp != i )
            Move_Zp2_Point( exc, i, dx, dy, TRUE );
        }
      }
    
    
      /**************************************************************************
       *
       * SHZ[a]:       SHift Zone
       * Opcode range: 0x36-37
       * Stack:        uint32 -->
       */
      static void
      Ins_SHZ( TT_ExecContext  exc,
               FT_Long*        args )
      {
        TT_GlyphZoneRec  zp;
        FT_UShort        refp;
        FT_F26Dot6       dx,
                         dy;
    
        FT_UShort        limit, i;
    
    
        if ( BOUNDS( args[0], 2 ) )
        {
          if ( exc->pedantic_hinting )
            exc->error = FT_THROW( Invalid_Reference );
          return;
        }
    
        if ( Compute_Point_Displacement( exc, &dx, &dy, &zp, &refp ) )
          return;
    
        /* XXX: UNDOCUMENTED! SHZ doesn't move the phantom points.     */
        /*      Twilight zone has no real contours, so use `n_points'. */
        /*      Normal zone's `n_points' includes phantoms, so must    */
        /*      use end of last contour.                               */
        if ( exc->GS.gep2 == 0 )
          limit = (FT_UShort)exc->zp2.n_points;
        else if ( exc->GS.gep2 == 1 && exc->zp2.n_contours > 0 )
          limit = (FT_UShort)( exc->zp2.contours[exc->zp2.n_contours - 1] + 1 );
        else
          limit = 0;
    
        /* XXX: UNDOCUMENTED! SHZ doesn't touch the points */
        for ( i = 0; i < limit; i++ )
        {
          if ( zp.cur != exc->zp2.cur || refp != i )
            Move_Zp2_Point( exc, i, dx, dy, FALSE );
        }
      }
    
    
      /**************************************************************************
       *
       * SHPIX[]:      SHift points by a PIXel amount
       * Opcode range: 0x38
       * Stack:        f26.6 uint32... -->
       */
      static void
      Ins_SHPIX( TT_ExecContext  exc,
                 FT_Long*        args )
      {
        FT_F26Dot6  dx, dy;
        FT_UShort   point;
    #ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY
        FT_Int      B1, B2;
    #endif
    #ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL
        FT_Bool     in_twilight = FT_BOOL( exc->GS.gep0 == 0 ||
                                           exc->GS.gep1 == 0 ||
                                           exc->GS.gep2 == 0 );
    #endif
    
    
    
        if ( exc->top < exc->GS.loop + 1 )
        {
          if ( exc->pedantic_hinting )
            exc->error = FT_THROW( Invalid_Reference );
          goto Fail;
        }
    
        dx = TT_MulFix14( args[0], exc->GS.freeVector.x );
        dy = TT_MulFix14( args[0], exc->GS.freeVector.y );
    
        while ( exc->GS.loop > 0 )
        {
          exc->args--;
    
          point = (FT_UShort)exc->stack[exc->args];
    
          if ( BOUNDS( point, exc->zp2.n_points ) )
          {
            if ( exc->pedantic_hinting )
            {
              exc->error = FT_THROW( Invalid_Reference );
              return;
            }
          }
          else
    #ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY
          if ( SUBPIXEL_HINTING_INFINALITY )
          {
            /*  If not using ignore_x_mode rendering, allow ZP2 move.        */
            /*  If inline deltas aren't allowed, skip ZP2 move.              */
            /*  If using ignore_x_mode rendering, allow ZP2 point move if:   */
            /*   - freedom vector is y and sph_compatibility_mode is off     */
            /*   - the glyph is composite and the move is in the Y direction */
            /*   - the glyph is specifically set to allow SHPIX moves        */
            /*   - the move is on a previously Y-touched point               */
    
            if ( exc->ignore_x_mode )
            {
              /* save point for later comparison */
              if ( exc->GS.freeVector.y != 0 )
                B1 = exc->zp2.cur[point].y;
              else
                B1 = exc->zp2.cur[point].x;
    
              if ( !exc->face->sph_compatibility_mode &&
                   exc->GS.freeVector.y != 0          )
              {
                Move_Zp2_Point( exc, point, dx, dy, TRUE );
    
                /* save new point */
                if ( exc->GS.freeVector.y != 0 )
                {
                  B2 = exc->zp2.cur[point].y;
    
                  /* reverse any disallowed moves */
                  if ( ( exc->sph_tweak_flags & SPH_TWEAK_SKIP_NONPIXEL_Y_MOVES ) &&
                       ( B1 & 63 ) != 0                                           &&
                       ( B2 & 63 ) != 0                                           &&
                       B1 != B2                                                   )
                    Move_Zp2_Point( exc,
                                    point,
                                    NEG_LONG( dx ),
                                    NEG_LONG( dy ),
                                    TRUE );
                }
              }
              else if ( exc->face->sph_compatibility_mode )
              {
                if ( exc->sph_tweak_flags & SPH_TWEAK_ROUND_NONPIXEL_Y_MOVES )
                {
                  dx = FT_PIX_ROUND( B1 + dx ) - B1;
                  dy = FT_PIX_ROUND( B1 + dy ) - B1;
                }
    
                /* skip post-iup deltas */
                if ( exc->iup_called                                          &&
                     ( ( exc->sph_in_func_flags & SPH_FDEF_INLINE_DELTA_1 ) ||
                       ( exc->sph_in_func_flags & SPH_FDEF_INLINE_DELTA_2 ) ) )
                  goto Skip;
    
                if ( !( exc->sph_tweak_flags & SPH_TWEAK_ALWAYS_SKIP_DELTAP ) &&
                      ( ( exc->is_composite && exc->GS.freeVector.y != 0 ) ||
                        ( exc->zp2.tags[point] & FT_CURVE_TAG_TOUCH_Y )    ||
                        ( exc->sph_tweak_flags & SPH_TWEAK_DO_SHPIX )      )  )
                  Move_Zp2_Point( exc, point, 0, dy, TRUE );
    
                /* save new point */
                if ( exc->GS.freeVector.y != 0 )
                {
                  B2 = exc->zp2.cur[point].y;
    
                  /* reverse any disallowed moves */
                  if ( ( B1 & 63 ) == 0 &&
                       ( B2 & 63 ) != 0 &&
                       B1 != B2         )
                    Move_Zp2_Point( exc, point, 0, NEG_LONG( dy ), TRUE );
                }
              }
              else if ( exc->sph_in_func_flags & SPH_FDEF_TYPEMAN_DIAGENDCTRL )
                Move_Zp2_Point( exc, point, dx, dy, TRUE );
            }
            else
              Move_Zp2_Point( exc, point, dx, dy, TRUE );
          }
          else
    #endif
    #ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL
          if ( SUBPIXEL_HINTING_MINIMAL    &&
               exc->backward_compatibility )
          {
            /* Special case: allow SHPIX to move points in the twilight zone.  */
            /* Otherwise, treat SHPIX the same as DELTAP.  Unbreaks various    */
            /* fonts such as older versions of Rokkitt and DTL Argo T Light    */
            /* that would glitch severely after calling ALIGNRP after a        */
            /* blocked SHPIX.                                                  */
            if ( in_twilight                                                ||
                 ( !( exc->iupx_called && exc->iupy_called )              &&
                   ( ( exc->is_composite && exc->GS.freeVector.y != 0 ) ||
                     ( exc->zp2.tags[point] & FT_CURVE_TAG_TOUCH_Y )    ) ) )
              Move_Zp2_Point( exc, point, 0, dy, TRUE );
          }
          else
    #endif
            Move_Zp2_Point( exc, point, dx, dy, TRUE );
    
    #ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY
        Skip:
    #endif
          exc->GS.loop--;
        }
    
      Fail:
        exc->GS.loop = 1;
        exc->new_top = exc->args;
      }
    
    
      /**************************************************************************
       *
       * MSIRP[a]:     Move Stack Indirect Relative Position
       * Opcode range: 0x3A-0x3B
       * Stack:        f26.6 uint32 -->
       */
      static void
      Ins_MSIRP( TT_ExecContext  exc,
                 FT_Long*        args )
      {
        FT_UShort   point = 0;
        FT_F26Dot6  distance;
    #ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY
        FT_F26Dot6  control_value_cutin = 0;
        FT_F26Dot6  delta;
    
    
        if ( SUBPIXEL_HINTING_INFINALITY )
        {
          control_value_cutin = exc->GS.control_value_cutin;
    
          if ( exc->ignore_x_mode                                 &&
               exc->GS.freeVector.x != 0                          &&
               !( exc->sph_tweak_flags & SPH_TWEAK_NORMAL_ROUND ) )
            control_value_cutin = 0;
        }
    #endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */
    
        point = (FT_UShort)args[0];
    
        if ( BOUNDS( point,       exc->zp1.n_points ) ||
             BOUNDS( exc->GS.rp0, exc->zp0.n_points ) )
        {
          if ( exc->pedantic_hinting )
            exc->error = FT_THROW( Invalid_Reference );
          return;
        }
    
        /* UNDOCUMENTED!  The MS rasterizer does that with */
        /* twilight points (confirmed by Greg Hitchcock)   */
        if ( exc->GS.gep1 == 0 )
        {
          exc->zp1.org[point] = exc->zp0.org[exc->GS.rp0];
          exc->func_move_orig( exc, &exc->zp1, point, args[1] );
          exc->zp1.cur[point] = exc->zp1.org[point];
        }
    
        distance = PROJECT( exc->zp1.cur + point, exc->zp0.cur + exc->GS.rp0 );
    
    #ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY
        delta = SUB_LONG( distance, args[1] );
        if ( delta < 0 )
          delta = NEG_LONG( delta );
    
        /* subpixel hinting - make MSIRP respect CVT cut-in; */
        if ( SUBPIXEL_HINTING_INFINALITY  &&
             exc->ignore_x_mode           &&
             exc->GS.freeVector.x != 0    &&
             delta >= control_value_cutin )
          distance = args[1];
    #endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */
    
        exc->func_move( exc,
                        &exc->zp1,
                        point,
                        SUB_LONG( args[1], distance ) );
    
        exc->GS.rp1 = exc->GS.rp0;
        exc->GS.rp2 = point;
    
        if ( ( exc->opcode & 1 ) != 0 )
          exc->GS.rp0 = point;
      }
    
    
      /**************************************************************************
       *
       * MDAP[a]:      Move Direct Absolute Point
       * Opcode range: 0x2E-0x2F
       * Stack:        uint32 -->
       */
      static void
      Ins_MDAP( TT_ExecContext  exc,
                FT_Long*        args )
      {
        FT_UShort   point;
        FT_F26Dot6  cur_dist;
        FT_F26Dot6  distance;
    
    
        point = (FT_UShort)args[0];
    
        if ( BOUNDS( point, exc->zp0.n_points ) )
        {
          if ( exc->pedantic_hinting )
            exc->error = FT_THROW( Invalid_Reference );
          return;
        }
    
        if ( ( exc->opcode & 1 ) != 0 )
        {
          cur_dist = FAST_PROJECT( &exc->zp0.cur[point] );
    #ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY
          if ( SUBPIXEL_HINTING_INFINALITY &&
               exc->ignore_x_mode          &&
               exc->GS.freeVector.x != 0   )
            distance = SUB_LONG(
                         Round_None( exc,
                                     cur_dist,
                                     exc->tt_metrics.compensations[0] ),
                         cur_dist );
          else
    #endif
            distance = SUB_LONG(
                         exc->func_round( exc,
                                          cur_dist,
                                          exc->tt_metrics.compensations[0] ),
                         cur_dist );
        }
        else
          distance = 0;
    
        exc->func_move( exc, &exc->zp0, point, distance );
    
        exc->GS.rp0 = point;
        exc->GS.rp1 = point;
      }
    
    
      /**************************************************************************
       *
       * MIAP[a]:      Move Indirect Absolute Point
       * Opcode range: 0x3E-0x3F
       * Stack:        uint32 uint32 -->
       */
      static void
      Ins_MIAP( TT_ExecContext  exc,
                FT_Long*        args )
      {
        FT_ULong    cvtEntry;
        FT_UShort   point;
        FT_F26Dot6  distance;
        FT_F26Dot6  org_dist;
        FT_F26Dot6  control_value_cutin;
    
    
        control_value_cutin = exc->GS.control_value_cutin;
        cvtEntry            = (FT_ULong)args[1];
        point               = (FT_UShort)args[0];
    
    #ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY
        if ( SUBPIXEL_HINTING_INFINALITY                        &&
             exc->ignore_x_mode                                 &&
             exc->GS.freeVector.x != 0                          &&
             exc->GS.freeVector.y == 0                          &&
             !( exc->sph_tweak_flags & SPH_TWEAK_NORMAL_ROUND ) )
          control_value_cutin = 0;
    #endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */
    
        if ( BOUNDS( point,     exc->zp0.n_points ) ||
             BOUNDSL( cvtEntry, exc->cvtSize )      )
        {
          if ( exc->pedantic_hinting )
            exc->error = FT_THROW( Invalid_Reference );
          goto Fail;
        }
    
        /* UNDOCUMENTED!                                                      */
        /*                                                                    */
        /* The behaviour of an MIAP instruction is quite different when used  */
        /* in the twilight zone.                                              */
        /*                                                                    */
        /* First, no control value cut-in 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 invalid according to the specification.         */
        /*                                                                    */
        /* We implement it with a special sequence for the twilight zone.     */
        /* This is a bad hack, but it seems to work.                          */
        /*                                                                    */
        /* Confirmed by Greg Hitchcock.                                       */
    
        distance = exc->func_read_cvt( exc, cvtEntry );
    
        if ( exc->GS.gep0 == 0 )   /* If in twilight zone */
        {
    #ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY
          /* Only adjust if not in sph_compatibility_mode or ignore_x_mode. */
          /* Determined via experimentation and may be incorrect...         */
          if ( !( SUBPIXEL_HINTING_INFINALITY           &&
                  ( exc->ignore_x_mode                &&
                    exc->face->sph_compatibility_mode ) ) )
    #endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */
            exc->zp0.org[point].x = TT_MulFix14( distance,
                                                 exc->GS.freeVector.x );
          exc->zp0.org[point].y = TT_MulFix14( distance,
                                               exc->GS.freeVector.y ),
          exc->zp0.cur[point]   = exc->zp0.org[point];
        }
    #ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY
        if ( SUBPIXEL_HINTING_INFINALITY                    &&
             exc->ignore_x_mode                             &&
             ( exc->sph_tweak_flags & SPH_TWEAK_MIAP_HACK ) &&
             distance > 0                                   &&
             exc->GS.freeVector.y != 0                      )
          distance = 0;
    #endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */
    
        org_dist = FAST_PROJECT( &exc->zp0.cur[point] );
    
        if ( ( exc->opcode & 1 ) != 0 )   /* rounding and control cut-in flag */
        {
          FT_F26Dot6  delta;
    
    
          delta = SUB_LONG( distance, org_dist );
          if ( delta < 0 )
            delta = NEG_LONG( delta );
    
          if ( delta > control_value_cutin )
            distance = org_dist;
    
    #ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY
          if ( SUBPIXEL_HINTING_INFINALITY &&
               exc->ignore_x_mode          &&
               exc->GS.freeVector.x != 0   )
            distance = Round_None( exc,
                                   distance,
                                   exc->tt_metrics.compensations[0] );
          else
    #endif
            distance = exc->func_round( exc,
                                        distance,
                                        exc->tt_metrics.compensations[0] );
        }
    
        exc->func_move( exc, &exc->zp0, point, SUB_LONG( distance, org_dist ) );
    
      Fail:
        exc->GS.rp0 = point;
        exc->GS.rp1 = point;
      }
    
    
      /**************************************************************************
       *
       * MDRP[abcde]:  Move Direct Relative Point
       * Opcode range: 0xC0-0xDF
       * Stack:        uint32 -->
       */
      static void
      Ins_MDRP( TT_ExecContext  exc,
                FT_Long*        args )
      {
        FT_UShort   point = 0;
        FT_F26Dot6  org_dist, distance, minimum_distance;
    
    
        minimum_distance = exc->GS.minimum_distance;
    
    #ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY
        if ( SUBPIXEL_HINTING_INFINALITY                        &&
             exc->ignore_x_mode                                 &&
             exc->GS.freeVector.x != 0                          &&
             !( exc->sph_tweak_flags & SPH_TWEAK_NORMAL_ROUND ) )
          minimum_distance = 0;
    #endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */
    
        point = (FT_UShort)args[0];
    
        if ( BOUNDS( point,       exc->zp1.n_points ) ||
             BOUNDS( exc->GS.rp0, exc->zp0.n_points ) )
        {
          if ( exc->pedantic_hinting )
            exc->error = FT_THROW( Invalid_Reference );
          goto Fail;
        }
    
        /* XXX: Is there some undocumented feature while in the */
        /*      twilight zone?                                  */
    
        /* XXX: UNDOCUMENTED: twilight zone special case */
    
        if ( exc->GS.gep0 == 0 || exc->GS.gep1 == 0 )
        {
          FT_Vector*  vec1 = &exc->zp1.org[point];
          FT_Vector*  vec2 = &exc->zp0.org[exc->GS.rp0];
    
    
          org_dist = DUALPROJ( vec1, vec2 );
        }
        else
        {
          FT_Vector*  vec1 = &exc->zp1.orus[point];
          FT_Vector*  vec2 = &exc->zp0.orus[exc->GS.rp0];
    
    
          if ( exc->metrics.x_scale == exc->metrics.y_scale )
          {
            /* this should be faster */
            org_dist = DUALPROJ( vec1, vec2 );
            org_dist = FT_MulFix( org_dist, exc->metrics.x_scale );
          }
          else
          {
            FT_Vector  vec;
    
    
            vec.x = FT_MulFix( SUB_LONG( vec1->x, vec2->x ),
                               exc->metrics.x_scale );
            vec.y = FT_MulFix( SUB_LONG( vec1->y, vec2->y ),
                               exc->metrics.y_scale );
    
            org_dist = FAST_DUALPROJ( &vec );
          }
        }
    
        /* single width cut-in test */
    
        /* |org_dist - single_width_value| < single_width_cutin */
        if ( exc->GS.single_width_cutin > 0          &&
             org_dist < exc->GS.single_width_value +
                          exc->GS.single_width_cutin &&
             org_dist > exc->GS.single_width_value -
                          exc->GS.single_width_cutin )
        {
          if ( org_dist >= 0 )
            org_dist = exc->GS.single_width_value;
          else
            org_dist = -exc->GS.single_width_value;
        }
    
        /* round flag */
    
        if ( ( exc->opcode & 4 ) != 0 )
        {
    #ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY
          if ( SUBPIXEL_HINTING_INFINALITY &&
               exc->ignore_x_mode          &&
               exc->GS.freeVector.x != 0   )
            distance = Round_None(
                         exc,
                         org_dist,
                         exc->tt_metrics.compensations[exc->opcode & 3] );
          else
    #endif
            distance = exc->func_round(
                         exc,
                         org_dist,
                         exc->tt_metrics.compensations[exc->opcode & 3] );
        }
        else
          distance = Round_None(
                       exc,
                       org_dist,
                       exc->tt_metrics.compensations[exc->opcode & 3] );
    
        /* minimum distance flag */
    
        if ( ( exc->opcode & 8 ) != 0 )
        {
          if ( org_dist >= 0 )
          {
            if ( distance < minimum_distance )
              distance = minimum_distance;
          }
          else
          {
            if ( distance > NEG_LONG( minimum_distance ) )
              distance = NEG_LONG( minimum_distance );
          }
        }
    
        /* now move the point */
    
        org_dist = PROJECT( exc->zp1.cur + point, exc->zp0.cur + exc->GS.rp0 );
    
        exc->func_move( exc, &exc->zp1, point, SUB_LONG( distance, org_dist ) );
    
      Fail:
        exc->GS.rp1 = exc->GS.rp0;
        exc->GS.rp2 = point;
    
        if ( ( exc->opcode & 16 ) != 0 )
          exc->GS.rp0 = point;
      }
    
    
      /**************************************************************************
       *
       * MIRP[abcde]:  Move Indirect Relative Point
       * Opcode range: 0xE0-0xFF
       * Stack:        int32? uint32 -->
       */
      static void
      Ins_MIRP( TT_ExecContext  exc,
                FT_Long*        args )
      {
        FT_UShort   point;
        FT_ULong    cvtEntry;
    
        FT_F26Dot6  cvt_dist,
                    distance,
                    cur_dist,
                    org_dist,
                    control_value_cutin,
                    minimum_distance;
    #ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY
        FT_Int      B1           = 0; /* pacify compiler */
        FT_Int      B2           = 0;
        FT_Bool     reverse_move = FALSE;
    #endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */
    
        FT_F26Dot6  delta;
    
    
        minimum_distance    = exc->GS.minimum_distance;
        control_value_cutin = exc->GS.control_value_cutin;
        point               = (FT_UShort)args[0];
        cvtEntry            = (FT_ULong)( ADD_LONG( args[1], 1 ) );
    
    #ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY
        if ( SUBPIXEL_HINTING_INFINALITY                        &&
             exc->ignore_x_mode                                 &&
             exc->GS.freeVector.x != 0                          &&
             !( exc->sph_tweak_flags & SPH_TWEAK_NORMAL_ROUND ) )
          control_value_cutin = minimum_distance = 0;
    #endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */
    
        /* XXX: UNDOCUMENTED! cvt[-1] = 0 always */
    
        if ( BOUNDS( point,       exc->zp1.n_points ) ||
             BOUNDSL( cvtEntry,   exc->cvtSize + 1 )  ||
             BOUNDS( exc->GS.rp0, exc->zp0.n_points ) )
        {
          if ( exc->pedantic_hinting )
            exc->error = FT_THROW( Invalid_Reference );
          goto Fail;
        }
    
        if ( !cvtEntry )
          cvt_dist = 0;
        else
          cvt_dist = exc->func_read_cvt( exc, cvtEntry - 1 );
    
        /* single width test */
    
        delta = SUB_LONG( cvt_dist, exc->GS.single_width_value );
        if ( delta < 0 )
          delta = NEG_LONG( delta );
    
        if ( delta < exc->GS.single_width_cutin )
        {
          if ( cvt_dist >= 0 )
            cvt_dist =  exc->GS.single_width_value;
          else
            cvt_dist = -exc->GS.single_width_value;
        }
    
        /* UNDOCUMENTED!  The MS rasterizer does that with */
        /* twilight points (confirmed by Greg Hitchcock)   */
        if ( exc->GS.gep1 == 0 )
        {
          exc->zp1.org[point].x = ADD_LONG(
                                    exc->zp0.org[exc->GS.rp0].x,
                                    TT_MulFix14( cvt_dist,
                                                 exc->GS.freeVector.x ) );
          exc->zp1.org[point].y = ADD_LONG(
                                    exc->zp0.org[exc->GS.rp0].y,
                                    TT_MulFix14( cvt_dist,
                                                 exc->GS.freeVector.y ) );
          exc->zp1.cur[point]   = exc->zp1.org[point];
        }
    
        org_dist = DUALPROJ( &exc->zp1.org[point], &exc->zp0.org[exc->GS.rp0] );
        cur_dist = PROJECT ( &exc->zp1.cur[point], &exc->zp0.cur[exc->GS.rp0] );
    
        /* auto-flip test */
    
        if ( exc->GS.auto_flip )
        {
          if ( ( org_dist ^ cvt_dist ) < 0 )
            cvt_dist = NEG_LONG( cvt_dist );
        }
    
    #ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY
        if ( SUBPIXEL_HINTING_INFINALITY                               &&
             exc->ignore_x_mode                                        &&
             exc->GS.freeVector.y != 0                                 &&
             ( exc->sph_tweak_flags & SPH_TWEAK_TIMES_NEW_ROMAN_HACK ) )
        {
          if ( cur_dist < -64 )
            cvt_dist -= 16;
          else if ( cur_dist > 64 && cur_dist < 84 )
            cvt_dist += 32;
        }
    #endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */
    
        /* control value cut-in and round */
    
        if ( ( exc->opcode & 4 ) != 0 )
        {
          /* XXX: UNDOCUMENTED!  Only perform cut-in test when both points */
          /*      refer to the same zone.                                  */
    
          if ( exc->GS.gep0 == exc->GS.gep1 )
          {
            /* XXX: According to Greg Hitchcock, the following wording is */
            /*      the right one:                                        */
            /*                                                            */
            /*        When the absolute difference between the value in   */
            /*        the table [CVT] and the measurement directly from   */
            /*        the outline is _greater_ than the cut_in value, the */
            /*        outline measurement is used.                        */
            /*                                                            */
            /*      This is from `instgly.doc'.  The description in       */
            /*      `ttinst2.doc', version 1.66, is thus incorrect since  */
            /*      it implies `>=' instead of `>'.                       */
    
            delta = SUB_LONG( cvt_dist, org_dist );
            if ( delta < 0 )
              delta = NEG_LONG( delta );
    
            if ( delta > control_value_cutin )
              cvt_dist = org_dist;
          }
    
          distance = exc->func_round(
                       exc,
                       cvt_dist,
                       exc->tt_metrics.compensations[exc->opcode & 3] );
        }
        else
        {
    
    #ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY
          /* do cvt cut-in always in MIRP for sph */
          if ( SUBPIXEL_HINTING_INFINALITY  &&
               exc->ignore_x_mode           &&
               exc->GS.gep0 == exc->GS.gep1 )
          {
            delta = SUB_LONG( cvt_dist, org_dist );
            if ( delta < 0 )
              delta = NEG_LONG( delta );
    
            if ( delta > control_value_cutin )
              cvt_dist = org_dist;
          }
    #endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */
    
          distance = Round_None(
                       exc,
                       cvt_dist,
                       exc->tt_metrics.compensations[exc->opcode & 3] );
        }
    
        /* minimum distance test */
    
        if ( ( exc->opcode & 8 ) != 0 )
        {
          if ( org_dist >= 0 )
          {
            if ( distance < minimum_distance )
              distance = minimum_distance;
          }
          else
          {
            if ( distance > NEG_LONG( minimum_distance ) )
              distance = NEG_LONG( minimum_distance );
          }
        }
    
    #ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY
        if ( SUBPIXEL_HINTING_INFINALITY )
        {
          B1 = exc->zp1.cur[point].y;
    
          /* Round moves if necessary */
          if ( exc->ignore_x_mode                                          &&
               exc->GS.freeVector.y != 0                                   &&
               ( exc->sph_tweak_flags & SPH_TWEAK_ROUND_NONPIXEL_Y_MOVES ) )
            distance = FT_PIX_ROUND( B1 + distance - cur_dist ) - B1 + cur_dist;
    
          if ( exc->ignore_x_mode                                      &&
               exc->GS.freeVector.y != 0                               &&
               ( exc->opcode & 16 ) == 0                               &&
               ( exc->opcode & 8 ) == 0                                &&
               ( exc->sph_tweak_flags & SPH_TWEAK_COURIER_NEW_2_HACK ) )
            distance += 64;
        }
    #endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */
    
        exc->func_move( exc,
                        &exc->zp1,
                        point,
                        SUB_LONG( distance, cur_dist ) );
    
    #ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY
        if ( SUBPIXEL_HINTING_INFINALITY )
        {
          B2 = exc->zp1.cur[point].y;
    
          /* Reverse move if necessary */
          if ( exc->ignore_x_mode )
          {
            if ( exc->face->sph_compatibility_mode &&
                 exc->GS.freeVector.y != 0         &&
                 ( B1 & 63 ) == 0                  &&
                 ( B2 & 63 ) != 0                  )
              reverse_move = TRUE;
    
            if ( ( exc->sph_tweak_flags & SPH_TWEAK_SKIP_NONPIXEL_Y_MOVES ) &&
                 exc->GS.freeVector.y != 0                                  &&
                 ( B2 & 63 ) != 0                                           &&
                 ( B1 & 63 ) != 0                                           )
              reverse_move = TRUE;
          }
    
          if ( reverse_move )
            exc->func_move( exc,
                            &exc->zp1,
                            point,
                            SUB_LONG( cur_dist, distance ) );
        }
    
    #endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */
    
      Fail:
        exc->GS.rp1 = exc->GS.rp0;
    
        if ( ( exc->opcode & 16 ) != 0 )
          exc->GS.rp0 = point;
    
        exc->GS.rp2 = point;
      }
    
    
      /**************************************************************************
       *
       * ALIGNRP[]:    ALIGN Relative Point
       * Opcode range: 0x3C
       * Stack:        uint32 uint32... -->
       */
      static void
      Ins_ALIGNRP( TT_ExecContext  exc )
      {
        FT_UShort   point;
        FT_F26Dot6  distance;
    
    
    #ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY
        if ( SUBPIXEL_HINTING_INFINALITY                               &&
             exc->ignore_x_mode                                        &&
             exc->iup_called                                           &&
             ( exc->sph_tweak_flags & SPH_TWEAK_NO_ALIGNRP_AFTER_IUP ) )
        {
          exc->error = FT_THROW( Invalid_Reference );
          goto Fail;
        }
    #endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */
    
        if ( exc->top < exc->GS.loop                  ||
             BOUNDS( exc->GS.rp0, exc->zp0.n_points ) )
        {
          if ( exc->pedantic_hinting )
            exc->error = FT_THROW( Invalid_Reference );
          goto Fail;
        }
    
        while ( exc->GS.loop > 0 )
        {
          exc->args--;
    
          point = (FT_UShort)exc->stack[exc->args];
    
          if ( BOUNDS( point, exc->zp1.n_points ) )
          {
            if ( exc->pedantic_hinting )
            {
              exc->error = FT_THROW( Invalid_Reference );
              return;
            }
          }
          else
          {
            distance = PROJECT( exc->zp1.cur + point,
                                exc->zp0.cur + exc->GS.rp0 );
    
            exc->func_move( exc, &exc->zp1, point, NEG_LONG( distance ) );
          }
    
          exc->GS.loop--;
        }
    
      Fail:
        exc->GS.loop = 1;
        exc->new_top = exc->args;
      }
    
    
      /**************************************************************************
       *
       * ISECT[]:      moves point to InterSECTion
       * Opcode range: 0x0F
       * Stack:        5 * uint32 -->
       */
      static void
      Ins_ISECT( TT_ExecContext  exc,
                 FT_Long*        args )
      {
        FT_UShort   point,
                    a0, a1,
                    b0, b1;
    
        FT_F26Dot6  discriminant, dotproduct;
    
        FT_F26Dot6  dx,  dy,
                    dax, day,
                    dbx, dby;
    
        FT_F26Dot6  val;
    
        FT_Vector   R;
    
    
        point = (FT_UShort)args[0];
    
        a0 = (FT_UShort)args[1];
        a1 = (FT_UShort)args[2];
        b0 = (FT_UShort)args[3];
        b1 = (FT_UShort)args[4];
    
        if ( BOUNDS( b0,    exc->zp0.n_points ) ||
             BOUNDS( b1,    exc->zp0.n_points ) ||
             BOUNDS( a0,    exc->zp1.n_points ) ||
             BOUNDS( a1,    exc->zp1.n_points ) ||
             BOUNDS( point, exc->zp2.n_points ) )
        {
          if ( exc->pedantic_hinting )
            exc->error = FT_THROW( Invalid_Reference );
          return;
        }
    
        /* Cramer's rule */
    
        dbx = SUB_LONG( exc->zp0.cur[b1].x, exc->zp0.cur[b0].x );
        dby = SUB_LONG( exc->zp0.cur[b1].y, exc->zp0.cur[b0].y );
    
        dax = SUB_LONG( exc->zp1.cur[a1].x, exc->zp1.cur[a0].x );
        day = SUB_LONG( exc->zp1.cur[a1].y, exc->zp1.cur[a0].y );
    
        dx = SUB_LONG( exc->zp0.cur[b0].x, exc->zp1.cur[a0].x );
        dy = SUB_LONG( exc->zp0.cur[b0].y, exc->zp1.cur[a0].y );
    
        discriminant = ADD_LONG( FT_MulDiv( dax, NEG_LONG( dby ), 0x40 ),
                                 FT_MulDiv( day, dbx, 0x40 ) );
        dotproduct   = ADD_LONG( FT_MulDiv( dax, dbx, 0x40 ),
                                 FT_MulDiv( day, dby, 0x40 ) );
    
        /* The discriminant above is actually a cross product of vectors     */
        /* da and db. Together with the dot product, they can be used as     */
        /* surrogates for sine and cosine of the angle between the vectors.  */
        /* Indeed,                                                           */
        /*       dotproduct   = |da||db|cos(angle)                           */
        /*       discriminant = |da||db|sin(angle)     .                     */
        /* We use these equations to reject grazing intersections by         */
        /* thresholding abs(tan(angle)) at 1/19, corresponding to 3 degrees. */
        if ( MUL_LONG( 19, FT_ABS( discriminant ) ) > FT_ABS( dotproduct ) )
        {
          val = ADD_LONG( FT_MulDiv( dx, NEG_LONG( dby ), 0x40 ),
                          FT_MulDiv( dy, dbx, 0x40 ) );
    
          R.x = FT_MulDiv( val, dax, discriminant );
          R.y = FT_MulDiv( val, day, discriminant );
    
          /* XXX: Block in backward_compatibility and/or post-IUP? */
          exc->zp2.cur[point].x = ADD_LONG( exc->zp1.cur[a0].x, R.x );
          exc->zp2.cur[point].y = ADD_LONG( exc->zp1.cur[a0].y, R.y );
        }
        else
        {
          /* else, take the middle of the middles of A and B */
    
          /* XXX: Block in backward_compatibility and/or post-IUP? */
          exc->zp2.cur[point].x =
            ADD_LONG( ADD_LONG( exc->zp1.cur[a0].x, exc->zp1.cur[a1].x ),
                      ADD_LONG( exc->zp0.cur[b0].x, exc->zp0.cur[b1].x ) ) / 4;
          exc->zp2.cur[point].y =
            ADD_LONG( ADD_LONG( exc->zp1.cur[a0].y, exc->zp1.cur[a1].y ),
                      ADD_LONG( exc->zp0.cur[b0].y, exc->zp0.cur[b1].y ) ) / 4;
        }
    
        exc->zp2.tags[point] |= FT_CURVE_TAG_TOUCH_BOTH;
      }
    
    
      /**************************************************************************
       *
       * ALIGNPTS[]:   ALIGN PoinTS
       * Opcode range: 0x27
       * Stack:        uint32 uint32 -->
       */
      static void
      Ins_ALIGNPTS( TT_ExecContext  exc,
                    FT_Long*        args )
      {
        FT_UShort   p1, p2;
        FT_F26Dot6  distance;
    
    
        p1 = (FT_UShort)args[0];
        p2 = (FT_UShort)args[1];
    
        if ( BOUNDS( p1, exc->zp1.n_points ) ||
             BOUNDS( p2, exc->zp0.n_points ) )
        {
          if ( exc->pedantic_hinting )
            exc->error = FT_THROW( Invalid_Reference );
          return;
        }
    
        distance = PROJECT( exc->zp0.cur + p2, exc->zp1.cur + p1 ) / 2;
    
        exc->func_move( exc, &exc->zp1, p1, distance );
        exc->func_move( exc, &exc->zp0, p2, NEG_LONG( distance ) );
      }
    
    
      /**************************************************************************
       *
       * IP[]:         Interpolate Point
       * Opcode range: 0x39
       * Stack:        uint32... -->
       */
    
      /* SOMETIMES, DUMBER CODE IS BETTER CODE */
    
      static void
      Ins_IP( TT_ExecContext  exc )
      {
        FT_F26Dot6  old_range, cur_range;
        FT_Vector*  orus_base;
        FT_Vector*  cur_base;
        FT_Int      twilight;
    
    
        if ( exc->top < exc->GS.loop )
        {
          if ( exc->pedantic_hinting )
            exc->error = FT_THROW( Invalid_Reference );
          goto Fail;
        }
    
        /*
         * We need to deal in a special way with the twilight zone.
         * Otherwise, by definition, the value of exc->twilight.orus[n] is (0,0),
         * for every n.
         */
        twilight = ( exc->GS.gep0 == 0 ||
                     exc->GS.gep1 == 0 ||
                     exc->GS.gep2 == 0 );
    
        if ( BOUNDS( exc->GS.rp1, exc->zp0.n_points ) )
        {
          if ( exc->pedantic_hinting )
            exc->error = FT_THROW( Invalid_Reference );
          goto Fail;
        }
    
        if ( twilight )
          orus_base = &exc->zp0.org[exc->GS.rp1];
        else
          orus_base = &exc->zp0.orus[exc->GS.rp1];
    
        cur_base = &exc->zp0.cur[exc->GS.rp1];
    
        /* 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( exc->GS.rp1, exc->zp0.n_points ) ||
             BOUNDS( exc->GS.rp2, exc->zp1.n_points ) )
        {
          old_range = 0;
          cur_range = 0;
        }
        else
        {
          if ( twilight )
            old_range = DUALPROJ( &exc->zp1.org[exc->GS.rp2], orus_base );
          else if ( exc->metrics.x_scale == exc->metrics.y_scale )
            old_range = DUALPROJ( &exc->zp1.orus[exc->GS.rp2], orus_base );
          else
          {
            FT_Vector  vec;
    
    
            vec.x = FT_MulFix( SUB_LONG( exc->zp1.orus[exc->GS.rp2].x,
                                         orus_base->x ),
                               exc->metrics.x_scale );
            vec.y = FT_MulFix( SUB_LONG( exc->zp1.orus[exc->GS.rp2].y,
                                         orus_base->y ),
                               exc->metrics.y_scale );
    
            old_range = FAST_DUALPROJ( &vec );
          }
    
          cur_range = PROJECT( &exc->zp1.cur[exc->GS.rp2], cur_base );
        }
    
        for ( ; exc->GS.loop > 0; exc->GS.loop-- )
        {
          FT_UInt     point = (FT_UInt)exc->stack[--exc->args];
          FT_F26Dot6  org_dist, cur_dist, new_dist;
    
    
          /* check point bounds */
          if ( BOUNDS( point, exc->zp2.n_points ) )
          {
            if ( exc->pedantic_hinting )
            {
              exc->error = FT_THROW( Invalid_Reference );
              return;
            }
            continue;
          }
    
          if ( twilight )
            org_dist = DUALPROJ( &exc->zp2.org[point], orus_base );
          else if ( exc->metrics.x_scale == exc->metrics.y_scale )
            org_dist = DUALPROJ( &exc->zp2.orus[point], orus_base );
          else
          {
            FT_Vector  vec;
    
    
            vec.x = FT_MulFix( SUB_LONG( exc->zp2.orus[point].x,
                                         orus_base->x ),
                               exc->metrics.x_scale );
            vec.y = FT_MulFix( SUB_LONG( exc->zp2.orus[point].y,
                                         orus_base->y ),
                               exc->metrics.y_scale );
    
            org_dist = FAST_DUALPROJ( &vec );
          }
    
          cur_dist = PROJECT( &exc->zp2.cur[point], cur_base );
    
          if ( org_dist )
          {
            if ( old_range )
              new_dist = FT_MulDiv( org_dist, cur_range, old_range );
            else
            {
              /* This is the same as what MS does for the invalid case:  */
              /*                                                         */
              /*   delta = (Original_Pt - Original_RP1) -                */
              /*           (Current_Pt - Current_RP1)         ;          */
              /*                                                         */
              /* In FreeType speak:                                      */
              /*                                                         */
              /*   delta = org_dist - cur_dist          .                */
              /*                                                         */
              /* We move `point' by `new_dist - cur_dist' after leaving  */
              /* this block, thus we have                                */
              /*                                                         */
              /*   new_dist - cur_dist = delta                   ,       */
              /*   new_dist - cur_dist = org_dist - cur_dist     ,       */
              /*              new_dist = org_dist                .       */
    
              new_dist = org_dist;
            }
          }
          else
            new_dist = 0;
    
          exc->func_move( exc,
                          &exc->zp2,
                          (FT_UShort)point,
                          SUB_LONG( new_dist, cur_dist ) );
        }
    
      Fail:
        exc->GS.loop = 1;
        exc->new_top = exc->args;
      }
    
    
      /**************************************************************************
       *
       * UTP[a]:       UnTouch Point
       * Opcode range: 0x29
       * Stack:        uint32 -->
       */
      static void
      Ins_UTP( TT_ExecContext  exc,
               FT_Long*        args )
      {
        FT_UShort  point;
        FT_Byte    mask;
    
    
        point = (FT_UShort)args[0];
    
        if ( BOUNDS( point, exc->zp0.n_points ) )
        {
          if ( exc->pedantic_hinting )
            exc->error = FT_THROW( Invalid_Reference );
          return;
        }
    
        mask = 0xFF;
    
        if ( exc->GS.freeVector.x != 0 )
          mask &= ~FT_CURVE_TAG_TOUCH_X;
    
        if ( exc->GS.freeVector.y != 0 )
          mask &= ~FT_CURVE_TAG_TOUCH_Y;
    
        exc->zp0.tags[point] &= mask;
      }
    
    
      /* Local variables for Ins_IUP: */
      typedef struct  IUP_WorkerRec_
      {
        FT_Vector*  orgs;   /* original and current coordinate */
        FT_Vector*  curs;   /* arrays                          */
        FT_Vector*  orus;
        FT_UInt     max_points;
    
      } IUP_WorkerRec, *IUP_Worker;
    
    
      static void
      _iup_worker_shift( IUP_Worker  worker,
                         FT_UInt     p1,
                         FT_UInt     p2,
                         FT_UInt     p )
      {
        FT_UInt     i;
        FT_F26Dot6  dx;
    
    
        dx = SUB_LONG( worker->curs[p].x, worker->orgs[p].x );
        if ( dx != 0 )
        {
          for ( i = p1; i < p; i++ )
            worker->curs[i].x = ADD_LONG( worker->curs[i].x, dx );
    
          for ( i = p + 1; i <= p2; i++ )
            worker->curs[i].x = ADD_LONG( worker->curs[i].x, dx );
        }
      }
    
    
      static void
      _iup_worker_interpolate( IUP_Worker  worker,
                               FT_UInt     p1,
                               FT_UInt     p2,
                               FT_UInt     ref1,
                               FT_UInt     ref2 )
      {
        FT_UInt     i;
        FT_F26Dot6  orus1, orus2, org1, org2, cur1, cur2, delta1, delta2;
    
    
        if ( p1 > p2 )
          return;
    
        if ( BOUNDS( ref1, worker->max_points ) ||
             BOUNDS( ref2, worker->max_points ) )
          return;
    
        orus1 = worker->orus[ref1].x;
        orus2 = worker->orus[ref2].x;
    
        if ( orus1 > orus2 )
        {
          FT_F26Dot6  tmp_o;
          FT_UInt     tmp_r;
    
    
          tmp_o = orus1;
          orus1 = orus2;
          orus2 = tmp_o;
    
          tmp_r = ref1;
          ref1  = ref2;
          ref2  = tmp_r;
        }
    
        org1   = worker->orgs[ref1].x;
        org2   = worker->orgs[ref2].x;
        cur1   = worker->curs[ref1].x;
        cur2   = worker->curs[ref2].x;
        delta1 = SUB_LONG( cur1, org1 );
        delta2 = SUB_LONG( cur2, org2 );
    
        if ( cur1 == cur2 || orus1 == orus2 )
        {
    
          /* trivial snap or shift of untouched points */
          for ( i = p1; i <= p2; i++ )
          {
            FT_F26Dot6  x = worker->orgs[i].x;
    
    
            if ( x <= org1 )
              x = ADD_LONG( x, delta1 );
    
            else if ( x >= org2 )
              x = ADD_LONG( x, delta2 );
    
            else
              x = cur1;
    
            worker->curs[i].x = x;
          }
        }
        else
        {
          FT_Fixed  scale       = 0;
          FT_Bool   scale_valid = 0;
    
    
          /* interpolation */
          for ( i = p1; i <= p2; i++ )
          {
            FT_F26Dot6  x = worker->orgs[i].x;
    
    
            if ( x <= org1 )
              x = ADD_LONG( x, delta1 );
    
            else if ( x >= org2 )
              x = ADD_LONG( x, delta2 );
    
            else
            {
              if ( !scale_valid )
              {
                scale_valid = 1;
                scale       = FT_DivFix( SUB_LONG( cur2, cur1 ),
                                         SUB_LONG( orus2, orus1 ) );
              }
    
              x = ADD_LONG( cur1,
                            FT_MulFix( SUB_LONG( worker->orus[i].x, orus1 ),
                                       scale ) );
            }
            worker->curs[i].x = x;
          }
        }
      }
    
    
      /**************************************************************************
       *
       * IUP[a]:       Interpolate Untouched Points
       * Opcode range: 0x30-0x31
       * Stack:        -->
       */
      static void
      Ins_IUP( TT_ExecContext  exc )
      {
        IUP_WorkerRec  V;
        FT_Byte        mask;
    
        FT_UInt   first_point;   /* first point of contour        */
        FT_UInt   end_point;     /* end point (last+1) of contour */
    
        FT_UInt   first_touched; /* first touched point in contour   */
        FT_UInt   cur_touched;   /* current touched point in contour */
    
        FT_UInt   point;         /* current point   */
        FT_Short  contour;       /* current contour */
    
    
    #ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL
        /* See `ttinterp.h' for details on backward compatibility mode.  */
        /* Allow IUP until it has been called on both axes.  Immediately */
        /* return on subsequent ones.                                    */
        if ( SUBPIXEL_HINTING_MINIMAL    &&
             exc->backward_compatibility )
        {
          if ( exc->iupx_called && exc->iupy_called )
            return;
    
          if ( exc->opcode & 1 )
            exc->iupx_called = TRUE;
          else
            exc->iupy_called = TRUE;
        }
    #endif
    
        /* ignore empty outlines */
        if ( exc->pts.n_contours == 0 )
          return;
    
        if ( exc->opcode & 1 )
        {
          mask   = FT_CURVE_TAG_TOUCH_X;
          V.orgs = exc->pts.org;
          V.curs = exc->pts.cur;
          V.orus = exc->pts.orus;
        }
        else
        {
          mask   = FT_CURVE_TAG_TOUCH_Y;
          V.orgs = (FT_Vector*)( (FT_Pos*)exc->pts.org + 1 );
          V.curs = (FT_Vector*)( (FT_Pos*)exc->pts.cur + 1 );
          V.orus = (FT_Vector*)( (FT_Pos*)exc->pts.orus + 1 );
        }
        V.max_points = exc->pts.n_points;
    
        contour = 0;
        point   = 0;
    
    #ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY
        if ( SUBPIXEL_HINTING_INFINALITY &&
             exc->ignore_x_mode          )
        {
          exc->iup_called = TRUE;
          if ( exc->sph_tweak_flags & SPH_TWEAK_SKIP_IUP )
            return;
        }
    #endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */
    
        do
        {
          end_point   = exc->pts.contours[contour] - exc->pts.first_point;
          first_point = point;
    
          if ( BOUNDS( end_point, exc->pts.n_points ) )
            end_point = exc->pts.n_points - 1;
    
          while ( point <= end_point && ( exc->pts.tags[point] & mask ) == 0 )
            point++;
    
          if ( point <= end_point )
          {
            first_touched = point;
            cur_touched   = point;
    
            point++;
    
            while ( point <= end_point )
            {
              if ( ( exc->pts.tags[point] & mask ) != 0 )
              {
                _iup_worker_interpolate( &V,
                                         cur_touched + 1,
                                         point - 1,
                                         cur_touched,
                                         point );
                cur_touched = point;
              }
    
              point++;
            }
    
            if ( cur_touched == first_touched )
              _iup_worker_shift( &V, first_point, end_point, cur_touched );
            else
            {
              _iup_worker_interpolate( &V,
                                       (FT_UShort)( cur_touched + 1 ),
                                       end_point,
                                       cur_touched,
                                       first_touched );
    
              if ( first_touched > 0 )
                _iup_worker_interpolate( &V,
                                         first_point,
                                         first_touched - 1,
                                         cur_touched,
                                         first_touched );
            }
          }
          contour++;
        } while ( contour < exc->pts.n_contours );
      }
    
    
      /**************************************************************************
       *
       * DELTAPn[]:    DELTA exceptions P1, P2, P3
       * Opcode range: 0x5D,0x71,0x72
       * Stack:        uint32 (2 * uint32)... -->
       */
      static void
      Ins_DELTAP( TT_ExecContext  exc,
                  FT_Long*        args )
      {
        FT_ULong   nump, k;
        FT_UShort  A;
        FT_ULong   C, P;
        FT_Long    B;
    #ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY
        FT_UShort  B1, B2;
    
    
        if ( SUBPIXEL_HINTING_INFINALITY                              &&
             exc->ignore_x_mode                                       &&
             exc->iup_called                                          &&
             ( exc->sph_tweak_flags & SPH_TWEAK_NO_DELTAP_AFTER_IUP ) )
          goto Fail;
    #endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */
    
        P    = (FT_ULong)exc->func_cur_ppem( exc );
        nump = (FT_ULong)args[0];   /* some points theoretically may occur more
                                       than once, thus UShort isn't enough */
    
        for ( k = 1; k <= nump; k++ )
        {
          if ( exc->args < 2 )
          {
            if ( exc->pedantic_hinting )
              exc->error = FT_THROW( Too_Few_Arguments );
            exc->args = 0;
            goto Fail;
          }
    
          exc->args -= 2;
    
          A = (FT_UShort)exc->stack[exc->args + 1];
          B = exc->stack[exc->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, exc->zp0.n_points ) )
          {
            C = ( (FT_ULong)B & 0xF0 ) >> 4;
    
            switch ( exc->opcode )
            {
            case 0x5D:
              break;
    
            case 0x71:
              C += 16;
              break;
    
            case 0x72:
              C += 32;
              break;
            }
    
            C += exc->GS.delta_base;
    
            if ( P == C )
            {
              B = ( (FT_ULong)B & 0xF ) - 8;
              if ( B >= 0 )
                B++;
              B *= 1L << ( 6 - exc->GS.delta_shift );
    
    #ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY
    
              if ( SUBPIXEL_HINTING_INFINALITY )
              {
                /*
                 * Allow delta move if
                 *
                 * - not using ignore_x_mode rendering,
                 * - glyph is specifically set to allow it, or
                 * - glyph is composite and freedom vector is not in subpixel
                 *   direction.
                 */
                if ( !exc->ignore_x_mode                                   ||
                     ( exc->sph_tweak_flags & SPH_TWEAK_ALWAYS_DO_DELTAP ) ||
                     ( exc->is_composite && exc->GS.freeVector.y != 0 )    )
                  exc->func_move( exc, &exc->zp0, A, B );
    
                /* Otherwise, apply subpixel hinting and compatibility mode */
                /* rules, always skipping deltas in subpixel direction.     */
                else if ( exc->ignore_x_mode && exc->GS.freeVector.y != 0 )
                {
                  /* save the y value of the point now; compare after move */
                  B1 = (FT_UShort)exc->zp0.cur[A].y;
    
                  /* Standard subpixel hinting: Allow y move for y-touched */
                  /* points.  This messes up DejaVu ...                    */
                  if ( !exc->face->sph_compatibility_mode          &&
                       ( exc->zp0.tags[A] & FT_CURVE_TAG_TOUCH_Y ) )
                    exc->func_move( exc, &exc->zp0, A, B );
    
                  /* compatibility mode */
                  else if ( exc->face->sph_compatibility_mode                        &&
                            !( exc->sph_tweak_flags & SPH_TWEAK_ALWAYS_SKIP_DELTAP ) )
                  {
                    if ( exc->sph_tweak_flags & SPH_TWEAK_ROUND_NONPIXEL_Y_MOVES )
                      B = FT_PIX_ROUND( B1 + B ) - B1;
    
                    /* Allow delta move if using sph_compatibility_mode,   */
                    /* IUP has not been called, and point is touched on Y. */
                    if ( !exc->iup_called                            &&
                         ( exc->zp0.tags[A] & FT_CURVE_TAG_TOUCH_Y ) )
                      exc->func_move( exc, &exc->zp0, A, B );
                  }
    
                  B2 = (FT_UShort)exc->zp0.cur[A].y;
    
                  /* Reverse this move if it results in a disallowed move */
                  if ( exc->GS.freeVector.y != 0                          &&
                       ( ( exc->face->sph_compatibility_mode          &&
                           ( B1 & 63 ) == 0                           &&
                           ( B2 & 63 ) != 0                           ) ||
                         ( ( exc->sph_tweak_flags                   &
                             SPH_TWEAK_SKIP_NONPIXEL_Y_MOVES_DELTAP ) &&
                           ( B1 & 63 ) != 0                           &&
                           ( B2 & 63 ) != 0                           ) ) )
                    exc->func_move( exc, &exc->zp0, A, NEG_LONG( B ) );
                }
              }
              else
    #endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */
    
              {
    
    #ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL
                /* See `ttinterp.h' for details on backward compatibility */
                /* mode.                                                  */
                if ( SUBPIXEL_HINTING_MINIMAL    &&
                     exc->backward_compatibility )
                {
                  if ( !( exc->iupx_called && exc->iupy_called )              &&
                       ( ( exc->is_composite && exc->GS.freeVector.y != 0 ) ||
                         ( exc->zp0.tags[A] & FT_CURVE_TAG_TOUCH_Y )        ) )
                    exc->func_move( exc, &exc->zp0, A, B );
                }
                else
    #endif
                  exc->func_move( exc, &exc->zp0, A, B );
              }
            }
          }
          else
            if ( exc->pedantic_hinting )
              exc->error = FT_THROW( Invalid_Reference );
        }
    
      Fail:
        exc->new_top = exc->args;
      }
    
    
      /**************************************************************************
       *
       * DELTACn[]:    DELTA exceptions C1, C2, C3
       * Opcode range: 0x73,0x74,0x75
       * Stack:        uint32 (2 * uint32)... -->
       */
      static void
      Ins_DELTAC( TT_ExecContext  exc,
                  FT_Long*        args )
      {
        FT_ULong  nump, k;
        FT_ULong  A, C, P;
        FT_Long   B;
    
    
        P    = (FT_ULong)exc->func_cur_ppem( exc );
        nump = (FT_ULong)args[0];
    
        for ( k = 1; k <= nump; k++ )
        {
          if ( exc->args < 2 )
          {
            if ( exc->pedantic_hinting )
              exc->error = FT_THROW( Too_Few_Arguments );
            exc->args = 0;
            goto Fail;
          }
    
          exc->args -= 2;
    
          A = (FT_ULong)exc->stack[exc->args + 1];
          B = exc->stack[exc->args];
    
          if ( BOUNDSL( A, exc->cvtSize ) )
          {
            if ( exc->pedantic_hinting )
            {
              exc->error = FT_THROW( Invalid_Reference );
              return;
            }
          }
          else
          {
            C = ( (FT_ULong)B & 0xF0 ) >> 4;
    
            switch ( exc->opcode )
            {
            case 0x73:
              break;
    
            case 0x74:
              C += 16;
              break;
    
            case 0x75:
              C += 32;
              break;
            }
    
            C += exc->GS.delta_base;
    
            if ( P == C )
            {
              B = ( (FT_ULong)B & 0xF ) - 8;
              if ( B >= 0 )
                B++;
              B *= 1L << ( 6 - exc->GS.delta_shift );
    
              exc->func_move_cvt( exc, A, B );
            }
          }
        }
    
      Fail:
        exc->new_top = exc->args;
      }
    
    
      /**************************************************************************
       *
       * MISC. INSTRUCTIONS
       *
       */
    
    
      /**************************************************************************
       *
       * GETINFO[]:    GET INFOrmation
       * Opcode range: 0x88
       * Stack:        uint32 --> uint32
       *
       * XXX: UNDOCUMENTED: Selector bits higher than 9 are currently (May
       *      2015) not documented in the OpenType specification.
       *
       *      Selector bit 11 is incorrectly described as bit 8, while the
       *      real meaning of bit 8 (vertical LCD subpixels) stays
       *      undocumented.  The same mistake can be found in Greg Hitchcock's
       *      whitepaper.
       */
      static void
      Ins_GETINFO( TT_ExecContext  exc,
                   FT_Long*        args )
      {
        FT_Long    K;
        TT_Driver  driver = (TT_Driver)FT_FACE_DRIVER( exc->face );
    
    
        K = 0;
    
    #ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY
        /*********************************
         * RASTERIZER VERSION
         * Selector Bit:  0
         * Return Bit(s): 0-7
         */
        if ( SUBPIXEL_HINTING_INFINALITY &&
             ( args[0] & 1 ) != 0        &&
             exc->subpixel_hinting       )
        {
          if ( exc->ignore_x_mode )
          {
            /* if in ClearType backward compatibility mode,         */
            /* we sometimes change the TrueType version dynamically */
            K = exc->rasterizer_version;
            FT_TRACE6(( "Setting rasterizer version %d\n",
                        exc->rasterizer_version ));
          }
          else
            K = TT_INTERPRETER_VERSION_38;
        }
        else
    #endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */
          if ( ( args[0] & 1 ) != 0 )
            K = driver->interpreter_version;
    
        /*********************************
         * GLYPH ROTATED
         * Selector Bit:  1
         * Return Bit(s): 8
         */
        if ( ( args[0] & 2 ) != 0 && exc->tt_metrics.rotated )
          K |= 1 << 8;
    
        /*********************************
         * GLYPH STRETCHED
         * Selector Bit:  2
         * Return Bit(s): 9
         */
        if ( ( args[0] & 4 ) != 0 && exc->tt_metrics.stretched )
          K |= 1 << 9;
    
    #ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
        /*********************************
         * VARIATION GLYPH
         * Selector Bit:  3
         * Return Bit(s): 10
         *
         * XXX: UNDOCUMENTED!
         */
        if ( (args[0] & 8 ) != 0 && exc->face->blend )
          K |= 1 << 10;
    #endif
    
        /*********************************
         * BI-LEVEL HINTING AND
         * GRAYSCALE RENDERING
         * Selector Bit:  5
         * Return Bit(s): 12
         */
        if ( ( args[0] & 32 ) != 0 && exc->grayscale )
          K |= 1 << 12;
    
    #ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL
        /* Toggle the following flags only outside of monochrome mode.      */
        /* Otherwise, instructions may behave weirdly and rendering results */
        /* may differ between v35 and v40 mode, e.g., in `Times New Roman   */
        /* Bold Italic'. */
        if ( SUBPIXEL_HINTING_MINIMAL && exc->subpixel_hinting_lean )
        {
          /*********************************
           * HINTING FOR SUBPIXEL
           * Selector Bit:  6
           * Return Bit(s): 13
           *
           * v40 does subpixel hinting by default.
           */
          if ( ( args[0] & 64 ) != 0 )
            K |= 1 << 13;
    
          /*********************************
           * VERTICAL LCD SUBPIXELS?
           * Selector Bit:  8
           * Return Bit(s): 15
           */
          if ( ( args[0] & 256 ) != 0 && exc->vertical_lcd_lean )
            K |= 1 << 15;
    
          /*********************************
           * SUBPIXEL POSITIONED?
           * Selector Bit:  10
           * Return Bit(s): 17
           *
           * XXX: FreeType supports it, dependent on what client does?
           */
          if ( ( args[0] & 1024 ) != 0 )
            K |= 1 << 17;
    
          /*********************************
           * SYMMETRICAL SMOOTHING
           * Selector Bit:  11
           * Return Bit(s): 18
           *
           * The only smoothing method FreeType supports unless someone sets
           * FT_LOAD_TARGET_MONO.
           */
          if ( ( args[0] & 2048 ) != 0 && exc->subpixel_hinting_lean )
            K |= 1 << 18;
    
          /*********************************
           * CLEARTYPE HINTING AND
           * GRAYSCALE RENDERING
           * Selector Bit:  12
           * Return Bit(s): 19
           *
           * Grayscale rendering is what FreeType does anyway unless someone
           * sets FT_LOAD_TARGET_MONO or FT_LOAD_TARGET_LCD(_V)
           */
          if ( ( args[0] & 4096 ) != 0 && exc->grayscale_cleartype )
            K |= 1 << 19;
        }
    #endif
    
    #ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY
    
        if ( SUBPIXEL_HINTING_INFINALITY                          &&
             exc->rasterizer_version >= TT_INTERPRETER_VERSION_35 )
        {
    
          if ( exc->rasterizer_version >= 37 )
          {
            /*********************************
             * HINTING FOR SUBPIXEL
             * Selector Bit:  6
             * Return Bit(s): 13
             */
            if ( ( args[0] & 64 ) != 0 && exc->subpixel_hinting )
              K |= 1 << 13;
    
            /*********************************
             * COMPATIBLE WIDTHS ENABLED
             * Selector Bit:  7
             * Return Bit(s): 14
             *
             * Functionality still needs to be added
             */
            if ( ( args[0] & 128 ) != 0 && exc->compatible_widths )
              K |= 1 << 14;
    
            /*********************************
             * VERTICAL LCD SUBPIXELS?
             * Selector Bit:  8
             * Return Bit(s): 15
             *
             * Functionality still needs to be added
             */
            if ( ( args[0] & 256 ) != 0 && exc->vertical_lcd )
              K |= 1 << 15;
    
            /*********************************
             * HINTING FOR BGR?
             * Selector Bit:  9
             * Return Bit(s): 16
             *
             * Functionality still needs to be added
             */
            if ( ( args[0] & 512 ) != 0 && exc->bgr )
              K |= 1 << 16;
    
            if ( exc->rasterizer_version >= 38 )
            {
              /*********************************
               * SUBPIXEL POSITIONED?
               * Selector Bit:  10
               * Return Bit(s): 17
               *
               * Functionality still needs to be added
               */
              if ( ( args[0] & 1024 ) != 0 && exc->subpixel_positioned )
                K |= 1 << 17;
    
              /*********************************
               * SYMMETRICAL SMOOTHING
               * Selector Bit:  11
               * Return Bit(s): 18
               *
               * Functionality still needs to be added
               */
              if ( ( args[0] & 2048 ) != 0 && exc->symmetrical_smoothing )
                K |= 1 << 18;
    
              /*********************************
               * GRAY CLEARTYPE
               * Selector Bit:  12
               * Return Bit(s): 19
               *
               * Functionality still needs to be added
               */
              if ( ( args[0] & 4096 ) != 0 && exc->gray_cleartype )
                K |= 1 << 19;
            }
          }
        }
    
    #endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */
    
        args[0] = K;
      }
    
    
    #ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
    
      /**************************************************************************
       *
       * GETVARIATION[]: get normalized variation (blend) coordinates
       * Opcode range: 0x91
       * Stack:        --> f2.14...
       *
       * XXX: UNDOCUMENTED!  There is no official documentation from Apple for
       *      this bytecode instruction.  Active only if a font has GX
       *      variation axes.
       */
      static void
      Ins_GETVARIATION( TT_ExecContext  exc,
                        FT_Long*        args )
      {
        FT_UInt    num_axes = exc->face->blend->num_axis;
        FT_Fixed*  coords   = exc->face->blend->normalizedcoords;
    
        FT_UInt  i;
    
    
        if ( BOUNDS( num_axes, exc->stackSize + 1 - exc->top ) )
        {
          exc->error = FT_THROW( Stack_Overflow );
          return;
        }
    
        if ( coords )
        {
          for ( i = 0; i < num_axes; i++ )
            args[i] = coords[i] >> 2; /* convert 16.16 to 2.14 format */
        }
        else
        {
          for ( i = 0; i < num_axes; i++ )
            args[i] = 0;
        }
      }
    
    
      /**************************************************************************
       *
       * GETDATA[]:    no idea what this is good for
       * Opcode range: 0x92
       * Stack:        --> 17
       *
       * XXX: UNDOCUMENTED!  There is no documentation from Apple for this
       *      very weird bytecode instruction.
       */
      static void
      Ins_GETDATA( FT_Long*  args )
      {
        args[0] = 17;
      }
    
    #endif /* TT_CONFIG_OPTION_GX_VAR_SUPPORT */
    
    
      static void
      Ins_UNKNOWN( TT_ExecContext  exc )
      {
        TT_DefRecord*  def   = exc->IDefs;
        TT_DefRecord*  limit = FT_OFFSET( def, exc->numIDefs );
    
    
        for ( ; def < limit; def++ )
        {
          if ( (FT_Byte)def->opc == exc->opcode && def->active )
          {
            TT_CallRec*  call;
    
    
            if ( exc->callTop >= exc->callSize )
            {
              exc->error = FT_THROW( Stack_Overflow );
              return;
            }
    
            call = exc->callStack + exc->callTop++;
    
            call->Caller_Range = exc->curRange;
            call->Caller_IP    = exc->IP + 1;
            call->Cur_Count    = 1;
            call->Def          = def;
    
            Ins_Goto_CodeRange( exc, def->range, def->start );
    
            exc->step_ins = FALSE;
            return;
          }
        }
    
        exc->error = FT_THROW( Invalid_Opcode );
      }
    
    
      /**************************************************************************
       *
       * 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 with TRUE, test IP < CodeSize to know whether it comes from
       * an 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.
       *
       */
    
    
      /* documentation is in ttinterp.h */
    
      FT_EXPORT_DEF( FT_Error )
      TT_RunIns( TT_ExecContext  exc )
      {
        FT_ULong   ins_counter = 0;  /* executed instructions counter */
        FT_ULong   num_twilight_points;
        FT_UShort  i;
    
    #ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY
        FT_Byte    opcode_pattern[1][2] = {
                      /* #8 TypeMan Talk Align */
                      {
                        0x06, /* SPVTL   */
                        0x7D, /* RDTG    */
                      },
                    };
        FT_UShort  opcode_patterns   = 1;
        FT_UShort  opcode_pointer[1] = { 0 };
        FT_UShort  opcode_size[1]    = { 1 };
    #endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */
    
    
    #ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY
        exc->iup_called = FALSE;
    #endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */
    
    #ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL
        /*
         * Toggle backward compatibility according to what font wants, except
         * when
         *
         * 1) we have a `tricky' font that heavily relies on the interpreter to
         *    render glyphs correctly, for example DFKai-SB, or
         * 2) FT_RENDER_MODE_MONO (i.e, monochome rendering) is requested.
         *
         * In those cases, backward compatibility needs to be turned off to get
         * correct rendering.  The rendering is then completely up to the
         * font's programming.
         *
         */
        if ( SUBPIXEL_HINTING_MINIMAL          &&
             exc->subpixel_hinting_lean        &&
             !FT_IS_TRICKY( &exc->face->root ) )
          exc->backward_compatibility = !( exc->GS.instruct_control & 4 );
        else
          exc->backward_compatibility = FALSE;
    
        exc->iupx_called = FALSE;
        exc->iupy_called = FALSE;
    #endif
    
        /* We restrict the number of twilight points to a reasonable,     */
        /* heuristic value to avoid slow execution of malformed bytecode. */
        num_twilight_points = FT_MAX( 30,
                                      2 * ( exc->pts.n_points + exc->cvtSize ) );
        if ( exc->twilight.n_points > num_twilight_points )
        {
          if ( num_twilight_points > 0xFFFFU )
            num_twilight_points = 0xFFFFU;
    
          FT_TRACE5(( "TT_RunIns: Resetting number of twilight points\n"
                      "           from %d to the more reasonable value %d\n",
                      exc->twilight.n_points,
                      num_twilight_points ));
          exc->twilight.n_points = (FT_UShort)num_twilight_points;
        }
    
        /* Set up loop detectors.  We restrict the number of LOOPCALL loops */
        /* and the number of JMPR, JROT, and JROF calls with a negative     */
        /* argument to values that depend on various parameters like the    */
        /* size of the CVT table or the number of points in the current     */
        /* glyph (if applicable).                                           */
        /*                                                                  */
        /* The idea is that in real-world bytecode you either iterate over  */
        /* all CVT entries (in the `prep' table), or over all points (or    */
        /* contours, in the `glyf' table) of a glyph, and such iterations   */
        /* don't happen very often.                                         */
        exc->loopcall_counter = 0;
        exc->neg_jump_counter = 0;
    
        /* The maximum values are heuristic. */
        if ( exc->pts.n_points )
          exc->loopcall_counter_max = FT_MAX( 50,
                                              10 * exc->pts.n_points ) +
                                      FT_MAX( 50,
                                              exc->cvtSize / 10 );
        else
          exc->loopcall_counter_max = 300 + 22 * exc->cvtSize;
    
        /* as a protection against an unreasonable number of CVT entries  */
        /* we assume at most 100 control values per glyph for the counter */
        if ( exc->loopcall_counter_max >
             100 * (FT_ULong)exc->face->root.num_glyphs )
          exc->loopcall_counter_max = 100 * (FT_ULong)exc->face->root.num_glyphs;
    
        FT_TRACE5(( "TT_RunIns: Limiting total number of loops in LOOPCALL"
                    " to %d\n", exc->loopcall_counter_max ));
    
        exc->neg_jump_counter_max = exc->loopcall_counter_max;
        FT_TRACE5(( "TT_RunIns: Limiting total number of backward jumps"
                    " to %d\n", exc->neg_jump_counter_max ));
    
        /* set PPEM and CVT functions */
        exc->tt_metrics.ratio = 0;
        if ( exc->metrics.x_ppem != exc->metrics.y_ppem )
        {
          /* non-square pixels, use the stretched routines */
          exc->func_cur_ppem  = Current_Ppem_Stretched;
          exc->func_read_cvt  = Read_CVT_Stretched;
          exc->func_write_cvt = Write_CVT_Stretched;
          exc->func_move_cvt  = Move_CVT_Stretched;
        }
        else
        {
          /* square pixels, use normal routines */
          exc->func_cur_ppem  = Current_Ppem;
          exc->func_read_cvt  = Read_CVT;
          exc->func_write_cvt = Write_CVT;
          exc->func_move_cvt  = Move_CVT;
        }
    
        Compute_Funcs( exc );
        Compute_Round( exc, (FT_Byte)exc->GS.round_state );
    
        do
        {
          exc->opcode = exc->code[exc->IP];
    
    #ifdef FT_DEBUG_LEVEL_TRACE
          {
            FT_Long  cnt = FT_MIN( 8, exc->top );
            FT_Long  n;
    
    
            /* if tracing level is 7, show current code position */
            /* and the first few stack elements also             */
            FT_TRACE6(( "  " ));
            FT_TRACE7(( "%06d ", exc->IP ));
            FT_TRACE6(( "%s", opcode_name[exc->opcode] + 2 ));
            FT_TRACE7(( "%*s", *opcode_name[exc->opcode] == 'A'
                                  ? 2
                                  : 12 - ( *opcode_name[exc->opcode] - '0' ),
                                  "#" ));
            for ( n = 1; n <= cnt; n++ )
              FT_TRACE7(( " %d", exc->stack[exc->top - n] ));
            FT_TRACE6(( "\n" ));
          }
    #endif /* FT_DEBUG_LEVEL_TRACE */
    
          if ( ( exc->length = opcode_length[exc->opcode] ) < 0 )
          {
            if ( exc->IP + 1 >= exc->codeSize )
              goto LErrorCodeOverflow_;
    
            exc->length = 2 - exc->length * exc->code[exc->IP + 1];
          }
    
          if ( exc->IP + exc->length > exc->codeSize )
            goto LErrorCodeOverflow_;
    
          /* First, let's check for empty stack and overflow */
          exc->args = exc->top - ( Pop_Push_Count[exc->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 ( exc->args < 0 )
          {
            if ( exc->pedantic_hinting )
            {
              exc->error = FT_THROW( Too_Few_Arguments );
              goto LErrorLabel_;
            }
    
            /* push zeroes onto the stack */
            for ( i = 0; i < Pop_Push_Count[exc->opcode] >> 4; i++ )
              exc->stack[i] = 0;
            exc->args = 0;
          }
    
    #ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
          if ( exc->opcode == 0x91 )
          {
            /* this is very special: GETVARIATION returns */
            /* a variable number of arguments             */
    
            /* it is the job of the application to `activate' GX handling, */
            /* this is, calling any of the GX API functions on the current */
            /* font to select a variation instance                         */
            if ( exc->face->blend )
              exc->new_top = exc->args + exc->face->blend->num_axis;
          }
          else
    #endif
            exc->new_top = exc->args + ( Pop_Push_Count[exc->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 ( exc->new_top > exc->stackSize )
          {
            exc->error = FT_THROW( Stack_Overflow );
            goto LErrorLabel_;
          }
    
          exc->step_ins = TRUE;
          exc->error    = FT_Err_Ok;
    
    #ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY
    
          if ( SUBPIXEL_HINTING_INFINALITY )
          {
            for ( i = 0; i < opcode_patterns; i++ )
            {
              if ( opcode_pointer[i] < opcode_size[i]                  &&
                   exc->opcode == opcode_pattern[i][opcode_pointer[i]] )
              {
                opcode_pointer[i] += 1;
    
                if ( opcode_pointer[i] == opcode_size[i] )
                {
                  FT_TRACE6(( "sph: opcode ptrn: %d, %s %s\n",
                              i,
                              exc->face->root.family_name,
                              exc->face->root.style_name ));
    
                  switch ( i )
                  {
                  case 0:
                    break;
                  }
                  opcode_pointer[i] = 0;
                }
              }
              else
                opcode_pointer[i] = 0;
            }
          }
    
    #endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */
    
          {
            FT_Long*  args   = exc->stack + exc->args;
            FT_Byte   opcode = exc->opcode;
    
    
            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 */
              Ins_SxyTCA( exc );
              break;
    
            case 0x06:  /* SPvTL // */
            case 0x07:  /* SPvTL +  */
              Ins_SPVTL( exc, args );
              break;
    
            case 0x08:  /* SFvTL // */
            case 0x09:  /* SFvTL +  */
              Ins_SFVTL( exc, args );
              break;
    
            case 0x0A:  /* SPvFS */
              Ins_SPVFS( exc, args );
              break;
    
            case 0x0B:  /* SFvFS */
              Ins_SFVFS( exc, args );
              break;
    
            case 0x0C:  /* GPv */
              Ins_GPV( exc, args );
              break;
    
            case 0x0D:  /* GFv */
              Ins_GFV( exc, args );
              break;
    
            case 0x0E:  /* SFvTPv */
              Ins_SFVTPV( exc );
              break;
    
            case 0x0F:  /* ISECT  */
              Ins_ISECT( exc, args );
              break;
    
            case 0x10:  /* SRP0 */
              Ins_SRP0( exc, args );
              break;
    
            case 0x11:  /* SRP1 */
              Ins_SRP1( exc, args );
              break;
    
            case 0x12:  /* SRP2 */
              Ins_SRP2( exc, args );
              break;
    
            case 0x13:  /* SZP0 */
              Ins_SZP0( exc, args );
              break;
    
            case 0x14:  /* SZP1 */
              Ins_SZP1( exc, args );
              break;
    
            case 0x15:  /* SZP2 */
              Ins_SZP2( exc, args );
              break;
    
            case 0x16:  /* SZPS */
              Ins_SZPS( exc, args );
              break;
    
            case 0x17:  /* SLOOP */
              Ins_SLOOP( exc, args );
              break;
    
            case 0x18:  /* RTG */
              Ins_RTG( exc );
              break;
    
            case 0x19:  /* RTHG */
              Ins_RTHG( exc );
              break;
    
            case 0x1A:  /* SMD */
              Ins_SMD( exc, args );
              break;
    
            case 0x1B:  /* ELSE */
              Ins_ELSE( exc );
              break;
    
            case 0x1C:  /* JMPR */
              Ins_JMPR( exc, args );
              break;
    
            case 0x1D:  /* SCVTCI */
              Ins_SCVTCI( exc, args );
              break;
    
            case 0x1E:  /* SSWCI */
              Ins_SSWCI( exc, args );
              break;
    
            case 0x1F:  /* SSW */
              Ins_SSW( exc, args );
              break;
    
            case 0x20:  /* DUP */
              Ins_DUP( args );
              break;
    
            case 0x21:  /* POP */
              Ins_POP();
              break;
    
            case 0x22:  /* CLEAR */
              Ins_CLEAR( exc );
              break;
    
            case 0x23:  /* SWAP */
              Ins_SWAP( args );
              break;
    
            case 0x24:  /* DEPTH */
              Ins_DEPTH( exc, args );
              break;
    
            case 0x25:  /* CINDEX */
              Ins_CINDEX( exc, args );
              break;
    
            case 0x26:  /* MINDEX */
              Ins_MINDEX( exc, args );
              break;
    
            case 0x27:  /* ALIGNPTS */
              Ins_ALIGNPTS( exc, args );
              break;
    
            case 0x28:  /* RAW */
              Ins_UNKNOWN( exc );
              break;
    
            case 0x29:  /* UTP */
              Ins_UTP( exc, args );
              break;
    
            case 0x2A:  /* LOOPCALL */
              Ins_LOOPCALL( exc, args );
              break;
    
            case 0x2B:  /* CALL */
              Ins_CALL( exc, args );
              break;
    
            case 0x2C:  /* FDEF */
              Ins_FDEF( exc, args );
              break;
    
            case 0x2D:  /* ENDF */
              Ins_ENDF( exc );
              break;
    
            case 0x2E:  /* MDAP */
            case 0x2F:  /* MDAP */
              Ins_MDAP( exc, args );
              break;
    
            case 0x30:  /* IUP */
            case 0x31:  /* IUP */
              Ins_IUP( exc );
              break;
    
            case 0x32:  /* SHP */
            case 0x33:  /* SHP */
              Ins_SHP( exc );
              break;
    
            case 0x34:  /* SHC */
            case 0x35:  /* SHC */
              Ins_SHC( exc, args );
              break;
    
            case 0x36:  /* SHZ */
            case 0x37:  /* SHZ */
              Ins_SHZ( exc, args );
              break;
    
            case 0x38:  /* SHPIX */
              Ins_SHPIX( exc, args );
              break;
    
            case 0x39:  /* IP    */
              Ins_IP( exc );
              break;
    
            case 0x3A:  /* MSIRP */
            case 0x3B:  /* MSIRP */
              Ins_MSIRP( exc, args );
              break;
    
            case 0x3C:  /* AlignRP */
              Ins_ALIGNRP( exc );
              break;
    
            case 0x3D:  /* RTDG */
              Ins_RTDG( exc );
              break;
    
            case 0x3E:  /* MIAP */
            case 0x3F:  /* MIAP */
              Ins_MIAP( exc, args );
              break;
    
            case 0x40:  /* NPUSHB */
              Ins_NPUSHB( exc, args );
              break;
    
            case 0x41:  /* NPUSHW */
              Ins_NPUSHW( exc, args );
              break;
    
            case 0x42:  /* WS */
              Ins_WS( exc, args );
              break;
    
            case 0x43:  /* RS */
              Ins_RS( exc, args );
              break;
    
            case 0x44:  /* WCVTP */
              Ins_WCVTP( exc, args );
              break;
    
            case 0x45:  /* RCVT */
              Ins_RCVT( exc, args );
              break;
    
            case 0x46:  /* GC */
            case 0x47:  /* GC */
              Ins_GC( exc, args );
              break;
    
            case 0x48:  /* SCFS */
              Ins_SCFS( exc, args );
              break;
    
            case 0x49:  /* MD */
            case 0x4A:  /* MD */
              Ins_MD( exc, args );
              break;
    
            case 0x4B:  /* MPPEM */
              Ins_MPPEM( exc, args );
              break;
    
            case 0x4C:  /* MPS */
              Ins_MPS( exc, args );
              break;
    
            case 0x4D:  /* FLIPON */
              Ins_FLIPON( exc );
              break;
    
            case 0x4E:  /* FLIPOFF */
              Ins_FLIPOFF( exc );
              break;
    
            case 0x4F:  /* DEBUG */
              Ins_DEBUG( exc );
              break;
    
            case 0x50:  /* LT */
              Ins_LT( args );
              break;
    
            case 0x51:  /* LTEQ */
              Ins_LTEQ( args );
              break;
    
            case 0x52:  /* GT */
              Ins_GT( args );
              break;
    
            case 0x53:  /* GTEQ */
              Ins_GTEQ( args );
              break;
    
            case 0x54:  /* EQ */
              Ins_EQ( args );
              break;
    
            case 0x55:  /* NEQ */
              Ins_NEQ( args );
              break;
    
            case 0x56:  /* ODD */
              Ins_ODD( exc, args );
              break;
    
            case 0x57:  /* EVEN */
              Ins_EVEN( exc, args );
              break;
    
            case 0x58:  /* IF */
              Ins_IF( exc, args );
              break;
    
            case 0x59:  /* EIF */
              Ins_EIF();
              break;
    
            case 0x5A:  /* AND */
              Ins_AND( args );
              break;
    
            case 0x5B:  /* OR */
              Ins_OR( args );
              break;
    
            case 0x5C:  /* NOT */
              Ins_NOT( args );
              break;
    
            case 0x5D:  /* DELTAP1 */
              Ins_DELTAP( exc, args );
              break;
    
            case 0x5E:  /* SDB */
              Ins_SDB( exc, args );
              break;
    
            case 0x5F:  /* SDS */
              Ins_SDS( exc, args );
              break;
    
            case 0x60:  /* ADD */
              Ins_ADD( args );
              break;
    
            case 0x61:  /* SUB */
              Ins_SUB( args );
              break;
    
            case 0x62:  /* DIV */
              Ins_DIV( exc, args );
              break;
    
            case 0x63:  /* MUL */
              Ins_MUL( args );
              break;
    
            case 0x64:  /* ABS */
              Ins_ABS( args );
              break;
    
            case 0x65:  /* NEG */
              Ins_NEG( args );
              break;
    
            case 0x66:  /* FLOOR */
              Ins_FLOOR( args );
              break;
    
            case 0x67:  /* CEILING */
              Ins_CEILING( args );
              break;
    
            case 0x68:  /* ROUND */
            case 0x69:  /* ROUND */
            case 0x6A:  /* ROUND */
            case 0x6B:  /* ROUND */
              Ins_ROUND( exc, args );
              break;
    
            case 0x6C:  /* NROUND */
            case 0x6D:  /* NROUND */
            case 0x6E:  /* NRRUND */
            case 0x6F:  /* NROUND */
              Ins_NROUND( exc, args );
              break;
    
            case 0x70:  /* WCVTF */
              Ins_WCVTF( exc, args );
              break;
    
            case 0x71:  /* DELTAP2 */
            case 0x72:  /* DELTAP3 */
              Ins_DELTAP( exc, args );
              break;
    
            case 0x73:  /* DELTAC0 */
            case 0x74:  /* DELTAC1 */
            case 0x75:  /* DELTAC2 */
              Ins_DELTAC( exc, args );
              break;
    
            case 0x76:  /* SROUND */
              Ins_SROUND( exc, args );
              break;
    
            case 0x77:  /* S45Round */
              Ins_S45ROUND( exc, args );
              break;
    
            case 0x78:  /* JROT */
              Ins_JROT( exc, args );
              break;
    
            case 0x79:  /* JROF */
              Ins_JROF( exc, args );
              break;
    
            case 0x7A:  /* ROFF */
              Ins_ROFF( exc );
              break;
    
            case 0x7B:  /* ???? */
              Ins_UNKNOWN( exc );
              break;
    
            case 0x7C:  /* RUTG */
              Ins_RUTG( exc );
              break;
    
            case 0x7D:  /* RDTG */
              Ins_RDTG( exc );
              break;
    
            case 0x7E:  /* SANGW */
              Ins_SANGW();
              break;
    
            case 0x7F:  /* AA */
              Ins_AA();
              break;
    
            case 0x80:  /* FLIPPT */
              Ins_FLIPPT( exc );
              break;
    
            case 0x81:  /* FLIPRGON */
              Ins_FLIPRGON( exc, args );
              break;
    
            case 0x82:  /* FLIPRGOFF */
              Ins_FLIPRGOFF( exc, args );
              break;
    
            case 0x83:  /* UNKNOWN */
            case 0x84:  /* UNKNOWN */
              Ins_UNKNOWN( exc );
              break;
    
            case 0x85:  /* SCANCTRL */
              Ins_SCANCTRL( exc, args );
              break;
    
            case 0x86:  /* SDPvTL */
            case 0x87:  /* SDPvTL */
              Ins_SDPVTL( exc, args );
              break;
    
            case 0x88:  /* GETINFO */
              Ins_GETINFO( exc, args );
              break;
    
            case 0x89:  /* IDEF */
              Ins_IDEF( exc, args );
              break;
    
            case 0x8A:  /* ROLL */
              Ins_ROLL( args );
              break;
    
            case 0x8B:  /* MAX */
              Ins_MAX( args );
              break;
    
            case 0x8C:  /* MIN */
              Ins_MIN( args );
              break;
    
            case 0x8D:  /* SCANTYPE */
              Ins_SCANTYPE( exc, args );
              break;
    
            case 0x8E:  /* INSTCTRL */
              Ins_INSTCTRL( exc, args );
              break;
    
            case 0x8F:  /* ADJUST */
            case 0x90:  /* ADJUST */
              Ins_UNKNOWN( exc );
              break;
    
    #ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
            case 0x91:
              /* it is the job of the application to `activate' GX handling, */
              /* this is, calling any of the GX API functions on the current */
              /* font to select a variation instance                         */
              if ( exc->face->blend )
                Ins_GETVARIATION( exc, args );
              else
                Ins_UNKNOWN( exc );
              break;
    
            case 0x92:
              /* there is at least one MS font (LaoUI.ttf version 5.01) that */
              /* uses IDEFs for 0x91 and 0x92; for this reason we activate   */
              /* GETDATA for GX fonts only, similar to GETVARIATION          */
              if ( exc->face->blend )
                Ins_GETDATA( args );
              else
                Ins_UNKNOWN( exc );
              break;
    #endif
    
            default:
              if ( opcode >= 0xE0 )
                Ins_MIRP( exc, args );
              else if ( opcode >= 0xC0 )
                Ins_MDRP( exc, args );
              else if ( opcode >= 0xB8 )
                Ins_PUSHW( exc, args );
              else if ( opcode >= 0xB0 )
                Ins_PUSHB( exc, args );
              else
                Ins_UNKNOWN( exc );
            }
          }
    
          if ( exc->error )
          {
            switch ( exc->error )
            {
              /* looking for redefined instructions */
            case FT_ERR( Invalid_Opcode ):
              {
                TT_DefRecord*  def   = exc->IDefs;
                TT_DefRecord*  limit = FT_OFFSET( def, exc->numIDefs );
    
    
                for ( ; def < limit; def++ )
                {
                  if ( def->active && exc->opcode == (FT_Byte)def->opc )
                  {
                    TT_CallRec*  callrec;
    
    
                    if ( exc->callTop >= exc->callSize )
                    {
                      exc->error = FT_THROW( Invalid_Reference );
                      goto LErrorLabel_;
                    }
    
                    callrec = &exc->callStack[exc->callTop];
    
                    callrec->Caller_Range = exc->curRange;
                    callrec->Caller_IP    = exc->IP + 1;
                    callrec->Cur_Count    = 1;
                    callrec->Def          = def;
    
                    if ( Ins_Goto_CodeRange( exc,
                                             def->range,
                                             def->start ) == FAILURE )
                      goto LErrorLabel_;
    
                    goto LSuiteLabel_;
                  }
                }
              }
    
              exc->error = FT_THROW( Invalid_Opcode );
              goto LErrorLabel_;
    
    #if 0
              break;   /* Unreachable code warning suppression.             */
                       /* Leave to remind in case a later change the editor */
                       /* to consider break;                                */
    #endif
    
            default:
              goto LErrorLabel_;
    
    #if 0
            break;
    #endif
            }
          }
    
          exc->top = exc->new_top;
    
          if ( exc->step_ins )
            exc->IP += exc->length;
    
          /* increment instruction counter and check if we didn't */
          /* run this program for too long (e.g. infinite loops). */
          if ( ++ins_counter > TT_CONFIG_OPTION_MAX_RUNNABLE_OPCODES )
            return FT_THROW( Execution_Too_Long );
    
        LSuiteLabel_:
          if ( exc->IP >= exc->codeSize )
          {
            if ( exc->callTop > 0 )
            {
              exc->error = FT_THROW( Code_Overflow );
              goto LErrorLabel_;
            }
            else
              goto LNo_Error_;
          }
        } while ( !exc->instruction_trap );
    
      LNo_Error_:
        FT_TRACE4(( "  %d instruction%s executed\n",
                    ins_counter,
                    ins_counter == 1 ? "" : "s" ));
        return FT_Err_Ok;
    
      LErrorCodeOverflow_:
        exc->error = FT_THROW( Code_Overflow );
    
      LErrorLabel_:
        if ( exc->error && !exc->instruction_trap )
          FT_TRACE1(( "  The interpreter returned error 0x%x\n", exc->error ));
    
        return exc->error;
      }
    
    #else /* !TT_USE_BYTECODE_INTERPRETER */
    
      /* ANSI C doesn't like empty source files */
      typedef int  _tt_interp_dummy;
    
    #endif /* !TT_USE_BYTECODE_INTERPRETER */
    
    
    /* END */