Skip to content

Instantly share code, notes, and snippets.

@oatberry
Created April 10, 2018 15:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save oatberry/6ee56a7642b9f5e698f2a7548ce18dd6 to your computer and use it in GitHub Desktop.
Save oatberry/6ee56a7642b9f5e698f2a7548ce18dd6 to your computer and use it in GitHub Desktop.
extern crate time;
mod ops;
mod vm;
use vm::*;
use std::env;
use std::error::Error;
use std::fs::File;
use std::io::{self, Read, BufReader, BufWriter};
use std::process;
use time::PreciseTime;
fn usage() {
eprintln!("usage: bvm <file>");
process::exit(1);
}
fn main() {
let args: Vec<String> = env::args().collect();
if args.len() != 2 {
usage()
}
let mut bytecode: Vec<u8> = match read_file(&args[1]) {
Ok(code) => code,
Err(why) => {
eprintln!("error: {}", why);
process::exit(1);
}
};
if bytecode.len() < 8 {
eprintln!("error: malformed header");
process::exit(1);
}
let header: &[u32] = unsafe {
std::mem::transmute(std::slice::from_raw_parts(
bytecode[0..8].as_ptr(),
bytecode[0..8].len() / std::mem::size_of::<u32>()
))
};
if header[0] != 0x4d564c52 {
eprintln!("error: file does not appear to be rlvm bytecode (incorrect header)");
process::exit(1);
}
let stack_size = header[1] as usize;
let mut data: Stack = Vec::with_capacity(stack_size + 1024);
data.extend_from_slice(&bytecode[8..(8 + stack_size)]);
bytecode.drain(0..(8 + stack_size)); // drop header and stack
let mut io_bundle = IO {
stdin: BufReader::new(io::stdin()),
stdout: BufWriter::new(io::stdout()),
stderr: BufWriter::new(io::stderr()),
};
let mut program = Program::new(bytecode);
let ops = ops::gen_ops();
let start = PreciseTime::now();
while program.ip < program.len {
let op = program.fetch() as usize;
ops[op](&mut program, &mut data, &mut io_bundle);
program.inc();
}
let end = PreciseTime::now();
io_bundle.flush_all();
println!("{} seconds to execute", start.to(end));
}
fn read_file(path: &str) -> Result<Vec<u8>, Box<Error>> {
let mut contents: Vec<u8> = Vec::new();
let mut file = File::open(path)?;
file.read_to_end(&mut contents)?;
Ok(contents)
}
use super::vm::*;
use std::io::{Read, Write};
use std::process;
pub fn gen_ops() -> Vec<fn(&mut Program, &mut Stack, &mut IO)> {
let mut ops: Vec<fn(&mut Program, &mut Stack, &mut IO)> = vec![op_nop; 256];
ops[0x01] = op_push;
ops[0x02] = op_print;
ops[0x03] = op_print_range;
ops[0x04] = op_read;
ops[0x05] = op_read_range;
ops[0xff] = op_halt;
ops
}
fn op_push(prg: &mut Program, stack: &mut Stack, _: &mut IO) {
let c: u8 = prg.fetch_next();
stack.push(c);
println!("PUSH_U8 0x{:02x}", c);
}
fn op_nop(prg: &mut Program, _: &mut Stack, _: &mut IO) {
println!("NOP");
prg.inc();
}
fn op_print(_: &mut Program, stack: &mut Stack, io: &mut IO) {
println!("PRINT");
let c: u8 = stack.pop().unwrap();
io.stdout.write(&[c]).unwrap();
}
fn op_read(_: &mut Program, stack: &mut Stack, io: &mut IO) {
println!("READ");
let mut buf: Vec<u8> = Vec::new();
let read = io.stdin.read_to_end(&mut buf).unwrap();
stack.extend_from_slice(&buf);
stack.push(read as u8);
}
fn op_read_range(_: &mut Program, stack: &mut Stack, io: &mut IO) {
println!("READ_R");
let num = stack.pop().unwrap();
let mut buf = Vec::with_capacity(num as usize);
io.stdin.read_exact(&mut buf).unwrap();
stack.extend_from_slice(&buf);
}
fn op_print_range(_: &mut Program, stack: &mut Stack, io: &mut IO) {
let num = stack.pop().unwrap();
println!("PRINT_R 0x{:02x}", num);
let len = stack.len();
let bytes: Vec<u8> = stack.drain((len - num as usize)..len).collect();
io.stdout.write(&bytes).unwrap();
}
fn op_halt(_: &mut Program, _: &mut Stack, io: &mut IO) {
println!("HALT");
io.flush_all();
process::exit(0);
}
use std::io::{self, Write};
use std::io::{BufReader, BufWriter};
pub type Stack = Vec<u8>;
pub struct Program {
pub ip: usize,
pub bytes: Vec<u8>,
pub len: usize,
}
impl Program {
pub fn new(bytes: Vec<u8>) -> Program {
Program {
ip: 0,
len: bytes.len(),
bytes,
}
}
pub fn inc(&mut self) {
self.ip += 1;
}
pub fn fetch(&self) -> u8 {
self.bytes[self.ip as usize]
}
pub fn fetch_next(&mut self) -> u8 {
self.ip += 1;
self.bytes[self.ip as usize]
}
}
pub struct IO {
pub stdin: BufReader<io::Stdin>,
pub stdout: BufWriter<io::Stdout>,
pub stderr: BufWriter<io::Stderr>,
}
impl IO {
pub fn flush_all(&mut self) {
let _ = self.stdout.flush();
let _ = self.stderr.flush();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment