using System; using System.Collections.Generic; using System.Text; namespace IFPSLib.Emit { public static class OpCodes { private static void FillTable(OpCode[] table, OpCode op) { for (int i = 0; i < table.Length; i++) if (table[i] == null) table[i] = op; } /// /// All one-byte opcodes /// public static readonly OpCode[] OneByteOpCodes = new OpCode[byte.MaxValue + 1]; /// /// All two-byte ALU opcodes (first byte is 0x01) /// public static readonly OpCode[] AluOpCodes = new OpCode[(int)AluOpCode.Xor + 1]; /// /// All two-byte comparison opcodes (first byte is 0x0C) /// public static readonly OpCode[] CmpOpCodes = new OpCode[(int)CmpOpCode.Is + 1]; /// /// All two-byte exception handler leave opcodes (first byte is 0x14) /// public static readonly OpCode[] PopEHOpCodes = new OpCode[(int)PopEHOpCode.EndHandler + 1]; /// /// All two-byte set flag opcodes (first byte is 0x11). /// The second byte is after the operand for some reason. /// public static readonly OpCode[] SetFlagOpCodes = new OpCode[(int)SetFlagOpCode.Zero + 1]; /// /// All non-experimental opcodes, indexed by opcode name. /// public static IReadOnlyDictionary ByName => m_OpCodesByName; internal static readonly Dictionary m_OpCodesByName = new Dictionary(); #pragma warning disable 1591 // disable XML doc warning public static readonly OpCode UNKNOWN1 = new OpCode("UNKNOWN1", Code.UNKNOWN1, OperandType.InlineNone, FlowControl.Next, OpCodeType.Experimental, StackBehaviour.Push0, StackBehaviour.Pop0, true); public static readonly OpCode UNKNOWN_ALU = new OpCode("UNKNOWN_ALU", Code.UNKNOWN_ALU, OperandType.InlineNone, FlowControl.Next, OpCodeType.Experimental, StackBehaviour.Push0, StackBehaviour.Pop0, true); public static readonly OpCode UNKNOWN_CMP = new OpCode("UNKNOWN_CMP", Code.UNKNOWN_CMP, OperandType.InlineNone, FlowControl.Next, OpCodeType.Experimental, StackBehaviour.Push0, StackBehaviour.Pop0, true); public static readonly OpCode UNKNOWN_POPEH = new OpCode("UNKNOWN_POPEH", Code.UNKNOWN_POPEH, OperandType.InlineNone, FlowControl.Next, OpCodeType.Experimental, StackBehaviour.Push0, StackBehaviour.Pop0, true); public static readonly OpCode UNKNOWN_SF = new OpCode("UNKNOWN_SF", Code.UNKNOWN_SF, OperandType.InlineValueSF, FlowControl.Next, OpCodeType.Experimental, StackBehaviour.Push0, StackBehaviour.Pop0, true); /// /// Loads the second value (usually an immediate) into the first value; op0 = op1
/// If op0 is a pointer, then op1 is written to that pointer; *op0 = op1 ///
public static readonly OpCode Assign = new OpCode("assign", Code.Assign, OperandType.InlineValueValue, FlowControl.Next, OpCodeType.Primitive); /// /// Adds the second value to the first value; op0 += op1 /// public static readonly OpCode Add = new OpCode("add", Code.Add, OperandType.InlineValueValue, FlowControl.Next, OpCodeType.Macro); /// /// Subtracts the second value from the first value; op0 -= op1 /// public static readonly OpCode Sub = new OpCode("sub", Code.Sub, OperandType.InlineValueValue, FlowControl.Next, OpCodeType.Macro); /// /// Multiplies the second value with the first value; op0 *= op1 /// public static readonly OpCode Mul = new OpCode("mul", Code.Mul, OperandType.InlineValueValue, FlowControl.Next, OpCodeType.Macro); /// /// Divides the second value from the first value; op0 /= op1 /// public static readonly OpCode Div = new OpCode("div", Code.Div, OperandType.InlineValueValue, FlowControl.Next, OpCodeType.Macro); /// /// Modulo divides the second value from the first value; op0 %= op1 /// public static readonly OpCode Mod = new OpCode("mod", Code.Mod, OperandType.InlineValueValue, FlowControl.Next, OpCodeType.Macro); /// /// Shifts the first value left by the second value; op0 <<= op1 /// public static readonly OpCode Shl = new OpCode("shl", Code.Shl, OperandType.InlineValueValue, FlowControl.Next, OpCodeType.Macro); /// /// Shifts the first value right by the second value; op0 >>= op1 /// public static readonly OpCode Shr = new OpCode("shr", Code.Shr, OperandType.InlineValueValue, FlowControl.Next, OpCodeType.Macro); /// /// Bitwise ANDs the first value by the second value; op0 &= op1 /// public static readonly OpCode And = new OpCode("and", Code.And, OperandType.InlineValueValue, FlowControl.Next, OpCodeType.Macro); /// /// Bitwise ORs the first value by the second value; op0 |= op1 /// public static readonly OpCode Or = new OpCode("or", Code.Or, OperandType.InlineValueValue, FlowControl.Next, OpCodeType.Macro); /// /// Bitwise XORs the first value by the second value; op0 ^= op1 /// public static readonly OpCode Xor = new OpCode("xor", Code.Xor, OperandType.InlineValueValue, FlowControl.Next, OpCodeType.Macro); /// /// Pushes the operand to the top of the stack. /// public static readonly OpCode Push = new OpCode("push", Code.Push, OperandType.InlineValue, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Push1); /// /// Pushes a pointer to the operand to the top of the stack. /// public static readonly OpCode PushVar = new OpCode("pushvar", Code.PushVar, OperandType.InlineValue, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Push1); /// /// Removes the value currently on top of the stack.
/// If that value is a return address, or the stack is empty, the operation is invalid. ///
public static readonly OpCode Pop = new OpCode("pop", Code.Pop, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Push0, StackBehaviour.Pop1); /// /// Calls the function indicated by the provided index.
/// Return address gets pushed to the top of the stack (function index, offset to next instruction, stack pointer).

/// Calling convention: /// /// push arguments onto the stack, from last to first /// if function returns a value, push a pointer to the return value /// ///
public static readonly OpCode Call = new OpCode("call", Code.Call, OperandType.InlineFunction, FlowControl.Call, OpCodeType.Primitive); /// /// Unconditionally branches to the target . /// public static readonly OpCode Jump = new OpCode("jump", Code.Jump, OperandType.InlineBrTarget, FlowControl.Branch, OpCodeType.Primitive); /// /// If the value operand is not zero, branch to the target . /// public static readonly OpCode JumpNZ = new OpCode("jnz", Code.JumpNZ, OperandType.InlineBrTargetValue, FlowControl.Cond_Branch, OpCodeType.Primitive); /// /// If the value operand is zero, branch to the target . /// public static readonly OpCode JumpZ = new OpCode("jz", Code.JumpZ, OperandType.InlineBrTargetValue, FlowControl.Cond_Branch, OpCodeType.Primitive); /// /// Returns from the current function.
/// Any finally blocks inside exception handlers will be executed first.
/// Pops the entirety of the current function's stack frame.
///
public static readonly OpCode Ret = new OpCode("ret", Code.Ret, OperandType.InlineNone, FlowControl.Return, OpCodeType.Primitive, StackBehaviour.Push0, StackBehaviour.Varpop); /// /// Removed between version 20 (1.20) and 22 (1.30), after that point this is an invalid opcode.
/// Destructs the given variable, creates a new one of the provided type.
/// If the provided type is then the operation is invalid. ///
public static readonly OpCode SetStackType = new OpCode("setstacktype", Code.SetStackType, OperandType.InlineTypeVariable, FlowControl.Next, OpCodeType.Primitive); /// /// Pushes a new uninitialised value of the given type to the top of the stack. /// public static readonly OpCode PushType = new OpCode("pushtype", Code.PushType, OperandType.InlineType, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Push1); /// /// op0 = op1 >= op2 /// public static readonly OpCode Ge = new OpCode("ge", Code.Ge, OperandType.InlineCmpValue, FlowControl.Next, OpCodeType.Macro); /// /// op0 = op1 <= op2 /// public static readonly OpCode Le = new OpCode("le", Code.Le, OperandType.InlineCmpValue, FlowControl.Next, OpCodeType.Macro); /// /// op0 = op1 > op2 /// public static readonly OpCode Gt = new OpCode("gt", Code.Gt, OperandType.InlineCmpValue, FlowControl.Next, OpCodeType.Macro); /// /// op0 = op1 < op2 /// public static readonly OpCode Lt = new OpCode("lt", Code.Lt, OperandType.InlineCmpValue, FlowControl.Next, OpCodeType.Macro); /// /// op0 = op1 != op2 /// public static readonly OpCode Ne = new OpCode("ne", Code.Ne, OperandType.InlineCmpValue, FlowControl.Next, OpCodeType.Macro); /// /// op0 = op1 == op2 /// public static readonly OpCode Eq = new OpCode("eq", Code.Eq, OperandType.InlineCmpValue, FlowControl.Next, OpCodeType.Macro); /// /// If operand 2 is Variant[], checks if operand 1 when converted to COM VARIANT is in the array.
/// If operand 2 is a Set, checks if operand 1 is in the Set.
/// Invalid if operand 2 is any other type.
/// Result (0 or 1) is placed in operand 0 ///
public static readonly OpCode In = new OpCode("in", Code.In, OperandType.InlineCmpValue, FlowControl.Next, OpCodeType.Macro); /// /// Operand 1 must be a , operand 2 is a that must be a /// Checks if operand 1 is of the referenced by operand 2. /// Operand 2 can also be a variable of type containing the index of the type to compare against. /// public static readonly OpCode Is = new OpCode("is", Code.Is, OperandType.InlineCmpValueType, FlowControl.Next, OpCodeType.Macro); /// /// Calls the function indicated by the value operand (as a to the entry point). /// public static readonly OpCode CallVar = new OpCode("callvar", Code.CallVar, OperandType.InlineValue, FlowControl.Call, OpCodeType.Primitive, StackBehaviour.Push1); /// /// op0 must be a pointer, which may be a null pointer.
/// If op1 is not a pointer, loads the equivalent address of op1 into op0; op0 = &op1
/// If op1 is a pointer, loads op1 into op0; op0 = op1 ///
public static readonly OpCode SetPtr = new OpCode("setptr", Code.SetPtr, OperandType.InlineValueValue, FlowControl.Next, OpCodeType.Primitive); /// /// op0 = op0 == 0 /// public static readonly OpCode SetZ = new OpCode("setz", Code.SetZ, OperandType.InlineValue, FlowControl.Next, OpCodeType.Primitive); /// /// op0 = -op0 /// public static readonly OpCode Neg = new OpCode("neg", Code.Neg, OperandType.InlineValue, FlowControl.Next, OpCodeType.Primitive); /// /// jf = op0 != 0 /// public static readonly OpCode SetFlagNZ = new OpCode("sfnz", Code.SetFlagNZ, OperandType.InlineValueSF, FlowControl.Next, OpCodeType.Macro); /// /// jf = op0 == 0 /// public static readonly OpCode SetFlagZ = new OpCode("sfz", Code.SetFlagZ, OperandType.InlineValueSF, FlowControl.Next, OpCodeType.Macro); /// /// If the jump flag (set or unset by or ) is set, branch to the target /// public static readonly OpCode JumpF = new OpCode("jf", Code.JumpF, OperandType.InlineBrTarget, FlowControl.Next, OpCodeType.Primitive); /// /// Initialises an exception handler.
/// The operands are four target s which may be null.
/// op0 is the instruction that starts a Finally block. /// op1 is the instruction that starts a Catch block. /// op2 is the instruction that starts a Finally block after a Catch block. /// op3 is the instruction after the last exception handler block. ///
public static readonly OpCode StartEH = new OpCode("starteh", Code.StartEH, OperandType.InlineEH, FlowControl.Next, OpCodeType.Primitive); /// /// Leaves the current Try block and unconditionally branches to the end of the exception handler. /// public static readonly OpCode EndTry = new OpCode("endtry", Code.EndTry, OperandType.InlineNone, FlowControl.Next, OpCodeType.Macro); /// /// Leaves the current Finally block and unconditionally branches to the end of the exception handler. /// public static readonly OpCode EndFinally = new OpCode("endfinally", Code.EndFinally, OperandType.InlineNone, FlowControl.Next, OpCodeType.Macro); /// /// Leaves the current Catch block and unconditionally branches to the end of the exception handler. /// public static readonly OpCode EndCatch = new OpCode("endcatch", Code.EndCatch, OperandType.InlineNone, FlowControl.Next, OpCodeType.Macro); /// /// Leaves the current Finally block after a Catch block and unconditionally branches to the end of the exception handler. /// public static readonly OpCode EndCF = new OpCode("endcf", Code.EndCF, OperandType.InlineNone, FlowControl.Next, OpCodeType.Macro); /// /// op0 = ~op0 /// public static readonly OpCode Not = new OpCode("not", Code.Not, OperandType.InlineValue, FlowControl.Next, OpCodeType.Primitive); /// /// Copy constructor.
/// Operand 0 must be a pointer, which may be a null pointer.
/// First, op0 is set to newly constructed pointer of op1's type (if op1 is a pointer, then *op1's type).
/// If Operand 1 is a pointer, *op0 = *op1
/// Otherwise, *op0 = op1 ///
public static readonly OpCode Cpval = new OpCode("cpval", Code.Cpval, OperandType.InlineValueValue, FlowControl.Next, OpCodeType.Primitive); /// /// op0++ /// public static readonly OpCode Inc = new OpCode("inc", Code.Inc, OperandType.InlineValue, FlowControl.Next, OpCodeType.Primitive); /// /// op0-- /// public static readonly OpCode Dec = new OpCode("dec", Code.Dec, OperandType.InlineValue, FlowControl.Next, OpCodeType.Primitive); /// /// Pops one value off of the stack (with no type restriction) and branches to the target /// public static readonly OpCode PopJump = new OpCode("popjump", Code.PopJump, OperandType.InlineBrTarget, FlowControl.Branch, OpCodeType.Primitive, StackBehaviour.Push0, StackBehaviour.Pop1); /// /// Pops two values off of the stack (with no type restriction) and branches to the target /// public static readonly OpCode PopPopJump = new OpCode("poppopjump", Code.PopPopJump, OperandType.InlineBrTarget, FlowControl.Branch, OpCodeType.Primitive, StackBehaviour.Push0, StackBehaviour.Pop2); /// /// No operation /// public static readonly OpCode Nop = new OpCode("nop", Code.Nop, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive); #pragma warning restore static OpCodes() { FillTable(OneByteOpCodes, UNKNOWN1); FillTable(AluOpCodes, UNKNOWN_ALU); FillTable(CmpOpCodes, UNKNOWN_CMP); FillTable(PopEHOpCodes, UNKNOWN_POPEH); FillTable(SetFlagOpCodes, UNKNOWN_SF); } } }