initial commit
This commit is contained in:
commit
92fc7ff44b
8 changed files with 335 additions and 0 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
./target
|
||||
*~
|
6
Cargo.toml
Normal file
6
Cargo.toml
Normal file
|
@ -0,0 +1,6 @@
|
|||
[package]
|
||||
name = "tisc"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
4
README.md
Normal file
4
README.md
Normal file
|
@ -0,0 +1,4 @@
|
|||
# tisc: Tiny Stack Computer
|
||||
|
||||
Minimalistic stack-based VM with Rust interfaces to
|
||||
build & execute programs.
|
58
src/assembler.rs
Normal file
58
src/assembler.rs
Normal file
|
@ -0,0 +1,58 @@
|
|||
use {
|
||||
crate::{VM_Word, VM_Instruction}
|
||||
};
|
||||
|
||||
pub struct Assembler {
|
||||
instructions: Vec< VM_Word >
|
||||
}
|
||||
|
||||
impl Assembler {
|
||||
pub fn new() -> Self {
|
||||
Assembler {
|
||||
instructions: Vec::new()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build(mut self) -> Vec< VM_Word > {
|
||||
self.instructions.push( VM_Instruction::Ret as VM_Word );
|
||||
self.instructions
|
||||
}
|
||||
|
||||
pub fn lit(mut self, w: VM_Word) -> Assembler {
|
||||
self.instructions.push( VM_Instruction::Lit as VM_Word );
|
||||
self.instructions.push( w );
|
||||
self
|
||||
}
|
||||
|
||||
pub fn litf(mut self, wf: f64) -> Assembler {
|
||||
let w : VM_Word = unsafe{ std::mem::transmute(wf) };
|
||||
self.instructions.push( VM_Instruction::Lit as VM_Word );
|
||||
self.instructions.push( w );
|
||||
self
|
||||
}
|
||||
|
||||
pub fn instruction(mut self, i: VM_Instruction) -> Assembler {
|
||||
self.instructions.push( i as VM_Word );
|
||||
self
|
||||
}
|
||||
|
||||
pub fn call(mut self, addr: VM_Word) -> Assembler {
|
||||
self.instructions.push(VM_Instruction::Call as VM_Word);
|
||||
self.instructions.push(addr);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn branch(mut self, mut if_branch: Assembler, mut else_branch: Assembler) -> Assembler {
|
||||
self.instructions.push(VM_Instruction::Branch as VM_Word );
|
||||
self.instructions.push( if_branch.instructions.len() as VM_Word + 2);
|
||||
|
||||
self.instructions.append( &mut if_branch.instructions );
|
||||
self.instructions.push( VM_Instruction::Jmp as VM_Word );
|
||||
self.instructions.push( else_branch.instructions.len() as VM_Word);
|
||||
|
||||
self.instructions.append( &mut else_branch.instructions );
|
||||
|
||||
self
|
||||
}
|
||||
}
|
||||
|
16
src/lib.rs
Normal file
16
src/lib.rs
Normal file
|
@ -0,0 +1,16 @@
|
|||
//! Tiny Stack Computer
|
||||
//!
|
||||
//! Minimalistic stack-based VM with Rust interfaces to
|
||||
//! build & execute programs.
|
||||
|
||||
pub mod vm;
|
||||
pub mod assembler;
|
||||
pub mod linker;
|
||||
pub mod test;
|
||||
|
||||
pub use {
|
||||
vm::{VM_Instruction, VM_Word, VM},
|
||||
assembler::Assembler,
|
||||
linker::Linker
|
||||
};
|
||||
|
27
src/linker.rs
Normal file
27
src/linker.rs
Normal file
|
@ -0,0 +1,27 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
pub struct Linker {
|
||||
symbols: HashMap<String, crate::VM_Word>,
|
||||
current_addr: crate::VM_Word,
|
||||
}
|
||||
|
||||
impl Linker {
|
||||
pub fn new(start_addr: crate::VM_Word) -> Self {
|
||||
Linker {
|
||||
symbols: HashMap::new(),
|
||||
current_addr: start_addr
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resolve_symbol(&self, name: &String) -> Option< crate::VM_Word > {
|
||||
self.symbols.get(name).cloned()
|
||||
}
|
||||
|
||||
pub fn link(&mut self, vm: &mut crate::VM, symbol: String, bytecode: Vec< crate::VM_Word >) {
|
||||
self.symbols.insert(symbol, self.current_addr);
|
||||
for i in 0 .. bytecode.len() {
|
||||
vm.memory[ self.current_addr as usize ] = bytecode[i];
|
||||
self.current_addr += 1;
|
||||
}
|
||||
}
|
||||
}
|
70
src/test.rs
Normal file
70
src/test.rs
Normal file
|
@ -0,0 +1,70 @@
|
|||
use crate::{
|
||||
VM, Linker, Assembler
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn test_vm() {
|
||||
let mut vm = VM::new(0x1000);
|
||||
let mut linker = Linker::new(0x100);
|
||||
|
||||
linker.link(&mut vm, "@".into(), crate::Assembler::new()
|
||||
.instruction( crate::VM_Instruction::Fetch )
|
||||
.build());
|
||||
linker.link(&mut vm, "!".into(), crate::Assembler::new()
|
||||
.instruction( crate::VM_Instruction::Store )
|
||||
.build());
|
||||
linker.link(&mut vm, "addi".into(), crate::Assembler::new()
|
||||
.instruction( crate::VM_Instruction::Add )
|
||||
.build());
|
||||
linker.link(&mut vm, "subi".into(), crate::Assembler::new()
|
||||
.instruction( crate::VM_Instruction::BitwiseNot )
|
||||
.lit(1)
|
||||
.instruction( crate::VM_Instruction::Add )
|
||||
.instruction( crate::VM_Instruction::Add )
|
||||
.build());
|
||||
|
||||
// declare variable 'x' at address 0x86
|
||||
linker.link(&mut vm, "x".into(), crate::Assembler::new()
|
||||
.lit(0x86)
|
||||
.build());
|
||||
|
||||
|
||||
/*
|
||||
* compile & run the following program:
|
||||
*
|
||||
* x = 100 + 23;
|
||||
* if( x != 123 ) {
|
||||
* emit('*');
|
||||
* 333
|
||||
* } else {
|
||||
* emit('+');
|
||||
* 666
|
||||
* }
|
||||
*/
|
||||
linker.link(&mut vm, "main".into(), crate::Assembler::new()
|
||||
// x = 123
|
||||
.lit(100)
|
||||
.lit(23)
|
||||
.call( linker.resolve_symbol(&"addi".into()).expect("unknown symbol 'addi'") )
|
||||
.call( linker.resolve_symbol(&"x".into()).expect("unknown symbol 'x'") )
|
||||
.call( linker.resolve_symbol(&"!".into()).expect("unknown symbol '!'") )
|
||||
|
||||
// 200 - x
|
||||
.lit(123)
|
||||
.call( linker.resolve_symbol(&"x".into()).expect("unknown symbol 'x'") )
|
||||
.call( linker.resolve_symbol(&"@".into()).expect("unknown symbol '@'") )
|
||||
.call( linker.resolve_symbol(&"subi".into()).expect("unknown symbol 'subi'") )
|
||||
.branch(
|
||||
crate::Assembler::new()
|
||||
.lit(42).instruction(crate::VM_Instruction::Emit)
|
||||
.lit(111),
|
||||
crate::Assembler::new()
|
||||
.lit(43).instruction(crate::VM_Instruction::Emit)
|
||||
.lit(222)
|
||||
)
|
||||
.build());
|
||||
|
||||
vm.execute( linker.resolve_symbol(&"main".into()).expect("unknown symbol 'main'") );
|
||||
assert_eq!( vm.data_stack, vec![ 222 ] );
|
||||
println!("\nvm.datastack = {:?}", vm.data_stack);
|
||||
}
|
152
src/vm.rs
Normal file
152
src/vm.rs
Normal file
|
@ -0,0 +1,152 @@
|
|||
pub type VM_Word = i64;
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[repr(u64)]
|
||||
pub enum VM_Instruction {
|
||||
Nop, Jmp, Call, Branch, Ret,
|
||||
Lit, Dup, Drop, Swap, Rot,
|
||||
Fetch, Store,
|
||||
Accept, Emit,
|
||||
Add, //Sub, Mul, Div, Rem,
|
||||
Addf,//Subf, Mulf, Divf
|
||||
BitwiseNot,
|
||||
}
|
||||
|
||||
pub struct VM {
|
||||
pub data_stack: Vec< VM_Word >,
|
||||
pub call_stack: Vec< VM_Word >,
|
||||
|
||||
pub inst_ptr: VM_Word,
|
||||
pub memory: Vec< VM_Word >,
|
||||
}
|
||||
|
||||
impl VM {
|
||||
pub fn new(memsize: usize) -> Self {
|
||||
VM {
|
||||
data_stack: Vec::new(),
|
||||
call_stack: Vec::new(),
|
||||
|
||||
inst_ptr: 0,
|
||||
memory: vec![0; memsize]
|
||||
}
|
||||
}
|
||||
|
||||
pub fn execute(&mut self, entry: VM_Word) {
|
||||
self.inst_ptr = entry;
|
||||
while self.execute_step() {}
|
||||
}
|
||||
|
||||
pub fn execute_step(&mut self) -> bool {
|
||||
// fetch instruction from memory
|
||||
let inst = unsafe { std::mem::transmute(self.memory[ self.inst_ptr as usize ]) };
|
||||
self.inst_ptr += 1;
|
||||
|
||||
// decode instruction & load immediate if neccesary
|
||||
let mut imm : VM_Word = 0;
|
||||
match inst {
|
||||
VM_Instruction::Jmp |
|
||||
VM_Instruction::Call |
|
||||
VM_Instruction::Branch |
|
||||
VM_Instruction::Lit => {
|
||||
imm = self.memory[ self.inst_ptr as usize ];
|
||||
self.inst_ptr += 1;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// execute instruction
|
||||
self.execute_instruction( inst, imm )
|
||||
}
|
||||
|
||||
pub fn execute_instruction(&mut self, inst: VM_Instruction, imm: VM_Word) -> bool {
|
||||
// eprintln!("execute {:?}, {}", inst, imm);
|
||||
match inst {
|
||||
VM_Instruction::Nop => {}
|
||||
VM_Instruction::Lit => {
|
||||
self.data_stack.push( imm );
|
||||
}
|
||||
VM_Instruction::Jmp => {
|
||||
self.inst_ptr += imm;
|
||||
}
|
||||
VM_Instruction::Call => {
|
||||
self.call_stack.push( self.inst_ptr );
|
||||
self.inst_ptr = imm;
|
||||
}
|
||||
VM_Instruction::Branch => {
|
||||
if self.data_stack.pop().expect("branch expects val") == 0 {
|
||||
//self.call_stack.push( self.inst_ptr );
|
||||
self.inst_ptr += imm;
|
||||
}
|
||||
}
|
||||
VM_Instruction::Ret => {
|
||||
if let Some(target) = self.call_stack.pop() {
|
||||
self.inst_ptr = target;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
VM_Instruction::Fetch => {
|
||||
let addr = self.data_stack.pop().expect("fetch requries addr");
|
||||
let val = self.memory[ addr as usize ];
|
||||
self.data_stack.push(val);
|
||||
}
|
||||
VM_Instruction::Store => {
|
||||
let addr = self.data_stack.pop().expect("store requires addr");
|
||||
let val = self.data_stack.pop().expect("store requires val");
|
||||
self.memory[ addr as usize ] = val;
|
||||
}
|
||||
VM_Instruction::Accept => {
|
||||
// todo: read word from stdin
|
||||
self.data_stack.push(0);
|
||||
}
|
||||
VM_Instruction::Emit => {
|
||||
let val = char::from(self.data_stack.pop().expect("emit expects value") as u8);
|
||||
print!("{}", val);
|
||||
}
|
||||
VM_Instruction::Dup => {
|
||||
self.data_stack.push( *self.data_stack.last().expect("dup expect val") );
|
||||
}
|
||||
VM_Instruction::Drop => {
|
||||
self.data_stack.pop().expect("drop expects value");
|
||||
}
|
||||
VM_Instruction::Swap => {
|
||||
let a = self.data_stack.pop().expect("swap expects value");
|
||||
let b = self.data_stack.pop().expect("swap expects value");
|
||||
self.data_stack.push(a);
|
||||
self.data_stack.push(b);
|
||||
}
|
||||
VM_Instruction::Rot => {
|
||||
let a = self.data_stack.pop().expect("rot expects value");
|
||||
let b = self.data_stack.pop().expect("rot expects value");
|
||||
let c = self.data_stack.pop().expect("rot expects value");
|
||||
self.data_stack.push(a);
|
||||
self.data_stack.push(c);
|
||||
self.data_stack.push(b);
|
||||
}
|
||||
VM_Instruction::Add => {
|
||||
let a = self.data_stack.pop().expect("add expects value");
|
||||
let b = self.data_stack.pop().expect("add expects value");
|
||||
self.data_stack.push( a + b );
|
||||
}
|
||||
VM_Instruction::Addf => {
|
||||
let a = self.data_stack.pop().expect("addf expects value");
|
||||
let af : f64 = unsafe{ std::mem::transmute(a) };
|
||||
|
||||
let b = self.data_stack.pop().expect("addf expects value");
|
||||
let bf : f64 = unsafe{ std::mem::transmute(a) };
|
||||
|
||||
let cf = af + bf;
|
||||
let c : i64 = unsafe{ std::mem::transmute(cf) };
|
||||
|
||||
self.data_stack.push(c);
|
||||
}
|
||||
VM_Instruction::BitwiseNot => {
|
||||
let a = self.data_stack.pop().expect("bitwise not expects value");
|
||||
self.data_stack.push( !a );
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in a new issue