mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-05-02 02:23:44 +02:00
4972 lines
141 KiB
ObjectPascal
4972 lines
141 KiB
ObjectPascal
(*******************************************************************
|
||
*
|
||
* TTInterp.pas 2.1
|
||
*
|
||
* TrueType bytecode intepreter.
|
||
*
|
||
* Copyright 1996 David Turner, Robert Wilhelm and Werner Lemberg
|
||
*
|
||
* This file is part of the FreeType project, and may only be used
|
||
* modified and distributed under the terms of the FreeType project
|
||
* license, LICENSE.TXT. By continuing to use, modify or distribute
|
||
* this file you indicate that you have read the license and
|
||
* understand and accept it fully.
|
||
*
|
||
* Changes between 2.1 and 2.0 :
|
||
*
|
||
* - Moved into TInterpreter class
|
||
* - Multithreading should be possible
|
||
* - Error log
|
||
* - Dynamic stack size
|
||
*
|
||
* Changes between 2.0 and 1.2 :
|
||
*
|
||
* - Lots, lots, of changes : This version is not re-entrant,
|
||
* but much faster.
|
||
*
|
||
*
|
||
******************************************************************)
|
||
|
||
unit TTInterp;
|
||
|
||
interface
|
||
|
||
{$R-} // TODO: Fix out-of-bounds accesses.
|
||
{$mode Delphi}
|
||
|
||
uses TTTypes,
|
||
TTObjs;
|
||
|
||
function Run_Ins( exec : PExec_Context ; AErrorLog: boolean = false) : TError;
|
||
(* Run the interpreter with the current code range and IP *)
|
||
|
||
implementation
|
||
uses
|
||
TTCalc, SysUtils, Classes, TTMemory;
|
||
|
||
const
|
||
maxStackSizeAllowed = 16000;
|
||
|
||
const
|
||
TT_Round_Off = 5;
|
||
TT_Round_To_Half_Grid = 0;
|
||
TT_Round_To_Grid = 1;
|
||
TT_Round_To_Double_Grid = 2;
|
||
TT_Round_Up_To_Grid = 4;
|
||
TT_Round_Down_To_Grid = 3;
|
||
TT_Round_Super = 6;
|
||
TT_ROund_Super_45 = 7;
|
||
|
||
TT_Flag_Touched_X = $02; (* X touched flag *)
|
||
TT_Flag_Touched_Y = $04; (* Y touched flag *)
|
||
|
||
TT_Flag_Touched_Both = TT_Flag_Touched_X or TT_FLag_Touched_Y;
|
||
|
||
type
|
||
TInstruction_Function = procedure( args : PStorage ) of object;
|
||
|
||
const
|
||
Null_Vector : TT_Vector = (x:0;y:0);
|
||
|
||
type
|
||
|
||
{ TInterpreter }
|
||
|
||
TInterpreter = class
|
||
private
|
||
pEC : PExec_Context;
|
||
opcode : Byte; (* current opcode *)
|
||
oplength : Int; (* length of current opcode *)
|
||
opargs : Int; (* number of arguments in opcode *)
|
||
|
||
top : Int; (* top of instance stack *)
|
||
new_top : Int; (* new stack top after opc. exec *)
|
||
callTop : Int; (* top of call stack *)
|
||
|
||
enableLog: boolean;
|
||
instructionLog: TStringList;
|
||
|
||
Instruct_Dispatch : array[0..255] of record
|
||
name: string;
|
||
func: TInstruction_Function;
|
||
end;
|
||
function GetLastInstruction: string;
|
||
public
|
||
constructor Create(AContext: PExec_Context; AEnableLog: boolean);
|
||
destructor Destroy; override;
|
||
function Run: TError;
|
||
property Context: PExec_Context read pEC;
|
||
property LastInstruction: string read GetLastInstruction;
|
||
|
||
private
|
||
function NeedStackSize(AValue: integer): TError; overload;
|
||
function NeedStackSize(AValue: integer; var APointerInStack : PStorage): TError; overload;
|
||
function Calc_Length: boolean;
|
||
procedure Compute_Funcs;
|
||
function Compute_Point_Displacement(out x: TT_F26dot6; out y: TT_F26dot6;
|
||
out zone: PGlyph_Zone; out refp: Int): TError;
|
||
procedure Compute_Round(round_mode: Byte);
|
||
procedure Direct_Move(zone: PGlyph_Zone; point: Int; distance: TT_F26dot6);
|
||
procedure Direct_Move_X(zone: PGlyph_Zone; point: Int; distance: TT_F26dot6
|
||
);
|
||
procedure Direct_Move_Y(zone: PGlyph_Zone; point: Int; distance: TT_F26dot6
|
||
);
|
||
function Dual_Project(var P1, P2: TT_Vector): TT_F26dot6;
|
||
function Free_Project(var P1, P2: TT_Vector): TT_F26dot6;
|
||
function GetShort: Short;
|
||
function Get_Current_Ratio: Long;
|
||
function Get_Ppem: Long;
|
||
function Goto_CodeRange(aRange, aIP: Int): boolean;
|
||
procedure Ins_AA({%H-}args: PStorage);
|
||
procedure Ins_ABS(args: PStorage);
|
||
procedure Ins_ADD(args: PStorage);
|
||
procedure Ins_ALIGNPTS(args: PStorage);
|
||
procedure Ins_ALIGNRP({%H-}args: PStorage);
|
||
procedure Ins_AND(args: PStorage);
|
||
procedure Ins_CALL(args: PStorage);
|
||
procedure Ins_CEILING(args: PStorage);
|
||
procedure Ins_CINDEX(args: PStorage);
|
||
procedure Ins_CLEAR({%H-}args: PStorage);
|
||
procedure Ins_DEBUG({%H-}args: PStorage);
|
||
procedure Ins_DELTAC(args: PStorage);
|
||
procedure Ins_DELTAP(args: PStorage);
|
||
procedure Ins_DEPTH(args: PStorage);
|
||
procedure Ins_DIV(args: PStorage);
|
||
procedure Ins_DUP(args: PStorage);
|
||
procedure Ins_EIF({%H-}args: PStorage);
|
||
procedure Ins_ELSE({%H-}args: PStorage);
|
||
procedure Ins_ENDF({%H-}args: PStorage);
|
||
procedure Ins_EQ(args: PStorage);
|
||
procedure Ins_EVEN(args: PStorage);
|
||
procedure Ins_FDEF(args: PStorage);
|
||
procedure Ins_FLIPOFF({%H-}args: PStorage);
|
||
procedure Ins_FLIPON({%H-}args: PStorage);
|
||
procedure Ins_FLIPPT({%H-}args: PStorage);
|
||
procedure Ins_FLIPRGOFF(args: PStorage);
|
||
procedure Ins_FLIPRGON(args: PStorage);
|
||
procedure Ins_FLOOR(args: PStorage);
|
||
procedure Ins_GC(args: PStorage);
|
||
procedure Ins_GETINFO(args: PStorage);
|
||
procedure Ins_GFV(args: PStorage);
|
||
procedure Ins_GPV(args: PStorage);
|
||
procedure Ins_GT(args: PStorage);
|
||
procedure Ins_GTEQ(args: PStorage);
|
||
procedure Ins_IDEF(args: PStorage);
|
||
procedure Ins_IF(args: PStorage);
|
||
procedure Ins_INSTCTRL(args: PStorage);
|
||
procedure Ins_IP({%H-}args: PStorage);
|
||
procedure Ins_ISECT(args: PStorage);
|
||
procedure Ins_IUP({%H-}args: PStorage);
|
||
procedure Ins_JMPR(args: PStorage);
|
||
procedure Ins_JROF(args: PStorage);
|
||
procedure Ins_JROT(args: PStorage);
|
||
procedure Ins_LOOPCALL(args: PStorage);
|
||
procedure Ins_LT(args: PStorage);
|
||
procedure Ins_LTEQ(args: PStorage);
|
||
procedure Ins_MAX(args: PStorage);
|
||
procedure Ins_MD(args: PStorage);
|
||
procedure Ins_MDAP(args: PStorage);
|
||
procedure Ins_MDRP(args: PStorage);
|
||
procedure Ins_MIAP(args: PStorage);
|
||
procedure Ins_MIN(args: PStorage);
|
||
procedure Ins_MINDEX(args: PStorage);
|
||
procedure Ins_MIRP(args: PStorage);
|
||
procedure Ins_MPPEM(args: PStorage);
|
||
procedure Ins_MPS(args: PStorage);
|
||
procedure Ins_MSIRP(args: PStorage);
|
||
procedure Ins_MUL(args: PStorage);
|
||
procedure Ins_NEG(args: PStorage);
|
||
procedure Ins_NEQ(args: PStorage);
|
||
procedure Ins_NOT(args: PStorage);
|
||
procedure Ins_NPUSHB(args: PStorage);
|
||
procedure Ins_NPUSHW(args: PStorage);
|
||
procedure Ins_NROUND(args: PStorage);
|
||
procedure Ins_ODD(args: PStorage);
|
||
procedure Ins_OR(args: PStorage);
|
||
procedure Ins_POP({%H-}args: PStorage);
|
||
procedure Ins_PUSHB(args: PStorage);
|
||
procedure Ins_PUSHW(args: PStorage);
|
||
procedure Ins_RCVT(args: PStorage);
|
||
procedure Ins_RDTG({%H-}args: PStorage);
|
||
procedure Ins_ROFF({%H-}args: PStorage);
|
||
procedure Ins_ROLL(args: PStorage);
|
||
procedure Ins_ROUND(args: PStorage);
|
||
procedure Ins_RS(args: PStorage);
|
||
procedure Ins_RTDG({%H-}args: PStorage);
|
||
procedure Ins_RTG({%H-}args: PStorage);
|
||
procedure Ins_RTHG({%H-}args: PStorage);
|
||
procedure Ins_RUTG({%H-}args: PStorage);
|
||
procedure Ins_S45ROUND(args: PStorage);
|
||
procedure Ins_SANGW({%H-}args: PStorage);
|
||
procedure Ins_SCANCTRL(args: PStorage);
|
||
procedure Ins_SCANTYPE(args: PStorage);
|
||
procedure Ins_SCFS(args: PStorage);
|
||
procedure Ins_SCVTCI(args: PStorage);
|
||
procedure Ins_SDB(args: PStorage);
|
||
procedure Ins_SDPVTL(args: PStorage);
|
||
procedure Ins_SDS(args: PStorage);
|
||
procedure Ins_SFVFS(args: PStorage);
|
||
procedure Ins_SFVTCA({%H-}args: PStorage);
|
||
procedure Ins_SFVTL(args: PStorage);
|
||
procedure Ins_SFVTPV({%H-}args: PStorage);
|
||
procedure Ins_SHC(args: PStorage);
|
||
procedure Ins_SHP({%H-}args: PStorage);
|
||
procedure Ins_SHPIX(args: PStorage);
|
||
procedure Ins_SHZ(args: PStorage);
|
||
procedure Ins_SLOOP(args: PStorage);
|
||
procedure Ins_SMD(args: PStorage);
|
||
procedure Ins_SPVFS(args: PStorage);
|
||
procedure Ins_SPVTCA({%H-}args: PStorage);
|
||
procedure Ins_SPVTL(args: PStorage);
|
||
procedure Ins_SROUND(args: PStorage);
|
||
procedure Ins_SRP0(args: PStorage);
|
||
procedure Ins_SRP1(args: PStorage);
|
||
procedure Ins_SRP2(args: PStorage);
|
||
procedure Ins_SSW(args: PStorage);
|
||
procedure Ins_SSWCI(args: PStorage);
|
||
procedure Ins_SUB(args: PStorage);
|
||
procedure Ins_SVTCA({%H-}args: PStorage);
|
||
procedure Ins_SWAP(args: PStorage);
|
||
function Ins_SxVTL(aIdx1: Int; aIdx2: Int; aOpc: Int; var Vec: TT_UnitVector
|
||
): boolean;
|
||
procedure Ins_SZP0(args: PStorage);
|
||
procedure Ins_SZP1(args: PStorage);
|
||
procedure Ins_SZP2(args: PStorage);
|
||
procedure Ins_SZPS(args: PStorage);
|
||
procedure Ins_UNKNOWN({%H-}args: PStorage);
|
||
procedure Ins_UTP(args: PStorage);
|
||
procedure Ins_WCVTF(args: PStorage);
|
||
procedure Ins_WCVTP(args: PStorage);
|
||
procedure Ins_WS(args: PStorage);
|
||
procedure Move_CVT(index: Int; value: TT_F26Dot6);
|
||
procedure Move_CVT_Stretched(index: Int; value: TT_F26dot6);
|
||
procedure Move_Zp2_Point(point: Int; dx: TT_F26dot6; dy: TT_F26dot6);
|
||
function Norm(X, Y: TT_F26dot6): TT_F26dot6;
|
||
function Normalize(U, V: TT_F26dot6; var R: TT_UnitVector): boolean;
|
||
function Project(var P1, P2: TT_Vector): TT_F26dot6;
|
||
function Project_x(var P1, P2: TT_Vector): TT_F26dot6;
|
||
function Project_y(var P1, P2: TT_Vector): TT_F26dot6;
|
||
function Read_CVT(index: Int): TT_F26Dot6;
|
||
function Read_CVT_Stretched(index: Int): TT_F26Dot6;
|
||
function Round_Down_To_Grid(distance: TT_F26dot6; compensation: TT_F26dot6
|
||
): TT_F26dot6;
|
||
function Round_None(distance: TT_F26dot6; compensation: TT_F26dot6
|
||
): TT_F26dot6;
|
||
function Round_Super(distance: TT_F26dot6; compensation: TT_F26dot6
|
||
): TT_F26dot6;
|
||
function Round_Super_45(distance: TT_F26dot6; compensation: TT_F26dot6
|
||
): TT_F26dot6;
|
||
function Round_To_Double_Grid(distance: TT_F26dot6; compensation: TT_F26dot6
|
||
): TT_F26dot6;
|
||
function Round_To_Grid(distance: TT_F26dot6; compensation: TT_F26dot6
|
||
): TT_F26dot6;
|
||
function Round_To_Half_Grid(distance: TT_F26dot6; compensation: TT_F26dot6
|
||
): TT_F26dot6;
|
||
function Round_Up_To_Grid(distance: TT_F26dot6; compensation: TT_F26dot6
|
||
): TT_F26dot6;
|
||
function Scale_Pixels(value: long): TT_F26Dot6;
|
||
procedure SetSuperRound(GridPeriod: TT_F26dot6; selector: Long);
|
||
function SkipCode: boolean;
|
||
procedure Write_CVT(index: Int; value: TT_F26Dot6);
|
||
procedure Write_CVT_Stretched(index: Int; value: TT_F26Dot6);
|
||
end;
|
||
|
||
const
|
||
|
||
(*********************************************************************)
|
||
(* *)
|
||
(* 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. *)
|
||
(* *)
|
||
(* Note that for opcodes with a varying numbre of parameters, *)
|
||
(* either 0 or 1 arg is verified before execution, depending *)
|
||
(* on the nature of the instruction : *)
|
||
(* *)
|
||
(* - if the number of arguments is given by the bytecode *)
|
||
(* stream or the loop variable, 0 is chosen. *)
|
||
(* *)
|
||
(* - if the first argument is a count n that is followed *)
|
||
(* by arguments a1..an, then 1 is chosen. *)
|
||
(* *)
|
||
(*********************************************************************)
|
||
|
||
Pop_Push_Count : array[0..511] of byte
|
||
= (
|
||
(* SVTCA y *) 0, 0,
|
||
(* SVTCA x *) 0, 0,
|
||
(* SPvTCA y *) 0, 0,
|
||
(* SPvTCA x *) 0, 0,
|
||
(* SFvTCA y *) 0, 0,
|
||
(* SFvTCA x *) 0, 0,
|
||
(* SPvTL // *) 2, 0,
|
||
(* SPvTL + *) 2, 0,
|
||
(* SFvTL // *) 2, 0,
|
||
(* SFvTL + *) 2, 0,
|
||
(* SPvFS *) 2, 0,
|
||
(* SFvFS *) 2, 0,
|
||
(* GPV *) 0, 2,
|
||
(* GFV *) 0, 2,
|
||
(* SFvTPv *) 0, 0,
|
||
(* ISECT *) 5, 0,
|
||
|
||
(* SRP0 *) 1, 0,
|
||
(* SRP1 *) 1, 0,
|
||
(* SRP2 *) 1, 0,
|
||
(* SZP0 *) 1, 0,
|
||
(* SZP1 *) 1, 0,
|
||
(* SZP2 *) 1, 0,
|
||
(* SZPS *) 1, 0,
|
||
(* SLOOP *) 1, 0,
|
||
(* RTG *) 0, 0,
|
||
(* RTHG *) 0, 0,
|
||
(* SMD *) 1, 0,
|
||
(* ELSE *) 0, 0,
|
||
(* JMPR *) 1, 0,
|
||
(* SCvTCi *) 1, 0,
|
||
(* SSwCi *) 1, 0,
|
||
(* SSW *) 1, 0,
|
||
|
||
(* DUP *) 1, 2,
|
||
(* POP *) 1, 0,
|
||
(* CLEAR *) 0, 0,
|
||
(* SWAP *) 2, 2,
|
||
(* DEPTH *) 0, 1,
|
||
(* CINDEX *) 1, 1,
|
||
(* MINDEX *) 1, 0, (* first arg *)
|
||
(* AlignPTS *) 2, 0,
|
||
(* INS_$28 *) 0, 0,
|
||
(* UTP *) 1, 0,
|
||
(* LOOPCALL *) 2, 0,
|
||
(* CALL *) 1, 0,
|
||
(* FDEF *) 1, 0,
|
||
(* ENDF *) 0, 0,
|
||
(* MDAP[0] *) 1, 0,
|
||
(* MDAP[1] *) 1, 0,
|
||
|
||
(* IUP[0] *) 0, 0,
|
||
(* IUP[1] *) 0, 0,
|
||
(* SHP[0] *) 0, 0, (* no args *)
|
||
(* SHP[1] *) 0, 0, (* no args *)
|
||
(* SHC[0] *) 1, 0,
|
||
(* SHC[1] *) 1, 0,
|
||
(* SHZ[0] *) 1, 0,
|
||
(* SHZ[1] *) 1, 0,
|
||
(* SHPIX *) 1, 0, (* first arg *)
|
||
(* IP *) 0, 0, (* no args *)
|
||
(* MSIRP[0] *) 2, 0,
|
||
(* MSIRP[1] *) 2, 0,
|
||
(* AlignRP *) 0, 0, (* no args *)
|
||
(* RTDG *) 0, 0,
|
||
(* MIAP[0] *) 2, 0,
|
||
(* MIAP[1] *) 2, 0,
|
||
|
||
(* NPushB *) 0, 0,
|
||
(* NPushW *) 0, 0,
|
||
(* WS *) 2, 0,
|
||
(* RS *) 1, 1,
|
||
(* WCvtP *) 2, 0,
|
||
(* RCvt *) 1, 1,
|
||
(* GC[0] *) 1, 1,
|
||
(* GC[1] *) 1, 1,
|
||
(* SCFS *) 2, 0,
|
||
(* MD[0] *) 2, 1,
|
||
(* MD[1] *) 2, 1,
|
||
(* MPPEM *) 0, 1,
|
||
(* MPS *) 0, 1,
|
||
(* FlipON *) 0, 0,
|
||
(* FlipOFF *) 0, 0,
|
||
(* DEBUG *) 1, 0,
|
||
|
||
(* LT *) 2, 1,
|
||
(* LTEQ *) 2, 1,
|
||
(* GT *) 2, 1,
|
||
(* GTEQ *) 2, 1,
|
||
(* EQ *) 2, 1,
|
||
(* NEQ *) 2, 1,
|
||
(* ODD *) 1, 1,
|
||
(* EVEN *) 1, 1,
|
||
(* IF *) 1, 0,
|
||
(* EIF *) 0, 0,
|
||
(* AND *) 2, 1,
|
||
(* OR *) 2, 1,
|
||
(* NOT *) 1, 1,
|
||
(* DeltaP1 *) 1, 0, (* first arg *)
|
||
(* SDB *) 1, 0,
|
||
(* SDS *) 1, 0,
|
||
|
||
(* ADD *) 2, 1,
|
||
(* SUB *) 2, 1,
|
||
(* DIV *) 2, 1,
|
||
(* MUL *) 2, 1,
|
||
(* ABS *) 1, 1,
|
||
(* NEG *) 1, 1,
|
||
(* FLOOR *) 1, 1,
|
||
(* CEILING *) 1, 1,
|
||
(* ROUND[0] *) 1, 1,
|
||
(* ROUND[1] *) 1, 1,
|
||
(* ROUND[2] *) 1, 1,
|
||
(* ROUND[3] *) 1, 1,
|
||
(* NROUND[0]*) 1, 1,
|
||
(* NROUND[1]*) 1, 1,
|
||
(* NROUND[2]*) 1, 1,
|
||
(* NROUND[3]*) 1, 1,
|
||
|
||
(* WCvtF *) 2, 0,
|
||
(* DeltaP2 *) 1, 0, (* first arg *)
|
||
(* DeltaP3 *) 1, 0, (* first arg *)
|
||
(* DeltaCn[0]*) 1, 0, (* first arg *)
|
||
(* DeltaCn[1]*) 1, 0, (* first arg *)
|
||
(* DeltaCn[2]*) 1, 0, (* first arg *)
|
||
(* SROUND *) 1, 0,
|
||
(* S45Round *) 1, 0,
|
||
(* JROT *) 2, 0,
|
||
(* JROF *) 2, 0,
|
||
(* ROFF *) 0, 0,
|
||
(* INS_$7B *) 0, 0,
|
||
(* RUTG *) 0, 0,
|
||
(* RDTG *) 0, 0,
|
||
(* SANGW *) 1, 0,
|
||
(* AA *) 1, 0,
|
||
|
||
(* FlipPT *) 0, 0, (* no args *)
|
||
(* FlipRgON *) 2, 0,
|
||
(* FlipRgOFF*) 2, 0,
|
||
(* INS_$83 *) 0, 0,
|
||
(* INS_$84 *) 0, 0,
|
||
(* ScanCTRL *) 1, 0,
|
||
(* SDVPTL[0]*) 2, 0,
|
||
(* SDVPTL[1]*) 2, 0,
|
||
(* GetINFO *) 1, 1,
|
||
(* IDEF *) 1, 0,
|
||
(* ROLL *) 3, 3, (* pops 3 args/push 3 args *)
|
||
(* MAX *) 2, 1,
|
||
(* MIN *) 2, 1,
|
||
(* ScanTYPE *) 1, 0,
|
||
(* InstCTRL *) 2, 0,
|
||
(* INS_$8F *) 0, 0,
|
||
|
||
(* INS_$90 *) 0, 0,
|
||
(* INS_$91 *) 0, 0,
|
||
(* INS_$92 *) 0, 0,
|
||
(* INS_$93 *) 0, 0,
|
||
(* INS_$94 *) 0, 0,
|
||
(* INS_$95 *) 0, 0,
|
||
(* INS_$96 *) 0, 0,
|
||
(* INS_$97 *) 0, 0,
|
||
(* INS_$98 *) 0, 0,
|
||
(* INS_$99 *) 0, 0,
|
||
(* INS_$9A *) 0, 0,
|
||
(* INS_$9B *) 0, 0,
|
||
(* INS_$9C *) 0, 0,
|
||
(* INS_$9D *) 0, 0,
|
||
(* INS_$9E *) 0, 0,
|
||
(* INS_$9F *) 0, 0,
|
||
|
||
(* INS_$A0 *) 0, 0,
|
||
(* INS_$A1 *) 0, 0,
|
||
(* INS_$A2 *) 0, 0,
|
||
(* INS_$A3 *) 0, 0,
|
||
(* INS_$A4 *) 0, 0,
|
||
(* INS_$A5 *) 0, 0,
|
||
(* INS_$A6 *) 0, 0,
|
||
(* INS_$A7 *) 0, 0,
|
||
(* INS_$A8 *) 0, 0,
|
||
(* INS_$A9 *) 0, 0,
|
||
(* INS_$AA *) 0, 0,
|
||
(* INS_$AB *) 0, 0,
|
||
(* INS_$AC *) 0, 0,
|
||
(* INS_$AD *) 0, 0,
|
||
(* INS_$AE *) 0, 0,
|
||
(* INS_$AF *) 0, 0,
|
||
|
||
(* PushB[0] *) 0, 1,
|
||
(* PushB[1] *) 0, 2,
|
||
(* PushB[2] *) 0, 3,
|
||
(* PushB[3] *) 0, 4,
|
||
(* PushB[4] *) 0, 5,
|
||
(* PushB[5] *) 0, 6,
|
||
(* PushB[6] *) 0, 7,
|
||
(* PushB[7] *) 0, 8,
|
||
(* PushW[0] *) 0, 1,
|
||
(* PushW[1] *) 0, 2,
|
||
(* PushW[2] *) 0, 3,
|
||
(* PushW[3] *) 0, 4,
|
||
(* PushW[4] *) 0, 5,
|
||
(* PushW[5] *) 0, 6,
|
||
(* PushW[6] *) 0, 7,
|
||
(* PushW[7] *) 0, 8,
|
||
|
||
(* MDRP[00] *) 1, 0,
|
||
(* MDRP[01] *) 1, 0,
|
||
(* MDRP[02] *) 1, 0,
|
||
(* MDRP[03] *) 1, 0,
|
||
(* MDRP[04] *) 1, 0,
|
||
(* MDRP[05] *) 1, 0,
|
||
(* MDRP[06] *) 1, 0,
|
||
(* MDRP[07] *) 1, 0,
|
||
(* MDRP[08] *) 1, 0,
|
||
(* MDRP[09] *) 1, 0,
|
||
(* MDRP[10] *) 1, 0,
|
||
(* MDRP[11] *) 1, 0,
|
||
(* MDRP[12] *) 1, 0,
|
||
(* MDRP[13] *) 1, 0,
|
||
(* MDRP[14] *) 1, 0,
|
||
(* MDRP[15] *) 1, 0,
|
||
(* MDRP[16] *) 1, 0,
|
||
(* MDRP[17] *) 1, 0,
|
||
|
||
(* MDRP[18] *) 1, 0,
|
||
(* MDRP[19] *) 1, 0,
|
||
(* MDRP[20] *) 1, 0,
|
||
(* MDRP[21] *) 1, 0,
|
||
(* MDRP[22] *) 1, 0,
|
||
(* MDRP[23] *) 1, 0,
|
||
(* MDRP[24] *) 1, 0,
|
||
(* MDRP[25] *) 1, 0,
|
||
(* MDRP[26] *) 1, 0,
|
||
(* MDRP[27] *) 1, 0,
|
||
(* MDRP[28] *) 1, 0,
|
||
(* MDRP[29] *) 1, 0,
|
||
(* MDRP[30] *) 1, 0,
|
||
(* MDRP[31] *) 1, 0,
|
||
|
||
(* MIRP[00] *) 2, 0,
|
||
(* MIRP[01] *) 2, 0,
|
||
(* MIRP[02] *) 2, 0,
|
||
(* MIRP[03] *) 2, 0,
|
||
(* MIRP[04] *) 2, 0,
|
||
(* MIRP[05] *) 2, 0,
|
||
(* MIRP[06] *) 2, 0,
|
||
(* MIRP[07] *) 2, 0,
|
||
(* MIRP[08] *) 2, 0,
|
||
(* MIRP[09] *) 2, 0,
|
||
(* MIRP[10] *) 2, 0,
|
||
(* MIRP[11] *) 2, 0,
|
||
(* MIRP[12] *) 2, 0,
|
||
(* MIRP[13] *) 2, 0,
|
||
(* MIRP[14] *) 2, 0,
|
||
(* MIRP[15] *) 2, 0,
|
||
(* MIRP[16] *) 2, 0,
|
||
(* MIRP[17] *) 2, 0,
|
||
|
||
(* MIRP[18] *) 2, 0,
|
||
(* MIRP[19] *) 2, 0,
|
||
(* MIRP[20] *) 2, 0,
|
||
(* MIRP[21] *) 2, 0,
|
||
(* MIRP[22] *) 2, 0,
|
||
(* MIRP[23] *) 2, 0,
|
||
(* MIRP[24] *) 2, 0,
|
||
(* MIRP[25] *) 2, 0,
|
||
(* MIRP[26] *) 2, 0,
|
||
(* MIRP[27] *) 2, 0,
|
||
(* MIRP[28] *) 2, 0,
|
||
(* MIRP[29] *) 2, 0,
|
||
(* MIRP[30] *) 2, 0,
|
||
(* MIRP[31] *) 2, 0
|
||
);
|
||
|
||
(*******************************************************************
|
||
*
|
||
* Function : Norm
|
||
*
|
||
* Description : returns the norm (length) of a vector
|
||
*
|
||
* Input : X, Y vector
|
||
*
|
||
* Output : returns length in F26dot6
|
||
*
|
||
*****************************************************************)
|
||
|
||
function TInterpreter.Norm( X, Y : TT_F26dot6 ): TT_F26dot6;
|
||
begin
|
||
result := sqrt64(int64(X)*int64(X)+int64(Y)*int64(Y));
|
||
end;
|
||
|
||
(*******************************************************************
|
||
*
|
||
* Function : Scale_Pixels
|
||
*
|
||
* Description : Converts from FUnits to Fractional pixels
|
||
* coordinates.
|
||
*
|
||
*****************************************************************)
|
||
|
||
function TInterpreter.Scale_Pixels( value : long ) : TT_F26Dot6;
|
||
{$IFDEF INLINE} inline; {$ENDIF}
|
||
begin
|
||
Scale_Pixels := MulDiv_Round( value,
|
||
pEC^.metrics.scale1,
|
||
pEC^.metrics.scale2 );
|
||
end;
|
||
|
||
function TInterpreter.Get_Current_Ratio : Long;
|
||
var
|
||
x, y : Long;
|
||
begin
|
||
if pEC^.metrics.ratio <> 0 then
|
||
Get_Current_Ratio := pEC^.metrics.ratio
|
||
else
|
||
begin
|
||
if pEC^.GS.projVector.y = 0 then
|
||
pEC^.metrics.ratio := pEC^.metrics.x_ratio
|
||
|
||
else if pEC^.GS.projVector.x = 0 then
|
||
pEC^.metrics.ratio := pEC^.metrics.y_ratio
|
||
|
||
else
|
||
begin
|
||
x := MulDiv_Round( pEC^.GS.projVector.x,
|
||
pEC^.metrics.x_ratio,
|
||
$4000 );
|
||
|
||
y := MulDiv_Round( pEC^.GS.projVector.y,
|
||
pEC^.metrics.y_ratio,
|
||
$4000 );
|
||
|
||
pEC^.metrics.ratio := Norm( x, y );
|
||
end;
|
||
|
||
Get_Current_Ratio := pEC^.metrics.ratio;
|
||
end
|
||
end;
|
||
|
||
function TInterpreter.Get_Ppem : Long;
|
||
{$IFDEF INLINE} inline; {$ENDIF}
|
||
begin
|
||
Get_Ppem := MulDiv_Round( pEC^.metrics.ppem, Get_Current_Ratio, $10000 );
|
||
end;
|
||
|
||
|
||
function TInterpreter.Read_CVT( index : Int ) : TT_F26Dot6;
|
||
begin
|
||
Read_CVT := pEC^.cvt^[index];
|
||
end;
|
||
|
||
function TInterpreter.Read_CVT_Stretched( index : Int ) : TT_F26Dot6;
|
||
begin
|
||
Read_CVT_Stretched := MulDiv_Round( pEC^.cvt^[index],
|
||
Get_Current_Ratio,
|
||
$10000 );
|
||
end;
|
||
|
||
|
||
procedure TInterpreter.Write_CVT( index : Int; value : TT_F26Dot6 );
|
||
begin
|
||
pEC^.cvt^[index] := value;
|
||
end;
|
||
|
||
procedure TInterpreter.Write_CVT_Stretched( index : Int; value : TT_F26Dot6 );
|
||
begin
|
||
pEC^.cvt^[index] := MulDiv_Round( value,
|
||
$10000,
|
||
Get_Current_Ratio );
|
||
end;
|
||
|
||
|
||
procedure TInterpreter.Move_CVT( index : Int; value : TT_F26Dot6 );
|
||
begin
|
||
inc( pEC^.cvt^[index], value );
|
||
end;
|
||
|
||
procedure TInterpreter.Move_CVT_Stretched( index : Int; value : TT_F26dot6 );
|
||
begin
|
||
inc( pEC^.cvt^[index], MulDiv_Round( value,
|
||
$10000,
|
||
Get_Current_Ratio ));
|
||
end;
|
||
|
||
(*******************************************************************
|
||
*
|
||
* Function : Calc_Length
|
||
*
|
||
* Description : Computes the length in bytes of current opcode
|
||
*
|
||
*****************************************************************)
|
||
|
||
function TInterpreter.Calc_Length : boolean;
|
||
begin
|
||
Calc_Length := false;
|
||
|
||
opcode := pEC^.Code^[pEC^.IP];
|
||
|
||
case opcode of
|
||
|
||
$40 : if pEC^.IP+1 >= pEC^.codeSize
|
||
then exit
|
||
else
|
||
oplength := pEC^.code^[pEC^.IP+1] + 2;
|
||
|
||
$41 : if pEC^.IP+1 >= pEC^.codeSize
|
||
then exit
|
||
else
|
||
oplength := pEC^.code^[pEC^.IP+1]*2 + 2;
|
||
|
||
$B0..$B7 : oplength := opcode-$B0 + 2;
|
||
$B8..$BF : oplength := (opcode-$B8)*2 + 3;
|
||
else
|
||
oplength := 1;
|
||
end;
|
||
|
||
Calc_Length := pEC^.IP+oplength <= pEC^.codeSize;
|
||
end;
|
||
|
||
(*******************************************************************
|
||
*
|
||
* Function : Get_Short
|
||
*
|
||
* Description : Return a short integer taken from the instruction
|
||
* stream at address IP.
|
||
*
|
||
* Input : None
|
||
*
|
||
* Output : Short read at Code^[IP..IP+1]
|
||
*
|
||
* Notes : This one could become a Macro in the C version
|
||
*
|
||
*****************************************************************)
|
||
|
||
function TInterpreter.GetShort : Short;
|
||
var
|
||
L1,L0 : Byte;
|
||
begin
|
||
L1 := pEC^.code^[pEC^.IP]; inc(pEC^.IP);
|
||
L0 := pEC^.code^[pEC^.IP]; inc(pEC^.IP);
|
||
if L1 >= 128 then
|
||
result := Short(-32768) + (Short(L1 and 127) shl 8) + L0
|
||
else
|
||
result := (L1 shl 8) + L0;
|
||
end;
|
||
|
||
|
||
function TInterpreter.Goto_CodeRange( aRange,
|
||
aIP : Int ): boolean;
|
||
begin
|
||
|
||
Goto_CodeRange := False;
|
||
|
||
with pEC^ do
|
||
begin
|
||
if (aRange<1) or (aRange>3) then
|
||
begin
|
||
pEC^.error := TT_Err_Bad_Argument;
|
||
exit;
|
||
end;
|
||
|
||
with CodeRangeTable[ARange] do
|
||
begin
|
||
|
||
if Base = nil then (* invalid coderange *)
|
||
begin
|
||
error := TT_Err_Invalid_Coderange;
|
||
exit;
|
||
end;
|
||
|
||
(* 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 > Size then
|
||
begin
|
||
error := TT_Err_Code_Overflow;
|
||
Goto_CodeRange := False;
|
||
exit;
|
||
end;
|
||
|
||
Code := PByte(Base);
|
||
CodeSize := Size;
|
||
IP := AIP;
|
||
end;
|
||
|
||
curRange := ARange;
|
||
end;
|
||
|
||
Goto_CodeRange := True;
|
||
end;
|
||
|
||
|
||
(*******************************************************************
|
||
*
|
||
* Function : Direct_Move
|
||
*
|
||
* Description : Moves a point by a given distance along the
|
||
* freedom vector.
|
||
*
|
||
* Input : Vx, Vy point coordinates to move
|
||
* touch touch flag to modify
|
||
* distance
|
||
*
|
||
* Output : None
|
||
*
|
||
*****************************************************************)
|
||
|
||
procedure TInterpreter.Direct_Move( zone : PGlyph_Zone;
|
||
point : Int;
|
||
distance : TT_F26dot6 );
|
||
var
|
||
v : TT_F26dot6;
|
||
begin
|
||
v := pEC^.GS.freeVector.x;
|
||
if v <> 0 then
|
||
begin
|
||
inc( zone^.cur^[point].x, MulDiv_Round( distance,
|
||
Long(v)*$10000,
|
||
pEC^.F_dot_P ));
|
||
|
||
zone^.flags^[point] := zone^.flags^[point] or TT_Flag_Touched_X;
|
||
end;
|
||
|
||
v := pEC^.GS.freeVector.y;
|
||
if v <> 0 then
|
||
begin
|
||
inc( zone^.cur^[point].y, MulDiv_Round( distance,
|
||
Long(v)*$10000,
|
||
pEC^.F_dot_P ));
|
||
|
||
zone^.flags^[point] := zone^.flags^[point] or TT_Flag_Touched_Y;
|
||
end;
|
||
end;
|
||
|
||
(* The following versions are used whenever both vectors are both *)
|
||
(* along one of the coordinate unit vectors, i.e. in 90% cases *)
|
||
|
||
procedure TInterpreter.Direct_Move_X( zone : PGlyph_Zone;
|
||
point : Int;
|
||
distance : TT_F26dot6 );
|
||
begin
|
||
inc( zone^.cur^[point].x, distance );
|
||
zone^.flags^[point] := zone^.flags^[point] or TT_Flag_Touched_X;
|
||
end;
|
||
|
||
procedure TInterpreter.Direct_Move_Y( zone : PGlyph_Zone;
|
||
point : Int;
|
||
distance : TT_F26dot6 );
|
||
begin
|
||
inc( zone^.cur^[point].y, distance );
|
||
zone^.flags^[point] := zone^.flags^[point] or TT_Flag_Touched_Y;
|
||
end;
|
||
|
||
(*******************************************************************
|
||
*
|
||
* Function : Round_None
|
||
*
|
||
* Description : Do not round, but add engine compensation
|
||
*
|
||
* Input : distance : distance to round
|
||
* compensation : engine compensation
|
||
*
|
||
* Output : rounded distance
|
||
*
|
||
* NOTE : The spec says very few about the relationship between
|
||
* rounding and engine compensation. However, it seems
|
||
* from the description of super round that we should
|
||
* should add the compensation before rounding
|
||
*
|
||
*****************************************************************)
|
||
|
||
function TInterpreter.Round_None( distance : TT_F26dot6;
|
||
compensation : TT_F26dot6 ) : TT_F26dot6;
|
||
var
|
||
val : TT_F26dot6;
|
||
begin
|
||
if distance >= 0 then
|
||
begin
|
||
val := distance + compensation;
|
||
if val < 0 then val := 0;
|
||
end
|
||
else
|
||
begin
|
||
val := distance - compensation;
|
||
if val > 0 then val := 0;
|
||
end;
|
||
|
||
Round_None := val;
|
||
end;
|
||
|
||
(*******************************************************************
|
||
*
|
||
* Function : Round_To_Grid
|
||
*
|
||
* Description : round value to grid after adding engine
|
||
* compensation
|
||
*
|
||
* Input : distance : distance to round
|
||
* compensation : engine compensation
|
||
*
|
||
* Output : rounded distance
|
||
*
|
||
*****************************************************************)
|
||
|
||
function TInterpreter.Round_To_Grid( distance : TT_F26dot6;
|
||
compensation : TT_F26dot6 ) : TT_F26dot6;
|
||
var
|
||
val : TT_F26dot6;
|
||
begin
|
||
if distance >= 0 then
|
||
begin
|
||
val := (distance + 32 + compensation) and -64;
|
||
if val < 0 then val := 0;
|
||
end
|
||
else
|
||
begin
|
||
val := - ((compensation - distance + 32) and -64);
|
||
if val > 0 then val := 0;
|
||
end;
|
||
|
||
Round_To_Grid := val;
|
||
end;
|
||
|
||
(*******************************************************************
|
||
*
|
||
* Function : Round_To_Half_Grid
|
||
*
|
||
* Description : round value to half grid after adding engine
|
||
* compensation
|
||
*
|
||
* Input : distance : distance to round
|
||
* compensation : engine compensation
|
||
*
|
||
* Output : rounded distance
|
||
*
|
||
*****************************************************************)
|
||
|
||
function TInterpreter.Round_To_Half_Grid( distance : TT_F26dot6;
|
||
compensation : TT_F26dot6 ) : TT_F26dot6;
|
||
var
|
||
val : TT_F26dot6;
|
||
begin
|
||
if distance >= 0 then
|
||
begin
|
||
val := (distance + compensation) and -64 + 32;
|
||
if val < 0 then val := 0;
|
||
end
|
||
else
|
||
begin
|
||
val := - ((-distance + compensation) and -64 + 32);
|
||
if val > 0 then val := 0;
|
||
end;
|
||
|
||
Round_To_Half_Grid := val;
|
||
end;
|
||
|
||
|
||
(*******************************************************************
|
||
*
|
||
* Function : Round_Down_To_Grid
|
||
*
|
||
* Description : round value down to grid after adding engine
|
||
* compensation
|
||
*
|
||
* Input : distance : distance to round
|
||
* compensation : engine compensation
|
||
*
|
||
* Output : rounded distance
|
||
*
|
||
*****************************************************************)
|
||
|
||
function TInterpreter.Round_Down_To_Grid( distance : TT_F26dot6;
|
||
compensation : TT_F26dot6 ) : TT_F26dot6;
|
||
var
|
||
val : TT_F26dot6;
|
||
begin
|
||
if distance >= 0 then
|
||
begin
|
||
val := (distance + compensation) and -64;
|
||
if val < 0 then val := 0;
|
||
end
|
||
else
|
||
begin
|
||
val := - ((-distance + compensation) and -64);
|
||
if val > 0 then val := 0;
|
||
end;
|
||
|
||
Round_Down_To_Grid := val;
|
||
end;
|
||
|
||
(*******************************************************************
|
||
*
|
||
* Function : Round_Up_To_Grid
|
||
*
|
||
* Description : round value up to grid after adding engine
|
||
* compensation
|
||
*
|
||
* Input : distance : distance to round
|
||
* compensation : engine compensation
|
||
*
|
||
* Output : rounded distance
|
||
*
|
||
*****************************************************************)
|
||
|
||
function TInterpreter.Round_Up_To_Grid( distance : TT_F26dot6;
|
||
compensation : TT_F26dot6 ) : TT_F26dot6;
|
||
var
|
||
val : TT_F26dot6;
|
||
begin
|
||
if distance >= 0 then
|
||
begin
|
||
val := (distance + 63 + compensation) and -64;
|
||
if val < 0 then val := 0;
|
||
end
|
||
else
|
||
begin
|
||
val := - ((-distance + 63 + compensation) and -64);
|
||
if val > 0 then val := 0;
|
||
end;
|
||
|
||
Round_Up_To_Grid := val;
|
||
end;
|
||
|
||
(*******************************************************************
|
||
*
|
||
* Function : Round_To_Double_Grid
|
||
*
|
||
* Description : round value to double grid after adding engine
|
||
* compensation
|
||
*
|
||
* Input : distance : distance to round
|
||
* compensation : engine compensation
|
||
*
|
||
* Output : rounded distance
|
||
*
|
||
*****************************************************************)
|
||
|
||
function TInterpreter.Round_To_Double_Grid( distance : TT_F26dot6;
|
||
compensation : TT_F26dot6 ) : TT_F26dot6;
|
||
var
|
||
val : TT_F26dot6;
|
||
begin
|
||
if distance >= 0 then
|
||
begin
|
||
val := (distance + 16 + compensation) and -32;
|
||
if val < 0 then val := 0;
|
||
end
|
||
else
|
||
begin
|
||
val := - ((-distance + 16 + compensation) and -32);
|
||
if val > 0 then val := 0;
|
||
end;
|
||
|
||
Round_To_Double_Grid := val;
|
||
end;
|
||
|
||
(*******************************************************************
|
||
*
|
||
* Function : Round_Super
|
||
*
|
||
* Description : super round value to grid after adding engine
|
||
* compensation
|
||
*
|
||
* Input : distance : distance to round
|
||
* compensation : engine compensation
|
||
*
|
||
* Output : rounded distance
|
||
*
|
||
* NOTE : The spec says very few about the relationship between
|
||
* rounding and engine compensation. However, it seems
|
||
* from the description of super round that we should
|
||
* should add the compensation before rounding
|
||
*
|
||
*****************************************************************)
|
||
|
||
function TInterpreter.Round_Super( distance : TT_F26dot6;
|
||
compensation : TT_F26dot6 ) : TT_F26dot6;
|
||
var
|
||
val : TT_F26dot6;
|
||
begin
|
||
with pEC^ do
|
||
|
||
if distance >= 0 then
|
||
begin
|
||
val := (distance - phase + threshold + compensation) and -period;
|
||
if val < 0 then val := 0;
|
||
val := val + phase;
|
||
end
|
||
else
|
||
begin
|
||
val := -((-distance - phase + threshold + compensation) and -period);
|
||
if val > 0 then val := 0;
|
||
val := val - phase;
|
||
end;
|
||
|
||
Round_Super := val;
|
||
end;
|
||
|
||
(*******************************************************************
|
||
*
|
||
* Function : Round_Super_45
|
||
*
|
||
* Description : super round value to grid after adding engine
|
||
* compensation
|
||
*
|
||
* Input : distance : distance to round
|
||
* compensation : engine compensation
|
||
*
|
||
* Output : rounded distance
|
||
*
|
||
* NOTE : There is a separate function for Round_Super_45 as we
|
||
* may need a greater precision.
|
||
*
|
||
*****************************************************************)
|
||
|
||
function TInterpreter.Round_Super_45( distance : TT_F26dot6;
|
||
compensation : TT_F26dot6 ) : TT_F26dot6;
|
||
var
|
||
val : TT_F26dot6;
|
||
begin
|
||
with pEC^ do
|
||
|
||
if distance >= 0 then
|
||
begin
|
||
val := ((distance - phase + threshold + compensation) div period)
|
||
* period;
|
||
if val < 0 then val := 0;
|
||
val := val + phase;
|
||
end
|
||
else
|
||
begin
|
||
val := -((-distance - phase + threshold + compensation) div period
|
||
* period );
|
||
if val > 0 then val := 0;
|
||
val := val - phase;
|
||
end;
|
||
|
||
Round_Super_45 := val;
|
||
end;
|
||
|
||
procedure TInterpreter.Compute_Round( round_mode : Byte );
|
||
begin
|
||
case Round_Mode of
|
||
|
||
TT_Round_Off : pEC^.func_round := Round_None;
|
||
TT_Round_To_Grid : pEC^.func_round := Round_To_Grid;
|
||
TT_Round_Up_To_Grid : pEC^.func_round := Round_Up_To_Grid;
|
||
TT_Round_Down_To_Grid : pEC^.func_round := Round_Down_To_Grid;
|
||
TT_Round_To_Half_Grid : pEC^.func_round := Round_To_Half_Grid;
|
||
TT_Round_To_Double_Grid : pEC^.func_round := Round_To_Double_Grid;
|
||
TT_Round_Super : pEC^.func_round := Round_Super;
|
||
TT_Round_Super_45 : pEC^.func_round := Round_Super_45;
|
||
end;
|
||
end;
|
||
|
||
|
||
(*******************************************************************
|
||
*
|
||
* Function : SetSuperRound
|
||
*
|
||
* Description : Set Super Round parameters
|
||
*
|
||
* Input : GridPeriod Grid period
|
||
* OpCode SROUND opcode
|
||
*
|
||
* Output : None
|
||
*
|
||
* Notes :
|
||
*
|
||
*****************************************************************)
|
||
|
||
procedure TInterpreter.SetSuperRound( GridPeriod : TT_F26dot6; selector : Long );
|
||
|
||
begin
|
||
with pEC^ do
|
||
begin
|
||
|
||
Case selector and $C0 of
|
||
|
||
$00 : period := GridPeriod div 2;
|
||
$40 : period := GridPeriod;
|
||
$80 : period := GridPeriod * 2;
|
||
|
||
(* This opcode is reserved, but ... *)
|
||
|
||
$C0 : period := GridPeriod;
|
||
end;
|
||
|
||
Case selector and $30 of
|
||
|
||
$00 : phase := 0;
|
||
$10 : phase := period div 4;
|
||
$20 : phase := period div 2;
|
||
$30 : phase := gridPeriod*3 div 4;
|
||
end;
|
||
|
||
if selector and $F = 0 then
|
||
|
||
Threshold := Period-1
|
||
else
|
||
Threshold := (Integer( selector and $F )-4)*period div 8;
|
||
|
||
period := period div 256;
|
||
phase := phase div 256;
|
||
threshold := threshold div 256;
|
||
|
||
end
|
||
end;
|
||
|
||
(*******************************************************************
|
||
*
|
||
* Function : Project
|
||
*
|
||
* Description : Computes the projection of (Vx,Vy) along the
|
||
* current projection vector
|
||
*
|
||
* Input : Vx, Vy input vector
|
||
*
|
||
* Output : return distance in F26dot6
|
||
*
|
||
*****************************************************************)
|
||
|
||
function TInterpreter.Project( var P1, P2 : TT_Vector ) : TT_F26dot6;
|
||
var
|
||
T1, T2 : Int64;
|
||
begin
|
||
with pEC^.GS.projVector do
|
||
begin
|
||
MulTo64( P1.x - P2.x, x, T1 );
|
||
MulTo64( P1.y - P2.y, y, T2 );
|
||
end;
|
||
|
||
Project := Div64by32( T1+T2, $4000 );
|
||
end;
|
||
|
||
|
||
function TInterpreter.Dual_Project( var P1, P2 : TT_Vector ) : TT_F26dot6;
|
||
var
|
||
T1, T2 : Int64;
|
||
begin
|
||
with pEC^.GS.dualVector do
|
||
begin
|
||
MulTo64( P1.x - P2.x, x, T1 );
|
||
MulTo64( P1.y - P2.y, y, T2 );
|
||
end;
|
||
|
||
Dual_Project := Div64by32( T1+T2, $4000 );
|
||
end;
|
||
|
||
|
||
function TInterpreter.Free_Project( var P1, P2 : TT_Vector ) : TT_F26dot6;
|
||
var
|
||
T1, T2 : Int64;
|
||
begin
|
||
with pEC^.GS.freeVector do
|
||
begin
|
||
MulTo64( P1.x - P2.x, x, T1 );
|
||
MulTo64( P1.y - P2.y, y, T2 );
|
||
end;
|
||
|
||
Free_Project := Div64by32( T1+T2, $4000 );
|
||
end;
|
||
|
||
|
||
function TInterpreter.Project_x( var P1, P2 : TT_Vector ) : TT_F26dot6;
|
||
begin
|
||
Project_x := P1.x - P2.x;
|
||
end;
|
||
|
||
function TInterpreter.Project_y( var P1, P2 : TT_Vector ) : TT_F26dot6;
|
||
begin
|
||
Project_y := P1.y - P2.y;
|
||
end;
|
||
|
||
(*******************************************************************
|
||
*
|
||
* Function : Compute_Funcs
|
||
*
|
||
* Description : Computes the projections and movement function
|
||
* pointers according to the current graphics state
|
||
*
|
||
* Input : None
|
||
*
|
||
*****************************************************************)
|
||
|
||
procedure TInterpreter.Compute_Funcs;
|
||
begin
|
||
with pEC^, GS do
|
||
begin
|
||
|
||
if (freeVector.x = $4000) then
|
||
begin
|
||
func_freeProj := Project_x;
|
||
F_dot_P := Long(projVector.x) * $10000;
|
||
end
|
||
else
|
||
if (freeVector.y = $4000) then
|
||
begin
|
||
func_freeProj := Project_y;
|
||
F_dot_P := Long(projVector.y) * $10000;
|
||
end
|
||
else
|
||
begin
|
||
func_move := Direct_Move;
|
||
func_freeProj := Free_Project;
|
||
F_dot_P := Long(projVector.x) * freeVector.x * 4 +
|
||
Long(projVector.y) * freeVector.y * 4;
|
||
end;
|
||
|
||
if (projVector.x = $4000) then func_Project := Project_x
|
||
else
|
||
if (projVector.y = $4000) then func_Project := Project_y
|
||
else
|
||
func_Project := Project;
|
||
|
||
if (dualVector.x = $4000) then func_dualproj := Project_x
|
||
else
|
||
if (dualVector.y = $4000) then func_dualproj := Project_y
|
||
else
|
||
func_dualproj := Dual_Project;
|
||
|
||
func_move := Direct_Move;
|
||
|
||
if F_dot_P = $40000000 then
|
||
|
||
if freeVector.x = $4000 then func_move := Direct_Move_x
|
||
else
|
||
if freeVector.y = $4000 then func_move := Direct_Move_y;
|
||
|
||
(* at small sizes, F_dot_P can become too small, resulting *)
|
||
(* in overflows and 'spikes' in a number of glyfs like 'w' *)
|
||
|
||
if abs( F_dot_P ) < $4000000 then F_dot_P := $40000000;
|
||
|
||
(* set aspect ratio to 0 to force recomputation by Get_Current_Ratio *)
|
||
metrics.ratio := 0;
|
||
end;
|
||
end;
|
||
|
||
|
||
(**************************************************)
|
||
(* *)
|
||
(* Normalize : Normer un vecteur ( U, V ) *)
|
||
(* r<>sultat dans ( X, Y ) *)
|
||
(* False si vecteur param<61>tre nul *)
|
||
(* *)
|
||
(**************************************************)
|
||
|
||
function TInterpreter.Normalize( U, V : TT_F26dot6; var R : TT_UnitVector ): boolean;
|
||
var
|
||
W : TT_F26dot6;
|
||
S1, S2 : Boolean;
|
||
begin
|
||
|
||
if (Abs(U) < $10000) and (Abs(V) < $10000) then
|
||
begin
|
||
U := U*$100;
|
||
V := V*$100;
|
||
|
||
W := Norm( U, V );
|
||
if W = 0 then
|
||
begin
|
||
(* XXX : Undocumented. Apparently, it is possible to try *)
|
||
(* to normalize the vector (0,0). Return success *)
|
||
(* in this case *)
|
||
Normalize := SUCCESS;
|
||
exit;
|
||
end;
|
||
|
||
R.x := MulDiv( U, $4000, W );
|
||
R.y := MulDiv( V, $4000, W );
|
||
|
||
end
|
||
else
|
||
begin
|
||
|
||
W := Norm( U, V );
|
||
|
||
if W > 0 then
|
||
begin
|
||
U := MulDiv( U, $4000, W );
|
||
V := MulDiv( V, $4000, W );
|
||
|
||
W := U*U + V*V;
|
||
|
||
(* Now, we want that Sqrt( W ) = $4000 *)
|
||
(* Or $1000000 <= W < $1004000 *)
|
||
|
||
if U < 0 then begin U := -U; S1 := True; end else S1 := False;
|
||
if V < 0 then begin V := -V; S2 := True; end else S2 := False;
|
||
|
||
while W < $1000000 do
|
||
begin
|
||
(* We need to increase W, by a minimal amount *)
|
||
if U < V then inc( U )
|
||
else inc( V );
|
||
W := U*U + V*V;
|
||
end;
|
||
|
||
while W >= $1004000 do
|
||
begin
|
||
(* We need to decrease W, by a minimal amount *)
|
||
if U < V then dec( U )
|
||
else dec( V );
|
||
W := U*U + V*V;
|
||
end;
|
||
|
||
(* Note that in various cases, we can only *)
|
||
(* compute a Sqrt(W) of $3FFF, eg. U=V *)
|
||
|
||
if S1 then U := -U;
|
||
if S2 then V := -V;
|
||
|
||
R.x := U; (* Type conversion *)
|
||
R.y := V; (* Type conversion *)
|
||
|
||
end
|
||
else
|
||
begin
|
||
Normalize := False;
|
||
pEC^.error := TT_Err_Divide_By_Zero;
|
||
end;
|
||
end;
|
||
|
||
Normalize := True;
|
||
end;
|
||
|
||
|
||
(****************************************************************)
|
||
(* *)
|
||
(* MANAGING THE STACK *)
|
||
(* *)
|
||
(* Instructions appear in the specs' order *)
|
||
(* *)
|
||
(****************************************************************)
|
||
|
||
(*******************************************)
|
||
(* DUP[] : Duplicate top stack element *)
|
||
(* CodeRange : $20 *)
|
||
|
||
procedure TInterpreter.Ins_DUP( args : PStorage );
|
||
begin
|
||
args^[1] := args^[0];
|
||
end;
|
||
|
||
(*******************************************)
|
||
(* POP[] : POPs the stack's top elt. *)
|
||
(* CodeRange : $21 *)
|
||
|
||
procedure TInterpreter.Ins_POP( args : PStorage );
|
||
begin
|
||
(* nothing to do *)
|
||
end;
|
||
|
||
(*******************************************)
|
||
(* CLEAR[] : Clear the entire stack *)
|
||
(* CodeRange : $22 *)
|
||
|
||
procedure TInterpreter.Ins_CLEAR( args : PStorage );
|
||
begin
|
||
new_top := 0;
|
||
end;
|
||
|
||
(*******************************************)
|
||
(* SWAP[] : Swap the top two elements *)
|
||
(* CodeRange : $23 *)
|
||
|
||
procedure TInterpreter.Ins_SWAP( args : PStorage );
|
||
var L : Long;
|
||
begin
|
||
L := args^[0];
|
||
args^[0] := args^[1];
|
||
args^[1] := L;
|
||
end;
|
||
|
||
(*******************************************)
|
||
(* DEPTH[] : return the stack depth *)
|
||
(* CodeRange : $24 *)
|
||
|
||
procedure TInterpreter.Ins_DEPTH( args : PStorage );
|
||
begin
|
||
args^[0] := top;
|
||
end;
|
||
|
||
(*******************************************)
|
||
(* CINDEX[] : copy indexed element *)
|
||
(* CodeRange : $25 *)
|
||
|
||
procedure TInterpreter.Ins_CINDEX( args : PStorage );
|
||
var
|
||
L : Long;
|
||
begin
|
||
L := args^[0];
|
||
if (L <= 0) or (L > opargs) then
|
||
pEC^.error := TT_Err_Invalid_Reference
|
||
else
|
||
args^[0] := pEC^.stack^[opargs-l];
|
||
end;
|
||
|
||
(*******************************************)
|
||
(* MINDEX[] : move indexed element *)
|
||
(* CodeRange : $26 *)
|
||
|
||
procedure TInterpreter.Ins_MINDEX( args : PStorage );
|
||
var
|
||
L, K : Long;
|
||
begin
|
||
L := args^[0];
|
||
if (L <= 0) or (L > opargs) then
|
||
pEC^.Error := TT_Err_Invalid_Reference
|
||
else
|
||
begin
|
||
K := pEC^.stack^[opargs-L];
|
||
|
||
move( pEC^.stack^[opargs-L+1],
|
||
pEC^.stack^[opargs-L],
|
||
(L-1)*sizeof(Long) );
|
||
|
||
pEC^.stack^[opargs-1] := K;
|
||
end;
|
||
end;
|
||
|
||
(*******************************************)
|
||
(* ROLL[] : roll top three elements *)
|
||
(* CodeRange : $8A *)
|
||
|
||
procedure TInterpreter.Ins_ROLL( args : PStorage );
|
||
var
|
||
A, B, C : Long;
|
||
begin
|
||
A := args^[2];
|
||
B := args^[1];
|
||
C := args^[0];
|
||
|
||
args^[2] := C;
|
||
args^[1] := A;
|
||
args^[0] := B;
|
||
end;
|
||
|
||
(****************************************************************)
|
||
(* *)
|
||
(* MANAGING THE FLOW OF CONTROL *)
|
||
(* *)
|
||
(* Instructions appear in the specs' order *)
|
||
(* *)
|
||
(****************************************************************)
|
||
|
||
function TInterpreter.SkipCode : boolean;
|
||
var
|
||
b : Boolean;
|
||
begin
|
||
b := False;
|
||
|
||
inc( pEC^.IP, oplength );
|
||
|
||
b := pEC^.IP < pEC^.codeSize;
|
||
|
||
if b then b := Calc_Length;
|
||
|
||
if not b then
|
||
pEC^.error := TT_Err_Code_Overflow;
|
||
|
||
SkipCode := b;
|
||
end;
|
||
|
||
|
||
(*******************************************)
|
||
(* IF[] : IF test *)
|
||
(* CodeRange : $58 *)
|
||
|
||
procedure TInterpreter.Ins_IF( args : PStorage );
|
||
var
|
||
nIfs : Int;
|
||
Out : Boolean;
|
||
begin
|
||
if args^[0] <> 0 then exit;
|
||
|
||
nIfs := 1;
|
||
Out := False;
|
||
|
||
Repeat
|
||
|
||
if not SkipCode then exit;
|
||
|
||
Case opcode of
|
||
|
||
(* IF *)
|
||
$58 : inc( nIfs );
|
||
|
||
(* ELSE *)
|
||
$1B : out:= nIfs=1;
|
||
|
||
(* EIF *)
|
||
$59 : begin
|
||
dec( nIfs );
|
||
out:= nIfs=0;
|
||
end;
|
||
end;
|
||
|
||
until Out;
|
||
end;
|
||
|
||
|
||
(*******************************************)
|
||
(* ELSE[] : ELSE *)
|
||
(* CodeRange : $1B *)
|
||
|
||
procedure TInterpreter.Ins_ELSE( args : PStorage );
|
||
var
|
||
nIfs : Int;
|
||
begin
|
||
nIfs := 1;
|
||
|
||
Repeat
|
||
|
||
if not SkipCode then exit;
|
||
|
||
case opcode of
|
||
|
||
(* IF *)
|
||
$58 : inc( nIfs );
|
||
|
||
(* EIF *)
|
||
$59 : dec( nIfs );
|
||
end;
|
||
|
||
until nIfs=0;
|
||
end;
|
||
|
||
(*******************************************)
|
||
(* EIF[] : End IF *)
|
||
(* CodeRange : $59 *)
|
||
|
||
procedure TInterpreter.Ins_EIF( args : PStorage );
|
||
begin
|
||
(* nothing to do *)
|
||
end;
|
||
|
||
(*******************************************)
|
||
(* JROT[] : Jump Relative On True *)
|
||
(* CodeRange : $78 *)
|
||
|
||
procedure TInterpreter.Ins_JROT( args : PStorage );
|
||
begin
|
||
if args^[1] <> 0 then
|
||
begin
|
||
inc( pEC^.IP, args^[0] );
|
||
pEC^.step_ins := false;
|
||
end;
|
||
end;
|
||
|
||
(*******************************************)
|
||
(* JMPR[] : JuMP Relative *)
|
||
(* CodeRange : $1C *)
|
||
|
||
procedure TInterpreter.Ins_JMPR( args : PStorage );
|
||
begin
|
||
inc( pEC^.IP, args^[0] );
|
||
pEC^.step_ins := false;
|
||
end;
|
||
|
||
(*******************************************)
|
||
(* JROF[] : Jump Relative On False *)
|
||
(* CodeRange : $79 *)
|
||
|
||
procedure TInterpreter.Ins_JROF( args : PStorage );
|
||
begin
|
||
if args^[1] = 0 then
|
||
begin
|
||
inc( pEC^.IP, args^[0] );
|
||
pEC^.step_ins := false;
|
||
end;
|
||
end;
|
||
|
||
(****************************************************************)
|
||
(* *)
|
||
(* LOGICAL FUNCTIONS *)
|
||
(* *)
|
||
(* Instructions appear in the specs' order *)
|
||
(* *)
|
||
(****************************************************************)
|
||
|
||
(*******************************************)
|
||
(* LT[] : Less Than *)
|
||
(* CodeRange : $50 *)
|
||
|
||
procedure TInterpreter.Ins_LT( args : PStorage );
|
||
begin
|
||
if args^[0] < args^[1] then args^[0] := 1
|
||
else args^[0] := 0;
|
||
end;
|
||
|
||
(*******************************************)
|
||
(* LTEQ[] : Less Than or EQual *)
|
||
(* CodeRange : $51 *)
|
||
|
||
procedure TInterpreter.Ins_LTEQ( args : PStorage );
|
||
begin
|
||
if args^[0] <= args^[1] then args^[0] := 1
|
||
else args^[0] := 0;
|
||
end;
|
||
|
||
(*******************************************)
|
||
(* GT[] : Greater Than *)
|
||
(* CodeRange : $52 *)
|
||
|
||
procedure TInterpreter.Ins_GT( args : PStorage );
|
||
begin
|
||
if args^[0] > args^[1] then args^[0] := 1
|
||
else args^[0] := 0;
|
||
end;
|
||
|
||
(*******************************************)
|
||
(* GTEQ[] : Greater Than or EQual *)
|
||
(* CodeRange : $53 *)
|
||
|
||
procedure TInterpreter.Ins_GTEQ( args : PStorage );
|
||
begin
|
||
if args^[0] >= args^[1] then args^[0] := 1
|
||
else args^[0] := 0;
|
||
end;
|
||
|
||
(*******************************************)
|
||
(* EQ[] : EQual *)
|
||
(* CodeRange : $54 *)
|
||
|
||
procedure TInterpreter.Ins_EQ( args : PStorage );
|
||
begin
|
||
if args^[0] = args^[1] then args^[0] := 1
|
||
else args^[0] := 0;
|
||
end;
|
||
|
||
(*******************************************)
|
||
(* NEQ[] : Not EQual *)
|
||
(* CodeRange : $55 *)
|
||
|
||
procedure TInterpreter.Ins_NEQ( args : PStorage );
|
||
begin
|
||
if args^[0] <> args^[1] then args^[0] := 1
|
||
else args^[0] := 0;
|
||
end;
|
||
|
||
(*******************************************)
|
||
(* ODD[] : Odd *)
|
||
(* CodeRange : $56 *)
|
||
|
||
procedure TInterpreter.Ins_ODD( args : PStorage );
|
||
begin
|
||
if pEC^.func_round( args^[0], 0 ) and 127 = 64 then args^[0] := 1
|
||
else args^[0] := 0;
|
||
end;
|
||
|
||
(*******************************************)
|
||
(* EVEN[] : Even *)
|
||
(* CodeRange : $57 *)
|
||
|
||
procedure TInterpreter.Ins_EVEN( args : PStorage );
|
||
begin
|
||
if pEC^.func_round( args^[0], 0 ) and 127 = 0 then args^[0] := 1
|
||
else args^[0] := 0;
|
||
end;
|
||
|
||
(*******************************************)
|
||
(* AND[] : logical AND *)
|
||
(* CodeRange : $5A *)
|
||
|
||
procedure TInterpreter.Ins_AND( args : PStorage );
|
||
begin
|
||
if ( args^[0] <> 0 ) and
|
||
( args^[1] <> 0 ) then args^[0] := 1
|
||
else args^[0] := 0;
|
||
end;
|
||
|
||
(*******************************************)
|
||
(* OR[] : logical OR *)
|
||
(* CodeRange : $5B *)
|
||
|
||
procedure TInterpreter.Ins_OR( args : PStorage );
|
||
begin
|
||
if ( args^[0] <> 0 ) or
|
||
( args^[1] <> 0 ) then args^[0] := 1
|
||
else args^[0] := 0;
|
||
end;
|
||
|
||
(*******************************************)
|
||
(* NOT[] : logical NOT *)
|
||
(* CodeRange : $5C *)
|
||
|
||
procedure TInterpreter.Ins_NOT( args : PStorage );
|
||
begin
|
||
if args^[0] <> 0 then args^[0] := 0
|
||
else args^[0] := 1;
|
||
end;
|
||
|
||
(****************************************************************)
|
||
(* *)
|
||
(* ARITHMETIC AND MATH INSTRUCTIONS *)
|
||
(* *)
|
||
(* Instructions appear in the specs' order *)
|
||
(* *)
|
||
(****************************************************************)
|
||
|
||
(*******************************************)
|
||
(* ADD[] : ADD *)
|
||
(* CodeRange : $60 *)
|
||
|
||
procedure TInterpreter.Ins_ADD( args : PStorage );
|
||
begin
|
||
inc( args^[0], args^[1] );
|
||
end;
|
||
|
||
(*******************************************)
|
||
(* SUB[] : SUBstract *)
|
||
(* CodeRange : $61 *)
|
||
|
||
procedure TInterpreter.Ins_SUB( args : PStorage );
|
||
begin
|
||
dec( args^[0], args^[1] );
|
||
end;
|
||
|
||
(*******************************************)
|
||
(* DIV[] : DIVide *)
|
||
(* CodeRange : $62 *)
|
||
|
||
procedure TInterpreter.Ins_DIV( args : PStorage );
|
||
begin
|
||
if args^[1] = 0 then
|
||
begin
|
||
pEC^.error := TT_Err_Divide_By_Zero;
|
||
exit;
|
||
end;
|
||
|
||
args^[0] := MulDiv_Round( args^[0], 64, args^[1] );
|
||
end;
|
||
|
||
(*******************************************)
|
||
(* MUL[] : MULtiply *)
|
||
(* CodeRange : $63 *)
|
||
|
||
procedure TInterpreter.Ins_MUL( args : PStorage );
|
||
begin
|
||
args^[0] := MulDiv_Round( args^[0], args^[1], 64 );
|
||
end;
|
||
|
||
(*******************************************)
|
||
(* ABS[] : ABSolute value *)
|
||
(* CodeRange : $64 *)
|
||
|
||
procedure TInterpreter.Ins_ABS( args : PStorage );
|
||
begin
|
||
args^[0] := abs( args^[0] );
|
||
end;
|
||
|
||
(*******************************************)
|
||
(* NEG[] : NEGate *)
|
||
(* CodeRange : $65 *)
|
||
|
||
procedure TInterpreter.Ins_NEG( args : PStorage );
|
||
begin
|
||
args^[0] := -args^[0];
|
||
end;
|
||
|
||
(*******************************************)
|
||
(* FLOOR[] : FLOOR *)
|
||
(* CodeRange : $66 *)
|
||
|
||
procedure TInterpreter.Ins_FLOOR( args : PStorage );
|
||
begin
|
||
args^[0] := args^[0] and -64;
|
||
end;
|
||
|
||
(*******************************************)
|
||
(* CEILING[] : CEILING *)
|
||
(* CodeRange : $67 *)
|
||
|
||
procedure TInterpreter.Ins_CEILING( args : PStorage );
|
||
begin
|
||
args^[0] := ( args^[0]+63 ) and -64;
|
||
end;
|
||
|
||
(*******************************************)
|
||
(* MAX[] : MAXimum *)
|
||
(* CodeRange : $68 *)
|
||
|
||
procedure TInterpreter.Ins_MAX( args : PStorage );
|
||
begin
|
||
if args^[1] > args^[0] then args^[0] := args^[1];
|
||
end;
|
||
|
||
(*******************************************)
|
||
(* MIN[] : MINimum *)
|
||
(* CodeRange : $69 *)
|
||
|
||
procedure TInterpreter.Ins_MIN( args : PStorage );
|
||
begin
|
||
if args^[1] < args^[0] then args^[0] := args^[1];
|
||
end;
|
||
|
||
(****************************************************************)
|
||
(* *)
|
||
(* COMPENSATING FOR THE ENGINE CHARACTERISTICS *)
|
||
(* *)
|
||
(* Instructions appear in the specs' order *)
|
||
(* *)
|
||
(****************************************************************)
|
||
|
||
(*******************************************)
|
||
(* ROUND[ab] : ROUND value *)
|
||
(* CodeRange : $68-$6B *)
|
||
|
||
procedure TInterpreter.Ins_ROUND( args : PStorage );
|
||
begin
|
||
args^[0] := pEC^.func_round( args^[0],
|
||
pEC^.metrics.compensations[ opcode-$68 ] );
|
||
end;
|
||
|
||
(*******************************************)
|
||
(* NROUND[ab]: No ROUNDing of value *)
|
||
(* CodeRange : $6C-$6F *)
|
||
|
||
procedure TInterpreter.Ins_NROUND( args : PStorage );
|
||
begin
|
||
args^[0] := Round_None( args^[0],
|
||
pEC^.metrics.compensations[ opcode-$6C ] );
|
||
end;
|
||
|
||
(****************************************************************)
|
||
(* *)
|
||
(* DEFINING AND USING FUNCTIONS AND INSTRUCTIONS *)
|
||
(* *)
|
||
(* Instructions appear in the specs' order *)
|
||
(* *)
|
||
(****************************************************************)
|
||
|
||
(*******************************************)
|
||
(* FDEF[] : Function DEFinition *)
|
||
(* CodeRange : $2C *)
|
||
|
||
procedure TInterpreter.Ins_FDEF( args : PStorage );
|
||
var
|
||
func : int;
|
||
begin
|
||
|
||
(* check space *)
|
||
if pEC^.numFDefs >= pEC^.maxFDefs then begin
|
||
pEC^.error := TT_Err_Too_Many_FuncDefs;
|
||
exit;
|
||
end;
|
||
|
||
func := Int(args^[0]);
|
||
with pEC^.FDefs^[pEC^.numFDefs] do
|
||
begin
|
||
Range := pEC^.curRange;
|
||
Opc := func;
|
||
Start := pEC^.IP+1;
|
||
Active := True;
|
||
end;
|
||
|
||
if func > pEC^.maxFunc then
|
||
pEC^.maxFunc := func;
|
||
|
||
inc(pEC^.numFDefs);
|
||
|
||
(* now skip the whole function definition *)
|
||
(* we don't allow nested IDEFS & FDEFs *)
|
||
|
||
while SkipCode do
|
||
|
||
case opcode of
|
||
|
||
$89, (* IDEF *)
|
||
$2C : (* FDEF *)
|
||
begin
|
||
pEC^.error := TT_Err_Nested_Defs;
|
||
exit;
|
||
end;
|
||
|
||
$2D : (* ENDF *)
|
||
exit;
|
||
end;
|
||
end;
|
||
|
||
(*******************************************)
|
||
(* ENDF[] : END Function definition *)
|
||
(* CodeRange : $2D *)
|
||
|
||
procedure TInterpreter.Ins_ENDF( args : PStorage );
|
||
begin
|
||
|
||
if callTop <= 0 then (* We encountered an ENDF without a call *)
|
||
begin
|
||
pEC^.error := TT_Err_ENDF_in_Exec_Stream;
|
||
exit;
|
||
end;
|
||
|
||
dec( callTop );
|
||
|
||
with pEC^.Callstack^[callTop] do
|
||
begin
|
||
dec( Cur_Count );
|
||
|
||
pEC^.step_ins := false;
|
||
|
||
if Cur_Count > 0 then
|
||
|
||
begin
|
||
(* Loop the current function *)
|
||
inc( callTop );
|
||
pEC^.IP := Cur_Restart;
|
||
end
|
||
|
||
else
|
||
(* exit the current call frame *)
|
||
(* NOTE : When the last intruction of a program *)
|
||
(* is a CALL or LOOPCALL, the return address *)
|
||
(* is always out of the code range. This is *)
|
||
(* valid address, and is why we do not test *)
|
||
(* the result of Goto_CodeRange here !! *)
|
||
|
||
Goto_CodeRange( Caller_Range, Caller_IP )
|
||
end;
|
||
|
||
end;
|
||
|
||
(*******************************************)
|
||
(* CALL[] : CALL function *)
|
||
(* CodeRange : $2B *)
|
||
|
||
procedure TInterpreter.Ins_CALL( args : PStorage );
|
||
var
|
||
ii, nn : Int;
|
||
def : PDefRecord;
|
||
label
|
||
Fail;
|
||
begin
|
||
|
||
(* First of all, check index *)
|
||
if (args^[0] < 0) or (args^[0] > pEC^.maxFunc) then
|
||
goto Fail;
|
||
|
||
(* Except for some old Apple fonts, all functions in a TrueType *)
|
||
(* fonts are defined in increasing order, starting from 0. *)
|
||
(* *)
|
||
(* This mean that, normally, we have : *)
|
||
(* *)
|
||
(* pEC^.maxFunc+1 = pEC^.numFDefs *)
|
||
(* pEC^.FDefs[n].opc = n for n in 0..pEC^.maxFunc *)
|
||
(* *)
|
||
|
||
nn := Int(args^[0]);
|
||
def := @pEC^.FDefs^[nn];
|
||
|
||
if ( pEC^.maxFunc+1 <> pEC^.numFDefs ) or ( def^.opc <> nn ) then begin
|
||
(* lookup the FDefs table *)
|
||
ii := 0;
|
||
def := @pEC^.FDefs^[0];
|
||
while (ii < pEC^.numFDefs) and (def^.opc <> nn) do begin
|
||
inc(ii);
|
||
inc(def);
|
||
end;
|
||
|
||
(* Fail if the function isn't listed *)
|
||
if ii >= pEC^.numFDefs then
|
||
goto Fail;
|
||
end;
|
||
|
||
(* check that the function is active *)
|
||
if not def^.active then
|
||
goto Fail;
|
||
|
||
(* check call stack *)
|
||
if callTop >= pEC^.callSize then
|
||
begin
|
||
pEC^.error := TT_Err_Stack_Overflow;
|
||
exit;
|
||
end;
|
||
|
||
with pEC^.callstack^[callTop] do
|
||
begin
|
||
Caller_Range := pEC^.curRange;
|
||
Caller_IP := pEC^.IP+1;
|
||
Cur_Count := 1;
|
||
Cur_Restart := def^.Start;
|
||
end;
|
||
|
||
inc( callTop );
|
||
|
||
with def^ do Goto_CodeRange( Range, Start );
|
||
|
||
pEC^.step_ins := false;
|
||
exit;
|
||
|
||
Fail:
|
||
pEC^.error := TT_Err_Invalid_Reference;
|
||
exit;
|
||
end;
|
||
|
||
(*******************************************)
|
||
(* LOOPCALL[]: LOOP and CALL function *)
|
||
(* CodeRange : $2A *)
|
||
|
||
procedure TInterpreter.Ins_LOOPCALL( args : PStorage );
|
||
begin
|
||
|
||
if ( args^[1] < 0 ) or ( args^[1] >= pEC^.numFDefs ) or
|
||
( not pEC^.FDefs^[args^[1]].Active ) then
|
||
begin
|
||
pEC^.error := TT_Err_Invalid_Reference;
|
||
exit;
|
||
end;
|
||
|
||
if callTop >= pEC^.callSize then
|
||
begin
|
||
pEC^.error := TT_Err_Stack_Overflow;
|
||
exit;
|
||
end;
|
||
|
||
if args^[0] > 0 then
|
||
begin
|
||
with pEC^.callstack^[callTop] do
|
||
begin
|
||
Caller_Range := pEC^.curRange;
|
||
Caller_IP := pEC^.IP+1;
|
||
Cur_Count := args^[0];
|
||
Cur_Restart := pEC^.FDefs^[args^[1]].Start;
|
||
end;
|
||
|
||
inc( callTop );
|
||
|
||
with pEC^.FDefs^[args^[1]] do Goto_CodeRange( Range, Start );
|
||
|
||
pEC^.step_ins := false;
|
||
end;
|
||
|
||
end;
|
||
|
||
(*******************************************)
|
||
(* IDEF[] : Instruction DEFinition *)
|
||
(* CodeRange : $89 *)
|
||
|
||
procedure TInterpreter.Ins_IDEF( args : PStorage );
|
||
var
|
||
A : Int;
|
||
begin
|
||
|
||
A := 0;
|
||
|
||
while ( A < pEC^.numIDefs ) do
|
||
with pEC^.IDefs^[A] do
|
||
begin
|
||
|
||
if not Active then
|
||
begin
|
||
Opc := args^[0];
|
||
Start := pEC^.IP+1;
|
||
Range := pEC^.curRange;
|
||
Active := True;
|
||
|
||
A := pEC^.numIDefs;
|
||
|
||
(* now skip the whole function definition *)
|
||
(* we don't allow nested IDEFS & FDEFs *)
|
||
|
||
while SkipCode do
|
||
case opcode of
|
||
|
||
$89, (* IDEF *)
|
||
$2C : (* FDEF *)
|
||
begin
|
||
pEC^.error := TT_Err_Nested_Defs;
|
||
exit;
|
||
end;
|
||
|
||
$2D : (* ENDF *)
|
||
exit;
|
||
end;
|
||
end
|
||
else
|
||
inc( A );
|
||
end;
|
||
end;
|
||
|
||
(****************************************************************)
|
||
(* *)
|
||
(* PUSHING DATA ONTO THE INTERPRETER STACK *)
|
||
(* *)
|
||
(* Instructions appear in the specs' order *)
|
||
(* *)
|
||
(****************************************************************)
|
||
|
||
(*******************************************)
|
||
(* NPUSHB[] : PUSH N Bytes *)
|
||
(* CodeRange : $40 *)
|
||
|
||
procedure TInterpreter.Ins_NPUSHB( args : PStorage );
|
||
var
|
||
L, K : Long;
|
||
begin
|
||
L := pEC^.code^[pEC^.IP+1];
|
||
|
||
if NeedStackSize(top + L, args) then exit;
|
||
|
||
for K := 1 to L do
|
||
args^[k-1] := pEC^.code^[pEC^.IP+1+k];
|
||
|
||
inc( new_top, L );
|
||
end;
|
||
|
||
(*******************************************)
|
||
(* NPUSHW[] : PUSH N Words *)
|
||
(* CodeRange : $41 *)
|
||
|
||
procedure TInterpreter.Ins_NPUSHW( args : PStorage );
|
||
var
|
||
L, K : Long;
|
||
begin
|
||
L := pEC^.code^[pEC^.IP+1];
|
||
|
||
if NeedStackSize(top + L, args) then exit;
|
||
|
||
inc( pEC^.IP, 2 );
|
||
|
||
for K := 1 to L do
|
||
args^[k-1] := GetShort;
|
||
|
||
pEC^.step_ins := false;
|
||
|
||
inc( new_top, L );
|
||
end;
|
||
|
||
(*******************************************)
|
||
(* PUSHB[abc]: PUSH Bytes *)
|
||
(* CodeRange : $B0-$B7 *)
|
||
|
||
procedure TInterpreter.Ins_PUSHB( args : PStorage );
|
||
var
|
||
L, K : Long;
|
||
begin
|
||
L := opcode - $B0+1;
|
||
|
||
if NeedStackSize(top + L + 1, args) then exit;
|
||
|
||
for k := 1 to L do
|
||
args^[k-1] := pEC^.code^[pEC^.ip+k];
|
||
|
||
end;
|
||
|
||
(*******************************************)
|
||
(* PUSHW[abc]: PUSH Words *)
|
||
(* CodeRange : $B8-$BF *)
|
||
|
||
procedure TInterpreter.Ins_PUSHW( args : PStorage );
|
||
var
|
||
L, K : Long;
|
||
begin
|
||
L := opcode - $B8+1;
|
||
|
||
if NeedStackSize(top + L + 1, args) then exit;
|
||
|
||
inc( pEC^.IP );
|
||
|
||
for k := 1 to L do
|
||
args^[k-1] := GetShort;
|
||
|
||
pEC^.step_ins := false;
|
||
|
||
end;
|
||
|
||
(****************************************************************)
|
||
(* *)
|
||
(* MANAGING THE STORAGE AREA *)
|
||
(* *)
|
||
(* Instructions appear in the specs' order *)
|
||
(* *)
|
||
(****************************************************************)
|
||
|
||
(*******************************************)
|
||
(* RS[] : Read Store *)
|
||
(* CodeRange : $43 *)
|
||
|
||
procedure TInterpreter.Ins_RS( args : PStorage );
|
||
begin
|
||
if (args^[0] < 0) or (args^[0] >= pEC^.storeSize) then
|
||
begin
|
||
pEC^.error := TT_Err_Invalid_Reference;
|
||
exit;
|
||
end;
|
||
|
||
args^[0] := pEC^.storage^[args^[0]];
|
||
end;
|
||
|
||
(*******************************************)
|
||
(* WS[] : Write Store *)
|
||
(* CodeRange : $42 *)
|
||
|
||
procedure TInterpreter.Ins_WS( args : PStorage );
|
||
begin
|
||
if (args^[0] < 0) or (args^[0] >= pEC^.storeSize) then
|
||
begin
|
||
pEC^.error := TT_Err_Invalid_Reference;
|
||
exit;
|
||
end;
|
||
|
||
pEC^.storage^[args^[0]] := args^[1];
|
||
end;
|
||
|
||
(*******************************************)
|
||
(* WCVTP[] : Write CVT in Pixel units *)
|
||
(* CodeRange : $44 *)
|
||
|
||
procedure TInterpreter.Ins_WCVTP( args : PStorage );
|
||
begin
|
||
if (args^[0] < 0) or (args^[0] >= pEC^.cvtSize) then
|
||
begin
|
||
pEC^.error := TT_Err_Invalid_Reference;
|
||
exit;
|
||
end;
|
||
|
||
pEC^.func_write_cvt( args^[0], args^[1] );
|
||
end;
|
||
|
||
(*******************************************)
|
||
(* WCVTF[] : Write CVT in FUnits *)
|
||
(* CodeRange : $70 *)
|
||
|
||
procedure TInterpreter.Ins_WCVTF( args : PStorage );
|
||
begin
|
||
if (args^[0] < 0) or (args^[0] >= pEC^.cvtSize) then
|
||
begin
|
||
pEC^.error := TT_Err_Invalid_Reference;
|
||
exit;
|
||
end;
|
||
|
||
pEC^.cvt^[args^[0]] := Scale_Pixels(args^[1]);
|
||
end;
|
||
|
||
(*******************************************)
|
||
(* RCVT[] : Read CVT *)
|
||
(* CodeRange : $45 *)
|
||
|
||
procedure TInterpreter.Ins_RCVT( args : PStorage );
|
||
begin
|
||
if (args^[0] < 0) or (args^[0] >= pEC^.cvtSize) then
|
||
begin
|
||
pEC^.error := TT_Err_Invalid_Reference;
|
||
exit;
|
||
end;
|
||
|
||
args^[0] := pEC^.func_read_cvt(args^[0]);
|
||
end;
|
||
|
||
(****************************************************************)
|
||
(* *)
|
||
(* MANAGING THE GRAPHICS STATE *)
|
||
(* *)
|
||
(* Instructions appear in the specs' order *)
|
||
(* *)
|
||
(****************************************************************)
|
||
|
||
(*******************************************)
|
||
(* SVTCA[a] : Set F and P vectors to axis *)
|
||
(* CodeRange : $00-$01 *)
|
||
|
||
procedure TInterpreter.Ins_SVTCA( args : PStorage );
|
||
var A, B : Short;
|
||
begin
|
||
case (opcode and 1) of
|
||
0 : A := $0000;
|
||
1 : A := $4000;
|
||
end;
|
||
B := A xor $4000;
|
||
|
||
pEC^.GS.freeVector.x := A;
|
||
pEC^.GS.projVector.x := A;
|
||
pEC^.GS.dualVector.x := A;
|
||
|
||
pEC^.GS.freeVector.y := B;
|
||
pEC^.GS.projVector.y := B;
|
||
pEC^.GS.dualVector.y := B;
|
||
|
||
Compute_Funcs;
|
||
end;
|
||
|
||
(*******************************************)
|
||
(* SPVTCA[a] : Set PVector to Axis *)
|
||
(* CodeRange : $02-$03 *)
|
||
|
||
procedure TInterpreter.Ins_SPVTCA( args : PStorage );
|
||
var A, B : Short;
|
||
begin
|
||
case (opcode and 1) of
|
||
0 : A := $0000;
|
||
1 : A := $4000;
|
||
end;
|
||
B := A xor $4000;
|
||
|
||
pEC^.GS.projVector.x := A;
|
||
pEC^.GS.dualVector.x := A;
|
||
|
||
pEC^.GS.projVector.y := B;
|
||
pEC^.GS.dualVector.y := B;
|
||
|
||
Compute_Funcs;
|
||
end;
|
||
|
||
(*******************************************)
|
||
(* SFVTCA[a] : Set FVector to Axis *)
|
||
(* CodeRange : $04-$05 *)
|
||
|
||
procedure TInterpreter.Ins_SFVTCA( args : PStorage );
|
||
var A, B : Short;
|
||
begin
|
||
case (opcode and 1) of
|
||
0 : A := $0000;
|
||
1 : A := $4000;
|
||
end;
|
||
B := A xor $4000;
|
||
|
||
pEC^.GS.freeVector.x := A;
|
||
pEC^.GS.freeVector.y := B;
|
||
|
||
Compute_Funcs;
|
||
end;
|
||
|
||
|
||
|
||
function TInterpreter.Ins_SxVTL( aIdx1 : Int;
|
||
aIdx2 : Int;
|
||
aOpc : Int;
|
||
var Vec : TT_UnitVector ) : boolean;
|
||
var
|
||
A, B, C : Long;
|
||
begin
|
||
Ins_SxVTL := False;
|
||
|
||
with pEC^ do
|
||
begin
|
||
|
||
if (aIdx2 >= zp1.n_points) or (aIdx1 >= zp2.n_points) then
|
||
begin
|
||
Error := TT_Err_Invalid_Reference;
|
||
exit;
|
||
end;
|
||
|
||
with zp1.Cur^[aIdx2] do
|
||
begin
|
||
A := x;
|
||
B := y;
|
||
end;
|
||
|
||
with zp2.Cur^[aIdx1] do
|
||
begin
|
||
dec( A, x );
|
||
dec( B, y );
|
||
end;
|
||
|
||
if aOpc and 1 <> 0 then
|
||
begin
|
||
C := B; (* CounterClockwise rotation *)
|
||
B := A;
|
||
A := -C;
|
||
end;
|
||
|
||
if not Normalize( A, B, Vec ) then
|
||
begin
|
||
pEC^.error := TT_Err_Ok;
|
||
Vec.x := $4000;
|
||
Vec.y := $0000;
|
||
end;
|
||
|
||
Ins_SxVTL := True;
|
||
end;
|
||
end;
|
||
|
||
|
||
(*******************************************)
|
||
(* SPVTL[a] : Set PVector to Line *)
|
||
(* CodeRange : $06-$07 *)
|
||
|
||
procedure TInterpreter.Ins_SPVTL( args : PStorage );
|
||
begin
|
||
if not INS_SxVTL( args^[1],
|
||
args^[0],
|
||
opcode,
|
||
pEC^.GS.projVector ) then exit;
|
||
|
||
pEC^.GS.dualVector := pEC^.GS.projVector;
|
||
Compute_Funcs;
|
||
end;
|
||
|
||
(*******************************************)
|
||
(* SFVTL[a] : Set FVector to Line *)
|
||
(* CodeRange : $08-$09 *)
|
||
|
||
procedure TInterpreter.Ins_SFVTL( args : PStorage );
|
||
begin
|
||
if not INS_SxVTL( args^[1],
|
||
args^[0],
|
||
opcode,
|
||
pEC^.GS.freeVector ) then exit;
|
||
|
||
Compute_Funcs;
|
||
end;
|
||
|
||
(*******************************************)
|
||
(* SFVTPV[] : Set FVector to PVector *)
|
||
(* CodeRange : $0E *)
|
||
|
||
procedure TInterpreter.Ins_SFVTPV( args : PStorage );
|
||
begin
|
||
pEC^.GS.freeVector := pEC^.GS.projVector;
|
||
Compute_Funcs;
|
||
end;
|
||
|
||
(*******************************************)
|
||
(* SDPVTL[a] : Set Dual PVector to Line *)
|
||
(* CodeRange : $86-$87 *)
|
||
|
||
procedure TInterpreter.Ins_SDPVTL( args : PStorage );
|
||
var
|
||
A, B, C : Long;
|
||
p1, p2 : Int;
|
||
begin
|
||
|
||
p1 := args^[1];
|
||
p2 := args^[0];
|
||
|
||
if (args^[0] < 0) or (args^[0] >= pEC^.zp1.n_points) or
|
||
(args^[1] < 0) or (args^[1] >= pEC^.zp2.n_points) then
|
||
begin
|
||
pEC^.error := TT_Err_Invalid_Reference;
|
||
exit;
|
||
end;
|
||
|
||
A := pEC^.zp1.org^[p2].x - pEC^.zp2.org^[p1].x;
|
||
B := pEC^.zp1.org^[p2].y - pEC^.zp2.org^[p1].y;
|
||
|
||
if opcode and 1 <> 0 then
|
||
begin
|
||
C := B; (* CounterClockwise rotation *)
|
||
B := A;
|
||
A := -C;
|
||
end;
|
||
|
||
Normalize( A, B, pEC^.GS.dualVector );
|
||
|
||
A := pEC^.zp1.cur^[p2].x - pEC^.zp2.cur^[p1].x;
|
||
B := pEC^.zp1.cur^[p2].y - pEC^.zp2.cur^[p1].y;
|
||
|
||
if opcode and 1 <> 0 then
|
||
begin
|
||
C := B; (* CounterClockwise rotation *)
|
||
B := A;
|
||
A := -C;
|
||
end;
|
||
|
||
Normalize( A, B, pEC^.GS.projVector );
|
||
|
||
Compute_Funcs;
|
||
pEC^.error := TT_Err_Ok;
|
||
end;
|
||
|
||
(*******************************************)
|
||
(* SPVFS[] : Set PVector From Stack *)
|
||
(* CodeRange : $0A *)
|
||
|
||
procedure TInterpreter.Ins_SPVFS( args : PStorage );
|
||
var
|
||
S : Short;
|
||
X, Y : Long;
|
||
begin
|
||
S := args^[1]; Y := S; (* type conversion; extends sign *)
|
||
S := args^[0]; X := S; (* type conversion; extends sign *)
|
||
|
||
if not Normalize( X, Y, pEC^.GS.projVector ) then exit;
|
||
|
||
pEC^.GS.dualVector := pEC^.GS.projVector;
|
||
|
||
Compute_Funcs;
|
||
end;
|
||
|
||
(*******************************************)
|
||
(* SFVFS[] : Set FVector From Stack *)
|
||
(* CodeRange : $0B *)
|
||
|
||
procedure TInterpreter.Ins_SFVFS( args : PStorage );
|
||
var
|
||
S : Short;
|
||
X, Y : Long;
|
||
begin
|
||
S := args^[1]; Y := S; (* type conversion; extends sign *)
|
||
S := args^[0]; X := S; (* type conversion; extends sign *)
|
||
|
||
if not Normalize( X, Y, pEC^.GS.freeVector ) then exit;
|
||
|
||
Compute_Funcs;
|
||
end;
|
||
|
||
(*******************************************)
|
||
(* GPV[] : Get Projection Vector *)
|
||
(* CodeRange : $0C *)
|
||
|
||
procedure TInterpreter.Ins_GPV( args : PStorage );
|
||
begin
|
||
args^[0] := pEC^.GS.projVector.x;
|
||
args^[1] := pEC^.GS.projVector.y;
|
||
end;
|
||
|
||
(*******************************************)
|
||
(* GFV[] : Get Freedom Vector *)
|
||
(* CodeRange : $0D *)
|
||
|
||
procedure TInterpreter.Ins_GFV( args : PStorage );
|
||
begin
|
||
args^[0] := pEC^.GS.freeVector.x;
|
||
args^[1] := pEC^.GS.freeVector.y;
|
||
end;
|
||
|
||
(*******************************************)
|
||
(* SRP0[] : Set Reference Point 0 *)
|
||
(* CodeRange : $10 *)
|
||
|
||
procedure TInterpreter.Ins_SRP0( args : PStorage );
|
||
begin
|
||
pEC^.GS.rp0 := args^[0];
|
||
end;
|
||
|
||
(*******************************************)
|
||
(* SRP1[] : Set Reference Point 1 *)
|
||
(* CodeRange : $11 *)
|
||
|
||
procedure TInterpreter.Ins_SRP1( args : PStorage );
|
||
begin
|
||
pEC^.GS.rp1 := args^[0];
|
||
end;
|
||
|
||
(*******************************************)
|
||
(* SRP2[] : Set Reference Point 2 *)
|
||
(* CodeRange : $12 *)
|
||
|
||
procedure TInterpreter.Ins_SRP2( args : PStorage );
|
||
begin
|
||
pEC^.GS.rp2 := args^[0];
|
||
end;
|
||
|
||
(*******************************************)
|
||
(* SZP0[] : Set Zone Pointer 0 *)
|
||
(* CodeRange : $13 *)
|
||
|
||
procedure TInterpreter.Ins_SZP0( args : PStorage );
|
||
begin
|
||
case args^[0] of
|
||
|
||
0 : pEC^.zp0 := pEC^.Twilight;
|
||
1 : pEC^.zp0 := pEC^.Pts;
|
||
else
|
||
pEC^.error := TT_Err_Invalid_Reference;
|
||
exit;
|
||
end;
|
||
|
||
pEC^.GS.gep0 := args^[0];
|
||
end;
|
||
|
||
(*******************************************)
|
||
(* SZP1[] : Set Zone Pointer 1 *)
|
||
(* CodeRange : $14 *)
|
||
|
||
procedure TInterpreter.Ins_SZP1( args : PStorage );
|
||
begin
|
||
case args^[0] of
|
||
|
||
0 : pEC^.zp1 := pEC^.Twilight;
|
||
1 : pEC^.zp1 := pEC^.Pts;
|
||
else
|
||
pEC^.error := TT_Err_Invalid_Reference;
|
||
exit;
|
||
end;
|
||
|
||
pEC^.GS.gep1 := args^[0];
|
||
end;
|
||
|
||
(*******************************************)
|
||
(* SZP2[] : Set Zone Pointer 2 *)
|
||
(* CodeRange : $15 *)
|
||
|
||
procedure TInterpreter.Ins_SZP2( args : PStorage );
|
||
begin
|
||
case args^[0] of
|
||
|
||
0 : pEC^.zp2 := pEC^.Twilight;
|
||
1 : pEC^.zp2 := pEC^.Pts;
|
||
else
|
||
pEC^.error := TT_Err_Invalid_Reference;
|
||
exit;
|
||
end;
|
||
|
||
pEC^.GS.gep2 := args^[0];
|
||
end;
|
||
|
||
(*******************************************)
|
||
(* SZPS[] : Set Zone Pointers *)
|
||
(* CodeRange : $16 *)
|
||
|
||
procedure TInterpreter.Ins_SZPS( args : PStorage );
|
||
begin
|
||
case args^[0] of
|
||
|
||
0 : pEC^.zp0 := pEC^.Twilight;
|
||
1 : pEC^.zp0 := pEC^.Pts;
|
||
else
|
||
pEC^.error := TT_Err_Invalid_Reference;
|
||
exit;
|
||
end;
|
||
|
||
pEC^.zp1 := pEC^.zp0;
|
||
pEC^.zp2 := pEC^.zp0;
|
||
|
||
pEC^.GS.gep0 := args^[0];
|
||
pEC^.GS.gep1 := args^[0];
|
||
pEC^.GS.gep2 := args^[0];
|
||
end;
|
||
|
||
(*******************************************)
|
||
(* RTHG[] : Round To Half Grid *)
|
||
(* CodeRange : $19 *)
|
||
|
||
procedure TInterpreter.Ins_RTHG( args : PStorage );
|
||
begin
|
||
pEC^.GS.round_state := TT_Round_To_Half_Grid;
|
||
pEC^.func_round := Round_To_Half_Grid;
|
||
end;
|
||
|
||
(*******************************************)
|
||
(* RTG[] : Round To Grid *)
|
||
(* CodeRange : $18 *)
|
||
|
||
procedure TInterpreter.Ins_RTG( args : PStorage );
|
||
begin
|
||
pEC^.GS.round_state := TT_Round_To_Grid;
|
||
pEC^.func_round := Round_To_Grid;
|
||
end;
|
||
|
||
(*******************************************)
|
||
(* RTDG[] : Round To Double Grid *)
|
||
(* CodeRange : $3D *)
|
||
|
||
procedure TInterpreter.Ins_RTDG( args : PStorage );
|
||
begin
|
||
pEC^.GS.round_state := TT_Round_To_Double_Grid;
|
||
pEC^.func_round := Round_To_Double_Grid;
|
||
end;
|
||
|
||
(*******************************************)
|
||
(* RUTG[] : Round Up To Grid *)
|
||
(* CodeRange : $7C *)
|
||
|
||
procedure TInterpreter.Ins_RUTG( args : PStorage );
|
||
begin
|
||
pEC^.GS.round_state := TT_Round_Up_To_Grid;
|
||
pEC^.func_round := Round_Up_To_Grid;
|
||
end;
|
||
|
||
(*******************************************)
|
||
(* RDTG[] : Round Down To Grid *)
|
||
(* CodeRange : $7D *)
|
||
|
||
procedure TInterpreter.Ins_RDTG( args : PStorage );
|
||
begin
|
||
pEC^.GS.round_state := TT_Round_Down_To_Grid;
|
||
pEC^.func_round := Round_Down_To_Grid;
|
||
end;
|
||
|
||
(*******************************************)
|
||
(* ROFF[] : Round OFF *)
|
||
(* CodeRange : $7A *)
|
||
|
||
procedure TInterpreter.Ins_ROFF( args : PStorage );
|
||
begin
|
||
pEC^.GS.round_state := TT_Round_Off;
|
||
pEC^.func_round := Round_None;
|
||
end;
|
||
|
||
(*******************************************)
|
||
(* SROUND[] : Super ROUND *)
|
||
(* CodeRange : $76 *)
|
||
|
||
procedure TInterpreter.Ins_SROUND( args : PStorage );
|
||
begin
|
||
SetSuperRound( $4000, args^[0] );
|
||
pEC^.GS.round_state := TT_Round_Super;
|
||
pEC^.func_round := Round_Super;
|
||
end;
|
||
|
||
(*******************************************)
|
||
(* S45ROUND[]: Super ROUND 45 degrees *)
|
||
(* CodeRange : $77 *)
|
||
|
||
procedure TInterpreter.Ins_S45ROUND( args : PStorage );
|
||
begin
|
||
SetSuperRound( $2D41, args^[0] );
|
||
pEC^.GS.round_state := TT_Round_Super_45;
|
||
pEC^.func_round := Round_Super_45;
|
||
end;
|
||
|
||
|
||
(*******************************************)
|
||
(* SLOOP[] : Set LOOP variable *)
|
||
(* CodeRange : $17 *)
|
||
|
||
procedure TInterpreter.Ins_SLOOP( args : PStorage );
|
||
begin
|
||
pEC^.GS.Loop := args^[0];
|
||
end;
|
||
|
||
(*******************************************)
|
||
(* SMD[] : Set Minimum Distance *)
|
||
(* CodeRange : $1A *)
|
||
|
||
procedure TInterpreter.Ins_SMD( args : PStorage );
|
||
begin
|
||
pEC^.GS.minimum_distance := args^[0];
|
||
end;
|
||
|
||
(*******************************************)
|
||
(* INSTCTRL[]: INSTruction ConTRol *)
|
||
(* CodeRange : $8e *)
|
||
|
||
procedure TInterpreter.Ins_INSTCTRL( args : PStorage );
|
||
var
|
||
K, L : Int;
|
||
begin
|
||
K := args^[1];
|
||
L := args^[0];
|
||
|
||
if ( K < 1 ) or ( K > 2 ) then
|
||
begin
|
||
pEC^.error := TT_Err_Invalid_Reference;
|
||
exit;
|
||
end;
|
||
|
||
if L <> 0 then L := K;
|
||
|
||
pEC^.GS.instruct_control := ( pEC^.GS.instruct_control and not K ) or L;
|
||
end;
|
||
|
||
(*******************************************)
|
||
(* SCANCTRL[]: SCAN ConTRol *)
|
||
(* CodeRange : $85 *)
|
||
|
||
procedure TInterpreter.Ins_SCANCTRL( args : PStorage );
|
||
var
|
||
A : Int;
|
||
begin
|
||
|
||
(* Get Threshold *)
|
||
A := args^[0] and $FF;
|
||
|
||
if A = $FF then
|
||
pEC^.GS.scan_Control := True
|
||
else
|
||
if A = 0 then
|
||
pEC^.GS.scan_Control := False
|
||
else
|
||
begin
|
||
|
||
A := A * 64;
|
||
|
||
(* XXX TODO : Add rotation and stretch cases *)
|
||
|
||
if ( args^[0] and $100 <> 0 ) and
|
||
( pEC^.metrics.pointSize <= A ) then pEC^.GS.scan_Control := True;
|
||
|
||
//if ( args^[0] and $200 <> 0 ) and
|
||
// ( false ) then pEC^.GS.scan_Control := True;
|
||
|
||
//if ( args^[0] and $400 <> 0 ) and
|
||
// ( false ) then pEC^.GS.scan_Control := True;
|
||
|
||
if ( args^[0] and $800 <> 0 ) and
|
||
( pEC^.metrics.pointSize > A ) then pEC^.GS.scan_Control := False;
|
||
|
||
if ( args^[0] and $1000 <> 0 ) and
|
||
( not False ) then pEC^.GS.scan_Control := False;
|
||
|
||
if ( args^[0] and $2000 <> 0 ) and
|
||
( not False ) then pEC^.GS.scan_Control := False;
|
||
end;
|
||
end;
|
||
|
||
(*******************************************)
|
||
(* SCANTYPE[]: SCAN TYPE *)
|
||
(* CodeRange : $8D *)
|
||
|
||
procedure TInterpreter.Ins_SCANTYPE( args : PStorage );
|
||
begin
|
||
(* For compatibility with future enhancements, *)
|
||
(* we must ignore new modes *)
|
||
|
||
if (args^[0] >= 0 ) and (args^[0] <= 5) then
|
||
begin
|
||
if args^[0] = 3 then args^[0] := 2;
|
||
|
||
pEC^.GS.scan_type := args^[0];
|
||
end;
|
||
end;
|
||
|
||
(**********************************************)
|
||
(* SCVTCI[] : Set Control Value Table Cut In *)
|
||
(* CodeRange : $1D *)
|
||
|
||
procedure TInterpreter.Ins_SCVTCI( args : PStorage );
|
||
begin
|
||
pEC^.GS.control_value_cutin := args^[0];
|
||
end;
|
||
|
||
(**********************************************)
|
||
(* SSWCI[] : Set Single Width Cut In *)
|
||
(* CodeRange : $1E *)
|
||
|
||
procedure TInterpreter.Ins_SSWCI( args : PStorage );
|
||
begin
|
||
pEC^.GS.single_width_cutin := args^[0];
|
||
end;
|
||
|
||
(**********************************************)
|
||
(* SSW[] : Set Single Width *)
|
||
(* CodeRange : $1F *)
|
||
|
||
procedure TInterpreter.Ins_SSW( args : PStorage );
|
||
begin
|
||
pEC^.GS.single_width_value := args^[0] div $400;
|
||
end;
|
||
|
||
(**********************************************)
|
||
(* FLIPON[] : Set Auto_flip to On *)
|
||
(* CodeRange : $4D *)
|
||
|
||
procedure TInterpreter.Ins_FLIPON( args : PStorage );
|
||
begin
|
||
pEC^.GS.auto_flip := True;
|
||
end;
|
||
|
||
(**********************************************)
|
||
(* FLIPOFF[] : Set Auto_flip to Off *)
|
||
(* CodeRange : $4E *)
|
||
|
||
procedure TInterpreter.Ins_FLIPOFF( args : PStorage );
|
||
begin
|
||
pEC^.GS.auto_flip := False;
|
||
end;
|
||
|
||
(**********************************************)
|
||
(* SANGW[] : Set Angle Weigth *)
|
||
(* CodeRange : $7E *)
|
||
|
||
procedure TInterpreter.Ins_SANGW( args : PStorage );
|
||
begin
|
||
(* instruction not supported anymore *)
|
||
end;
|
||
|
||
(**********************************************)
|
||
(* SDB[] : Set Delta Base *)
|
||
(* CodeRange : $5E *)
|
||
|
||
procedure TInterpreter.Ins_SDB( args : PStorage );
|
||
begin
|
||
pEC^.GS.delta_base := args^[0]
|
||
end;
|
||
|
||
(**********************************************)
|
||
(* SDS[] : Set Delta Shift *)
|
||
(* CodeRange : $5F *)
|
||
|
||
procedure TInterpreter.Ins_SDS( args : PStorage );
|
||
begin
|
||
pEC^.GS.delta_shift := args^[0]
|
||
end;
|
||
|
||
(**********************************************)
|
||
(* GC[a] : Get Coordinate projected onto *)
|
||
(* CodeRange : $46-$47 *)
|
||
|
||
(* BULLSHIT : Measures from the original glyph must to be taken *)
|
||
(* along the dual projection vector !! *)
|
||
|
||
procedure TInterpreter.Ins_GC( args : PStorage );
|
||
var
|
||
L : Int;
|
||
begin
|
||
L := args^[0];
|
||
|
||
if (L < 0) or (L >= pEC^.zp2.n_points) then
|
||
begin
|
||
pEC^.error := TT_Err_Invalid_Reference;
|
||
exit;
|
||
end;
|
||
|
||
case opcode and 1 of
|
||
|
||
0 : L := pEC^.func_project ( pEC^.zp2.cur^[L], Null_Vector );
|
||
1 : L := pEC^.func_dualProj( pEC^.zp2.org^[L], Null_Vector );
|
||
end;
|
||
|
||
args^[0] := L;
|
||
end;
|
||
|
||
(**********************************************)
|
||
(* SCFS[] : Set Coordinate From Stack *)
|
||
(* CodeRange : $48 *)
|
||
(* *)
|
||
(* Formule : *)
|
||
(* *)
|
||
(* OA := OA + ( value - OA.p )/( f.p ) x f *)
|
||
(* *)
|
||
|
||
procedure TInterpreter.Ins_SCFS( args : PStorage );
|
||
var
|
||
K, L : Int;
|
||
begin
|
||
L := args^[0];
|
||
|
||
if (args^[0] < 0) or (args^[0] >= pEC^.zp2.n_points) then
|
||
begin
|
||
pEC^.error := TT_Err_Invalid_Reference;
|
||
exit;
|
||
end;
|
||
|
||
K := pEC^.func_project( pEC^.zp2.cur^[L], Null_Vector );
|
||
|
||
pEC^.func_move( @pEC^.zp2, L, args^[1] - K );
|
||
|
||
(* not part of the specs, but here for safety *)
|
||
|
||
if pEC^.GS.gep2 = 0 then
|
||
pEC^.zp2.org^[L] := pEC^.zp2.cur^[L];
|
||
|
||
end;
|
||
|
||
(**********************************************)
|
||
(* MD[a] : Measure Distance *)
|
||
(* CodeRange : $49-$4A *)
|
||
|
||
(* BULLSHIT : Measure taken in the original glyph must be along *)
|
||
(* the dual projection vector *)
|
||
|
||
(* Second BULLSHIT : Flag attributions are inverted !! *)
|
||
(* 0 => measure distance in original outline *)
|
||
(* 1 => measure distance in grid-fitted outline *)
|
||
|
||
procedure TInterpreter.Ins_MD( args : PStorage );
|
||
var
|
||
K, L : Int;
|
||
D : TT_F26dot6;
|
||
begin
|
||
K := args^[1];
|
||
L := args^[0];
|
||
|
||
if (args^[0] < 0) or (args^[0] >= pEC^.zp0.n_points) or
|
||
(args^[1] < 0) or (args^[1] >= pEC^.zp1.n_points) then
|
||
begin
|
||
pEC^.error := TT_Err_Invalid_Reference;
|
||
exit;
|
||
end;
|
||
|
||
case opcode and 1 of
|
||
|
||
0 : D := pEC^.func_dualProj( pEC^.zp0.org^[L], pEC^.zp1.org^[K] );
|
||
1 : D := pEC^.func_project ( pEC^.zp0.cur^[L], pEC^.zp1.cur^[K] );
|
||
end;
|
||
|
||
args^[0] := D;
|
||
end;
|
||
|
||
(**********************************************)
|
||
(* MPPEM[] : Measure Pixel Per EM *)
|
||
(* CodeRange : $4B *)
|
||
|
||
procedure TInterpreter.Ins_MPPEM( args : PStorage );
|
||
begin
|
||
args^[0] := Get_Ppem;
|
||
end;
|
||
|
||
(**********************************************)
|
||
(* MPS[] : Measure PointSize *)
|
||
(* CodeRange : $4C *)
|
||
|
||
procedure TInterpreter.Ins_MPS( args : PStorage );
|
||
begin
|
||
args^[0] := pEC^.metrics.pointSize;
|
||
end;
|
||
|
||
(****************************************************************)
|
||
(* *)
|
||
(* MANAGING OUTLINES *)
|
||
(* *)
|
||
(* Instructions appear in the specs' order *)
|
||
(* *)
|
||
(****************************************************************)
|
||
|
||
|
||
(**********************************************)
|
||
(* FLIPPT[] : FLIP PoinT *)
|
||
(* CodeRange : $80 *)
|
||
|
||
procedure TInterpreter.Ins_FLIPPT( args : PStorage );
|
||
var
|
||
point : Int;
|
||
begin
|
||
if top < pEC^.GS.loop then
|
||
begin
|
||
pEC^.error := TT_Err_Too_Few_Arguments;
|
||
exit;
|
||
end;
|
||
|
||
while pEC^.GS.loop > 0 do
|
||
begin
|
||
dec( opargs );
|
||
|
||
point := pEC^.stack^[ opargs ];
|
||
|
||
if (point < 0) or (point >= pEC^.pts.n_points) then
|
||
begin
|
||
pEC^.error := TT_Err_Invalid_Reference;
|
||
exit;
|
||
end;
|
||
|
||
pEC^.pts.flags^[point] := pEC^.pts.flags^[point] xor TT_Flag_On_Curve;
|
||
|
||
dec( pEC^.GS.loop );
|
||
end;
|
||
|
||
pEC^.GS.loop := 1;
|
||
new_top := opargs;
|
||
end;
|
||
|
||
(**********************************************)
|
||
(* FLIPRGON[]: FLIP RanGe ON *)
|
||
(* CodeRange : $81 *)
|
||
|
||
procedure TInterpreter.Ins_FLIPRGON( args : PStorage );
|
||
var
|
||
I, K, L : Int;
|
||
begin
|
||
K := args^[1];
|
||
L := args^[0];
|
||
|
||
if (K < 0) or (K >= pEC^.pts.n_points) or
|
||
(L < 0) or (L >= pEC^.pts.n_points) then
|
||
begin
|
||
pEC^.error := TT_Err_Invalid_Reference;
|
||
exit;
|
||
end;
|
||
|
||
for I := L to K do
|
||
pEC^.pts.flags^[I] := pEC^.pts.flags^[I] or TT_Flag_On_Curve;
|
||
end;
|
||
|
||
(**********************************************)
|
||
(* FLIPRGOFF : FLIP RanGe OFF *)
|
||
(* CodeRange : $82 *)
|
||
|
||
procedure TInterpreter.Ins_FLIPRGOFF( args : PStorage );
|
||
var
|
||
I, K, L : Int;
|
||
begin
|
||
K := args^[1];
|
||
L := args^[0];
|
||
|
||
if (K < 0) or (K >= pEC^.pts.n_points) or
|
||
(L < 0) or (L >= pEC^.pts.n_points) then
|
||
begin
|
||
pEC^.error := TT_Err_Invalid_Reference;
|
||
exit;
|
||
end;
|
||
|
||
for I := L to K do
|
||
pEC^.pts.flags^[I] := pEC^.pts.flags^[I] and not TT_Flag_On_Curve;
|
||
end;
|
||
|
||
|
||
|
||
function TInterpreter.Compute_Point_Displacement( out x : TT_F26dot6;
|
||
out y : TT_F26dot6;
|
||
out zone : PGlyph_Zone;
|
||
out refp : Int ) : TError;
|
||
var
|
||
zp : PGlyph_Zone;
|
||
p : Int;
|
||
d : TT_F26dot6;
|
||
begin
|
||
|
||
Compute_Point_Displacement := Success;
|
||
|
||
case opcode and 1 of
|
||
0 : begin zp := @pEC^.zp1; p := pEC^.GS.rp2; end;
|
||
1 : begin zp := @pEC^.zp0; p := pEC^.GS.rp1; end;
|
||
end;
|
||
|
||
if (p < 0) or (p >= zp^.n_points) then
|
||
begin
|
||
pEC^.error := TT_Err_Invalid_Displacement;
|
||
Compute_Point_Displacement := Failure;
|
||
exit;
|
||
end;
|
||
|
||
zone := zp;
|
||
refp := p;
|
||
|
||
d := pEC^.func_project( zp^.cur^[p], zp^.org^[p] );
|
||
|
||
x := MulDiv_Round( d, Long(pEC^.GS.freeVector.x)*$10000, pEC^.F_dot_P );
|
||
y := MulDiv_Round( d, Long(pEC^.GS.freeVector.y)*$10000, pEC^.F_dot_P );
|
||
|
||
end;
|
||
|
||
|
||
procedure TInterpreter.Move_Zp2_Point( point : Int;
|
||
dx : TT_F26dot6;
|
||
dy : TT_F26dot6 );
|
||
begin
|
||
if pEC^.GS.freeVector.x <> 0 then
|
||
begin
|
||
inc( pEC^.zp2.cur^[point].x, dx );
|
||
pEC^.zp2.flags^[point] := pEC^.zp2.flags^[point] or TT_Flag_Touched_X;
|
||
end;
|
||
|
||
if pEC^.GS.freeVector.y <> 0 then
|
||
begin
|
||
inc( pEC^.zp2.cur^[point].y, dy );
|
||
pEC^.zp2.flags^[point] := pEC^.zp2.flags^[point] or TT_Flag_Touched_Y;
|
||
end;
|
||
end;
|
||
|
||
(**********************************************)
|
||
(* SHP[a] : SHift Point by the last point *)
|
||
(* CodeRange : $32-33 *)
|
||
|
||
procedure TInterpreter.Ins_SHP( args : PStorage );
|
||
var
|
||
zp : PGlyph_Zone;
|
||
refp : Int;
|
||
|
||
dx : TT_F26dot6;
|
||
dy : TT_F26dot6;
|
||
point: Int;
|
||
begin
|
||
|
||
if Compute_Point_Displacement( dx, dy, zp, refp ) then
|
||
exit;
|
||
|
||
if top < pEC^.GS.loop then
|
||
begin
|
||
pEC^.error := TT_Err_Invalid_Reference;
|
||
exit;
|
||
end;
|
||
|
||
while pEC^.GS.loop > 0 do
|
||
begin
|
||
|
||
dec( opargs );
|
||
|
||
point := pEC^.stack^[ opargs ];
|
||
|
||
if (point < 0) or (point >= pEC^.zp2.n_points) then
|
||
begin
|
||
pEC^.error := TT_Err_Invalid_Reference;
|
||
exit;
|
||
end;
|
||
|
||
Move_Zp2_Point( point, dx, dy );
|
||
|
||
dec( pEC^.GS.loop );
|
||
|
||
end;
|
||
|
||
pEC^.GS.loop := 1;
|
||
new_top := opargs;
|
||
end;
|
||
|
||
(**********************************************)
|
||
(* SHC[a] : SHift Contour *)
|
||
(* CodeRange : $34-35 *)
|
||
|
||
procedure TInterpreter.Ins_SHC( args : PStorage );
|
||
var
|
||
zp : PGlyph_Zone;
|
||
refp : Int;
|
||
dx : TT_F26dot6;
|
||
dy : TT_F26dot6;
|
||
|
||
contour, i : Int;
|
||
|
||
first_point, last_point : Int;
|
||
begin
|
||
|
||
contour := args^[0];
|
||
|
||
if (args^[0] < 0) or (args^[0] >= pEC^.pts.n_contours ) then
|
||
begin
|
||
pEC^.error := TT_Err_Invalid_Reference;
|
||
exit;
|
||
end;
|
||
|
||
if Compute_Point_Displacement( dx, dy, zp, refp ) then
|
||
exit;
|
||
|
||
if contour = 0 then first_point := 0 else
|
||
first_point := pEC^.pts.conEnds^[contour-1]+1;
|
||
|
||
last_point := pEC^.pts.conEnds^[contour];
|
||
|
||
for i := first_point to last_point do
|
||
begin
|
||
if (zp^.cur <> pEC^.zp2.cur) or
|
||
(refp <> i ) then
|
||
|
||
Move_Zp2_Point( i, dx, dy );
|
||
end;
|
||
|
||
end;
|
||
|
||
(**********************************************)
|
||
(* SHZ[a] : SHift Zone *)
|
||
(* CodeRange : $36-37 *)
|
||
|
||
procedure TInterpreter.Ins_SHZ( args : PStorage );
|
||
var
|
||
zp : PGlyph_Zone;
|
||
refp : Int;
|
||
dx : TT_F26dot6;
|
||
dy : TT_F26dot6;
|
||
|
||
i : Int;
|
||
|
||
last_point : Int;
|
||
begin
|
||
|
||
//zone := args^[0];
|
||
|
||
if (args^[0] < 0) or (args^[0] > 1) then
|
||
begin
|
||
pEC^.error := TT_Err_Invalid_Reference;
|
||
exit;
|
||
end;
|
||
|
||
if Compute_Point_Displacement( dx, dy, zp, refp ) then
|
||
exit;
|
||
|
||
last_point := zp^.n_points-1;
|
||
|
||
for i := 0 to last_point do
|
||
begin
|
||
if (zp^.cur <> pEC^.zp2.cur) or
|
||
(refp <> i ) then
|
||
|
||
Move_Zp2_Point( i, dx, dy );
|
||
end;
|
||
|
||
end;
|
||
|
||
(**********************************************)
|
||
(* SHPIX[] : SHift points by a PIXel amount *)
|
||
(* CodeRange : $38 *)
|
||
|
||
procedure TInterpreter.Ins_SHPIX( args : PStorage );
|
||
var
|
||
dx : TT_F26dot6;
|
||
dy : TT_F26dot6;
|
||
point: Int;
|
||
begin
|
||
|
||
if top < pEC^.GS.loop then
|
||
begin
|
||
pEC^.error := TT_Err_Invalid_Reference;
|
||
exit;
|
||
end;
|
||
|
||
dx := MulDiv_Round( args^[0],
|
||
pEC^.GS.freeVector.x,
|
||
$4000 );
|
||
|
||
dy := MulDiv_Round( args^[0],
|
||
pEC^.GS.freeVector.y,
|
||
$4000 );
|
||
|
||
while pEC^.GS.loop > 0 do
|
||
begin
|
||
|
||
dec( opargs );
|
||
|
||
point := pEC^.stack^[ opargs ];
|
||
|
||
if (point < 0) or (point >= pEC^.zp2.n_points) then
|
||
begin
|
||
pEC^.error := TT_Err_Invalid_Reference;
|
||
exit;
|
||
end;
|
||
|
||
Move_Zp2_Point( point, dx, dy );
|
||
|
||
dec( pEC^.GS.loop );
|
||
|
||
end;
|
||
|
||
pEC^.GS.loop := 1;
|
||
new_top := opargs;
|
||
end;
|
||
|
||
(**********************************************)
|
||
(* MSIRP[a] : Move Stack Indirect Relative *)
|
||
(* CodeRange : $3A-$3B *)
|
||
|
||
procedure TInterpreter.Ins_MSIRP( args : PStorage );
|
||
var
|
||
point : Int;
|
||
distance : TT_F26dot6;
|
||
begin
|
||
|
||
point := args^[0];
|
||
|
||
if (args^[0] < 0) or (args^[0] >= pEC^.zp1.n_points) then
|
||
begin
|
||
pEC^.error := TT_Err_Invalid_Reference;
|
||
exit;
|
||
end;
|
||
|
||
(* XXX : UNDOCUMENTED - Twilight Zone *)
|
||
|
||
(* Again, one stupid undocumented feature found in the *)
|
||
(* twilight zone. What did these guys had in mind when *)
|
||
(* they wrote the spec ? There _must_ be another *)
|
||
(* specification than the published one !! #@%$& !! *)
|
||
|
||
if pEC^.GS.gep0 = 0 then (* if in twilight zone *)
|
||
begin
|
||
pEC^.zp1.org^[point] := pEC^.zp0.org^[pEC^.GS.rp0];
|
||
pEC^.zp1.cur^[point] := pEC^.zp1.org^[point];
|
||
end;
|
||
|
||
distance := pEC^.func_project( pEC^.zp1.cur^[point],
|
||
pEC^.zp0.cur^[pEC^.GS.rp0] );
|
||
|
||
pEC^.func_move( @pEC^.zp1, point, args^[1] - distance );
|
||
|
||
pEC^.GS.rp1 := pEC^.GS.rp0;
|
||
pEC^.GS.rp2 := point;
|
||
|
||
if opcode and 1 <> 0 then pEC^.GS.rp0 := point;
|
||
end;
|
||
|
||
(**********************************************)
|
||
(* MDAP[a] : Move Direct Absolute Point *)
|
||
(* CodeRange : $2E-$2F *)
|
||
|
||
procedure TInterpreter.Ins_MDAP( args : PStorage );
|
||
var
|
||
point : Int;
|
||
cur_dist : TT_F26dot6;
|
||
distance : TT_F26dot6;
|
||
begin
|
||
point := args^[0];
|
||
|
||
if (args^[0] < 0) or (args^[0] >= pEC^.zp0.n_points) then
|
||
begin
|
||
pEC^.error := TT_Err_Invalid_Reference;
|
||
exit;
|
||
end;
|
||
|
||
(* XXXX Is there some undocumented feature while in the *)
|
||
(* twilight zone ?? *)
|
||
|
||
if opcode and 1 <> 0 then
|
||
begin
|
||
|
||
cur_dist := pEC^.func_project( pEC^.zp0.cur^[point], Null_Vector );
|
||
|
||
distance := pEC^.func_round( cur_dist,
|
||
pEC^.metrics.compensations[0] ) -
|
||
cur_dist;
|
||
end
|
||
else
|
||
distance := 0;
|
||
|
||
pEC^.func_move( @pEC^.zp0, point, distance );
|
||
|
||
pEC^.GS.rp0 := point;
|
||
pEC^.GS.rp1 := point;
|
||
end;
|
||
|
||
(**********************************************)
|
||
(* MIAP[a] : Move Indirect Absolute Point *)
|
||
(* CodeRange : $3E-$3F *)
|
||
|
||
procedure TInterpreter.Ins_MIAP( args : PStorage );
|
||
var
|
||
cvtEntry : Int;
|
||
point : Int;
|
||
distance : TT_F26dot6;
|
||
org_dist : TT_F26dot6;
|
||
begin
|
||
cvtEntry := args^[1];
|
||
point := args^[0];
|
||
|
||
if (args^[0] < 0) or (args^[0] >= pEC^.zp0.n_points ) or
|
||
(args^[1] < 0) or (args^[1] >= pEC^.cvtSize) then
|
||
begin
|
||
pEC^.error := TT_Err_Invalid_Reference;
|
||
exit;
|
||
end;
|
||
|
||
(* Undocumented : *)
|
||
(* *)
|
||
(* The behaviour of an MIAP instruction is quite *)
|
||
(* different when used in the twilight zone^. *)
|
||
(* *)
|
||
(* First, no control value cutin test is performed *)
|
||
(* as it would fail anyway. Second, the original *)
|
||
(* point, i.e. (org_x,org_y) of zp0.point, is set *)
|
||
(* to the absolute, unrounded, distance found in *)
|
||
(* the CVT. *)
|
||
(* *)
|
||
(* This is used in the CVT programs of the Microsoft *)
|
||
(* fonts Arial, Times, etc.., in order to re-adjust *)
|
||
(* some key font heights. It allows the use of the *)
|
||
(* IP instruction in the twilight zone, which *)
|
||
(* otherwise would be "illegal" per se the specs :) *)
|
||
(* *)
|
||
(* We implement it with a special sequence for the *)
|
||
(* twilight zone. This is a bad hack, but it seems *)
|
||
(* to work.. *)
|
||
(* - David *)
|
||
|
||
distance := pEC^.func_read_cvt(cvtEntry);
|
||
|
||
if pEC^.GS.gep0 = 0 then (* If in twilight zone *)
|
||
begin
|
||
pEC^.zp0.org^[point].y := MulDiv_Round( pEC^.GS.freeVector.x,
|
||
distance,
|
||
$4000 );
|
||
|
||
pEC^.zp0.org^[point].y := MulDiv_Round( pEC^.GS.freeVector.y,
|
||
distance,
|
||
$4000 );
|
||
|
||
pEC^.zp0.cur^[point] := pEC^.zp0.org^[point];
|
||
end;
|
||
|
||
org_dist := pEC^.func_project( pEC^.zp0.cur^[point], Null_Vector );
|
||
|
||
if opcode and 1 <> 0 then (* rounding and control cutin flag *)
|
||
begin
|
||
|
||
if abs( distance-org_dist ) > pEC^.GS.control_value_cutin then
|
||
distance := org_dist;
|
||
|
||
distance := pEC^.func_round( distance,
|
||
pEC^.metrics.compensations[0] );
|
||
end;
|
||
|
||
pEC^.func_move( @pEC^.zp0, point, distance - org_dist );
|
||
|
||
pEC^.GS.rp0 := point;
|
||
pEC^.GS.rp1 := point;
|
||
|
||
end;
|
||
|
||
(**********************************************)
|
||
(* MDRP[abcde] : Move Direct Relative Point *)
|
||
(* CodeRange : $C0-$DF *)
|
||
|
||
procedure TInterpreter.Ins_MDRP( args : PStorage );
|
||
var
|
||
point : Int;
|
||
distance : TT_F26dot6;
|
||
org_dist : TT_F26dot6;
|
||
begin
|
||
point := args^[0];
|
||
|
||
if (args^[0] < 0) or (args^[0] >= pEC^.zp1.n_points) then
|
||
begin
|
||
pEC^.error := TT_Err_Invalid_Reference;
|
||
exit;
|
||
end;
|
||
|
||
(* XXXX Is there some undocumented feature while in the *)
|
||
(* twilight zone ?? *)
|
||
|
||
org_dist := pEC^.func_dualProj( pEC^.zp1.org^[point],
|
||
pEC^.zp0.org^[pEC^.GS.rp0] );
|
||
(* single width cutin test *)
|
||
|
||
if abs(org_dist) < pEC^.GS.single_width_cutin then
|
||
|
||
if org_dist >= 0 then org_dist := pEC^.GS.single_width_value
|
||
else org_dist := -pEC^.GS.single_width_value;
|
||
|
||
(* round flag *)
|
||
|
||
if opcode and 4 <> 0 then
|
||
|
||
distance := pEC^.func_round( org_dist,
|
||
pEC^.metrics.compensations[ opcode and 3 ] )
|
||
else
|
||
distance := Round_None( org_dist,
|
||
pEC^.metrics.compensations[ opcode and 3 ] );
|
||
|
||
(* minimum distance flag *)
|
||
|
||
if opcode and 8 <> 0 then
|
||
begin
|
||
|
||
if org_dist >= 0 then
|
||
|
||
if distance < pEC^.GS.minimum_distance then
|
||
distance := pEC^.GS.minimum_distance
|
||
else
|
||
else
|
||
if distance > -pEC^.GS.minimum_distance then
|
||
distance := -pEC^.GS.minimum_distance;
|
||
end;
|
||
|
||
(* now move the point *)
|
||
|
||
org_dist := pEC^.func_project( pEC^.zp1.cur^[point],
|
||
pEC^.zp0.cur^[pEC^.GS.rp0] );
|
||
|
||
pEC^.func_move( @pEC^.zp1, point, distance - org_dist );
|
||
|
||
pEC^.GS.rp1 := pEC^.GS.rp0;
|
||
pEC^.GS.rp2 := point;
|
||
|
||
if opcode and 16 <> 0 then pEC^.GS.rp0 := point;
|
||
end;
|
||
|
||
(**********************************************)
|
||
(* MIRP[abcde] : Move Indirect Relative Point *)
|
||
(* CodeRange : $E0-$FF *)
|
||
|
||
procedure TInterpreter.Ins_MIRP( args : PStorage );
|
||
var
|
||
point : Int;
|
||
cvtEntry : Int;
|
||
cvt_dist : TT_F26dot6;
|
||
distance : TT_F26dot6;
|
||
cur_dist : TT_F26dot6;
|
||
org_dist : TT_F26dot6;
|
||
begin
|
||
|
||
point := args^[0];
|
||
cvtEntry := args^[1];
|
||
|
||
(* XXX : UNDOCUMENTED => cvt[-1] = 0 ???? *)
|
||
|
||
if (args^[0] < 0 ) or (args^[0] >= pEC^.zp1.n_points) or
|
||
(args^[1] < -1) or (args^[1] >= pEC^.cvtSize) then
|
||
begin
|
||
pEC^.error := TT_Err_Invalid_Reference;
|
||
exit;
|
||
end;
|
||
|
||
if cvtEntry < 0 then
|
||
cvt_dist := 0
|
||
else
|
||
cvt_dist := pEC^.func_read_cvt(cvtEntry);
|
||
|
||
(* single width test *)
|
||
|
||
if abs(cvt_dist) < pEC^.GS.single_width_cutin then
|
||
|
||
if cvt_dist >= 0 then cvt_dist := pEC^.GS.single_width_value
|
||
else cvt_dist := -pEC^.GS.single_width_value;
|
||
|
||
(* XXX : Undocumented - twilight zone *)
|
||
|
||
if pEC^.GS.gep1 = 0 then (* if in twilight zone *)
|
||
begin
|
||
pEC^.zp1.org^[point].x := pEC^.zp0.org^[pEC^.GS.rp0].x +
|
||
MulDiv_Round( cvt_dist,
|
||
pEC^.GS.freeVector.x,
|
||
$4000 );
|
||
|
||
pEC^.zp1.org^[point].x := pEC^.zp0.org^[pEC^.GS.rp0].y +
|
||
MulDiv_Round( cvt_dist,
|
||
pEC^.GS.freeVector.y,
|
||
$4000 );
|
||
|
||
pEC^.zp1.cur^[point] := pEC^.zp1.org^[point];
|
||
end;
|
||
|
||
|
||
org_dist := pEC^.func_dualProj( pEC^.zp1.org^[point],
|
||
pEC^.zp0.org^[pEC^.GS.rp0] );
|
||
|
||
cur_dist := pEC^.func_Project( pEC^.zp1.cur^[point],
|
||
pEC^.zp0.cur^[pEC^.GS.rp0] );
|
||
|
||
(* auto-flip test *)
|
||
|
||
if pEC^.GS.auto_flip then
|
||
if (org_dist xor cvt_dist < 0) then
|
||
cvt_dist := -cvt_dist;
|
||
|
||
(* control value cutin and round *)
|
||
|
||
if opcode and 4 <> 0 then
|
||
begin
|
||
(* XXX : UNDOCUMENTED : only perform cut-in test when both *)
|
||
(* zone pointers refer to the points zone *)
|
||
|
||
if pEC^.GS.gep0 = pEC^.GS.gep1 then
|
||
if abs( cvt_dist - org_dist ) >= pEC^.GS.control_value_cutin then
|
||
cvt_dist := org_dist;
|
||
|
||
distance := pEC^.func_round( cvt_dist,
|
||
pEC^.metrics.compensations[ opcode and 3 ] );
|
||
end
|
||
else
|
||
distance := Round_None( cvt_dist,
|
||
pEC^.metrics.compensations[ opcode and 3 ] );
|
||
|
||
(* minimum distance test *)
|
||
|
||
if opcode and 8 <> 0 then
|
||
begin
|
||
if org_dist >= 0 then
|
||
|
||
if distance < pEC^.GS.minimum_distance then
|
||
distance := pEC^.GS.minimum_distance
|
||
else
|
||
else
|
||
if distance > -pEC^.GS.minimum_distance then
|
||
distance := -pEC^.GS.minimum_distance;
|
||
end;
|
||
|
||
pEC^.func_move( @pEC^.zp1, point, distance - cur_dist );
|
||
|
||
pEC^.GS.rp1 := pEC^.GS.rp0;
|
||
|
||
if opcode and 16 <> 0 then pEC^.GS.rp0 := point;
|
||
|
||
(* UNDOCUMENTED !! *)
|
||
|
||
pEC^.GS.rp2 := point;
|
||
end;
|
||
|
||
(**********************************************)
|
||
(* ALIGNRP[] : ALIGN Relative Point *)
|
||
(* CodeRange : $3C *)
|
||
|
||
procedure TInterpreter.Ins_ALIGNRP(args : PStorage );
|
||
var
|
||
point : Int;
|
||
distance : TT_F26dot6;
|
||
begin
|
||
if top < pEC^.GS.loop then
|
||
begin
|
||
pEC^.error := TT_Err_Invalid_Reference;
|
||
exit;
|
||
end;
|
||
|
||
while pEC^.GS.loop > 0 do
|
||
begin
|
||
|
||
dec( opargs );
|
||
|
||
point := pEC^.stack^[ opargs ];
|
||
|
||
if (point < 0) or (point >= pEC^.zp1.n_points) then
|
||
begin
|
||
pEC^.error := TT_Err_Invalid_Reference;
|
||
exit;
|
||
end;
|
||
|
||
distance := pEC^.func_project( pEC^.zp1.cur^[point],
|
||
pEC^.zp0.cur^[pEC^.GS.rp0] );
|
||
|
||
pEC^.func_move( @pEC^.zp1, point, -distance );
|
||
|
||
dec( pEC^.GS.loop );
|
||
end;
|
||
|
||
pEC^.GS.loop := 1;
|
||
new_top := opargs;
|
||
end;
|
||
|
||
(**********************************************)
|
||
(* AA[] : Adjust Angle *)
|
||
(* CodeRange : $7F *)
|
||
|
||
procedure TInterpreter.Ins_AA( args : PStorage );
|
||
begin
|
||
(* Intentional - no longer supported *)
|
||
end;
|
||
|
||
(**********************************************)
|
||
(* ISECT[] : moves point to InterSECTion *)
|
||
(* CodeRange : $0F *)
|
||
|
||
procedure TInterpreter.Ins_ISECT( args : PStorage );
|
||
var
|
||
point : Int;
|
||
a0, a1 : Int;
|
||
b0, b1 : Int;
|
||
|
||
discriminant : TT_F26dot6;
|
||
dx, dy,
|
||
dax, day,
|
||
dbx, dby : TT_F26dot6;
|
||
|
||
val : TT_F26dot6;
|
||
|
||
R : TT_Vector;
|
||
|
||
begin
|
||
|
||
point := args^[0];
|
||
a0 := args^[1];
|
||
a1 := args^[2];
|
||
b0 := args^[3];
|
||
b1 := args^[4];
|
||
|
||
if (b0 >= pEC^.zp0.n_points) or (b1 >= pEC^.zp0.n_points) or
|
||
(a0 >= pEC^.zp1.n_points) or (a1 >= pEC^.zp1.n_points) or
|
||
(point >= pEC^.zp0.n_points) then
|
||
begin
|
||
pEC^.error := TT_Err_Invalid_Reference;
|
||
exit;
|
||
end;
|
||
(*
|
||
if Normalize( pEC^.zp1.cur_x^[a1] - pEC^.zp1.cur_x^[a0],
|
||
pEC^.zp1.cur_y^[a1] - pEC^.zp1.cur_y^[a0],
|
||
U )
|
||
and
|
||
Normalize( - pEC^.zp0.cur_x^[b1] - pEC^.zp0.cur_x^[b0],
|
||
pEC^.zp0.cur_y^[b1] - pEC^.zp0.cur_y^[b0],
|
||
V )
|
||
then
|
||
begin
|
||
|
||
dx := MulDiv_Round( pEC^.zp0.cur_x^[b0] -
|
||
pEC^.zp1.cur_x^[a0],
|
||
V.x,
|
||
$4000 ) +
|
||
|
||
MulDiv_Round( pEC^.zp0.cur_y^[b0] -
|
||
pEC^.zp1.cur_y^[a0],
|
||
V.y,
|
||
$4000 );
|
||
|
||
dy := MulDiv_Round( U.x, V.x, $4000 ) +
|
||
MulDiv_Round( U.y, V.y, $4000 );
|
||
|
||
if dy <> 0 then
|
||
begin
|
||
dx := MulDiv_Round( dx, $4000, dy );
|
||
|
||
pEC^.zp2.flags^[point] := pEC^.zp2.flags^[point] or
|
||
TT_Flag_Touched_Both;
|
||
|
||
pEC^.zp2.cur_x^[point] := pEC^.zp1.cur_x^[a0] +
|
||
|
||
MulDiv_Round( dx, U.x, $4000 );
|
||
|
||
pEC^.zp2.cur_y^[point] := pEC^.zp1.cur_y^[a0] +
|
||
|
||
MulDiv_Round( dx, U.y, $4000 );
|
||
|
||
exit;
|
||
end;
|
||
end;
|
||
*)
|
||
dbx := pEC^.zp0.cur^[b1].x - pEC^.zp0.cur^[b0].x;
|
||
dby := pEC^.zp0.cur^[b1].y - pEC^.zp0.cur^[b0].y;
|
||
|
||
dax := pEC^.zp1.cur^[a1].x - pEC^.zp1.cur^[a0].x;
|
||
day := pEC^.zp1.cur^[a1].y - pEC^.zp1.cur^[a0].y;
|
||
|
||
dx := pEC^.zp0.cur^[b0].x - pEC^.zp1.cur^[a0].x;
|
||
dy := pEC^.zp0.cur^[b0].y - pEC^.zp1.cur^[a0].y;
|
||
|
||
pEC^.zp2.flags^[point] := pEC^.zp2.flags^[point] or
|
||
TT_Flag_Touched_Both;
|
||
|
||
discriminant := MulDiv( dax, -dby, $40 ) +
|
||
MulDiv( day, dbx, $40 );
|
||
|
||
if abs(discriminant) >= $40 then
|
||
begin
|
||
|
||
val := MulDiv( dx, -dby, $40 ) +
|
||
MulDiv( dy, dbx, $40 );
|
||
|
||
R.x := MulDiv( val, dax, discriminant );
|
||
R.y := MulDiv( val, day, discriminant );
|
||
|
||
pEC^.zp2.cur^[point].x := pEC^.zp1.cur^[a0].x + R.x;
|
||
pEC^.zp2.cur^[point].y := pEC^.zp1.cur^[a0].y + R.y;
|
||
end
|
||
else
|
||
begin
|
||
|
||
(* else, take the middle of the middles of A and B *)
|
||
|
||
pEC^.zp2.cur^[point].x := ( pEC^.zp1.cur^[a0].x +
|
||
pEC^.zp1.cur^[a1].x +
|
||
pEC^.zp0.cur^[b0].x +
|
||
pEC^.zp0.cur^[b1].x ) div 4;
|
||
|
||
pEC^.zp2.cur^[point].y := ( pEC^.zp1.cur^[a0].y +
|
||
pEC^.zp1.cur^[a1].y +
|
||
pEC^.zp0.cur^[b0].y +
|
||
pEC^.zp0.cur^[b1].y ) div 4;
|
||
end;
|
||
end;
|
||
|
||
(**********************************************)
|
||
(* ALIGNPTS[] : ALIGN PoinTS *)
|
||
(* CodeRange : $27 *)
|
||
|
||
procedure TInterpreter.Ins_ALIGNPTS( args : PStorage );
|
||
var
|
||
p1, p2 : Int;
|
||
distance : TT_F26dot6;
|
||
begin
|
||
p1 := args^[0];
|
||
p2 := args^[1];
|
||
|
||
if (args^[0] < 0) or (args^[0] >= pEC^.zp1.n_points) or
|
||
(args^[1] < 0) or (args^[1] >= pEC^.zp0.n_points) then
|
||
begin
|
||
pEC^.error := TT_Err_Invalid_Reference;
|
||
exit;
|
||
end;
|
||
|
||
distance := pEC^.func_project( pEC^.zp0.cur^[p2],
|
||
pEC^.zp1.cur^[p1] ) div 2;
|
||
|
||
pEC^.func_move( @pEC^.zp1, p1, distance );
|
||
pEC^.func_move( @pEC^.zp0, p2, -distance );
|
||
end;
|
||
|
||
(**********************************************)
|
||
(* IP[] : Interpolate Point *)
|
||
(* CodeRange : $39 *)
|
||
|
||
procedure TInterpreter.Ins_IP( args : PStorage );
|
||
var
|
||
org_a : TT_F26dot6;
|
||
org_b : TT_F26dot6;
|
||
org_x : TT_F26dot6;
|
||
cur_a : TT_F26dot6;
|
||
cur_b : TT_F26dot6;
|
||
cur_x : TT_F26dot6;
|
||
|
||
distance : TT_F26dot6;
|
||
|
||
point : Int;
|
||
begin
|
||
|
||
if top < pEC^.GS.loop then
|
||
begin
|
||
pEC^.error := TT_Err_Invalid_Reference;
|
||
exit;
|
||
end;
|
||
|
||
org_a := pEC^.func_dualProj( pEC^.zp0.org^[pEC^.GS.rp1], Null_Vector );
|
||
|
||
org_b := pEC^.func_dualProj( pEC^.zp1.org^[pEC^.GS.rp2], Null_Vector );
|
||
|
||
cur_a := pEC^.func_project( pEC^.zp0.cur^[pEC^.GS.rp1], Null_Vector );
|
||
|
||
cur_b := pEC^.func_project( pEC^.zp1.cur^[pEC^.GS.rp2], Null_Vector );
|
||
|
||
while pEC^.GS.loop > 0 do
|
||
begin
|
||
|
||
dec( opargs );
|
||
|
||
point := pEC^.stack^[ opargs ];
|
||
|
||
org_x := pEC^.func_dualProj( pEC^.zp2.org^[point], Null_Vector );
|
||
|
||
cur_x := pEC^.func_project( pEC^.zp2.cur^[point], Null_Vector );
|
||
|
||
if (( org_a <= org_b ) and ( org_x <= org_a )) or
|
||
(( org_a > org_b ) and ( org_x >= org_a )) then
|
||
begin
|
||
distance := ( cur_a - org_a ) + ( org_x - cur_x );
|
||
end
|
||
else
|
||
if (( org_a <= org_b ) and ( org_x >= org_b )) or
|
||
(( org_a > org_b ) and ( org_x < org_b )) then
|
||
begin
|
||
distance := ( cur_b - org_b ) + ( org_x - cur_x );
|
||
end
|
||
else
|
||
begin
|
||
(* note : it seems that rounding this value isn't a good *)
|
||
(* idea ( width of capital 'S' in Times *)
|
||
|
||
distance := MulDiv( cur_b - cur_a,
|
||
org_x - org_a,
|
||
org_b - org_a ) + ( cur_a - cur_x );
|
||
end;
|
||
|
||
pEC^.func_move( @pEC^.zp2, point, distance );
|
||
|
||
dec( pEC^.GS.loop );
|
||
end;
|
||
|
||
pEC^.GS.loop := 1;
|
||
new_top := opargs;
|
||
end;
|
||
|
||
(**********************************************)
|
||
(* UTP[a] : UnTouch Point *)
|
||
(* CodeRange : $29 *)
|
||
|
||
procedure TInterpreter.Ins_UTP( args : PStorage );
|
||
var
|
||
mask : Byte;
|
||
begin
|
||
if (args^[0] < 0) or (args^[0] >= pEC^.zp0.n_points) then
|
||
begin
|
||
pEC^.error := TT_Err_Invalid_Reference;
|
||
exit;
|
||
end;
|
||
|
||
mask := $FF;
|
||
|
||
if pEC^.GS.freeVector.x <> 0 then mask := mask and not TT_Flag_Touched_X;
|
||
if pEC^.GS.freeVector.y <> 0 then mask := mask and not TT_Flag_Touched_Y;
|
||
|
||
pEC^.zp0.flags^[args^[0]] := pEC^.zp0.flags^[args^[0]] and mask;
|
||
end;
|
||
|
||
(**********************************************)
|
||
(* IUP[a] : Interpolate Untouched Points *)
|
||
(* CodeRange : $30-$31 *)
|
||
|
||
procedure TInterpreter.Ins_IUP( args : PStorage );
|
||
var
|
||
mask : byte;
|
||
|
||
first_point, (* first point of contour *)
|
||
end_point, (* end point (last+1) of contour *)
|
||
|
||
first_touched, (* first touched point in contour *)
|
||
cur_touched, (* current touched point in contour *)
|
||
|
||
point, (* current point *)
|
||
contour : Int; (* current contour *)
|
||
|
||
orgs, (* original and current coordinate *)
|
||
curs : TT_Points; (* arrays *)
|
||
|
||
procedure Shift_X( p1, p2, p : Int );
|
||
var
|
||
i : Int;
|
||
x : TT_F26dot6;
|
||
begin
|
||
x := curs^[p].x - orgs^[p].x;
|
||
|
||
for i := p1 to p-1 do inc( curs^[i].x, x );
|
||
for i := p+1 to p2 do inc( curs^[i].x, x );
|
||
end;
|
||
|
||
procedure Shift_Y( p1, p2, p : Int );
|
||
var
|
||
i : Int;
|
||
y : TT_F26dot6;
|
||
begin
|
||
y := curs^[p].y - orgs^[p].y;
|
||
|
||
for i := p1 to p-1 do inc( curs^[i].y, y );
|
||
for i := p+1 to p2 do inc( curs^[i].y, y );
|
||
end;
|
||
|
||
|
||
procedure Interp_X( p1, p2, ref1, ref2 : Int );
|
||
var
|
||
i : Int;
|
||
x, x1, x2, d1, d2 : TT_F26dot6;
|
||
begin
|
||
|
||
if p1 > p2 then exit;
|
||
|
||
x1 := orgs^[ref1].x; d1 := curs^[ref1].x - orgs^[ref1].x;
|
||
x2 := orgs^[ref2].x; d2 := curs^[ref2].x - orgs^[ref2].x;
|
||
|
||
if x1 = x2 then
|
||
for i := p1 to p2 do
|
||
begin
|
||
x := orgs^[i].x;
|
||
if x <= x1 then x := x + d1
|
||
else x := x + d2;
|
||
|
||
curs^[i].x := x;
|
||
end
|
||
|
||
else
|
||
if x1 < x2 then
|
||
|
||
for i := p1 to p2 do
|
||
begin
|
||
x := orgs^[i].x;
|
||
|
||
if (x <= x1) then x := x + d1
|
||
else
|
||
if (x >= x2) then x := x + d2
|
||
else
|
||
x := curs^[ref1].x +
|
||
MulDiv( x-x1, curs^[ref2].x-curs^[ref1].x, x2-x1 );
|
||
|
||
curs^[i].x := x;
|
||
end
|
||
else
|
||
|
||
(* x2 < x1 *)
|
||
|
||
for i := p1 to p2 do
|
||
begin
|
||
x := orgs^[i].x;
|
||
|
||
if ( x <= x2 ) then x := x + d2
|
||
else
|
||
if ( x >= x1 ) then x := x + d1
|
||
else
|
||
x := curs^[ref1].x +
|
||
MulDiv( x-x1, curs^[ref2].x-curs^[ref1].x, x2-x1 );
|
||
|
||
curs^[i].x := x;
|
||
end;
|
||
end;
|
||
|
||
procedure Interp_Y( p1, p2, ref1, ref2 : Int );
|
||
var
|
||
i : Int;
|
||
y, y1, y2, d1, d2 : TT_F26dot6;
|
||
begin
|
||
|
||
if p1 > p2 then exit;
|
||
|
||
y1 := orgs^[ref1].y; d1 := curs^[ref1].y - orgs^[ref1].y;
|
||
y2 := orgs^[ref2].y; d2 := curs^[ref2].y - orgs^[ref2].y;
|
||
|
||
if y1 = y2 then
|
||
for i := p1 to p2 do
|
||
begin
|
||
y := orgs^[i].y;
|
||
if y <= y1 then y := y + d1
|
||
else y := y + d2;
|
||
|
||
curs^[i].y := y;
|
||
end
|
||
|
||
else
|
||
if y1 < y2 then
|
||
|
||
for i := p1 to p2 do
|
||
begin
|
||
y := orgs^[i].y;
|
||
|
||
if (y <= y1) then y := y + d1
|
||
else
|
||
if (y >= y2) then y := y + d2
|
||
else
|
||
y := curs^[ref1].y +
|
||
MulDiv( y-y1, curs^[ref2].y-curs^[ref1].y, y2-y1 );
|
||
|
||
curs^[i].y := y;
|
||
end
|
||
else
|
||
|
||
(* y2 < y1 *)
|
||
|
||
for i := p1 to p2 do
|
||
begin
|
||
y := orgs^[i].y;
|
||
|
||
if ( y <= y2 ) then y := y + d2
|
||
else
|
||
if ( y >= y1 ) then y := y + d1
|
||
else
|
||
y := curs^[ref1].y +
|
||
MulDiv( y-y1, curs^[ref2].y-curs^[ref1].y, y2-y1 );
|
||
|
||
curs^[i].y := y;
|
||
end;
|
||
end;
|
||
|
||
begin
|
||
orgs := pEC^.pts.org;
|
||
curs := pEC^.pts.cur;
|
||
|
||
case opcode and 1 of
|
||
0 : mask := TT_Flag_Touched_Y;
|
||
1 : mask := TT_Flag_Touched_X;
|
||
end;
|
||
|
||
with pEC^ do
|
||
begin
|
||
|
||
contour := 0;
|
||
point := 0;
|
||
|
||
repeat
|
||
|
||
end_point := pts.conEnds^[contour];
|
||
first_point := point;
|
||
|
||
while ( point <= end_point ) and
|
||
( pts.flags^[point] and mask = 0 ) do inc(point);
|
||
|
||
if point <= end_point then
|
||
begin
|
||
|
||
first_touched := point;
|
||
cur_touched := point;
|
||
|
||
inc( point );
|
||
|
||
while ( point <= end_point ) do
|
||
begin
|
||
if pts.flags^[point] and mask <> 0 then
|
||
begin
|
||
if opcode and 1 <> 0 then
|
||
Interp_X( cur_touched+1, point-1, cur_touched, point )
|
||
else
|
||
Interp_Y( cur_touched+1, point-1, cur_touched, point );
|
||
|
||
cur_touched := point;
|
||
end;
|
||
|
||
inc( point );
|
||
end;
|
||
|
||
if cur_touched = first_touched then
|
||
if opcode and 1 <> 0 then
|
||
Shift_X( first_point, end_point, cur_touched )
|
||
else
|
||
Shift_Y( first_point, end_point, cur_touched )
|
||
else
|
||
begin
|
||
if opcode and 1 <> 0 then
|
||
begin
|
||
interp_x( cur_touched+1, end_point, cur_touched, first_touched );
|
||
interp_x( first_point, first_touched-1, cur_touched, first_touched );
|
||
end
|
||
else
|
||
begin
|
||
interp_y( cur_touched+1, end_point, cur_touched, first_touched );
|
||
interp_y( first_point, first_touched-1, cur_touched, first_touched );
|
||
end;
|
||
end;
|
||
|
||
end;
|
||
|
||
inc( contour );
|
||
|
||
until contour >= pts.n_contours;
|
||
|
||
end;
|
||
|
||
end;
|
||
|
||
(**********************************************)
|
||
(* DELTAPn[] : DELTA Exceptions P1, P2, P3 *)
|
||
(* CodeRange : $5D,$71,$72 *)
|
||
|
||
procedure TInterpreter.Ins_DELTAP( args : PStorage );
|
||
var
|
||
nump : Int;
|
||
k : Int;
|
||
A, B, C :Int;
|
||
begin
|
||
|
||
nump := args^[0];
|
||
|
||
for K := 1 to nump do
|
||
begin
|
||
if opargs < 2 then
|
||
begin
|
||
pEC^.error := TT_Err_Too_Few_Arguments;
|
||
exit;
|
||
end;
|
||
|
||
dec( opargs, 2 );
|
||
|
||
A := pEC^.stack^[opargs+1];
|
||
B := pEC^.stack^[ opargs ];
|
||
|
||
(* XXX : *)
|
||
(* some commonly fonts have broke programs where the *)
|
||
(* the point reference has an invalid value. Here, we *)
|
||
(* simply ignore them, because a DeltaP won't change *)
|
||
(* a glyph shape dramatically.. *)
|
||
(* *)
|
||
|
||
if A < pEC^.zp0.n_points then
|
||
begin
|
||
C := ( B and $F0 ) shr 4;
|
||
|
||
Case opcode of
|
||
$5D : ;
|
||
$71 : C := C+16;
|
||
$72 : C := C+32;
|
||
end;
|
||
|
||
C := C + pEC^.GS.delta_Base;
|
||
|
||
if GET_Ppem = C then
|
||
begin
|
||
B := (B and $F) - 8;
|
||
if B >= 0 then B := B+1;
|
||
B := ( B*64 ) div ( 1 shl pEC^.GS.delta_Shift );
|
||
|
||
pEC^.func_move( @pEC^.zp0, A, B );
|
||
end;
|
||
end;
|
||
|
||
end;
|
||
|
||
new_top := opargs;
|
||
end;
|
||
|
||
|
||
(**********************************************)
|
||
(* DELTACn[] : DELTA Exceptions C1, C2, C3 *)
|
||
(* CodeRange : $73,$74,$75 *)
|
||
|
||
procedure TInterpreter.Ins_DELTAC( args : PStorage );
|
||
var
|
||
nump : Int;
|
||
k : Int;
|
||
A, B, C :Int;
|
||
begin
|
||
|
||
nump := args^[0];
|
||
|
||
for K := 1 to nump do
|
||
begin
|
||
if opargs < 2 then
|
||
begin
|
||
pEC^.error := TT_Err_Too_Few_Arguments;
|
||
exit;
|
||
end;
|
||
|
||
dec( opargs, 2 );
|
||
|
||
A := pEC^.stack^[opargs+1];
|
||
B := pEC^.stack^[ opargs ];
|
||
|
||
if A >= pEC^.cvtSize then
|
||
begin
|
||
pEC^.error := TT_Err_Invalid_Reference;
|
||
exit;
|
||
end;
|
||
|
||
C := ( B and $F0 ) shr 4;
|
||
|
||
Case opcode of
|
||
$73 : ;
|
||
$74 : C := C+16;
|
||
$75 : C := C+32;
|
||
end;
|
||
|
||
C := C + pEC^.GS.delta_Base;
|
||
|
||
if GET_Ppem = C then
|
||
begin
|
||
B := (B and $F) - 8;
|
||
if B >= 0 then B := B+1;
|
||
B := ( B*64 ) div ( 1 shl pEC^.GS.delta_Shift );
|
||
|
||
pEC^.func_move_cvt( A, B );
|
||
end;
|
||
end;
|
||
|
||
new_top := opargs;
|
||
end;
|
||
|
||
(****************************************************************)
|
||
(* *)
|
||
(* MISC. INSTRUCTIONS *)
|
||
(* *)
|
||
(****************************************************************)
|
||
|
||
(***********************************************************)
|
||
(* DEBUG[] : DEBUG. Unsupported *)
|
||
(* CodeRange : $4F *)
|
||
|
||
(* NOTE : The original instruction pops a value from the stack *)
|
||
|
||
procedure TInterpreter.Ins_DEBUG( args : PStorage );
|
||
begin
|
||
pEC^.error := TT_Err_Debug_Opcode;
|
||
end;
|
||
|
||
(**********************************************)
|
||
(* GETINFO[] : GET INFOrmation *)
|
||
(* CodeRange : $88 *)
|
||
|
||
procedure TInterpreter.Ins_GETINFO( args : PStorage );
|
||
var
|
||
K : Int;
|
||
begin
|
||
K := 0;
|
||
|
||
if args^[0] and 1 <> 0 then K := 3;
|
||
(* We return then Windows 3.1 version number *)
|
||
(* for the font scaler *)
|
||
|
||
if false then {%H-}K := K or $80;
|
||
(* Has the glyph been rotated ? *)
|
||
(* XXXX TO DO *)
|
||
|
||
if false then {%H-}K := K or $100;
|
||
(* Has the glyph been stretched ? *)
|
||
(* XXXX TO DO *)
|
||
|
||
args^[0] := K;
|
||
end;
|
||
|
||
|
||
procedure TInterpreter.Ins_UNKNOWN( args : PStorage );
|
||
begin
|
||
pEC^.error := TT_Err_Invalid_Opcode;
|
||
end;
|
||
|
||
function TInterpreter.GetLastInstruction: string;
|
||
begin
|
||
result := Instruct_Dispatch[opcode].name;
|
||
end;
|
||
|
||
constructor TInterpreter.Create(AContext: PExec_Context; AEnableLog: boolean);
|
||
var numIns: integer;
|
||
procedure addIns(AName: string; AFunc: TInstruction_Function);
|
||
begin
|
||
if numIns < high(Instruct_Dispatch)+1 then
|
||
begin
|
||
with Instruct_Dispatch[numIns] do
|
||
begin
|
||
name := AName;
|
||
func := AFunc;
|
||
end;
|
||
inc(numIns);
|
||
end else
|
||
raise exception.Create('Too much instructions');
|
||
end;
|
||
|
||
begin
|
||
pEC := AContext;
|
||
|
||
enableLog:= AEnableLog;
|
||
if enableLog then instructionLog := TStringList.Create;
|
||
|
||
numIns := low(Instruct_Dispatch);
|
||
addIns('SVTCA y', Ins_SVTCA);
|
||
addIns('SVTCA x', Ins_SVTCA);
|
||
addIns('SPvTCA y', Ins_SPVTCA);
|
||
addIns('SPvTCA x', Ins_SPVTCA);
|
||
addIns('SFvTCA y', Ins_SFVTCA);
|
||
addIns('SFvTCA x', Ins_SFVTCA);
|
||
addIns('SPvTL //', Ins_SPVTL);
|
||
addIns('SPvTL +', Ins_SPVTL);
|
||
addIns('SFvTL //', Ins_SFVTL);
|
||
addIns('SFvTL +', Ins_SFVTL);
|
||
addIns('SPvFS', Ins_SPVFS);
|
||
addIns('SFvFS', Ins_SFVFS);
|
||
addIns('GPV', Ins_GPV);
|
||
addIns('GFV', Ins_GFV);
|
||
addIns('SFvTPv', Ins_SFVTPV);
|
||
addIns('ISECT', Ins_ISECT);
|
||
|
||
addIns('SRP0', Ins_SRP0);
|
||
addIns('SRP1', Ins_SRP1);
|
||
addIns('SRP2', Ins_SRP2);
|
||
addIns('SZP0', Ins_SZP0);
|
||
addIns('SZP1', Ins_SZP1);
|
||
addIns('SZP2', Ins_SZP2);
|
||
addIns('SZPS', Ins_SZPS);
|
||
addIns('SLOOP', Ins_SLOOP);
|
||
addIns('RTG', Ins_RTG);
|
||
addIns('RTHG', Ins_RTHG);
|
||
addIns('SMD', Ins_SMD);
|
||
addIns('ELSE', Ins_ELSE);
|
||
addIns('JMPR', Ins_JMPR);
|
||
addIns('SCvTCi', Ins_SCVTCI);
|
||
addIns('SSwCi', Ins_SSWCI);
|
||
addIns('SSW', Ins_SSW);
|
||
|
||
addIns('DUP', Ins_DUP);
|
||
addIns('POP', Ins_POP);
|
||
addIns('CLEAR', Ins_CLEAR);
|
||
addIns('SWAP', Ins_SWAP);
|
||
addIns('DEPTH', Ins_DEPTH);
|
||
addIns('CINDEX', Ins_CINDEX);
|
||
addIns('MINDEX', Ins_MINDEX);
|
||
addIns('AlignPTS', Ins_ALIGNPTS);
|
||
addIns('INS_$28', Ins_UNKNOWN);
|
||
addIns('UTP', Ins_UTP);
|
||
addIns('LOOPCALL', Ins_LOOPCALL);
|
||
addIns('CALL', Ins_CALL);
|
||
addIns('FDEF', Ins_FDEF);
|
||
addIns('ENDF', Ins_ENDF);
|
||
addIns('MDAP[0]', Ins_MDAP);
|
||
addIns('MDAP[1]', Ins_MDAP);
|
||
|
||
addIns('IUP[0]', Ins_IUP);
|
||
addIns('IUP[1]', Ins_IUP);
|
||
addIns('SHP[0]', Ins_SHP);
|
||
addIns('SHP[1]', Ins_SHP);
|
||
addIns('SHC[0]', Ins_SHC);
|
||
addIns('SHC[1]', Ins_SHC);
|
||
addIns('SHZ[0]', Ins_SHZ);
|
||
addIns('SHZ[1]', Ins_SHZ);
|
||
addIns('SHPIX', Ins_SHPIX);
|
||
addIns('IP', Ins_IP);
|
||
addIns('MSIRP[0]', Ins_MSIRP);
|
||
addIns('MSIRP[1]', Ins_MSIRP);
|
||
addIns('AlignRP', Ins_ALIGNRP);
|
||
addIns('RTDG', Ins_RTDG);
|
||
addIns('MIAP[0]', Ins_MIAP);
|
||
addIns('MIAP[1]', Ins_MIAP);
|
||
|
||
addIns('NPushB', Ins_NPUSHB);
|
||
addIns('NPushW', Ins_NPUSHW);
|
||
addIns('WS', Ins_WS);
|
||
addIns('RS', Ins_RS);
|
||
addIns('WCvtP', Ins_WCVTP);
|
||
addIns('RCvt', Ins_RCVT);
|
||
addIns('GC[0]', Ins_GC);
|
||
addIns('GC[1]', Ins_GC);
|
||
addIns('SCFS', Ins_SCFS);
|
||
addIns('MD[0]', Ins_MD);
|
||
addIns('MD[1]', Ins_MD);
|
||
addIns('MPPEM', Ins_MPPEM);
|
||
addIns('MPS', Ins_MPS);
|
||
addIns('FlipON', Ins_FLIPON);
|
||
addIns('FlipOFF', Ins_FLIPOFF);
|
||
addIns('DEBUG', Ins_DEBUG);
|
||
|
||
addIns('LT', Ins_LT);
|
||
addIns('LTEQ', Ins_LTEQ);
|
||
addIns('GT', Ins_GT);
|
||
addIns('GTEQ', Ins_GTEQ);
|
||
addIns('EQ', Ins_EQ);
|
||
addIns('NEQ', Ins_NEQ);
|
||
addIns('ODD', Ins_ODD);
|
||
addIns('EVEN', Ins_EVEN);
|
||
addIns('IF', Ins_IF);
|
||
addIns('EIF', Ins_EIF);
|
||
addIns('AND', Ins_AND);
|
||
addIns('OR', Ins_OR);
|
||
addIns('NOT', Ins_NOT);
|
||
addIns('DeltaP1', Ins_DELTAP);
|
||
addIns('SDB', Ins_SDB);
|
||
addIns('SDS', Ins_SDS);
|
||
|
||
addIns('ADD', Ins_ADD);
|
||
addIns('SUB', Ins_SUB);
|
||
addIns('DIV', Ins_DIV);
|
||
addIns('MUL', Ins_MUL);
|
||
addIns('ABS', Ins_ABS);
|
||
addIns('NEG', Ins_NEG);
|
||
addIns('FLOOR', Ins_FLOOR);
|
||
addIns('CEILING', Ins_CEILING);
|
||
addIns('ROUND[0]', Ins_ROUND);
|
||
addIns('ROUND[1]', Ins_ROUND);
|
||
addIns('ROUND[2]', Ins_ROUND);
|
||
addIns('ROUND[3]', Ins_ROUND);
|
||
addIns('NROUND[0]', Ins_ROUND);
|
||
addIns('NROUND[1]', Ins_ROUND);
|
||
addIns('NROUND[2]', Ins_ROUND);
|
||
addIns('NROUND[3]', Ins_ROUND);
|
||
|
||
addIns('WCvtF', Ins_WCVTF);
|
||
addIns('DeltaP2', Ins_DELTAP);
|
||
addIns('DeltaP3', Ins_DELTAP);
|
||
addIns('DeltaCn[0]', Ins_DELTAC);
|
||
addIns('DeltaCn[1]', Ins_DELTAC);
|
||
addIns('DeltaCn[2]', Ins_DELTAC);
|
||
addIns('SROUND', Ins_SROUND);
|
||
addIns('S45Round', Ins_S45ROUND);
|
||
addIns('JROT', Ins_JROT);
|
||
addIns('JROF', Ins_JROF);
|
||
addIns('ROFF', Ins_ROFF);
|
||
addIns('INS_$7B', Ins_UNKNOWN);
|
||
addIns('RUTG', Ins_RUTG);
|
||
addIns('RDTG', Ins_RDTG);
|
||
addIns('SANGW', Ins_SANGW);
|
||
addIns('AA', Ins_AA);
|
||
|
||
addIns('FlipPT', Ins_FLIPPT);
|
||
addIns('FlipRgON', Ins_FLIPRGON);
|
||
addIns('FlipRgOFF', Ins_FLIPRGOFF);
|
||
addIns('INS_$83', Ins_UNKNOWN);
|
||
addIns('INS_$84', Ins_UNKNOWN);
|
||
addIns('ScanCTRL', Ins_SCANCTRL);
|
||
addIns('SDPVTL[0]', Ins_SDPVTL);
|
||
addIns('SDPVTL[1]', Ins_SDPVTL);
|
||
addIns('GetINFO', Ins_GETINFO);
|
||
addIns('IDEF', Ins_IDEF);
|
||
addIns('ROLL', Ins_ROLL);
|
||
addIns('MAX', Ins_MAX);
|
||
addIns('MIN', Ins_MIN);
|
||
addIns('ScanTYPE', Ins_SCANTYPE);
|
||
addIns('InstCTRL', Ins_INSTCTRL);
|
||
addIns('INS_$8F', Ins_UNKNOWN);
|
||
|
||
addIns('INS_$90', Ins_UNKNOWN);
|
||
addIns('INS_$91', Ins_UNKNOWN);
|
||
addIns('INS_$92', Ins_UNKNOWN);
|
||
addIns('INS_$93', Ins_UNKNOWN);
|
||
addIns('INS_$94', Ins_UNKNOWN);
|
||
addIns('INS_$95', Ins_UNKNOWN);
|
||
addIns('INS_$96', Ins_UNKNOWN);
|
||
addIns('INS_$97', Ins_UNKNOWN);
|
||
addIns('INS_$98', Ins_UNKNOWN);
|
||
addIns('INS_$99', Ins_UNKNOWN);
|
||
addIns('INS_$9A', Ins_UNKNOWN);
|
||
addIns('INS_$9B', Ins_UNKNOWN);
|
||
addIns('INS_$9C', Ins_UNKNOWN);
|
||
addIns('INS_$9D', Ins_UNKNOWN);
|
||
addIns('INS_$9E', Ins_UNKNOWN);
|
||
addIns('INS_$9F', Ins_UNKNOWN);
|
||
|
||
addIns('INS_$A0', Ins_UNKNOWN);
|
||
addIns('INS_$A1', Ins_UNKNOWN);
|
||
addIns('INS_$A2', Ins_UNKNOWN);
|
||
addIns('INS_$A3', Ins_UNKNOWN);
|
||
addIns('INS_$A4', Ins_UNKNOWN);
|
||
addIns('INS_$A5', Ins_UNKNOWN);
|
||
addIns('INS_$A6', Ins_UNKNOWN);
|
||
addIns('INS_$A7', Ins_UNKNOWN);
|
||
addIns('INS_$A8', Ins_UNKNOWN);
|
||
addIns('INS_$A9', Ins_UNKNOWN);
|
||
addIns('INS_$AA', Ins_UNKNOWN);
|
||
addIns('INS_$AB', Ins_UNKNOWN);
|
||
addIns('INS_$AC', Ins_UNKNOWN);
|
||
addIns('INS_$AD', Ins_UNKNOWN);
|
||
addIns('INS_$AE', Ins_UNKNOWN);
|
||
addIns('INS_$AF', Ins_UNKNOWN);
|
||
|
||
addIns('PushB[0]', Ins_PUSHB);
|
||
addIns('PushB[1]', Ins_PUSHB);
|
||
addIns('PushB[2]', Ins_PUSHB);
|
||
addIns('PushB[3]', Ins_PUSHB);
|
||
addIns('PushB[4]', Ins_PUSHB);
|
||
addIns('PushB[5]', Ins_PUSHB);
|
||
addIns('PushB[6]', Ins_PUSHB);
|
||
addIns('PushB[7]', Ins_PUSHB);
|
||
addIns('PushW[0]', Ins_PUSHW);
|
||
addIns('PushW[1]', Ins_PUSHW);
|
||
addIns('PushW[2]', Ins_PUSHW);
|
||
addIns('PushW[3]', Ins_PUSHW);
|
||
addIns('PushW[4]', Ins_PUSHW);
|
||
addIns('PushW[5]', Ins_PUSHW);
|
||
addIns('PushW[6]', Ins_PUSHW);
|
||
addIns('PushW[7]', Ins_PUSHW);
|
||
|
||
addIns('MDRP[00]', Ins_MDRP);
|
||
addIns('MDRP[01]', Ins_MDRP);
|
||
addIns('MDRP[02]', Ins_MDRP);
|
||
addIns('MDRP[03]', Ins_MDRP);
|
||
addIns('MDRP[04]', Ins_MDRP);
|
||
addIns('MDRP[05]', Ins_MDRP);
|
||
addIns('MDRP[06]', Ins_MDRP);
|
||
addIns('MDRP[07]', Ins_MDRP);
|
||
addIns('MDRP[08]', Ins_MDRP);
|
||
addIns('MDRP[09]', Ins_MDRP);
|
||
addIns('MDRP[10]', Ins_MDRP);
|
||
addIns('MDRP[11]', Ins_MDRP);
|
||
addIns('MDRP[12]', Ins_MDRP);
|
||
addIns('MDRP[13]', Ins_MDRP);
|
||
addIns('MDRP[14]', Ins_MDRP);
|
||
addIns('MDRP[15]', Ins_MDRP);
|
||
addIns('MDRP[16]', Ins_MDRP);
|
||
addIns('MDRP[17]', Ins_MDRP);
|
||
|
||
addIns('MDRP[18]', Ins_MDRP);
|
||
addIns('MDRP[19]', Ins_MDRP);
|
||
addIns('MDRP[20]', Ins_MDRP);
|
||
addIns('MDRP[21]', Ins_MDRP);
|
||
addIns('MDRP[22]', Ins_MDRP);
|
||
addIns('MDRP[23]', Ins_MDRP);
|
||
addIns('MDRP[24]', Ins_MDRP);
|
||
addIns('MDRP[25]', Ins_MDRP);
|
||
addIns('MDRP[26]', Ins_MDRP);
|
||
addIns('MDRP[27]', Ins_MDRP);
|
||
addIns('MDRP[28]', Ins_MDRP);
|
||
addIns('MDRP[29]', Ins_MDRP);
|
||
addIns('MDRP[30]', Ins_MDRP);
|
||
addIns('MDRP[31]', Ins_MDRP);
|
||
|
||
addIns('MIRP[00]', Ins_MIRP);
|
||
addIns('MIRP[01]', Ins_MIRP);
|
||
addIns('MIRP[02]', Ins_MIRP);
|
||
addIns('MIRP[03]', Ins_MIRP);
|
||
addIns('MIRP[04]', Ins_MIRP);
|
||
addIns('MIRP[05]', Ins_MIRP);
|
||
addIns('MIRP[06]', Ins_MIRP);
|
||
addIns('MIRP[07]', Ins_MIRP);
|
||
addIns('MIRP[08]', Ins_MIRP);
|
||
addIns('MIRP[09]', Ins_MIRP);
|
||
addIns('MIRP[10]', Ins_MIRP);
|
||
addIns('MIRP[11]', Ins_MIRP);
|
||
addIns('MIRP[12]', Ins_MIRP);
|
||
addIns('MIRP[13]', Ins_MIRP);
|
||
addIns('MIRP[14]', Ins_MIRP);
|
||
addIns('MIRP[15]', Ins_MIRP);
|
||
|
||
addIns('MIRP[16]', Ins_MIRP);
|
||
addIns('MIRP[17]', Ins_MIRP);
|
||
addIns('MIRP[18]', Ins_MIRP);
|
||
addIns('MIRP[19]', Ins_MIRP);
|
||
addIns('MIRP[20]', Ins_MIRP);
|
||
addIns('MIRP[21]', Ins_MIRP);
|
||
addIns('MIRP[22]', Ins_MIRP);
|
||
addIns('MIRP[23]', Ins_MIRP);
|
||
addIns('MIRP[24]', Ins_MIRP);
|
||
addIns('MIRP[25]', Ins_MIRP);
|
||
addIns('MIRP[26]', Ins_MIRP);
|
||
addIns('MIRP[27]', Ins_MIRP);
|
||
addIns('MIRP[28]', Ins_MIRP);
|
||
addIns('MIRP[29]', Ins_MIRP);
|
||
addIns('MIRP[30]', Ins_MIRP);
|
||
addIns('MIRP[31]', Ins_MIRP);
|
||
|
||
if numIns <> high(Instruct_Dispatch)+1 then
|
||
raise exception.Create('Missing instruction');
|
||
end;
|
||
|
||
destructor TInterpreter.Destroy;
|
||
begin
|
||
instructionLog.Free;
|
||
inherited Destroy;
|
||
end;
|
||
|
||
function TInterpreter.Run: TError;
|
||
label
|
||
SuiteLabel, ErrorLabel, No_Error;
|
||
var
|
||
A : Int;
|
||
begin
|
||
top := 0;
|
||
callTop := 0;
|
||
if enableLog then instructionLog.Clear;
|
||
|
||
(* set cvt functions *)
|
||
|
||
pEC^.metrics.ratio := 0;
|
||
if pEC^.instance^.metrics.x_ppem <> pEC^.instance^.metrics.y_ppem then
|
||
begin
|
||
pEC^.func_read_cvt := Read_CVT_Stretched;
|
||
pEC^.func_write_cvt := Write_CVT_Stretched;
|
||
pEC^.func_move_cvt := Move_CVT_Stretched;
|
||
end
|
||
else
|
||
begin
|
||
pEC^.func_read_cvt := Read_CVT;
|
||
pEC^.func_write_cvt := Write_CVT;
|
||
pEC^.func_move_cvt := Move_CVT;
|
||
end;
|
||
Compute_Funcs;
|
||
Compute_Round( pEC^.GS.round_state );
|
||
|
||
repeat
|
||
Calc_Length;
|
||
|
||
(* First, let's check for empty stack and overflow *)
|
||
|
||
opargs := top - Pop_Push_Count[ opcode*2 ];
|
||
|
||
(* args is the top of the stack once arguments have been popped *)
|
||
(* one can also see it as the index of the last argument *)
|
||
|
||
if opargs < 0 then
|
||
begin
|
||
pEC^.error := TT_Err_Too_Few_Arguments;
|
||
goto ErrorLabel;
|
||
end;
|
||
|
||
new_top := opargs + Pop_Push_Count[ opcode*2+1 ];
|
||
|
||
(* new_top is the new top of the stack, after the instruction's *)
|
||
(* execution. top will be set to new_top after the 'case' *)
|
||
|
||
if NeedStackSize(new_top) then goto ErrorLabel;
|
||
|
||
pEC^.step_ins := true;
|
||
pEC^.error := TT_Err_Ok;
|
||
|
||
if enableLog then instructionLog.Add('0x'+IntToHex(pEC^.IP,4)+': '+Instruct_Dispatch[ opcode ].name + ' (SP=' + IntToStr(top)+')');
|
||
Instruct_Dispatch[ opcode ].func( PStorage(@pEC^.stack^[opargs]) );
|
||
|
||
if pEC^.error <> TT_Err_Ok then
|
||
begin
|
||
|
||
case pEC^.error of
|
||
|
||
TT_Err_Invalid_Opcode: (* looking for redefined instructions *)
|
||
|
||
begin
|
||
A := 0;
|
||
while ( A < pEC^.numIDefs ) do
|
||
with pEC^.IDefs^[A] do
|
||
|
||
if Active and ( opcode = Opc ) then
|
||
begin
|
||
if callTop >= pEC^.callSize then
|
||
begin
|
||
pEC^.error := TT_Err_Invalid_Reference;
|
||
goto ErrorLabel;
|
||
end;
|
||
|
||
with pEC^.callstack^[callTop] do
|
||
begin
|
||
Caller_Range := pEC^.curRange;
|
||
Caller_IP := pEC^.IP+1;
|
||
Cur_Count := 1;
|
||
Cur_Restart := Start;
|
||
end;
|
||
|
||
if not Goto_CodeRange( Range, Start ) then
|
||
goto ErrorLabel;
|
||
|
||
goto SuiteLabel;
|
||
end
|
||
else
|
||
inc(A);
|
||
|
||
pEC^.error := TT_Err_Invalid_Opcode;
|
||
goto ErrorLabel;
|
||
|
||
end;
|
||
else
|
||
pEC^.error := pEC^.error;
|
||
goto ErrorLabel;
|
||
end;
|
||
|
||
end;
|
||
|
||
top := new_top;
|
||
|
||
if pEC^.step_ins then inc( pEC^.IP, oplength );
|
||
|
||
SuiteLabel:
|
||
|
||
if (pEC^.IP >= pEC^.codeSize) then
|
||
|
||
if callTop > 0 then
|
||
begin
|
||
pEC^.error := TT_Err_Code_Overflow;
|
||
goto ErrorLabel;
|
||
end
|
||
else
|
||
goto No_Error;
|
||
|
||
until pEC^.instruction_trap;
|
||
|
||
No_Error:
|
||
result := Success;
|
||
exit;
|
||
|
||
ErrorLabel:
|
||
result := Failure;
|
||
|
||
end;
|
||
|
||
function TInterpreter.NeedStackSize(AValue: integer): TError;
|
||
var newSize: integer;
|
||
newStack: PStorage;
|
||
begin
|
||
if AValue > pEC^.stackSize then
|
||
begin
|
||
if pEC^.stackSize < maxStackSizeAllowed then
|
||
begin
|
||
newSize := pEC^.stackSize*2+1;
|
||
if newSize > maxStackSizeAllowed then newSize := maxStackSizeAllowed;
|
||
newStack := nil;
|
||
if Alloc( newStack, newSize*sizeof(Long) ) then
|
||
begin //cannot allocate
|
||
pEC^.error := TT_Err_Stack_Overflow;
|
||
result := Failure;
|
||
end;
|
||
move(pEC^.stack^[0], newStack^[0], pEC^.stackSize*sizeof(Long) );
|
||
TTMemory.Free( pEC^.stack );
|
||
pEC^.stack := newStack;
|
||
pEC^.stackSize := newSize;
|
||
result := Success; //stack expanded
|
||
end else
|
||
begin
|
||
//maximum allowed reached
|
||
pEC^.error := TT_Err_Stack_Overflow;
|
||
result := Failure;
|
||
end;
|
||
end else
|
||
result := Success;
|
||
end;
|
||
|
||
function TInterpreter.NeedStackSize(AValue: integer;
|
||
var APointerInStack: PStorage): TError;
|
||
var APosInStack: integer;
|
||
begin
|
||
if (APointerInStack <> nil) then
|
||
begin
|
||
APosInStack:= System.PByte(APointerInStack) - System.PByte(pEC^.stack);
|
||
result := NeedStackSize(AValue);
|
||
APointerInStack := PStorage(System.PByte(pEC^.stack) + APosInStack);
|
||
end else
|
||
result := NeedStackSize(AValue);
|
||
end;
|
||
|
||
(****************************************************************)
|
||
(* *)
|
||
(* RUN *)
|
||
(* *)
|
||
(* This function executes a run of opcodes. It will exit *)
|
||
(* in the following cases : *)
|
||
(* *)
|
||
(* - Errors ( in which case it returns FALSE ) *)
|
||
(* *)
|
||
(* - Reaching the end of the main code range (returns TRUE) *)
|
||
(* reaching the end of a code range within a function *)
|
||
(* call is an error. *)
|
||
(* *)
|
||
(* - After executing one single opcode, if the flag *)
|
||
(* 'Instruction_Trap' is set to TRUE. (returns TRUE) *)
|
||
(* *)
|
||
(* On exit whith TRUE, test IP < CodeSize to know wether it *)
|
||
(* comes from a instruction trap or a normal termination *)
|
||
(* *)
|
||
(* *)
|
||
(* Note : The documented DEBUG opcode pops a value from *)
|
||
(* the stack. This behaviour is unsupported, here *)
|
||
(* a DEBUG opcode is always an error. *)
|
||
(* *)
|
||
(* *)
|
||
(* THIS IS THE INTERPRETER'S MAIN LOOP *)
|
||
(* *)
|
||
(* Instructions appear in the specs' order *)
|
||
(* *)
|
||
(****************************************************************)
|
||
|
||
function Run_Ins( exec : PExec_Context; AErrorLog: boolean ) : TError;
|
||
var interpreter: TInterpreter;
|
||
logfile: TFileStream;
|
||
procedure writelnLog(s: string);
|
||
begin
|
||
s+= LineEnding;
|
||
logfile.WriteBuffer(s[1],length(s));
|
||
end;
|
||
|
||
begin
|
||
if exec.interpreter = nil then
|
||
begin
|
||
interpreter := TInterpreter.Create(exec,AErrorLog);
|
||
exec.interpreter := interpreter;
|
||
end else
|
||
interpreter := TInterpreter(exec.interpreter);
|
||
result := interpreter.Run;
|
||
if AErrorLog and result then
|
||
begin
|
||
logfile := TFileStream.Create('ttinterp.log',fmOpenWrite or fmCreate);
|
||
writelnLog('----------------------- '+DateTimeToStr(Now));
|
||
writelnLog('Error ' + IntToHex(exec.error,4) + ' on ' + interpreter.LastInstruction);
|
||
writelnLog('Program:');
|
||
interpreter.instructionLog.SaveToStream(logfile);
|
||
writelnLog('-----------------------');
|
||
logfile.Free;
|
||
end;
|
||
end;
|
||
|
||
end.
|