Add object layout, object parsing, instruction layout

* Object layout and parsing are done in the vm::obj module
* Add MemCursor, a wrapper around the std::io::Cursor type for walking
  through VM memory
* Add vm::tick module for containing the Vm::tick() method
  implementation, since it's pretty big
* Instructions are now variable-sized, and are read lazily,
  one-at-a-time directly from memory.
* Add VM runtime error structure
* Probably some other stuff I forgot

Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
This commit is contained in:
2020-02-03 17:56:32 -05:00
parent 47ee61ca0d
commit 214f0b8aed
12 changed files with 554 additions and 502 deletions

View File

@@ -1,41 +1,32 @@
use crate::vm::{
reg::*,
inst::*,
flags::*,
};
use byteorder::{LE, ReadBytesExt, WriteBytesExt};
use std::{
mem,
io::Cursor,
};
use crate::vm::{error::*, flags::*, mem::*, reg::*};
use byteorder::{ReadBytesExt, WriteBytesExt, LE};
use std::{io::Cursor, mem};
pub type Word = u64;
pub type HalfWord = u32;
pub type Registers = [Word; 64];
pub type Addr = u64;
pub struct Vm {
code: Vec<Inst>,
memory: Vec<u8>,
registers: Registers,
pub(super) mem: Vec<u8>,
pub(super) registers: Registers,
}
#[allow(dead_code)]
impl Vm {
pub fn new(code: Vec<Inst>, memory: Vec<u8>, registers: Registers) -> Self {
Vm { code, memory, registers }
pub fn new(mem: Vec<u8>, registers: Registers) -> Self {
Vm { mem, registers }
}
pub fn run(&mut self) {
pub fn mem_cursor(&self, at: usize) -> MemCursor {
// TODO : MemCursor
MemCursor::new(&self.mem[at..])
}
pub fn run(&mut self) -> Result<u64> {
while !self.is_halted() {
self.tick();
self.tick()?;
}
}
pub fn tick(&mut self) {
let inst = self.decode();
let ip = self.ip();
self.set_reg(IP, ip + 1);
self.execute(inst);
Ok(self.get_reg(STATUS))
}
pub fn resume(&mut self) {
@@ -47,38 +38,42 @@ impl Vm {
}
pub fn get_word(&self, addr: Word) -> Word {
let mut reader = Cursor::new(&self.memory[(addr as usize)..]);
reader.read_u64::<LE>()
let mut reader = Cursor::new(&self.mem[(addr as usize)..]);
reader
.read_u64::<LE>()
.expect("word outside of address range")
}
pub fn get_halfword(&self, addr: Word) -> HalfWord {
let mut reader = Cursor::new(&self.memory[(addr as usize)..]);
reader.read_u32::<LE>()
let mut reader = Cursor::new(&self.mem[(addr as usize)..]);
reader
.read_u32::<LE>()
.expect("word outside of address range")
}
pub fn get_byte(&self, addr: Word) -> u8 {
let mut reader = Cursor::new(&self.memory[(addr as usize)..]);
reader.read_u8()
.expect("word outside of address range")
let mut reader = Cursor::new(&self.mem[(addr as usize)..]);
reader.read_u8().expect("word outside of address range")
}
pub fn set_word(&mut self, addr: Word, value: Word) {
let mut writer = Cursor::new(&mut self.memory[(addr as usize)..]);
writer.write_u64::<LE>(value)
let mut writer = Cursor::new(&mut self.mem[(addr as usize)..]);
writer
.write_u64::<LE>(value)
.expect("word outside of address range");
}
pub fn set_halfword(&mut self, addr: Word, value: HalfWord) {
let mut writer = Cursor::new(&mut self.memory[(addr as usize)..]);
writer.write_u32::<LE>(value)
let mut writer = Cursor::new(&mut self.mem[(addr as usize)..]);
writer
.write_u32::<LE>(value)
.expect("word outside of address range");
}
pub fn set_byte(&mut self, addr: Word, value: u8) {
let mut writer = Cursor::new(&mut self.memory[(addr as usize)..]);
writer.write_u8(value)
let mut writer = Cursor::new(&mut self.mem[(addr as usize)..]);
writer
.write_u8(value)
.expect("word outside of address range");
}
@@ -108,7 +103,7 @@ impl Vm {
// anyway, but if it does, they're ignored
unsafe { Flags::from_bits_unchecked(self.get_reg(FLAGS)) }
}
pub fn insert_flags(&mut self, flags: Flags) {
let mut new_flags = self.flags();
new_flags.insert(flags);
@@ -124,201 +119,4 @@ impl Vm {
pub fn set_flags(&mut self, flags: Flags) {
self.set_reg(FLAGS, flags.bits());
}
pub fn map_flags<F>(&mut self, mapping: F)
where F: FnOnce(Flags) -> Flags
{
let in_flags = self.flags();
self.set_flags((mapping)(in_flags));
}
pub fn decode(&self) -> Inst {
self.code[self.ip() as usize]
}
pub fn execute(&mut self, inst: Inst) {
macro_rules! map {
($r0:ident = $($tail:tt)*) => {{
let w0 = map!($($tail)+);
self.set_reg($r0, w0);
}};
($r1:ident $sym:tt $r2:ident) => {{
let w1 = self.get_reg($r1);
let w2 = self.get_reg($r2);
(w1 $sym w2)
}}
}
match inst {
Inst::Add(r1, r2) => {
let w1 = self.get_reg(r1);
let w2 = self.get_reg(r2);
let value = w1.wrapping_add(w2);
self.set_reg(r1, value);
}
Inst::Mul(r1, r2) => {
let w1 = self.get_reg(r1);
let w2 = self.get_reg(r2);
let value = w1.wrapping_mul(w2);
self.set_reg(r1, value);
}
Inst::Div(r1, r2) => map!(r1 = r1 / r2),
Inst::Mod(r1, r2) => map!(r1 = r1 % r2),
Inst::INeg(r1) => {
let value = self.get_reg(r1);
let ivalue = -i64::from_ne_bytes(value.to_ne_bytes());
let uvalue = u64::from_ne_bytes(ivalue.to_ne_bytes());
self.set_reg(r1, uvalue);
}
Inst::And(r1, r2) => map!(r1 = r1 & r2),
Inst::Or(r1, r2) => map!(r1 = r1 | r2),
Inst::Xor(r1, r2) => map!(r1 = r1 ^ r2),
Inst::Shl(r1, r2) => map!(r1 = r1 << r2),
Inst::Shr(r1, r2) => map!(r1 = r1 >> r2),
Inst::CmpEq(r1, r2) => {
if map!(r1 == r2) {
self.insert_flags(Flags::COMPARE);
} else {
self.remove_flags(Flags::COMPARE);
}
}
Inst::CmpLt(r1, r2) => {
if map!(r1 < r2) {
self.insert_flags(Flags::COMPARE);
} else {
self.remove_flags(Flags::COMPARE);
}
}
Inst::Jmp(r1) => {
let w1 = self.get_reg(r1);
self.set_reg(IP, w1);
}
Inst::Jz(r1) => {
if !self.flags().contains(Flags::COMPARE) {
let w1 = self.get_reg(r1);
self.set_reg(IP, w1);
}
}
Inst::Jnz(r1) => {
if self.flags().contains(Flags::COMPARE) {
let w1 = self.get_reg(r1);
self.set_reg(IP, w1);
}
}
Inst::Load(r1, r2) => {
let value = self.load(r2);
self.set_reg(r1, value);
}
Inst::Store(r1, r2) => {
let value = self.get_reg(r1);
self.store(r2, value);
}
Inst::StoreImm(r1, value) => {
self.set_reg(r1, value as u64);
}
Inst::RegCopy(r1, r2) => {
let w1 = self.get_reg(r2);
self.set_reg(r1, w1);
}
Inst::MemCopy(r1, r2) => {
let w1 = self.load(r2);
self.store(r1, w1);
}
Inst::Nop => {}
Inst::Halt => {
self.insert_flags(Flags::HALT);
}
}
}
fn with_registers<F, B>(&self, r1: Reg, r2: Reg, map_fn: F) -> B
where F: FnOnce(Word, Word) -> B
{
let w1 = self.get_reg(r1);
let w2 = self.get_reg(r2);
(map_fn)(w1, w2)
}
}
#[cfg(test)]
mod test {
use super::*;
macro_rules! itou {
{ $expr:expr } => { u64::from_ne_bytes(($expr).to_ne_bytes()) };
}
#[test]
fn test_arithmetic() {
use Inst::*;
use Reg::*;
let code = vec![
StoreImm(R01, 25),
Add(R00, R01),
Halt,
Mul(R00, R01),
Halt,
Div(R00, R01),
Halt,
StoreImm(R01, 15),
And(R00, R01),
Halt, // should be 9
// Storing large numbers
StoreImm(R00, 0xABCD_EF98),
StoreImm(R01, 32),
Shl(R00, R01),
Or(R00, R01),
Halt,
Shr(R00, R01),
Halt,
Xor(R00, R00),
Halt,
];
let mut vm = Vm::new(code, Default::default(), [0; 64]);
vm.run();
assert_eq!(vm.get_reg(R00), 25);
assert_eq!(vm.get_reg(R01), 25);
vm.resume();
vm.run();
assert_eq!(vm.get_reg(R00), 625);
assert_eq!(vm.get_reg(R01), 25);
vm.resume();
vm.run();
assert_eq!(vm.get_reg(R00), 25);
assert_eq!(vm.get_reg(R01), 25);
vm.resume();
vm.run();
assert_eq!(vm.get_reg(R00), 9);
assert_eq!(vm.get_reg(R01), 15);
vm.resume();
vm.run();
assert_eq!(vm.get_reg(R00), 0xABCD_EF98_0000_0020);
assert_eq!(vm.get_reg(R01), 32);
vm.resume();
// TODO : signed instructions
vm.run();
assert_eq!(vm.get_reg(R00), 0xABCD_EF98);
vm.resume();
vm.run();
assert_eq!(vm.get_reg(R00), 0);
vm.resume();
}
}