omy.Utils.VirtualMachine 2.0.0-rc.1

This is a prerelease version of omy.Utils.VirtualMachine.
dotnet add package omy.Utils.VirtualMachine --version 2.0.0-rc.1
                    
NuGet\Install-Package omy.Utils.VirtualMachine -Version 2.0.0-rc.1
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="omy.Utils.VirtualMachine" Version="2.0.0-rc.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="omy.Utils.VirtualMachine" Version="2.0.0-rc.1" />
                    
Directory.Packages.props
<PackageReference Include="omy.Utils.VirtualMachine" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add omy.Utils.VirtualMachine --version 2.0.0-rc.1
                    
#r "nuget: omy.Utils.VirtualMachine, 2.0.0-rc.1"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package omy.Utils.VirtualMachine@2.0.0-rc.1
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=omy.Utils.VirtualMachine&version=2.0.0-rc.1&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=omy.Utils.VirtualMachine&version=2.0.0-rc.1&prerelease
                    
Install as a Cake Tool

omy.Utils.VirtualMachine (byte-code interpreter)

omy.Utils.VirtualMachine implements a small, attribute-driven byte-code interpreter for custom instruction sets.

Install

dotnet add package omy.Utils.VirtualMachine

Supported frameworks

  • net8.0

Features

  • VirtualProcessor<T> — abstract base class; derive and annotate methods with [Instruction] to register opcodes.
  • [Instruction(name, params byte[] opcode)] — maps a method to a single- or multi-byte opcode.
  • Context — abstract execution context carrying the byte-code array and instruction pointer.
  • DefaultContext — concrete context with a Stack<object> for stack-based machines.
  • Supports little-endian (default) and big-endian operand reading.

Quick usage

using Utils.VirtualMachine;

class StackMachine : VirtualProcessor<DefaultContext>
{
    [Instruction("PUSH", 0x01)]
    void Push(DefaultContext ctx, byte value) => ctx.Stack.Push(value);

    [Instruction("ADD", 0x10)]
    void Add(DefaultContext ctx)
    {
        byte b = (byte)ctx.Stack.Pop();
        byte a = (byte)ctx.Stack.Pop();
        ctx.Stack.Push((byte)(a + b));
    }
}

byte[] program = [0x01, 3, 0x01, 4, 0x10]; // PUSH 3, PUSH 4, ADD
var machine = new StackMachine();
var context = new DefaultContext(program);
machine.Execute(context);

Console.WriteLine((byte)context.Stack.Peek()); // 7

Multi-byte opcodes

When different instruction variants share a common prefix, use a multi-byte opcode to disambiguate:

using Utils.VirtualMachine;

class ExtendedMachine : VirtualProcessor<DefaultContext>
{
    [Instruction("PUSH_INT", 0x01, 0x01)]
    void PushInt(DefaultContext ctx, int value) => ctx.Stack.Push(value);

    [Instruction("PUSH_BYTE", 0x01, 0x02)]
    void PushByte(DefaultContext ctx, byte value) => ctx.Stack.Push(value);
}

Custom context

Derive from Context to add registers, memory, or other execution state:

using Utils.VirtualMachine;

class CpuContext : Context
{
    public CpuContext(byte[] program) : base(program) { }

    public int[] Registers { get; } = new int[8];
}

class Cpu : VirtualProcessor<CpuContext>
{
    [Instruction("LOAD", 0x20)]
    void Load(CpuContext ctx, byte reg, int value)
        => ctx.Registers[reg] = value;

    [Instruction("ADD_REG", 0x21)]
    void AddReg(CpuContext ctx, byte dest, byte src)
        => ctx.Registers[dest] += ctx.Registers[src];
}

Big-endian mode

Pass littleEndian: false to interpret multi-byte operands as big-endian:

using Utils.VirtualMachine;

class BigEndianMachine : VirtualProcessor<DefaultContext>
{
    protected BigEndianMachine() : base(littleEndian: false) { }

    [Instruction("PUSH16", 0x03)]
    void Push16(DefaultContext ctx, short value) => ctx.Stack.Push(value);
}

Cooperative scheduler

