A High-Performance Virtual Machine for Delphi
๐ง NitroVM is a Work in Progress
NitroVM is currently under active development and evolving quickly. Some features described in this documentation may be incomplete, experimental, or subject to significant changes as the project matures.
We welcome your feedback, ideas, and issue reports โ your input will directly influence the direction and quality of NitroVM as we strive to build the ultimate modern Pascal development platform.
NitroVMโข is a sophisticated, high-performance virtual machine designed specifically for modern Delphi applications. It provides a comprehensive platform for executing custom bytecode with dynamic module loading, external DLL integration, cross-platform support, and hot reloading capabilities. Built with enterprise-grade architecture, NitroVM enables developers to create extensible, modular applications with unprecedented flexibility.
- Stack-based virtual machine with optimized bytecode execution
- Native calling conventions with Windows x64 ABI support
- Separate opcode and operand arrays for cache efficiency
- Inline critical paths with minimal memory allocations
- Load and create modules at runtime (.nvmm format)
- Hot reloading of modules without stopping execution
- Cross-module function calls with symbol resolution
- Persistent module storage with dependency management
- Call any external DLL/shared library function
- Automatic type marshalling between Pascal and native types
- Native memory allocation and pointer manipulation
- Platform abstraction layer ready for Windows/Linux/macOS (Linux and macOS coming soon)
- Complete Pascal type support with RTTI integration
- Automatic serialization/deserialization of complex types
- Arrays, records, variants, and custom types
- Type-safe stack operations with runtime validation
- Fluent interface for elegant code construction
- Step-by-step debugging and execution control
- Comprehensive error handling with VM state information
- Program persistence with binary format (.nvmp)
- Platform: Windows 64-bit (Linux x64 and macOS x64 coming in future releases)
- Compiler: Delphi 12 Athens or higher
- Dependencies: None (self-contained)
- Clone the repository:
git clone https://github.com/tinyBigGAMES/NitroVM.git
-
Add the source files to your Delphi project:
NitroVM.pas
- Main virtual machine unitNitroVM.Platform.pas
- Platform abstraction layer
-
Add
NitroVM
to your uses clause:
uses
NitroVM;
uses
NitroVM;
var
LVM: TNitroVM;
LResult: TNVMValue;
begin
LVM := TNitroVM.Create();
try
// Build and execute a simple program
LVM.BeginProgram()
.AddInstruction(NVM_OP_PUSH, TNVMValue.From<Int32>(42))
.AddInstruction(NVM_OP_PUSH, TNVMValue.From<Int32>(10))
.AddInstruction(NVM_OP_ADD_INT32)
.AddInstruction(NVM_OP_STOP)
.ExecuteProgram();
// Get result
LResult := LVM.Pop();
WriteLn('Result: ', LResult.AsType<Int32>()); // Output: 52
finally
LVM.Free();
end;
end;
var
LVM: TNitroVM;
LModule: TNVMModule;
LResult: TNVMValue;
begin
LVM := TNitroVM.Create();
try
// Create a new module with a function
LModule := LVM.CreateModule('MathLib');
LVM.BeginModuleBuilder(LModule)
.BeginFunction('Add', 2)
.AddModuleInstruction(LModule, NVM_OP_ADD_INT32)
.AddModuleInstruction(LModule, NVM_OP_RETURN)
.EndFunction()
.EndModuleBuilder(LModule)
.RegisterCreatedModule(LModule);
// Call the module function
LResult := LVM.CallModuleFunction('MathLib', 'Add', [25, 17]);
WriteLn('25 + 17 = ', LResult.AsType<Int32>()); // Output: 42
finally
LVM.Free();
end;
end;
var
LVM: TNitroVM;
LResult: TNVMValue;
begin
LVM := TNitroVM.Create();
try
// Load Windows API
LVM.LoadExternalLibrary('user32.dll');
LVM.RegisterExternalFunction('MessageBoxW', 'user32.dll');
// Call MessageBox function
LResult := LVM.CallExternalFunction('MessageBoxW', [
0, // hWnd
PWideChar('Hello from NitroVM!'), // lpText
PWideChar('NitroVM Demo'), // lpCaption
0 // uType
]);
WriteLn('MessageBox result: ', LResult.AsType<Int64>());
finally
LVM.Free();
end;
end;
NitroVM uses a stack-based architecture where all operations manipulate values on an execution stack:
// Stack operations
LVM.Push(TNVMValue.From<Integer>(42)) // Push integer
.Push(TNVMValue.From<String>('Hello')) // Push string
.Push(TNVMValue.From<Double>(3.14)); // Push double
// Stack inspection
LSize := LVM.StackSize(); // Get current size
LTop := LVM.Peek(0); // Peek at top (index 0)
LSecond := LVM.Peek(1); // Peek at second (index 1)
// Pop values
LValue := LVM.Pop(); // Pop and return value
LVM.ClearStack(); // Clear all values
The module system provides dynamic loading and execution of bytecode modules:
// Load module from file
LModule := LVM.LoadModule('MyLibrary.nvmm');
// Create module programmatically
LModule := LVM.CreateModule('CustomLib')
.BeginModuleBuilder()
.AddModuleInstruction(NVM_OP_PUSH, TNVMValue.From<String>('Module loaded!'))
.AddModuleInstruction(NVM_OP_WRITELN)
.EndModuleBuilder()
.RegisterCreatedModule();
// Hot reload support
if LVM.CanHotReload('MyLibrary') then
begin
LVM.StartHotReload();
LVM.ReloadModule('MyLibrary');
LVM.CompleteHotReload();
end;
NitroVM provides seamless integration with external libraries through platform abstraction:
// Cross-platform library loading
LHandle := LVM.LoadExternalLibrary('mylib'); // Automatically adds .dll/.so/.dylib
// Native memory management
LPtr := LVM.AllocateNativeMemory(1024);
try
// Use native memory...
FillChar(LPtr^, 1024, 0);
finally
LVM.FreeNativeMemory(LPtr);
end;
// Type marshalling
LNativeValue := LVM.MarshalToNative(LPascalValue, etInt32);
LPascalValue := LVM.MarshalFromNative(LNativePtr, etString);
Opcode | Name | Description |
---|---|---|
0 | NVM_OP_NOP | No operation |
1 | NVM_OP_STOP | Stop execution |
2 | NVM_OP_PUSH | Push operand value onto stack |
3 | NVM_OP_POP | Pop and discard top value |
4 | NVM_OP_DUP | Duplicate top stack value |
5 | NVM_OP_SWAP | Swap top two stack values |
Opcode | Name | Description |
---|---|---|
10 | NVM_OP_ADD_INT32 | Add two Int32 values |
11 | NVM_OP_SUB_INT32 | Subtract two Int32 values |
12 | NVM_OP_MUL_INT32 | Multiply two Int32 values |
13 | NVM_OP_DIV_INT32 | Divide two Int32 values |
14 | NVM_OP_MOD_INT32 | Modulo two Int32 values |
15 | NVM_OP_NEG_INT32 | Negate Int32 value |
Opcode | Name | Description |
---|---|---|
20 | NVM_OP_ADD_DOUBLE | Add two Double values |
21 | NVM_OP_SUB_DOUBLE | Subtract two Double values |
22 | NVM_OP_MUL_DOUBLE | Multiply two Double values |
23 | NVM_OP_DIV_DOUBLE | Divide two Double values |
24 | NVM_OP_NEG_DOUBLE | Negate Double value |
Opcode | Name | Description |
---|---|---|
30 | NVM_OP_CONCAT_STR | Concatenate two strings |
31 | NVM_OP_LENGTH_STR | Get string length |
32 | NVM_OP_SUBSTR | Get substring |
Opcode | Name | Description |
---|---|---|
40-45 | NVM_OP_EQ/NE/LT/LE/GT/GE_INT32 | Integer comparisons |
50-55 | NVM_OP_EQ/NE/LT/LE/GT/GE_DOUBLE | Double comparisons |
60-61 | NVM_OP_EQ/NE_STR | String comparisons |
Opcode | Name | Description |
---|---|---|
70 | NVM_OP_AND_BOOL | Logical AND |
71 | NVM_OP_OR_BOOL | Logical OR |
72 | NVM_OP_NOT_BOOL | Logical NOT |
Opcode | Name | Description |
---|---|---|
80 | NVM_OP_JUMP | Unconditional jump |
81 | NVM_OP_JUMP_TRUE | Jump if true |
82 | NVM_OP_JUMP_FALSE | Jump if false |
83 | NVM_OP_CALL | Call function |
84 | NVM_OP_RETURN | Return from function |
85 | NVM_OP_EXIT | Exit program |
Opcode | Name | Description |
---|---|---|
90 | NVM_OP_LOAD_VAR | Load variable value |
91 | NVM_OP_STORE_VAR | Store variable value |
92 | NVM_OP_LOAD_GLOBAL | Load global variable |
93 | NVM_OP_STORE_GLOBAL | Store global variable |
Opcode | Name | Description |
---|---|---|
100 | NVM_OP_LOAD_MODULE | Load .nvmm module |
101 | NVM_OP_CALL_MODULE | Call function in module |
102 | NVM_OP_CALL_RESOLVED | Call with pre-resolved symbol ID |
103 | NVM_OP_RETURN_MODULE | Return from cross-module call |
104 | NVM_OP_UNLOAD_MODULE | Unload module |
105 | NVM_OP_GET_SYMBOL | Get symbol address |
106 | NVM_OP_HOT_RELOAD | Hot reload module |
Opcode | Name | Description |
---|---|---|
110 | NVM_OP_WRITELN | WriteLn output |
111 | NVM_OP_READLN | ReadLn input |
112 | NVM_OP_INT_TO_STR | Convert integer to string |
113 | NVM_OP_STR_TO_INT | Convert string to integer |
114 | NVM_OP_DOUBLE_TO_STR | Convert double to string |
115 | NVM_OP_STR_TO_DOUBLE | Convert string to double |
Opcode | Name | Description |
---|---|---|
120 | NVM_OP_LOAD_LIBRARY | Load external DLL |
121 | NVM_OP_UNLOAD_LIBRARY | Unload external DLL |
122 | NVM_OP_REGISTER_EXTERNAL | Register external function |
123 | NVM_OP_CALL_EXTERNAL | Call external function |
124 | NVM_OP_ALLOC_NATIVE | Allocate native memory |
125 | NVM_OP_FREE_NATIVE | Free native memory |
126 | NVM_OP_MARSHAL_TO_NATIVE | Marshal Pascal value to native |
127 | NVM_OP_MARSHAL_FROM_NATIVE | Marshal native value to Pascal |
128 | NVM_OP_GET_PROC_ADDRESS | Get function address from DLL |
129 | NVM_OP_REGISTER_CALLBACK | Register Pascal callback |
NitroVM modules use a binary format with the following structure:
Header (48 bytes):
Magic: 'NVM1' (4 bytes)
Version: Format version (2 bytes)
Flags: Module type flags (2 bytes)
CodeSize: Number of opcodes (4 bytes)
DataSize: Number of operands (4 bytes)
ExportCount: Number of exported symbols (4 bytes)
ImportCount: Number of imported symbols (4 bytes)
EntryPoint: Module initialization entry point (4 bytes)
ModuleNameOffset: Offset to module name (4 bytes)
Checksum: Header + code checksum (4 bytes)
Reserved: Future expansion (16 bytes)
Data Sections:
Module Name: Length-prefixed UTF-16 string
Bytecode: Encoded opcodes array
Operands: RTTI-serialized operand values
Exports: Function export table
Imports: Import dependency table
External Libraries: Required DLL dependencies
External Functions: Function-to-library mappings
Complete programs with multiple modules and external dependencies:
Program File Structure:
Module Count: Number of embedded modules (4 bytes)
Modules: Array of serialized module data
Library Count: Number of external libraries (4 bytes)
Libraries: Array of library names
Function Count: Number of external functions (4 bytes)
Functions: Function-to-library mappings
Program State: VM execution state
Bytecode: Integrated program opcodes
Operands: Integrated program operands
Library collections containing multiple related modules:
Library File Structure:
Module Count: Number of modules in library (4 bytes)
Module Entries: Array of:
Name Length: Module name length (4 bytes)
Name: Module name string
Data Size: Module data size (4 bytes)
Data: Complete module binary data
Register your own opcodes for domain-specific operations:
// Opcode without operand
LVM.RegisterOpcode(200, procedure(const AVM: TNitroVM)
begin
// Custom operation
AVM.Push(TNVMValue.From<String>('Custom opcode executed!'));
end);
// Opcode with operand
LVM.RegisterOpcode(201, procedure(const AVM: TNitroVM; const AOperand: TNVMValue)
begin
// Process operand and push result
AVM.Push(AOperand);
end);
// Execute one instruction at a time
while LVM.IsRunning() and not LVM.IsHalted() do
begin
WriteLn('PC: ', LVM.GetProgramCounter(), ' Stack: ', LVM.StackSize());
LVM.ExecuteStep();
// Implement breakpoints, variable inspection, etc.
if LVM.GetProgramCounter() = LBreakpoint then
begin
WriteLn('Breakpoint hit!');
// Inspect VM state...
end;
end;
// Limited execution with step count
LVM.ExecuteUntil(1000); // Execute maximum 1000 instructions
// Save complete program with all modules and dependencies
LVM.SaveProgram('myapp.nvmp');
// Load and restore complete state
LVM.Reset()
.LoadProgram('myapp.nvmp')
.ExecuteProgram();
// Save/load individual modules
LModule.SaveToFile('mathlib.nvmm');
LLoadedModule := LVM.LoadModule('mathlib.nvmm');
// Save/load module collections
LVM.SaveLibrary('utilities.nvml');
LVM.LoadLibrary('utilities.nvml');
// Currently supported: Windows
{$IFDEF MSWINDOWS}
LVM.LoadExternalLibrary('kernel32.dll');
LVM.RegisterExternalFunction('GetCurrentProcessId', 'kernel32.dll');
{$ENDIF}
// Planned for future releases:
{$IFDEF LINUX}
LVM.LoadExternalLibrary('libc.so.6');
LVM.RegisterExternalFunction('getpid', 'libc.so.6');
{$ENDIF}
{$IFDEF MACOS}
LVM.LoadExternalLibrary('libSystem.dylib');
LVM.RegisterExternalFunction('getpid', 'libSystem.dylib');
{$ENDIF}
// Platform information (works on all platforms)
WriteLn('Platform: ', NVMGetPlatformName()); // Windows/Linux/macOS
WriteLn('Architecture: ', NVMGetArchitectureName()); // x64/x86/ARM64
WriteLn('Library ext: ', NVMGetLibraryExtension()); // .dll/.so/.dylib
NitroVM provides comprehensive error information with VM state:
try
LVM.ExecuteProgram();
except
on E: ENVMException do
begin
WriteLn('VM Error: ', E.Message);
WriteLn('Error Category: ', E.ErrorCategory);
if E.HasVMState then
begin
WriteLn('Program Counter: ', E.VMProgramCounter);
WriteLn('Stack Size: ', E.VMStackSize);
WriteLn('Current Module: ', E.VMCurrentModule);
WriteLn('Execution State: ', E.VMExecutionState);
end;
if E.HasOpcodeInfo then
WriteLn('Current Opcode: ', E.VMOpcode);
// Get detailed debug information
WriteLn('Debug Info: ', E.GetVMDebugInfo());
WriteLn('Detailed Message: ', E.GetDetailedMessage());
end;
end;
// Global variables
LVM.SetVariable('GlobalCounter', TNVMValue.From<Integer>(0));
LCounter := LVM.GetVariable('GlobalCounter');
// Check variable existence
if LVM.HasVariable('MyVar') then
WriteLn('Variable exists: ', LVM.GetVariable('MyVar').AsType<String>());
// Local scope management (automatic with function calls)
// Variables are automatically scoped to function call frames
// Allocate native memory block
LPtr := LVM.AllocateNativeMemory(4096);
try
// Direct memory access
PInteger(LPtr)^ := 42;
WriteLn('Value: ', PInteger(LPtr)^);
// Reallocate if needed
LPtr := LVM.ReallocateNativeMemory(LPtr, 8192);
// Check memory validity
if LVM.IsNativeMemoryValid(LPtr) then
WriteLn('Memory block is valid');
finally
LVM.FreeNativeMemory(LPtr);
end;
// Memory statistics
WriteLn('Total memory usage: ', LVM.GetMemoryUsage(), ' bytes');
WriteLn('Active memory blocks: ', Length(LVM.GetAllocatedMemoryBlocks()));
- Separate Arrays: Opcodes and operands stored separately for optimal cache usage
- Inline Functions: Critical execution paths use inlined methods for speed
- Minimal Allocations: Pre-allocated arrays and efficient memory management
- Direct Dispatch: Fast opcode handler lookup without virtual method calls
- Native Calling: Direct calling conventions for external functions
- Symbol Caching: O(1) symbol resolution with pre-computed lookup tables
NitroVM is designed with cross-platform support in mind through the NitroVM.Platform
abstraction layer:
Currently Supported:
- Windows x64: Full support with Windows API integration and x64 calling convention
Planned for Future Releases:
- Linux x64: System V ABI calling convention and shared library support
- macOS x64: Native macOS support with dylib integration
The platform abstraction layer is already in place to handle cross-platform differences:
// Platform detection (works on all platforms)
WriteLn('Running on: ', NVMGetPlatformName()); // Windows/Linux/macOS
WriteLn('Architecture: ', NVMGetArchitectureName()); // x64/x86/ARM64
WriteLn('64-bit platform: ', NVMIs64BitPlatform());
// Platform-specific operations are abstracted
LHandle := NVMLoadLibrary('mylib'); // Will handle .dll/.so/.dylib
LFuncPtr := NVMGetProcAddress(LHandle, 'MyFunction');
LResult := NVMCallExternalFunction(LFuncPtr, @LParams[0], LParamCount);
Current Windows-specific example:
// Windows API access (currently supported)
LVM.LoadExternalLibrary('kernel32.dll');
LVM.RegisterExternalFunction('GetCurrentProcessId', 'kernel32.dll');
LResult := LVM.CallExternalFunction('GetCurrentProcessId', []);
Future Linux/macOS support will include:
{$IFDEF LINUX}
LVM.LoadExternalLibrary('libc.so.6');
LVM.RegisterExternalFunction('getpid', 'libc.so.6');
{$ENDIF}
{$IFDEF MACOS}
LVM.LoadExternalLibrary('libSystem.dylib');
LVM.RegisterExternalFunction('getpid', 'libSystem.dylib');
{$ENDIF}
Join our growing community of developers building the future of Pascal development!
This project is licensed under the BSD 3-Clause License - see the LICENSE file for details.
BSD 3-Clause License
Copyright ยฉ 2025-present tinyBigGAMESโข LLC
All Rights Reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice
2. Redistributions in binary form must reproduce the above copyright notice
3. Neither the name of the copyright holder nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
- ๐ Homepage: Homepage โ Official website
- ๐ Issues: GitHub Issues - Bug reports & feature requests
- ๐ฌ Discussions: GitHub Discussions - General questions & ideas
- ๐ฌ Discord: Join our community - Real-time chat & support
- ๐ฆ Bluesky: @tinybiggames.com - Updates & announcements
- ๐ Built with love for the Delphi community
- ๐ Focused on practical, real-world usage scenarios
- ๐ Committed to enterprise-grade quality and performance
- ๐ Supporting the future of Pascal application development
- Delphi Community - For continued support and feedback
- Beta Testers - Early adopters who helped shape NitroVM
- Contributors - Everyone who has submitted issues, PRs, and improvements
Made with โค๏ธ by tinyBigGAMESโข
NitroVM - Powering the next generation of Delphi applications with high-performance virtual machine technology.