From 214f0b8aed8b46ea45380a0709dc98bad1872bbc Mon Sep 17 00:00:00 2001 From: Alek Ratzloff Date: Mon, 3 Feb 2020 17:56:32 -0500 Subject: [PATCH] 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 --- src/main.rs | 2 + src/vm/error.rs | 9 ++ src/vm/inst.rs | 66 ++++++----- src/vm/mem.rs | 80 +++++++++++++ src/vm/mod.rs | 3 + src/vm/obj/error.rs | 42 +++++++ src/vm/obj/mod.rs | 237 +------------------------------------ src/vm/obj/obj.rs | 107 +++++++++++++++++ src/vm/obj/parser.rs | 108 +++++++++++++++++ src/vm/reg.rs | 5 +- src/vm/tick.rs | 127 ++++++++++++++++++++ src/vm/vm.rs | 270 ++++++------------------------------------- 12 files changed, 554 insertions(+), 502 deletions(-) create mode 100644 src/vm/error.rs create mode 100644 src/vm/mem.rs create mode 100644 src/vm/obj/error.rs create mode 100644 src/vm/obj/obj.rs create mode 100644 src/vm/obj/parser.rs create mode 100644 src/vm/tick.rs diff --git a/src/main.rs b/src/main.rs index 818e7e0..2d9d174 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,5 @@ +#![allow(dead_code)] + mod common; //mod syn; mod vm; diff --git a/src/vm/error.rs b/src/vm/error.rs new file mode 100644 index 0000000..5b9898e --- /dev/null +++ b/src/vm/error.rs @@ -0,0 +1,9 @@ +use snafu::Snafu; +use crate::vm::vm::*; + +#[derive(Snafu, Debug, Clone)] +pub enum VmError { + MemOutOfBounds { addr: Addr, } +} + +pub type Result = std::result::Result; diff --git a/src/vm/inst.rs b/src/vm/inst.rs index b888a4c..8f6f515 100644 --- a/src/vm/inst.rs +++ b/src/vm/inst.rs @@ -1,33 +1,39 @@ -use crate::vm::reg::*; +macro_rules! instructions { + { + $($variant:ident = $value:expr),* $(,)? + } => { + pub type InstOp = u16; -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -#[allow(dead_code)] -pub enum Inst { - Add(Reg, Reg), - Mul(Reg, Reg), - Div(Reg, Reg), - Mod(Reg, Reg), - INeg(Reg), - And(Reg, Reg), - Or(Reg, Reg), - Xor(Reg, Reg), - Shl(Reg, Reg), - Shr(Reg, Reg), - - CmpEq(Reg, Reg), - CmpLt(Reg, Reg), - Jmp(Reg), - Jz(Reg), - Jnz(Reg), - - Load(Reg, Reg), - Store(Reg, Reg), - StoreImm(Reg, u64), - MemCopy(Reg, Reg), - RegCopy(Reg, Reg), - - Nop, - Halt, + $( + pub const $variant: InstOp = $value; + )* + }; } -// https://crates.io/crates/packed_struct +instructions! { + ADD = 0x0000, + MUL = 0x0001, + DIV = 0x0002, + MOD = 0x0003, + INEG = 0x0004, + AND = 0x0005, + OR = 0x0006, + INV = 0x0007, + NOT = 0x0008, + XOR = 0x0009, + SHL = 0x000a, + SHR = 0x000b, + CMPEQ = 0x1000, + CMPLT = 0x1001, + JMP = 0x1100, + JZ = 0x1101, + JNZ = 0x1102, + LOAD = 0x2000, + REGCOPY = 0x2001, + STOREIMM64 = 0x2100, + STOREIMM32 = 0x2101, + MEMCOPY = 0x2200, + STORE = 0x2201, + HALT = 0xF000, + NOP = 0xF001, +} diff --git a/src/vm/mem.rs b/src/vm/mem.rs new file mode 100644 index 0000000..e5d6efe --- /dev/null +++ b/src/vm/mem.rs @@ -0,0 +1,80 @@ +use crate::vm::{error::*, reg::*}; +use byteorder::{LE, ReadBytesExt}; +use std::{ + ops::{Deref, DerefMut}, + io::Cursor, +}; + +const R1_MASK: u16 = 0b1111_1100_0000_0000; +const R2_MASK: u16 = 0b0000_0011_1111_0000; + +pub struct MemCursor<'mem> { + cursor: Cursor<&'mem [u8]>, +} + +impl<'mem> MemCursor<'mem> { + pub fn new(mem: &'mem [u8]) -> Self { + MemCursor { + cursor: Cursor::new(mem) + } + } + + pub fn cursor(&self) -> &Cursor<&'mem [u8]> { + &self.cursor + } + + pub fn cursor_mut(&mut self) -> &mut Cursor<&'mem [u8]> { + &mut self.cursor + } + + pub fn next_u8(&mut self) -> Result { + self.read_u8().map_err(|_| VmError::MemOutOfBounds { + addr: self.position(), + }) + } + + pub fn next_u16(&mut self) -> Result { + self.read_u16::().map_err(|_| VmError::MemOutOfBounds { + addr: self.position(), + }) + } + + pub fn next_u32(&mut self) -> Result { + self.read_u32::().map_err(|_| VmError::MemOutOfBounds { + addr: self.position(), + }) + } + + pub fn next_u64(&mut self) -> Result { + self.read_u64::().map_err(|_| VmError::MemOutOfBounds { + addr: self.position(), + }) + } + + pub fn next_regs(&mut self) -> Result<(Reg, Reg)> { + let next16 = self.next_u16()?; + let r1 = ((R1_MASK & next16) >> 10) as Reg; + let r2 = ((R2_MASK & next16) >> 4) as Reg; + Ok((r1, r2)) + } + + pub fn next_reg(&mut self) -> Result { + let next16 = self.next_u16()?; + let r1 = ((R1_MASK & next16) >> 10) as Reg; + Ok(r1) + } +} + +impl<'mem> Deref for MemCursor<'mem> { + type Target = Cursor<&'mem [u8]>; + + fn deref(&self) -> &Self::Target { + self.cursor() + } +} + +impl<'mem> DerefMut for MemCursor<'mem> { + fn deref_mut(&mut self) -> &mut Self::Target { + self.cursor_mut() + } +} diff --git a/src/vm/mod.rs b/src/vm/mod.rs index 9134539..3cfc6c4 100644 --- a/src/vm/mod.rs +++ b/src/vm/mod.rs @@ -1,6 +1,9 @@ +pub mod error; pub mod flags; pub mod inst; +pub mod mem; pub mod obj; pub mod reg; pub mod syn; +mod tick; pub mod vm; diff --git a/src/vm/obj/error.rs b/src/vm/obj/error.rs new file mode 100644 index 0000000..fcec7a9 --- /dev/null +++ b/src/vm/obj/error.rs @@ -0,0 +1,42 @@ +use snafu::Snafu; +use std::{ + fmt::Debug, + io, +}; + +#[derive(Debug, Snafu)] +pub enum ParseError { + #[snafu(display("IO error: {}", source))] + Io { source: io::Error }, + + #[snafu(display("wrong magic number"))] + WrongMagic, + + #[snafu(display("unknown section kind: 0x{:02x}", kind))] + UnknownSectionKind { kind: u8 }, + + #[snafu(display("invalid UTF-8 string: {}", source))] + InvalidUtf8String { source: std::string::FromUtf8Error }, +} + +macro_rules! into_parse_error { + ( + $($type:ty : $variant:ident),* $(,)? + ) => { + $( + impl From<$type> for ParseError { + fn from(other: $type) -> Self { + ParseError::$variant { source: other } + } + } + )* + } +} + +into_parse_error! { + io::Error: Io, + std::string::FromUtf8Error: InvalidUtf8String, +} + +pub type Result = std::result::Result; + diff --git a/src/vm/obj/mod.rs b/src/vm/obj/mod.rs index f4787f1..4c5ad61 100644 --- a/src/vm/obj/mod.rs +++ b/src/vm/obj/mod.rs @@ -1,234 +1,3 @@ -use crate::vm::inst::Inst; -use byteorder::{ReadBytesExt, LE}; -use snafu::{ensure, Snafu}; -use std::{ - convert::{TryFrom, TryInto}, - fmt::Debug, - io::{self, Cursor, Read}, -}; - -pub const MAGIC: u64 = 0xDEAD_BEA7_BA5E_BA11; - -#[derive(Debug)] -pub struct Object { - header: Header, - sections: Vec>, -} - -#[derive(Debug, Clone, Copy)] -pub struct Header { - version: u16, - sections: u16, -} - -macro_rules! section_kind { - ( - pub enum $enum_name:ident { - $($name:ident = $value:expr),* $(,)? - } - ) => { - - #[derive(Debug, Clone, Copy)] - #[repr(u8)] - pub enum $enum_name { - $($name = $value),* - } - - impl TryFrom for SectionKind { - type Error = ParseError; - - fn try_from(other: u8) -> std::result::Result { - match other { - $( - $value => Ok($enum_name::$name), - )* - _ => Err(ParseError::UnknownSectionKind { kind: other }), - } - } - } - }; -} - -section_kind! { - pub enum SectionKind { - Data = 0x00, - Code = 0x10, - Meta = 0xFF, - } -} - -pub trait Section: Debug { - fn header(&self) -> SectionHeader; -} - -#[derive(Debug, Clone, Copy)] -pub struct SectionHeader { - kind: SectionKind, - checksum: u32, - len: u64, -} - -#[derive(Debug, Clone)] -pub struct DataSection { - header: SectionHeader, - load_location: u64, - contents: Vec, -} - -impl Section for DataSection { - fn header(&self) -> SectionHeader { - self.header - } -} - -#[derive(Debug, Clone)] -pub struct CodeSection { - header: SectionHeader, - load_location: u64, - contents: Vec, -} - -impl Section for CodeSection { - fn header(&self) -> SectionHeader { - self.header - } -} - -#[derive(Debug, Clone)] -pub struct MetaSection { - header: SectionHeader, - entry_count: u64, - entries: Vec<(String, Vec)>, -} - -impl Section for MetaSection { - fn header(&self) -> SectionHeader { - self.header - } -} - -#[derive(Debug, Clone)] -pub struct ObjectParser { - bytes: Cursor>, -} - -impl ObjectParser { - pub fn parse(&mut self) -> Result { - let header = self.parse_header()?; - let sections = self.parse_sections(header)?; - Ok(Object { header, sections }) - } - - fn parse_header(&mut self) -> Result
{ - let magic = self.bytes.read_u64::()?; - ensure!(magic == MAGIC, WrongMagic); - let version = self.bytes.read_u16::()?; - let sections = self.bytes.read_u16::()?; - Ok(Header { version, sections }) - } - - fn parse_sections(&mut self, header: Header) -> Result>> { - (0..header.sections) - .map(|_| self.parse_section()) - .collect() - } - - fn parse_section(&mut self) -> Result> { - let header = self.parse_section_header()?; - let section: Box = match header.kind { - SectionKind::Data => self.parse_data_section(header).map(Box::new)?, - SectionKind::Code => self.parse_code_section(header).map(Box::new)?, - SectionKind::Meta => self.parse_meta_section(header).map(Box::new)?, - }; - Ok(section) - } - - fn parse_section_header(&mut self) -> Result { - let kind: SectionKind = self.bytes.read_u8()?.try_into()?; - let checksum = self.bytes.read_u32::()?; - let len = self.bytes.read_u64::()?; - Ok(SectionHeader { - kind, - checksum, - len, - }) - } - - fn parse_data_section(&mut self, header: SectionHeader) -> Result { - let load_location = self.bytes.read_u64::()?; - let contents = self.take_bytes(header.len)?; - Ok(DataSection { - header, - load_location, - contents, - }) - } - - fn parse_code_section(&mut self, _header: SectionHeader) -> Result { - let _load_location = self.bytes.read_u64::()?; - todo!("instruction parsing") - } - - fn parse_meta_section(&mut self, header: SectionHeader) -> Result { - let entry_count = self.bytes.read_u64::()?; - let mut entries = Vec::with_capacity(entry_count as usize); - for offset in 0 .. entry_count { - let key_bytes = self.parse_sized_data()?; - let key = String::from_utf8(key_bytes)?; - let value = self.parse_sized_data()?; - entries.push((key, value)); - } - Ok(MetaSection { - header, - entry_count, - entries, - }) - } - - fn parse_sized_data(&mut self) -> Result> { - let size = self.bytes.read_u64::()?; - self.take_bytes(size) - } - - fn take_bytes(&mut self, count: u64) -> Result> { - let mut contents = vec!(0u8; count as usize); - self.bytes.read_exact(&mut contents)?; - Ok(contents) - } -} - -#[derive(Debug, Snafu)] -pub enum ParseError { - #[snafu(display("IO error: {}", source))] - Io { source: io::Error }, - - #[snafu(display("wrong magic number"))] - WrongMagic, - - #[snafu(display("unknown section kind: 0x{:02x}", kind))] - UnknownSectionKind { kind: u8 }, - - #[snafu(display("invalid UTF-8 string: {}", source))] - InvalidUtf8String { source: std::string::FromUtf8Error }, -} - -macro_rules! into_parse_error { - ( - $($type:ty : $variant:ident),* $(,)? - ) => { - $( - impl From<$type> for ParseError { - fn from(other: $type) -> Self { - ParseError::$variant { source: other } - } - } - )* - } -} - -into_parse_error! { - io::Error: Io, - std::string::FromUtf8Error: InvalidUtf8String, -} - -pub type Result = std::result::Result; +pub mod obj; +pub mod parser; +pub mod error; diff --git a/src/vm/obj/obj.rs b/src/vm/obj/obj.rs new file mode 100644 index 0000000..49253c2 --- /dev/null +++ b/src/vm/obj/obj.rs @@ -0,0 +1,107 @@ +use crate::vm::obj::error::ParseError; +use std::{ + convert::TryFrom, + fmt::Debug, +}; + +pub const MAGIC: u64 = 0xDEAD_BEA7_BA5E_BA11; + +#[derive(Debug)] +pub struct Object { + pub header: Header, + pub sections: Vec>, +} + +#[derive(Debug, Clone, Copy)] +pub struct Header { + pub version: u16, + pub sections: u16, +} + +macro_rules! section_kind { + ( + pub enum $enum_name:ident { + $($name:ident = $value:expr),* $(,)? + } + ) => { + + #[derive(Debug, Clone, Copy)] + #[repr(u8)] + pub enum $enum_name { + $($name = $value),* + } + + impl TryFrom for SectionKind { + type Error = ParseError; + + fn try_from(other: u8) -> std::result::Result { + match other { + $( + $value => Ok($enum_name::$name), + )* + _ => Err(ParseError::UnknownSectionKind { kind: other }), + } + } + } + }; +} + +section_kind! { + pub enum SectionKind { + Data = 0x00, + Code = 0x10, + Meta = 0xFF, + } +} + +pub trait Section: Debug { + fn header(&self) -> SectionHeader; +} + +#[derive(Debug, Clone, Copy)] +pub struct SectionHeader { + pub kind: SectionKind, + pub checksum: u32, + pub len: u64, +} + +#[derive(Debug, Clone)] +pub struct DataSection { + pub header: SectionHeader, + pub load_location: u64, + pub contents: Vec, +} + +impl Section for DataSection { + fn header(&self) -> SectionHeader { + self.header + } +} + +#[derive(Debug, Clone)] +pub struct CodeSection { + pub header: SectionHeader, + pub load_location: u64, + pub contents: Vec, +} + +impl Section for CodeSection { + fn header(&self) -> SectionHeader { + self.header + } +} + +#[derive(Debug, Clone)] +pub struct MetaSection { + pub header: SectionHeader, + pub entry_count: u64, + pub entries: Vec<(String, Vec)>, +} + +impl Section for MetaSection { + fn header(&self) -> SectionHeader { + self.header + } +} + + diff --git a/src/vm/obj/parser.rs b/src/vm/obj/parser.rs new file mode 100644 index 0000000..ccfa90b --- /dev/null +++ b/src/vm/obj/parser.rs @@ -0,0 +1,108 @@ +use byteorder::{ReadBytesExt, LE}; +use crate::vm::obj::{ + obj::*, + error::*, +}; +use std::{ + convert::TryInto, + fmt::Debug, + io::{Cursor, Read}, +}; + +#[derive(Debug, Clone)] +pub struct ObjectParser { + bytes: Cursor>, +} + +impl ObjectParser { + pub fn parse(&mut self) -> Result { + let header = self.parse_header()?; + let sections = self.parse_sections(header)?; + Ok(Object { header, sections }) + } + + fn parse_header(&mut self) -> Result
{ + let magic = self.bytes.read_u64::()?; + if magic != magic { + return Err(ParseError::WrongMagic); + } + let version = self.bytes.read_u16::()?; + let sections = self.bytes.read_u16::()?; + Ok(Header { version, sections }) + } + + fn parse_sections(&mut self, header: Header) -> Result>> { + (0..header.sections) + .map(|_| self.parse_section()) + .collect() + } + + fn parse_section(&mut self) -> Result> { + let header = self.parse_section_header()?; + let section: Box = match header.kind { + SectionKind::Data => self.parse_data_section(header).map(Box::new)?, + SectionKind::Code => self.parse_code_section(header).map(Box::new)?, + SectionKind::Meta => self.parse_meta_section(header).map(Box::new)?, + }; + Ok(section) + } + + fn parse_section_header(&mut self) -> Result { + let kind: SectionKind = self.bytes.read_u8()?.try_into()?; + let checksum = self.bytes.read_u32::()?; + let len = self.bytes.read_u64::()?; + Ok(SectionHeader { + kind, + checksum, + len, + }) + } + + fn parse_data_section(&mut self, header: SectionHeader) -> Result { + let load_location = self.bytes.read_u64::()?; + let contents = self.take_bytes(header.len)?; + Ok(DataSection { + header, + load_location, + contents, + }) + } + + fn parse_code_section(&mut self, header: SectionHeader) -> Result { + let load_location = self.bytes.read_u64::()?; + let contents = self.take_bytes(header.len)?; + Ok(CodeSection { + header, + load_location, + contents, + }) + } + + fn parse_meta_section(&mut self, header: SectionHeader) -> Result { + let entry_count = self.bytes.read_u64::()?; + let mut entries = Vec::with_capacity(entry_count as usize); + for _ in 0 .. entry_count { + let key_bytes = self.parse_sized_data()?; + let key = String::from_utf8(key_bytes)?; + let value = self.parse_sized_data()?; + entries.push((key, value)); + } + Ok(MetaSection { + header, + entry_count, + entries, + }) + } + + fn parse_sized_data(&mut self) -> Result> { + let size = self.bytes.read_u64::()?; + self.take_bytes(size) + } + + fn take_bytes(&mut self, count: u64) -> Result> { + let mut contents = vec!(0u8; count as usize); + self.bytes.read_exact(&mut contents)?; + Ok(contents) + } +} + diff --git a/src/vm/reg.rs b/src/vm/reg.rs index 388a7cf..ebab219 100644 --- a/src/vm/reg.rs +++ b/src/vm/reg.rs @@ -5,7 +5,6 @@ macro_rules! registers { pub type Reg = u8; $( - #[allow(dead_code)] pub const $variant: Reg = $value; )* }; @@ -27,7 +26,9 @@ registers! { // Flags FLAGS = 3, - UNUSED00 = 4, + // Zero + NULL = 4, + UNUSED01 = 5, UNUSED02 = 6, UNUSED03 = 7, diff --git a/src/vm/tick.rs b/src/vm/tick.rs new file mode 100644 index 0000000..2fdb46e --- /dev/null +++ b/src/vm/tick.rs @@ -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(()) + } +} diff --git a/src/vm/vm.rs b/src/vm/vm.rs index cf566f0..4d9fb17 100644 --- a/src/vm/vm.rs +++ b/src/vm/vm.rs @@ -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, - memory: Vec, - registers: Registers, + pub(super) mem: Vec, + pub(super) registers: Registers, } -#[allow(dead_code)] impl Vm { - pub fn new(code: Vec, memory: Vec, registers: Registers) -> Self { - Vm { code, memory, registers } + pub fn new(mem: Vec, 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 { 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::() + let mut reader = Cursor::new(&self.mem[(addr as usize)..]); + reader + .read_u64::() .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::() + let mut reader = Cursor::new(&self.mem[(addr as usize)..]); + reader + .read_u32::() .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::(value) + let mut writer = Cursor::new(&mut self.mem[(addr as usize)..]); + writer + .write_u64::(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::(value) + let mut writer = Cursor::new(&mut self.mem[(addr as usize)..]); + writer + .write_u32::(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(&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(&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(); - } }