コード例 #1
0
        public override void PostConditionalVisit(IfStat ifStat)
        {
            string elseIdentifier = $"else_{ifStat.Location.line}_{ifStat.Location.column}";

            this.Load(ifStat.Condition, "r1");
            InstructionStream.Add($"bz r1, {elseIdentifier}", $"If {ifStat.Condition}.");
        }
コード例 #2
0
 public override void Visit(FuncDef funcDef)
 {
     InstructionStream.Add(new string[] {
         "lw r15, 0(r14)",
         "jr r15"
     }, "Load the return address, and return.");
 }
コード例 #3
0
        public override void PostForLoopConditionalVisit(ForStat forStat)
        {
            string endForIdentifier = $"endfor_{forStat.Location.line}_{forStat.Location.column}";

            this.Load(forStat.Condition, "r1");
            InstructionStream.Add($"bz r1, {endForIdentifier}");
        }
コード例 #4
0
        /// <summary>
        /// Concat all il line codes as readable text
        /// </summary>
        /// <returns>A single string with \n literals included</returns>
        public new string ToString()
        {
            string result = string.Empty;

            InstructionStream.ForEach(I => result += I.ToString() + "\n");
            return(result);
        }
コード例 #5
0
ファイル: Automata.AutomataStack.cs プロジェクト: Egaros/lib
 public void Sync(InstructionStream instructionStream, int instructionPointer, Node node, AutomataStack stack)
 {
     InstructionStream  = instructionStream;
     InstructionPointer = instructionPointer;
     Node = node;
     Stack.Sync(stack);
 }
コード例 #6
0
ファイル: RegisterMachine.cs プロジェクト: mstanford/water
 public void jle(int position)
 {
     if (this._cmp <= 0)
     {
         InstructionStream.Jump(position);
     }
 }
コード例 #7
0
ファイル: RegisterMachine.cs プロジェクト: mstanford/water
 public void jg(int position)
 {
     if (this._cmp > 1)
     {
         InstructionStream.Jump(position);
     }
 }
コード例 #8
0
        public override void PreVisit(MainStatBlock mainStatBlock)
        {
            this.FunctionScope      = GlobalScope.Get("main", Classification.Function).Link;
            this.ClassInstanceScope = new SymbolTable();

            InstructionStream.Add("entry");
        }
コード例 #9
0
        public void SubVisit(FCall fCall)
        {
            int stackFrameSize = this.FunctionScope.GetStackFrameSize();
            var scope          = this.GlobalScope.Get(fCall.Id, Classification.Function)?.Link;

            if (scope == null)
            {
                string inheritedClass = fCall.Id.Split(':')[0];
                string functionName   = fCall.Id.Split(':').Last();

                scope = this.GlobalScope.Get(inheritedClass, Classification.Class)?.Link;
                var functionEntry = scope.Get(functionName, Classification.Function);

                string newLink = $"{functionEntry.OriginalFunctionOwner}::{functionName}";
                scope    = this.GlobalScope.Get(newLink, Classification.Function).Link;
                fCall.Id = newLink;
            }

            int paramOffset = stackFrameSize + 4 + scope.Get("retval", Classification.SubCalculationStackSpace).EntryMemorySize;

            foreach (var expression in fCall.Parameters.Expressions)
            {
                this.LoadAndStore(expression, paramOffset, expression.NodeMemorySize, $"Passing parameter {expression}");
                paramOffset += expression.NodeMemorySize;
            }

            InstructionStream.Add($"addi r14, r14, {stackFrameSize}");
            InstructionStream.Add($"jl r15, function_{fCall.Id.Replace(':', '_')}", $"Call the function {fCall.Id}");
            InstructionStream.Add($"subi r14, r14, {stackFrameSize}");


            this.LoadAndStore(stackFrameSize + 4, fCall, fCall.NodeMemorySize, "Retrieve the returnvalue");
        }
コード例 #10
0
ファイル: Automata.Process.Thread.cs プロジェクト: Egaros/lib
 public Thread(Node node, InstructionStream instructionStream, AutomataContextState contextState) : this()
 {
     CurrentNode        = node;
     InstructionPointer = InstructionStream.InitializeInstructionPointer();
     InstructionStream  = instructionStream.AddReference();
     ContextState       = contextState;
 }
コード例 #11
0
        /// <summary>
        /// Retrieve only call, calli, and callvirt instructions from the stream
        /// </summary>
        /// <returns></returns>
        public ArchList <Instruction> GetCallInstructions()
        {
            ArchList <Instruction> list = new ArchList <Instruction>();

            list.AddRange(InstructionStream.FindAll(I => I.Code.Name.Contains("call")));
            list.AddRange(StrayInstructions.FindAll(I => I.Code.Name.Contains("call")));
            return(list);
        }
コード例 #12
0
ファイル: Automata.AutomataStack.cs プロジェクト: Egaros/lib
            public void Eval(ExecutionPath executionPath)
            {
                var pointer = InstructionPointer;

                Node = Stack.Eval(executionPath);
                InstructionStream.Move(executionPath.LookAheadMatch.Length, ref pointer);
                InstructionPointer = pointer;
            }
コード例 #13
0
        public override void PreElseBlockVisit(IfStat ifStat)
        {
            string elseIdentifier  = $"else_{ifStat.Location.line}_{ifStat.Location.column}";
            string endifIdentifier = $"endif_{ifStat.Location.line}_{ifStat.Location.column}";

            InstructionStream.Add($"j {endifIdentifier}", "Go to the end of the else block.");
            InstructionStream.Add($"{elseIdentifier}    nop", "Start the else block");
        }
コード例 #14
0
 public override void Visit(Not not)
 {
     this.Load(not.Factor, "r1");
     InstructionStream.Add(new string[] {
         "not r1, r1",
         $"sw {not.stackOffset}(r14), r1"
     }, $"Invert the sign");
 }
コード例 #15
0
        public MoonVisitor(SymbolTable globalScope)
        {
            this.GlobalScope = globalScope;

            // Reset the instructions.
            InstructionStream.Instructions.Clear();
            InstructionStream.Add(File.ReadAllLines("puts.m").Where(line => line.Trim().Length != 0).ToArray(), "Link the puts library");
        }
