@@ -1,6 +1,6 @@
|
|||||||
use cfgrammar::yacc::YaccKind;
|
use cfgrammar::yacc::YaccKind;
|
||||||
use lrlex::LexerBuilder;
|
use lrlex::LexerBuilder;
|
||||||
use lrpar::{CTParserBuilder};
|
use lrpar::CTParserBuilder;
|
||||||
use rerun_except::rerun_except;
|
use rerun_except::rerun_except;
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
@@ -10,9 +10,6 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
LexerBuilder::new()
|
LexerBuilder::new()
|
||||||
.rule_ids_map(lex_rule_ids_map)
|
.rule_ids_map(lex_rule_ids_map)
|
||||||
.process_file_in_src("obj/syn/lexer.l")?;
|
.process_file_in_src("obj/syn/lexer.l")?;
|
||||||
rerun_except(&[
|
rerun_except(&["examples/*.asm", "tests/*.asm"]).unwrap();
|
||||||
"examples/*.asm",
|
|
||||||
"tests/*.asm",
|
|
||||||
]).unwrap();
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ macro_rules! impl_add_assign {
|
|||||||
self.0 = self.0 + (rhs as u64);
|
self.0 = self.0 + (rhs as u64);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_add_assign!(usize);
|
impl_add_assign!(usize);
|
||||||
@@ -53,7 +53,7 @@ macro_rules! impl_cmp {
|
|||||||
self.0.partial_cmp(&other)
|
self.0.partial_cmp(&other)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_cmp!(usize);
|
impl_cmp!(usize);
|
||||||
@@ -66,7 +66,7 @@ macro_rules! impl_from {
|
|||||||
Addr(other as u64)
|
Addr(other as u64)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_from!(usize);
|
impl_from!(usize);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use crate::{inst::InstOp, reg::Reg, addr::*,};
|
use crate::{addr::*, inst::InstOp, reg::Reg};
|
||||||
use snafu::Snafu;
|
use snafu::Snafu;
|
||||||
|
|
||||||
#[derive(Snafu, Debug, Clone)]
|
#[derive(Snafu, Debug, Clone)]
|
||||||
|
|||||||
@@ -125,34 +125,28 @@ impl Inst {
|
|||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
match self {
|
match self {
|
||||||
Inst::Add(dest, source)
|
Inst::Add(dest, source)
|
||||||
| Inst::Sub(dest, source)
|
| Inst::Sub(dest, source)
|
||||||
| Inst::Mul(dest, source)
|
| Inst::Mul(dest, source)
|
||||||
| Inst::Div(dest, source)
|
| Inst::Div(dest, source)
|
||||||
| Inst::IDiv(dest, source)
|
| Inst::IDiv(dest, source)
|
||||||
| Inst::Mod(dest, source)
|
| Inst::Mod(dest, source)
|
||||||
| Inst::And(dest, source)
|
| Inst::And(dest, source)
|
||||||
| Inst::Or(dest, source)
|
| Inst::Or(dest, source)
|
||||||
| Inst::Xor(dest, source)
|
| Inst::Xor(dest, source)
|
||||||
| Inst::Shl(dest, source)
|
| Inst::Shl(dest, source)
|
||||||
| Inst::Shr(dest, source)
|
| Inst::Shr(dest, source)
|
||||||
| Inst::INeg(dest, source)
|
| Inst::INeg(dest, source)
|
||||||
| Inst::Inv(dest, source)
|
| Inst::Inv(dest, source)
|
||||||
| Inst::Not(dest, source)
|
| Inst::Not(dest, source)
|
||||||
| Inst::Mov(dest, source) => { 3 + dest.len() + source.len() }
|
| Inst::Mov(dest, source) => 3 + dest.len() + source.len(),
|
||||||
Inst::CmpEq(s1, s2)
|
Inst::CmpEq(s1, s2) | Inst::CmpLt(s1, s2) | Inst::Int(s1, s2) => {
|
||||||
| Inst::CmpLt(s1, s2)
|
3 + s1.len() + s2.len()
|
||||||
| Inst::Int(s1, s2) => { 3 + s1.len() + s2.len() }
|
}
|
||||||
Inst::Jmp(v)
|
Inst::Jmp(v) | Inst::Jz(v) | Inst::Jnz(v) | Inst::Call(v) | Inst::Push(v) => {
|
||||||
| Inst::Jz(v)
|
3 + v.len()
|
||||||
| Inst::Jnz(v)
|
}
|
||||||
| Inst::Call(v)
|
Inst::Pop(v) => 3 + v.len(),
|
||||||
| Inst::Push(v) => { 3 + v.len() }
|
Inst::Ret | Inst::Halt | Inst::Nop | Inst::Dump | Inst::IRet => 2,
|
||||||
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 {
|
pub fn len(&self) -> usize {
|
||||||
match self {
|
match self {
|
||||||
Source::Addr64(_) | Source::Addr32(_) | Source::Addr16(_) | Source::Addr8(_) => 8,
|
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::Reg(_) => 1,
|
||||||
Source::Imm(_) => 8,
|
Source::Imm(_) => 8,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ pub const IVT_LENGTH: usize = 512;
|
|||||||
pub struct Interrupt(u64);
|
pub struct Interrupt(u64);
|
||||||
|
|
||||||
const ENABLED_MASK: u64 = 0x8000_0000_0000_0000;
|
const ENABLED_MASK: u64 = 0x8000_0000_0000_0000;
|
||||||
const ADDR_MASK: u64 = 0x07ff_ffff_ffff_ffff;
|
const ADDR_MASK: u64 = 0x07ff_ffff_ffff_ffff;
|
||||||
|
|
||||||
impl Interrupt {
|
impl Interrupt {
|
||||||
pub fn enabled(&self) -> bool {
|
pub fn enabled(&self) -> bool {
|
||||||
|
|||||||
@@ -8,11 +8,14 @@ pub struct MemCursor<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<T> MemCursor<T>
|
impl<T> MemCursor<T>
|
||||||
where Cursor<T>: ReadBytesExt,
|
where
|
||||||
T: AsRef<[u8]>
|
Cursor<T>: ReadBytesExt,
|
||||||
|
T: AsRef<[u8]>,
|
||||||
{
|
{
|
||||||
pub fn new(mem: T) -> Self {
|
pub fn new(mem: T) -> Self {
|
||||||
MemCursor { cursor: Cursor::new(mem) }
|
MemCursor {
|
||||||
|
cursor: Cursor::new(mem),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn position(&self) -> u64 {
|
pub fn position(&self) -> u64 {
|
||||||
@@ -32,7 +35,7 @@ impl<T> MemCursor<T>
|
|||||||
let end = start + count;
|
let end = start + count;
|
||||||
self.check_addr(end as u64 - 1)?;
|
self.check_addr(end as u64 - 1)?;
|
||||||
self.cursor.set_position(end as u64);
|
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 {
|
pub fn next_u8_unchecked(&mut self) -> u8 {
|
||||||
@@ -220,7 +223,8 @@ impl<T> MemCursor<T>
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<T> MemCursor<T>
|
impl<T> MemCursor<T>
|
||||||
where T: AsRef<[u8]>
|
where
|
||||||
|
T: AsRef<[u8]>,
|
||||||
{
|
{
|
||||||
fn check_addr(&self, addr: u64) -> Result<()> {
|
fn check_addr(&self, addr: u64) -> Result<()> {
|
||||||
if addr >= (self.cursor.get_ref().as_ref().len() as u64) {
|
if addr >= (self.cursor.get_ref().as_ref().len() as u64) {
|
||||||
@@ -232,8 +236,9 @@ impl<T> MemCursor<T>
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<T> MemCursor<T>
|
impl<T> MemCursor<T>
|
||||||
where Cursor<T>: WriteBytesExt,
|
where
|
||||||
T: AsRef<[u8]>
|
Cursor<T>: WriteBytesExt,
|
||||||
|
T: AsRef<[u8]>,
|
||||||
{
|
{
|
||||||
pub fn write_u8_unchecked(&mut self, value: u8) {
|
pub fn write_u8_unchecked(&mut self, value: u8) {
|
||||||
self.cursor.write_u8(value).unwrap();
|
self.cursor.write_u8(value).unwrap();
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod session;
|
|
||||||
mod includes;
|
mod includes;
|
||||||
mod names;
|
mod names;
|
||||||
|
pub mod session;
|
||||||
|
|
||||||
use self::{error::*, session::AsmSession};
|
use self::{error::*, session::AsmSession};
|
||||||
use crate::{
|
use crate::{
|
||||||
@@ -11,7 +11,7 @@ use crate::{
|
|||||||
obj::{
|
obj::{
|
||||||
obj::{self, Object},
|
obj::{self, Object},
|
||||||
syn::ast::*,
|
syn::ast::*,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
use byteorder::{WriteBytesExt, LE};
|
use byteorder::{WriteBytesExt, LE};
|
||||||
use std::{collections::HashMap, path::Path};
|
use std::{collections::HashMap, path::Path};
|
||||||
@@ -48,7 +48,7 @@ impl Asm for Directive {
|
|||||||
match self {
|
match self {
|
||||||
Directive::Data(section) => Ok(Some(obj::Section::Data(section.assemble(asm)?))),
|
Directive::Data(section) => Ok(Some(obj::Section::Data(section.assemble(asm)?))),
|
||||||
Directive::Meta(section) => section.assemble(asm).map(Some),
|
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);
|
let mut contents = Vec::with_capacity(content_len as usize);
|
||||||
for (pos, line) in self.lines(start) {
|
for (pos, line) in self.lines(start) {
|
||||||
let expected_len = pos - (start as usize);
|
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
|
// resize contents if necessary, this pads out aligned values
|
||||||
contents.resize(expected_len, 0);
|
contents.resize(expected_len, 0);
|
||||||
contents.extend(line.assemble(session)?);
|
contents.extend(line.assemble(session)?);
|
||||||
@@ -93,11 +96,7 @@ impl Asm for DataSection {
|
|||||||
"in section {}",
|
"in section {}",
|
||||||
self.name
|
self.name
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(session.pos - start, content_len, "in section {}", self.name);
|
||||||
session.pos - start, content_len,
|
|
||||||
"in section {}",
|
|
||||||
self.name
|
|
||||||
);
|
|
||||||
|
|
||||||
session.name_stack.pop();
|
session.name_stack.pop();
|
||||||
Ok(obj::DataSection {
|
Ok(obj::DataSection {
|
||||||
@@ -122,8 +121,15 @@ impl Asm for MetaSection {
|
|||||||
}
|
}
|
||||||
let value = match &line.value {
|
let value = match &line.value {
|
||||||
Value::Int(i) => *i,
|
Value::Int(i) => *i,
|
||||||
Value::Name(s) => session.lookup_name(s.as_str())
|
Value::Name(s) => {
|
||||||
.ok_or_else(|| AsmError::UnknownName { name: s.to_string() })?.addr.0,
|
session
|
||||||
|
.lookup_name(s.as_str())
|
||||||
|
.ok_or_else(|| AsmError::UnknownName {
|
||||||
|
name: s.to_string(),
|
||||||
|
})?
|
||||||
|
.addr
|
||||||
|
.0
|
||||||
|
}
|
||||||
Value::Reg(_) | Value::Here | Value::Addr(_, _) => {
|
Value::Reg(_) | Value::Here | Value::Addr(_, _) => {
|
||||||
return Err(AsmError::IllegalMetaValue {
|
return Err(AsmError::IllegalMetaValue {
|
||||||
name: line.name.to_string(),
|
name: line.name.to_string(),
|
||||||
@@ -218,10 +224,7 @@ impl Asm for Inst {
|
|||||||
let dest_encoding = dest.source_encoding() << 4;
|
let dest_encoding = dest.source_encoding() << 4;
|
||||||
bytes.write_u8(dest_encoding).unwrap();
|
bytes.write_u8(dest_encoding).unwrap();
|
||||||
bytes.extend(dest.assemble(session)?);
|
bytes.extend(dest.assemble(session)?);
|
||||||
assert_eq!(
|
assert_eq!(len, bytes.len(),);
|
||||||
len,
|
|
||||||
bytes.len(),
|
|
||||||
);
|
|
||||||
Ok(bytes)
|
Ok(bytes)
|
||||||
}
|
}
|
||||||
Inst::Int(v1, v2) => self.map_source_source(session, inst::INT, v1, v2),
|
Inst::Int(v1, v2) => self.map_source_source(session, inst::INT, v1, v2),
|
||||||
@@ -235,47 +238,56 @@ impl Asm for Inst {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl 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 len = self.len();
|
||||||
let mut bytes = Vec::with_capacity(len);
|
let mut bytes = Vec::with_capacity(len);
|
||||||
bytes.write_u16::<LE>(op).unwrap();
|
bytes.write_u16::<LE>(op).unwrap();
|
||||||
let dest_encoding =
|
let dest_encoding = dest
|
||||||
dest.dest_encoding()
|
.dest_encoding()
|
||||||
.ok_or_else(|| AsmError::IllegalDestValue {
|
.ok_or_else(|| AsmError::IllegalDestValue {
|
||||||
value: dest.clone(),
|
value: dest.clone(),
|
||||||
})?;
|
})?;
|
||||||
let source_encoding = source.source_encoding();
|
let source_encoding = source.source_encoding();
|
||||||
bytes
|
bytes
|
||||||
.write_u8((dest_encoding << 4) | source_encoding)
|
.write_u8((dest_encoding << 4) | source_encoding)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
bytes.extend(dest.assemble(session)?);
|
bytes.extend(dest.assemble(session)?);
|
||||||
bytes.extend(source.assemble(session)?);
|
bytes.extend(source.assemble(session)?);
|
||||||
assert_eq!(
|
assert_eq!(len, bytes.len(),);
|
||||||
len,
|
|
||||||
bytes.len(),
|
|
||||||
);
|
|
||||||
Ok(bytes)
|
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 len = self.len();
|
||||||
let mut bytes = Vec::with_capacity(len);
|
let mut bytes = Vec::with_capacity(len);
|
||||||
bytes.write_u16::<LE>(op).unwrap();
|
bytes.write_u16::<LE>(op).unwrap();
|
||||||
let s1_encoding = s1.source_encoding();
|
let s1_encoding = s1.source_encoding();
|
||||||
let s2_encoding = s2.source_encoding();
|
let s2_encoding = s2.source_encoding();
|
||||||
bytes
|
bytes.write_u8((s1_encoding << 4) | s2_encoding).unwrap();
|
||||||
.write_u8((s1_encoding << 4) | s2_encoding)
|
|
||||||
.unwrap();
|
|
||||||
bytes.extend(s1.assemble(session)?);
|
bytes.extend(s1.assemble(session)?);
|
||||||
bytes.extend(s2.assemble(session)?);
|
bytes.extend(s2.assemble(session)?);
|
||||||
assert_eq!(
|
assert_eq!(len, bytes.len(),);
|
||||||
len,
|
|
||||||
bytes.len(),
|
|
||||||
);
|
|
||||||
Ok(bytes)
|
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 len = self.len();
|
||||||
let mut bytes = Vec::with_capacity(len);
|
let mut bytes = Vec::with_capacity(len);
|
||||||
bytes.write_u16::<LE>(op).unwrap();
|
bytes.write_u16::<LE>(op).unwrap();
|
||||||
@@ -290,10 +302,7 @@ impl Inst {
|
|||||||
let len = self.len();
|
let len = self.len();
|
||||||
let mut bytes = Vec::with_capacity(len);
|
let mut bytes = Vec::with_capacity(len);
|
||||||
bytes.write_u16::<LE>(op).unwrap();
|
bytes.write_u16::<LE>(op).unwrap();
|
||||||
assert_eq!(
|
assert_eq!(len, bytes.len(),);
|
||||||
len,
|
|
||||||
bytes.len(),
|
|
||||||
);
|
|
||||||
Ok(bytes)
|
Ok(bytes)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -306,8 +315,12 @@ impl Asm for Value {
|
|||||||
Value::Int(i) => Ok(i.to_le_bytes().to_vec()),
|
Value::Int(i) => Ok(i.to_le_bytes().to_vec()),
|
||||||
Value::Reg(r) => Ok(vec![*r]),
|
Value::Reg(r) => Ok(vec![*r]),
|
||||||
Value::Name(name) => {
|
Value::Name(name) => {
|
||||||
let value = session.lookup_name(name.as_str())
|
let value =
|
||||||
.ok_or_else(|| AsmError::UnknownName { name: name.to_string() })?;
|
session
|
||||||
|
.lookup_name(name.as_str())
|
||||||
|
.ok_or_else(|| AsmError::UnknownName {
|
||||||
|
name: name.to_string(),
|
||||||
|
})?;
|
||||||
Ok(value.addr.0.to_le_bytes().to_vec())
|
Ok(value.addr.0.to_le_bytes().to_vec())
|
||||||
}
|
}
|
||||||
Value::Here => Ok(session.pos.to_le_bytes().to_vec()),
|
Value::Here => Ok(session.pos.to_le_bytes().to_vec()),
|
||||||
@@ -331,7 +344,7 @@ mod test {
|
|||||||
fn test_inst_len() {
|
fn test_inst_len() {
|
||||||
let mut session = AsmSession::default();
|
let mut session = AsmSession::default();
|
||||||
//asm.names
|
//asm.names
|
||||||
//.push(vec![("test".to_string(), Addr(0u64))].into_iter().collect());
|
//.push(vec![("test".to_string(), Addr(0u64))].into_iter().collect());
|
||||||
|
|
||||||
macro_rules! assert_len {
|
macro_rules! assert_len {
|
||||||
($inst:expr) => {{
|
($inst:expr) => {{
|
||||||
|
|||||||
@@ -112,4 +112,3 @@ impl From<LexParseError> for SyntaxError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub type Result<T, E = AsmError> = std::result::Result<T, E>;
|
pub type Result<T, E = AsmError> = std::result::Result<T, E>;
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
addr::Addr,
|
addr::Addr,
|
||||||
obj::{
|
obj::{
|
||||||
assemble::{
|
assemble::{error::*, session::AsmSession},
|
||||||
session::AsmSession,
|
syn::ast::{DataLine, DataSection, Directive},
|
||||||
error::*,
|
|
||||||
},
|
|
||||||
syn::ast::{DataSection, DataLine, 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
|
// TODO(asm) make custom Names type that has "merge" that will catch errors with duplicate names
|
||||||
pub type Names = HashMap<String, Name>;
|
pub type Names = HashMap<String, Name>;
|
||||||
@@ -54,11 +54,14 @@ pub fn get_section_names(section: &DataSection) -> Result<Names> {
|
|||||||
}
|
}
|
||||||
let export = exports.remove(name);
|
let export = exports.remove(name);
|
||||||
|
|
||||||
names.insert(name.clone(), Name {
|
names.insert(
|
||||||
name: name.clone(),
|
name.clone(),
|
||||||
addr: Addr(pos as u64),
|
Name {
|
||||||
export,
|
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() {
|
if exports.is_empty() {
|
||||||
Ok(names)
|
Ok(names)
|
||||||
} else {
|
} 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() {
|
for name_map in all_exports.iter() {
|
||||||
let names_set: HashSet<_> = name_map.keys().collect();
|
let names_set: HashSet<_> = name_map.keys().collect();
|
||||||
if let Some(dupe) = exports.intersection(&names_set).next() {
|
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);
|
exports.extend(names_set);
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: this can probably be done with a fancy combinator chain
|
// NOTE: this can probably be done with a fancy combinator chain
|
||||||
Ok(all_exports.into_iter()
|
Ok(all_exports.into_iter().fold(Names::new(), |mut acc, val| {
|
||||||
.fold(Names::new(), |mut acc, val| { acc.extend(val); acc }))
|
acc.extend(val);
|
||||||
|
acc
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,12 @@
|
|||||||
use crate::{
|
use crate::obj::{
|
||||||
obj::{
|
assemble::{
|
||||||
assemble::{
|
error::*,
|
||||||
Asm,
|
includes::GetIncludes,
|
||||||
includes::GetIncludes,
|
names::{self, Name, Names},
|
||||||
names::{self, Name, Names},
|
Asm,
|
||||||
error::*,
|
|
||||||
},
|
|
||||||
obj::Object,
|
|
||||||
syn::ast::Ast,
|
|
||||||
},
|
},
|
||||||
|
obj::Object,
|
||||||
|
syn::ast::Ast,
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
collections::BTreeMap,
|
collections::BTreeMap,
|
||||||
@@ -19,19 +17,17 @@ use std::{
|
|||||||
/// A shared session for the assembler.
|
/// A shared session for the assembler.
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct AsmSession {
|
pub struct AsmSession {
|
||||||
pub (in super) includes: Rc<BTreeMap<PathBuf, Ast>>,
|
pub(super) includes: Rc<BTreeMap<PathBuf, Ast>>,
|
||||||
pub (in super) include_search_paths: Vec<PathBuf>,
|
pub(super) include_search_paths: Vec<PathBuf>,
|
||||||
pub (in super) include_stack: Vec<PathBuf>,
|
pub(super) include_stack: Vec<PathBuf>,
|
||||||
pub (in super) name_stack: Vec<Names>,
|
pub(super) name_stack: Vec<Names>,
|
||||||
pub (in super) pos: u64,
|
pub(super) pos: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsmSession {
|
impl AsmSession {
|
||||||
pub fn assemble(&mut self) -> Result<Object> {
|
pub fn assemble(&mut self) -> Result<Object> {
|
||||||
let includes = Rc::clone(&self.includes);
|
let includes = Rc::clone(&self.includes);
|
||||||
let sections: Vec<_> = includes.iter()
|
let sections: Vec<_> = includes.iter().flat_map(|(_, ast)| ast).collect();
|
||||||
.flat_map(|(_, ast)| ast)
|
|
||||||
.collect();
|
|
||||||
sections.assemble(self)
|
sections.assemble(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,19 +42,21 @@ impl AsmSession {
|
|||||||
Ok(())
|
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)
|
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();
|
let path = path.as_ref();
|
||||||
self.current_include_path()
|
self.current_include_path()
|
||||||
.and_then(|last_path| last_path.parent())
|
.and_then(|last_path| last_path.parent())
|
||||||
.map(|last_dir| last_dir.join(path))
|
.map(|last_dir| last_dir.join(path))
|
||||||
.or_else(|| self.include_search_paths
|
.or_else(|| {
|
||||||
.iter()
|
self.include_search_paths
|
||||||
.filter_map(|include| include.join(path).canonicalize().ok())
|
.iter()
|
||||||
.next())
|
.filter_map(|include| include.join(path).canonicalize().ok())
|
||||||
|
.next()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn lookup_name(&self, name: &str) -> Option<&Name> {
|
pub fn lookup_name(&self, name: &str) -> Option<&Name> {
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
use crate::{mem::MemCursor, obj::obj::*};
|
use crate::{mem::MemCursor, obj::obj::*};
|
||||||
use prettytable::{Table, row, cell};
|
use prettytable::{cell, row, Table};
|
||||||
use std::io::{self, Write as Write};
|
use std::io::{self, Write};
|
||||||
|
|
||||||
const SEP: &str = "================================================================================";
|
const SEP: &str =
|
||||||
|
"================================================================================";
|
||||||
|
|
||||||
pub trait Disasm {
|
pub trait Disasm {
|
||||||
fn disasm(&self, writer: &mut dyn Write) -> io::Result<()>;
|
fn disasm(&self, writer: &mut dyn Write) -> io::Result<()>;
|
||||||
@@ -61,8 +62,8 @@ impl Disasm for DataSection {
|
|||||||
if let Ok(inst) = cursor.next_inst() {
|
if let Ok(inst) = cursor.next_inst() {
|
||||||
let start = cursor_pos as usize;
|
let start = cursor_pos as usize;
|
||||||
let end = start + inst.len();
|
let end = start + inst.len();
|
||||||
let data = &self.contents.as_slice()[start .. end];
|
let data = &self.contents.as_slice()[start..end];
|
||||||
table.add_row(row! [
|
table.add_row(row![
|
||||||
format!("{:016x} <{}+{:x}>", pos, self.name, cursor_pos),
|
format!("{:016x} <{}+{:x}>", pos, self.name, cursor_pos),
|
||||||
bytes_hex(data),
|
bytes_hex(data),
|
||||||
format!("{:?}", inst),
|
format!("{:?}", inst),
|
||||||
|
|||||||
@@ -42,4 +42,3 @@ into_parse_error! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub type Result<T, E = ParseError> = std::result::Result<T, E>;
|
pub type Result<T, E = ParseError> = std::result::Result<T, E>;
|
||||||
|
|
||||||
|
|||||||
@@ -114,8 +114,8 @@ pub enum Section {
|
|||||||
impl Section {
|
impl Section {
|
||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
match self {
|
match self {
|
||||||
Section::Data(s) => { 1 + 8 + s.len() },
|
Section::Data(s) => 1 + 8 + s.len(),
|
||||||
Section::Meta(s) => { 1 + 8 + s.len() },
|
Section::Meta(s) => 1 + 8 + s.len(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,7 +129,7 @@ impl Section {
|
|||||||
Section::Meta(s) => {
|
Section::Meta(s) => {
|
||||||
cursor.write_u8(SectionKind::Meta.into()).unwrap();
|
cursor.write_u8(SectionKind::Meta.into()).unwrap();
|
||||||
s.to_bytes()
|
s.to_bytes()
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
cursor.write_u64::<LE>(bytes.len() as u64).unwrap();
|
cursor.write_u64::<LE>(bytes.len() as u64).unwrap();
|
||||||
cursor.write(&bytes).unwrap();
|
cursor.write(&bytes).unwrap();
|
||||||
@@ -181,7 +181,7 @@ impl DataSection {
|
|||||||
let name_len = cursor.read_u16::<LE>()? as usize;
|
let name_len = cursor.read_u16::<LE>()? as usize;
|
||||||
let name_start = cursor.position() as usize;
|
let name_start = cursor.position() as usize;
|
||||||
let name_end = name_start + name_len;
|
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())?;
|
let name_string = String::from_utf8(name_bytes.to_vec())?;
|
||||||
cursor.set_position(name_end as u64);
|
cursor.set_position(name_end as u64);
|
||||||
|
|
||||||
@@ -205,7 +205,9 @@ pub struct MetaSection {
|
|||||||
|
|
||||||
impl MetaSection {
|
impl MetaSection {
|
||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
8 + self.entries.iter()
|
8 + self
|
||||||
|
.entries
|
||||||
|
.iter()
|
||||||
.map(|(k, _)| 8 + k.as_bytes().len() + 8)
|
.map(|(k, _)| 8 + k.as_bytes().len() + 8)
|
||||||
.sum::<usize>()
|
.sum::<usize>()
|
||||||
}
|
}
|
||||||
@@ -254,11 +256,11 @@ mod test {
|
|||||||
name: "data".to_string(),
|
name: "data".to_string(),
|
||||||
start: 0,
|
start: 0,
|
||||||
len: 16,
|
len: 16,
|
||||||
contents: vec!(0u8; 16),
|
contents: vec![0u8; 16],
|
||||||
}),
|
}),
|
||||||
Section::Meta(MetaSection {
|
Section::Meta(MetaSection {
|
||||||
entries: Default::default(),
|
entries: Default::default(),
|
||||||
})
|
}),
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,4 @@
|
|||||||
use crate::{
|
use crate::{inst, interrupt::Interrupt, reg::Reg};
|
||||||
interrupt::Interrupt,
|
|
||||||
inst,
|
|
||||||
reg::Reg,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub type Ast = Vec<Directive>;
|
pub type Ast = Vec<Directive>;
|
||||||
|
|
||||||
@@ -291,34 +287,30 @@ impl Inst {
|
|||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
match self {
|
match self {
|
||||||
Inst::Add(v1, v2)
|
Inst::Add(v1, v2)
|
||||||
| Inst::Sub(v1, v2)
|
| Inst::Sub(v1, v2)
|
||||||
| Inst::Mul(v1, v2)
|
| Inst::Mul(v1, v2)
|
||||||
| Inst::Div(v1, v2)
|
| Inst::Div(v1, v2)
|
||||||
| Inst::IDiv(v1, v2)
|
| Inst::IDiv(v1, v2)
|
||||||
| Inst::Mod(v1, v2)
|
| Inst::Mod(v1, v2)
|
||||||
| Inst::And(v1, v2)
|
| Inst::And(v1, v2)
|
||||||
| Inst::Or(v1, v2)
|
| Inst::Or(v1, v2)
|
||||||
| Inst::Xor(v1, v2)
|
| Inst::Xor(v1, v2)
|
||||||
| Inst::Shl(v1, v2)
|
| Inst::Shl(v1, v2)
|
||||||
| Inst::Shr(v1, v2)
|
| Inst::Shr(v1, v2)
|
||||||
| Inst::INeg(v1, v2)
|
| Inst::INeg(v1, v2)
|
||||||
| Inst::Inv(v1, v2)
|
| Inst::Inv(v1, v2)
|
||||||
| Inst::Not(v1, v2)
|
| Inst::Not(v1, v2)
|
||||||
| Inst::CmpEq(v1, v2)
|
| Inst::CmpEq(v1, v2)
|
||||||
| Inst::CmpLt(v1, v2)
|
| Inst::CmpLt(v1, v2)
|
||||||
| Inst::Int(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::Jmp(v)
|
||||||
| Inst::Jz(v)
|
| Inst::Jz(v)
|
||||||
| Inst::Jnz(v)
|
| Inst::Jnz(v)
|
||||||
| Inst::Call(v)
|
| Inst::Call(v)
|
||||||
| Inst::Push(v)
|
| Inst::Push(v)
|
||||||
| Inst::Pop(v) => { 3 + v.len() }
|
| Inst::Pop(v) => 3 + v.len(),
|
||||||
Inst::Ret
|
Inst::Ret | Inst::IRet | Inst::Halt | Inst::Nop | Inst::Dump => 2,
|
||||||
| Inst::IRet
|
|
||||||
| Inst::Halt
|
|
||||||
| Inst::Nop
|
|
||||||
| Inst::Dump => { 2 }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -146,21 +146,25 @@ impl State {
|
|||||||
pub fn ivt(&self) -> Result<&[Interrupt]> {
|
pub fn ivt(&self) -> Result<&[Interrupt]> {
|
||||||
let ivt_addr = self.get_reg_unchecked(IVT);
|
let ivt_addr = self.get_reg_unchecked(IVT);
|
||||||
if ivt_addr % 64 != 0 {
|
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 start = ivt_addr as usize;
|
||||||
let end = start + (IVT_LENGTH * mem::size_of::<Interrupt>());
|
let end = start + (IVT_LENGTH * mem::size_of::<Interrupt>());
|
||||||
|
|
||||||
if end > self.mem.len() {
|
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
|
// This is safe because we check the bounds above
|
||||||
let slice_ptr = (&self.mem[start..]).as_ptr();
|
let slice_ptr = (&self.mem[start..]).as_ptr();
|
||||||
let slice = unsafe {
|
let slice =
|
||||||
std::slice::from_raw_parts(slice_ptr as *const Interrupt, IVT_LENGTH)
|
unsafe { std::slice::from_raw_parts(slice_ptr as *const Interrupt, IVT_LENGTH) };
|
||||||
};
|
|
||||||
|
|
||||||
Ok(slice)
|
Ok(slice)
|
||||||
}
|
}
|
||||||
@@ -386,7 +390,9 @@ impl State {
|
|||||||
|
|
||||||
// create a destination based on the size of the source
|
// create a destination based on the size of the source
|
||||||
let dest = match 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::Addr32(_) | Source::RegAddr32(_) => Dest::Addr32(Addr(stack_addr)),
|
||||||
Source::Addr16(_) | Source::RegAddr16(_) => Dest::Addr16(Addr(stack_addr)),
|
Source::Addr16(_) | Source::RegAddr16(_) => Dest::Addr16(Addr(stack_addr)),
|
||||||
Source::Addr8(_) | Source::RegAddr8(_) => Dest::Addr8(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;
|
extern crate libvm as vm;
|
||||||
|
|
||||||
use structopt::StructOpt;
|
|
||||||
use snafu::Snafu;
|
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;
|
const DEFAULT_MAX_MEM: usize = 64 * 1024 * 1024;
|
||||||
|
|
||||||
#[derive(Snafu, Debug, Clone, PartialEq)]
|
#[derive(Snafu, Debug, Clone, PartialEq)]
|
||||||
enum ArgParseError<'a> {
|
enum ArgParseError<'a> {
|
||||||
#[snafu(display("invalid specified size: {} (sizes may end in G, M, or K)", text))]
|
#[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>> {
|
fn from_bytes_size<'a>(mut text: &'a str) -> std::result::Result<usize, ArgParseError<'a>> {
|
||||||
if text.is_empty() {
|
if text.is_empty() {
|
||||||
return Err(ArgParseError::InvalidSize { text })
|
return Err(ArgParseError::InvalidSize { text });
|
||||||
}
|
}
|
||||||
let multiplier = match text.chars().last().unwrap() {
|
let multiplier = match text.chars().last().unwrap() {
|
||||||
'k' | 'K' => 1024,
|
'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
|
// trim the last character
|
||||||
if multiplier != 1 {
|
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)
|
Ok(value * multiplier)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,7 +63,6 @@ fn test_bytes_size() {
|
|||||||
struct Options {
|
struct Options {
|
||||||
// maybe some other options:
|
// maybe some other options:
|
||||||
// * debug
|
// * debug
|
||||||
|
|
||||||
/// The input file to work with.
|
/// The input file to work with.
|
||||||
///
|
///
|
||||||
/// By default, the file will be executed. If -c is passed, it will not run and only compile.
|
/// 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.
|
/// Only compile the input file to an object.
|
||||||
#[structopt(short = "c", long)]
|
#[structopt(short = "c", long)]
|
||||||
compile_only: bool,
|
compile_only: bool,
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;
|
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<()> {
|
fn main() -> Result<()> {
|
||||||
use vm::{
|
use vm::{
|
||||||
state::State,
|
|
||||||
obj::{assemble, disassemble::Disasm},
|
obj::{assemble, disassemble::Disasm},
|
||||||
|
state::State,
|
||||||
};
|
};
|
||||||
|
|
||||||
let opt = Options::from_args();
|
let opt = Options::from_args();
|
||||||
@@ -125,7 +130,9 @@ fn main() -> Result<()> {
|
|||||||
writer.write(&bytes)?;
|
writer.write(&bytes)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
} else if opt.disassemble {
|
} else if opt.disassemble {
|
||||||
let outfile = opt.out.as_ref()
|
let outfile = opt
|
||||||
|
.out
|
||||||
|
.as_ref()
|
||||||
.map(|p| p.as_path())
|
.map(|p| p.as_path())
|
||||||
.unwrap_or_else(|| Path::new("-"));
|
.unwrap_or_else(|| Path::new("-"));
|
||||||
let mut writer = get_writer(&outfile)?;
|
let mut writer = get_writer(&outfile)?;
|
||||||
@@ -138,4 +145,3 @@ fn main() -> Result<()> {
|
|||||||
process::exit((status & 0xffff_ffff) as i32);
|
process::exit((status & 0xffff_ffff) as i32);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user