First part of libvm transition
Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
This commit is contained in:
25
Cargo.lock
generated
25
Cargo.lock
generated
@@ -262,6 +262,22 @@ name = "libc"
|
|||||||
version = "0.2.66"
|
version = "0.2.66"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libvm"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"cfgrammar 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"lrlex 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"lrpar 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"regex 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"rerun_except 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"snafu 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"structopt 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "log"
|
name = "log"
|
||||||
version = "0.4.8"
|
version = "0.4.8"
|
||||||
@@ -419,14 +435,7 @@ dependencies = [
|
|||||||
name = "rasp"
|
name = "rasp"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libvm 0.1.0",
|
||||||
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"cfgrammar 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"lrlex 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"lrpar 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"regex 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"rerun_except 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"snafu 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"snafu 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"structopt 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
"structopt 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|||||||
17
Cargo.toml
17
Cargo.toml
@@ -4,24 +4,9 @@ version = "0.1.0"
|
|||||||
authors = ["Alek Ratzloff <alekratz@gmail.com>"]
|
authors = ["Alek Ratzloff <alekratz@gmail.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
build = "build.rs"
|
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
structopt = "0.3"
|
structopt = "0.3"
|
||||||
bitflags = "1"
|
|
||||||
byteorder = "1"
|
|
||||||
lazy_static = "1"
|
|
||||||
snafu = "0.6.2"
|
snafu = "0.6.2"
|
||||||
|
libvm = { path = "src/libvm" }
|
||||||
cfgrammar = "0.6"
|
|
||||||
lrlex = "0.6"
|
|
||||||
lrpar = "0.6"
|
|
||||||
regex = "*"
|
|
||||||
|
|
||||||
[build-dependencies]
|
|
||||||
cfgrammar = "0.6"
|
|
||||||
lrlex = "0.6"
|
|
||||||
lrpar = "0.6"
|
|
||||||
rerun_except = "0.1"
|
|
||||||
|
|||||||
18
build.rs
18
build.rs
@@ -1,18 +0,0 @@
|
|||||||
use cfgrammar::yacc::YaccKind;
|
|
||||||
use lrlex::LexerBuilder;
|
|
||||||
use lrpar::{CTParserBuilder};
|
|
||||||
use rerun_except::rerun_except;
|
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
let lex_rule_ids_map = CTParserBuilder::new()
|
|
||||||
.yacckind(YaccKind::Grmtools)
|
|
||||||
.process_file_in_src("vm/obj/syn/parser.y")?;
|
|
||||||
LexerBuilder::new()
|
|
||||||
.rule_ids_map(lex_rule_ids_map)
|
|
||||||
.process_file_in_src("vm/obj/syn/lexer.l")?;
|
|
||||||
rerun_except(&[
|
|
||||||
"examples/*.asm",
|
|
||||||
"tests/*.asm",
|
|
||||||
]).unwrap();
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
|
|
||||||
mod common;
|
mod common;
|
||||||
mod vm;
|
|
||||||
|
extern crate libvm as vm;
|
||||||
|
|
||||||
use structopt::StructOpt;
|
use structopt::StructOpt;
|
||||||
use snafu::Snafu;
|
use snafu::Snafu;
|
||||||
@@ -130,3 +131,4 @@ fn main() -> Result<()> {
|
|||||||
process::exit((status & 0xffff_ffff) as i32);
|
process::exit((status & 0xffff_ffff) as i32);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,73 +0,0 @@
|
|||||||
use std::{
|
|
||||||
cmp::Ordering,
|
|
||||||
fmt::{self, Formatter, LowerHex},
|
|
||||||
ops::{Add, AddAssign},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
|
||||||
pub struct Addr(pub u64);
|
|
||||||
|
|
||||||
impl LowerHex for Addr {
|
|
||||||
fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
|
|
||||||
let Addr(v) = self;
|
|
||||||
LowerHex::fmt(v, fmt)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Add<T> for Addr
|
|
||||||
where
|
|
||||||
T: Add<u64, Output = u64>,
|
|
||||||
u64: Add<T, Output = u64>,
|
|
||||||
{
|
|
||||||
type Output = Addr;
|
|
||||||
|
|
||||||
fn add(self, rhs: T) -> Self::Output {
|
|
||||||
Addr(self.0 + rhs)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! impl_add_assign {
|
|
||||||
($ty:ty) => {
|
|
||||||
impl AddAssign<$ty> for Addr {
|
|
||||||
fn add_assign(&mut self, rhs: $ty) {
|
|
||||||
self.0 = self.0 + (rhs as u64);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl_add_assign!(usize);
|
|
||||||
impl_add_assign!(u64);
|
|
||||||
|
|
||||||
macro_rules! impl_cmp {
|
|
||||||
($ty:ty) => {
|
|
||||||
impl PartialEq<$ty> for Addr {
|
|
||||||
fn eq(&self, other: &$ty) -> bool {
|
|
||||||
self.0 == (*other as u64)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialOrd<$ty> for Addr {
|
|
||||||
fn partial_cmp(&self, other: &$ty) -> Option<Ordering> {
|
|
||||||
let other = *other as u64;
|
|
||||||
self.0.partial_cmp(&other)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl_cmp!(usize);
|
|
||||||
impl_cmp!(u64);
|
|
||||||
|
|
||||||
macro_rules! impl_from {
|
|
||||||
($ty:ty) => {
|
|
||||||
impl From<$ty> for Addr {
|
|
||||||
fn from(other: $ty) -> Self {
|
|
||||||
Addr(other as u64)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl_from!(usize);
|
|
||||||
impl_from!(u64);
|
|
||||||
@@ -1,294 +0,0 @@
|
|||||||
use crate::vm::{
|
|
||||||
error::*,
|
|
||||||
inst::*,
|
|
||||||
mem::MemCursor,
|
|
||||||
reg::*,
|
|
||||||
visit::*,
|
|
||||||
vm::{Addr, HalfWord, Word},
|
|
||||||
};
|
|
||||||
use std::io::Write;
|
|
||||||
|
|
||||||
const WIDTH: usize = 60;
|
|
||||||
|
|
||||||
pub struct Disassemble<'w, 'o> {
|
|
||||||
writer: &'w mut dyn Write,
|
|
||||||
cursor: MemCursor<'o>,
|
|
||||||
addr_offset: Addr,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'w, 'o> Disassemble<'w, 'o> {
|
|
||||||
pub fn new(writer: &'w mut dyn Write, content: &'o [u8], addr_offset: Addr) -> Self {
|
|
||||||
Disassemble {
|
|
||||||
writer,
|
|
||||||
cursor: MemCursor::new(content),
|
|
||||||
addr_offset,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_done(&self) -> bool {
|
|
||||||
self.cursor.position() >= (self.cursor.get_ref().len() as u64)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn adv(&mut self) -> Result<()> {
|
|
||||||
// note the () - this explicitly clones the cursor
|
|
||||||
let op = self.cursor().next_u16()?;
|
|
||||||
let next = self.cursor.position() + (inst_len(op) as u64);
|
|
||||||
self.cursor.set_position(next);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_addr(&mut self, addr: Addr) {
|
|
||||||
write!(self.writer, "{:06x} | ", self.addr_offset + addr).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_bytes(&mut self, bytes: &[u8]) {
|
|
||||||
for b in bytes {
|
|
||||||
write!(self.writer, "{:02x} ", b).unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_inst_bytes(&mut self, op: InstOp) {
|
|
||||||
let len = inst_len(op);
|
|
||||||
let start = self.cursor.position() as usize;
|
|
||||||
let end = start + len;
|
|
||||||
let bytes = &self.cursor.get_ref()[start..end];
|
|
||||||
self.write_bytes(bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_r1_r2_inst(&mut self, addr: Addr, op: InstOp, r1: Reg, r2: Reg) {
|
|
||||||
let len = inst_len(op);
|
|
||||||
let line_width = 6 + 3 + (3 * len);
|
|
||||||
let line_offset = WIDTH - line_width;
|
|
||||||
self.write_addr(addr);
|
|
||||||
self.write_inst_bytes(op);
|
|
||||||
|
|
||||||
let iname = inst_name(op).unwrap();
|
|
||||||
let r1name = reg_name(r1).unwrap().to_lowercase();
|
|
||||||
let r2name = reg_name(r2).unwrap().to_lowercase();
|
|
||||||
|
|
||||||
writeln!(
|
|
||||||
self.writer,
|
|
||||||
"{}| {:>10} %{} %{}",
|
|
||||||
" ".repeat(line_offset),
|
|
||||||
iname,
|
|
||||||
r1name,
|
|
||||||
r2name
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_r1_inst(&mut self, addr: Addr, op: InstOp, r1: Reg) {
|
|
||||||
let line_width = 6 + 3 + (3 * inst_len(op));
|
|
||||||
let line_offset = WIDTH - line_width;
|
|
||||||
self.write_addr(addr);
|
|
||||||
self.write_inst_bytes(op);
|
|
||||||
|
|
||||||
let iname = inst_name(op).unwrap();
|
|
||||||
let r1name = reg_name(r1).unwrap().to_lowercase();
|
|
||||||
|
|
||||||
writeln!(
|
|
||||||
self.writer,
|
|
||||||
"{}| {:>10} %{}",
|
|
||||||
" ".repeat(line_offset),
|
|
||||||
iname,
|
|
||||||
r1name,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_r1_imm_inst(&mut self, addr: Addr, op: InstOp, r1: Reg, imm: Word) {
|
|
||||||
let line_width = 6 + 3 + (3 * inst_len(op));
|
|
||||||
let line_offset = WIDTH - line_width;
|
|
||||||
self.write_addr(addr);
|
|
||||||
self.write_inst_bytes(op);
|
|
||||||
|
|
||||||
let iname = inst_name(op).unwrap();
|
|
||||||
let r1name = reg_name(r1).unwrap().to_lowercase();
|
|
||||||
|
|
||||||
writeln!(
|
|
||||||
self.writer,
|
|
||||||
"{}| {:>10} %{} {:#X}",
|
|
||||||
" ".repeat(line_offset),
|
|
||||||
iname,
|
|
||||||
r1name,
|
|
||||||
imm,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl VisitInst for Disassemble<'_, '_> {
|
|
||||||
type Out = ();
|
|
||||||
|
|
||||||
fn cursor(&self) -> MemCursor {
|
|
||||||
self.cursor.clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn add(&mut self, r1: Reg, r2: Reg) -> Result<Self::Out> {
|
|
||||||
self.write_r1_r2_inst(self.cursor.position(), ADD, r1, r2);
|
|
||||||
self.adv()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn mul(&mut self, r1: Reg, r2: Reg) -> Result<Self::Out> {
|
|
||||||
self.write_r1_r2_inst(self.cursor.position(), MUL, r1, r2);
|
|
||||||
self.adv()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn div(&mut self, r1: Reg, r2: Reg) -> Result<Self::Out> {
|
|
||||||
self.write_r1_r2_inst(self.cursor.position(), DIV, r1, r2);
|
|
||||||
self.adv()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn mod_(&mut self, r1: Reg, r2: Reg) -> Result<Self::Out> {
|
|
||||||
self.write_r1_r2_inst(self.cursor.position(), MOD, r1, r2);
|
|
||||||
self.adv()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ineg(&mut self, r1: Reg) -> Result<Self::Out> {
|
|
||||||
self.write_r1_inst(self.cursor.position(), INEG, r1);
|
|
||||||
self.adv()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn and(&mut self, r1: Reg, r2: Reg) -> Result<Self::Out> {
|
|
||||||
self.write_r1_r2_inst(self.cursor.position(), AND, r1, r2);
|
|
||||||
self.adv()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn or(&mut self, r1: Reg, r2: Reg) -> Result<Self::Out> {
|
|
||||||
self.write_r1_r2_inst(self.cursor.position(), OR, r1, r2);
|
|
||||||
self.adv()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn inv(&mut self, r1: Reg) -> Result<Self::Out> {
|
|
||||||
self.write_r1_inst(self.cursor.position(), INV, r1);
|
|
||||||
self.adv()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn not(&mut self, r1: Reg) -> Result<Self::Out> {
|
|
||||||
self.write_r1_inst(self.cursor.position(), NOT, r1);
|
|
||||||
self.adv()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn xor(&mut self, r1: Reg, r2: Reg) -> Result<Self::Out> {
|
|
||||||
self.write_r1_r2_inst(self.cursor.position(), XOR, r1, r2);
|
|
||||||
self.adv()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn shl(&mut self, r1: Reg, r2: Reg) -> Result<Self::Out> {
|
|
||||||
self.write_r1_r2_inst(self.cursor.position(), SHL, r1, r2);
|
|
||||||
self.adv()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn shr(&mut self, r1: Reg, r2: Reg) -> Result<Self::Out> {
|
|
||||||
self.write_r1_r2_inst(self.cursor.position(), SHR, r1, r2);
|
|
||||||
self.adv()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cmpeq(&mut self, r1: Reg, r2: Reg) -> Result<Self::Out> {
|
|
||||||
self.write_r1_r2_inst(self.cursor.position(), CMPEQ, r1, r2);
|
|
||||||
self.adv()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cmplt(&mut self, r1: Reg, r2: Reg) -> Result<Self::Out> {
|
|
||||||
self.write_r1_r2_inst(self.cursor.position(), CMPLT, r1, r2);
|
|
||||||
self.adv()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn jmp(&mut self, r1: Reg) -> Result<Self::Out> {
|
|
||||||
self.write_r1_inst(self.cursor.position(), JMP, r1);
|
|
||||||
self.adv()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn jz(&mut self, r1: Reg) -> Result<Self::Out> {
|
|
||||||
self.write_r1_inst(self.cursor.position(), JZ, r1);
|
|
||||||
self.adv()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn jnz(&mut self, r1: Reg) -> Result<Self::Out> {
|
|
||||||
self.write_r1_inst(self.cursor.position(), JNZ, r1);
|
|
||||||
self.adv()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn load(&mut self, r1: Reg, r2: Reg) -> Result<Self::Out> {
|
|
||||||
self.write_r1_r2_inst(self.cursor.position(), LOAD, r1, r2);
|
|
||||||
self.adv()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn regcopy(&mut self, r1: Reg, r2: Reg) -> Result<Self::Out> {
|
|
||||||
self.write_r1_r2_inst(self.cursor.position(), REGCOPY, r1, r2);
|
|
||||||
self.adv()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn storeimm64(&mut self, r1: Reg, w1: Word) -> Result<Self::Out> {
|
|
||||||
self.write_r1_imm_inst(self.cursor.position(), STOREIMM64, r1, w1);
|
|
||||||
self.adv()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn storeimm32(&mut self, r1: Reg, w1: HalfWord) -> Result<Self::Out> {
|
|
||||||
self.write_r1_imm_inst(self.cursor.position(), STOREIMM32, r1, w1 as u64);
|
|
||||||
self.adv()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn memcopy(&mut self, r1: Reg, r2: Reg) -> Result<Self::Out> {
|
|
||||||
self.write_r1_r2_inst(self.cursor.position(), MEMCOPY, r1, r2);
|
|
||||||
self.adv()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn store(&mut self, r1: Reg, r2: Reg) -> Result<Self::Out> {
|
|
||||||
self.write_r1_r2_inst(self.cursor.position(), STORE, r1, r2);
|
|
||||||
self.adv()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn halt(&mut self) -> Result<Self::Out> {
|
|
||||||
let line_width = 6 + 3 + 3 + 3;
|
|
||||||
let line_offset = WIDTH - line_width;
|
|
||||||
self.write_addr(self.cursor.position());
|
|
||||||
self.write_inst_bytes(HALT);
|
|
||||||
writeln!(
|
|
||||||
self.writer,
|
|
||||||
"{}| {:>10}",
|
|
||||||
" ".repeat(line_offset),
|
|
||||||
"HALT",
|
|
||||||
).unwrap();
|
|
||||||
self.adv()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn nop(&mut self) -> Result<Self::Out> {
|
|
||||||
let line_width = 6 + 3 + 3 + 3;
|
|
||||||
let line_offset = WIDTH - line_width;
|
|
||||||
self.write_addr(self.cursor.position());
|
|
||||||
self.write_inst_bytes(NOP);
|
|
||||||
writeln!(
|
|
||||||
self.writer,
|
|
||||||
"{}| {:>10}",
|
|
||||||
" ".repeat(line_offset),
|
|
||||||
"NOP",
|
|
||||||
).unwrap();
|
|
||||||
self.adv()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
use crate::vm::{inst::InstOp, reg::Reg, addr::*,};
|
|
||||||
use snafu::Snafu;
|
|
||||||
|
|
||||||
#[derive(Snafu, Debug, Clone)]
|
|
||||||
pub enum VmError {
|
|
||||||
#[snafu(display("illegal register: 0x{:02x}", reg))]
|
|
||||||
IllegalReg { reg: Reg },
|
|
||||||
|
|
||||||
#[snafu(display("memory address out of bounds: 0x{:016x}", addr))]
|
|
||||||
MemOutOfBounds { addr: Addr },
|
|
||||||
|
|
||||||
#[snafu(display("illegal instruction opcode: 0x{:04x}", op))]
|
|
||||||
IllegalOp { op: InstOp },
|
|
||||||
|
|
||||||
#[snafu(display("illegal destination specification: 0b{:08b}", spec))]
|
|
||||||
IllegalDestSpec { spec: u8 },
|
|
||||||
|
|
||||||
#[snafu(display("illegal source specification: 0b{:08b}", spec))]
|
|
||||||
IllegalSourceSpec { spec: u8 },
|
|
||||||
|
|
||||||
#[snafu(display("object to load spans too much memory"))]
|
|
||||||
ObjectTooLarge { object_size: usize, max_mem: usize },
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type Result<T, E = VmError> = std::result::Result<T, E>;
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
use bitflags::bitflags;
|
|
||||||
|
|
||||||
bitflags! {
|
|
||||||
pub struct Flags: u64 {
|
|
||||||
const HALT = 1;
|
|
||||||
const COMPARE = 1 << 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
263
src/vm/inst.rs
263
src/vm/inst.rs
@@ -1,263 +0,0 @@
|
|||||||
use crate::vm::{addr::Addr, reg::Reg};
|
|
||||||
|
|
||||||
macro_rules! instructions {
|
|
||||||
{
|
|
||||||
$($variant:ident = $value:expr),* $(,)?
|
|
||||||
} => {
|
|
||||||
|
|
||||||
$(
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub const $variant: InstOp = $value;
|
|
||||||
)*
|
|
||||||
|
|
||||||
pub fn inst_name(op: InstOp) -> Option<&'static str> {
|
|
||||||
match op {
|
|
||||||
$(
|
|
||||||
$value => Some(stringify!($variant)),
|
|
||||||
)*
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
pub type InstOp = u16;
|
|
||||||
|
|
||||||
instructions! {
|
|
||||||
ADD = 0x0000,
|
|
||||||
SUB = 0x0001,
|
|
||||||
MUL = 0x0002,
|
|
||||||
DIV = 0x0003,
|
|
||||||
IDIV = 0x0004,
|
|
||||||
MOD = 0x0005,
|
|
||||||
AND = 0x0006,
|
|
||||||
OR = 0x0007,
|
|
||||||
XOR = 0x0008,
|
|
||||||
SHL = 0x0009,
|
|
||||||
SHR = 0x000a,
|
|
||||||
INEG = 0x000b,
|
|
||||||
INV = 0x000c,
|
|
||||||
NOT = 0x000d,
|
|
||||||
CMPEQ = 0x1000,
|
|
||||||
CMPLT = 0x1001,
|
|
||||||
JMP = 0x1002,
|
|
||||||
JZ = 0x1003,
|
|
||||||
JNZ = 0x1004,
|
|
||||||
CALL = 0x2000,
|
|
||||||
RET = 0x2001,
|
|
||||||
PUSH = 0x2002,
|
|
||||||
POP = 0x2003,
|
|
||||||
MOV = 0x3000,
|
|
||||||
HALT = 0xF000,
|
|
||||||
NOP = 0xF001,
|
|
||||||
DUMP = 0xF002,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
|
||||||
pub enum Inst {
|
|
||||||
Add(Dest, Source),
|
|
||||||
Sub(Dest, Source),
|
|
||||||
Mul(Dest, Source),
|
|
||||||
Div(Dest, Source),
|
|
||||||
IDiv(Dest, Source),
|
|
||||||
Mod(Dest, Source),
|
|
||||||
And(Dest, Source),
|
|
||||||
Or(Dest, Source),
|
|
||||||
Xor(Dest, Source),
|
|
||||||
Shl(Dest, Source),
|
|
||||||
Shr(Dest, Source),
|
|
||||||
INeg(Dest, Source),
|
|
||||||
Inv(Dest, Source),
|
|
||||||
Not(Dest, Source),
|
|
||||||
CmpEq(Source, Source),
|
|
||||||
CmpLt(Source, Source),
|
|
||||||
Jmp(Source),
|
|
||||||
Jz(Source),
|
|
||||||
Jnz(Source),
|
|
||||||
Call(Source),
|
|
||||||
Ret,
|
|
||||||
Push(Source),
|
|
||||||
Pop(Dest),
|
|
||||||
Mov(Dest, Source),
|
|
||||||
Halt,
|
|
||||||
Nop,
|
|
||||||
Dump,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Inst {
|
|
||||||
pub fn op(&self) -> InstOp {
|
|
||||||
match self {
|
|
||||||
Inst::Add(_, _) => ADD,
|
|
||||||
Inst::Sub(_, _) => SUB,
|
|
||||||
Inst::Mul(_, _) => MUL,
|
|
||||||
Inst::Div(_, _) => DIV,
|
|
||||||
Inst::IDiv(_, _) => IDIV,
|
|
||||||
Inst::Mod(_, _) => MOD,
|
|
||||||
Inst::And(_, _) => AND,
|
|
||||||
Inst::Or(_, _) => OR,
|
|
||||||
Inst::Xor(_, _) => XOR,
|
|
||||||
Inst::Shl(_, _) => SHL,
|
|
||||||
Inst::Shr(_, _) => SHL,
|
|
||||||
Inst::INeg(_, _) => INEG,
|
|
||||||
Inst::Inv(_, _) => INV,
|
|
||||||
Inst::Not(_, _) => NOT,
|
|
||||||
Inst::CmpEq(_, _) => CMPEQ,
|
|
||||||
Inst::CmpLt(_, _) => CMPLT,
|
|
||||||
Inst::Jmp(_) => JMP,
|
|
||||||
Inst::Jz(_) => JZ,
|
|
||||||
Inst::Jnz(_) => JNZ,
|
|
||||||
Inst::Mov(_, _) => MOV,
|
|
||||||
Inst::Call(_) => CALL,
|
|
||||||
Inst::Ret => RET,
|
|
||||||
Inst::Push(_) => PUSH,
|
|
||||||
Inst::Pop(_) => POP,
|
|
||||||
Inst::Halt => HALT,
|
|
||||||
Inst::Nop => NOP,
|
|
||||||
Inst::Dump => DUMP,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn len(&self) -> usize {
|
|
||||||
match self {
|
|
||||||
Inst::Add(dest, source)
|
|
||||||
| Inst::Sub(dest, source)
|
|
||||||
| Inst::Mul(dest, source)
|
|
||||||
| Inst::Div(dest, source)
|
|
||||||
| Inst::IDiv(dest, source)
|
|
||||||
| Inst::Mod(dest, source)
|
|
||||||
| Inst::And(dest, source)
|
|
||||||
| Inst::Or(dest, source)
|
|
||||||
| Inst::Xor(dest, source)
|
|
||||||
| Inst::Shl(dest, source)
|
|
||||||
| Inst::Shr(dest, source)
|
|
||||||
| Inst::INeg(dest, source)
|
|
||||||
| Inst::Inv(dest, source)
|
|
||||||
| Inst::Not(dest, source)
|
|
||||||
| Inst::Mov(dest, source) => { 3 + dest.len() + source.len() }
|
|
||||||
Inst::CmpEq(s1, s2)
|
|
||||||
| Inst::CmpLt(s1, s2) => { 3 + s1.len() + s2.len() }
|
|
||||||
Inst::Jmp(v)
|
|
||||||
| Inst::Jz(v)
|
|
||||||
| Inst::Jnz(v)
|
|
||||||
| Inst::Call(v)
|
|
||||||
| Inst::Push(v) => { 3 + v.len() }
|
|
||||||
Inst::Pop(v) => { 3 + v.len() }
|
|
||||||
Inst::Ret
|
|
||||||
| Inst::Halt
|
|
||||||
| Inst::Nop
|
|
||||||
| Inst::Dump => { 2 }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
||||||
pub enum Source {
|
|
||||||
Addr64(Addr),
|
|
||||||
Addr32(Addr),
|
|
||||||
Addr16(Addr),
|
|
||||||
Addr8(Addr),
|
|
||||||
RegAddr64(Reg),
|
|
||||||
RegAddr32(Reg),
|
|
||||||
RegAddr16(Reg),
|
|
||||||
RegAddr8(Reg),
|
|
||||||
Reg(Reg),
|
|
||||||
Imm(u64),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Source {
|
|
||||||
/// The length of this source in its instruction.
|
|
||||||
pub fn len(&self) -> usize {
|
|
||||||
match self {
|
|
||||||
Source::Addr64(_) | Source::Addr32(_) | Source::Addr16(_) | Source::Addr8(_) => 8,
|
|
||||||
Source::RegAddr64(_) | Source::RegAddr32(_) | Source::RegAddr16(_) | Source::RegAddr8(_) => 1,
|
|
||||||
Source::Reg(_) => 1,
|
|
||||||
Source::Imm(_) => 8,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The length of the value that this source points to.
|
|
||||||
pub fn value_len(&self) -> usize {
|
|
||||||
match self {
|
|
||||||
Source::Addr64(_) | Source::RegAddr64(_) | Source::Reg(_) | Source::Imm(_) => 8,
|
|
||||||
Source::Addr32(_) | Source::RegAddr32(_) => 4,
|
|
||||||
Source::Addr16(_) | Source::RegAddr16(_) => 2,
|
|
||||||
Source::Addr8(_) | Source::RegAddr8(_) => 1,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Dest> for Source {
|
|
||||||
fn from(other: Dest) -> Self {
|
|
||||||
match other {
|
|
||||||
Dest::Addr64(a) => Source::Addr64(a),
|
|
||||||
Dest::Addr32(a) => Source::Addr32(a),
|
|
||||||
Dest::Addr16(a) => Source::Addr16(a),
|
|
||||||
Dest::Addr8(a) => Source::Addr8(a),
|
|
||||||
Dest::RegAddr64(r) => Source::RegAddr64(r),
|
|
||||||
Dest::RegAddr32(r) => Source::RegAddr32(r),
|
|
||||||
Dest::RegAddr16(r) => Source::RegAddr16(r),
|
|
||||||
Dest::RegAddr8(r) => Source::RegAddr8(r),
|
|
||||||
Dest::Reg(r) => Source::Reg(r),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
||||||
pub enum Dest {
|
|
||||||
Addr64(Addr),
|
|
||||||
Addr32(Addr),
|
|
||||||
Addr16(Addr),
|
|
||||||
Addr8(Addr),
|
|
||||||
RegAddr64(Reg),
|
|
||||||
RegAddr32(Reg),
|
|
||||||
RegAddr16(Reg),
|
|
||||||
RegAddr8(Reg),
|
|
||||||
Reg(Reg),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Dest {
|
|
||||||
/// The length of this destination in its instruction.
|
|
||||||
pub fn len(&self) -> usize {
|
|
||||||
match self {
|
|
||||||
Dest::Addr64(_) | Dest::Addr32(_) | Dest::Addr16(_) | Dest::Addr8(_) => 8,
|
|
||||||
Dest::RegAddr64(_) | Dest::RegAddr32(_) | Dest::RegAddr16(_) | Dest::RegAddr8(_) => 1,
|
|
||||||
Dest::Reg(_) => 1,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The length of the value that this destination points to.
|
|
||||||
pub fn value_len(&self) -> usize {
|
|
||||||
match self {
|
|
||||||
Dest::Addr64(_) | Dest::RegAddr64(_) | Dest::Reg(_) => 8,
|
|
||||||
Dest::Addr32(_) | Dest::RegAddr32(_) => 4,
|
|
||||||
Dest::Addr16(_) | Dest::RegAddr16(_) => 2,
|
|
||||||
Dest::Addr8(_) | Dest::RegAddr8(_) => 1,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO : make this an enum
|
|
||||||
|
|
||||||
pub const DEST_ADDR64: u8 = 0b0000;
|
|
||||||
pub const DEST_ADDR32: u8 = 0b0001;
|
|
||||||
pub const DEST_ADDR16: u8 = 0b0010;
|
|
||||||
pub const DEST_ADDR8: u8 = 0b0011;
|
|
||||||
pub const DEST_REG_ADDR64: u8 = 0b0100;
|
|
||||||
pub const DEST_REG_ADDR32: u8 = 0b0101;
|
|
||||||
pub const DEST_REG_ADDR16: u8 = 0b0110;
|
|
||||||
pub const DEST_REG_ADDR8: u8 = 0b0111;
|
|
||||||
/* immediates - not used, invalid */
|
|
||||||
pub const DEST_REG: u8 = 0b1100;
|
|
||||||
|
|
||||||
pub const SOURCE_ADDR64: u8 = 0b0000;
|
|
||||||
pub const SOURCE_ADDR32: u8 = 0b0001;
|
|
||||||
pub const SOURCE_ADDR16: u8 = 0b0010;
|
|
||||||
pub const SOURCE_ADDR8: u8 = 0b0011;
|
|
||||||
pub const SOURCE_REG_ADDR64: u8 = 0b0100;
|
|
||||||
pub const SOURCE_REG_ADDR32: u8 = 0b0101;
|
|
||||||
pub const SOURCE_REG_ADDR16: u8 = 0b0110;
|
|
||||||
pub const SOURCE_REG_ADDR8: u8 = 0b0111;
|
|
||||||
pub const SOURCE_IMM64: u8 = 0b1000;
|
|
||||||
pub const SOURCE_IMM32: u8 = 0b1001;
|
|
||||||
pub const SOURCE_IMM16: u8 = 0b1010;
|
|
||||||
pub const SOURCE_IMM8: u8 = 0b1011;
|
|
||||||
pub const SOURCE_REG: u8 = 0b1100;
|
|
||||||
278
src/vm/mem.rs
278
src/vm/mem.rs
@@ -1,278 +0,0 @@
|
|||||||
use crate::vm::{addr::*, error::*, inst::*, reg::*};
|
|
||||||
use byteorder::{ReadBytesExt, WriteBytesExt, LE};
|
|
||||||
use std::io::Cursor;
|
|
||||||
|
|
||||||
pub struct MemCursor<T> {
|
|
||||||
cursor: Cursor<T>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> MemCursor<T>
|
|
||||||
where Cursor<T>: ReadBytesExt,
|
|
||||||
T: AsRef<[u8]>
|
|
||||||
{
|
|
||||||
pub fn new(mem: T) -> Self {
|
|
||||||
MemCursor { cursor: Cursor::new(mem) }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn position(&self) -> u64 {
|
|
||||||
self.cursor.position()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_position<P: Into<Addr>>(&mut self, position: P) {
|
|
||||||
self.cursor.set_position((position.into()).0)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn next_u8_unchecked(&mut self) -> u8 {
|
|
||||||
self.cursor.read_u8().unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn next_u8(&mut self) -> Result<u8> {
|
|
||||||
self.check_addr(self.position())
|
|
||||||
.map(|_| self.next_u8_unchecked())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn next_u16_unchecked(&mut self) -> u16 {
|
|
||||||
self.cursor.read_u16::<LE>().unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn next_u16(&mut self) -> Result<u16> {
|
|
||||||
self.check_addr(self.position())
|
|
||||||
.map(|_| self.next_u16_unchecked())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn next_u32_unchecked(&mut self) -> u32 {
|
|
||||||
self.cursor.read_u32::<LE>().unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn next_u32(&mut self) -> Result<u32> {
|
|
||||||
self.check_addr(self.position())
|
|
||||||
.map(|_| self.next_u32_unchecked())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn next_u64_unchecked(&mut self) -> u64 {
|
|
||||||
self.cursor.read_u64::<LE>().unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn next_u64(&mut self) -> Result<u64> {
|
|
||||||
self.check_addr(self.position())
|
|
||||||
.map(|_| self.next_u64_unchecked())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn next_addr(&mut self) -> Result<Addr> {
|
|
||||||
self.check_addr(self.position())
|
|
||||||
.map(|_| self.next_addr_unchecked())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn next_addr_unchecked(&mut self) -> Addr {
|
|
||||||
Addr(self.next_u64_unchecked())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn next_inst(&mut self) -> Result<Inst> {
|
|
||||||
let start = self.position();
|
|
||||||
let op = self.next_u16()?;
|
|
||||||
|
|
||||||
macro_rules! dest_source {
|
|
||||||
($variant:ident) => {{
|
|
||||||
let (d, s) = self.next_dest_source()?;
|
|
||||||
Ok(Inst::$variant(d, s))
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
macro_rules! source_source {
|
|
||||||
($variant:ident) => {{
|
|
||||||
let (s1, s2) = self.next_source_source()?;
|
|
||||||
Ok(Inst::$variant(s1, s2))
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
macro_rules! source {
|
|
||||||
($variant:ident) => {{
|
|
||||||
let spec = (self.next_u8()? & 0xF0) >> 4;
|
|
||||||
let source = self.next_source(spec)?;
|
|
||||||
Ok(Inst::$variant(source))
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
macro_rules! dest {
|
|
||||||
($variant:ident) => {{
|
|
||||||
let spec = (self.next_u8()? & 0xF0) >> 4;
|
|
||||||
let dest = self.next_dest(spec)?;
|
|
||||||
Ok(Inst::$variant(dest))
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
let inst = match op {
|
|
||||||
ADD => dest_source!(Add),
|
|
||||||
SUB => dest_source!(Sub),
|
|
||||||
MUL => dest_source!(Mul),
|
|
||||||
DIV => dest_source!(Div),
|
|
||||||
IDIV => dest_source!(IDiv),
|
|
||||||
MOD => dest_source!(Mod),
|
|
||||||
AND => dest_source!(And),
|
|
||||||
OR => dest_source!(Or),
|
|
||||||
XOR => dest_source!(Xor),
|
|
||||||
SHL => dest_source!(Shl),
|
|
||||||
SHR => dest_source!(Shr),
|
|
||||||
INEG => dest_source!(INeg),
|
|
||||||
INV => dest_source!(Inv),
|
|
||||||
NOT => dest_source!(Not),
|
|
||||||
CMPEQ => source_source!(CmpEq),
|
|
||||||
CMPLT => source_source!(CmpLt),
|
|
||||||
JMP => source!(Jmp),
|
|
||||||
JZ => source!(Jz),
|
|
||||||
JNZ => source!(Jnz),
|
|
||||||
CALL => source!(Call),
|
|
||||||
RET => Ok(Inst::Ret),
|
|
||||||
PUSH => source!(Push),
|
|
||||||
POP => dest!(Pop),
|
|
||||||
MOV => dest_source!(Mov),
|
|
||||||
HALT => Ok(Inst::Halt),
|
|
||||||
NOP => Ok(Inst::Nop),
|
|
||||||
DUMP => Ok(Inst::Dump),
|
|
||||||
_ => Err(VmError::IllegalOp { op }),
|
|
||||||
}?;
|
|
||||||
let end = self.position();
|
|
||||||
let len = (end - start) as usize;
|
|
||||||
assert_eq!(len, inst.len());
|
|
||||||
Ok(inst)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn next_source_source(&mut self) -> Result<(Source, Source)> {
|
|
||||||
let spec = self.next_u8()?;
|
|
||||||
let s1_spec = (spec & 0xF0) >> 4;
|
|
||||||
let s2_spec = spec & 0x0F;
|
|
||||||
let s1 = self.next_source(s1_spec)?;
|
|
||||||
let s2 = self.next_source(s2_spec)?;
|
|
||||||
Ok((s1, s2))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn next_dest_source(&mut self) -> Result<(Dest, Source)> {
|
|
||||||
let spec = self.next_u8()?;
|
|
||||||
let dest_spec = (spec & 0xF0) >> 4;
|
|
||||||
let source_spec = spec & 0x0F;
|
|
||||||
let dest = self.next_dest(dest_spec)?;
|
|
||||||
let source = self.next_source(source_spec)?;
|
|
||||||
Ok((dest, source))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn next_dest(&mut self, spec: u8) -> Result<Dest> {
|
|
||||||
match spec {
|
|
||||||
DEST_ADDR64 => Ok(Dest::Addr64(self.next_addr()?)),
|
|
||||||
DEST_ADDR32 => Ok(Dest::Addr32(self.next_addr()?)),
|
|
||||||
DEST_ADDR16 => Ok(Dest::Addr16(self.next_addr()?)),
|
|
||||||
DEST_ADDR8 => Ok(Dest::Addr8(self.next_addr()?)),
|
|
||||||
DEST_REG_ADDR64 => Ok(Dest::RegAddr64(self.next_reg()?)),
|
|
||||||
DEST_REG_ADDR32 => Ok(Dest::RegAddr32(self.next_reg()?)),
|
|
||||||
DEST_REG_ADDR16 => Ok(Dest::RegAddr16(self.next_reg()?)),
|
|
||||||
DEST_REG_ADDR8 => Ok(Dest::RegAddr8(self.next_reg()?)),
|
|
||||||
DEST_REG => Ok(Dest::Reg(self.next_reg()?)),
|
|
||||||
_ => Err(VmError::IllegalDestSpec { spec }),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn next_source(&mut self, spec: u8) -> Result<Source> {
|
|
||||||
match spec {
|
|
||||||
SOURCE_ADDR64 => Ok(Source::Addr64(self.next_addr()?)),
|
|
||||||
SOURCE_ADDR32 => Ok(Source::Addr32(self.next_addr()?)),
|
|
||||||
SOURCE_ADDR16 => Ok(Source::Addr16(self.next_addr()?)),
|
|
||||||
SOURCE_ADDR8 => Ok(Source::Addr8(self.next_addr()?)),
|
|
||||||
SOURCE_REG_ADDR64 => Ok(Source::RegAddr64(self.next_reg()?)),
|
|
||||||
SOURCE_REG_ADDR32 => Ok(Source::RegAddr32(self.next_reg()?)),
|
|
||||||
SOURCE_REG_ADDR16 => Ok(Source::RegAddr16(self.next_reg()?)),
|
|
||||||
SOURCE_REG_ADDR8 => Ok(Source::RegAddr8(self.next_reg()?)),
|
|
||||||
SOURCE_REG => Ok(Source::Reg(self.next_reg()?)),
|
|
||||||
SOURCE_IMM64 => Ok(Source::Imm(self.next_u64()?)),
|
|
||||||
SOURCE_IMM32 => Ok(Source::Imm(self.next_u32()? as u64)),
|
|
||||||
SOURCE_IMM16 => Ok(Source::Imm(self.next_u16()? as u64)),
|
|
||||||
SOURCE_IMM8 => Ok(Source::Imm(self.next_u8()? as u64)),
|
|
||||||
_ => Err(VmError::IllegalSourceSpec { spec }),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn next_reg(&mut self) -> Result<Reg> {
|
|
||||||
let reg = self.next_u8()?;
|
|
||||||
if (reg as usize) >= NUM_REGS {
|
|
||||||
Err(VmError::IllegalReg { reg })
|
|
||||||
} else {
|
|
||||||
Ok(reg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> MemCursor<T>
|
|
||||||
where T: AsRef<[u8]>
|
|
||||||
{
|
|
||||||
fn check_addr(&self, addr: u64) -> Result<()> {
|
|
||||||
if addr > (self.cursor.get_ref().as_ref().len() as u64) {
|
|
||||||
Err(VmError::MemOutOfBounds { addr: Addr(addr) })
|
|
||||||
} else {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> MemCursor<T>
|
|
||||||
where Cursor<T>: WriteBytesExt,
|
|
||||||
T: AsRef<[u8]>
|
|
||||||
{
|
|
||||||
pub fn write_u8_unchecked(&mut self, value: u8) {
|
|
||||||
self.cursor.write_u8(value).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write_u8(&mut self, value: u8) -> Result<()> {
|
|
||||||
self.check_addr(self.position())
|
|
||||||
.map(|_| self.write_u8_unchecked(value))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write_u16_unchecked(&mut self, value: u16) {
|
|
||||||
self.cursor.write_u16::<LE>(value).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write_u16(&mut self, value: u16) -> Result<()> {
|
|
||||||
self.check_addr(self.position())
|
|
||||||
.map(|_| self.write_u16_unchecked(value))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write_u32_unchecked(&mut self, value: u32) {
|
|
||||||
self.cursor.write_u32::<LE>(value).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write_u32(&mut self, value: u32) -> Result<()> {
|
|
||||||
self.check_addr(self.position())
|
|
||||||
.map(|_| self.write_u32_unchecked(value))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write_u64_unchecked(&mut self, value: u64) {
|
|
||||||
self.cursor.write_u64::<LE>(value).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write_u64(&mut self, value: u64) -> Result<()> {
|
|
||||||
self.check_addr(self.position())
|
|
||||||
.map(|_| self.write_u64_unchecked(value))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Index impl
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
impl<T: AsRef<[u8]>> Index<usize> for MemCursor<T> {
|
|
||||||
type Output = u8;
|
|
||||||
|
|
||||||
fn index(&self, addr: usize) -> &Self::Output {
|
|
||||||
self.mem.as_ref().index(addr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: AsRef<[u8]>> Index<u64> for MemCursor<T> {
|
|
||||||
type Output = u8;
|
|
||||||
|
|
||||||
fn index(&self, addr: u64) -> &Self::Output {
|
|
||||||
self.index(addr as usize)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: AsRef<[u8]>> Index<Addr> for MemCursor<T> {
|
|
||||||
type Output = u8;
|
|
||||||
|
|
||||||
fn index(&self, addr: Addr) -> &Self::Output {
|
|
||||||
self.index(addr.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
pub mod addr;
|
|
||||||
pub mod error;
|
|
||||||
pub mod flags;
|
|
||||||
pub mod inst;
|
|
||||||
pub mod mem;
|
|
||||||
pub mod obj;
|
|
||||||
pub mod reg;
|
|
||||||
pub mod state;
|
|
||||||
@@ -1,360 +0,0 @@
|
|||||||
pub mod error;
|
|
||||||
pub mod session;
|
|
||||||
mod includes;
|
|
||||||
mod names;
|
|
||||||
|
|
||||||
use self::{error::*, session::AsmSession};
|
|
||||||
use crate::vm::{
|
|
||||||
addr::Addr,
|
|
||||||
inst,
|
|
||||||
obj::{
|
|
||||||
obj::{self, Object},
|
|
||||||
syn::ast::*,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
use byteorder::{WriteBytesExt, LE};
|
|
||||||
use std::{collections::HashMap, path::Path};
|
|
||||||
|
|
||||||
pub trait Asm {
|
|
||||||
type Out;
|
|
||||||
fn assemble(&self, asm: &mut AsmSession) -> Result<Self::Out>;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn assemble_path(path: impl AsRef<Path>) -> Result<Object> {
|
|
||||||
let mut session = AsmSession::default();
|
|
||||||
session.include_path(path)?;
|
|
||||||
session.assemble()
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Asm for Vec<&'_ Directive> {
|
|
||||||
type Out = obj::Object;
|
|
||||||
fn assemble(&self, asm: &mut AsmSession) -> Result<Self::Out> {
|
|
||||||
let sections = self
|
|
||||||
.iter()
|
|
||||||
.filter_map(|section| section.assemble(asm).transpose())
|
|
||||||
.collect::<Result<_>>()?;
|
|
||||||
Ok(obj::Object {
|
|
||||||
version: obj::OBJ_VERSION,
|
|
||||||
sections,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Asm for Directive {
|
|
||||||
type Out = Option<obj::Section>;
|
|
||||||
|
|
||||||
fn assemble(&self, asm: &mut AsmSession) -> Result<Self::Out> {
|
|
||||||
match self {
|
|
||||||
Directive::Data(section) => Ok(Some(obj::Section::Data(section.assemble(asm)?))),
|
|
||||||
Directive::Meta(section) => section.assemble(asm).map(Some),
|
|
||||||
Directive::Include(_) => { Ok(None) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Asm for DataSection {
|
|
||||||
type Out = obj::DataSection;
|
|
||||||
|
|
||||||
fn assemble(&self, session: &mut AsmSession) -> Result<Self::Out> {
|
|
||||||
let names = names::get_section_names(self)?;
|
|
||||||
session.name_stack.push(names);
|
|
||||||
let section_len = self.len() as u64;
|
|
||||||
let (start, end) = match self.org {
|
|
||||||
SectionOrg::Start(start) => (start, start + (section_len as u64)),
|
|
||||||
SectionOrg::StartEnd(start, end) => (start, end),
|
|
||||||
};
|
|
||||||
session.pos = Addr(start);
|
|
||||||
if start > end {
|
|
||||||
return Err(AsmError::StartGreaterThanEnd { start, end });
|
|
||||||
}
|
|
||||||
let len = end - start - 1;
|
|
||||||
if len > section_len {
|
|
||||||
return Err(AsmError::SectionTooShort {
|
|
||||||
section_end: end,
|
|
||||||
section_size: start + section_len,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut contents = Vec::with_capacity(section_len as usize);
|
|
||||||
for line in self.lines.iter() {
|
|
||||||
contents.extend(line.assemble(session)?);
|
|
||||||
session.pos += line.len();
|
|
||||||
}
|
|
||||||
assert_eq!(
|
|
||||||
contents.len() as u64,
|
|
||||||
section_len,
|
|
||||||
"in section {}",
|
|
||||||
self.name
|
|
||||||
);
|
|
||||||
session.name_stack.pop();
|
|
||||||
Ok(obj::DataSection {
|
|
||||||
name: self.name.clone(),
|
|
||||||
start,
|
|
||||||
len: section_len,
|
|
||||||
contents,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Asm for MetaSection {
|
|
||||||
type Out = obj::Section;
|
|
||||||
|
|
||||||
fn assemble(&self, session: &mut AsmSession) -> Result<Self::Out> {
|
|
||||||
let mut entries = HashMap::new();
|
|
||||||
for line in self.lines.iter() {
|
|
||||||
if entries.contains_key(&line.name) {
|
|
||||||
return Err(AsmError::DuplicateMetaName {
|
|
||||||
name: line.name.to_string(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
let value = match &line.value {
|
|
||||||
Value::Int(i) => *i,
|
|
||||||
Value::Name(s) => session.lookup_name(s.as_str())
|
|
||||||
.ok_or_else(|| AsmError::UnknownName { name: s.to_string() })?.addr.0,
|
|
||||||
Value::Reg(_) | Value::Here | Value::Addr(_, _) => {
|
|
||||||
return Err(AsmError::IllegalMetaValue {
|
|
||||||
name: line.name.to_string(),
|
|
||||||
value: line.value.clone(),
|
|
||||||
})
|
|
||||||
} // TODO :
|
|
||||||
// * deref constexpr?
|
|
||||||
// * pre-startup static init?
|
|
||||||
};
|
|
||||||
entries.insert(line.name.to_string(), value);
|
|
||||||
}
|
|
||||||
Ok(obj::Section::Meta(obj::MetaSection { entries }))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Asm for DataLine {
|
|
||||||
type Out = Vec<u8>;
|
|
||||||
|
|
||||||
fn assemble(&self, session: &mut AsmSession) -> Result<Self::Out> {
|
|
||||||
match self {
|
|
||||||
DataLine::ValueDef(v) => v.assemble(session),
|
|
||||||
DataLine::Inst(i) => i.assemble(session),
|
|
||||||
DataLine::Export(_) | DataLine::Label(_) => Ok(Vec::new()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Asm for ValueDef {
|
|
||||||
type Out = Vec<u8>;
|
|
||||||
|
|
||||||
fn assemble(&self, _: &mut AsmSession) -> Result<Self::Out> {
|
|
||||||
match self {
|
|
||||||
ValueDef::Int(x, s) => Ok(x.to_le_bytes().iter().copied().take(s.len()).collect()),
|
|
||||||
ValueDef::String(s) => {
|
|
||||||
let bytes = s.bytes();
|
|
||||||
let mut out = s.len().to_le_bytes().to_vec();
|
|
||||||
out.extend(bytes);
|
|
||||||
Ok(out)
|
|
||||||
}
|
|
||||||
ValueDef::ZString(z) => {
|
|
||||||
let bytes = z.bytes();
|
|
||||||
let mut out = z.len().to_le_bytes().to_vec();
|
|
||||||
out.extend(bytes);
|
|
||||||
Ok(out)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Asm for Inst {
|
|
||||||
type Out = Vec<u8>;
|
|
||||||
|
|
||||||
fn assemble(&self, session: &mut AsmSession) -> Result<Self::Out> {
|
|
||||||
let len = self.len();
|
|
||||||
|
|
||||||
macro_rules! map_inst {
|
|
||||||
($op:expr, $dest:expr, $source:expr) => {{
|
|
||||||
let mut bytes = Vec::with_capacity(len);
|
|
||||||
bytes.write_u16::<LE>($op).unwrap();
|
|
||||||
let dest = $dest;
|
|
||||||
let dest_encoding =
|
|
||||||
dest.dest_encoding()
|
|
||||||
.ok_or_else(|| AsmError::IllegalDestValue {
|
|
||||||
value: dest.clone(),
|
|
||||||
})?;
|
|
||||||
let source = $source;
|
|
||||||
let source_encoding = source.source_encoding();
|
|
||||||
bytes
|
|
||||||
.write_u8((dest_encoding << 4) | source_encoding)
|
|
||||||
.unwrap();
|
|
||||||
bytes.extend(dest.assemble(session)?);
|
|
||||||
bytes.extend(source.assemble(session)?);
|
|
||||||
assert_eq!(
|
|
||||||
self.len(),
|
|
||||||
bytes.len(),
|
|
||||||
"instruction size mismatch in {} instruction - {:?} produces these bytes {:?}",
|
|
||||||
stringify!($op),
|
|
||||||
self,
|
|
||||||
bytes
|
|
||||||
);
|
|
||||||
Ok(bytes)
|
|
||||||
}};
|
|
||||||
|
|
||||||
($op:expr, $source:expr) => {{
|
|
||||||
let mut bytes = Vec::with_capacity(len);
|
|
||||||
bytes.write_u16::<LE>($op).unwrap();
|
|
||||||
let source = $source;
|
|
||||||
let source_encoding = source.source_encoding() << 4;
|
|
||||||
bytes.write_u8(source_encoding).unwrap();
|
|
||||||
bytes.extend(source.assemble(session)?);
|
|
||||||
assert_eq!(
|
|
||||||
self.len(),
|
|
||||||
bytes.len(),
|
|
||||||
"instruction size mismatch in {} instruction - {:?} produces these bytes {:?}",
|
|
||||||
stringify!($op),
|
|
||||||
self,
|
|
||||||
bytes
|
|
||||||
);
|
|
||||||
Ok(bytes)
|
|
||||||
}};
|
|
||||||
|
|
||||||
($op:expr) => {{
|
|
||||||
let mut bytes = Vec::with_capacity(len);
|
|
||||||
bytes.write_u16::<LE>($op).unwrap();
|
|
||||||
assert_eq!(
|
|
||||||
self.len(),
|
|
||||||
bytes.len(),
|
|
||||||
"instruction size mismatch in {} instruction - {:?} produces these bytes {:?}",
|
|
||||||
stringify!($op),
|
|
||||||
self,
|
|
||||||
bytes
|
|
||||||
);
|
|
||||||
Ok(bytes)
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
match self {
|
|
||||||
Inst::Add(v1, v2) => map_inst!(inst::ADD, v1, v2),
|
|
||||||
Inst::Sub(v1, v2) => map_inst!(inst::SUB, v1, v2),
|
|
||||||
Inst::Mul(v1, v2) => map_inst!(inst::MUL, v1, v2),
|
|
||||||
Inst::Div(v1, v2) => map_inst!(inst::DIV, v1, v2),
|
|
||||||
Inst::IDiv(v1, v2) => map_inst!(inst::IDIV, v1, v2),
|
|
||||||
Inst::Mod(v1, v2) => map_inst!(inst::MOD, v1, v2),
|
|
||||||
Inst::And(v1, v2) => map_inst!(inst::AND, v1, v2),
|
|
||||||
Inst::Or(v1, v2) => map_inst!(inst::OR, v1, v2),
|
|
||||||
Inst::Xor(v1, v2) => map_inst!(inst::XOR, v1, v2),
|
|
||||||
Inst::Shl(v1, v2) => map_inst!(inst::SHL, v1, v2),
|
|
||||||
Inst::Shr(v1, v2) => map_inst!(inst::SHR, v1, v2),
|
|
||||||
Inst::INeg(v1, v2) => map_inst!(inst::INEG, v1, v2),
|
|
||||||
Inst::Inv(v1, v2) => map_inst!(inst::INV, v1, v2),
|
|
||||||
Inst::Not(v1, v2) => map_inst!(inst::NOT, v1, v2),
|
|
||||||
// TODO/BUG: CmpEq and CmpLt both take two sources instead of a source and destination
|
|
||||||
Inst::CmpEq(v1, v2) => map_inst!(inst::CMPEQ, v1, v2),
|
|
||||||
Inst::CmpLt(v1, v2) => map_inst!(inst::CMPLT, v1, v2),
|
|
||||||
Inst::Jmp(v) => map_inst!(inst::JMP, v),
|
|
||||||
Inst::Jz(v) => map_inst!(inst::JZ, v),
|
|
||||||
Inst::Jnz(v) => map_inst!(inst::JNZ, v),
|
|
||||||
Inst::Call(v) => map_inst!(inst::CALL, v),
|
|
||||||
Inst::Ret => map_inst!(inst::RET),
|
|
||||||
Inst::Push(v) => map_inst!(inst::PUSH, v),
|
|
||||||
Inst::Pop(dest) => {
|
|
||||||
let mut bytes = Vec::with_capacity(len);
|
|
||||||
bytes.write_u16::<LE>(inst::POP).unwrap();
|
|
||||||
let dest_encoding = dest.source_encoding() << 4;
|
|
||||||
bytes.write_u8(dest_encoding).unwrap();
|
|
||||||
bytes.extend(dest.assemble(session)?);
|
|
||||||
assert_eq!(
|
|
||||||
self.len(),
|
|
||||||
bytes.len(),
|
|
||||||
"instruction size mismatch in inst::PUSH instruction - {:?} produces these bytes {:?}",
|
|
||||||
self,
|
|
||||||
bytes
|
|
||||||
);
|
|
||||||
Ok(bytes)
|
|
||||||
}
|
|
||||||
Inst::Mov(v1, v2) => map_inst!(inst::MOV, v1, v2),
|
|
||||||
Inst::Halt => map_inst!(inst::HALT),
|
|
||||||
Inst::Nop => map_inst!(inst::NOP),
|
|
||||||
Inst::Dump => map_inst!(inst::DUMP),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Asm for Value {
|
|
||||||
type Out = Vec<u8>;
|
|
||||||
|
|
||||||
fn assemble(&self, session: &mut AsmSession) -> Result<Self::Out> {
|
|
||||||
match self {
|
|
||||||
Value::Int(i) => Ok(i.to_le_bytes().to_vec()),
|
|
||||||
Value::Reg(r) => Ok(vec![*r]),
|
|
||||||
Value::Name(name) => {
|
|
||||||
let value = session.lookup_name(name.as_str())
|
|
||||||
.ok_or_else(|| AsmError::UnknownName { name: name.to_string() })?;
|
|
||||||
Ok(value.addr.0.to_le_bytes().to_vec())
|
|
||||||
}
|
|
||||||
Value::Here => Ok(session.pos.0.to_le_bytes().to_vec()),
|
|
||||||
Value::Addr(v, _) => {
|
|
||||||
if let Value::Addr(_, _) = &**v {
|
|
||||||
// double deref is not allowed
|
|
||||||
todo!()
|
|
||||||
} else {
|
|
||||||
v.assemble(session)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_inst_len() {
|
|
||||||
let mut session = AsmSession::default();
|
|
||||||
//asm.names
|
|
||||||
//.push(vec![("test".to_string(), Addr(0u64))].into_iter().collect());
|
|
||||||
|
|
||||||
macro_rules! assert_len {
|
|
||||||
($inst:expr) => {{
|
|
||||||
let inst = $inst;
|
|
||||||
let asm_size = $inst.assemble(&mut session).unwrap().len();
|
|
||||||
assert_eq!(inst.len(), asm_size, "Instruction {:?}.len() indicates it should be {} bytes long but was assembled as {} bytes", inst, inst.len(), asm_size);
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
||||||
use Inst::*;
|
|
||||||
|
|
||||||
let dummy_dests = &[
|
|
||||||
Value::Reg(0),
|
|
||||||
Value::Addr(Box::new(Value::Reg(0)), IntSize::U8),
|
|
||||||
Value::Addr(Box::new(Value::Here), IntSize::U16),
|
|
||||||
//Value::Addr(Box::new(Value::Name("test".to_string())), IntSize::U32),
|
|
||||||
Value::Addr(Box::new(Value::Int(0)), IntSize::U64),
|
|
||||||
];
|
|
||||||
|
|
||||||
let dummy_sources = &[
|
|
||||||
Value::Int(0),
|
|
||||||
Value::Reg(0),
|
|
||||||
//Value::Name("test".to_string()),
|
|
||||||
Value::Here,
|
|
||||||
Value::Addr(Box::new(Value::Reg(0)), IntSize::U8),
|
|
||||||
Value::Addr(Box::new(Value::Here), IntSize::U16),
|
|
||||||
//Value::Addr(Box::new(Value::Name("test".to_string())), IntSize::U32),
|
|
||||||
Value::Addr(Box::new(Value::Int(0)), IntSize::U32),
|
|
||||||
];
|
|
||||||
|
|
||||||
for v1 in dummy_dests {
|
|
||||||
for v2 in dummy_sources {
|
|
||||||
assert_len!(Add(v1.clone(), v2.clone()));
|
|
||||||
assert_len!(Sub(v1.clone(), v2.clone()));
|
|
||||||
assert_len!(Mul(v1.clone(), v2.clone()));
|
|
||||||
assert_len!(Div(v1.clone(), v2.clone()));
|
|
||||||
assert_len!(Mod(v1.clone(), v2.clone()));
|
|
||||||
assert_len!(And(v1.clone(), v2.clone()));
|
|
||||||
assert_len!(Or(v1.clone(), v2.clone()));
|
|
||||||
assert_len!(Xor(v1.clone(), v2.clone()));
|
|
||||||
assert_len!(Shl(v1.clone(), v2.clone()));
|
|
||||||
assert_len!(Shr(v1.clone(), v2.clone()));
|
|
||||||
assert_len!(INeg(v1.clone(), v2.clone()));
|
|
||||||
assert_len!(Inv(v1.clone(), v2.clone()));
|
|
||||||
assert_len!(Not(v1.clone(), v2.clone()));
|
|
||||||
assert_len!(Mov(v1.clone(), v2.clone()));
|
|
||||||
// TODO more length tests
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,114 +0,0 @@
|
|||||||
use crate::vm::obj::syn::ast::*;
|
|
||||||
use snafu::Snafu;
|
|
||||||
use std::path::PathBuf;
|
|
||||||
|
|
||||||
pub type LexError = lrpar::LexError;
|
|
||||||
pub type ParseError = lrpar::ParseError<u32>;
|
|
||||||
pub type LexParseError = lrpar::LexParseError<u32>;
|
|
||||||
|
|
||||||
#[derive(Debug, Snafu)]
|
|
||||||
pub enum AsmError {
|
|
||||||
#[snafu(display("unknown name: {}", name))]
|
|
||||||
UnknownName {
|
|
||||||
name: String,
|
|
||||||
},
|
|
||||||
|
|
||||||
#[snafu(display("unknown export name: {}", name))]
|
|
||||||
UnknownExport {
|
|
||||||
name: String,
|
|
||||||
},
|
|
||||||
|
|
||||||
#[snafu(display("duplicate label definition: {}", name))]
|
|
||||||
DuplicateLabel {
|
|
||||||
name: String,
|
|
||||||
},
|
|
||||||
|
|
||||||
#[snafu(display("duplicate meta entry name: {}", name))]
|
|
||||||
DuplicateMetaName {
|
|
||||||
name: String,
|
|
||||||
},
|
|
||||||
|
|
||||||
#[snafu(display("illegal meta value for entry name {}: {:?}", name, value))]
|
|
||||||
IllegalMetaValue {
|
|
||||||
name: String,
|
|
||||||
value: Value,
|
|
||||||
},
|
|
||||||
|
|
||||||
#[snafu(display("duplicate exported name: {}", name))]
|
|
||||||
DuplicateExport {
|
|
||||||
name: String,
|
|
||||||
},
|
|
||||||
|
|
||||||
#[snafu(display("section start ({:#x}) is greater than end ({:#x})", start, end))]
|
|
||||||
StartGreaterThanEnd {
|
|
||||||
start: u64,
|
|
||||||
end: u64,
|
|
||||||
},
|
|
||||||
|
|
||||||
#[snafu(display(
|
|
||||||
"section end ({:#x}) too short for section content size ({:#x})",
|
|
||||||
section_end,
|
|
||||||
section_size
|
|
||||||
))]
|
|
||||||
SectionTooShort {
|
|
||||||
section_end: u64,
|
|
||||||
section_size: u64,
|
|
||||||
},
|
|
||||||
|
|
||||||
#[snafu(display("illegal instruction destination value: {:?}", value))]
|
|
||||||
IllegalDestValue {
|
|
||||||
value: Value,
|
|
||||||
},
|
|
||||||
|
|
||||||
#[snafu(display("deref of a deref value is not allowed"))]
|
|
||||||
DoubleDeref {
|
|
||||||
value: Value,
|
|
||||||
},
|
|
||||||
|
|
||||||
// TODO(asm) : Path error wrapper for assembling things
|
|
||||||
#[snafu(display("could not read path: {}", path.display()))]
|
|
||||||
BadPath {
|
|
||||||
path: PathBuf,
|
|
||||||
},
|
|
||||||
|
|
||||||
Syntax {
|
|
||||||
source: SyntaxError,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Snafu)]
|
|
||||||
pub enum SyntaxError {
|
|
||||||
Lex { source: LexError },
|
|
||||||
Parse { source: ParseError },
|
|
||||||
Multi { errors: Vec<LexParseError> },
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<LexError> for SyntaxError {
|
|
||||||
fn from(source: LexError) -> Self {
|
|
||||||
SyntaxError::Lex { source }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<ParseError> for SyntaxError {
|
|
||||||
fn from(source: ParseError) -> Self {
|
|
||||||
SyntaxError::Parse { source }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Vec<LexParseError>> for SyntaxError {
|
|
||||||
fn from(errors: Vec<LexParseError>) -> Self {
|
|
||||||
SyntaxError::Multi { errors }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<LexParseError> for SyntaxError {
|
|
||||||
fn from(source: LexParseError) -> Self {
|
|
||||||
match source {
|
|
||||||
LexParseError::LexError(e) => SyntaxError::Lex { source: e },
|
|
||||||
LexParseError::ParseError(e) => SyntaxError::Parse { source: e },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type Result<T, E = AsmError> = std::result::Result<T, E>;
|
|
||||||
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
use crate::vm::obj::{
|
|
||||||
assemble::{error::*, session::AsmSession, Asm},
|
|
||||||
syn::{ast::Directive, lexer, parser},
|
|
||||||
};
|
|
||||||
use std::{path::PathBuf, rc::Rc};
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct GetIncludes {
|
|
||||||
path: PathBuf,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GetIncludes {
|
|
||||||
pub fn assemble_from_path(path: PathBuf, session: &mut AsmSession) -> Result<()> {
|
|
||||||
let path = path
|
|
||||||
.canonicalize()
|
|
||||||
.map_err(|_| AsmError::BadPath { path })?;
|
|
||||||
GetIncludes { path }.assemble(session)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Asm for GetIncludes {
|
|
||||||
type Out = ();
|
|
||||||
|
|
||||||
fn assemble(&self, session: &mut AsmSession) -> Result<Self::Out> {
|
|
||||||
assert!(self.path.is_absolute());
|
|
||||||
if session.includes.contains_key(&self.path) {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
let text = std::fs::read_to_string(&self.path).map_err(|_| AsmError::BadPath {
|
|
||||||
path: self.path.clone(),
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let lexerdef = lexer::lexerdef();
|
|
||||||
let lexer = lexerdef.lexer(&text);
|
|
||||||
let (res, errors) = parser::parse(&lexer);
|
|
||||||
|
|
||||||
if !errors.is_empty() {
|
|
||||||
return Err(AsmError::Syntax {
|
|
||||||
source: errors.into(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// insert a dummy AST in the includes and replace it when we're done with the actual AST
|
|
||||||
Rc::get_mut(&mut session.includes)
|
|
||||||
.unwrap()
|
|
||||||
.insert(self.path.clone(), Default::default());
|
|
||||||
session.include_stack.push(self.path.clone());
|
|
||||||
let ast = res.unwrap();
|
|
||||||
for directive in ast.iter() {
|
|
||||||
if let Directive::Include(include_path) = directive {
|
|
||||||
GetIncludes::assemble_from_path(PathBuf::from(include_path), session)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
session.include_stack.pop();
|
|
||||||
|
|
||||||
let dummy = Rc::get_mut(&mut session.includes)
|
|
||||||
.unwrap()
|
|
||||||
.insert(self.path.clone(), ast)
|
|
||||||
.expect("ast was removed after its inclusion (why did you do that?)");
|
|
||||||
assert!(
|
|
||||||
dummy.is_empty(),
|
|
||||||
"ast was modified after its inclusion (why did you do that?)"
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,104 +0,0 @@
|
|||||||
use crate::vm::{
|
|
||||||
addr::Addr,
|
|
||||||
obj::{
|
|
||||||
assemble::{
|
|
||||||
session::AsmSession,
|
|
||||||
error::*,
|
|
||||||
},
|
|
||||||
syn::ast::{DataSection, DataLine, Directive},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
use std::{collections::{HashMap, HashSet}, rc::Rc};
|
|
||||||
|
|
||||||
// TODO(asm) make custom Names type that has "merge" that will catch errors with duplicate names
|
|
||||||
pub type Names = HashMap<String, Name>;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
||||||
pub struct Name {
|
|
||||||
pub name: String,
|
|
||||||
pub addr: Addr,
|
|
||||||
pub export: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_section_names(section: &DataSection) -> Result<Names> {
|
|
||||||
let mut pos = Addr(section.org.start());
|
|
||||||
let mut names = HashMap::new();
|
|
||||||
let mut exports = HashSet::new();
|
|
||||||
|
|
||||||
// This isn't immediately straightforward in code, so this is what's happening.
|
|
||||||
//
|
|
||||||
// A name may be specified exactly once. It is added to the "names" mapping. If the name is
|
|
||||||
// specified again, it is a duplicate error and handled as such. A name may be flagged as
|
|
||||||
// being exported.
|
|
||||||
//
|
|
||||||
// Exports are gathered at the beginning into a set. Afterwards, as name definitions
|
|
||||||
// are gathered, exports are removed from their set if they exist. Since duplicate names
|
|
||||||
// cause an error, export names will only be removed once.
|
|
||||||
//
|
|
||||||
// At the end of name gathering, if any exports have not been removed, then we know those
|
|
||||||
// are exports whose names are undefined and we can return an UnknownExport error.
|
|
||||||
|
|
||||||
// get exported names
|
|
||||||
for line in section.lines.iter() {
|
|
||||||
if let DataLine::Export(name) = line {
|
|
||||||
exports.insert(name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// get names
|
|
||||||
for line in section.lines.iter() {
|
|
||||||
if let DataLine::Label(name) = line {
|
|
||||||
if names.contains_key(name) {
|
|
||||||
return Err(AsmError::DuplicateLabel { name: name.clone() });
|
|
||||||
}
|
|
||||||
let export = exports.remove(name);
|
|
||||||
|
|
||||||
names.insert(name.clone(), Name {
|
|
||||||
name: name.clone(),
|
|
||||||
addr: pos,
|
|
||||||
export,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
pos += line.len();
|
|
||||||
}
|
|
||||||
|
|
||||||
// all exports map 1:1 with names
|
|
||||||
if exports.is_empty() {
|
|
||||||
Ok(names)
|
|
||||||
} else {
|
|
||||||
Err(AsmError::UnknownExport { name: exports.iter().next().unwrap().to_string() })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_exports(session: &mut AsmSession) -> Result<Names> {
|
|
||||||
let includes = Rc::clone(&session.includes);
|
|
||||||
let mut all_exports = Vec::new();
|
|
||||||
|
|
||||||
// get *all* exports first
|
|
||||||
for (_, ast) in includes.iter() {
|
|
||||||
for directive in ast.iter() {
|
|
||||||
if let Directive::Data(section) = directive {
|
|
||||||
let names: HashMap<_, _> = get_section_names(section)?
|
|
||||||
.into_iter()
|
|
||||||
.filter(|(_, name)| name.export)
|
|
||||||
.collect();
|
|
||||||
all_exports.push(names);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut exports = HashSet::new();
|
|
||||||
|
|
||||||
// make a set of all export names, erroring on duplicate export
|
|
||||||
for name_map in all_exports.iter() {
|
|
||||||
let names_set: HashSet<_> = name_map.keys().collect();
|
|
||||||
if let Some(dupe) = exports.intersection(&names_set).next() {
|
|
||||||
return Err(AsmError::DuplicateExport { name: dupe.to_string() });
|
|
||||||
}
|
|
||||||
exports.extend(names_set);
|
|
||||||
}
|
|
||||||
|
|
||||||
// NOTE: this can probably be done with a fancy combinator chain
|
|
||||||
Ok(all_exports.into_iter()
|
|
||||||
.fold(Names::new(), |mut acc, val| { acc.extend(val); acc }))
|
|
||||||
}
|
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
use crate::vm::{
|
|
||||||
addr::Addr,
|
|
||||||
obj::{
|
|
||||||
assemble::{
|
|
||||||
Asm,
|
|
||||||
includes::GetIncludes,
|
|
||||||
names::{self, Name, Names},
|
|
||||||
error::*,
|
|
||||||
},
|
|
||||||
obj::Object,
|
|
||||||
syn::ast::Ast,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
use std::{
|
|
||||||
collections::BTreeMap,
|
|
||||||
path::{Path, PathBuf},
|
|
||||||
rc::Rc,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// A shared session for the assembler.
|
|
||||||
#[derive(Debug, Default)]
|
|
||||||
pub struct AsmSession {
|
|
||||||
pub (in super) includes: Rc<BTreeMap<PathBuf, Ast>>,
|
|
||||||
pub (in super) include_search_paths: Vec<PathBuf>,
|
|
||||||
pub (in super) include_stack: Vec<PathBuf>,
|
|
||||||
pub (in super) name_stack: Vec<Names>,
|
|
||||||
pub (in super) pos: Addr,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsmSession {
|
|
||||||
pub fn assemble(&mut self) -> Result<Object> {
|
|
||||||
let includes = Rc::clone(&self.includes);
|
|
||||||
let sections: Vec<_> = includes.iter()
|
|
||||||
.flat_map(|(_, ast)| ast)
|
|
||||||
.collect();
|
|
||||||
sections.assemble(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn include_path(&mut self, path: impl AsRef<Path>) -> Result<()> {
|
|
||||||
self.include_stack.clear();
|
|
||||||
let path = path.as_ref().to_path_buf();
|
|
||||||
|
|
||||||
GetIncludes::assemble_from_path(path, self)?;
|
|
||||||
let exports = names::get_exports(self)?;
|
|
||||||
// TODO(asm) - merge exports
|
|
||||||
self.name_stack.push(exports);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub (in super) fn current_include_path(&self) -> Option<&Path> {
|
|
||||||
self.include_stack.last().map(PathBuf::as_path)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub (in super) fn resolve_include_path(&self, path: impl AsRef<Path>) -> Option<PathBuf> {
|
|
||||||
let path = path.as_ref();
|
|
||||||
self.current_include_path()
|
|
||||||
.and_then(|last_path| last_path.parent())
|
|
||||||
.map(|last_dir| last_dir.join(path))
|
|
||||||
.or_else(|| self.include_search_paths
|
|
||||||
.iter()
|
|
||||||
.filter_map(|include| include.join(path).canonicalize().ok())
|
|
||||||
.next())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn lookup_name(&self, name: &str) -> Option<&Name> {
|
|
||||||
self.name_stack
|
|
||||||
.iter()
|
|
||||||
.rev()
|
|
||||||
.filter_map(|names| names.get(name))
|
|
||||||
.next()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
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 },
|
|
||||||
|
|
||||||
#[snafu(display("duplicate symbol name: {}", name))]
|
|
||||||
DuplicateName { name: String },
|
|
||||||
|
|
||||||
#[snafu(display("duplicate exported symbol name: {}", name))]
|
|
||||||
DuplicateExportName { name: String },
|
|
||||||
}
|
|
||||||
|
|
||||||
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, E = ParseError> = std::result::Result<T, E>;
|
|
||||||
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
pub mod assemble;
|
|
||||||
pub mod error;
|
|
||||||
pub mod obj;
|
|
||||||
pub mod syn;
|
|
||||||
@@ -1,269 +0,0 @@
|
|||||||
use crate::vm::obj::error::{ParseError, Result};
|
|
||||||
use byteorder::{ReadBytesExt, WriteBytesExt, LE};
|
|
||||||
use std::{
|
|
||||||
collections::HashMap,
|
|
||||||
convert::{TryFrom, TryInto},
|
|
||||||
fmt::Debug,
|
|
||||||
io::{Cursor, Read, Write},
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const MAGIC: u64 = 0xDEAD_BEA7_BA5E_BA11;
|
|
||||||
pub const OBJ_VERSION: u32 = 0;
|
|
||||||
const OBJECT_HEADER_LEN: usize = 16; // 8 + 4 + 4
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
|
||||||
pub struct Object {
|
|
||||||
pub version: u32,
|
|
||||||
pub sections: Vec<Section>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Object {
|
|
||||||
pub fn to_bytes(&self) -> Vec<u8> {
|
|
||||||
let mut cursor = Cursor::new(Vec::new());
|
|
||||||
cursor.write_u64::<LE>(MAGIC).unwrap();
|
|
||||||
cursor.write_u32::<LE>(OBJ_VERSION).unwrap();
|
|
||||||
cursor.write_u32::<LE>(self.sections.len() as u32).unwrap();
|
|
||||||
for section in self.sections.iter() {
|
|
||||||
cursor.write(§ion.to_bytes()).unwrap();
|
|
||||||
}
|
|
||||||
cursor.into_inner()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
|
|
||||||
let mut cursor = Cursor::new(bytes);
|
|
||||||
let magic = cursor.read_u64::<LE>()?;
|
|
||||||
if magic != MAGIC {
|
|
||||||
return Err(ParseError::WrongMagic);
|
|
||||||
}
|
|
||||||
let version = cursor.read_u32::<LE>()?;
|
|
||||||
let section_count = cursor.read_u32::<LE>()?;
|
|
||||||
|
|
||||||
let mut sections = Vec::new();
|
|
||||||
for _ in 0..section_count {
|
|
||||||
let start = cursor.position() as usize;
|
|
||||||
let section = Section::from_bytes(&bytes[start..])?;
|
|
||||||
cursor.set_position((start + section.len()) as u64);
|
|
||||||
sections.push(section);
|
|
||||||
}
|
|
||||||
Ok(Object { version, sections })
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn virtual_len(&self) -> usize {
|
|
||||||
self.sections
|
|
||||||
.iter()
|
|
||||||
.map(|s| match s {
|
|
||||||
Section::Data(DataSection { start, len, .. }) => (start + len) as usize,
|
|
||||||
Section::Meta { .. } => 0,
|
|
||||||
})
|
|
||||||
.max()
|
|
||||||
.unwrap_or(0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! section_kind {
|
|
||||||
(
|
|
||||||
pub enum $enum_name:ident {
|
|
||||||
$($name:ident = $value:expr),* $(,)?
|
|
||||||
}
|
|
||||||
) => {
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
||||||
#[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 }),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<SectionKind> for u8 {
|
|
||||||
fn from(other: SectionKind) -> Self {
|
|
||||||
match other {
|
|
||||||
$(
|
|
||||||
$enum_name::$name => $value,
|
|
||||||
)*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
section_kind! {
|
|
||||||
pub enum SectionKind {
|
|
||||||
Data = 0x00,
|
|
||||||
Meta = 0xFF,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
|
||||||
pub enum Section {
|
|
||||||
Data(DataSection),
|
|
||||||
Meta(MetaSection),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Section {
|
|
||||||
pub fn len(&self) -> usize {
|
|
||||||
match self {
|
|
||||||
Section::Data(s) => { 1 + 8 + s.len() },
|
|
||||||
Section::Meta(s) => { 1 + 8 + s.len() },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn to_bytes(&self) -> Vec<u8> {
|
|
||||||
let mut cursor = Cursor::new(Vec::new());
|
|
||||||
let bytes = match self {
|
|
||||||
Section::Data(s) => {
|
|
||||||
cursor.write_u8(SectionKind::Data.into()).unwrap();
|
|
||||||
s.to_bytes()
|
|
||||||
}
|
|
||||||
Section::Meta(s) => {
|
|
||||||
cursor.write_u8(SectionKind::Meta.into()).unwrap();
|
|
||||||
s.to_bytes()
|
|
||||||
},
|
|
||||||
};
|
|
||||||
cursor.write_u64::<LE>(bytes.len() as u64).unwrap();
|
|
||||||
cursor.write(&bytes).unwrap();
|
|
||||||
cursor.into_inner()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
|
|
||||||
let mut cursor = Cursor::new(bytes);
|
|
||||||
let kind: SectionKind = cursor.read_u8()?.try_into()?;
|
|
||||||
let len = cursor.read_u64::<LE>()?;
|
|
||||||
let start = cursor.position() as usize;
|
|
||||||
let end = start + len as usize;
|
|
||||||
|
|
||||||
let bytes = &cursor.get_ref()[start..end];
|
|
||||||
match kind {
|
|
||||||
SectionKind::Data => Ok(Section::Data(DataSection::from_bytes(bytes)?)),
|
|
||||||
SectionKind::Meta => Ok(Section::Meta(MetaSection::from_bytes(bytes)?)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
|
||||||
pub struct DataSection {
|
|
||||||
pub name: String,
|
|
||||||
pub start: u64,
|
|
||||||
pub len: u64,
|
|
||||||
pub contents: Vec<u8>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DataSection {
|
|
||||||
pub fn len(&self) -> usize {
|
|
||||||
2 + self.name.as_bytes().len() + 8 + 8 + self.contents.len()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn to_bytes(&self) -> Vec<u8> {
|
|
||||||
let mut cursor = Cursor::new(Vec::new());
|
|
||||||
assert!(self.name.len() < u16::max_value() as usize);
|
|
||||||
cursor.write_u16::<LE>(self.name.len() as u16).unwrap();
|
|
||||||
cursor.write(self.name.as_bytes()).unwrap();
|
|
||||||
cursor.write_u64::<LE>(self.start).unwrap();
|
|
||||||
cursor.write_u64::<LE>(self.len).unwrap();
|
|
||||||
cursor.write(&self.contents).unwrap();
|
|
||||||
cursor.into_inner()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
|
|
||||||
let mut cursor = Cursor::new(bytes);
|
|
||||||
|
|
||||||
let name_len = cursor.read_u16::<LE>()? as usize;
|
|
||||||
let name_start = cursor.position() as usize;
|
|
||||||
let name_end = name_start + name_len;
|
|
||||||
let name_bytes = &bytes[name_start .. name_end];
|
|
||||||
let name_string = String::from_utf8(name_bytes.to_vec())?;
|
|
||||||
cursor.set_position(name_end as u64);
|
|
||||||
|
|
||||||
let start = cursor.read_u64::<LE>()?;
|
|
||||||
let len = cursor.read_u64::<LE>()?;
|
|
||||||
|
|
||||||
let contents = &bytes[cursor.position() as usize..];
|
|
||||||
Ok(DataSection {
|
|
||||||
name: name_string,
|
|
||||||
start,
|
|
||||||
len,
|
|
||||||
contents: From::from(contents),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
|
||||||
pub struct MetaSection {
|
|
||||||
pub entries: HashMap<String, u64>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MetaSection {
|
|
||||||
pub fn len(&self) -> usize {
|
|
||||||
8 + self.entries.iter()
|
|
||||||
.map(|(k, _)| 8 + k.as_bytes().len() + 8)
|
|
||||||
.sum::<usize>()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn to_bytes(&self) -> Vec<u8> {
|
|
||||||
let mut cursor = Cursor::new(Vec::new());
|
|
||||||
cursor.write_u64::<LE>(self.entries.len() as u64).unwrap();
|
|
||||||
for (k, v) in self.entries.iter() {
|
|
||||||
let key_len = k.as_bytes().len();
|
|
||||||
cursor.write_u64::<LE>(key_len as u64).unwrap();
|
|
||||||
cursor.write(k.as_bytes()).unwrap();
|
|
||||||
cursor.write_u64::<LE>(*v).unwrap();
|
|
||||||
}
|
|
||||||
cursor.into_inner()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
|
|
||||||
let mut cursor = Cursor::new(bytes);
|
|
||||||
let entry_count = cursor.read_u64::<LE>()?;
|
|
||||||
let mut entries = HashMap::new();
|
|
||||||
for _ in 0..entry_count {
|
|
||||||
// key
|
|
||||||
let key_len = cursor.read_u64::<LE>()?;
|
|
||||||
let mut key_bytes = vec![0u8; key_len as usize];
|
|
||||||
cursor.read_exact(&mut key_bytes)?;
|
|
||||||
let key = String::from_utf8(key_bytes)?;
|
|
||||||
|
|
||||||
// value
|
|
||||||
let value = cursor.read_u64::<LE>()?;
|
|
||||||
entries.insert(key, value);
|
|
||||||
}
|
|
||||||
Ok(MetaSection { entries })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_to_from_bytes() {
|
|
||||||
let obj = Object {
|
|
||||||
version: OBJ_VERSION,
|
|
||||||
sections: vec![
|
|
||||||
Section::Data(DataSection {
|
|
||||||
name: "data".to_string(),
|
|
||||||
start: 0,
|
|
||||||
len: 16,
|
|
||||||
contents: vec!(0u8; 16),
|
|
||||||
}),
|
|
||||||
Section::Meta(MetaSection {
|
|
||||||
entries: Default::default(),
|
|
||||||
})
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
let obj_bytes = obj.to_bytes();
|
|
||||||
let converted = Object::from_bytes(&obj_bytes).unwrap();
|
|
||||||
assert_eq!(obj, converted);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,255 +0,0 @@
|
|||||||
use crate::vm::{
|
|
||||||
inst,
|
|
||||||
reg::Reg,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub type Ast = Vec<Directive>;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub enum Directive {
|
|
||||||
Meta(MetaSection),
|
|
||||||
Data(DataSection),
|
|
||||||
Include(String),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct MetaSection {
|
|
||||||
pub lines: Vec<MetaLine>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct MetaLine {
|
|
||||||
pub name: String,
|
|
||||||
pub value: Value,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct DataSection {
|
|
||||||
pub name: String,
|
|
||||||
pub org: SectionOrg,
|
|
||||||
pub lines: Vec<DataLine>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DataSection {
|
|
||||||
pub fn exports(&self) -> impl Iterator<Item=&str> {
|
|
||||||
self.lines.iter()
|
|
||||||
.filter_map(|line| if let DataLine::Export(s) = line {
|
|
||||||
Some(s.as_str())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn len(&self) -> usize {
|
|
||||||
self.lines.iter()
|
|
||||||
.map(DataLine::len)
|
|
||||||
.sum()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub enum SectionOrg {
|
|
||||||
Start(u64),
|
|
||||||
StartEnd(u64, u64),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SectionOrg {
|
|
||||||
pub fn start(&self) -> u64 {
|
|
||||||
match self {
|
|
||||||
SectionOrg::Start(start) | SectionOrg::StartEnd(start, _) => *start,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub enum DataLine {
|
|
||||||
ValueDef(ValueDef),
|
|
||||||
Inst(Inst),
|
|
||||||
Export(String),
|
|
||||||
Label(String),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DataLine {
|
|
||||||
pub fn len(&self) -> usize {
|
|
||||||
match self {
|
|
||||||
DataLine::ValueDef(v) => v.len(),
|
|
||||||
DataLine::Inst(i) => i.len(),
|
|
||||||
DataLine::Export(_) | DataLine::Label(_) => 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub enum ValueDef {
|
|
||||||
Int(u64, IntSize),
|
|
||||||
String(String),
|
|
||||||
ZString(String),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ValueDef {
|
|
||||||
pub fn len(&self) -> usize {
|
|
||||||
match self {
|
|
||||||
ValueDef::Int(_, s) => s.len(),
|
|
||||||
ValueDef::String(s) => 8 + s.as_bytes().len(),
|
|
||||||
ValueDef::ZString(s) => s.as_bytes().len() + 1,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub enum Value {
|
|
||||||
// TODO : immediate int sizes
|
|
||||||
// Int(u64, IntSize)
|
|
||||||
Int(u64),
|
|
||||||
Reg(Reg),
|
|
||||||
Name(String),
|
|
||||||
Here,
|
|
||||||
Addr(Box<Value>, IntSize),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Value {
|
|
||||||
pub fn len(&self) -> usize {
|
|
||||||
match self {
|
|
||||||
Value::Int(_) => 8,
|
|
||||||
Value::Reg(_) => 1,
|
|
||||||
Value::Name(_) => 8,
|
|
||||||
Value::Here => 8,
|
|
||||||
Value::Addr(v, _) => v.len(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn dest_encoding(&self) -> Option<u8> {
|
|
||||||
match self {
|
|
||||||
Value::Int(_) | Value::Name(_) | Value::Here => None,
|
|
||||||
Value::Reg(_) => Some(inst::DEST_REG),
|
|
||||||
// TODO : check reg vs int value, and use dest_reg_addr8/16/32/64 values
|
|
||||||
Value::Addr(v, size) => {
|
|
||||||
if let Value::Reg(_) = &**v {
|
|
||||||
match size {
|
|
||||||
IntSize::U64 => Some(inst::DEST_REG_ADDR64),
|
|
||||||
IntSize::U32 => Some(inst::DEST_REG_ADDR32),
|
|
||||||
IntSize::U16 => Some(inst::DEST_REG_ADDR16),
|
|
||||||
IntSize::U8 => Some(inst::DEST_REG_ADDR8),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
match size {
|
|
||||||
IntSize::U64 => Some(inst::DEST_ADDR64),
|
|
||||||
IntSize::U32 => Some(inst::DEST_ADDR32),
|
|
||||||
IntSize::U16 => Some(inst::DEST_ADDR16),
|
|
||||||
IntSize::U8 => Some(inst::DEST_ADDR8),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn source_encoding(&self) -> u8 {
|
|
||||||
match self {
|
|
||||||
Value::Int(_) => inst::SOURCE_IMM64,
|
|
||||||
Value::Reg(_) => inst::SOURCE_REG,
|
|
||||||
// TODO : check reg vs int value, and use source_reg_addr8/16/32/64 values
|
|
||||||
Value::Name(_) | Value::Here => inst::SOURCE_IMM64,
|
|
||||||
Value::Addr(v, size) => {
|
|
||||||
if let Value::Reg(_) = &**v {
|
|
||||||
match size {
|
|
||||||
IntSize::U64 => inst::SOURCE_REG_ADDR64,
|
|
||||||
IntSize::U32 => inst::SOURCE_REG_ADDR32,
|
|
||||||
IntSize::U16 => inst::SOURCE_REG_ADDR16,
|
|
||||||
IntSize::U8 => inst::SOURCE_REG_ADDR8,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
match size {
|
|
||||||
IntSize::U64 => inst::SOURCE_ADDR64,
|
|
||||||
IntSize::U32 => inst::SOURCE_ADDR32,
|
|
||||||
IntSize::U16 => inst::SOURCE_ADDR16,
|
|
||||||
IntSize::U8 => inst::SOURCE_ADDR8,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub enum IntSize {
|
|
||||||
U8,
|
|
||||||
U16,
|
|
||||||
U32,
|
|
||||||
U64,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl IntSize {
|
|
||||||
pub fn len(&self) -> usize {
|
|
||||||
match self {
|
|
||||||
IntSize::U8 => 1,
|
|
||||||
IntSize::U16 => 2,
|
|
||||||
IntSize::U32 => 4,
|
|
||||||
IntSize::U64 => 8,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub enum Inst {
|
|
||||||
Add(Value, Value),
|
|
||||||
Sub(Value, Value),
|
|
||||||
Mul(Value, Value),
|
|
||||||
Div(Value, Value),
|
|
||||||
IDiv(Value, Value),
|
|
||||||
Mod(Value, Value),
|
|
||||||
And(Value, Value),
|
|
||||||
Or(Value, Value),
|
|
||||||
Xor(Value, Value),
|
|
||||||
Shl(Value, Value),
|
|
||||||
Shr(Value, Value),
|
|
||||||
INeg(Value, Value),
|
|
||||||
Inv(Value, Value),
|
|
||||||
Not(Value, Value),
|
|
||||||
CmpEq(Value, Value),
|
|
||||||
CmpLt(Value, Value),
|
|
||||||
Jmp(Value),
|
|
||||||
Jz(Value),
|
|
||||||
Jnz(Value),
|
|
||||||
Call(Value),
|
|
||||||
Ret,
|
|
||||||
Push(Value),
|
|
||||||
Pop(Value),
|
|
||||||
Mov(Value, Value),
|
|
||||||
Halt,
|
|
||||||
Nop,
|
|
||||||
Dump,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Inst {
|
|
||||||
pub fn len(&self) -> usize {
|
|
||||||
match self {
|
|
||||||
Inst::Add(v1, v2)
|
|
||||||
| Inst::Sub(v1, v2)
|
|
||||||
| Inst::Mul(v1, v2)
|
|
||||||
| Inst::Div(v1, v2)
|
|
||||||
| Inst::IDiv(v1, v2)
|
|
||||||
| Inst::Mod(v1, v2)
|
|
||||||
| Inst::And(v1, v2)
|
|
||||||
| Inst::Or(v1, v2)
|
|
||||||
| Inst::Xor(v1, v2)
|
|
||||||
| Inst::Shl(v1, v2)
|
|
||||||
| Inst::Shr(v1, v2)
|
|
||||||
| Inst::INeg(v1, v2)
|
|
||||||
| Inst::Inv(v1, v2)
|
|
||||||
| Inst::Not(v1, v2)
|
|
||||||
| Inst::CmpEq(v1, v2)
|
|
||||||
| Inst::CmpLt(v1, v2)
|
|
||||||
| Inst::Mov(v1, v2) => { 3 + v1.len() + v2.len() }
|
|
||||||
Inst::Jmp(v)
|
|
||||||
| Inst::Jz(v)
|
|
||||||
| Inst::Jnz(v)
|
|
||||||
| Inst::Call(v)
|
|
||||||
| Inst::Push(v)
|
|
||||||
| Inst::Pop(v) => { 3 + v.len() }
|
|
||||||
Inst::Ret
|
|
||||||
| Inst::Halt
|
|
||||||
| Inst::Nop
|
|
||||||
| Inst::Dump => { 2 }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
struct GetLayout {
|
|
||||||
}
|
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
%%
|
|
||||||
\$[0-9]+ "DEC_INT"
|
|
||||||
\$0[Xx][0-9a-fA-F]+ "HEX_INT"
|
|
||||||
\$0[Bb][01]+ "BIN_INT"
|
|
||||||
\.meta "DIR_META"
|
|
||||||
\.section "DIR_SECTION"
|
|
||||||
\.export "DIR_EXPORT"
|
|
||||||
\.include "DIR_INCLUDE"
|
|
||||||
\( "LPAREN"
|
|
||||||
\) "RPAREN"
|
|
||||||
\{ "LBRACE"
|
|
||||||
\} "RBRACE"
|
|
||||||
\[ "LBRACKET"
|
|
||||||
\] "RBRACKET"
|
|
||||||
\.\. "DOTDOT"
|
|
||||||
: "COLON"
|
|
||||||
, "COMMA"
|
|
||||||
\$\$ "BUCKBUCK"
|
|
||||||
u8 "U8"
|
|
||||||
u16 "U16"
|
|
||||||
u32 "U32"
|
|
||||||
u64 "U64"
|
|
||||||
\.u8 "U8_DEF"
|
|
||||||
\.u16 "U16_DEF"
|
|
||||||
\.u32 "U32_DEF"
|
|
||||||
\.u64 "U64_DEF"
|
|
||||||
\.[iu](8|16|32|64) "INT_DEF"
|
|
||||||
\.string "STR_DEF"
|
|
||||||
\.zstring "ZSTR_DEF"
|
|
||||||
"([^"]|\\[\\nt0"'])*" "STRING"
|
|
||||||
add "ADD"
|
|
||||||
sub "SUB"
|
|
||||||
mul "MUL"
|
|
||||||
div "DIV"
|
|
||||||
idiv "IDIV"
|
|
||||||
mod "MOD"
|
|
||||||
and "AND"
|
|
||||||
or "OR"
|
|
||||||
xor "XOR"
|
|
||||||
shl "SHL"
|
|
||||||
shr "SHR"
|
|
||||||
ineg "INEG"
|
|
||||||
inv "INV"
|
|
||||||
not "NOT"
|
|
||||||
cmpeq "CMPEQ"
|
|
||||||
cmplt "CMPLT"
|
|
||||||
jmp "JMP"
|
|
||||||
jz "JZ"
|
|
||||||
jnz "JNZ"
|
|
||||||
call "CALL"
|
|
||||||
ret "RET"
|
|
||||||
push "PUSH"
|
|
||||||
pop "POP"
|
|
||||||
mov "MOV"
|
|
||||||
halt "HALT"
|
|
||||||
nop "NOP"
|
|
||||||
dump "DUMP"
|
|
||||||
%ip "REG_IP"
|
|
||||||
%sp "REG_SP"
|
|
||||||
%fp "REG_FP"
|
|
||||||
%flags "REG_FLAGS"
|
|
||||||
%null "REG_NULL"
|
|
||||||
%status "REG_STATUS"
|
|
||||||
%r[0-9]{1,2} "REG_GENERAL"
|
|
||||||
[a-zA-Z_][a-zA-Z0-9_]* "NAME"
|
|
||||||
;[^\n]* ;
|
|
||||||
[ \n\t]+ ;
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
pub mod ast;
|
|
||||||
|
|
||||||
pub mod parser {
|
|
||||||
use lrpar::lrpar_mod;
|
|
||||||
lrpar_mod!("vm/obj/syn/parser.y");
|
|
||||||
|
|
||||||
pub use self::parser_y::*;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod lexer {
|
|
||||||
use lrlex::lrlex_mod;
|
|
||||||
lrlex_mod!("vm/obj/syn/lexer.l");
|
|
||||||
|
|
||||||
pub use self::lexer_l::*;
|
|
||||||
}
|
|
||||||
@@ -1,221 +0,0 @@
|
|||||||
%start Top
|
|
||||||
%%
|
|
||||||
|
|
||||||
Top -> Vec<Directive>:
|
|
||||||
Top Directive { $1.push($2); $1 }
|
|
||||||
| { Vec::new() }
|
|
||||||
;
|
|
||||||
|
|
||||||
Directive -> Directive:
|
|
||||||
'DIR_META' MetaBlock { Directive::Meta(MetaSection { lines: $2 }) }
|
|
||||||
| 'DIR_SECTION' Name SectionOrg DataBlock {
|
|
||||||
Directive::Data(DataSection {
|
|
||||||
name: $2,
|
|
||||||
org: $3,
|
|
||||||
lines: $4,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
| 'DIR_INCLUDE' String { Directive::Include($2) }
|
|
||||||
;
|
|
||||||
|
|
||||||
MetaBlock -> Vec<MetaLine>: 'LBRACE' MetaLines 'RBRACE' { $2 };
|
|
||||||
|
|
||||||
MetaLines -> Vec<MetaLine>:
|
|
||||||
MetaLines MetaLine { $1.push($2); $1 }
|
|
||||||
| { Vec::new() }
|
|
||||||
;
|
|
||||||
|
|
||||||
|
|
||||||
MetaLine -> MetaLine: Name 'COLON' Value { MetaLine { name: $1, value: $3 } };
|
|
||||||
|
|
||||||
SectionOrg -> SectionOrg:
|
|
||||||
Int { SectionOrg::Start($1) }
|
|
||||||
| Int 'DOTDOT' Int { SectionOrg::StartEnd($1, $3) }
|
|
||||||
;
|
|
||||||
|
|
||||||
DataBlock -> Vec<DataLine>: 'LBRACE' DataLines 'RBRACE' { $2 };
|
|
||||||
|
|
||||||
DataLines -> Vec<DataLine>:
|
|
||||||
DataLines DataLine { $1.push($2); $1 }
|
|
||||||
| { Vec::new() }
|
|
||||||
;
|
|
||||||
|
|
||||||
DataLine -> DataLine:
|
|
||||||
ValueDef { DataLine::ValueDef($1) }
|
|
||||||
| Inst { DataLine::Inst($1) }
|
|
||||||
| 'DIR_EXPORT' Name { DataLine::Export($2) }
|
|
||||||
| Name 'COLON' { DataLine::Label($1) }
|
|
||||||
;
|
|
||||||
|
|
||||||
ValueDef -> ValueDef:
|
|
||||||
'U8_DEF' Int { ValueDef::Int($2, IntSize::U8) }
|
|
||||||
| 'U16_DEF' Int { ValueDef::Int($2, IntSize::U16) }
|
|
||||||
| 'U32_DEF' Int { ValueDef::Int($2, IntSize::U32) }
|
|
||||||
| 'U64_DEF' Int { ValueDef::Int($2, IntSize::U64) }
|
|
||||||
| 'STR_DEF' String { ValueDef::String($2) }
|
|
||||||
| 'ZSTR_DEF' String { ValueDef::ZString($2) }
|
|
||||||
;
|
|
||||||
|
|
||||||
Value -> Value:
|
|
||||||
Int { Value::Int($1) }
|
|
||||||
| Reg { Value::Reg($1) }
|
|
||||||
| Name { Value::Name($1) }
|
|
||||||
| 'BUCKBUCK' { Value::Here }
|
|
||||||
| 'LPAREN' Value 'RPAREN' { Value::Addr(Box::new($2), IntSize::U64) }
|
|
||||||
| 'LPAREN' Value 'RPAREN' 'U8' { Value::Addr(Box::new($2), IntSize::U8) }
|
|
||||||
| 'LPAREN' Value 'RPAREN' 'U16' { Value::Addr(Box::new($2), IntSize::U16) }
|
|
||||||
| 'LPAREN' Value 'RPAREN' 'U32' { Value::Addr(Box::new($2), IntSize::U32) }
|
|
||||||
| 'LPAREN' Value 'RPAREN' 'U64' { Value::Addr(Box::new($2), IntSize::U64) }
|
|
||||||
//| 'LBRACKET' ArrayValues 'RBRACKET' { Value::Array($2) }
|
|
||||||
;
|
|
||||||
|
|
||||||
/*
|
|
||||||
ArrayValues -> Vec<Value>:
|
|
||||||
ArrayValues Value { $1.push($2); $1 }
|
|
||||||
| { Vec::new() }
|
|
||||||
;
|
|
||||||
*/
|
|
||||||
|
|
||||||
Inst -> Inst:
|
|
||||||
'ADD' Value 'COMMA' Value { Inst::Add($2, $4) }
|
|
||||||
| 'SUB' Value 'COMMA' Value { Inst::Sub($2, $4) }
|
|
||||||
| 'MUL' Value 'COMMA' Value { Inst::Mul($2, $4) }
|
|
||||||
| 'DIV' Value 'COMMA' Value { Inst::Div($2, $4) }
|
|
||||||
| 'IDIV' Value 'COMMA' Value { Inst::IDiv($2, $4) }
|
|
||||||
| 'MOD' Value 'COMMA' Value { Inst::Mod($2, $4) }
|
|
||||||
| 'AND' Value 'COMMA' Value { Inst::And($2, $4) }
|
|
||||||
| 'OR' Value 'COMMA' Value { Inst::Or($2, $4) }
|
|
||||||
| 'XOR' Value 'COMMA' Value { Inst::Xor($2, $4) }
|
|
||||||
| 'SHL' Value 'COMMA' Value { Inst::Shl($2, $4) }
|
|
||||||
| 'SHR' Value 'COMMA' Value { Inst::Shr($2, $4) }
|
|
||||||
| 'INEG' Value 'COMMA' Value { Inst::INeg($2, $4) }
|
|
||||||
| 'INV' Value 'COMMA' Value { Inst::Inv($2, $4) }
|
|
||||||
| 'NOT' Value 'COMMA' Value { Inst::Not($2, $4) }
|
|
||||||
| 'CMPEQ' Value 'COMMA' Value { Inst::CmpEq($2, $4) }
|
|
||||||
| 'CMPLT' Value 'COMMA' Value { Inst::CmpLt($2, $4) }
|
|
||||||
| 'JMP' Value { Inst::Jmp($2) }
|
|
||||||
| 'JZ' Value { Inst::Jz($2) }
|
|
||||||
| 'JNZ' Value { Inst::Jnz($2) }
|
|
||||||
| 'CALL' Value { Inst::Call($2) }
|
|
||||||
| 'RET' { Inst::Ret }
|
|
||||||
| 'PUSH' Value { Inst::Push($2) }
|
|
||||||
| 'POP' Value { Inst::Pop($2) }
|
|
||||||
| 'MOV' Value 'COMMA' Value { Inst::Mov($2, $4) }
|
|
||||||
| 'HALT' { Inst::Halt }
|
|
||||||
| 'NOP' { Inst::Nop }
|
|
||||||
| 'DUMP' { Inst::Dump }
|
|
||||||
;
|
|
||||||
|
|
||||||
Name -> String:
|
|
||||||
'NAME' {
|
|
||||||
let v = $1.expect("could not parse name");
|
|
||||||
$lexer.span_str(v.span()).to_string()
|
|
||||||
}
|
|
||||||
;
|
|
||||||
|
|
||||||
Int -> u64:
|
|
||||||
'DEC_INT' {
|
|
||||||
let span = $1.expect("could not parse dec_int").span();
|
|
||||||
let s = &$lexer.span_str(span)[1..];
|
|
||||||
s.parse().unwrap()
|
|
||||||
}
|
|
||||||
| 'HEX_INT' {
|
|
||||||
let span = $1.expect("could not parse hex_int").span();
|
|
||||||
let s = &$lexer.span_str(span)[3..];
|
|
||||||
u64::from_str_radix(s, 16).unwrap()
|
|
||||||
}
|
|
||||||
| 'BIN_INT' {
|
|
||||||
let span = $1.expect("could not parse bin_int").span();
|
|
||||||
let s = &$lexer.span_str(span)[3..];
|
|
||||||
u64::from_str_radix(s, 2).unwrap()
|
|
||||||
}
|
|
||||||
;
|
|
||||||
|
|
||||||
Reg -> Reg:
|
|
||||||
'REG_IP' { IP }
|
|
||||||
| 'REG_SP' { SP }
|
|
||||||
| 'REG_FP' { FP }
|
|
||||||
| 'REG_FLAGS' { FLAGS }
|
|
||||||
| 'REG_NULL' { NULL }
|
|
||||||
| 'REG_STATUS' { STATUS }
|
|
||||||
| 'REG_GENERAL' {
|
|
||||||
let v = $1.expect("could not parse reg");
|
|
||||||
parse_reg($lexer.span_str(v.span())).unwrap()
|
|
||||||
}
|
|
||||||
;
|
|
||||||
|
|
||||||
String -> String:
|
|
||||||
'STRING' {
|
|
||||||
let v = $1.expect("could not parse string");
|
|
||||||
parse_string($lexer.span_str(v.span()))
|
|
||||||
}
|
|
||||||
;
|
|
||||||
%%
|
|
||||||
|
|
||||||
use crate::vm::{
|
|
||||||
obj::syn::ast::*,
|
|
||||||
reg::*,
|
|
||||||
};
|
|
||||||
|
|
||||||
fn parse_string(input: &str) -> String {
|
|
||||||
let mut s = String::new();
|
|
||||||
let input = &input[1..input.bytes().len() - 1];
|
|
||||||
let mut chars = input.chars();
|
|
||||||
while let Some(c) = chars.next() {
|
|
||||||
if c == '\\' {
|
|
||||||
let next = chars.next().unwrap();
|
|
||||||
let c = match next {
|
|
||||||
'\\' => '\\',
|
|
||||||
'n' => '\n',
|
|
||||||
't' => '\t',
|
|
||||||
'"' => '"',
|
|
||||||
'\'' => '\'',
|
|
||||||
'0' => '\0',
|
|
||||||
_ => unreachable!(),
|
|
||||||
};
|
|
||||||
s.push(c);
|
|
||||||
} else {
|
|
||||||
s.push(c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
s
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_reg(input: &str) -> Option<Reg> {
|
|
||||||
use regex::Regex;
|
|
||||||
use lazy_static::lazy_static;
|
|
||||||
lazy_static! {
|
|
||||||
static ref REG_RE: Regex = Regex::new(r"^%r([0-9]{1,2})$").unwrap();
|
|
||||||
}
|
|
||||||
let captures = REG_RE.captures(input)?;
|
|
||||||
let reg_no: Reg = captures.get(1)?
|
|
||||||
.as_str()
|
|
||||||
.parse()
|
|
||||||
.unwrap();
|
|
||||||
let reg = R00 + reg_no;
|
|
||||||
if reg > R31 {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(reg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use crate::vm::reg::*;
|
|
||||||
use super::parse_reg;
|
|
||||||
#[test]
|
|
||||||
fn test_parse_reg() {
|
|
||||||
assert_eq!(parse_reg("%r00"), Some(R00));
|
|
||||||
assert_eq!(parse_reg("%r0"), Some(R00));
|
|
||||||
assert_eq!(parse_reg("%r1"), Some(R01));
|
|
||||||
assert_eq!(parse_reg("%r01"), Some(R01));
|
|
||||||
|
|
||||||
assert_eq!(parse_reg("%r31"), Some(R31));
|
|
||||||
assert_eq!(parse_reg("%r32"), None);
|
|
||||||
assert_eq!(parse_reg("%r0000"), None);
|
|
||||||
assert_eq!(parse_reg("%r9"), Some(R09));
|
|
||||||
assert_eq!(parse_reg("%r"), None);
|
|
||||||
assert_eq!(parse_reg("%r12"), Some(R12));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
macro_rules! registers {
|
|
||||||
{
|
|
||||||
$($variant:ident = $value:expr),* $(,)?
|
|
||||||
} => {
|
|
||||||
pub type Reg = u8;
|
|
||||||
|
|
||||||
$(
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub const $variant: Reg = $value;
|
|
||||||
)*
|
|
||||||
|
|
||||||
pub fn reg_name(reg: Reg) -> Option<&'static str> {
|
|
||||||
match reg {
|
|
||||||
$(
|
|
||||||
$value => Some(stringify!($variant)),
|
|
||||||
)*
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
registers! {
|
|
||||||
// Instruction pointer
|
|
||||||
IP = 0,
|
|
||||||
|
|
||||||
// Stack pointer
|
|
||||||
SP = 1,
|
|
||||||
|
|
||||||
// Frame pointer
|
|
||||||
FP = 2,
|
|
||||||
|
|
||||||
// Flags
|
|
||||||
FLAGS = 3,
|
|
||||||
|
|
||||||
// Zero
|
|
||||||
NULL = 4,
|
|
||||||
|
|
||||||
// General status code
|
|
||||||
STATUS = 5,
|
|
||||||
|
|
||||||
R00 = 6,
|
|
||||||
R01 = 7,
|
|
||||||
R02 = 8,
|
|
||||||
R03 = 9,
|
|
||||||
R04 = 10,
|
|
||||||
R05 = 11,
|
|
||||||
R06 = 12,
|
|
||||||
R07 = 13,
|
|
||||||
R08 = 14,
|
|
||||||
R09 = 15,
|
|
||||||
R10 = 16,
|
|
||||||
R11 = 17,
|
|
||||||
R12 = 18,
|
|
||||||
R13 = 19,
|
|
||||||
R14 = 20,
|
|
||||||
R15 = 21,
|
|
||||||
R16 = 22,
|
|
||||||
R17 = 23,
|
|
||||||
R18 = 24,
|
|
||||||
R19 = 25,
|
|
||||||
R20 = 26,
|
|
||||||
R21 = 27,
|
|
||||||
R22 = 28,
|
|
||||||
R23 = 29,
|
|
||||||
R24 = 30,
|
|
||||||
R25 = 31,
|
|
||||||
R26 = 32,
|
|
||||||
R27 = 33,
|
|
||||||
R28 = 34,
|
|
||||||
R29 = 35,
|
|
||||||
R30 = 36,
|
|
||||||
R31 = 37,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const LAST_REG: Reg = 63;
|
|
||||||
pub const NUM_REGS: usize = 64;
|
|
||||||
387
src/vm/state.rs
387
src/vm/state.rs
@@ -1,387 +0,0 @@
|
|||||||
use crate::vm::{addr::*, error::*, flags::*, inst::*, mem::*, obj::obj::*, reg::*};
|
|
||||||
|
|
||||||
pub struct State {
|
|
||||||
regs: [u64; NUM_REGS],
|
|
||||||
mem: Vec<u8>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl State {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
State {
|
|
||||||
regs: [0; NUM_REGS],
|
|
||||||
mem: Default::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn load_object(&mut self, object: Object, max_mem: usize) -> Result<()> {
|
|
||||||
// TODO : detecting section overlap
|
|
||||||
let mem_len = object.virtual_len();
|
|
||||||
if mem_len > max_mem {
|
|
||||||
return Err(VmError::ObjectTooLarge {
|
|
||||||
object_size: mem_len,
|
|
||||||
max_mem,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
let mut mem = vec![0u8; max_mem];
|
|
||||||
for section in object.sections {
|
|
||||||
match section {
|
|
||||||
Section::Data(DataSection {
|
|
||||||
name: _,
|
|
||||||
start,
|
|
||||||
len,
|
|
||||||
contents,
|
|
||||||
}) => {
|
|
||||||
for offset in 0..len {
|
|
||||||
mem[(start + offset) as usize] = contents[offset as usize];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Section::Meta(MetaSection { entries }) => {
|
|
||||||
if let Some(addr) = entries.get("entry") {
|
|
||||||
self.set_reg_unchecked(IP, *addr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.mem = mem;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn mem_cursor(&self, addr: Addr) -> MemCursor<&[u8]> {
|
|
||||||
let mut cursor = MemCursor::new(self.mem.as_slice());
|
|
||||||
cursor.set_position(addr);
|
|
||||||
cursor
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn mem_cursor_mut(&mut self, addr: Addr) -> MemCursor<&mut [u8]> {
|
|
||||||
let mut cursor = MemCursor::new(self.mem.as_mut_slice());
|
|
||||||
cursor.set_position(addr);
|
|
||||||
cursor
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Registers
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
pub fn get_reg_unchecked(&self, reg: Reg) -> u64 {
|
|
||||||
self.regs[reg as usize]
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_reg(&self, reg: Reg) -> Result<u64> {
|
|
||||||
if (reg as usize) >= NUM_REGS {
|
|
||||||
Err(VmError::IllegalReg { reg })
|
|
||||||
} else {
|
|
||||||
Ok(self.get_reg_unchecked(reg))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_reg_unchecked(&mut self, reg: Reg, value: u64) {
|
|
||||||
self.regs[reg as usize] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_reg(&mut self, reg: Reg, value: u64) -> Result<()> {
|
|
||||||
if (reg as usize) >= NUM_REGS {
|
|
||||||
Err(VmError::IllegalReg { reg })
|
|
||||||
} else {
|
|
||||||
Ok(self.set_reg_unchecked(reg, value))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Registers
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
pub fn ip(&self) -> u64 {
|
|
||||||
self.get_reg_unchecked(IP)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn sp(&self) -> u64 {
|
|
||||||
self.get_reg_unchecked(SP)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn fp(&self) -> u64 {
|
|
||||||
self.get_reg_unchecked(FP)
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Flags
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
pub fn flags(&self) -> Flags {
|
|
||||||
// this is safe because it's OK if there are random bits flipped - this shouldn't happen
|
|
||||||
// anyway, but if it does, they're ignored
|
|
||||||
unsafe { Flags::from_bits_unchecked(self.get_reg_unchecked(FLAGS)) }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn contains_flags(&self, flags: Flags) -> bool {
|
|
||||||
self.flags().contains(flags)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn insert_flags(&mut self, flags: Flags) {
|
|
||||||
let mut new_flags = self.flags();
|
|
||||||
new_flags.insert(flags);
|
|
||||||
self.set_flags(new_flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn remove_flags(&mut self, flags: Flags) {
|
|
||||||
let mut new_flags = self.flags();
|
|
||||||
new_flags.remove(flags);
|
|
||||||
self.set_flags(new_flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_flags(&mut self, flags: Flags) {
|
|
||||||
self.set_reg_unchecked(FLAGS, flags.bits());
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Execution
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
pub fn is_halted(&self) -> bool {
|
|
||||||
self.contains_flags(Flags::HALT)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn exec(&mut self) -> Result<u64> {
|
|
||||||
while !self.is_halted() {
|
|
||||||
self.tick()?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(self.get_reg_unchecked(STATUS))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn tick(&mut self) -> Result<()> {
|
|
||||||
let mut cursor = self.mem_cursor(Addr(self.ip()));
|
|
||||||
let inst = cursor.next_inst()?;
|
|
||||||
let mut next_ip = self.ip() + (inst.len() as u64);
|
|
||||||
match inst {
|
|
||||||
Inst::Add(d, s) => {
|
|
||||||
let value = self.load_dest(d)?.wrapping_add(self.load_source(s)?);
|
|
||||||
self.store_dest(d, value)?;
|
|
||||||
}
|
|
||||||
Inst::Sub(d, s) => {
|
|
||||||
let value = self.load_dest(d)?.wrapping_sub(self.load_source(s)?);
|
|
||||||
self.store_dest(d, value)?;
|
|
||||||
}
|
|
||||||
Inst::Mul(d, s) => {
|
|
||||||
let value = self.load_dest(d)?.wrapping_mul(self.load_source(s)?);
|
|
||||||
self.store_dest(d, value)?;
|
|
||||||
}
|
|
||||||
Inst::Div(d, s) => {
|
|
||||||
// TODO : catch divide by zero
|
|
||||||
let value = self.load_dest(d)?.wrapping_div(self.load_source(s)?);
|
|
||||||
self.store_dest(d, value)?;
|
|
||||||
}
|
|
||||||
Inst::IDiv(d, s) => {
|
|
||||||
// TODO : catch divide by zero
|
|
||||||
let dest = self.load_dest(d)? as i64;
|
|
||||||
let source = self.load_source(s)? as i64;
|
|
||||||
let value = dest.wrapping_div(source);
|
|
||||||
self.store_dest(d, value as u64)?;
|
|
||||||
}
|
|
||||||
Inst::Mod(d, s) => {
|
|
||||||
let value = self.load_dest(d)? % self.load_source(s)?;
|
|
||||||
self.store_dest(d, value)?;
|
|
||||||
}
|
|
||||||
Inst::And(d, s) => {
|
|
||||||
let value = self.load_dest(d)? & self.load_source(s)?;
|
|
||||||
self.store_dest(d, value)?;
|
|
||||||
}
|
|
||||||
Inst::Or(d, s) => {
|
|
||||||
let value = self.load_dest(d)? | self.load_source(s)?;
|
|
||||||
self.store_dest(d, value)?;
|
|
||||||
}
|
|
||||||
Inst::Xor(d, s) => {
|
|
||||||
let value = self.load_dest(d)? ^ self.load_source(s)?;
|
|
||||||
self.store_dest(d, value)?;
|
|
||||||
}
|
|
||||||
Inst::Shl(d, s) => {
|
|
||||||
let value = self.load_dest(d)? << self.load_source(s)?;
|
|
||||||
self.store_dest(d, value)?;
|
|
||||||
}
|
|
||||||
Inst::Shr(d, s) => {
|
|
||||||
let value = self.load_dest(d)? >> self.load_source(s)?;
|
|
||||||
self.store_dest(d, value)?;
|
|
||||||
}
|
|
||||||
Inst::INeg(d, s) => {
|
|
||||||
let value = (!self.load_source(s)?).wrapping_add(1);
|
|
||||||
self.store_dest(d, value)?;
|
|
||||||
}
|
|
||||||
Inst::Inv(d, s) => {
|
|
||||||
let value = !self.load_source(s)?;
|
|
||||||
self.store_dest(d, value)?;
|
|
||||||
}
|
|
||||||
Inst::Not(d, s) => {
|
|
||||||
let value = (self.load_source(s)? == 0) as u64;
|
|
||||||
self.store_dest(d, value)?;
|
|
||||||
}
|
|
||||||
Inst::CmpEq(s1, s2) => {
|
|
||||||
let cmp = self.load_source(s1)? == self.load_source(s2)?;
|
|
||||||
if cmp {
|
|
||||||
self.insert_flags(Flags::COMPARE);
|
|
||||||
} else {
|
|
||||||
self.remove_flags(Flags::COMPARE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Inst::CmpLt(s1, s2) => {
|
|
||||||
let cmp = self.load_source(s1)? < self.load_source(s2)?;
|
|
||||||
if cmp {
|
|
||||||
self.insert_flags(Flags::COMPARE);
|
|
||||||
} else {
|
|
||||||
self.remove_flags(Flags::COMPARE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Inst::Jmp(s) => {
|
|
||||||
next_ip = self.load_source(s)?;
|
|
||||||
}
|
|
||||||
Inst::Jz(s) => {
|
|
||||||
if !self.contains_flags(Flags::COMPARE) {
|
|
||||||
next_ip = self.load_source(s)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Inst::Jnz(s) => {
|
|
||||||
if self.contains_flags(Flags::COMPARE) {
|
|
||||||
next_ip = self.load_source(s)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Inst::Call(s) => {
|
|
||||||
{
|
|
||||||
let fp = self.fp();
|
|
||||||
let ip = next_ip;
|
|
||||||
self.push(Source::Imm(fp))?;
|
|
||||||
self.push(Source::Imm(ip))?;
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
let sp = self.sp();
|
|
||||||
self.set_reg_unchecked(FP, sp - 16);
|
|
||||||
|
|
||||||
next_ip = self.load_source(s)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Inst::Ret => {
|
|
||||||
let fp = self.fp();
|
|
||||||
let sp = fp + 16;
|
|
||||||
self.set_reg_unchecked(SP, sp);
|
|
||||||
|
|
||||||
self.pop(Dest::Reg(IP))?;
|
|
||||||
self.pop(Dest::Reg(FP))?;
|
|
||||||
|
|
||||||
next_ip = self.ip();
|
|
||||||
}
|
|
||||||
Inst::Push(s) => {
|
|
||||||
self.push(s)?;
|
|
||||||
}
|
|
||||||
Inst::Pop(d) => {
|
|
||||||
self.pop(d)?;
|
|
||||||
}
|
|
||||||
Inst::Mov(d, s) => {
|
|
||||||
let value = self.load_source(s)?;
|
|
||||||
self.store_dest(d, value)?;
|
|
||||||
}
|
|
||||||
Inst::Halt => {
|
|
||||||
self.insert_flags(Flags::HALT);
|
|
||||||
}
|
|
||||||
Inst::Nop => {}
|
|
||||||
Inst::Dump => {
|
|
||||||
// TODO - dump
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.set_reg_unchecked(IP, next_ip);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn push(&mut self, source: Source) -> Result<()> {
|
|
||||||
let value = self.load_source(source)?;
|
|
||||||
let mut stack_addr = self.sp();
|
|
||||||
|
|
||||||
// create a destination based on the size of the source
|
|
||||||
let dest = match source {
|
|
||||||
Source::Addr64(_) | Source::RegAddr64(_) | Source::Reg(_) | Source::Imm(_) => Dest::Addr64(Addr(stack_addr)),
|
|
||||||
Source::Addr32(_) | Source::RegAddr32(_) => Dest::Addr32(Addr(stack_addr)),
|
|
||||||
Source::Addr16(_) | Source::RegAddr16(_) => Dest::Addr16(Addr(stack_addr)),
|
|
||||||
Source::Addr8(_) | Source::RegAddr8(_) => Dest::Addr8(Addr(stack_addr)),
|
|
||||||
};
|
|
||||||
self.store_dest(dest, value)?;
|
|
||||||
assert_eq!(source.value_len(), dest.value_len());
|
|
||||||
|
|
||||||
stack_addr += source.value_len() as u64;
|
|
||||||
self.set_reg_unchecked(SP, stack_addr);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn pop(&mut self, dest: Dest) -> Result<()> {
|
|
||||||
let sp = self.sp() - (dest.value_len() as u64);
|
|
||||||
|
|
||||||
let sp_source = match dest {
|
|
||||||
Dest::Addr64(_) | Dest::RegAddr64(_) | Dest::Reg(_) => Source::Addr64(Addr(sp)),
|
|
||||||
Dest::Addr32(_) | Dest::RegAddr32(_) => Source::Addr32(Addr(sp)),
|
|
||||||
Dest::Addr16(_) | Dest::RegAddr16(_) => Source::Addr16(Addr(sp)),
|
|
||||||
Dest::Addr8(_) | Dest::RegAddr8(_) => Source::Addr8(Addr(sp)),
|
|
||||||
};
|
|
||||||
|
|
||||||
let value = self.load_source(sp_source)?;
|
|
||||||
|
|
||||||
// Set the SP first, because the destination may be the SP itself
|
|
||||||
self.set_reg_unchecked(SP, sp);
|
|
||||||
self.store_dest(dest, value)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn store_dest(&mut self, dest: Dest, value: u64) -> Result<()> {
|
|
||||||
match dest {
|
|
||||||
Dest::Addr64(a) => self.mem_cursor_mut(a).write_u64(value),
|
|
||||||
Dest::Addr32(a) => self
|
|
||||||
.mem_cursor_mut(a)
|
|
||||||
.write_u32((value & 0xffff_ffff) as u32),
|
|
||||||
Dest::Addr16(a) => self.mem_cursor_mut(a).write_u16((value & 0xffff) as u16),
|
|
||||||
Dest::Addr8(a) => self.mem_cursor_mut(a).write_u8((value & 0xff) as u8),
|
|
||||||
Dest::RegAddr64(r) => {
|
|
||||||
let addr = Addr(self.get_reg(r)?);
|
|
||||||
self.mem_cursor_mut(addr).write_u64(value)
|
|
||||||
}
|
|
||||||
Dest::RegAddr32(r) => {
|
|
||||||
let addr = Addr(self.get_reg(r)?);
|
|
||||||
self.mem_cursor_mut(addr)
|
|
||||||
.write_u32((value & 0xffff_ffff) as u32)
|
|
||||||
}
|
|
||||||
Dest::RegAddr16(r) => {
|
|
||||||
let addr = Addr(self.get_reg(r)?);
|
|
||||||
self.mem_cursor_mut(addr).write_u16((value & 0xffff) as u16)
|
|
||||||
}
|
|
||||||
Dest::RegAddr8(r) => {
|
|
||||||
let addr = Addr(self.get_reg(r)?);
|
|
||||||
self.mem_cursor_mut(addr).write_u8((value & 0xff) as u8)
|
|
||||||
}
|
|
||||||
Dest::Reg(reg) => self.set_reg(reg, value),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn load_source(&self, source: Source) -> Result<u64> {
|
|
||||||
let value = match source {
|
|
||||||
Source::Addr64(a) => self.mem_cursor(a).next_u64()?,
|
|
||||||
Source::Addr32(a) => self.mem_cursor(a).next_u32()? as u64,
|
|
||||||
Source::Addr16(a) => self.mem_cursor(a).next_u16()? as u64,
|
|
||||||
Source::Addr8(a) => self.mem_cursor(a).next_u8()? as u64,
|
|
||||||
Source::RegAddr64(r) => self.mem_cursor(Addr(self.get_reg(r)?)).next_u64()?,
|
|
||||||
Source::RegAddr32(r) => self.mem_cursor(Addr(self.get_reg(r)?)).next_u32()? as u64,
|
|
||||||
Source::RegAddr16(r) => self.mem_cursor(Addr(self.get_reg(r)?)).next_u16()? as u64,
|
|
||||||
Source::RegAddr8(r) => self.mem_cursor(Addr(self.get_reg(r)?)).next_u8()? as u64,
|
|
||||||
Source::Reg(reg) => self.get_reg(reg)?,
|
|
||||||
Source::Imm(u) => u,
|
|
||||||
};
|
|
||||||
Ok(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn load_dest(&self, dest: Dest) -> Result<u64> {
|
|
||||||
let value = match dest {
|
|
||||||
Dest::Addr64(a) => self.mem_cursor(a).next_u64()?,
|
|
||||||
Dest::Addr32(a) => self.mem_cursor(a).next_u32()? as u64,
|
|
||||||
Dest::Addr16(a) => self.mem_cursor(a).next_u16()? as u64,
|
|
||||||
Dest::Addr8(a) => self.mem_cursor(a).next_u8()? as u64,
|
|
||||||
Dest::RegAddr64(r) => self.mem_cursor(Addr(self.get_reg(r)?)).next_u64()?,
|
|
||||||
Dest::RegAddr32(r) => self.mem_cursor(Addr(self.get_reg(r)?)).next_u32()? as u64,
|
|
||||||
Dest::RegAddr16(r) => self.mem_cursor(Addr(self.get_reg(r)?)).next_u16()? as u64,
|
|
||||||
Dest::RegAddr8(r) => self.mem_cursor(Addr(self.get_reg(r)?)).next_u8()? as u64,
|
|
||||||
Dest::Reg(reg) => self.get_reg(reg)?,
|
|
||||||
};
|
|
||||||
Ok(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
189
src/vm/tick.rs
189
src/vm/tick.rs
@@ -1,189 +0,0 @@
|
|||||||
use crate::vm::{error::*, flags::Flags, inst::*, reg::*, vm::*, visit::*, mem::MemCursor};
|
|
||||||
|
|
||||||
impl Vm {
|
|
||||||
pub fn tick(&mut self) -> Result<()> {
|
|
||||||
let next_ip = self.visit_inst()?;
|
|
||||||
self.set_reg(IP, next_ip);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn next_ip(&self) -> Result<Addr> {
|
|
||||||
let ip = self.ip();
|
|
||||||
let op = self.get_inst_op(ip)?;
|
|
||||||
Ok(ip + (inst_len(op) as u64))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn with_regs<F, B>(&mut self, r1: Reg, r2: Reg, mapping: F) -> B
|
|
||||||
where F: FnOnce(Word, Word) -> B
|
|
||||||
{
|
|
||||||
let w1 = self.get_reg(r1);
|
|
||||||
let w2 = self.get_reg(r2);
|
|
||||||
(mapping)(w1, w2)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl VisitInst for Vm {
|
|
||||||
type Out = Addr;
|
|
||||||
|
|
||||||
fn cursor(&self) -> MemCursor {
|
|
||||||
self.mem_cursor(self.ip() as usize)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn add(&mut self, r1: Reg, r2: Reg) -> Result<Self::Out> {
|
|
||||||
let out = self.with_regs(r1, r2, |w1, w2| w1.wrapping_add(w2));
|
|
||||||
self.set_reg(r1, out);
|
|
||||||
self.next_ip()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn mul(&mut self, r1: Reg, r2: Reg) -> Result<Self::Out> {
|
|
||||||
let out = self.with_regs(r1, r2, |w1, w2| w1.wrapping_mul(w2));
|
|
||||||
self.set_reg(r1, out);
|
|
||||||
self.next_ip()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn div(&mut self, r1: Reg, r2: Reg) -> Result<Self::Out> {
|
|
||||||
// TODO : check w2 == 0 and throw error/exception
|
|
||||||
let out = self.with_regs(r1, r2, |w1, w2| w1 / w2);
|
|
||||||
self.set_reg(r1, out);
|
|
||||||
self.next_ip()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn mod_(&mut self, r1: Reg, r2: Reg) -> Result<Self::Out> {
|
|
||||||
// TODO : check w2 == 0 and throw error/exception
|
|
||||||
let out = self.with_regs(r1, r2, |w1, w2| w1 % w2);
|
|
||||||
self.set_reg(r1, out);
|
|
||||||
self.next_ip()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ineg(&mut self, r1: Reg) -> Result<Self::Out> {
|
|
||||||
let w1 = self.get_reg(r1);
|
|
||||||
self.set_reg(r1, (!w1).wrapping_add(1));
|
|
||||||
self.next_ip()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn and(&mut self, r1: Reg, r2: Reg) -> Result<Self::Out> {
|
|
||||||
let out = self.with_regs(r1, r2, |w1, w2| w1 & w2);
|
|
||||||
self.set_reg(r1, out);
|
|
||||||
self.next_ip()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn or(&mut self, r1: Reg, r2: Reg) -> Result<Self::Out> {
|
|
||||||
let out = self.with_regs(r1, r2, |w1, w2| w1 | w2);
|
|
||||||
self.set_reg(r1, out);
|
|
||||||
self.next_ip()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn inv(&mut self, r1: Reg) -> Result<Self::Out> {
|
|
||||||
let w1 = self.get_reg(r1);
|
|
||||||
self.set_reg(r1, !w1);
|
|
||||||
self.next_ip()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn not(&mut self, r1: Reg) -> Result<Self::Out> {
|
|
||||||
let w1 = self.get_reg(r1);
|
|
||||||
self.set_reg(r1, (w1 == 0) as Word);
|
|
||||||
self.next_ip()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn xor(&mut self, r1: Reg, r2: Reg) -> Result<Self::Out> {
|
|
||||||
let out = self.with_regs(r1, r2, |w1, w2| w1 ^ w2);
|
|
||||||
self.set_reg(r1, out);
|
|
||||||
self.next_ip()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn shl(&mut self, r1: Reg, r2: Reg) -> Result<Self::Out> {
|
|
||||||
let out = self.with_regs(r1, r2, |w1, w2| w1 << w2);
|
|
||||||
self.set_reg(r1, out);
|
|
||||||
self.next_ip()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn shr(&mut self, r1: Reg, r2: Reg) -> Result<Self::Out> {
|
|
||||||
let out = self.with_regs(r1, r2, |w1, w2| w1 >> w2);
|
|
||||||
self.set_reg(r1, out);
|
|
||||||
self.next_ip()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cmpeq(&mut self, r1: Reg, r2: Reg) -> Result<Self::Out> {
|
|
||||||
let cmp = self.with_regs(r1, r2, |w1, w2| w1 == w2);
|
|
||||||
if cmp {
|
|
||||||
self.insert_flags(Flags::COMPARE);
|
|
||||||
} else {
|
|
||||||
self.remove_flags(Flags::COMPARE);
|
|
||||||
}
|
|
||||||
self.next_ip()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cmplt(&mut self, r1: Reg, r2: Reg) -> Result<Self::Out> {
|
|
||||||
let cmp = self.with_regs(r1, r2, |w1, w2| w1 < w2);
|
|
||||||
if cmp {
|
|
||||||
self.insert_flags(Flags::COMPARE);
|
|
||||||
} else {
|
|
||||||
self.remove_flags(Flags::COMPARE);
|
|
||||||
}
|
|
||||||
self.next_ip()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn jmp(&mut self, r1: Reg) -> Result<Self::Out> {
|
|
||||||
let addr = self.get_reg(r1);
|
|
||||||
Ok(addr)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn jz(&mut self, r1: Reg) -> Result<Self::Out> {
|
|
||||||
if !self.flags().contains(Flags::COMPARE) {
|
|
||||||
Ok(self.get_reg(r1))
|
|
||||||
} else {
|
|
||||||
self.next_ip()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn jnz(&mut self, r1: Reg) -> Result<Self::Out> {
|
|
||||||
if self.flags().contains(Flags::COMPARE) {
|
|
||||||
Ok(self.get_reg(r1))
|
|
||||||
} else {
|
|
||||||
self.next_ip()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn load(&mut self, r1: Reg, r2: Reg) -> Result<Self::Out> {
|
|
||||||
let value = Vm::load(self, r2)?;
|
|
||||||
self.set_reg_checked(r1, value)?;
|
|
||||||
self.next_ip()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn regcopy(&mut self, r1: Reg, r2: Reg) -> Result<Self::Out> {
|
|
||||||
let value = self.get_reg_checked(r2)?;
|
|
||||||
self.set_reg_checked(r1, value)?;
|
|
||||||
self.next_ip()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn storeimm64(&mut self, r1: Reg, w1: Word) -> Result<Self::Out> {
|
|
||||||
self.set_reg_checked(r1, w1)?;
|
|
||||||
self.next_ip()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn storeimm32(&mut self, r1: Reg, w1: HalfWord) -> Result<Self::Out> {
|
|
||||||
self.set_reg_checked(r1, w1 as Word)?;
|
|
||||||
self.next_ip()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn memcopy(&mut self, r1: Reg, r2: Reg) -> Result<Self::Out> {
|
|
||||||
let value = Vm::load(self, r2)?;
|
|
||||||
self.store(r1, value)?;
|
|
||||||
self.next_ip()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn store(&mut self, r1: Reg, r2: Reg) -> Result<Self::Out> {
|
|
||||||
let value = self.get_reg_checked(r1)?;
|
|
||||||
self.store(r2, value)?;
|
|
||||||
self.next_ip()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn halt(&mut self) -> Result<Self::Out> {
|
|
||||||
self.insert_flags(Flags::HALT);
|
|
||||||
self.next_ip()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn nop(&mut self) -> Result<Self::Out> {
|
|
||||||
self.next_ip()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,96 +0,0 @@
|
|||||||
use crate::vm::{
|
|
||||||
error::*,
|
|
||||||
inst::*,
|
|
||||||
mem::MemCursor,
|
|
||||||
reg::Reg,
|
|
||||||
vm::{HalfWord, Word},
|
|
||||||
};
|
|
||||||
|
|
||||||
pub trait VisitInst {
|
|
||||||
type Out;
|
|
||||||
|
|
||||||
fn cursor(&self) -> MemCursor;
|
|
||||||
fn add(&mut self, r1: Reg, r2: Reg) -> Result<Self::Out>;
|
|
||||||
fn mul(&mut self, r1: Reg, r2: Reg) -> Result<Self::Out>;
|
|
||||||
fn div(&mut self, r1: Reg, r2: Reg) -> Result<Self::Out>;
|
|
||||||
fn mod_(&mut self, r1: Reg, r2: Reg) -> Result<Self::Out>;
|
|
||||||
fn ineg(&mut self, r1: Reg) -> Result<Self::Out>;
|
|
||||||
fn and(&mut self, r1: Reg, r2: Reg) -> Result<Self::Out>;
|
|
||||||
fn or(&mut self, r1: Reg, r2: Reg) -> Result<Self::Out>;
|
|
||||||
fn inv(&mut self, r1: Reg) -> Result<Self::Out>;
|
|
||||||
fn not(&mut self, r1: Reg) -> Result<Self::Out>;
|
|
||||||
fn xor(&mut self, r1: Reg, r2: Reg) -> Result<Self::Out>;
|
|
||||||
fn shl(&mut self, r1: Reg, r2: Reg) -> Result<Self::Out>;
|
|
||||||
fn shr(&mut self, r1: Reg, r2: Reg) -> Result<Self::Out>;
|
|
||||||
fn cmpeq(&mut self, r1: Reg, r2: Reg) -> Result<Self::Out>;
|
|
||||||
fn cmplt(&mut self, r1: Reg, r2: Reg) -> Result<Self::Out>;
|
|
||||||
fn jmp(&mut self, r1: Reg) -> Result<Self::Out>;
|
|
||||||
fn jz(&mut self, r1: Reg) -> Result<Self::Out>;
|
|
||||||
fn jnz(&mut self, r1: Reg) -> Result<Self::Out>;
|
|
||||||
fn load(&mut self, r1: Reg, r2: Reg) -> Result<Self::Out>;
|
|
||||||
fn regcopy(&mut self, r1: Reg, r2: Reg) -> Result<Self::Out>;
|
|
||||||
fn storeimm64(&mut self, r1: Reg, w1: Word) -> Result<Self::Out>;
|
|
||||||
fn storeimm32(&mut self, r1: Reg, w1: HalfWord) -> Result<Self::Out>;
|
|
||||||
fn memcopy(&mut self, r1: Reg, r2: Reg) -> Result<Self::Out>;
|
|
||||||
fn store(&mut self, r1: Reg, r2: Reg) -> Result<Self::Out>;
|
|
||||||
fn halt(&mut self) -> Result<Self::Out>;
|
|
||||||
fn nop(&mut self) -> Result<Self::Out>;
|
|
||||||
|
|
||||||
fn visit_inst(&mut self) -> Result<Self::Out> {
|
|
||||||
let mut cursor = self.cursor();
|
|
||||||
//panic!("cursor pos: {}", cursor.position());
|
|
||||||
let op = cursor.next_u16()?;
|
|
||||||
|
|
||||||
macro_rules! r1_r2_inst {
|
|
||||||
($fun:ident) => {{
|
|
||||||
let (r1, r2) = cursor.next_regs()?;
|
|
||||||
self.$fun(r1, r2)
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
macro_rules! r1_inst {
|
|
||||||
($fun:ident) => {{
|
|
||||||
let r1 = cursor.next_reg()?;
|
|
||||||
self.$fun(r1)
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
match op {
|
|
||||||
ADD => r1_r2_inst!(add),
|
|
||||||
MUL => r1_r2_inst!(mul),
|
|
||||||
DIV => r1_r2_inst!(div),
|
|
||||||
MOD => r1_r2_inst!(mod_),
|
|
||||||
INEG => r1_inst!(ineg),
|
|
||||||
AND => r1_r2_inst!(and),
|
|
||||||
OR => r1_r2_inst!(or),
|
|
||||||
INV => r1_inst!(inv),
|
|
||||||
NOT => r1_inst!(not),
|
|
||||||
XOR => r1_r2_inst!(xor),
|
|
||||||
SHL => r1_r2_inst!(shl),
|
|
||||||
SHR => r1_r2_inst!(shr),
|
|
||||||
CMPEQ => r1_r2_inst!(cmpeq),
|
|
||||||
CMPLT => r1_r2_inst!(cmplt),
|
|
||||||
JMP => r1_inst!(jmp),
|
|
||||||
JZ => r1_inst!(jz),
|
|
||||||
JNZ => r1_inst!(jnz),
|
|
||||||
LOAD => r1_r2_inst!(load),
|
|
||||||
REGCOPY => r1_r2_inst!(regcopy),
|
|
||||||
STOREIMM64 => {
|
|
||||||
let r1 = cursor.next_reg()?;
|
|
||||||
// skip
|
|
||||||
cursor.next_u32()?;
|
|
||||||
let imm = cursor.next_u64()?;
|
|
||||||
self.storeimm64(r1, imm)
|
|
||||||
}
|
|
||||||
STOREIMM32 => {
|
|
||||||
let r1 = cursor.next_reg()?;
|
|
||||||
let imm = cursor.next_u32()?;
|
|
||||||
self.storeimm32(r1, imm)
|
|
||||||
}
|
|
||||||
MEMCOPY => r1_r2_inst!(memcopy),
|
|
||||||
STORE => r1_r2_inst!(store),
|
|
||||||
HALT => self.halt(),
|
|
||||||
NOP => self.nop(),
|
|
||||||
_ => Err(VmError::IllegalOp { op }),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user