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:
@@ -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
9
src/vm/error.rs
Normal 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>;
|
||||||
@@ -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
80
src/vm/mem.rs
Normal 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()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
42
src/vm/obj/error.rs
Normal 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>;
|
||||||
|
|
||||||
@@ -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
107
src/vm/obj/obj.rs
Normal 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
108
src/vm/obj/parser.rs
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -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
127
src/vm/tick.rs
Normal 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(())
|
||||||
|
}
|
||||||
|
}
|
||||||
270
src/vm/vm.rs
270
src/vm/vm.rs
@@ -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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user