コード例 #16
0
        public override void Visit(ReturnStat returnStat)
        {
            this.LoadAndStore(returnStat.ReturnValueExpression, 4, returnStat.ReturnValueExpression.NodeMemorySize, $"Returning {returnStat.ReturnValueExpression}");

            InstructionStream.Add(new string[] {
                "lw r15, 0(r14)",
                "jr r15"
            }, "Load the return address, and return.");
        }
コード例 #17
0
        public override void Visit(MainStatBlock mainStatBlock)
        {
            InstructionStream.Add("hlt");

            int programSize   = InstructionStream.Instructions.Count * 4;
            int indexToInsert = InstructionStream.Instructions.IndexOf("entry") + 1;

            InstructionStream.Instructions.Insert(indexToInsert, $"addi r14, r0, {programSize}  % Set the stack pointer");
        }
コード例 #18
0
ファイル: Automata.AutomataStack.cs プロジェクト: Egaros/lib
            public AutomataExecutionContext Mount(InstructionStream instructionStream, int instructionPointer, Node node, AutomataStack stack)
            {
                InstructionStream  = instructionStream.AddReference();
                InstructionPointer = instructionPointer;
                Node  = node;
                Stack = stack.AddReference();

                return(AddReference());
            }
コード例 #19
0
ファイル: Automata.AutomataStack.cs プロジェクト: Egaros/lib
 public void Release()
 {
     InstructionStream.ReleaseReference();
     Stack.ReleaseReference();
     Node = null;
     InstructionStream  = null;
     Stack              = null;
     InstructionPointer = 0;
 }
コード例 #20
0
ファイル: NPUSHB.cs プロジェクト: Vengarioth/XUI
        public void StreamTooShort()
        {
            var streamData = new byte[] { 0x2 };
            var stream = new InstructionStream(streamData);
            var stack = new InstructionStack();
            var flags = 0;

            Instructions.NPUSHB(flags, stream, stack);
        }
コード例 #21
0
        public override void PostInitializationVisit(ForStat forStat)
        {
            string startForIdentifier  = $"for_{forStat.Location.line}_{forStat.Location.column}";
            string conditionIdentifier = $"forcond_{forStat.Location.line}_{forStat.Location.column}";

            this.LoadAndStore(forStat.Initialization, forStat.stackOffset, forStat.NodeMemorySize, $"{forStat.Type} {forStat.Id} = {forStat.Initialization}");

            InstructionStream.Add($"j {conditionIdentifier}");
            InstructionStream.Add($"{startForIdentifier}    nop", "Start the for loop");
        }
コード例 #22
0
        public override void Visit(ForStat forStat)
        {
            string startForIdentifier = $"for_{forStat.Location.line}_{forStat.Location.column}";
            string endForIdentifier   = $"endfor_{forStat.Location.line}_{forStat.Location.column}";

            InstructionStream.Add(new string[] {
                $"j {startForIdentifier}",
                $"{endForIdentifier}    nop"
            });
        }
コード例 #23
0
                public ReaderProcess(TextSource textSource, Lexer <TGrammar, TToken> lexer, LexerContext <TToken> lexerContext) : base(textSource)
                {
                    _dfaInitialState    = lexer.Automata._dfaBuilder.InitialState;
                    _instructionStream  = new InstructionStream().AddReference().Mount(new TextInstructionReader(textSource.CreateReader()), lexer.Automata);
                    _instructionPointer = InstructionStream.InitializeInstructionPointer();

                    Context = new LexerAutomataContext();

                    Context.Mount(textSource, lexerContext);
                }
コード例 #24
0
ファイル: Automata.AutomataStack.cs プロジェクト: Egaros/lib
            public AutomataExecutionContext Clone()
            {
                var clone = Pool.Get().AddReference();

                clone.Stack              = Stack.Clone();
                clone.InstructionStream  = InstructionStream.AddReference();
                clone.InstructionPointer = InstructionPointer;
                clone.Node = Node;

                return(clone);
            }
コード例 #25
0
ファイル: PowerReduction.cs プロジェクト: AustinWise/CSC431
 public override IEnumerable<MilocInstruction> Div(DivInstruction s, InstructionStream<MilocInstruction> stream)
 {
     var d = constantValue(s, s.RegSource1);
     if (d.HasValue)
     {
         yield return new SraInstruction(s.RegSource0, d.Value, s.RegDest0);
     }
     else
     {
         yield return s;
     }
 }
コード例 #26
0
 void OnExplorerClauseChanged(Clause clause)
 {
     InstructionStream.Clear();
     if (clause == null)
     {
         return;
     }
     foreach (var item in clause.PrologInstructionStream)
     {
         InstructionStream.Add(item);
     }
 }
コード例 #27
0
        public override void PreVisit(FuncDef funcDef)
        {
            this.FunctionScope = this.GlobalScope.Get(funcDef.Entry.ID, Classification.Function).Link;

            this.ClassInstanceScope = new SymbolTable();
            if (funcDef.ScopeResolution != null)
            {
                this.ClassInstanceScope = this.GlobalScope.Get(funcDef.ScopeResolution.ID.Replace(':', '_'), Classification.Class).Link;
            }

            InstructionStream.Add($"function_{funcDef.Entry.ID.Replace(':', '_')} sw 0(r14), r15", $"Store the return address.");
        }
コード例 #28
0
        private void Load(Node node, params string[] registers)
        {
            for (int i = 0; i < registers.Length; i++)
            {
                string register = registers[i];
                InstructionStream.Add($"lw {register}, {node.stackOffset + i * 4}(r14)", $"Loading the value of {node}");

                if (!node.IsLiteral)
                {
                    InstructionStream.Add($"lw {register}, 0({register})", $"Pointer detected. Dereferencing {node}");
                }
            }
        }
コード例 #29
0
ファイル: Interpreter.cs プロジェクト: Vengarioth/XUI
        public void Interpret(byte[] instructions)
        {
            EnsureInstructionsLoaded();

            var stack = new InstructionStack();
            var stream = new InstructionStream(instructions);

            while (stream.Position < stream.Length)
            {
                var opCode = (int)stream.ReadByte();
                instructionMap[opCode].Execute(opCode, stream, stack);
            }
        }