Scheduler<T> runs multiple processes concurrently using cooperative, priority-based time-slicing. Each Step() call advances all ready processes in descending priority order, giving each one up to quantumSteps instructions before moving to the next.

using Utils.VirtualMachine;

var scheduler = new Scheduler<DefaultContext>(quantumSteps: 10);

// Add processes; optional name aids diagnostics.
var p1 = scheduler.AddProcess(ctx1, machine1, priority: 1, name: "worker-A");
var p2 = scheduler.AddProcess(ctx2, machine2, priority: 2, name: "worker-B");

// Blocking run (executes on the calling thread).
scheduler.Run();

// Non-blocking async variant — yields between each quantum.
await scheduler.RunAsync(cancellationToken);

Console.WriteLine(p1.Name);      // "worker-A"
Console.WriteLine(p1.State);     // ProcessState.Terminated

Structured control flow (CallStack / ControlFlow)

ControlFlowStack tracks nested conditionals, loops, and try/catch/finally blocks at runtime. ControlFlowContext bundles a DefaultContext with a ControlFlowStack; FullContext also adds a call stack.

using Utils.VirtualMachine;

class MyMachine : VirtualProcessor<ControlFlowContext>
{
    // TRY — opens an exception block.
    [Instruction("TRY", 0x30)]
    void Try(ControlFlowContext ctx, int catchAddr, int finallyAddr)
        => ctx.ControlFlow.PushException(ctx.InstructionPointer - 1, catchAddr, finallyAddr);

    // THROW — finds the nearest handler; runs finally first when present.
    [Instruction("THROW", 0x31)]
    void Throw(ControlFlowContext ctx)
        => ctx.ControlFlow.Throw(ctx, ctx.Stack.Pop());

    // ENDFINALLY — after finally completes, jumps to catch (if pending) or pops the block.
    [Instruction("ENDFINALLY", 0x32)]
    void EndFinally(ControlFlowContext ctx)
        => ctx.ControlFlow.EndFinally(ctx);
}

When Throw() finds a block with both a catch and a finally clause, it stores the catch address in ExceptionBlock.PendingCatchAddress and jumps to the finally block first. EndFinally() then redirects to the catch handler automatically.

Virtual memory

VirtualMemory<TAddress> provides a paged, process-isolated address space. Each physical page can be mapped into multiple processes with independent access rights.

using Utils.VirtualMachine;

var mem = new VirtualMemory<int>(pageSize: 256);
var page = mem.AllocatePage();  // auto-mapped into MasterProcess with ReadWrite.

var child = mem.CreateProcess();
mem.MapPage(child, page, virtualPageIndex: 0, PageAccess.ReadOnly);

// Read and write through a process view.
mem.MasterProcess.Write(0, new byte[] { 1, 2, 3 });
var buf = new byte[3];
child.Read(0, buf);   // succeeds (ReadOnly)

// Release a child process and unmap all its pages.
mem.FreeProcess(child);

Debugging with IVmInspector

Attach an IVmInspector<T> to a VirtualProcessor<T> to intercept every instruction and trigger breakpoints without modifying the instruction set.

using Utils.VirtualMachine;

class TraceInspector : IVmInspector<DefaultContext>
{
    public void BeforeInstruction(DefaultContext ctx, int address, string name)
        => Console.WriteLine($"[{address:X4}] {name}");

    public void OnBreakpoint(DefaultContext ctx, int address, string name)
        => Console.WriteLine($"*** breakpoint at {address:X4} ({name})");
}

var machine = new StackMachine();
machine.Inspector = new TraceInspector();
machine.Breakpoints.Add(0x0005);  // break when IP == 5

machine.Execute(ctx);

BeforeInstruction is called for every instruction. OnBreakpoint is called first when the current address is in Breakpoints, then BeforeInstruction follows.

  • omy.Utils.IO – binary parsing helpers used by the VM framework.
  • omy.Utils.Fonts – uses the VM framework for font table parsing.

Versioned API documentation

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on omy.Utils.VirtualMachine:

Package Downloads
omy.Utils.Fonts

TrueType/PostScript font parsing with encoding tables, glyph metrics, and rendering helpers.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
2.0.0-rc.1 132 8/28/2026
0.1.0 455 6/20/2025
0.0.1 296 8/20/2025