initial commit

This commit is contained in:
Michael Sippel 2024-05-04 18:11:13 +02:00
commit 92fc7ff44b
Signed by: senvas
GPG key ID: F96CF119C34B64A6
8 changed files with 335 additions and 0 deletions

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
./target
*~

6
Cargo.toml Normal file
View file

@ -0,0 +1,6 @@
[package]
name = "tisc"
version = "0.1.0"
edition = "2021"
[dependencies]

4
README.md Normal file
View 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
View 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
View 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
View 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
View 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
View 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
}
}