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,3 +1,5 @@
#![allow(dead_code)]
mod common; mod common;
//mod syn; //mod syn;
mod vm; mod vm;

9
src/vm/error.rs Normal file
View File

@@ -0,0 +1,9 @@
use snafu::Snafu;
use crate::vm::vm::*;
#[derive(Snafu, Debug, Clone)]
pub enum VmError {
MemOutOfBounds { addr: Addr, }
}
pub type Result<T, E = VmError> = std::result::Result<T, E>;

View File

@@ -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 const $variant: InstOp = $value;
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,
} }
// 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,
}

80
src/vm/mem.rs Normal file
View File

@@ -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<u8> {
self.read_u8().map_err(|_| VmError::MemOutOfBounds {
addr: self.position(),
})
}
pub fn next_u16(&mut self) -> Result<u16> {
self.read_u16::<LE>().map_err(|_| VmError::MemOutOfBounds {
addr: self.position(),
})
}
pub fn next_u32(&mut self) -> Result<u32> {
self.read_u32::<LE>().map_err(|_| VmError::MemOutOfBounds {
addr: self.position(),
})
}
pub fn next_u64(&mut self) -> Result<u64> {
self.read_u64::<LE>().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<Reg> {
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()
}
}

View File

@@ -1,6 +1,9 @@
pub mod error;
pub mod flags; pub mod flags;
pub mod inst; pub mod inst;
pub mod mem;
pub mod obj; pub mod obj;
pub mod reg; pub mod reg;
pub mod syn; pub mod syn;
mod tick;
pub mod vm; pub mod vm;

42
src/vm/obj/error.rs Normal file
View File

@@ -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<T> = std::result::Result<T, ParseError>;

View File

@@ -1,234 +1,3 @@
use crate::vm::inst::Inst; pub mod obj;
use byteorder::{ReadBytesExt, LE}; pub mod parser;
use snafu::{ensure, Snafu}; pub mod error;
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<Box<dyn Section>>,
}
#[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<u8> for SectionKind {
type Error = ParseError;
fn try_from(other: u8) -> std::result::Result<Self, Self::Error> {
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<u8>,
}
impl Section for DataSection {
fn header(&self) -> SectionHeader {
self.header
}
}
#[derive(Debug, Clone)]
pub struct CodeSection {
header: SectionHeader,
load_location: u64,
contents: Vec<Inst>,
}
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<u8>)>,
}
impl Section for MetaSection {
fn header(&self) -> SectionHeader {
self.header
}
}
#[derive(Debug, Clone)]
pub struct ObjectParser {
bytes: Cursor<Vec<u8>>,
}
impl ObjectParser {
pub fn parse(&mut self) -> Result<Object> {
let header = self.parse_header()?;
let sections = self.parse_sections(header)?;
Ok(Object { header, sections })
}
fn parse_header(&mut self) -> Result<Header> {
let magic = self.bytes.read_u64::<LE>()?;
ensure!(magic == MAGIC, WrongMagic);
let version = self.bytes.read_u16::<LE>()?;
let sections = self.bytes.read_u16::<LE>()?;
Ok(Header { version, sections })
}
fn parse_sections(&mut self, header: Header) -> Result<Vec<Box<dyn Section>>> {
(0..header.sections)
.map(|_| self.parse_section())
.collect()
}
fn parse_section(&mut self) -> Result<Box<dyn Section>> {
let header = self.parse_section_header()?;
let section: Box<dyn Section> = 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<SectionHeader> {
let kind: SectionKind = self.bytes.read_u8()?.try_into()?;
let checksum = self.bytes.read_u32::<LE>()?;
let len = self.bytes.read_u64::<LE>()?;
Ok(SectionHeader {
kind,
checksum,
len,
})
}
fn parse_data_section(&mut self, header: SectionHeader) -> Result<DataSection> {
let load_location = self.bytes.read_u64::<LE>()?;
let contents = self.take_bytes(header.len)?;
Ok(DataSection {
header,
load_location,
contents,
})
}
fn parse_code_section(&mut self, _header: SectionHeader) -> Result<CodeSection> {
let _load_location = self.bytes.read_u64::<LE>()?;
todo!("instruction parsing")
}
fn parse_meta_section(&mut self, header: SectionHeader) -> Result<MetaSection> {
let entry_count = self.bytes.read_u64::<LE>()?;
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<Vec<u8>> {
let size = self.bytes.read_u64::<LE>()?;
self.take_bytes(size)
}
fn take_bytes(&mut self, count: u64) -> Result<Vec<u8>> {
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<T> = std::result::Result<T, ParseError>;

107
src/vm/obj/obj.rs Normal file
View File

@@ -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<Box<dyn Section>>,
}
#[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<u8> for SectionKind {
type Error = ParseError;
fn try_from(other: u8) -> std::result::Result<Self, Self::Error> {
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<u8>,
}
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<u8>,
}
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<u8>)>,
}
impl Section for MetaSection {
fn header(&self) -> SectionHeader {
self.header
}
}

