2024-02-17 23:16:32 +00:00
|
|
|
struct VM<'p> {
|
|
|
|
program: &'p Program,
|
|
|
|
block: u32, ip: u32,
|
|
|
|
heap: Heap<'p>,
|
2024-02-17 23:18:29 +00:00
|
|
|
locals: Object,
|
2024-02-17 23:18:59 +00:00
|
|
|
stack: Vec<Value>,
|
2024-02-17 23:16:32 +00:00
|
|
|
status: Status
|
|
|
|
}
|
2024-02-17 21:07:00 +00:00
|
|
|
|
2024-02-17 23:16:32 +00:00
|
|
|
#[derive(Clone, Copy)]
|
2024-02-17 21:07:00 +00:00
|
|
|
enum Status {
|
|
|
|
Running,
|
2024-02-17 23:16:32 +00:00
|
|
|
AwaitSyscall(u32),
|
2024-02-17 21:07:00 +00:00
|
|
|
Crash(&'static str),
|
2024-02-17 23:16:32 +00:00
|
|
|
Exit,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Clone, Copy, Debug)]
|
2024-02-17 23:18:59 +00:00
|
|
|
enum Value {
|
2024-02-17 23:18:29 +00:00
|
|
|
Object(Object),
|
2024-02-17 23:16:32 +00:00
|
|
|
Integer(u32),
|
|
|
|
}
|
2024-02-17 23:18:59 +00:00
|
|
|
impl Value {
|
2024-02-17 23:18:29 +00:00
|
|
|
fn object(self) -> Option<Object> {
|
2024-02-17 23:18:59 +00:00
|
|
|
if let Value::Object(t) = self { return Some(t) }
|
2024-02-17 23:16:32 +00:00
|
|
|
return None
|
|
|
|
}
|
|
|
|
|
|
|
|
fn integer(self) -> Option<u32> {
|
2024-02-17 23:18:59 +00:00
|
|
|
if let Value::Integer(u) = self { return Some(u) }
|
2024-02-17 23:16:32 +00:00
|
|
|
return None
|
|
|
|
}
|
2024-02-17 21:07:00 +00:00
|
|
|
}
|
|
|
|
|
2024-02-17 23:16:32 +00:00
|
|
|
#[derive(Clone, Copy, Debug)]
|
2024-02-17 23:18:29 +00:00
|
|
|
struct Object { index: u32 }
|
2024-02-17 23:16:32 +00:00
|
|
|
struct Program {
|
|
|
|
blocks: Vec<BlockData>,
|
|
|
|
types: Vec<TypeData>,
|
|
|
|
}
|
|
|
|
struct BlockData { instructions: Vec<Instruction> }
|
|
|
|
|
|
|
|
struct Block { index: u32 }
|
2024-02-17 21:07:00 +00:00
|
|
|
|
2024-02-17 23:16:32 +00:00
|
|
|
#[derive(Debug)]
|
2024-02-17 21:07:00 +00:00
|
|
|
enum Instruction {
|
2024-02-17 23:16:32 +00:00
|
|
|
// push NULL to stack
|
|
|
|
PushNull,
|
|
|
|
// push an integer to the stack
|
|
|
|
PushInteger(u32),
|
|
|
|
|
2024-02-17 23:18:29 +00:00
|
|
|
// push a new object to the stack with the given type
|
2024-02-17 23:16:32 +00:00
|
|
|
PushNew(Type),
|
|
|
|
|
|
|
|
// get the locals register
|
|
|
|
PushLocals,
|
|
|
|
// pop the locals register
|
|
|
|
PopLocals,
|
|
|
|
|
|
|
|
Op11(Op11), // [t1] -> [t2]
|
|
|
|
Op21(Op21), // [t1, t2] -> [t3]
|
|
|
|
|
|
|
|
// assert that the stack has n items and jump to the given block
|
|
|
|
Call { block: u32, n: u32, },
|
|
|
|
// assert that the stack has n items, clear it, and execute a syscall
|
|
|
|
Syscall { syscall: u32, n: u32 },
|
|
|
|
|
2024-02-17 21:07:00 +00:00
|
|
|
// kill the program
|
|
|
|
Crash(&'static str),
|
|
|
|
// end the program, popping the top stack item
|
|
|
|
Exit
|
|
|
|
}
|
|
|
|
|
2024-02-17 23:16:32 +00:00
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
enum Op11 { Get(u32) }
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
enum Op21 { Add, Set(u32) }
|
|
|
|
|
|
|
|
const TY_NULL: Type = Type { id: 0x0 };
|
2024-02-17 23:18:29 +00:00
|
|
|
const NULL: Object = Object { index: 0 };
|
2024-02-17 23:16:32 +00:00
|
|
|
|
2024-02-17 21:07:00 +00:00
|
|
|
const SYSCALL_DBG: u32 = 0x1000;
|
|
|
|
|
|
|
|
|
|
|
|
fn main() {
|
2024-02-17 23:16:32 +00:00
|
|
|
let mut program = Program::new();
|
|
|
|
let ty_locals = program.type_create(3);
|
|
|
|
program.block_create(
|
|
|
|
BlockData {
|
|
|
|
instructions: vec![
|
|
|
|
Instruction::PushNew(ty_locals), Instruction::PopLocals,
|
|
|
|
|
|
|
|
Instruction::PushLocals,
|
|
|
|
Instruction::PushInteger(0xdead0000), Instruction::Op21(Op21::Set(0)),
|
|
|
|
Instruction::PushInteger(0x0000ba75), Instruction::Op21(Op21::Set(1)),
|
|
|
|
// stack: [locals]
|
|
|
|
|
|
|
|
Instruction::Call { block: 1, n: 1 },
|
|
|
|
]
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
program.block_create(
|
|
|
|
BlockData {
|
|
|
|
instructions: vec![
|
|
|
|
Instruction::PopLocals,
|
|
|
|
|
|
|
|
Instruction::PushLocals,
|
|
|
|
Instruction::PushLocals, Instruction::Op11(Op11::Get(0)),
|
|
|
|
Instruction::PushLocals, Instruction::Op11(Op11::Get(1)),
|
|
|
|
Instruction::Op21(Op21::Add),
|
|
|
|
Instruction::Op21(Op21::Set(2)),
|
|
|
|
|
|
|
|
Instruction::Call { block: 2, n: 1 }
|
|
|
|
]
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
program.block_create(
|
|
|
|
BlockData {
|
|
|
|
instructions: vec![
|
|
|
|
Instruction::PopLocals,
|
|
|
|
|
|
|
|
Instruction::PushLocals,
|
|
|
|
Instruction::Op11(Op11::Get(0)),
|
|
|
|
Instruction::Syscall { syscall: SYSCALL_DBG, n: 1 },
|
|
|
|
|
2024-02-17 21:07:00 +00:00
|
|
|
Instruction::Exit,
|
2024-02-17 23:16:32 +00:00
|
|
|
]
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
host_program(&program).unwrap()
|
2024-02-17 21:07:00 +00:00
|
|
|
}
|
|
|
|
|
2024-02-17 23:16:32 +00:00
|
|
|
fn host_program(p: &Program) -> Result<(), String> {
|
2024-02-17 21:07:00 +00:00
|
|
|
let mut vm = VM::start(p);
|
|
|
|
loop {
|
|
|
|
let status = vm.step();
|
|
|
|
match status {
|
|
|
|
Status::Running => {}
|
2024-02-17 23:16:32 +00:00
|
|
|
Status::AwaitSyscall(ix) => {
|
|
|
|
match ix {
|
2024-02-17 21:07:00 +00:00
|
|
|
SYSCALL_DBG => {
|
2024-02-17 23:16:32 +00:00
|
|
|
let top = vm.stack.pop().ok_or("should be an item on the stack")?;
|
|
|
|
println!("debug print: {:08x?}", top);
|
|
|
|
vm.complete_syscall()
|
2024-02-17 21:07:00 +00:00
|
|
|
}
|
2024-02-17 23:16:32 +00:00
|
|
|
_ => { return Err(format!("unknown syscall: {}", ix)) }
|
2024-02-17 21:07:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
Status::Crash(err) => { return Err(err.to_string()); }
|
2024-02-17 23:16:32 +00:00
|
|
|
Status::Exit => { return Ok(()); }
|
2024-02-17 21:07:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl<'p> VM<'p> {
|
|
|
|
pub fn start(program: &'p Program) -> VM<'p> {
|
2024-02-17 23:18:29 +00:00
|
|
|
let heap = Heap::new(program);
|
2024-02-17 23:16:32 +00:00
|
|
|
|
2024-02-17 21:07:00 +00:00
|
|
|
VM {
|
|
|
|
program,
|
|
|
|
block: 0,
|
|
|
|
ip: 0,
|
2024-02-17 23:16:32 +00:00
|
|
|
heap,
|
|
|
|
locals: NULL,
|
|
|
|
stack: vec![],
|
2024-02-17 21:07:00 +00:00
|
|
|
status: Status::Running,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-02-17 23:16:32 +00:00
|
|
|
pub fn step(&mut self) -> Status {
|
2024-02-17 21:07:00 +00:00
|
|
|
let error = match self.internal_step() {
|
2024-02-17 23:16:32 +00:00
|
|
|
Ok(_) => { return self.status; }
|
2024-02-17 21:07:00 +00:00
|
|
|
Err(e) => e
|
|
|
|
};
|
|
|
|
self.status = Status::Crash(error);
|
2024-02-17 23:16:32 +00:00
|
|
|
self.status
|
2024-02-17 21:07:00 +00:00
|
|
|
}
|
|
|
|
fn internal_step(&mut self) -> Result<(), &'static str> {
|
|
|
|
if let Status::Running = self.status { } else { return Ok(()); }
|
|
|
|
|
|
|
|
let instruction =
|
|
|
|
self.program
|
|
|
|
.blocks.get(self.block as usize).ok_or("block must exist")?
|
|
|
|
.instructions.get(self.ip as usize).ok_or("instruction must exist")?;
|
|
|
|
self.ip += 1;
|
2024-02-17 23:16:32 +00:00
|
|
|
println!("instruction: {:?}", instruction);
|
2024-02-17 21:07:00 +00:00
|
|
|
|
|
|
|
match instruction {
|
2024-02-17 23:16:32 +00:00
|
|
|
&Instruction::PushNull => {
|
2024-02-17 23:18:59 +00:00
|
|
|
self.stack.push(Value::Object(NULL))
|
2024-02-17 21:07:00 +00:00
|
|
|
}
|
2024-02-17 23:16:32 +00:00
|
|
|
|
|
|
|
&Instruction::PushInteger(int) => {
|
2024-02-17 23:18:59 +00:00
|
|
|
self.stack.push(Value::Integer(int))
|
2024-02-17 23:16:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
&Instruction::PushNew(ty) => {
|
2024-02-17 23:18:29 +00:00
|
|
|
let t = self.heap.object_create(ty);
|
2024-02-17 23:18:59 +00:00
|
|
|
self.stack.push(Value::Object(t))
|
2024-02-17 23:16:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Instruction::PushLocals => {
|
2024-02-17 23:18:59 +00:00
|
|
|
self.stack.push(Value::Object(self.locals))
|
2024-02-17 23:16:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Instruction::PopLocals => {
|
2024-02-17 23:18:29 +00:00
|
|
|
let t0 = self.stack.pop().ok_or("t0 must be present")?.object().ok_or("t0 must be a object")?;
|
2024-02-17 23:16:32 +00:00
|
|
|
self.locals = t0;
|
|
|
|
}
|
|
|
|
|
|
|
|
Instruction::Op11(op) => {
|
2024-02-17 21:07:00 +00:00
|
|
|
let t0 = self.stack.pop().ok_or("t0 must be present")?;
|
2024-02-17 23:16:32 +00:00
|
|
|
self.stack.push(op.perform(&mut self.heap, t0)?);
|
2024-02-17 21:07:00 +00:00
|
|
|
}
|
2024-02-17 23:16:32 +00:00
|
|
|
|
|
|
|
Instruction::Op21(op) => {
|
|
|
|
let t1 = self.stack.pop().ok_or("t1 must be present")?;
|
|
|
|
let t0 = self.stack.pop().ok_or("t0 must be present")?;
|
|
|
|
self.stack.push(op.perform(&mut self.heap, t0, t1)?);
|
2024-02-17 21:07:00 +00:00
|
|
|
}
|
2024-02-17 23:16:32 +00:00
|
|
|
|
|
|
|
&Instruction::Call { block, n } => {
|
|
|
|
if self.stack.len() != n as usize {
|
|
|
|
return Err("stack has wrong number of elements");
|
|
|
|
}
|
|
|
|
self.locals = NULL;
|
2024-02-17 21:07:00 +00:00
|
|
|
self.block = block;
|
|
|
|
self.ip = 0;
|
|
|
|
|
|
|
|
}
|
2024-02-17 23:16:32 +00:00
|
|
|
&Instruction::Syscall { syscall, n } => {
|
|
|
|
if self.stack.len() != n as usize {
|
|
|
|
return Err("stack has wrong number of elements");
|
|
|
|
}
|
|
|
|
self.status = Status::AwaitSyscall(syscall)
|
2024-02-17 21:07:00 +00:00
|
|
|
}
|
|
|
|
Instruction::Crash(c) => {
|
|
|
|
panic!("crashed: {}", c)
|
|
|
|
}
|
|
|
|
Instruction::Exit => {
|
2024-02-17 23:16:32 +00:00
|
|
|
self.status = Status::Exit
|
2024-02-17 21:07:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2024-02-17 23:16:32 +00:00
|
|
|
pub fn complete_syscall(&mut self) {
|
2024-02-17 21:07:00 +00:00
|
|
|
match &self.status {
|
|
|
|
Status::AwaitSyscall(_) => {
|
|
|
|
self.status = Status::Running;
|
|
|
|
}, // continue
|
|
|
|
_ => {
|
|
|
|
self.status = Status::Crash("tried to complete a syscall while not awaiting one")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-02-17 23:16:32 +00:00
|
|
|
impl Op11 {
|
2024-02-17 23:18:59 +00:00
|
|
|
fn perform(&self, heap: &mut Heap, v0: Value) -> Result<Value, &'static str> {
|
2024-02-17 23:16:32 +00:00
|
|
|
match self {
|
|
|
|
&Op11::Get(ix) => {
|
2024-02-17 23:18:29 +00:00
|
|
|
let t0 = v0.object().ok_or("must be a object")?;
|
|
|
|
Ok(heap.object_get_arg(t0, ix))
|
2024-02-17 23:16:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Op21 {
|
2024-02-17 23:18:59 +00:00
|
|
|
fn perform(&self, heap: &mut Heap, v0: Value, v1: Value) -> Result<Value, &'static str> {
|
2024-02-17 23:16:32 +00:00
|
|
|
match self {
|
|
|
|
Op21::Add => {
|
|
|
|
let i0 = v0.integer().ok_or("must be an integer")?;
|
|
|
|
let i1 = v1.integer().ok_or("must be an integer")?;
|
2024-02-17 23:18:59 +00:00
|
|
|
Ok(Value::Integer(i0.wrapping_add(i1)))
|
2024-02-17 23:16:32 +00:00
|
|
|
}
|
|
|
|
&Op21::Set(ix) => {
|
2024-02-17 23:18:29 +00:00
|
|
|
let t0 = v0.object().ok_or("must be a object")?;
|
|
|
|
heap.object_set_arg(t0, ix, v1);
|
2024-02-17 23:16:32 +00:00
|
|
|
Ok(v0)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl Program {
|
|
|
|
fn new() -> Program {
|
|
|
|
let mut p = Self {
|
|
|
|
blocks: vec![],
|
|
|
|
types: vec![],
|
|
|
|
};
|
|
|
|
let ty_null = p.type_create(0);
|
|
|
|
assert_eq!(TY_NULL, ty_null);
|
|
|
|
return p
|
|
|
|
}
|
|
|
|
|
|
|
|
fn type_create(&mut self, len: u32) -> Type {
|
|
|
|
let index = self.types.len();
|
|
|
|
self.types.push(TypeData { len });
|
|
|
|
Type { id: index as u32 }
|
|
|
|
}
|
|
|
|
|
|
|
|
fn type_get(&self, ty: Type) -> &TypeData {
|
|
|
|
return self.types.get(ty.id as usize).expect("type should have existed")
|
|
|
|
}
|
|
|
|
|
|
|
|
fn block_create(&mut self, block: BlockData) -> Block {
|
|
|
|
let index = self.blocks.len();
|
|
|
|
self.blocks.push(block);
|
|
|
|
Block { index: index as u32 }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct Heap<'p> {
|
|
|
|
program: &'p Program,
|
|
|
|
data: Vec<HeapItem>
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
enum HeapItem {
|
|
|
|
Type(Type),
|
2024-02-17 23:18:59 +00:00
|
|
|
Value(Value),
|
2024-02-17 23:16:32 +00:00
|
|
|
}
|
2024-02-17 21:07:00 +00:00
|
|
|
|
2024-02-17 23:16:32 +00:00
|
|
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
|
|
|
struct Type {
|
|
|
|
id: u32
|
2024-02-17 21:07:00 +00:00
|
|
|
}
|
|
|
|
|
2024-02-17 23:16:32 +00:00
|
|
|
struct TypeData {
|
|
|
|
len: u32
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'p> Heap<'p> {
|
|
|
|
fn new(p: &'p Program) -> Self {
|
|
|
|
let mut h = Heap {
|
|
|
|
program: p,
|
|
|
|
data: vec![],
|
|
|
|
};
|
2024-02-17 23:18:29 +00:00
|
|
|
let null = h.object_create(TY_NULL);
|
2024-02-17 23:16:32 +00:00
|
|
|
assert_eq!(NULL.index, null.index);
|
|
|
|
h
|
|
|
|
}
|
|
|
|
|
2024-02-17 23:18:29 +00:00
|
|
|
fn object_create(&mut self, ty: Type) -> Object {
|
2024-02-17 23:16:32 +00:00
|
|
|
let td = self.program.type_get(ty);
|
|
|
|
let index = self.data.len() as u32;
|
|
|
|
self.data.push(HeapItem::Type(ty));
|
|
|
|
for _ in 0..td.len {
|
2024-02-17 23:18:59 +00:00
|
|
|
self.data.push(HeapItem::Value(Value::Object(NULL)));
|
2024-02-17 23:16:32 +00:00
|
|
|
}
|
2024-02-17 23:18:29 +00:00
|
|
|
Object { index }
|
2024-02-17 23:16:32 +00:00
|
|
|
}
|
|
|
|
|
2024-02-17 23:18:29 +00:00
|
|
|
fn object_get_type(&self, object: Object) -> Type {
|
|
|
|
let header = self.data.get(object.index as usize).expect("object should have been valid");
|
2024-02-17 23:16:32 +00:00
|
|
|
if let &HeapItem::Type(ty) = header {
|
|
|
|
ty
|
|
|
|
} else {
|
|
|
|
panic!("header is invalid: {:?}", header)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-02-17 23:18:59 +00:00
|
|
|
fn object_get_arg(&self, object: Object, ix: u32) -> Value {
|
2024-02-17 23:18:29 +00:00
|
|
|
let ty = self.object_get_type(object);
|
2024-02-17 23:16:32 +00:00
|
|
|
let td = self.program.type_get(ty);
|
|
|
|
|
|
|
|
if ix < td.len {
|
2024-02-17 23:18:29 +00:00
|
|
|
let value = self.data.get((object.index + ix + 1) as usize).expect("should have been present");
|
2024-02-17 23:16:32 +00:00
|
|
|
match value {
|
2024-02-17 23:18:59 +00:00
|
|
|
&HeapItem::Value(si) => si,
|
|
|
|
_ => panic!("should have been a Value")
|
2024-02-17 23:16:32 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
panic!("invalid ix: {}", ix)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-02-17 23:18:59 +00:00
|
|
|
fn object_set_arg(&mut self, object: Object, ix: u32, arg: Value) {
|
2024-02-17 23:18:29 +00:00
|
|
|
let ty = self.object_get_type(object);
|
2024-02-17 23:16:32 +00:00
|
|
|
let td = self.program.type_get(ty);
|
|
|
|
|
|
|
|
if ix < td.len {
|
2024-02-17 23:18:29 +00:00
|
|
|
let slot = self.data.get_mut((object.index + ix + 1) as usize).expect("should have been present");
|
2024-02-17 23:18:59 +00:00
|
|
|
*slot = HeapItem::Value(arg)
|
2024-02-17 23:16:32 +00:00
|
|
|
} else {
|
|
|
|
panic!("invalid ix: {}", ix)
|
|
|
|
}
|
|
|
|
}
|
2024-02-17 21:07:00 +00:00
|
|
|
}
|