コード例 #30
0
            /// <summary>
            /// Fetch and get 32-bit instruction
            /// </summary>
            /// <param name="instruction">Fetched instruction</param>
            void IRV32Fetch32B.Fetch32B()
            {
                uint pc_addr = reg_pc.Value & 0xFFFFFFFCU; // Simulate word-address memory address bus

                InstructionStream?.Seek(pc_addr, SeekOrigin.Begin);
                byte[] buffer = new byte[4];
                InstructionStream?.Read(buffer, 0, 4); // Simulate 32-bit memory data bus

                // Little endian
                if (!BitConverter.IsLittleEndian)
                {
                    Array.Reverse(buffer);
                }

                fetchRegister.Value = BitConverter.ToUInt32(buffer);
            }
コード例 #31
0
        public void PostVisit(Var var)
        {
            InstructionStream.Add($"addi r1, r14, 0", $"Calculating the REAL offset for {var}");

            foreach (var entry in var.Elements)
            {
                if (entry is DataMember member)
                {
                    InstructionStream.Add(new string[] {
                        $"lw r2, {member.stackOffset}(r14)",
                        $"add r1, r1, r2"
                    });
                }

                if (entry is FCall call)
                {
                    InstructionStream.Add(new string[] {
                        $"addi r1, r14, {call.stackOffset}"
                    }, "Get the function call's pointer");
                }
            }

            if (var.Elements.Last() as DataMember != null)
            {
                for (int i = 0; i < var.NodeMemorySize; i += 4)
                {
                    if (i != 0)
                    {
                        InstructionStream.Add($"addi r1, r1, 4");
                    }
                    InstructionStream.Add($"sw {var.stackOffset + i}(r14), r1");
                }
                var.IsLiteral = false;
            }
            else
            {
                for (int i = 0; i < var.NodeMemorySize; i += 4)
                {
                    InstructionStream.Add(new string[] {
                        $"lw r2, {var.Elements.Last().stackOffset + i}(r14)",
                        $"sw {var.stackOffset + i}(r14), r2"
                    });
                }
                var.IsLiteral = true;
            }
        }
コード例 #32
0
        public override void Visit(GetStat getStat)
        {
            InstructionStream.Add(new string[] {
                $"addi r5, r14, {getStat.Variable.stackOffset}",
                $"lw r5, 0(r5)",
                $"jl r15, geti_func"
            });

            if (getStat.Variable.SemanticalType == "float")
            {
                InstructionStream.Add(new string[] {
                    $"addi r5, r14, {getStat.Variable.stackOffset + 4}",
                    $"lw r5, 0(r5)",
                    $"jl r15, geti_func"
                });
            }
        }
コード例 #33
0
ファイル: Automata.Process.Thread.cs プロジェクト: Egaros/lib
                public Thread(ThreadParent parent, Node node, InstructionStream instructionStream, int instructionPointer, AutomataContextState contextState) : this()
                {
                    CurrentNode        = node;
                    InstructionPointer = instructionPointer;
                    ContextState       = contextState;
                    InstructionStream  = instructionStream.AddReference();

                    InstructionStream.LockPointer(InstructionPointer);

                    if (parent == null)
                    {
                        return;
                    }

                    Parent = parent;
                    Parent.AddReference();
                }
コード例 #34
0
ファイル: PowerReduction.cs プロジェクト: AustinWise/CSC431
 public override IEnumerable<MilocInstruction> Mult(MultInstruction s, InstructionStream<MilocInstruction> stream)
 {
     var d0 = constantValue(s, s.RegSource0);
     var d1 = constantValue(s, s.RegSource1);
     if (d0.HasValue == d1.HasValue)
     {
         yield return s;
         yield break;
     }
     if (d0.HasValue)
     {
         yield return new SllInstruction(s.RegSource1, d0.Value, s.RegDest0);
     }
     else
     {
         yield return new SllInstruction(s.RegSource0, d1.Value, s.RegDest0);
     }
 }
コード例 #35
0
ファイル: NPUSHB.cs プロジェクト: Vengarioth/XUI
        public void PushOntoStack()
        {
            var streamData = new byte[] { 0x0b, 0x07, 0x01, 0x03, 0x04, 0x00, 0x03, 0x05, 0x05, 0x09, 0x04, 0x00 };
            var stream = new InstructionStream(streamData);
            var stack = new InstructionStack();
            var flags = 0;

            Instructions.NPUSHB(flags, stream, stack);

            Assert.AreEqual(0x00, stack.PopInt32());
            Assert.AreEqual(0x04, stack.PopInt32());
            Assert.AreEqual(0x09, stack.PopInt32());
            Assert.AreEqual(0x05, stack.PopInt32());
            Assert.AreEqual(0x05, stack.PopInt32());
            Assert.AreEqual(0x03, stack.PopInt32());
            Assert.AreEqual(0x00, stack.PopInt32());
            Assert.AreEqual(0x04, stack.PopInt32());
            Assert.AreEqual(0x03, stack.PopInt32());
            Assert.AreEqual(0x01, stack.PopInt32());
            Assert.AreEqual(0x07, stack.PopInt32());
        }
コード例 #36
0
ファイル: Interpreter.cs プロジェクト: Mofangbao/CSharpGL
        static OpCode SkipNext(ref InstructionStream stream)
        {
            // grab the next opcode, and if it's one of the push instructions skip over its arguments
            var opcode = stream.NextOpCode();
            switch (opcode)
            {
                case OpCode.NPUSHB:
                case OpCode.PUSHB1:
                case OpCode.PUSHB2:
                case OpCode.PUSHB3:
                case OpCode.PUSHB4:
                case OpCode.PUSHB5:
                case OpCode.PUSHB6:
                case OpCode.PUSHB7:
                case OpCode.PUSHB8:
                    {
                        var count = opcode == OpCode.NPUSHB ? stream.NextByte() : opcode - OpCode.PUSHB1 + 1;
                        for (int i = 0; i < count; i++)
                            stream.NextByte();
                    }
                    break;
                case OpCode.NPUSHW:
                case OpCode.PUSHW1:
                case OpCode.PUSHW2:
                case OpCode.PUSHW3:
                case OpCode.PUSHW4:
                case OpCode.PUSHW5:
                case OpCode.PUSHW6:
                case OpCode.PUSHW7:
                case OpCode.PUSHW8:
                    {
                        var count = opcode == OpCode.NPUSHW ? stream.NextByte() : opcode - OpCode.PUSHW1 + 1;
                        for (int i = 0; i < count; i++)
                            stream.NextWord();
                    }
                    break;
            }

            return opcode;
        }
