Created
April 10, 2018 15:27
-
-
Save oatberry/6ee56a7642b9f5e698f2a7548ce18dd6 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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