The memory module provides register and RAM management for VM execution.
Constants
Number of general-purpose registers available in the VM
Registers Struct
Manages 16 general-purpose 64-bit registers (R0-R15).
pub struct Registers {
values: [u64; NUM_REGISTERS],
}
Methods
new
Create a new register set with all values initialized to zero.
New register set with all registers set to 0
get
Get the value of a register.
pub fn get(&self, r: usize) -> u64
Register index (0-15). Values outside this range are wrapped using modulo.
Current value in the register
Example:
let mut regs = Registers::new();
regs.set(5, 42);
assert_eq!(regs.get(5), 42);
set
Set the value of a register.
pub fn set(&mut self, r: usize, value: u64)
Register index (0-15). Values outside this range are wrapped using modulo.
Value to store in the register
values
Get a reference to all register values.
pub fn values(&self) -> &[u64; NUM_REGISTERS]
Immutable reference to the register array
Example:
let regs = Registers::new();
let all_values = regs.values();
println!("R0: {}, R1: {}", all_values[0], all_values[1]);
Memory Struct
Manages linear memory with dynamic expansion up to a maximum size.
pub struct Memory {
data: Vec<u8>,
max_size: usize,
}
Dynamically sized memory buffer
Maximum allowed memory size in bytes (default: 1MB)
Methods
new
Create a new memory instance with specified maximum size.
pub fn new(max_size: usize) -> Self
Maximum memory size in bytes
New memory instance starting at 0 bytes, expandable to max_size
Example:
// 1MB memory limit
let memory = Memory::new(1024 * 1024);
load8
Load an 8-bit value from memory.
pub fn load8(&self, offset: u32) -> u8
Memory address to read from
Byte at the specified offset, or 0 if out of bounds
Note: Reading beyond allocated memory returns 0 without error.
load64
Load a 64-bit value from memory (little-endian).
pub fn load64(&self, offset: u32) -> u64
Memory address to read from (must have 8 bytes available)
64-bit value in little-endian format, or 0 if insufficient space
Example:
let mut mem = Memory::new(1024);
mem.store64(0, 0x123456789ABCDEF0)?;
assert_eq!(mem.load64(0), 0x123456789ABCDEF0);
store8
Store an 8-bit value to memory.
pub fn store8(&mut self, offset: u32, value: u8) -> Result<(), VmError>
Memory address to write to
Ok(()) on success, Err(VmError::MemoryOverflow) if offset >= max_size
Note: Memory automatically expands to accommodate the write if within max_size.
store64
Store a 64-bit value to memory (little-endian).
pub fn store64(&mut self, offset: u32, value: u64) -> Result<(), VmError>
Memory address to write to
Ok(()) on success, Err(VmError::MemoryOverflow) if offset + 8 > max_size
Example:
let mut mem = Memory::new(1024);
mem.store64(100, 0xDEADBEEF)?;
mcopy
Copy a region of memory from one location to another.
pub fn mcopy(&mut self, dest: u32, src: u32, length: u32) -> Result<(), VmError>
Ok(()) on success, Err(VmError::MemoryOverflow) if operation exceeds max_size
Note: Handles overlapping regions correctly by creating a temporary copy.
Example:
let mut mem = Memory::new(1024);
mem.store64(0, 0x12345678)?;
mem.mcopy(100, 0, 8)?; // Copy 8 bytes from offset 0 to 100
assert_eq!(mem.load64(100), 0x12345678);
size
Get the current allocated memory size.
pub fn size(&self) -> usize
Number of bytes currently allocated
Note: This is the current size, not the maximum size. Memory grows dynamically.
Usage Examples
Basic Register Operations
use minichain_vm::{Registers, NUM_REGISTERS};
let mut regs = Registers::new();
// Set some register values
regs.set(0, 100);
regs.set(1, 200);
regs.set(2, regs.get(0) + regs.get(1));
assert_eq!(regs.get(2), 300);
// Access all registers
let all = regs.values();
println!("All registers: {:?}", all);
Memory Operations
use minichain_vm::Memory;
let mut mem = Memory::new(1024 * 1024); // 1MB
// Store individual bytes
mem.store8(0, 0xFF)?;
mem.store8(1, 0xAA)?;
// Store 64-bit values
mem.store64(100, 0xDEADBEEFCAFEBABE)?;
// Load values back
assert_eq!(mem.load8(0), 0xFF);
assert_eq!(mem.load64(100), 0xDEADBEEFCAFEBABE);
// Check memory size
println!("Memory used: {} bytes", mem.size());
Memory Copy Operations
use minichain_vm::Memory;
let mut mem = Memory::new(4096);
// Write a pattern
for i in 0..10 {
mem.store8(i, i as u8)?;
}
// Copy the pattern
mem.mcopy(100, 0, 10)?;
// Verify the copy
for i in 0..10 {
assert_eq!(mem.load8(100 + i), i as u8);
}
Memory Expansion
use minichain_vm::{Memory, VmError};
let mut mem = Memory::new(256); // Small 256-byte limit
// This works - within limit
mem.store8(255, 0xFF)?;
// This fails - exceeds limit
let result = mem.store8(256, 0xFF);
assert!(matches!(result, Err(VmError::MemoryOverflow)));
// Memory size grows automatically
assert_eq!(mem.size(), 256);
Working with VM
use minichain_vm::{Vm, Memory, Registers};
use minichain_core::Address;
// Bytecode that uses memory:
// LOADI R0, 42
// LOADI R1, 100
// STORE64 R1, R0
// HALT
let bytecode = vec![
0x70, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x70, 0x10, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x43, 0x10,
0x00,
];
let mut vm = Vm::new(
bytecode,
10000,
Address::zero(),
Address::zero(),
0,
);
let result = vm.run()?;
assert!(result.success);
// Access register state
let regs = vm.get_registers();
println!("Final R0: {}", regs[0]);
Memory Layout
Memory is a flat, byte-addressable space:
0x0000: [........................................]
↑ ↑
start max_size
(default: 1MB)
- Unallocated reads: Return 0
- Out-of-bounds writes: Return
VmError::MemoryOverflow
- Dynamic expansion: Memory grows automatically on writes
- Alignment: No alignment requirements; any offset is valid