108
src/vm/obj/parser.rs Normal file
View File

@@ -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<Vec<u8>>,
}
impl ObjectParser {
pub fn parse(&mut self) -> Result<Object> {
let header = self.parse_header()?;
let sections = self.parse_sections(header)?;
Ok(Object { header, sections })
}
fn parse_header(&mut self) -> Result<Header> {
let magic = self.bytes.read_u64::<LE>()?;
if magic != magic {
return Err(ParseError::WrongMagic);
}
let version = self.bytes.read_u16::<LE>()?;
let sections = self.bytes.read_u16::<LE>()?;
Ok(Header { version, sections })
}
fn parse_sections(&mut self, header: Header) -> Result<Vec<Box<dyn Section>>> {
(0..header.sections)
.map(|_| self.parse_section())
.collect()
}
fn parse_section(&mut self) -> Result<Box<dyn Section>> {
let header = self.parse_section_header()?;
let section: Box<dyn Section> = 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<SectionHeader> {
let kind: SectionKind = self.bytes.read_u8()?.try_into()?;
let checksum = self.bytes.read_u32::<LE>()?;
let len = self.bytes.read_u64::<LE>()?;
Ok(SectionHeader {
kind,
checksum,
len,
})
}
fn parse_data_section(&mut self, header: SectionHeader) -> Result<DataSection> {
let load_location = self.bytes.read_u64::<LE>()?;
let contents = self.take_bytes(header.len)?;
Ok(DataSection {
header,
load_location,
contents,
})
}
fn parse_code_section(&mut self, header: SectionHeader) -> Result<CodeSection> {
let load_location = self.bytes.read_u64::<LE>()?;
let contents = self.take_bytes(header.len)?;
Ok(CodeSection {
header,
load_location,
contents,
})
}
fn parse_meta_section(&mut self, header: SectionHeader) -> Result<MetaSection> {
let entry_count = self.bytes.read_u64::<LE>()?;
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<Vec<u8>> {
let size = self.bytes.read_u64::<LE>()?;
self.take_bytes(size)
}
fn take_bytes(&mut self, count: u64) -> Result<Vec<u8>> {
let mut contents = vec!(0u8; count as usize);
self.bytes.read_exact(&mut contents)?;
Ok(contents)
}
}

View File

@@ -5,7 +5,6 @@ macro_rules! registers {
pub type Reg = u8; pub type Reg = u8;
$( $(
#[allow(dead_code)]
pub const $variant: Reg = $value; pub const $variant: Reg = $value;
)* )*
}; };
@@ -27,7 +26,9 @@ registers! {
// Flags // Flags
FLAGS = 3, FLAGS = 3,
UNUSED00 = 4, // Zero
NULL = 4,
UNUSED01 = 5, UNUSED01 = 5,
UNUSED02 = 6, UNUSED02 = 6,
UNUSED03 = 7, UNUSED03 = 7,

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(())
}
}

View File