コード例 #37
0
ファイル: Interpreter.cs プロジェクト: Mofangbao/CSharpGL
        void Execute(InstructionStream stream, bool inFunction, bool allowFunctionDefs)
        {
            // dispatch each instruction in the stream
            while (!stream.Done)
            {
                var opcode = stream.NextOpCode();
                debugList.Add(opcode);
                switch (opcode)
                {
                    // ==== PUSH INSTRUCTIONS ====
                    case OpCode.NPUSHB:
                    case OpCode.PUSHB1:
                    case OpCode.PUSHB2:
                    case OpCode.PUSHB3:
                    case OpCode.PUSHB4:
                    case OpCode.PUSHB5:
                    case OpCode.PUSHB6:
                    case OpCode.PUSHB7:
                    case OpCode.PUSHB8:
                        {
                            var count = opcode == OpCode.NPUSHB ? stream.NextByte() : opcode - OpCode.PUSHB1 + 1;
                            for (int i = 0; i < count; i++)
                                stack.Push(stream.NextByte());
                        }
                        break;
                    case OpCode.NPUSHW:
                    case OpCode.PUSHW1:
                    case OpCode.PUSHW2:
                    case OpCode.PUSHW3:
                    case OpCode.PUSHW4:
                    case OpCode.PUSHW5:
                    case OpCode.PUSHW6:
                    case OpCode.PUSHW7:
                    case OpCode.PUSHW8:
                        {
                            var count = opcode == OpCode.NPUSHW ? stream.NextByte() : opcode - OpCode.PUSHW1 + 1;
                            for (int i = 0; i < count; i++)
                                stack.Push(stream.NextWord());
                        }
                        break;

                    // ==== STORAGE MANAGEMENT ====
                    case OpCode.RS:
                        {
                            var loc = CheckIndex(stack.Pop(), storage.Length);
                            stack.Push(storage[loc]);
                        }
                        break;
                    case OpCode.WS:
                        {
                            var value = stack.Pop();
                            var loc = CheckIndex(stack.Pop(), storage.Length);
                            storage[loc] = value;
                        }
                        break;

                    // ==== CONTROL VALUE TABLE ====
                    case OpCode.WCVTP:
                        {
                            var value = stack.PopFloat();
                            var loc = CheckIndex(stack.Pop(), controlValueTable.Length);
                            controlValueTable[loc] = value;
                        }
                        break;
                    case OpCode.WCVTF:
                        {
                            var value = stack.Pop();
                            var loc = CheckIndex(stack.Pop(), controlValueTable.Length);
                            controlValueTable[loc] = value * scale;
                        }
                        break;
                    case OpCode.RCVT: stack.Push(ReadCvt()); break;

                    // ==== STATE VECTORS ====
                    case OpCode.SVTCA0:
                    case OpCode.SVTCA1:
                        {
                            var axis = opcode - OpCode.SVTCA0;
                            SetFreedomVectorToAxis(axis);
                            SetProjectionVectorToAxis(axis);
                        }
                        break;
                    case OpCode.SFVTPV: state.Freedom = state.Projection; OnVectorsUpdated(); break;
                    case OpCode.SPVTCA0:
                    case OpCode.SPVTCA1: SetProjectionVectorToAxis(opcode - OpCode.SPVTCA0); break;
                    case OpCode.SFVTCA0:
                    case OpCode.SFVTCA1: SetFreedomVectorToAxis(opcode - OpCode.SFVTCA0); break;
                    case OpCode.SPVTL0:
                    case OpCode.SPVTL1:
                    case OpCode.SFVTL0:
                    case OpCode.SFVTL1: SetVectorToLine(opcode - OpCode.SPVTL0, false); break;
                    case OpCode.SDPVTL0:
                    case OpCode.SDPVTL1: SetVectorToLine(opcode - OpCode.SDPVTL0, true); break;
                    case OpCode.SPVFS:
                    case OpCode.SFVFS:
                        {
                            var y = stack.Pop();
                            var x = stack.Pop();
                            var vec = Vector2.Normalize(new Vector2(F2Dot14ToFloat(x), F2Dot14ToFloat(y)));
                            if (opcode == OpCode.SFVFS)
                                state.Freedom = vec;
                            else
                            {
                                state.Projection = vec;
                                state.DualProjection = vec;
                            }
                            OnVectorsUpdated();
                        }
                        break;
                    case OpCode.GPV:
                    case OpCode.GFV:
                        {
                            var vec = opcode == OpCode.GPV ? state.Projection : state.Freedom;
                            stack.Push(FloatToF2Dot14(vec.X));
                            stack.Push(FloatToF2Dot14(vec.Y));
                        }
                        break;

                    // ==== GRAPHICS STATE ====
                    case OpCode.SRP0: state.Rp0 = stack.Pop(); break;
                    case OpCode.SRP1: state.Rp1 = stack.Pop(); break;
                    case OpCode.SRP2: state.Rp2 = stack.Pop(); break;
                    case OpCode.SZP0: zp0 = GetZoneFromStack(); break;
                    case OpCode.SZP1: zp1 = GetZoneFromStack(); break;
                    case OpCode.SZP2: zp2 = GetZoneFromStack(); break;
                    case OpCode.SZPS: zp0 = zp1 = zp2 = GetZoneFromStack(); break;
                    case OpCode.RTHG: state.RoundState = RoundMode.ToHalfGrid; break;
                    case OpCode.RTG: state.RoundState = RoundMode.ToGrid; break;
                    case OpCode.RTDG: state.RoundState = RoundMode.ToDoubleGrid; break;
                    case OpCode.RDTG: state.RoundState = RoundMode.DownToGrid; break;
                    case OpCode.RUTG: state.RoundState = RoundMode.UpToGrid; break;
                    case OpCode.ROFF: state.RoundState = RoundMode.Off; break;
                    case OpCode.SROUND: state.RoundState = RoundMode.Super; SetSuperRound(1.0f); break;
                    case OpCode.S45ROUND: state.RoundState = RoundMode.Super45; SetSuperRound(Sqrt2Over2); break;
                    case OpCode.INSTCTRL:
                        {
                            var selector = stack.Pop();
                            if (selector >= 1 && selector <= 2)
                            {
                                // value is false if zero, otherwise shift the right bit into the flags
                                var bit = 1 << (selector - 1);
                                if (stack.Pop() == 0)
                                    state.InstructionControl = (InstructionControlFlags)((int)state.InstructionControl & ~bit);
                                else
                                    state.InstructionControl = (InstructionControlFlags)((int)state.InstructionControl | bit);
                            }
                        }
                        break;
                    case OpCode.SCANCTRL: /* instruction unspported */ stack.Pop(); break;
                    case OpCode.SCANTYPE: /* instruction unspported */ stack.Pop(); break;
                    case OpCode.SANGW: /* instruction unspported */ stack.Pop(); break;
                    case OpCode.SLOOP: state.Loop = stack.Pop(); break;
                    case OpCode.SMD: state.MinDistance = stack.PopFloat(); break;
                    case OpCode.SCVTCI: state.ControlValueCutIn = stack.PopFloat(); break;
                    case OpCode.SSWCI: state.SingleWidthCutIn = stack.PopFloat(); break;
                    case OpCode.SSW: state.SingleWidthValue = stack.Pop() * scale; break;
                    case OpCode.FLIPON: state.AutoFlip = true; break;
                    case OpCode.FLIPOFF: state.AutoFlip = false; break;
                    case OpCode.SDB: state.DeltaBase = stack.Pop(); break;
                    case OpCode.SDS: state.DeltaShift = stack.Pop(); break;

                    // ==== POINT MEASUREMENT ====
                    case OpCode.GC0: stack.Push(Project(zp2.GetCurrent(stack.Pop()))); break;
                    case OpCode.GC1: stack.Push(DualProject(zp2.GetOriginal(stack.Pop()))); break;
                    case OpCode.SCFS:
                        {
                            var value = stack.PopFloat();
                            var index = stack.Pop();
                            var point = zp2.GetCurrent(index);
                            MovePoint(zp2, index, value - Project(point));

                            // moving twilight points moves their "original" value also
                            if (zp2.IsTwilight)
                                zp2.Original[index].P = zp2.Current[index].P;
                        }
                        break;
                    case OpCode.MD0:
                        {
                            var p1 = zp1.GetOriginal(stack.Pop());
                            var p2 = zp0.GetOriginal(stack.Pop());
                            stack.Push(DualProject(p2 - p1));
                        }
                        break;
                    case OpCode.MD1:
                        {
                            var p1 = zp1.GetCurrent(stack.Pop());
                            var p2 = zp0.GetCurrent(stack.Pop());
                            stack.Push(Project(p2 - p1));
                        }
                        break;
                    case OpCode.MPS: // MPS should return point size, but we assume DPI so it's the same as pixel size
                    case OpCode.MPPEM: stack.Push(ppem); break;
                    case OpCode.AA: /* deprecated instruction */ stack.Pop(); break;

                    // ==== POINT MODIFICATION ====
                    case OpCode.FLIPPT:
                        {
                            for (int i = 0; i < state.Loop; i++)
                            {
                                var index = stack.Pop();
                                if (points.Current[index].Type == PointType.OnCurve)
                                    points.Current[index].Type = PointType.Quadratic;
                                else
                                    points.Current[index].Type = PointType.OnCurve;
                            }
                            state.Loop = 1;
                        }
                        break;
                    case OpCode.FLIPRGON:
                        {
                            var end = stack.Pop();
                            for (int i = stack.Pop(); i <= end; i++)
                                points.Current[i].Type = PointType.OnCurve;
                        }
                        break;
                    case OpCode.FLIPRGOFF:
                        {
                            var end = stack.Pop();
                            for (int i = stack.Pop(); i <= end; i++)
                                points.Current[i].Type = PointType.Quadratic;
                        }
                        break;
                    case OpCode.SHP0:
                    case OpCode.SHP1:
                        {
                            Zone zone;
                            int point;
                            var displacement = ComputeDisplacement((int)opcode, out zone, out point);
                            ShiftPoints(displacement);
                        }
                        break;
                    case OpCode.SHPIX: ShiftPoints(stack.PopFloat() * state.Freedom); break;
                    case OpCode.SHC0:
                    case OpCode.SHC1:
                        {
                            Zone zone;
                            int point;
                            var displacement = ComputeDisplacement((int)opcode, out zone, out point);
                            var touch = GetTouchState();
                            var contour = stack.Pop();
                            var start = contour == 0 ? 0 : contours[contour - 1] + 1;
                            var count = zp2.IsTwilight ? zp2.Current.Length : contours[contour] + 1;

                            for (int i = start; i < count; i++)
                            {
                                // don't move the reference point
                                if (zone.Current != zp2.Current || point != i)
                                {
                                    zp2.Current[i].P += displacement;
                                    zp2.TouchState[i] |= touch;
                                }
                            }
                        }
                        break;
                    case OpCode.SHZ0:
                    case OpCode.SHZ1:
                        {
                            Zone zone;
                            int point;
                            var displacement = ComputeDisplacement((int)opcode, out zone, out point);
                            var count = 0;
                            if (zp2.IsTwilight)
                                count = zp2.Current.Length;
                            else if (contours.Length > 0)
                                count = contours[contours.Length - 1] + 1;

                            for (int i = 0; i < count; i++)
                            {
                                // don't move the reference point
                                if (zone.Current != zp2.Current || point != i)
                                    zp2.Current[i].P += displacement;
                            }
                        }
                        break;
                    case OpCode.MIAP0:
                    case OpCode.MIAP1:
                        {
                            var distance = ReadCvt();
                            var pointIndex = stack.Pop();

                            // this instruction is used in the CVT to set up twilight points with original values
                            if (zp0.IsTwilight)
                            {
                                var original = state.Freedom * distance;
                                zp0.Original[pointIndex].P = original;
                                zp0.Current[pointIndex].P = original;
                            }

                            // current position of the point along the projection vector
                            var point = zp0.GetCurrent(pointIndex);
                            var currentPos = Project(point);
                            if (opcode == OpCode.MIAP1)
                            {
                                // only use the CVT if we are above the cut-in point
                                if (Math.Abs(distance - currentPos) > state.ControlValueCutIn)
                                    distance = currentPos;
                                distance = Round(distance);
                            }

                            MovePoint(zp0, pointIndex, distance - currentPos);
                            state.Rp0 = pointIndex;
                            state.Rp1 = pointIndex;
                        }
                        break;
                    case OpCode.MDAP0:
                    case OpCode.MDAP1:
                        {
                            var pointIndex = stack.Pop();
                            var point = zp0.GetCurrent(pointIndex);
                            var distance = 0.0f;
                            if (opcode == OpCode.MDAP1)
                            {
                                distance = Project(point);
                                distance = Round(distance) - distance;
                            }

                            MovePoint(zp0, pointIndex, distance);
                            state.Rp0 = pointIndex;
                            state.Rp1 = pointIndex;
                        }
                        break;
                    case OpCode.MSIRP0:
                    case OpCode.MSIRP1:
                        {
                            var targetDistance = stack.PopFloat();
                            var pointIndex = stack.Pop();

                            // if we're operating on the twilight zone, initialize the points
                            if (zp1.IsTwilight)
                            {
                                zp1.Original[pointIndex].P = zp0.Original[state.Rp0].P + targetDistance * state.Freedom / fdotp;
                                zp1.Current[pointIndex].P = zp1.Original[pointIndex].P;
                            }

                            var currentDistance = Project(zp1.GetCurrent(pointIndex) - zp0.GetCurrent(state.Rp0));
                            MovePoint(zp1, pointIndex, targetDistance - currentDistance);

                            state.Rp1 = state.Rp0;
                            state.Rp2 = pointIndex;
                            if (opcode == OpCode.MSIRP1)
                                state.Rp0 = pointIndex;
                        }
                        break;
                    case OpCode.IP:
                        {
                            var originalBase = zp0.GetOriginal(state.Rp1);
                            var currentBase = zp0.GetCurrent(state.Rp1);
                            var originalRange = DualProject(zp1.GetOriginal(state.Rp2) - originalBase);
                            var currentRange = Project(zp1.GetCurrent(state.Rp2) - currentBase);

                            for (int i = 0; i < state.Loop; i++)
                            {
                                var pointIndex = stack.Pop();
                                var point = zp2.GetCurrent(pointIndex);
                                var currentDistance = Project(point - currentBase);
                                var originalDistance = DualProject(zp2.GetOriginal(pointIndex) - originalBase);

                                var newDistance = 0.0f;
                                if (originalDistance != 0.0f)
                                {
                                    // a range of 0.0f is invalid according to the spec (would result in a div by zero)
                                    if (originalRange == 0.0f)
                                        newDistance = originalDistance;
                                    else
                                        newDistance = originalDistance * currentRange / originalRange;
                                }

                                MovePoint(zp2, pointIndex, newDistance - currentDistance);
                            }
                            state.Loop = 1;
                        }
                        break;
                    case OpCode.ALIGNRP:
                        {
                            for (int i = 0; i < state.Loop; i++)
                            {
                                var pointIndex = stack.Pop();
                                var p1 = zp1.GetCurrent(pointIndex);
                                var p2 = zp0.GetCurrent(state.Rp0);
                                MovePoint(zp1, pointIndex, -Project(p1 - p2));
                            }
                            state.Loop = 1;
                        }
                        break;
                    case OpCode.ALIGNPTS:
                        {
                            var p1 = stack.Pop();
                            var p2 = stack.Pop();
                            var distance = Project(zp0.GetCurrent(p2) - zp1.GetCurrent(p1)) / 2;
                            MovePoint(zp1, p1, distance);
                            MovePoint(zp0, p2, -distance);
                        }
                        break;
                    case OpCode.UTP: zp0.TouchState[stack.Pop()] &= ~GetTouchState(); break;
                    case OpCode.IUP0:
                    case OpCode.IUP1:
                        unsafe
                        {
                            // bail if no contours (empty outline)
                            if (contours.Length == 0)
                                break;

                            fixed (PointF* currentPtr = points.Current) fixed (PointF* originalPtr = points.Original)
                            {
                                // opcode controls whether we care about X or Y direction
                                // do some pointer trickery so we can operate on the
                                // points in a direction-agnostic manner
                                TouchState touchMask;
                                byte* current;
                                byte* original;
                                if (opcode == OpCode.IUP0)
                                {
                                    touchMask = TouchState.Y;
                                    current = (byte*)&currentPtr->P.Y;
                                    original = (byte*)&originalPtr->P.Y;
                                }
                                else
                                {
                                    touchMask = TouchState.X;
                                    current = (byte*)&currentPtr->P.X;
                                    original = (byte*)&originalPtr->P.X;
                                }

                                var point = 0;
                                for (int i = 0; i < contours.Length; i++)
                                {
                                    var endPoint = contours[i];
                                    var firstPoint = point;
                                    var firstTouched = -1;
                                    var lastTouched = -1;

                                    for (; point <= endPoint; point++)
                                    {
                                        // check whether this point has been touched
                                        if ((points.TouchState[point] & touchMask) != 0)
                                        {
                                            // if this is the first touched point in the contour, note it and continue
                                            if (firstTouched < 0)
                                            {
                                                firstTouched = point;
                                                lastTouched = point;
                                                continue;
                                            }

                                            // otherwise, interpolate all untouched points
                                            // between this point and our last touched point
                                            InterpolatePoints(current, original, lastTouched + 1, point - 1, lastTouched, point);
                                            lastTouched = point;
                                        }
                                    }

                                    // check if we had any touched points at all in this contour
                                    if (firstTouched >= 0)
                                    {
                                        // there are two cases left to handle:
                                        // 1. there was only one touched point in the whole contour, in
                                        //    which case we want to shift everything relative to that one
                                        // 2. several touched points, in which case handle the gap from the
                                        //    beginning to the first touched point and the gap from the last
                                        //    touched point to the end of the contour
                                        if (lastTouched == firstTouched)
                                        {
                                            var delta = *GetPoint(current, lastTouched) - *GetPoint(original, lastTouched);
                                            if (delta != 0.0f)
                                            {
                                                for (int j = firstPoint; j < lastTouched; j++)
                                                    *GetPoint(current, j) += delta;
                                                for (int j = lastTouched + 1; j <= endPoint; j++)
                                                    *GetPoint(current, j) += delta;
                                            }
                                        }
                                        else
                                        {
                                            InterpolatePoints(current, original, lastTouched + 1, endPoint, lastTouched, firstTouched);
                                            if (firstTouched > 0)
                                                InterpolatePoints(current, original, firstPoint, firstTouched - 1, lastTouched, firstTouched);
                                        }
                                    }
                                }
                            }
                        }
                        break;
                    case OpCode.ISECT:
                        {
                            // move point P to the intersection of lines A and B
                            var b1 = zp0.GetCurrent(stack.Pop());
                            var b0 = zp0.GetCurrent(stack.Pop());
                            var a1 = zp1.GetCurrent(stack.Pop());
                            var a0 = zp1.GetCurrent(stack.Pop());
                            var index = stack.Pop();

                            // calculate intersection using determinants: https://en.wikipedia.org/wiki/Line%E2%80%93line_intersection#Given_two_points_on_each_line
                            var da = a0 - a1;
                            var db = b0 - b1;
                            var den = (da.X * db.Y) - (da.Y * db.X);
                            if (Math.Abs(den) <= Epsilon)
                            {
                                // parallel lines; spec says to put the ppoint "into the middle of the two lines"
                                zp2.Current[index].P = (a0 + a1 + b0 + b1) / 4;
                            }
                            else
                            {
                                var t = (a0.X * a1.Y) - (a0.Y * a1.X);
                                var u = (b0.X * b1.Y) - (b0.Y * b1.X);
                                var p = new Vector2(
                                    (t * db.X) - (da.X * u),
                                    (t * db.Y) - (da.Y * u)
                                );
                                zp2.Current[index].P = p / den;
                            }
                            zp2.TouchState[index] = TouchState.Both;
                        }
                        break;

                    // ==== STACK MANAGEMENT ====
                    case OpCode.DUP: stack.Duplicate(); break;
                    case OpCode.POP: stack.Pop(); break;
                    case OpCode.CLEAR: stack.Clear(); break;
                    case OpCode.SWAP: stack.Swap(); break;
                    case OpCode.DEPTH: stack.Depth(); break;
                    case OpCode.CINDEX: stack.Copy(); break;
                    case OpCode.MINDEX: stack.Move(); break;
                    case OpCode.ROLL: stack.Roll(); break;

                    // ==== FLOW CONTROL ====
                    case OpCode.IF:
                        {
                            // value is false; jump to the next else block or endif marker
                            // otherwise, we don't have to do anything; we'll keep executing this block
                            if (!stack.PopBool())
                            {
                                int indent = 1;
                                while (indent > 0)
                                {
                                    opcode = SkipNext(ref stream);
                                    switch (opcode)
                                    {
                                        case OpCode.IF: indent++; break;
                                        case OpCode.EIF: indent--; break;
                                        case OpCode.ELSE:
                                            if (indent == 1)
                                                indent = 0;
                                            break;
                                    }
                                }
                            }
                        }
                        break;
                    case OpCode.ELSE:
                        {
                            // assume we hit the true statement of some previous if block
                            // if we had hit false, we would have jumped over this
                            int indent = 1;
                            while (indent > 0)
                            {
                                opcode = SkipNext(ref stream);
                                switch (opcode)
                                {
                                    case OpCode.IF: indent++; break;
                                    case OpCode.EIF: indent--; break;
                                }
                            }
                        }
                        break;
                    case OpCode.EIF: /* nothing to do */ break;
                    case OpCode.JROT:
                    case OpCode.JROF:
                        {
                            if (stack.PopBool() == (opcode == OpCode.JROT))
                                stream.Jump(stack.Pop() - 1);
                            else
                                stack.Pop();    // ignore the offset
                        }
                        break;
                    case OpCode.JMPR: stream.Jump(stack.Pop() - 1); break;

                    // ==== LOGICAL OPS ====
                    case OpCode.LT:
                        {
                            var b = stack.Pop();
                            var a = stack.Pop();
                            stack.Push(a < b);
                        }
                        break;
                    case OpCode.LTEQ:
                        {
                            var b = stack.Pop();
                            var a = stack.Pop();
                            stack.Push(a <= b);
                        }
                        break;
                    case OpCode.GT:
                        {
                            var b = stack.Pop();
                            var a = stack.Pop();
                            stack.Push(a > b);
                        }
                        break;
                    case OpCode.GTEQ:
                        {
                            var b = stack.Pop();
                            var a = stack.Pop();
                            stack.Push(a >= b);
                        }
                        break;
                    case OpCode.EQ:
                        {
                            var b = stack.Pop();
                            var a = stack.Pop();
                            stack.Push(a == b);
                        }
                        break;
                    case OpCode.NEQ:
                        {
                            var b = stack.Pop();
                            var a = stack.Pop();
                            stack.Push(a != b);
                        }
                        break;
                    case OpCode.AND:
                        {
                            var b = stack.PopBool();
                            var a = stack.PopBool();
                            stack.Push(a && b);
                        }
                        break;
                    case OpCode.OR:
                        {
                            var b = stack.PopBool();
                            var a = stack.PopBool();
                            stack.Push(a || b);
                        }
                        break;
                    case OpCode.NOT: stack.Push(!stack.PopBool()); break;
                    case OpCode.ODD:
                        {
                            var value = (int)Round(stack.PopFloat());
                            stack.Push(value % 2 != 0);
                        }
                        break;
                    case OpCode.EVEN:
                        {
                            var value = (int)Round(stack.PopFloat());
                            stack.Push(value % 2 == 0);
                        }
                        break;

                    // ==== ARITHMETIC ====
                    case OpCode.ADD:
                        {
                            var b = stack.Pop();
                            var a = stack.Pop();
                            stack.Push(a + b);
                        }
                        break;
                    case OpCode.SUB:
                        {
                            var b = stack.Pop();
                            var a = stack.Pop();
                            stack.Push(a - b);
                        }
                        break;
                    case OpCode.DIV:
                        {
                            var b = stack.Pop();
                            if (b == 0)
                                throw new InvalidFontException("Division by zero.");

                            var a = stack.Pop();
                            var result = ((long)a << 6) / b;
                            stack.Push((int)result);
                        }
                        break;
                    case OpCode.MUL:
                        {
                            var b = stack.Pop();
                            var a = stack.Pop();
                            var result = ((long)a * b) >> 6;
                            stack.Push((int)result);
                        }
                        break;
                    case OpCode.ABS: stack.Push(Math.Abs(stack.Pop())); break;
                    case OpCode.NEG: stack.Push(-stack.Pop()); break;
                    case OpCode.FLOOR: stack.Push(stack.Pop() & ~63); break;
                    case OpCode.CEILING: stack.Push((stack.Pop() + 63) & ~63); break;
                    case OpCode.MAX: stack.Push(Math.Max(stack.Pop(), stack.Pop())); break;
                    case OpCode.MIN: stack.Push(Math.Min(stack.Pop(), stack.Pop())); break;

                    // ==== FUNCTIONS ====
                    case OpCode.FDEF:
                        {
                            if (!allowFunctionDefs || inFunction)
                                throw new InvalidFontException("Can't define functions here.");

                            functions[stack.Pop()] = stream;
                            while (SkipNext(ref stream) != OpCode.ENDF) ;
                        }
                        break;
                    case OpCode.IDEF:
                        {
                            if (!allowFunctionDefs || inFunction)
                                throw new InvalidFontException("Can't define functions here.");

                            instructionDefs[stack.Pop()] = stream;
                            while (SkipNext(ref stream) != OpCode.ENDF) ;
                        }
                        break;
                    case OpCode.ENDF:
                        {
                            if (!inFunction)
                                throw new InvalidFontException("Found invalid ENDF marker outside of a function definition.");
                            return;
                        }
                    case OpCode.CALL:
                    case OpCode.LOOPCALL:
                        {
                            callStackSize++;
                            if (callStackSize > MaxCallStack)
                                throw new InvalidFontException("Stack overflow; infinite recursion?");

                            var function = functions[stack.Pop()];
                            var count = opcode == OpCode.LOOPCALL ? stack.Pop() : 1;
                            for (int i = 0; i < count; i++)
                                Execute(function, true, false);
                            callStackSize--;
                        }
                        break;

                    // ==== ROUNDING ====
                    // we don't have "engine compensation" so the variants are unnecessary
                    case OpCode.ROUND0:
                    case OpCode.ROUND1:
                    case OpCode.ROUND2:
                    case OpCode.ROUND3: stack.Push(Round(stack.PopFloat())); break;
                    case OpCode.NROUND0:
                    case OpCode.NROUND1:
                    case OpCode.NROUND2:
                    case OpCode.NROUND3: break;

                    // ==== DELTA EXCEPTIONS ====
                    case OpCode.DELTAC1:
                    case OpCode.DELTAC2:
                    case OpCode.DELTAC3:
                        {
                            var last = stack.Pop();
                            for (int i = 1; i <= last; i++)
                            {
                                var cvtIndex = stack.Pop();
                                var arg = stack.Pop();

                                // upper 4 bits of the 8-bit arg is the relative ppem
                                // the opcode specifies the base to add to the ppem
                                var triggerPpem = (arg >> 4) & 0xF;
                                triggerPpem += (opcode - OpCode.DELTAC1) * 16;
                                triggerPpem += state.DeltaBase;

                                // if the current ppem matches the trigger, apply the exception
                                if (ppem == triggerPpem)
                                {
                                    // the lower 4 bits of the arg is the amount to shift
                                    // it's encoded such that 0 isn't an allowable value (who wants to shift by 0 anyway?)
                                    var amount = (arg & 0xF) - 8;
                                    if (amount >= 0)
                                        amount++;
                                    amount *= 1 << (6 - state.DeltaShift);

                                    // update the CVT
                                    CheckIndex(cvtIndex, controlValueTable.Length);
                                    controlValueTable[cvtIndex] += F26Dot6ToFloat(amount);
                                }
                            }
                        }
                        break;
                    case OpCode.DELTAP1:
                    case OpCode.DELTAP2:
                    case OpCode.DELTAP3:
                        {
                            var last = stack.Pop();
                            for (int i = 1; i <= last; i++)
                            {
                                var pointIndex = stack.Pop();
                                var arg = stack.Pop();

                                // upper 4 bits of the 8-bit arg is the relative ppem
                                // the opcode specifies the base to add to the ppem
                                var triggerPpem = (arg >> 4) & 0xF;
                                triggerPpem += state.DeltaBase;
                                if (opcode != OpCode.DELTAP1)
                                    triggerPpem += (opcode - OpCode.DELTAP2 + 1) * 16;

                                // if the current ppem matches the trigger, apply the exception
                                if (ppem == triggerPpem)
                                {
                                    // the lower 4 bits of the arg is the amount to shift
                                    // it's encoded such that 0 isn't an allowable value (who wants to shift by 0 anyway?)
                                    var amount = (arg & 0xF) - 8;
                                    if (amount >= 0)
                                        amount++;
                                    amount *= 1 << (6 - state.DeltaShift);

                                    MovePoint(zp0, pointIndex, F26Dot6ToFloat(amount));
                                }
                            }
                        }
                        break;

                    // ==== MISCELLANEOUS ====
                    case OpCode.DEBUG: stack.Pop(); break;
                    case OpCode.GETINFO:
                        {
                            var selector = stack.Pop();
                            var result = 0;
                            if ((selector & 0x1) != 0)
                            {
                                // pretend we are MS Rasterizer v35
                                result = 35;
                            }

                            // TODO: rotation and stretching
                            //if ((selector & 0x2) != 0)
                            //if ((selector & 0x4) != 0)

                            // we're always rendering in grayscale
                            if ((selector & 0x20) != 0)
                                result |= 1 << 12;

                            // TODO: ClearType flags

                            stack.Push(result);
                        }
                        break;

                    default:
                        if (opcode >= OpCode.MIRP)
                            MoveIndirectRelative(opcode - OpCode.MIRP);
                        else if (opcode >= OpCode.MDRP)
                            MoveDirectRelative(opcode - OpCode.MDRP);
                        else
                        {
                            // check if this is a runtime-defined opcode
                            var index = (int)opcode;
                            if (index > instructionDefs.Length || !instructionDefs[index].IsValid)
                                throw new InvalidFontException("Unknown opcode in font program.");

                            callStackSize++;
                            if (callStackSize > MaxCallStack)
                                throw new InvalidFontException("Stack overflow; infinite recursion?");

                            Execute(instructionDefs[index], true, false);
                            callStackSize--;
                        }
                        break;
                }
            }
        }