@@ -1,6 +1,6 @@
|
||||
use cfgrammar::yacc::YaccKind;
|
||||
use lrlex::LexerBuilder;
|
||||
use lrpar::{CTParserBuilder};
|
||||
use lrpar::CTParserBuilder;
|
||||
use rerun_except::rerun_except;
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
@@ -10,9 +10,6 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
LexerBuilder::new()
|
||||
.rule_ids_map(lex_rule_ids_map)
|
||||
.process_file_in_src("obj/syn/lexer.l")?;
|
||||
rerun_except(&[
|
||||
"examples/*.asm",
|
||||
"tests/*.asm",
|
||||
]).unwrap();
|
||||
rerun_except(&["examples/*.asm", "tests/*.asm"]).unwrap();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ macro_rules! impl_add_assign {
|
||||
self.0 = self.0 + (rhs as u64);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_add_assign!(usize);
|
||||
@@ -53,7 +53,7 @@ macro_rules! impl_cmp {
|
||||
self.0.partial_cmp(&other)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_cmp!(usize);
|
||||
@@ -66,7 +66,7 @@ macro_rules! impl_from {
|
||||
Addr(other as u64)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_from!(usize);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::{inst::InstOp, reg::Reg, addr::*,};
|
||||
use crate::{addr::*, inst::InstOp, reg::Reg};
|
||||
use snafu::Snafu;
|
||||
|
||||
#[derive(Snafu, Debug, Clone)]
|
||||
|
||||
@@ -138,21 +138,15 @@ impl Inst {
|
||||
| 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)
|
||||
| Inst::Int(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
|
||||
| Inst::IRet => { 2 }
|
||||
| Inst::Mov(dest, source) => 3 + dest.len() + source.len(),
|
||||
Inst::CmpEq(s1, s2) | Inst::CmpLt(s1, s2) | Inst::Int(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 | Inst::IRet => 2,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -176,7 +170,10 @@ impl Source {
|
||||
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::RegAddr64(_)
|
||||
| Source::RegAddr32(_)
|
||||
| Source::RegAddr16(_)
|
||||
| Source::RegAddr8(_) => 1,
|
||||
Source::Reg(_) => 1,
|
||||
Source::Imm(_) => 8,
|
||||
}
|
||||
|
||||
@@ -8,11 +8,14 @@ pub struct MemCursor<T> {
|
||||
}
|
||||
|
||||
impl<T> MemCursor<T>
|
||||
where Cursor<T>: ReadBytesExt,
|
||||
T: AsRef<[u8]>
|
||||
where
|
||||
Cursor<T>: ReadBytesExt,
|
||||
T: AsRef<[u8]>,
|
||||
{
|
||||
pub fn new(mem: T) -> Self {
|
||||
MemCursor { cursor: Cursor::new(mem) }
|
||||
MemCursor {
|
||||
cursor: Cursor::new(mem),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn position(&self) -> u64 {
|
||||
@@ -32,7 +35,7 @@ impl<T> MemCursor<T>
|
||||
let end = start + count;
|
||||
self.check_addr(end as u64 - 1)?;
|
||||
self.cursor.set_position(end as u64);
|
||||
Ok(&self.cursor.get_ref().as_ref()[start .. end])
|
||||
Ok(&self.cursor.get_ref().as_ref()[start..end])
|
||||
}
|
||||
|
||||
pub fn next_u8_unchecked(&mut self) -> u8 {
|
||||
@@ -220,7 +223,8 @@ impl<T> MemCursor<T>
|
||||
}
|
||||
|
||||
impl<T> MemCursor<T>
|
||||
where T: AsRef<[u8]>
|
||||
where
|
||||
T: AsRef<[u8]>,
|
||||
{
|
||||
fn check_addr(&self, addr: u64) -> Result<()> {
|
||||
if addr >= (self.cursor.get_ref().as_ref().len() as u64) {
|
||||
@@ -232,8 +236,9 @@ impl<T> MemCursor<T>
|
||||
}
|
||||
|
||||
impl<T> MemCursor<T>
|
||||
where Cursor<T>: WriteBytesExt,
|
||||
T: AsRef<[u8]>
|
||||
where
|
||||
Cursor<T>: WriteBytesExt,
|
||||
T: AsRef<[u8]>,
|
||||
{
|
||||
pub fn write_u8_unchecked(&mut self, value: u8) {
|
||||
self.cursor.write_u8(value).unwrap();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
pub mod error;
|
||||
pub mod session;
|
||||
mod includes;
|
||||
mod names;
|
||||
pub mod session;
|
||||
|
||||
use self::{error::*, session::AsmSession};
|
||||
use crate::{
|
||||
@@ -11,7 +11,7 @@ use crate::{
|
||||
obj::{
|
||||
obj::{self, Object},
|
||||
syn::ast::*,
|
||||
}
|
||||
},
|
||||
};
|
||||
use byteorder::{WriteBytesExt, LE};
|
||||
use std::{collections::HashMap, path::Path};
|
||||
@@ -48,7 +48,7 @@ impl Asm for Directive {
|
||||
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) }
|
||||
Directive::Include(_) => Ok(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -81,7 +81,10 @@ impl Asm for DataSection {
|
||||
let mut contents = Vec::with_capacity(content_len as usize);
|
||||
for (pos, line) in self.lines(start) {
|
||||
let expected_len = pos - (start as usize);
|
||||
assert!(expected_len >= contents.len(), "next line size would cause section to shrink");
|
||||
assert!(
|
||||
expected_len >= contents.len(),
|
||||
"next line size would cause section to shrink"
|
||||
);
|
||||
// resize contents if necessary, this pads out aligned values
|
||||
contents.resize(expected_len, 0);
|
||||
contents.extend(line.assemble(session)?);
|
||||
@@ -93,11 +96,7 @@ impl Asm for DataSection {
|
||||
"in section {}",
|
||||
self.name
|
||||
);
|
||||
assert_eq!(
|
||||
session.pos - start, content_len,
|
||||
"in section {}",
|
||||
self.name
|
||||
);
|
||||
assert_eq!(session.pos - start, content_len, "in section {}", self.name);
|
||||
|
||||
session.name_stack.pop();
|
||||
Ok(obj::DataSection {
|
||||
@@ -122,8 +121,15 @@ impl Asm for MetaSection {
|
||||
}
|
||||
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::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(),
|
||||
@@ -218,10 +224,7 @@ impl Asm for Inst {
|
||||
let dest_encoding = dest.source_encoding() << 4;
|
||||
bytes.write_u8(dest_encoding).unwrap();
|
||||
bytes.extend(dest.assemble(session)?);
|
||||
assert_eq!(
|
||||
len,
|
||||
bytes.len(),
|
||||
);
|
||||
assert_eq!(len, bytes.len(),);
|
||||
Ok(bytes)
|
||||
}
|
||||
Inst::Int(v1, v2) => self.map_source_source(session, inst::INT, v1, v2),
|
||||
@@ -235,12 +238,18 @@ impl Asm for Inst {
|
||||
}
|
||||
|
||||
impl Inst {
|
||||
fn map_dest_source(&self, session: &mut AsmSession, op: inst::InstOp, dest: &Value, source: &Value) -> Result<Vec<u8>> {
|
||||
fn map_dest_source(
|
||||
&self,
|
||||
session: &mut AsmSession,
|
||||
op: inst::InstOp,
|
||||
dest: &Value,
|
||||
source: &Value,
|
||||
) -> Result<Vec<u8>> {
|
||||
let len = self.len();
|
||||
let mut bytes = Vec::with_capacity(len);
|
||||
bytes.write_u16::<LE>(op).unwrap();
|
||||
let dest_encoding =
|
||||
dest.dest_encoding()
|
||||
let dest_encoding = dest
|
||||
.dest_encoding()
|
||||
.ok_or_else(|| AsmError::IllegalDestValue {
|
||||
value: dest.clone(),
|
||||
})?;
|
||||
@@ -250,32 +259,35 @@ impl Inst {
|
||||
.unwrap();
|
||||
bytes.extend(dest.assemble(session)?);
|
||||
bytes.extend(source.assemble(session)?);
|
||||
assert_eq!(
|
||||
len,
|
||||
bytes.len(),
|
||||
);
|
||||
assert_eq!(len, bytes.len(),);
|
||||
Ok(bytes)
|
||||
}
|
||||
|
||||
fn map_source_source(&self, session: &mut AsmSession, op: inst::InstOp, s1: &Value, s2: &Value) -> Result<Vec<u8>> {
|
||||
fn map_source_source(
|
||||
&self,
|
||||
session: &mut AsmSession,
|
||||
op: inst::InstOp,
|
||||
s1: &Value,
|
||||
s2: &Value,
|
||||
) -> Result<Vec<u8>> {
|
||||
let len = self.len();
|
||||
let mut bytes = Vec::with_capacity(len);
|
||||
bytes.write_u16::<LE>(op).unwrap();
|
||||
let s1_encoding = s1.source_encoding();
|
||||
let s2_encoding = s2.source_encoding();
|
||||
bytes
|
||||
.write_u8((s1_encoding << 4) | s2_encoding)
|
||||
.unwrap();
|
||||
bytes.write_u8((s1_encoding << 4) | s2_encoding).unwrap();
|
||||
bytes.extend(s1.assemble(session)?);
|
||||
bytes.extend(s2.assemble(session)?);
|
||||
assert_eq!(
|
||||
len,
|
||||
bytes.len(),
|
||||
);
|
||||
assert_eq!(len, bytes.len(),);
|
||||
Ok(bytes)
|
||||
}
|
||||
|
||||
fn map_source(&self, session: &mut AsmSession, op: inst::InstOp, source: &Value) -> Result<Vec<u8>> {
|
||||
fn map_source(
|
||||
&self,
|
||||
session: &mut AsmSession,
|
||||
op: inst::InstOp,
|
||||
source: &Value,
|
||||
) -> Result<Vec<u8>> {
|
||||
let len = self.len();
|
||||
let mut bytes = Vec::with_capacity(len);
|
||||
bytes.write_u16::<LE>(op).unwrap();
|
||||
@@ -290,10 +302,7 @@ impl Inst {
|
||||
let len = self.len();
|
||||
let mut bytes = Vec::with_capacity(len);
|
||||
bytes.write_u16::<LE>(op).unwrap();
|
||||
assert_eq!(
|
||||
len,
|
||||
bytes.len(),
|
||||
);
|
||||
assert_eq!(len, bytes.len(),);
|
||||
Ok(bytes)
|
||||
}
|
||||
}
|
||||
@@ -306,8 +315,12 @@ impl Asm for Value {
|
||||
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() })?;
|
||||
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.to_le_bytes().to_vec()),
|
||||
|
||||
@@ -112,4 +112,3 @@ impl From<LexParseError> for SyntaxError {
|
||||
}
|
||||
|
||||
pub type Result<T, E = AsmError> = std::result::Result<T, E>;
|
||||
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
use crate::{
|
||||
addr::Addr,
|
||||
obj::{
|
||||
assemble::{
|
||||
session::AsmSession,
|
||||
error::*,
|
||||
},
|
||||
syn::ast::{DataSection, DataLine, Directive},
|
||||
assemble::{error::*, session::AsmSession},
|
||||
syn::ast::{DataLine, DataSection, Directive},
|
||||
},
|
||||
};
|
||||
use std::{collections::{HashMap, HashSet}, rc::Rc};
|
||||
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>;
|
||||
@@ -54,11 +54,14 @@ pub fn get_section_names(section: &DataSection) -> Result<Names> {
|
||||
}
|
||||
let export = exports.remove(name);
|
||||
|
||||
names.insert(name.clone(), Name {
|
||||
names.insert(
|
||||
name.clone(),
|
||||
Name {
|
||||
name: name.clone(),
|
||||
addr: Addr(pos as u64),
|
||||
export,
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,7 +69,9 @@ pub fn get_section_names(section: &DataSection) -> Result<Names> {
|
||||
if exports.is_empty() {
|
||||
Ok(names)
|
||||
} else {
|
||||
Err(AsmError::UnknownExport { name: exports.iter().next().unwrap().to_string() })
|
||||
Err(AsmError::UnknownExport {
|
||||
name: exports.iter().next().unwrap().to_string(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,12 +98,16 @@ pub fn get_exports(session: &mut AsmSession) -> Result<Names> {
|
||||
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() });
|
||||
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 }))
|
||||
Ok(all_exports.into_iter().fold(Names::new(), |mut acc, val| {
|
||||
acc.extend(val);
|
||||
acc
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
use crate::{
|
||||
obj::{
|
||||
use crate::obj::{
|
||||
assemble::{
|
||||
Asm,
|
||||
error::*,
|
||||
includes::GetIncludes,
|
||||
names::{self, Name, Names},
|
||||
error::*,
|
||||
Asm,
|
||||
},
|
||||
obj::Object,
|
||||
syn::ast::Ast,
|
||||
},
|
||||
};
|
||||
use std::{
|
||||
collections::BTreeMap,
|
||||
@@ -19,19 +17,17 @@ use std::{
|
||||
/// 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: u64,
|
||||
pub(super) includes: Rc<BTreeMap<PathBuf, Ast>>,
|
||||
pub(super) include_search_paths: Vec<PathBuf>,
|
||||
pub(super) include_stack: Vec<PathBuf>,
|
||||
pub(super) name_stack: Vec<Names>,
|
||||
pub(super) pos: u64,
|
||||
}
|
||||
|
||||
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();
|
||||
let sections: Vec<_> = includes.iter().flat_map(|(_, ast)| ast).collect();
|
||||
sections.assemble(self)
|
||||
}
|
||||
|
||||
@@ -46,19 +42,21 @@ impl AsmSession {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub (in super) fn current_include_path(&self) -> Option<&Path> {
|
||||
pub(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> {
|
||||
pub(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
|
||||
.or_else(|| {
|
||||
self.include_search_paths
|
||||
.iter()
|
||||
.filter_map(|include| include.join(path).canonicalize().ok())
|
||||
.next())
|
||||
.next()
|
||||
})
|
||||
}
|
||||
|
||||
pub fn lookup_name(&self, name: &str) -> Option<&Name> {
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
use crate::{mem::MemCursor, obj::obj::*};
|
||||
use prettytable::{Table, row, cell};
|
||||
use std::io::{self, Write as Write};
|
||||
use prettytable::{cell, row, Table};
|
||||
use std::io::{self, Write};
|
||||
|
||||
const SEP: &str = "================================================================================";
|
||||
const SEP: &str =
|
||||
"================================================================================";
|
||||
|
||||
pub trait Disasm {
|
||||
fn disasm(&self, writer: &mut dyn Write) -> io::Result<()>;
|
||||
@@ -61,8 +62,8 @@ impl Disasm for DataSection {
|
||||
if let Ok(inst) = cursor.next_inst() {
|
||||
let start = cursor_pos as usize;
|
||||
let end = start + inst.len();
|
||||
let data = &self.contents.as_slice()[start .. end];
|
||||
table.add_row(row! [
|
||||
let data = &self.contents.as_slice()[start..end];
|
||||
table.add_row(row![
|
||||
format!("{:016x} <{}+{:x}>", pos, self.name, cursor_pos),
|
||||
bytes_hex(data),
|
||||
format!("{:?}", inst),
|
||||
|
||||
@@ -42,4 +42,3 @@ into_parse_error! {
|
||||
}
|
||||
|
||||
pub type Result<T, E = ParseError> = std::result::Result<T, E>;
|
||||
|
||||
|
||||
@@ -114,8 +114,8 @@ pub enum Section {
|
||||
impl Section {
|
||||
pub fn len(&self) -> usize {
|
||||
match self {
|
||||
Section::Data(s) => { 1 + 8 + s.len() },
|
||||
Section::Meta(s) => { 1 + 8 + s.len() },
|
||||
Section::Data(s) => 1 + 8 + s.len(),
|
||||
Section::Meta(s) => 1 + 8 + s.len(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,7 +129,7 @@ impl Section {
|
||||
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();
|
||||
@@ -181,7 +181,7 @@ impl DataSection {
|
||||
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_bytes = &bytes[name_start..name_end];
|
||||
let name_string = String::from_utf8(name_bytes.to_vec())?;
|
||||
cursor.set_position(name_end as u64);
|
||||
|
||||
@@ -205,7 +205,9 @@ pub struct MetaSection {
|
||||
|
||||
impl MetaSection {
|
||||
pub fn len(&self) -> usize {
|
||||
8 + self.entries.iter()
|
||||
8 + self
|
||||
.entries
|
||||
.iter()
|
||||
.map(|(k, _)| 8 + k.as_bytes().len() + 8)
|
||||
.sum::<usize>()
|
||||
}
|
||||
@@ -254,11 +256,11 @@ mod test {
|
||||
name: "data".to_string(),
|
||||
start: 0,
|
||||
len: 16,
|
||||
contents: vec!(0u8; 16),
|
||||
contents: vec![0u8; 16],
|
||||
}),
|
||||
Section::Meta(MetaSection {
|
||||
entries: Default::default(),
|
||||
})
|
||||
}),
|
||||
],
|
||||
};
|
||||
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
use crate::{
|
||||
interrupt::Interrupt,
|
||||
inst,
|
||||
reg::Reg,
|
||||
};
|
||||
use crate::{inst, interrupt::Interrupt, reg::Reg};
|
||||
|
||||
pub type Ast = Vec<Directive>;
|
||||
|
||||
@@ -307,18 +303,14 @@ impl Inst {
|
||||
| Inst::CmpEq(v1, v2)
|
||||
| Inst::CmpLt(v1, v2)
|
||||
| Inst::Int(v1, v2)
|
||||
| Inst::Mov(v1, v2) => { 3 + v1.len() + v2.len() }
|
||||
| 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::IRet
|
||||
| Inst::Halt
|
||||
| Inst::Nop
|
||||
| Inst::Dump => { 2 }
|
||||
| Inst::Pop(v) => 3 + v.len(),
|
||||
Inst::Ret | Inst::IRet | Inst::Halt | Inst::Nop | Inst::Dump => 2,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,21 +146,25 @@ impl State {
|
||||
pub fn ivt(&self) -> Result<&[Interrupt]> {
|
||||
let ivt_addr = self.get_reg_unchecked(IVT);
|
||||
if ivt_addr % 64 != 0 {
|
||||
panic!("illegal IVT address {:#x}; must be divisible by 64", ivt_addr);
|
||||
panic!(
|
||||
"illegal IVT address {:#x}; must be divisible by 64",
|
||||
ivt_addr
|
||||
);
|
||||
}
|
||||
|
||||
let start = ivt_addr as usize;
|
||||
let end = start + (IVT_LENGTH * mem::size_of::<Interrupt>());
|
||||
|
||||
if end > self.mem.len() {
|
||||
return Err(VmError::MemOutOfBounds { addr: Addr(ivt_addr) });
|
||||
return Err(VmError::MemOutOfBounds {
|
||||
addr: Addr(ivt_addr),
|
||||
});
|
||||
}
|
||||
|
||||
// This is safe because we check the bounds above
|
||||
let slice_ptr = (&self.mem[start..]).as_ptr();
|
||||
let slice = unsafe {
|
||||
std::slice::from_raw_parts(slice_ptr as *const Interrupt, IVT_LENGTH)
|
||||
};
|
||||
let slice =
|
||||
unsafe { std::slice::from_raw_parts(slice_ptr as *const Interrupt, IVT_LENGTH) };
|
||||
|
||||
Ok(slice)
|
||||
}
|
||||
@@ -386,7 +390,9 @@ impl State {
|
||||
|
||||
// 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::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)),
|
||||
|
||||
28
src/main.rs
28
src/main.rs
@@ -2,21 +2,26 @@ mod common;
|
||||
|
||||
extern crate libvm as vm;
|
||||
|
||||
use structopt::StructOpt;
|
||||
use snafu::Snafu;
|
||||
use std::{fs, io::{stdout, Write}, path::{Path, PathBuf}, process};
|
||||
use std::{
|
||||
fs,
|
||||
io::{stdout, Write},
|
||||
path::{Path, PathBuf},
|
||||
process,
|
||||
};
|
||||
use structopt::StructOpt;
|
||||
|
||||
const DEFAULT_MAX_MEM: usize = 64 * 1024 * 1024;
|
||||
|
||||
#[derive(Snafu, Debug, Clone, PartialEq)]
|
||||
enum ArgParseError<'a> {
|
||||
#[snafu(display("invalid specified size: {} (sizes may end in G, M, or K)", text))]
|
||||
InvalidSize { text: &'a str }
|
||||
InvalidSize { text: &'a str },
|
||||
}
|
||||
|
||||
fn from_bytes_size<'a>(mut text: &'a str) -> std::result::Result<usize, ArgParseError<'a>> {
|
||||
if text.is_empty() {
|
||||
return Err(ArgParseError::InvalidSize { text })
|
||||
return Err(ArgParseError::InvalidSize { text });
|
||||
}
|
||||
let multiplier = match text.chars().last().unwrap() {
|
||||
'k' | 'K' => 1024,
|
||||
@@ -26,9 +31,11 @@ fn from_bytes_size<'a>(mut text: &'a str) -> std::result::Result<usize, ArgParse
|
||||
};
|
||||
// trim the last character
|
||||
if multiplier != 1 {
|
||||
text = &text[0 .. text.len() - 1];
|
||||
text = &text[0..text.len() - 1];
|
||||
}
|
||||
let value = text.parse::<usize>().map_err(|_| ArgParseError::InvalidSize { text })?;
|
||||
let value = text
|
||||
.parse::<usize>()
|
||||
.map_err(|_| ArgParseError::InvalidSize { text })?;
|
||||
Ok(value * multiplier)
|
||||
}
|
||||
|
||||
@@ -56,7 +63,6 @@ fn test_bytes_size() {
|
||||
struct Options {
|
||||
// maybe some other options:
|
||||
// * debug
|
||||
|
||||
/// The input file to work with.
|
||||
///
|
||||
/// By default, the file will be executed. If -c is passed, it will not run and only compile.
|
||||
@@ -82,7 +88,6 @@ struct Options {
|
||||
/// Only compile the input file to an object.
|
||||
#[structopt(short = "c", long)]
|
||||
compile_only: bool,
|
||||
|
||||
}
|
||||
|
||||
type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;
|
||||
@@ -107,8 +112,8 @@ fn get_writer(path: impl AsRef<Path>) -> Result<Box<dyn Write>> {
|
||||
|
||||
fn main() -> Result<()> {
|
||||
use vm::{
|
||||
state::State,
|
||||
obj::{assemble, disassemble::Disasm},
|
||||
state::State,
|
||||
};
|
||||
|
||||
let opt = Options::from_args();
|
||||
@@ -125,7 +130,9 @@ fn main() -> Result<()> {
|
||||
writer.write(&bytes)?;
|
||||
Ok(())
|
||||
} else if opt.disassemble {
|
||||
let outfile = opt.out.as_ref()
|
||||
let outfile = opt
|
||||
.out
|
||||
.as_ref()
|
||||
.map(|p| p.as_path())
|
||||
.unwrap_or_else(|| Path::new("-"));
|
||||
let mut writer = get_writer(&outfile)?;
|
||||
@@ -138,4 +145,3 @@ fn main() -> Result<()> {
|
||||
process::exit((status & 0xffff_ffff) as i32);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user