mirror of
https://github.com/ZDoom/gzdoom.git
synced 2024-11-10 23:01:50 +00:00
- Make PClass derive from DObject so that it can participate in garbage collection.
- Import VM. SVN r1841 (scripting)
This commit is contained in:
parent
45842e28bd
commit
fefc306a54
12 changed files with 3399 additions and 19 deletions
|
@ -149,10 +149,10 @@ static TArray<const PClass *> WeaponNames;
|
|||
// List of states that are hacked to use a codepointer
|
||||
struct MBFParamState
|
||||
{
|
||||
FState * state;
|
||||
FState *state;
|
||||
int pointer;
|
||||
};
|
||||
static TArray<MBFParamState *> MBFParamStates;
|
||||
static TArray<MBFParamState> MBFParamStates;
|
||||
// Data on how to correctly modify the codepointers
|
||||
struct CodePointerAlias
|
||||
{
|
||||
|
@ -1610,9 +1610,9 @@ static void SetPointer(FState *state, PSymbol *sym, int frame = 0)
|
|||
{
|
||||
if (!symname.CompareNoCase(MBFCodePointers[i].name))
|
||||
{
|
||||
MBFParamState * newstate = new MBFParamState;
|
||||
newstate->state = state;
|
||||
newstate->pointer = i;
|
||||
MBFParamState newstate;
|
||||
newstate.state = state;
|
||||
newstate.pointer = i;
|
||||
MBFParamStates.Push(newstate);
|
||||
break; // No need to cycle through the rest of the list.
|
||||
}
|
||||
|
@ -2494,7 +2494,7 @@ static void UnloadDehSupp ()
|
|||
// Handle MBF params here, before the required arrays are cleared
|
||||
for (unsigned int i=0; i < MBFParamStates.Size(); i++)
|
||||
{
|
||||
SetDehParams(MBFParamStates[i]->state, MBFParamStates[i]->pointer);
|
||||
SetDehParams(MBFParamStates[i].state, MBFParamStates[i].pointer);
|
||||
}
|
||||
MBFParamStates.Clear();
|
||||
MBFParamStates.ShrinkToFit();
|
||||
|
|
|
@ -327,6 +327,15 @@ static void MarkRoot()
|
|||
SectorMarker->SecNum = 0;
|
||||
}
|
||||
Mark(SectorMarker);
|
||||
// Mark symbol tables
|
||||
for (unsigned j = 0; j < PClass::m_Types.Size(); ++j)
|
||||
{
|
||||
PClass *cls = PClass::m_Types[j];
|
||||
if (cls != NULL)
|
||||
{
|
||||
cls->Symbols.MarkSymbols();
|
||||
}
|
||||
}
|
||||
// Mark bot stuff.
|
||||
Mark(bglobal.firstthing);
|
||||
Mark(bglobal.body1);
|
||||
|
|
|
@ -467,21 +467,37 @@ const PClass *PClass::NativeClass() const
|
|||
|
||||
// Symbol tables ------------------------------------------------------------
|
||||
|
||||
IMPLEMENT_ABSTRACT_CLASS(PSymbol);
|
||||
IMPLEMENT_CLASS(PSymbolConst);
|
||||
IMPLEMENT_CLASS(PSymbolVariable);
|
||||
IMPLEMENT_CLASS(PSymbolActionFunction);
|
||||
|
||||
PSymbol::~PSymbol()
|
||||
{
|
||||
}
|
||||
|
||||
PSymbolTable::PSymbolTable()
|
||||
: ParentSymbolTable(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
PSymbolTable::~PSymbolTable ()
|
||||
{
|
||||
ReleaseSymbols();
|
||||
}
|
||||
|
||||
void PSymbolTable::ReleaseSymbols()
|
||||
void PSymbolTable::MarkSymbols()
|
||||
{
|
||||
for (unsigned int i = 0; i < Symbols.Size(); ++i)
|
||||
{
|
||||
delete Symbols[i];
|
||||
GC::Mark(Symbols[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void PSymbolTable::ReleaseSymbols()
|
||||
{
|
||||
// The GC will take care of deleting the symbols. We just need to
|
||||
// clear our references to them.
|
||||
Symbols.Clear();
|
||||
}
|
||||
|
||||
|
|
|
@ -16,8 +16,10 @@ enum ESymbolType
|
|||
SYM_ActionFunction
|
||||
};
|
||||
|
||||
struct PSymbol
|
||||
class PSymbol : public DObject
|
||||
{
|
||||
DECLARE_ABSTRACT_CLASS(PSymbol, DObject);
|
||||
public:
|
||||
virtual ~PSymbol();
|
||||
|
||||
ESymbolType SymbolType;
|
||||
|
@ -29,8 +31,10 @@ protected:
|
|||
|
||||
// A constant value ---------------------------------------------------------
|
||||
|
||||
struct PSymbolConst : public PSymbol
|
||||
class PSymbolConst : public PSymbol
|
||||
{
|
||||
DECLARE_CLASS(PSymbolConst, PSymbol);
|
||||
public:
|
||||
int ValueType;
|
||||
union
|
||||
{
|
||||
|
@ -39,17 +43,21 @@ struct PSymbolConst : public PSymbol
|
|||
};
|
||||
|
||||
PSymbolConst(FName name) : PSymbol(name, SYM_Const) {}
|
||||
PSymbolConst() : PSymbol(NAME_None, SYM_Const) {}
|
||||
};
|
||||
|
||||
// A variable ---------------------------------------------------------
|
||||
|
||||
struct PSymbolVariable : public PSymbol
|
||||
class PSymbolVariable : public PSymbol
|
||||
{
|
||||
DECLARE_CLASS(PSymbolVariable, PSymbol);
|
||||
public:
|
||||
FExpressionType ValueType;
|
||||
int size;
|
||||
intptr_t offset;
|
||||
|
||||
PSymbolVariable(FName name) : PSymbol(name, SYM_Variable) {}
|
||||
PSymbolVariable() : PSymbol(NAME_None, SYM_Variable) {}
|
||||
};
|
||||
|
||||
// An action function -------------------------------------------------------
|
||||
|
@ -74,26 +82,27 @@ struct FState;
|
|||
struct StateCallData;
|
||||
typedef void (*actionf_p)(AActor *self, AActor *stateowner, FState *state, int parameters, StateCallData *statecall);
|
||||
|
||||
struct PSymbolActionFunction : public PSymbol
|
||||
class PSymbolActionFunction : public PSymbol
|
||||
{
|
||||
DECLARE_CLASS(PSymbolActionFunction, PSymbol);
|
||||
public:
|
||||
FString Arguments;
|
||||
actionf_p Function;
|
||||
int defaultparameterindex;
|
||||
|
||||
PSymbolActionFunction(FName name) : PSymbol(name, SYM_ActionFunction) {}
|
||||
PSymbolActionFunction() : PSymbol(NAME_None, SYM_ActionFunction) {}
|
||||
};
|
||||
|
||||
// A symbol table -----------------------------------------------------------
|
||||
|
||||
class PSymbolTable
|
||||
struct PSymbolTable
|
||||
{
|
||||
public:
|
||||
PSymbolTable() : ParentSymbolTable(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
PSymbolTable();
|
||||
~PSymbolTable();
|
||||
|
||||
void MarkSymbols();
|
||||
|
||||
// Sets the table to use for searches if this one doesn't contain the
|
||||
// requested symbol.
|
||||
void SetParentTable (PSymbolTable *parent);
|
||||
|
|
|
@ -382,7 +382,7 @@ protected:
|
|||
DFrameBuffer () {}
|
||||
|
||||
private:
|
||||
DWORD LastMS, LastSec, FrameCount, LastCount, LastTic;
|
||||
uint32 LastMS, LastSec, FrameCount, LastCount, LastTic;
|
||||
};
|
||||
|
||||
|
||||
|
|
28
zdoom.vcproj
28
zdoom.vcproj
|
@ -6446,6 +6446,34 @@
|
|||
>
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="ZScript"
|
||||
>
|
||||
<File
|
||||
RelativePath=".\zscript\vm.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\zscript\vmdisasm.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\zscript\vmexec.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\zscript\vmexec.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\zscript\vmframe.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\zscript\vmops.h"
|
||||
>
|
||||
</File>
|
||||
</Filter>
|
||||
</Files>
|
||||
<Globals>
|
||||
</Globals>
|
||||
|
|
791
zscript/vm.h
Normal file
791
zscript/vm.h
Normal file
|
@ -0,0 +1,791 @@
|
|||
#ifndef VM_H
|
||||
#define VM_H
|
||||
|
||||
#include "zstring.h"
|
||||
#include "dobject.h"
|
||||
|
||||
#define MAX_RETURNS 8 // Maximum number of results a function called by script code can return
|
||||
#define MAX_TRY_DEPTH 8 // Maximum number of nested TRYs in a single function
|
||||
|
||||
|
||||
typedef unsigned char VM_UBYTE;
|
||||
typedef signed char VM_SBYTE;
|
||||
typedef unsigned short VM_UHALF;
|
||||
typedef signed short VM_SHALF;
|
||||
typedef unsigned int VM_UWORD;
|
||||
typedef signed int VM_SWORD;
|
||||
|
||||
#define VM_EPSILON (1/1024.0)
|
||||
|
||||
enum
|
||||
{
|
||||
#include "vmops.h"
|
||||
NUM_OPS
|
||||
};
|
||||
|
||||
// Flags for A field of CMPS
|
||||
enum
|
||||
{
|
||||
CMP_CHECK = 1,
|
||||
|
||||
CMP_EQ = 0,
|
||||
CMP_LT = 2,
|
||||
CMP_LE = 4,
|
||||
CMP_METHOD_MASK = 6,
|
||||
|
||||
CMP_BK = 8,
|
||||
CMP_CK = 16,
|
||||
CMP_APPROX = 32,
|
||||
};
|
||||
|
||||
// Floating point operations for FLOP
|
||||
enum
|
||||
{
|
||||
FLOP_ABS,
|
||||
FLOP_NEG,
|
||||
FLOP_ACOS,
|
||||
FLOP_ASIN,
|
||||
FLOP_ATAN,
|
||||
FLOP_COS,
|
||||
FLOP_COSH,
|
||||
FLOP_EXP,
|
||||
FLOP_LOG,
|
||||
FLOP_LOG10,
|
||||
FLOP_SIN,
|
||||
FLOP_SINH,
|
||||
FLOP_TAN,
|
||||
FLOP_TANH,
|
||||
FLOP_SQRT,
|
||||
FLOP_CEIL,
|
||||
FLOP_FLOOR,
|
||||
};
|
||||
|
||||
// Cast operations
|
||||
enum
|
||||
{
|
||||
CAST_I2F,
|
||||
CAST_I2S,
|
||||
CAST_F2I,
|
||||
CAST_F2S,
|
||||
CAST_P2S,
|
||||
CAST_S2I,
|
||||
CAST_S2F,
|
||||
};
|
||||
|
||||
// Register types for VMParam
|
||||
enum
|
||||
{
|
||||
REGT_INT = 0,
|
||||
REGT_FLOAT = 1,
|
||||
REGT_STRING = 2,
|
||||
REGT_POINTER = 3,
|
||||
REGT_TYPE = 3,
|
||||
|
||||
REGT_KONST = 4,
|
||||
REGT_MULTIREG = 8, // (e.g. a vector)
|
||||
REGT_FINAL = 16, // used with RET: this is the final return value
|
||||
REGT_ADDROF = 32, // used with PARAM: pass address of this register
|
||||
|
||||
REGT_NIL = 255 // parameter was omitted
|
||||
};
|
||||
|
||||
// Tags for address registers
|
||||
enum
|
||||
{
|
||||
ATAG_GENERIC, // pointer to something; we don't care what
|
||||
ATAG_OBJECT, // pointer to an object; will be followed by GC
|
||||
|
||||
// The following are all for documentation during debugging and are
|
||||
// functionally no different than ATAG_GENERIC.
|
||||
|
||||
ATAG_FRAMEPOINTER, // pointer to extra stack frame space for this function
|
||||
ATAG_DREGISTER, // pointer to a data register
|
||||
ATAG_FREGISTER, // pointer to a float register
|
||||
ATAG_SREGISTER, // pointer to a string register
|
||||
ATAG_AREGISTER, // pointer to an address register
|
||||
|
||||
};
|
||||
|
||||
class VMFunction : public DObject
|
||||
{
|
||||
DECLARE_CLASS(VMFunction, DObject);
|
||||
public:
|
||||
bool Native;
|
||||
};
|
||||
|
||||
enum EVMOpMode
|
||||
{
|
||||
MODE_ASHIFT = 0,
|
||||
MODE_BSHIFT = 4,
|
||||
MODE_CSHIFT = 8,
|
||||
MODE_BCSHIFT = 12,
|
||||
|
||||
MODE_ATYPE = 15 << MODE_ASHIFT,
|
||||
MODE_BTYPE = 15 << MODE_BSHIFT,
|
||||
MODE_CTYPE = 15 << MODE_CSHIFT,
|
||||
MODE_BCTYPE = 31 << MODE_BCSHIFT,
|
||||
|
||||
MODE_I = 0,
|
||||
MODE_F,
|
||||
MODE_S,
|
||||
MODE_P,
|
||||
MODE_V,
|
||||
MODE_X,
|
||||
MODE_KI,
|
||||
MODE_KF,
|
||||
MODE_KS,
|
||||
MODE_KP,
|
||||
MODE_KV,
|
||||
MODE_UNUSED,
|
||||
MODE_IMMS,
|
||||
MODE_IMMZ,
|
||||
MODE_JOINT,
|
||||
|
||||
MODE_PARAM,
|
||||
MODE_THROW,
|
||||
MODE_CATCH,
|
||||
MODE_CAST,
|
||||
|
||||
MODE_AI = MODE_I << MODE_ASHIFT,
|
||||
MODE_AF = MODE_F << MODE_ASHIFT,
|
||||
MODE_AS = MODE_S << MODE_ASHIFT,
|
||||
MODE_AP = MODE_P << MODE_ASHIFT,
|
||||
MODE_AV = MODE_V << MODE_ASHIFT,
|
||||
MODE_AX = MODE_X << MODE_ASHIFT,
|
||||
MODE_AKP = MODE_KP << MODE_ASHIFT,
|
||||
MODE_AUNUSED = MODE_UNUSED << MODE_ASHIFT,
|
||||
MODE_AIMMS = MODE_IMMS << MODE_ASHIFT,
|
||||
MODE_AIMMZ = MODE_IMMZ << MODE_ASHIFT,
|
||||
|
||||
MODE_BI = MODE_I << MODE_BSHIFT,
|
||||
MODE_BF = MODE_F << MODE_BSHIFT,
|
||||
MODE_BS = MODE_S << MODE_BSHIFT,
|
||||
MODE_BP = MODE_P << MODE_BSHIFT,
|
||||
MODE_BV = MODE_V << MODE_BSHIFT,
|
||||
MODE_BX = MODE_X << MODE_BSHIFT,
|
||||
MODE_BKI = MODE_KI << MODE_BSHIFT,
|
||||
MODE_BKF = MODE_KF << MODE_BSHIFT,
|
||||
MODE_BKS = MODE_KS << MODE_BSHIFT,
|
||||
MODE_BKP = MODE_KP << MODE_BSHIFT,
|
||||
MODE_BKV = MODE_KV << MODE_BSHIFT,
|
||||
MODE_BUNUSED = MODE_UNUSED << MODE_BSHIFT,
|
||||
MODE_BIMMS = MODE_IMMS << MODE_BSHIFT,
|
||||
MODE_BIMMZ = MODE_IMMZ << MODE_BSHIFT,
|
||||
|
||||
MODE_CI = MODE_I << MODE_CSHIFT,
|
||||
MODE_CF = MODE_F << MODE_CSHIFT,
|
||||
MODE_CS = MODE_S << MODE_CSHIFT,
|
||||
MODE_CP = MODE_P << MODE_CSHIFT,
|
||||
MODE_CV = MODE_V << MODE_CSHIFT,
|
||||
MODE_CX = MODE_X << MODE_CSHIFT,
|
||||
MODE_CKI = MODE_KI << MODE_CSHIFT,
|
||||
MODE_CKF = MODE_KF << MODE_CSHIFT,
|
||||
MODE_CKS = MODE_KS << MODE_CSHIFT,
|
||||
MODE_CKP = MODE_KP << MODE_CSHIFT,
|
||||
MODE_CKV = MODE_KV << MODE_CSHIFT,
|
||||
MODE_CUNUSED = MODE_UNUSED << MODE_CSHIFT,
|
||||
MODE_CIMMS = MODE_IMMS << MODE_CSHIFT,
|
||||
MODE_CIMMZ = MODE_IMMZ << MODE_CSHIFT,
|
||||
|
||||
MODE_BCJOINT = (MODE_JOINT << MODE_BSHIFT) | (MODE_JOINT << MODE_CSHIFT),
|
||||
MODE_BCKI = MODE_KI << MODE_BCSHIFT,
|
||||
MODE_BCKF = MODE_KF << MODE_BCSHIFT,
|
||||
MODE_BCKS = MODE_KS << MODE_BCSHIFT,
|
||||
MODE_BCKP = MODE_KP << MODE_BCSHIFT,
|
||||
MODE_BCIMMS = MODE_IMMS << MODE_BCSHIFT,
|
||||
MODE_BCIMMZ = MODE_IMMZ << MODE_BCSHIFT,
|
||||
MODE_BCPARAM = MODE_PARAM << MODE_BCSHIFT,
|
||||
MODE_BCTHROW = MODE_THROW << MODE_BCSHIFT,
|
||||
MODE_BCCATCH = MODE_CATCH << MODE_BCSHIFT,
|
||||
MODE_BCCAST = MODE_CAST << MODE_BCSHIFT,
|
||||
|
||||
MODE_ABCJOINT = (MODE_JOINT << MODE_ASHIFT) | MODE_BCJOINT,
|
||||
};
|
||||
|
||||
struct VMOpInfo
|
||||
{
|
||||
const char *Name;
|
||||
int Mode;
|
||||
};
|
||||
|
||||
extern const VMOpInfo OpInfo[NUM_OPS];
|
||||
|
||||
struct VMReturn
|
||||
{
|
||||
void *Location;
|
||||
VM_SHALF RegNum; // Used to find ObjFlag index for pointers; set negative if the caller is native code and doesn't care
|
||||
VM_UBYTE RegType; // Same as VMParam RegType, except REGT_KONST is invalid; only used by asserts
|
||||
|
||||
void SetInt(int val)
|
||||
{
|
||||
*(int *)Location = val;
|
||||
}
|
||||
void SetFloat(double val)
|
||||
{
|
||||
*(double *)Location = val;
|
||||
}
|
||||
void SetVector(const double val[3])
|
||||
{
|
||||
((double *)Location)[0] = val[0];
|
||||
((double *)Location)[1] = val[1];
|
||||
((double *)Location)[2] = val[2];
|
||||
}
|
||||
void SetString(const FString &val)
|
||||
{
|
||||
*(FString *)Location = val;
|
||||
}
|
||||
void SetPointer(void *val)
|
||||
{
|
||||
*(void **)Location = val;
|
||||
}
|
||||
|
||||
void IntAt(int *loc)
|
||||
{
|
||||
Location = loc;
|
||||
RegNum = -1;
|
||||
RegType = REGT_INT;
|
||||
}
|
||||
void FloatAt(double *loc)
|
||||
{
|
||||
Location = loc;
|
||||
RegNum = -1;
|
||||
RegType = REGT_FLOAT;
|
||||
}
|
||||
void StringAt(FString *loc)
|
||||
{
|
||||
Location = loc;
|
||||
RegNum = -1;
|
||||
RegType = REGT_STRING;
|
||||
}
|
||||
void PointerAt(void **loc)
|
||||
{
|
||||
Location = loc;
|
||||
RegNum = -1;
|
||||
RegType = REGT_POINTER;
|
||||
}
|
||||
};
|
||||
|
||||
struct VMRegisters;
|
||||
|
||||
|
||||
struct VMValue
|
||||
{
|
||||
union
|
||||
{
|
||||
int i;
|
||||
struct { void *a; int atag; };
|
||||
double f;
|
||||
struct { int pad[3]; int Type; };
|
||||
struct { int foo[4]; } biggest;
|
||||
};
|
||||
|
||||
// Unfortunately, FString cannot be used directly.
|
||||
// Fortunately, it is relatively simple.
|
||||
FString &s() { return *(FString *)&a; }
|
||||
const FString &s() const { return *(FString *)&a; }
|
||||
|
||||
VMValue()
|
||||
{
|
||||
a = NULL;
|
||||
Type = REGT_NIL;
|
||||
}
|
||||
~VMValue()
|
||||
{
|
||||
Kill();
|
||||
}
|
||||
VMValue(const VMValue &o)
|
||||
{
|
||||
biggest = o.biggest;
|
||||
if (Type == REGT_STRING)
|
||||
{
|
||||
::new(&s()) FString(o.s());
|
||||
}
|
||||
}
|
||||
VMValue(int v)
|
||||
{
|
||||
i = v;
|
||||
Type = REGT_INT;
|
||||
}
|
||||
VMValue(double v)
|
||||
{
|
||||
f = v;
|
||||
Type = REGT_FLOAT;
|
||||
}
|
||||
VMValue(const char *s)
|
||||
{
|
||||
::new(&a) FString(s);
|
||||
Type = REGT_STRING;
|
||||
}
|
||||
VMValue(const FString &s)
|
||||
{
|
||||
::new(&a) FString(s);
|
||||
Type = REGT_STRING;
|
||||
}
|
||||
VMValue(DObject *v)
|
||||
{
|
||||
a = v;
|
||||
atag = ATAG_OBJECT;
|
||||
Type = REGT_POINTER;
|
||||
}
|
||||
VMValue(void *v)
|
||||
{
|
||||
a = v;
|
||||
atag = ATAG_GENERIC;
|
||||
Type = REGT_POINTER;
|
||||
}
|
||||
VMValue(void *v, int tag)
|
||||
{
|
||||
a = v;
|
||||
atag = tag;
|
||||
Type = REGT_POINTER;
|
||||
}
|
||||
VMValue &operator=(const VMValue &o)
|
||||
{
|
||||
if (o.Type == REGT_STRING)
|
||||
{
|
||||
if (Type == REGT_STRING)
|
||||
{
|
||||
s() = o.s();
|
||||
}
|
||||
else
|
||||
{
|
||||
new(&s()) FString(o.s());
|
||||
Type = REGT_STRING;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Kill();
|
||||
biggest = o.biggest;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
VMValue &operator=(int v)
|
||||
{
|
||||
Kill();
|
||||
i = v;
|
||||
Type = REGT_INT;
|
||||
return *this;
|
||||
}
|
||||
VMValue &operator=(double v)
|
||||
{
|
||||
Kill();
|
||||
f = v;
|
||||
Type = REGT_FLOAT;
|
||||
return *this;
|
||||
}
|
||||
VMValue &operator=(const FString &v)
|
||||
{
|
||||
if (Type == REGT_STRING)
|
||||
{
|
||||
s() = v;
|
||||
}
|
||||
else
|
||||
{
|
||||
::new(&s()) FString(v);
|
||||
Type = REGT_STRING;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
VMValue &operator=(const char *v)
|
||||
{
|
||||
if (Type == REGT_STRING)
|
||||
{
|
||||
s() = v;
|
||||
}
|
||||
else
|
||||
{
|
||||
::new(&s()) FString(v);
|
||||
Type = REGT_STRING;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
VMValue &operator=(DObject *v)
|
||||
{
|
||||
Kill();
|
||||
a = v;
|
||||
atag = ATAG_OBJECT;
|
||||
Type = REGT_POINTER;
|
||||
return *this;
|
||||
}
|
||||
VMValue &operator=(void *v)
|
||||
{
|
||||
Kill();
|
||||
a = v;
|
||||
atag = ATAG_GENERIC;
|
||||
Type = REGT_POINTER;
|
||||
return *this;
|
||||
}
|
||||
void SetNil()
|
||||
{
|
||||
Kill();
|
||||
Type = REGT_NIL;
|
||||
}
|
||||
bool operator==(const VMValue &o)
|
||||
{
|
||||
return Test(o) == 0;
|
||||
}
|
||||
bool operator!=(const VMValue &o)
|
||||
{
|
||||
return Test(o) != 0;
|
||||
}
|
||||
bool operator< (const VMValue &o)
|
||||
{
|
||||
return Test(o) < 0;
|
||||
}
|
||||
bool operator<=(const VMValue &o)
|
||||
{
|
||||
return Test(o) <= 0;
|
||||
}
|
||||
bool operator> (const VMValue &o)
|
||||
{
|
||||
return Test(o) > 0;
|
||||
}
|
||||
bool operator>=(const VMValue &o)
|
||||
{
|
||||
return Test(o) >= 0;
|
||||
}
|
||||
int Test(const VMValue &o, int inexact=false)
|
||||
{
|
||||
double diff;
|
||||
|
||||
if (Type == o.Type)
|
||||
{
|
||||
switch(Type)
|
||||
{
|
||||
case REGT_NIL:
|
||||
return 0;
|
||||
|
||||
case REGT_INT:
|
||||
return i - o.i;
|
||||
|
||||
case REGT_FLOAT:
|
||||
diff = f - o.f;
|
||||
do_double: if (inexact)
|
||||
{
|
||||
return diff < -VM_EPSILON ? -1 : diff > VM_EPSILON ? 1 : 0;
|
||||
}
|
||||
return diff < 0 ? -1 : diff > 0 ? 1 : 0;
|
||||
|
||||
case REGT_STRING:
|
||||
return inexact ? s().CompareNoCase(o.s()) : s().Compare(o.s());
|
||||
|
||||
case REGT_POINTER:
|
||||
return int((const VM_UBYTE *)a - (const VM_UBYTE *)o.a);
|
||||
}
|
||||
assert(0); // Should not get here
|
||||
return 2;
|
||||
}
|
||||
if (Type == REGT_FLOAT && o.Type == REGT_INT)
|
||||
{
|
||||
diff = f - o.i;
|
||||
goto do_double;
|
||||
}
|
||||
if (Type == REGT_INT && o.Type == REGT_FLOAT)
|
||||
{
|
||||
diff = i - o.f;
|
||||
goto do_double;
|
||||
}
|
||||
// Bad comparison
|
||||
return 2;
|
||||
}
|
||||
FString ToString()
|
||||
{
|
||||
if (Type == REGT_STRING)
|
||||
{
|
||||
return s();
|
||||
}
|
||||
else if (Type == REGT_NIL)
|
||||
{
|
||||
return "nil";
|
||||
}
|
||||
FString t;
|
||||
if (Type == REGT_INT)
|
||||
{
|
||||
t.Format ("%d", i);
|
||||
}
|
||||
else if (Type == REGT_FLOAT)
|
||||
{
|
||||
t.Format ("%.14g", f);
|
||||
}
|
||||
else if (Type == REGT_POINTER)
|
||||
{
|
||||
// FIXME
|
||||
t.Format ("Object: %p", a);
|
||||
}
|
||||
return t;
|
||||
}
|
||||
int ToInt()
|
||||
{
|
||||
if (Type == REGT_INT)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
if (Type == REGT_FLOAT)
|
||||
{
|
||||
return int(f);
|
||||
}
|
||||
if (Type == REGT_STRING)
|
||||
{
|
||||
return s().ToLong();
|
||||
}
|
||||
// FIXME
|
||||
return 0;
|
||||
}
|
||||
double ToDouble()
|
||||
{
|
||||
if (Type == REGT_FLOAT)
|
||||
{
|
||||
return f;
|
||||
}
|
||||
if (Type == REGT_INT)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
if (Type == REGT_STRING)
|
||||
{
|
||||
return s().ToDouble();
|
||||
}
|
||||
// FIXME
|
||||
return 0;
|
||||
}
|
||||
void Kill()
|
||||
{
|
||||
if (Type == REGT_STRING)
|
||||
{
|
||||
s().~FString();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// VM frame layout:
|
||||
// VMFrame header
|
||||
// parameter stack - 16 byte boundary, 16 bytes each
|
||||
// double registers - 8 bytes each
|
||||
// string registers - 4 or 8 bytes each
|
||||
// address registers - 4 or 8 bytes each
|
||||
// data registers - 4 bytes each
|
||||
// address register tags-1 byte each
|
||||
// extra space - 16 byte boundary
|
||||
struct VMFrame
|
||||
{
|
||||
VMFrame *ParentFrame;
|
||||
VMFunction *Func;
|
||||
VM_UBYTE NumRegD;
|
||||
VM_UBYTE NumRegF;
|
||||
VM_UBYTE NumRegS;
|
||||
VM_UBYTE NumRegA;
|
||||
VM_UHALF MaxParam;
|
||||
VM_UHALF NumParam; // current number of parameters
|
||||
|
||||
static int FrameSize(int numregd, int numregf, int numregs, int numrega, int numparam, int numextra)
|
||||
{
|
||||
int size = (sizeof(VMFrame) + 15) & ~15;
|
||||
size += numparam * sizeof(VMValue);
|
||||
size += numregf * sizeof(double);
|
||||
size += numrega * (sizeof(void *) + sizeof(VM_UBYTE));
|
||||
size += numregs * sizeof(FString);
|
||||
size += numregd * sizeof(int);
|
||||
if (numextra != 0)
|
||||
{
|
||||
size = (size + 15) & ~15;
|
||||
size += numextra;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
int *GetRegD() const
|
||||
{
|
||||
return (int *)(GetRegA() + NumRegA);
|
||||
}
|
||||
|
||||
double *GetRegF() const
|
||||
{
|
||||
return (double *)(GetParam() + MaxParam);
|
||||
}
|
||||
|
||||
FString *GetRegS() const
|
||||
{
|
||||
return (FString *)(GetRegF() + NumRegF);
|
||||
}
|
||||
|
||||
void **GetRegA() const
|
||||
{
|
||||
return (void **)(GetRegS() + NumRegS);
|
||||
}
|
||||
|
||||
VM_UBYTE *GetRegATag() const
|
||||
{
|
||||
return (VM_UBYTE *)(GetRegD() + NumRegD);
|
||||
}
|
||||
|
||||
VMValue *GetParam() const
|
||||
{
|
||||
return (VMValue *)(((size_t)(this + 1) + 15) & ~15);
|
||||
}
|
||||
|
||||
void *GetExtra() const
|
||||
{
|
||||
VM_UBYTE *ptag = GetRegATag();
|
||||
ptrdiff_t ofs = ptag - (VM_UBYTE *)this;
|
||||
return (VM_UBYTE *)this + ((ofs + NumRegA + 15) & ~15);
|
||||
}
|
||||
|
||||
void GetAllRegs(int *&d, double *&f, FString *&s, void **&a, VM_UBYTE *&atag, VMValue *¶m) const
|
||||
{
|
||||
// Calling the individual functions produces suboptimal code. :(
|
||||
param = GetParam();
|
||||
f = (double *)(param + MaxParam);
|
||||
s = (FString *)(f + NumRegF);
|
||||
a = (void **)(s + NumRegS);
|
||||
d = (int *)(a + NumRegA);
|
||||
atag = (VM_UBYTE *)(d + NumRegD);
|
||||
}
|
||||
|
||||
void InitRegS();
|
||||
};
|
||||
|
||||
struct VMRegisters
|
||||
{
|
||||
VMRegisters(const VMFrame *frame)
|
||||
{
|
||||
frame->GetAllRegs(d, f, s, a, atag, param);
|
||||
}
|
||||
|
||||
VMRegisters(const VMRegisters &o)
|
||||
: d(o.d), f(o.f), s(o.s), a(o.a), atag(o.atag), param(o.param)
|
||||
{ }
|
||||
|
||||
int *d;
|
||||
double *f;
|
||||
FString *s;
|
||||
void **a;
|
||||
VM_UBYTE *atag;
|
||||
VMValue *param;
|
||||
};
|
||||
|
||||
struct VMException : public DObject
|
||||
{
|
||||
DECLARE_CLASS(VMFunction, DObject);
|
||||
};
|
||||
|
||||
class VMScriptFunction : public VMFunction
|
||||
{
|
||||
DECLARE_CLASS(VMScriptFunction, VMFunction);
|
||||
public:
|
||||
const VM_UBYTE *Code;
|
||||
int *KonstD;
|
||||
double *KonstF;
|
||||
FString *KonstS;
|
||||
void **KonstA;
|
||||
int ExtraSpace;
|
||||
int NumCodeBytes;
|
||||
VM_UBYTE NumRegD;
|
||||
VM_UBYTE NumRegF;
|
||||
VM_UBYTE NumRegS;
|
||||
VM_UBYTE NumRegA;
|
||||
VM_UBYTE NumKonstD;
|
||||
VM_UBYTE NumKonstF;
|
||||
VM_UBYTE NumKonstS;
|
||||
VM_UBYTE NumKonstA;
|
||||
VM_UHALF MaxParam; // Maximum number of parameters this function has on the stack at once
|
||||
VM_UBYTE NumArgs; // Number of arguments this function takes
|
||||
};
|
||||
|
||||
class VMFrameStack
|
||||
{
|
||||
public:
|
||||
VMFrameStack();
|
||||
~VMFrameStack();
|
||||
VMFrame *AllocFrame(int numregd, int numregf, int numregs, int numrega);
|
||||
VMFrame *AllocFrame(VMScriptFunction *func);
|
||||
VMFrame *PopFrame();
|
||||
VMFrame *TopFrame()
|
||||
{
|
||||
assert(Blocks != NULL && Blocks->LastFrame != NULL);
|
||||
return Blocks->LastFrame;
|
||||
}
|
||||
int Call(VMFunction *func, VMValue *params, int numparams, VMReturn *results, int numresults, VMException **trap=NULL);
|
||||
private:
|
||||
enum { BLOCK_SIZE = 4096 }; // Default block size
|
||||
struct BlockHeader
|
||||
{
|
||||
BlockHeader *NextBlock;
|
||||
VMFrame *LastFrame;
|
||||
VM_UBYTE *FreeSpace;
|
||||
int BlockSize;
|
||||
};
|
||||
BlockHeader *Blocks;
|
||||
BlockHeader *UnusedBlocks;
|
||||
VMFrame *Alloc(int size);
|
||||
};
|
||||
|
||||
class VMNativeFunction : public VMFunction
|
||||
{
|
||||
DECLARE_CLASS(VMNativeFunction, VMFunction);
|
||||
public:
|
||||
// Return value is the number of results.
|
||||
int (*NativeCall)(VMFrameStack *stack, VMValue *param, int numparam, VMReturn *ret, int numret);
|
||||
};
|
||||
|
||||
|
||||
class VMParamFiller
|
||||
{
|
||||
public:
|
||||
VMParamFiller(const VMFrame *frame) : Reg(frame), RegD(0), RegF(0), RegS(0), RegA(0) {}
|
||||
VMParamFiller(const VMRegisters *reg) : Reg(*reg), RegD(0), RegF(0), RegS(0), RegA(0) {}
|
||||
|
||||
void ParamInt(int val)
|
||||
{
|
||||
Reg.d[RegD++] = val;
|
||||
}
|
||||
|
||||
void ParamFloat(double val)
|
||||
{
|
||||
Reg.f[RegF++] = val;
|
||||
}
|
||||
|
||||
void ParamString(FString &val)
|
||||
{
|
||||
Reg.s[RegS++] = val;
|
||||
}
|
||||
|
||||
void ParamString(const char *val)
|
||||
{
|
||||
Reg.s[RegS++] = val;
|
||||
}
|
||||
|
||||
void ParamObject(DObject *obj)
|
||||
{
|
||||
Reg.a[RegA] = obj;
|
||||
Reg.atag[RegA] = true;
|
||||
RegA++;
|
||||
}
|
||||
|
||||
void ParamPointer(void *ptr)
|
||||
{
|
||||
Reg.a[RegA] = ptr;
|
||||
Reg.atag[RegA] = false;
|
||||
RegA++;
|
||||
}
|
||||
|
||||
private:
|
||||
const VMRegisters Reg;
|
||||
int RegD, RegF, RegS, RegA;
|
||||
};
|
||||
|
||||
|
||||
enum EVMEngine
|
||||
{
|
||||
VMEngine_Default,
|
||||
VMEngine_Unchecked,
|
||||
VMEngine_Checked
|
||||
};
|
||||
|
||||
void VMSelectEngine(EVMEngine engine);
|
||||
extern int (*VMExec)(VMFrameStack *stack, const VM_UBYTE *pc, VMReturn *ret, int numret);
|
||||
void VMFillParams(VMValue *params, VMFrame *callee, int numparam);
|
||||
|
||||
void VMDisasm(const VM_UBYTE *code, int codesize, const VMScriptFunction *func);
|
||||
|
||||
#endif
|
343
zscript/vmdisasm.cpp
Normal file
343
zscript/vmdisasm.cpp
Normal file
|
@ -0,0 +1,343 @@
|
|||
#include "vm.h"
|
||||
|
||||
#define LI MODE_AI | MODE_BCJOINT | MODE_BCIMMS
|
||||
#define LKI MODE_AI | MODE_BCJOINT | MODE_BCKI
|
||||
#define LKF MODE_AF | MODE_BCJOINT | MODE_BCKF
|
||||
#define LKS MODE_AS | MODE_BCJOINT | MODE_BCKS
|
||||
#define LKP MODE_AP | MODE_BCJOINT | MODE_BCKP
|
||||
#define LFP MODE_AP | MODE_BUNUSED | MODE_CUNUSED
|
||||
|
||||
#define RIRPKI MODE_AI | MODE_BP | MODE_CKI
|
||||
#define RIRPRI MODE_AI | MODE_BP | MODE_CI
|
||||
#define RFRPKI MODE_AF | MODE_BP | MODE_CKI
|
||||
#define RFRPRI MODE_AF | MODE_BP | MODE_CI
|
||||
#define RSRPKI MODE_AS | MODE_BP | MODE_CKI
|
||||
#define RSRPRI MODE_AS | MODE_BP | MODE_CI
|
||||
#define RPRPKI MODE_AP | MODE_BP | MODE_CKI
|
||||
#define RPRPRI MODE_AP | MODE_BP | MODE_CI
|
||||
#define RVRPKI MODE_AV | MODE_BP | MODE_CKI
|
||||
#define RVRPRI MODE_AV | MODE_BP | MODE_CI
|
||||
#define RIRPI8 MODE_AI | MODE_BP | MODE_CIMMZ
|
||||
|
||||
#define RPRIKI MODE_AP | MODE_BI | MODE_CKI
|
||||
#define RPRIRI MODE_AP | MODE_BI | MODE_CI
|
||||
#define RPRFKI MODE_AP | MODE_BF | MODE_CKI
|
||||
#define RPRFRI MODE_AP | MODE_BF | MODE_CI
|
||||
#define RPRSKI MODE_AP | MODE_BS | MODE_CKI
|
||||
#define RPRSRI MODE_AP | MODE_BS | MODE_CI
|
||||
#define RPRPKI MODE_AP | MODE_BP | MODE_CKI
|
||||
#define RPRPRI MODE_AP | MODE_BP | MODE_CI
|
||||
#define RPRVKI MODE_AP | MODE_BV | MODE_CKI
|
||||
#define RPRVRI MODE_AP | MODE_BV | MODE_CI
|
||||
#define RPRII8 MODE_AP | MODE_BI | MODE_CIMMZ
|
||||
|
||||
#define RIRI MODE_AI | MODE_BI | MODE_CUNUSED
|
||||
#define RFRF MODE_AF | MODE_BF | MODE_CUNUSED
|
||||
#define RSRS MODE_AS | MODE_BS | MODE_CUNUSED
|
||||
#define RPRP MODE_AP | MODE_BP | MODE_CUNUSED
|
||||
#define RXRXI8 MODE_AX | MODE_BX | MODE_CIMMZ
|
||||
#define RPRPRP MODE_AP | MODE_BP | MODE_CP
|
||||
#define RPRPKP MODE_AP | MODE_BP | MODE_CKP
|
||||
|
||||
#define RII16 MODE_AI | MODE_BCJOINT | MODE_BCIMMS
|
||||
#define I24 MODE_ABCJOINT
|
||||
#define I8 MODE_AIMMZ | MODE_BUNUSED | MODE_CUNUSED
|
||||
#define __BCP MODE_AUNUSED | MODE_BCJOINT | MODE_BCPARAM
|
||||
#define RPI8I8 MODE_AP | MODE_BIMMZ | MODE_CIMMZ
|
||||
#define KPI8I8 MODE_AKP | MODE_BIMMZ | MODE_CIMMZ
|
||||
#define I8BCP MODE_AIMMZ | MODE_BCJOINT | MODE_BCPARAM
|
||||
#define THROW MODE_AIMMZ | MODE_BCTHROW
|
||||
#define CATCH MODE_AIMMZ | MODE_BCCATCH
|
||||
#define CAST MODE_AX | MODE_BX | MODE_CIMMZ | MODE_BCCAST
|
||||
|
||||
#define RSRSRS MODE_AS | MODE_BS | MODE_CS
|
||||
#define RIRS MODE_AI | MODE_BS | MODE_CUNUSED
|
||||
#define I8RXRX MODE_AIMMZ | MODE_BX | MODE_CX
|
||||
|
||||
#define RIRIRI MODE_AI | MODE_BI | MODE_CI
|
||||
#define RIRII8 MODE_AI | MODE_BI | MODE_CIMMZ
|
||||
#define RIRIKI MODE_AI | MODE_BI | MODE_CKI
|
||||
#define RIKIRI MODE_AI | MODE_BKI | MODE_CI
|
||||
#define RIKII8 MODE_AI | MODE_BKI | MODE_CIMMZ
|
||||
#define RIRIIs MODE_AI | MODE_BI | MODE_CIMMS
|
||||
#define RIRI MODE_AI | MODE_BI | MODE_CUNUSED
|
||||
#define I8RIRI MODE_AIMMZ | MODE_BI | MODE_CI
|
||||
#define I8RIKI MODE_AIMMZ | MODE_BI | MODE_CKI
|
||||
#define I8KIRI MODE_AIMMZ | MODE_BKI | MODE_CI
|
||||
|
||||
#define RFRFRF MODE_AF | MODE_BF | MODE_CF
|
||||
#define RFRFKF MODE_AF | MODE_BF | MODE_CKF
|
||||
#define RFKFRF MODE_AF | MODE_BKF | MODE_CF
|
||||
#define I8RFRF MODE_AIMMZ | MODE_BF | MODE_CF
|
||||
#define I8RFKF MODE_AIMMZ | MODE_BF | MODE_CKF
|
||||
#define I8KFRF MODE_AIMMZ | MODE_BKF | MODE_CF
|
||||
#define RFRFI8 MODE_AF | MODE_BF | MODE_CIMMZ
|
||||
|
||||
#define RVRV MODE_AV | MODE_BV | MODE_CUNUSED
|
||||
#define RVRVRV MODE_AV | MODE_BV | MODE_CV
|
||||
#define RVRVKV MODE_AV | MODE_BV | MODE_CKV
|
||||
#define RVKVRV MODE_AV | MODE_BKV | MODE_CV
|
||||
#define RFRV MODE_AF | MODE_BV | MODE_CUNUSED
|
||||
#define I8RVRV MODE_AIMMZ | MODE_BV | MODE_CV
|
||||
#define I8RVKV MODE_AIMMZ | MODE_BV | MODE_CKV
|
||||
|
||||
#define RPRPRI MODE_AP | MODE_BP | MODE_CI
|
||||
#define RPRPKI MODE_AP | MODE_BP | MODE_CKI
|
||||
#define RIRPRP MODE_AI | MODE_BP | MODE_CP
|
||||
#define I8RPRP MODE_AIMMZ | MODE_BP | MODE_CP
|
||||
#define I8RPKP MODE_AIMMZ | MODE_BP | MODE_CKP
|
||||
|
||||
const VMOpInfo OpInfo[NUM_OPS] =
|
||||
{
|
||||
#define xx(op, name, mode) { #name, mode }
|
||||
#include "vmops.h"
|
||||
};
|
||||
|
||||
#ifdef WORDS_BIGENDIAN
|
||||
#define JMPOFS(x) ((*(VM_SWORD *)(x) << 8) >> 6)
|
||||
#else
|
||||
#define JMPOFS(x) ((*(VM_SWORD *)(x) >> 6) & ~3)
|
||||
#endif
|
||||
|
||||
static int print_reg(int col, int arg, int mode, int immshift, const VMScriptFunction *func);
|
||||
|
||||
void VMDisasm(const VM_UBYTE *code, int codesize, const VMScriptFunction *func)
|
||||
{
|
||||
const char *name;
|
||||
int col;
|
||||
int mode;
|
||||
int a;
|
||||
|
||||
for (int i = 0; i < codesize; i += 4)
|
||||
{
|
||||
name = OpInfo[code[i]].Name;
|
||||
mode = OpInfo[code[i]].Mode;
|
||||
a = code[i+1];
|
||||
|
||||
// String comparison encodes everything in a single instruction.
|
||||
if (code[i] == OP_CMPS)
|
||||
{
|
||||
switch (a & CMP_METHOD_MASK)
|
||||
{
|
||||
case CMP_EQ: name = "eq"; break;
|
||||
case CMP_LT: name = "lt"; break;
|
||||
case CMP_LE: name = "le"; break;
|
||||
}
|
||||
mode = MODE_AIMMZ;
|
||||
mode |= (a & CMP_BK) ? MODE_BKS : MODE_BS;
|
||||
mode |= (a & CMP_CK) ? MODE_CKS : MODE_CS;
|
||||
a &= CMP_CHECK | CMP_APPROX;
|
||||
}
|
||||
|
||||
printf("%08x: %02x%02x%02x%02x %-8s", i, code[i], code[i+1], code[i+2], code[i+3], name);
|
||||
col = 0;
|
||||
switch (code[i])
|
||||
{
|
||||
case OP_JMP:
|
||||
case OP_TRY:
|
||||
col = printf("%08x", i + 4 + JMPOFS(&code[i]));
|
||||
break;
|
||||
|
||||
case OP_RET:
|
||||
if (code[i+2] != REGT_NIL)
|
||||
{
|
||||
if ((code[i+2] & REGT_FINAL) && a == 0)
|
||||
{
|
||||
col = print_reg(0, *(VM_UHALF *)&code[i+2], MODE_PARAM, 16, func);
|
||||
}
|
||||
else
|
||||
{
|
||||
col = print_reg(0, a, (mode & MODE_ATYPE) >> MODE_ASHIFT, 24, func);
|
||||
col += print_reg(col, *(VM_UHALF *)&code[i+2], MODE_PARAM, 16, func);
|
||||
if (code[i+2] & REGT_FINAL)
|
||||
{
|
||||
col += printf(" [final]");
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
if ((mode & MODE_BCTYPE) == MODE_BCCAST)
|
||||
{
|
||||
switch (code[i+3])
|
||||
{
|
||||
case CAST_I2F:
|
||||
mode = MODE_AF | MODE_BI | MODE_CUNUSED;
|
||||
break;
|
||||
case CAST_I2S:
|
||||
mode = MODE_AS | MODE_BI | MODE_CUNUSED;
|
||||
break;
|
||||
case CAST_F2I:
|
||||
mode = MODE_AI | MODE_BF | MODE_CUNUSED;
|
||||
break;
|
||||
case CAST_F2S:
|
||||
mode = MODE_AS | MODE_BF | MODE_CUNUSED;
|
||||
break;
|
||||
case CAST_P2S:
|
||||
mode = MODE_AS | MODE_BP | MODE_CUNUSED;
|
||||
break;
|
||||
case CAST_S2I:
|
||||
mode = MODE_AI | MODE_BS | MODE_CUNUSED;
|
||||
break;
|
||||
case CAST_S2F:
|
||||
mode = MODE_AF | MODE_BS | MODE_CUNUSED;
|
||||
break;
|
||||
default:
|
||||
mode = MODE_AX | MODE_BX | MODE_CIMMZ;
|
||||
break;
|
||||
}
|
||||
}
|
||||
col = print_reg(0, a, (mode & MODE_ATYPE) >> MODE_ASHIFT, 24, func);
|
||||
if ((mode & MODE_BCTYPE) == MODE_BCTHROW)
|
||||
{
|
||||
mode = (code[i+1] == 0) ? (MODE_BP | MODE_CUNUSED) : (MODE_BKP | MODE_CUNUSED);
|
||||
}
|
||||
else if ((mode & MODE_BCTYPE) == MODE_BCCATCH)
|
||||
{
|
||||
switch (code[i+1])
|
||||
{
|
||||
case 0:
|
||||
mode = MODE_BUNUSED | MODE_CUNUSED;
|
||||
break;
|
||||
case 1:
|
||||
mode = MODE_BUNUSED | MODE_CP;
|
||||
break;
|
||||
case 2:
|
||||
mode = MODE_BP | MODE_CP;
|
||||
break;
|
||||
case 3:
|
||||
mode = MODE_BKP | MODE_CP;
|
||||
break;
|
||||
default:
|
||||
mode = MODE_BIMMZ | MODE_CIMMZ;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ((mode & (MODE_BTYPE | MODE_CTYPE)) == MODE_BCJOINT)
|
||||
{
|
||||
col += print_reg(col, *(VM_UHALF *)&code[i+2], (mode & MODE_BCTYPE) >> MODE_BCSHIFT, 16, func);
|
||||
}
|
||||
else
|
||||
{
|
||||
col += print_reg(col, code[i+2], (mode & MODE_BTYPE) >> MODE_BSHIFT, 24, func);
|
||||
col += print_reg(col, code[i+3], (mode & MODE_CTYPE) >> MODE_CSHIFT, 24, func);
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (col > 30)
|
||||
{
|
||||
col = 30;
|
||||
}
|
||||
printf("%*c", 30 - col, ';');
|
||||
if (code[i] == OP_JMP || code[i] == OP_TRY)
|
||||
{
|
||||
printf("%d\n", JMPOFS(&code[i]) >> 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("%d,%d,%d\n", code[i+1], code[i+2], code[i+3]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int print_reg(int col, int arg, int mode, int immshift, const VMScriptFunction *func)
|
||||
{
|
||||
if (mode == MODE_UNUSED)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if (col > 0)
|
||||
{
|
||||
col = printf(",");
|
||||
}
|
||||
switch(mode)
|
||||
{
|
||||
case MODE_I:
|
||||
return col+printf("d%d", arg);
|
||||
case MODE_F:
|
||||
return col+printf("f%d", arg);
|
||||
case MODE_S:
|
||||
return col+printf("s%d", arg);
|
||||
case MODE_P:
|
||||
return col+printf("a%d", arg);
|
||||
case MODE_V:
|
||||
return col+printf("v%d", arg);
|
||||
|
||||
case MODE_KI:
|
||||
if (func != NULL)
|
||||
{
|
||||
return col+printf("%d", func->KonstD[arg]);
|
||||
}
|
||||
return printf("kd%d", arg);
|
||||
case MODE_KF:
|
||||
if (func != NULL)
|
||||
{
|
||||
return col+printf("%f", func->KonstF[arg]);
|
||||
}
|
||||
return col+printf("kf%d", arg);
|
||||
case MODE_KS:
|
||||
if (func != NULL)
|
||||
{
|
||||
return col+printf("\"%s\"", func->KonstS[arg].GetChars());
|
||||
}
|
||||
return col+printf("ks%d", arg);
|
||||
case MODE_KP:
|
||||
if (func != NULL)
|
||||
{
|
||||
return col+printf("%p", func->KonstA[arg]);
|
||||
}
|
||||
return col+printf("ka%d", arg);
|
||||
case MODE_KV:
|
||||
if (func != NULL)
|
||||
{
|
||||
return col+printf("(%f,%f,%f)", func->KonstF[arg], func->KonstF[arg+1], func->KonstF[arg+2]);
|
||||
}
|
||||
return col+printf("kv%d", arg);
|
||||
|
||||
case MODE_IMMS:
|
||||
return col+printf("%d", (arg << immshift) >> immshift);
|
||||
|
||||
case MODE_IMMZ:
|
||||
return col+printf("%d", arg);
|
||||
|
||||
case MODE_PARAM:
|
||||
{
|
||||
union { VM_UHALF Together; struct { VM_UBYTE RegType, RegNum; }; } p;
|
||||
p.Together = arg;
|
||||
switch (p.RegType & (REGT_TYPE | REGT_KONST | REGT_MULTIREG))
|
||||
{
|
||||
case REGT_INT:
|
||||
return col+printf("d%d", p.RegNum);
|
||||
case REGT_FLOAT:
|
||||
return col+printf("f%d", p.RegNum);
|
||||
case REGT_STRING:
|
||||
return col+printf("s%d", p.RegNum);
|
||||
case REGT_POINTER:
|
||||
return col+printf("a%d", p.RegNum);
|
||||
case REGT_FLOAT | REGT_MULTIREG:
|
||||
return col+printf("v%d", p.RegNum);
|
||||
case REGT_INT | REGT_KONST:
|
||||
return col+print_reg(0, p.RegNum, MODE_KI, 0, func);
|
||||
case REGT_FLOAT | REGT_KONST:
|
||||
return col+print_reg(0, p.RegNum, MODE_KF, 0, func);
|
||||
case REGT_STRING | REGT_KONST:
|
||||
return col+print_reg(0, p.RegNum, MODE_KS, 0, func);
|
||||
case REGT_POINTER | REGT_KONST:
|
||||
return col+print_reg(0, p.RegNum, MODE_KP, 0, func);
|
||||
case REGT_FLOAT | REGT_MULTIREG | REGT_KONST:
|
||||
return col+print_reg(0, p.RegNum, MODE_KV, 0, func);
|
||||
default:
|
||||
return col+printf("param[t=%d,%c,%c,n=%d]",
|
||||
p.RegType & REGT_TYPE,
|
||||
p.RegType & REGT_KONST ? 'k' : 'r',
|
||||
p.RegType & REGT_MULTIREG ? 'm' : 's',
|
||||
p.RegNum);
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
return col+printf("$%d", arg);
|
||||
}
|
||||
return col;
|
||||
}
|
185
zscript/vmexec.cpp
Normal file
185
zscript/vmexec.cpp
Normal file
|
@ -0,0 +1,185 @@
|
|||
#include <math.h>
|
||||
#include "vm.h"
|
||||
|
||||
#define IMPLEMENT_VMEXEC
|
||||
|
||||
#if !defined(COMPGOTO) && defined(__GNUC__)
|
||||
#define COMPGOTO 1
|
||||
#endif
|
||||
|
||||
#if COMPGOTO
|
||||
#define OP(x) x
|
||||
#define NEXTOP do { unsigned op = *pc; a = pc[1]; pc += 4; goto *ops[op]; } while(0)
|
||||
#else
|
||||
#define OP(x) case OP_##x
|
||||
#define NEXTOP break
|
||||
#endif
|
||||
|
||||
#define luai_nummod(a,b) ((a) - floor((a)/(b))*(b))
|
||||
|
||||
#define A (*(pc - 3))
|
||||
#define B (*(pc - 2))
|
||||
#define C ( *(pc - 1))
|
||||
#define Cs (*(VM_SBYTE *)(pc - 1))
|
||||
#define BC (*(VM_UHALF *)(pc - 2))
|
||||
#define BCs (*(VM_SHALF *)(pc - 2))
|
||||
|
||||
#ifdef WORDS_BIGENDIAN
|
||||
#define JMPOFS(x) ((*(VM_SWORD *)(x) << 8) >> 6)
|
||||
#else
|
||||
#define JMPOFS(x) ((*(VM_SWORD *)(x) >> 6) & ~3)
|
||||
#endif
|
||||
|
||||
#define KC (konst[C])
|
||||
#define RC (reg.i[C])
|
||||
|
||||
#define PA (reg.a[A])
|
||||
#define PB (reg.a[B])
|
||||
|
||||
#define ASSERTD(x) assert((unsigned)(x) < f->NumRegD)
|
||||
#define ASSERTF(x) assert((unsigned)(x) < f->NumRegF)
|
||||
#define ASSERTA(x) assert((unsigned)(x) < f->NumRegA)
|
||||
#define ASSERTS(x) assert((unsigned)(x) < f->NumRegS)
|
||||
|
||||
#define ASSERTKD(x) assert(sfunc != NULL && (unsigned)(x) < sfunc->NumKonstD)
|
||||
#define ASSERTKF(x) assert(sfunc != NULL && (unsigned)(x) < sfunc->NumKonstF)
|
||||
#define ASSERTKA(x) assert(sfunc != NULL && (unsigned)(x) < sfunc->NumKonstA)
|
||||
#define ASSERTKS(x) assert(sfunc != NULL && (unsigned)(x) < sfunc->NumKonstS)
|
||||
|
||||
#define THROW(x)
|
||||
|
||||
#define CMPJMP(test) \
|
||||
if ((test) == (a & CMP_CHECK)) { \
|
||||
assert(*pc == OP_JMP); \
|
||||
pc += 4 + JMPOFS(pc); \
|
||||
} else { \
|
||||
pc += 4; \
|
||||
}
|
||||
|
||||
enum
|
||||
{
|
||||
X_READ_NIL,
|
||||
X_WRITE_NIL,
|
||||
X_TOO_MANY_TRIES,
|
||||
};
|
||||
|
||||
#define GETADDR(a,o,x) \
|
||||
if (a == 0) { THROW(x); } \
|
||||
ptr = (VM_SBYTE *)a + x \
|
||||
|
||||
static const VM_UWORD ZapTable[16] =
|
||||
{
|
||||
0x00000000, 0x000000FF, 0x0000FF00, 0x0000FFFF,
|
||||
0x00FF0000, 0x00FF00FF, 0x00FFFF00, 0x00FFFFFF,
|
||||
0xFF000000, 0xFF0000FF, 0xFF00FF00, 0xFF00FFFF,
|
||||
0xFFFF0000, 0xFFFF00FF, 0xFFFFFF00, 0xFFFFFFFF
|
||||
};
|
||||
|
||||
#ifdef NDEBUG
|
||||
#define WAS_NDEBUG 1
|
||||
#else
|
||||
#define WAS_NDEBUG 0
|
||||
#endif
|
||||
|
||||
#if WAS_NDEBUG
|
||||
#undef NDEBUG
|
||||
#endif
|
||||
#undef assert
|
||||
#include <assert.h>
|
||||
struct VMExec_Checked
|
||||
{
|
||||
#include "vmexec.h"
|
||||
};
|
||||
#if WAS_NDEBUG
|
||||
#define NDEBUG
|
||||
#endif
|
||||
|
||||
#if !WAS_NDEBUG
|
||||
#define NDEBUG
|
||||
#endif
|
||||
#undef assert
|
||||
#include <assert.h>
|
||||
struct VMExec_Unchecked
|
||||
{
|
||||
#include "vmexec.h"
|
||||
};
|
||||
#if !WAS_NDEBUG
|
||||
#undef NDEBUG
|
||||
#endif
|
||||
#undef assert
|
||||
#include <assert.h>
|
||||
|
||||
int (*VMExec)(VMFrameStack *stack, const VM_UBYTE *pc, VMReturn *ret, int numret) =
|
||||
#ifdef NDEBUG
|
||||
VMExec_Unchecked::Exec
|
||||
#else
|
||||
VMExec_Checked::Exec
|
||||
#endif
|
||||
;
|
||||
|
||||
void VMSelectEngine(EVMEngine engine)
|
||||
{
|
||||
switch (engine)
|
||||
{
|
||||
case VMEngine_Default:
|
||||
#ifdef NDEBUG
|
||||
VMExec = VMExec_Unchecked::Exec;
|
||||
#else
|
||||
VMExec = VMExec_Checked::Exec;
|
||||
#endif
|
||||
break;
|
||||
case VMEngine_Unchecked:
|
||||
VMExec = VMExec_Unchecked::Exec;
|
||||
break;
|
||||
case VMEngine_Checked:
|
||||
VMExec = VMExec_Checked::Exec;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// VMFillParams
|
||||
//
|
||||
// Takes parameters from the paramater stack and stores them in the callee's
|
||||
// registers.
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
void VMFillParams(VMValue *params, VMFrame *callee, int numparam)
|
||||
{
|
||||
unsigned int regd, regf, regs, rega;
|
||||
VMScriptFunction *calleefunc = static_cast<VMScriptFunction *>(callee->Func);
|
||||
const VMRegisters calleereg(callee);
|
||||
|
||||
assert(calleefunc != NULL && !calleefunc->Native);
|
||||
assert(numparam == calleefunc->NumArgs);
|
||||
assert(REGT_INT == 0 && REGT_FLOAT == 1 && REGT_STRING == 2 && REGT_POINTER == 3);
|
||||
|
||||
regd = regf = regs = rega = 0;
|
||||
for (int i = 0; i < numparam; ++i)
|
||||
{
|
||||
VMValue &p = params[i];
|
||||
if (p.Type < REGT_STRING)
|
||||
{
|
||||
if (p.Type == REGT_INT)
|
||||
{
|
||||
calleereg.d[regd++] = p.i;
|
||||
}
|
||||
else // p.Type == REGT_FLOAT
|
||||
{
|
||||
calleereg.f[regf++] = p.f;
|
||||
}
|
||||
}
|
||||
else if (p.Type == REGT_STRING)
|
||||
{
|
||||
calleereg.s[regs++] = p.s();
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(p.Type == REGT_POINTER);
|
||||
calleereg.a[rega] = p.a;
|
||||
calleereg.atag[rega++] = p.atag;
|
||||
}
|
||||
}
|
||||
}
|
1495
zscript/vmexec.h
Normal file
1495
zscript/vmexec.h
Normal file
File diff suppressed because it is too large
Load diff
294
zscript/vmframe.cpp
Normal file
294
zscript/vmframe.cpp
Normal file
|
@ -0,0 +1,294 @@
|
|||
#include <new>
|
||||
#include "vm.h"
|
||||
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// VMFrame :: InitRegS
|
||||
//
|
||||
// Initialize the string registers of a newly-allocated VMFrame.
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
void VMFrame::InitRegS()
|
||||
{
|
||||
FString *regs = GetRegS();
|
||||
for (int i = 0; i < NumRegS; ++i)
|
||||
{
|
||||
::new(®s[i]) FString;
|
||||
}
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// VMFrameStack - Constructor
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
VMFrameStack::VMFrameStack()
|
||||
{
|
||||
Blocks = NULL;
|
||||
UnusedBlocks = NULL;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// VMFrameStack - Destructor
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
VMFrameStack::~VMFrameStack()
|
||||
{
|
||||
while (PopFrame() != NULL)
|
||||
{ }
|
||||
if (Blocks != NULL)
|
||||
{
|
||||
BlockHeader *block, *next;
|
||||
for (block = Blocks; block != NULL; block = next)
|
||||
{
|
||||
next = block->NextBlock;
|
||||
delete[] (VM_UBYTE *)block;
|
||||
}
|
||||
}
|
||||
if (UnusedBlocks != NULL)
|
||||
{
|
||||
BlockHeader *block, *next;
|
||||
for (block = UnusedBlocks; block != NULL; block = next)
|
||||
{
|
||||
next = block->NextBlock;
|
||||
delete[] (VM_UBYTE *)block;
|
||||
}
|
||||
}
|
||||
Blocks = NULL;
|
||||
UnusedBlocks = NULL;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// VMFrameStack :: AllocFrame
|
||||
//
|
||||
// Allocates a frame from the stack with the desired number of registers.
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
VMFrame *VMFrameStack::AllocFrame(int numregd, int numregf, int numregs, int numrega)
|
||||
{
|
||||
assert((unsigned)numregd < 255);
|
||||
assert((unsigned)numregf < 255);
|
||||
assert((unsigned)numregs < 255);
|
||||
assert((unsigned)numrega < 255);
|
||||
// To keep the arguments to this function simpler, it assumes that every
|
||||
// register might be used as a parameter for a single call.
|
||||
int numparam = numregd + numregf + numregs + numrega;
|
||||
int size = VMFrame::FrameSize(numregd, numregf, numregs, numrega, numparam, 0);
|
||||
VMFrame *frame = Alloc(size);
|
||||
frame->NumRegD = numregd;
|
||||
frame->NumRegF = numregf;
|
||||
frame->NumRegS = numregs;
|
||||
frame->NumRegA = numrega;
|
||||
frame->MaxParam = numparam;
|
||||
frame->InitRegS();
|
||||
return frame;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// VMFrameStack :: AllocFrame
|
||||
//
|
||||
// Allocates a frame from the stack suitable for calling a particular
|
||||
// function.
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
VMFrame *VMFrameStack::AllocFrame(VMScriptFunction *func)
|
||||
{
|
||||
int size = VMFrame::FrameSize(func->NumRegD, func->NumRegF, func->NumRegS, func->NumRegA,
|
||||
func->MaxParam, func->ExtraSpace);
|
||||
VMFrame *frame = Alloc(size);
|
||||
frame->Func = func;
|
||||
frame->NumRegD = func->NumRegD;
|
||||
frame->NumRegF = func->NumRegF;
|
||||
frame->NumRegS = func->NumRegS;
|
||||
frame->NumRegA = func->NumRegA;
|
||||
frame->MaxParam = func->MaxParam;
|
||||
frame->Func = func;
|
||||
frame->InitRegS();
|
||||
return frame;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// VMFrameStack :: Alloc
|
||||
//
|
||||
// Allocates space for a frame. Its size will be rounded up to a multiple
|
||||
// of 16 bytes.
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
VMFrame *VMFrameStack::Alloc(int size)
|
||||
{
|
||||
BlockHeader *block;
|
||||
VMFrame *frame, *parent;
|
||||
|
||||
size = (size + 15) & ~15;
|
||||
block = Blocks;
|
||||
if (block != NULL)
|
||||
{
|
||||
parent = block->LastFrame;
|
||||
}
|
||||
else
|
||||
{
|
||||
parent = NULL;
|
||||
}
|
||||
if (block == NULL || ((VM_UBYTE *)block + block->BlockSize) < (block->FreeSpace + size))
|
||||
{ // Not enough space. Allocate a new block.
|
||||
int blocksize = ((sizeof(BlockHeader) + 15) & ~15) + size;
|
||||
BlockHeader **blockp;
|
||||
if (blocksize < BLOCK_SIZE)
|
||||
{
|
||||
blocksize = BLOCK_SIZE;
|
||||
}
|
||||
for (blockp = &UnusedBlocks, block = *blockp; block != NULL; block = block->NextBlock)
|
||||
{
|
||||
if (block->BlockSize >= blocksize)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (block != NULL)
|
||||
{
|
||||
*blockp = block->NextBlock;
|
||||
}
|
||||
else
|
||||
{
|
||||
block = (BlockHeader *)new VM_UBYTE[blocksize];
|
||||
block->BlockSize = blocksize;
|
||||
}
|
||||
block->FreeSpace = (VM_UBYTE *)block + ((sizeof(BlockHeader) + 15) & ~15);
|
||||
block->LastFrame = NULL;
|
||||
block->NextBlock = Blocks;
|
||||
Blocks = block;
|
||||
}
|
||||
frame = (VMFrame *)block->FreeSpace;
|
||||
memset(frame, 0, size);
|
||||
frame->ParentFrame = parent;
|
||||
block->FreeSpace += size;
|
||||
block->LastFrame = frame;
|
||||
return frame;
|
||||
}
|
||||
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// VMFrameStack :: PopFrame
|
||||
//
|
||||
// Pops the top frame off the stack, returning a pointer to the new top
|
||||
// frame.
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
VMFrame *VMFrameStack::PopFrame()
|
||||
{
|
||||
if (Blocks == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
VMFrame *frame = Blocks->LastFrame;
|
||||
if (frame == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
// Free any string registers this frame had.
|
||||
FString *regs = frame->GetRegS();
|
||||
for (int i = frame->NumRegS; i != 0; --i)
|
||||
{
|
||||
(regs++)->~FString();
|
||||
}
|
||||
// Free any parameters this frame left behind.
|
||||
VMValue *param = frame->GetParam();
|
||||
for (int i = frame->NumParam; i != 0; --i)
|
||||
{
|
||||
(param++)->~VMValue();
|
||||
}
|
||||
VMFrame *parent = frame->ParentFrame;
|
||||
if (parent == NULL)
|
||||
{
|
||||
// Popping the last frame off the stack.
|
||||
if (Blocks != NULL)
|
||||
{
|
||||
assert(Blocks->NextBlock == NULL);
|
||||
Blocks->LastFrame = NULL;
|
||||
Blocks->FreeSpace = (VM_UBYTE *)Blocks + ((sizeof(BlockHeader) + 15) & ~15);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
if ((VM_UBYTE *)parent < (VM_UBYTE *)Blocks || (VM_UBYTE *)parent >= (VM_UBYTE *)Blocks + Blocks->BlockSize)
|
||||
{ // Parent frame is in a different block, so move this one to the unused list.
|
||||
BlockHeader *next = Blocks->NextBlock;
|
||||
assert(next != NULL);
|
||||
assert((VM_UBYTE *)parent >= (VM_UBYTE *)next && (VM_UBYTE *)parent < (VM_UBYTE *)next + next->BlockSize);
|
||||
Blocks->NextBlock = UnusedBlocks;
|
||||
UnusedBlocks = Blocks;
|
||||
Blocks = next;
|
||||
}
|
||||
else
|
||||
{
|
||||
Blocks->LastFrame = parent;
|
||||
Blocks->FreeSpace = (VM_UBYTE *)frame;
|
||||
}
|
||||
return parent;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// VMFrameStack :: Call
|
||||
//
|
||||
// Calls a function, either native or scripted. If an exception occurs while
|
||||
// executing, the stack is cleaned up. If trap is non-NULL, it is set to the
|
||||
// VMException that was caught and the return value is negative. Otherwise,
|
||||
// any caught exceptions will be rethrown. Under normal termination, the
|
||||
// return value is the number of results from the function.
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
int VMFrameStack::Call(VMFunction *func, VMValue *params, int numparams, VMReturn *results, int numresults, VMException **trap)
|
||||
{
|
||||
bool allocated = false;
|
||||
try
|
||||
{
|
||||
if (func->Native)
|
||||
{
|
||||
return static_cast<VMNativeFunction *>(func)->NativeCall(this, params, numparams, results, numresults);
|
||||
}
|
||||
else
|
||||
{
|
||||
AllocFrame(static_cast<VMScriptFunction *>(func));
|
||||
allocated = true;
|
||||
VMFillParams(params, TopFrame(), numparams);
|
||||
int numret = VMExec(this, static_cast<VMScriptFunction *>(func)->Code, results, numresults);
|
||||
PopFrame();
|
||||
return numret;
|
||||
}
|
||||
}
|
||||
catch (VMException *exception)
|
||||
{
|
||||
if (allocated)
|
||||
{
|
||||
PopFrame();
|
||||
}
|
||||
if (trap != NULL)
|
||||
{
|
||||
*trap = exception;
|
||||
return -1;
|
||||
}
|
||||
throw;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
if (allocated)
|
||||
{
|
||||
PopFrame();
|
||||
}
|
||||
throw;
|
||||
}
|
||||
}
|
210
zscript/vmops.h
Normal file
210
zscript/vmops.h
Normal file
|
@ -0,0 +1,210 @@
|
|||
#ifndef xx
|
||||
#define xx(op, name, mode) OP_##op
|
||||
#endif
|
||||
|
||||
// Load constants.
|
||||
xx(LI, li, LI), // load immediate signed 16-bit constant
|
||||
xx(LK, lk, LKI), // load integer constant
|
||||
xx(LKF, lk, LKF), // load float constant
|
||||
xx(LKS, lk, LKS), // load string constant
|
||||
xx(LKP, lk, LKP), // load pointer constant
|
||||
xx(LFP, lf, LFP), // load frame pointer
|
||||
|
||||
// Load from memory. rA = *(rB + rkC)
|
||||
xx(LB, lb, RIRPKI), // load byte
|
||||
xx(LB_R, lb, RIRPRI),
|
||||
xx(LH, lh, RIRPKI), // load halfword
|
||||
xx(LH_R, lh, RIRPRI),
|
||||
xx(LW, lw, RIRPKI), // load word
|
||||
xx(LW_R, lw, RIRPRI),
|
||||
xx(LBU, lbu, RIRPKI), // load byte unsigned
|
||||
xx(LBU_R, lbu, RIRPRI),
|
||||
xx(LHU, lhu, RIRPKI), // load halfword unsigned
|
||||
xx(LHU_R, lhu, RIRPRI),
|
||||
xx(LSP, lsp, RFRPKI), // load single-precision fp
|
||||
xx(LSP_R, lsp, RFRPRI),
|
||||
xx(LDP, ldp, RFRPKI), // load double-precision fp
|
||||
xx(LDP_R, ldp, RFRPRI),
|
||||
xx(LS, ls, RSRPKI), // load string
|
||||
xx(LS_R, ls, RSRPRI),
|
||||
xx(LO, lo, RPRPKI), // load object
|
||||
xx(LO_R, lo, RPRPRI),
|
||||
xx(LP, lp, RPRPKI), // load pointer
|
||||
xx(LP_R, lp, RPRPRI),
|
||||
xx(LV, lv, RVRPKI), // load vector
|
||||
xx(LV_R, lv, RVRPRI),
|
||||
xx(LX, lx, RFRPKI), // load fixed point
|
||||
xx(LX_R, lx, RFRPRI),
|
||||
|
||||
xx(LBIT, lbit, RIRPI8), // rA = !!(*rB & C) -- *rB is a byte
|
||||
|
||||
// Store instructions. *(rA + rkC) = rB
|
||||
xx(SB, sb, RPRIKI), // store byte
|
||||
xx(SB_R, sb, RPRIRI),
|
||||
xx(SH, sh, RPRIKI), // store halfword
|
||||
xx(SH_R, sh, RPRIRI),
|
||||
xx(SW, sw, RPRIKI), // store word
|
||||
xx(SW_R, sw, RPRIRI),
|
||||
xx(SSP, ssp, RPRFKI), // store single-precision fp
|
||||
xx(SSP_R, ssp, RPRFRI),
|
||||
xx(SDP, sdp, RPRFKI), // store double-precision fp
|
||||
xx(SDP_R, sdp, RPRFRI),
|
||||
xx(SS, ss, RPRSKI), // store string
|
||||
xx(SS_R, ss, RPRSRI),
|
||||
xx(SP, sp, RPRPKI), // store pointer
|
||||
xx(SP_R, sp, RPRPRI),
|
||||
xx(SV, sv, RPRVKI), // store vector
|
||||
xx(SV_R, sv, RPRVRI),
|
||||
xx(SX, sx, RPRFKI), // store fixed point
|
||||
xx(SX_R, sx, RPRFRI),
|
||||
|
||||
xx(SBIT, sbit, RPRII8), // *rA |= C if rB is true, *rA &= ~C otherwise
|
||||
|
||||
// Move instructions.
|
||||
xx(MOVE, mov, RIRI), // dA = dB
|
||||
xx(MOVEF, mov, RFRF), // fA = fB
|
||||
xx(MOVES, mov, RSRS), // sA = sB
|
||||
xx(MOVEA, mov, RPRP), // aA = aB
|
||||
xx(CAST, cast, CAST), // xA = xB, conversion specified by C
|
||||
xx(DYNCAST_R, dyncast,RPRPRP), // aA = aB after casting to rkC (specifying a class)
|
||||
xx(DYNCAST_K, dyncast,RPRPKP),
|
||||
|
||||
// Control flow.
|
||||
xx(TEST, test, RII16), // if (dA != BC) then pc++
|
||||
xx(JMP, jmp, I24), // pc += ABC -- The ABC fields contain a signed 24-bit offset.
|
||||
xx(IJMP, ijmp, RII16), // pc += dA + BC -- BC is a signed offset. The target instruction must be a JMP.
|
||||
xx(PARAM, param, __BCP), // push parameter encoded in BC for function call or result for return
|
||||
xx(CALL, call, RPI8I8), // Call function pkA with parameter count B and expected result count C
|
||||
xx(CALL_K, call, KPI8I8),
|
||||
xx(RESULT, result, __BCP), // Result should go in register encoded in BC (in caller, after CALL)
|
||||
xx(RET, ret, I8BCP), // Copy value from register encoded in BC to return value A, possibly returning
|
||||
xx(TRY, try, I24), // When an exception is thrown, start searching for a handler at pc + ABC
|
||||
xx(UNTRY, untry, I8), // Pop A entries off the exception stack
|
||||
xx(THROW, throw, THROW), // A == 0: Throw exception object pB
|
||||
// A != 0: Throw exception object pkB
|
||||
xx(CATCH, catch, CATCH), // A == 0: continue search on next try
|
||||
// A == 1: continue execution at instruction immediately following CATCH (catches any exception)
|
||||
// A == 2: (pB == <type of exception thrown>) then pc++ ; next instruction must JMP to another CATCH
|
||||
// A == 3: (pkB == <type of exception thrown>) then pc++ ; next instruction must JMP to another CATCH
|
||||
// for A > 0, exception is stored in pC
|
||||
|
||||
// String instructions.
|
||||
xx(CONCAT, concat, RSRSRS), // sA = sB.. ... ..sC
|
||||
xx(LENS, lens, RIRS), // dA = sB.Length
|
||||
xx(CMPS, cmps, I8RXRX), // if ((skB op skC) != (A & 1)) then pc++
|
||||
|
||||
// Integer math.
|
||||
xx(SLL_RR, sll, RIRIRI), // dA = dkB << diC
|
||||
xx(SLL_RI, sll, RIRII8),
|
||||
xx(SLL_KR, sll, RIKIRI),
|
||||
xx(SRL_RR, srl, RIRIRI), // dA = dkB >> diC -- unsigned
|
||||
xx(SRL_RI, srl, RIRII8),
|
||||
xx(SRL_KR, srl, RIKIRI),
|
||||
xx(SRA_RR, sra, RIRIRI), // dA = dkB >> diC -- signed
|
||||
xx(SRA_RI, sra, RIRII8),
|
||||
xx(SRA_KR, sra, RIKIRI),
|
||||
xx(ADD_RR, add, RIRIRI), // dA = dB + dkC
|
||||
xx(ADD_RK, add, RIRIKI),
|
||||
xx(ADDI, add, RIRIIs), // dA = dB + C -- C is a signed 8-bit constant
|
||||
xx(SUB_RR, sub, RIRIRI), // dA = dkB - dkC
|
||||
xx(SUB_RK, sub, RIRIKI),
|
||||
xx(SUB_KR, sub, RIKIRI),
|
||||
xx(MUL_RR, mul, RIRIRI), // dA = dB * dkC
|
||||
xx(MUL_RK, mul, RIRIKI),
|
||||
xx(DIV_RR, div, RIRIRI), // dA = dkB / dkC
|
||||
xx(DIV_RK, div, RIRIKI),
|
||||
xx(DIV_KR, div, RIKIRI),
|
||||
xx(MOD_RR, mod, RIRIRI), // dA = dkB % dkC
|
||||
xx(MOD_RK, mod, RIRIKI),
|
||||
xx(MOD_KR, mod, RIKIRI),
|
||||
xx(AND_RR, and, RIRIRI), // dA = dB & dkC
|
||||
xx(AND_RK, and, RIRIKI),
|
||||
xx(OR_RR, or, RIRIRI), // dA = dB | dkC
|
||||
xx(OR_RK, or, RIRIKI),
|
||||
xx(XOR_RR, xor, RIRIRI), // dA = dB ^ dkC
|
||||
xx(XOR_RK, xor, RIRIKI),
|
||||
xx(MIN_RR, min, RIRIRI), // dA = min(dB,dkC)
|
||||
xx(MIN_RK, min, RIRIKI),
|
||||
xx(MAX_RR, max, RIRIRI), // dA = max(dB,dkC)
|
||||
xx(MAX_RK, max, RIRIKI),
|
||||
xx(ABS, abs, RIRI), // dA = abs(dB)
|
||||
xx(NEG, neg, RIRI), // dA = -dB
|
||||
xx(NOT, not, RIRI), // dA = !dB
|
||||
xx(SEXT, sext, RIRII8), // dA = dB, sign extended by shifting left then right by C
|
||||
xx(ZAP_R, zap, RIRIRI), // dA = dB, with bytes zeroed where bits in C/dC are one
|
||||
xx(ZAP_I, zap, RIRII8),
|
||||
xx(ZAPNOT_R, zapnot, RIRIRI), // dA = dB, with bytes zeroed where bits in C/dC are zero
|
||||
xx(ZAPNOT_I, zapnot, RIRII8),
|
||||
xx(EQ_R, eq, I8RIRI), // if ((dB == dkC) != A) then pc++
|
||||
xx(EQ_K, eq, I8RIKI),
|
||||
xx(LT_RR, lt, I8RIRI), // if ((dkB < dkC) != A) then pc++
|
||||
xx(LT_RK, lt, I8RIKI),
|
||||
xx(LT_KR, lt, I8KIRI),
|
||||
xx(LE_RR, le, I8RIRI), // if ((dkB <= dkC) != A) then pc++
|
||||
xx(LE_RK, le, I8RIKI),
|
||||
xx(LE_KR, le, I8KIRI),
|
||||
xx(LTU_RR, ltu, I8RIRI), // if ((dkB < dkC) != A) then pc++ -- unsigned
|
||||
xx(LTU_RK, ltu, I8RIKI),
|
||||
xx(LTU_KR, ltu, I8KIRI),
|
||||
xx(LEU_RR, leu, I8RIRI), // if ((dkB <= dkC) != A) then pc++ -- unsigned
|
||||
xx(LEU_RK, leu, I8RIKI),
|
||||
xx(LEU_KR, leu, I8KIRI),
|
||||
|
||||
// Double-precision floating point math.
|
||||
xx(ADDF_RR, add, RFRFRF), // fA = fB + fkC
|
||||
xx(ADDF_RK, add, RFRFKF),
|
||||
xx(SUBF_RR, sub, RFRFRF), // fA = fkB - fkC
|
||||
xx(SUBF_RK, sub, RFRFKF),
|
||||
xx(SUBF_KR, sub, RFKFRF),
|
||||
xx(MULF_RR, mul, RFRFRF), // fA = fB * fkC
|
||||
xx(MULF_RK, mul, RFRFKF),
|
||||
xx(DIVF_RR, div, RFRFRF), // fA = fkB / fkC
|
||||
xx(DIVF_RK, div, RFRFKF),
|
||||
xx(DIVF_KR, div, RFKFRF),
|
||||
xx(MODF_RR, mod, RFRFRF), // fA = fkB % fkC
|
||||
xx(MODF_RK, mod, RFRFKF),
|
||||
xx(MODF_KR, mod, RFKFRF),
|
||||
xx(POWF_RR, pow, RFRFRF), // fA = fkB ** fkC
|
||||
xx(POWF_RK, pow, RFRFKF),
|
||||
xx(POWF_KR, pow, RFKFRF),
|
||||
xx(MINF_RR, min, RFRFRF), // fA = min(fB),fkC)
|
||||
xx(MINF_RK, min, RFRFKF),
|
||||
xx(MAXF_RR, max, RFRFRF), // fA = max(fB),fkC)
|
||||
xx(MAXF_RK, max, RFRFKF),
|
||||
xx(FLOP, flop, RFRFI8), // fA = f(fB), where function is selected by C
|
||||
xx(EQF_R, eq, I8RFRF), // if ((fB == fkC) != (A & 1)) then pc++
|
||||
xx(EQF_K, eq, I8RFKF),
|
||||
xx(LTF_RR, lt, I8RFRF), // if ((fkB < fkC) != (A & 1)) then pc++
|
||||
xx(LTF_RK, lt, I8RFKF),
|
||||
xx(LTF_KR, lt, I8KFRF),
|
||||
xx(LEF_RR, le, I8RFRF), // if ((fkb <= fkC) != (A & 1)) then pc++
|
||||
xx(LEF_RK, le, I8RFKF),
|
||||
xx(LEF_KR, le, I8KFRF),
|
||||
|
||||
// Vector math.
|
||||
xx(NEGV, negv, RVRV), // vA = -vB
|
||||
xx(ADDV_RR, addv, RVRVRV), // vA = vB + vkC
|
||||
xx(ADDV_RK, addv, RVRVKV),
|
||||
xx(SUBV_RR, subv, RVRVRV), // vA = vkB - vkC
|
||||
xx(SUBV_RK, subv, RVRVKV),
|
||||
xx(SUBV_KR, subv, RVKVRV),
|
||||
xx(DOTV_RR, dotv, RVRVRV), // va = vB dot vkC
|
||||
xx(DOTV_RK, dotv, RVRVKV),
|
||||
xx(CROSSV_RR, crossv, RVRVRV), // vA = vkB cross vkC
|
||||
xx(CROSSV_RK, crossv, RVRVKV),
|
||||
xx(CROSSV_KR, crossv, RVKVRV),
|
||||
xx(MULVF_RR, mulv, RVRVRV), // vA = vkB * fkC
|
||||
xx(MULVF_RK, mulv, RVRVKV),
|
||||
xx(MULVF_KR, mulv, RVKVRV),
|
||||
xx(LENV, lenv, RFRV), // fA = vB.Length
|
||||
xx(EQV_R, eqv, I8RVRV), // if ((vB == vkC) != A) then pc++ (inexact if A & 32)
|
||||
xx(EQV_K, eqv, I8RVKV),
|
||||
|
||||
// Pointer math.
|
||||
xx(ADDA_RR, add, RPRPRI), // pA = pB + dkC
|
||||
xx(ADDA_RK, add, RPRPKI),
|
||||
xx(SUBA, sub, RIRPRP), // dA = pB - pC
|
||||
xx(EQA_R, eq, I8RPRP), // if ((pB == pkC) != A) then pc++
|
||||
xx(EQA_K, eq, I8RPKP),
|
||||
|
||||
#undef xx
|
Loading…
Reference in a new issue