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

127
src/vm/tick.rs Normal file
View File

@@ -0,0 +1,127 @@
use crate::vm::{error::*, flags::Flags, inst::*, reg::*, vm::*};
impl Vm {
pub fn tick(&mut self) -> Result<()> {
let mut cursor = self.mem_cursor(self.ip() as usize);
let op = cursor.next_u16()?;
let next_ip;
macro_rules! math_inst {
($mapping:expr) => {{
let (r1, r2) = cursor.next_regs()?;
next_ip = cursor.position();
let value = ($mapping)(self.get_reg(r1), self.get_reg(r2));
self.set_reg(r1, value);
}}
}
match op {
ADD => math_inst!(|w1: u64, w2: u64| w1.wrapping_add(w2)),
MUL => math_inst!(|w1: u64, w2: u64| w1.wrapping_mul(w2)),
DIV => math_inst!(|w1: u64, w2: u64| w1.wrapping_div(w2)),
MOD => math_inst!(|w1: u64, w2: u64| w1 % w2),
INEG => { todo!() }
AND => math_inst!(|w1: u64, w2: u64| w1 & w2),
OR => math_inst!(|w1: u64, w2: u64| w1 | w2),
INV => {
let r1 = cursor.next_reg()?;
next_ip = cursor.position();
let value = self.get_reg(r1);
self.set_reg(r1, !value);
}
NOT => {
let r1 = cursor.next_reg()?;
next_ip = cursor.position();
let value = self.get_reg(r1);
self.set_reg(r1, (value == 0) as Word);
}
XOR => math_inst!(|w1: u64, w2: u64| w1 ^ w2),
SHL => math_inst!(|w1: u64, w2: u64| w1 << w2),
SHR => math_inst!(|w1: u64, w2: u64| w1 >> w2),
CMPEQ => {
let (r1, r2) = cursor.next_regs()?;
next_ip = cursor.position();
let cmp = self.get_reg(r1) == self.get_reg(r2);
if cmp {
self.insert_flags(Flags::COMPARE);
} else {
self.remove_flags(Flags::COMPARE);
}
}
CMPLT => {
let (r1, r2) = cursor.next_regs()?;
next_ip = cursor.position();
let cmp = self.get_reg(r1) < self.get_reg(r2);
if cmp {
self.insert_flags(Flags::COMPARE);
} else {
self.remove_flags(Flags::COMPARE);
}
}
JMP => {
let r1 = cursor.next_reg()?;
next_ip = self.get_reg(r1);
}
JZ => {
let r1 = cursor.next_reg()?;
if !self.flags().contains(Flags::COMPARE) {
next_ip = self.get_reg(r1);
} else {
next_ip = cursor.position();
}
}
JNZ => {
let r1 = cursor.next_reg()?;
if self.flags().contains(Flags::COMPARE) {
next_ip = self.get_reg(r1);
} else {
next_ip = cursor.position();
}
}
LOAD => {
let (r1, r2) = cursor.next_regs()?;
next_ip = cursor.position();
let value = self.load(r2);
self.set_reg(r1, value);
}
REGCOPY => math_inst!(|_: u64, w2: u64| w2),
STOREIMM64 => {
let r1 = cursor.next_reg()?;
// skip
cursor.next_u32()?;
let imm = cursor.next_u64()?;
next_ip = cursor.position();
self.set_reg(r1, imm);
}
STOREIMM32 => {
let r1 = cursor.next_reg()?;
let imm = cursor.next_u32()?;
next_ip = cursor.position();
self.set_reg(r1, imm as u64);
}
MEMCOPY => {
let (r1, r2) = cursor.next_regs()?;
next_ip = cursor.position();
let value = self.load(r2);
self.store(r1, value);
}
STORE => {
let (r1, r2) = cursor.next_regs()?;
next_ip = cursor.position();
let value = self.get_reg(r1);
self.store(r2, value);
}
HALT => {
next_ip = cursor.position();
self.insert_flags(Flags::HALT);
}
NOP => {
next_ip = cursor.position();
}
_ => panic!("unknown instruction opcode: 0x{:04x}", op),
}
self.set_reg(IP, next_ip);
Ok(())
}
}