@@ -1,41 +1,32 @@
use crate::vm::{ use crate::vm::{error::*, flags::*, mem::*, reg::*};
reg::*, use byteorder::{ReadBytesExt, WriteBytesExt, LE};
inst::*, use std::{io::Cursor, mem};
flags::*,
};
use byteorder::{LE, ReadBytesExt, WriteBytesExt};
use std::{
mem,
io::Cursor,
};
pub type Word = u64; pub type Word = u64;
pub type HalfWord = u32; pub type HalfWord = u32;
pub type Registers = [Word; 64]; pub type Registers = [Word; 64];
pub type Addr = u64;
pub struct Vm { pub struct Vm {
code: Vec<Inst>, pub(super) mem: Vec<u8>,
memory: Vec<u8>, pub(super) registers: Registers,
registers: Registers,
} }
#[allow(dead_code)]
impl Vm { impl Vm {
pub fn new(code: Vec<Inst>, memory: Vec<u8>, registers: Registers) -> Self { pub fn new(mem: Vec<u8>, registers: Registers) -> Self {
Vm { code, memory, registers } 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() { while !self.is_halted() {
self.tick(); self.tick()?;
} }
} Ok(self.get_reg(STATUS))
pub fn tick(&mut self) {
let inst = self.decode();
let ip = self.ip();
self.set_reg(IP, ip + 1);
self.execute(inst);
} }
pub fn resume(&mut self) { pub fn resume(&mut self) {
@@ -47,38 +38,42 @@ impl Vm {
} }
pub fn get_word(&self, addr: Word) -> Word { pub fn get_word(&self, addr: Word) -> Word {
let mut reader = Cursor::new(&self.memory[(addr as usize)..]); let mut reader = Cursor::new(&self.mem[(addr as usize)..]);
reader.read_u64::<LE>() reader
.read_u64::<LE>()
.expect("word outside of address range") .expect("word outside of address range")
} }
pub fn get_halfword(&self, addr: Word) -> HalfWord { pub fn get_halfword(&self, addr: Word) -> HalfWord {
let mut reader = Cursor::new(&self.memory[(addr as usize)..]); let mut reader = Cursor::new(&self.mem[(addr as usize)..]);
reader.read_u32::<LE>() reader
.read_u32::<LE>()
.expect("word outside of address range") .expect("word outside of address range")
} }
pub fn get_byte(&self, addr: Word) -> u8 { pub fn get_byte(&self, addr: Word) -> u8 {
let mut reader = Cursor::new(&self.memory[(addr as usize)..]); let mut reader = Cursor::new(&self.mem[(addr as usize)..]);
reader.read_u8() reader.read_u8().expect("word outside of address range")
.expect("word outside of address range")
} }
pub fn set_word(&mut self, addr: Word, value: Word) { pub fn set_word(&mut self, addr: Word, value: Word) {
let mut writer = Cursor::new(&mut self.memory[(addr as usize)..]); let mut writer = Cursor::new(&mut self.mem[(addr as usize)..]);
writer.write_u64::<LE>(value) writer
.write_u64::<LE>(value)
.expect("word outside of address range"); .expect("word outside of address range");
} }
pub fn set_halfword(&mut self, addr: Word, value: HalfWord) { pub fn set_halfword(&mut self, addr: Word, value: HalfWord) {
let mut writer = Cursor::new(&mut self.memory[(addr as usize)..]); let mut writer = Cursor::new(&mut self.mem[(addr as usize)..]);
writer.write_u32::<LE>(value) writer
.write_u32::<LE>(value)
.expect("word outside of address range"); .expect("word outside of address range");
} }
pub fn set_byte(&mut self, addr: Word, value: u8) { pub fn set_byte(&mut self, addr: Word, value: u8) {
let mut writer = Cursor::new(&mut self.memory[(addr as usize)..]); let mut writer = Cursor::new(&mut self.mem[(addr as usize)..]);
writer.write_u8(value) writer
.write_u8(value)
.expect("word outside of address range"); .expect("word outside of address range");
} }
@@ -108,7 +103,7 @@ impl Vm {
// anyway, but if it does, they're ignored // anyway, but if it does, they're ignored
unsafe { Flags::from_bits_unchecked(self.get_reg(FLAGS)) } unsafe { Flags::from_bits_unchecked(self.get_reg(FLAGS)) }
} }
pub fn insert_flags(&mut self, flags: Flags) { pub fn insert_flags(&mut self, flags: Flags) {
let mut new_flags = self.flags(); let mut new_flags = self.flags();
new_flags.insert(flags); new_flags.insert(flags);
@@ -124,201 +119,4 @@ impl Vm {
pub fn set_flags(&mut self, flags: Flags) { pub fn set_flags(&mut self, flags: Flags) {
self.set_reg(FLAGS, flags.bits()); 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();
}
} }