#![allow(clippy::type_complexity)]
mod tree;
mod assembly_source;
mod import;
mod macros;
mod raw;
mod align;
mod label;
mod strings;
mod section;
mod define;
pub mod utils;
use crate::tree::*;
use crate::tree::AsmNode::*;
use crate::assembly_source::parse_source;
use std::collections::HashMap;
use macros::*;
pub struct Assembler<'a> {
root: AsmNode,
macros: HashMap<MacroID, String>,
macro_expansion_count: usize,
defines: HashMap<String, Vec<String>>,
wordsize: usize,
pub align_pattern: Vec<u8>,
pub start_address: usize,
pub implementation_macro: &'a dyn Fn(&Vec<String>) -> Result<Option<String>, String>,
pub micro_assembly: &'a dyn Fn(&Vec<String>) -> Result<Vec<u8>, String>,
}
impl Assembler<'_> {
pub fn from_text(text: &str) -> Self {
Self::from_named_text(text, "./__asm_init")
}
fn from_named_text(text: &str, name: &str) -> Self {
fn default_implementation_macro(_: &Vec<String>) -> Result<Option<String>, String> {
Ok(None)
}
fn default_micro_assembly(_: &Vec<String>) -> Result<Vec<u8>, String> {
Err("Micro assembly function should be given by the assembler implementation.".to_string())
}
Assembler {
root: parse_source(text, name),
macros: HashMap::new(),
defines: HashMap::new(),
wordsize: 0,
align_pattern: vec![0],
start_address: 0,
implementation_macro: &default_implementation_macro,
micro_assembly: &default_micro_assembly,
macro_expansion_count: 0,
}
}
pub fn from_file(path: &str) -> Self {
let mut import_directive = "@import \"".to_string();
import_directive.push_str(path);
import_directive.push('\"');
Self::from_text(&import_directive)
}
pub fn set_word_size(&mut self, wordsize: usize) -> Option<&str> {
match wordsize {
8 | 16 | 32 | 64 | 128 => {
self.wordsize = wordsize / 8;
None
},
x => {
if x > 128 {
Some("Only word sizes of 128 bits or less are supported. This is not a limitation of the Reflet architecture but one of this assembler.")
} else {
Some("Valid word sizes should be 8 bits times a power of two such as 8, 16, or 32.")
}
},
}
}
pub fn add_text_before(&mut self, txt: &str, name: &str) {
match &mut self.root {
Inode(list) => {
list.insert(0, parse_source(txt, name));
},
_ => {
panic!("Assembler's root should have been an inode!");
},
}
}
pub fn add_text_after(&mut self, txt: &str, name: &str) {
match &mut self.root {
Inode(list) => {
list.push(parse_source(txt, name));
},
_ => {
panic!("Assembler's root should have been an inode!");
},
}
}
fn run_text_adding_passes(&mut self) {
const TEXT_ADDING_PASSES: [&dyn Fn(&mut Assembler) -> bool; 5] = [
&import::include_source,
¯os::register_macros,
&define::handle_define,
&run_implementation_macros,
¯os::expand_macros,
];
let mut pass_index = 0;
while pass_index < TEXT_ADDING_PASSES.len() {
if TEXT_ADDING_PASSES[pass_index](self) {
pass_index = 0;
} else {
pass_index += 1;
}
}
}
pub fn assemble(&mut self) -> Result<Vec<u8>, String> {
self.run_text_adding_passes();
section::handle_sections(self);
label::register_labels(self);
align::register_align(self);
raw::expand_constants(self);
raw::decode_raw_bytes(self);
strings::register_strings(self);
self.run_micro_assembler();
align::expand_align(self);
label::expand_labels(self);
let ret = self.collect_raw();
match self.root.check_for_errors() {
Some(txt) => Err(txt),
None => Ok(ret),
}
}
fn run_micro_assembler(&mut self) {
let mut running_micro_assembler = | node: &AsmNode | -> Option<AsmNode> {
match node {
Source{code, meta} => match (self.micro_assembly)(code) {
Err(msg) => Some(Error{msg, meta: meta.clone()}),
Ok(raw) => Some(Raw(raw)),
},
_ => None,
}
};
self.root.traverse_tree(&mut running_micro_assembler);
}
pub fn label_dump(&mut self) -> String {
label::label_dump(self)
}
fn collect_raw(&mut self) -> Vec<u8> {
let mut ret: Vec<u8> = vec![];
let mut collecting_raw = | node: &AsmNode | -> Option<AsmNode> {
match node {
Raw(data) => {
ret.extend(data);
None
},
Error{msg: _, meta: _} => None,
Label{name: _, is_definition: true, meta: _} => None,
x => Some(Error{msg: format!("There is a bug in the assembler, the node {} should not be left over in collect_raw.", &x.to_string()), meta: Metadata{line: !0, raw: "!!!".to_string(), source_file: "!!!".to_string()}}),
}
};
self.root.traverse_tree(&mut collecting_raw);
ret
}
fn format_number(&self, number: u128) -> Option<Vec<u8>> {
let mut n = number;
let mut ret = vec![];
for _i in 0..self.wordsize {
ret.push((n & 0xFF) as u8);
n >>= 8
}
if n == 0 {
Some(ret)
} else {
None
}
}
fn format_string_into_number(&self, s: &str) -> Option<Vec<u8>> {
match utils::format_string_into_number(s) {
Some((num, false)) => self.format_number(num),
Some((num, true)) => {
let mut num_shorten = num;
for i in self.wordsize..(128/8) {
let mask: u128 = (0xFF << (i * 8)) >> 1;
if ((num & mask) << 1) >> (i * 8) != 0xFF {
return None
}
num_shorten &= !(0xFF << (i*8));
}
self.format_number(num_shorten)
},
None => None,
}
}
}
fn run_implementation_macros(asm: &mut Assembler) -> bool {
let mut expanded_macros = false;
let mut running_implementation_macros = | node: &AsmNode | -> Option<AsmNode> {
match node {
Source{code, meta} => match (asm.implementation_macro)(code) {
Err(msg) => Some(Error{msg, meta: meta.clone()}),
Ok(None) => None,
Ok(Some(txt)) => {
expanded_macros = true;
Some(parse_source(&txt, &format!("Expantion of line {} from file {}, being {}", meta.line, &meta.source_file, &meta.raw)))
},
},
_ => None,
}
};
asm.root.traverse_tree(&mut running_implementation_macros);
expanded_macros
}
#[test]
fn test_set_word_size() {
let mut asm = Assembler::from_text("");
assert_eq!(asm.set_word_size(8), None);
assert_eq!(asm.set_word_size(128), None);
assert_ne!(asm.set_word_size(256), None);
assert_ne!(asm.set_word_size(63), None);
assert_ne!(asm.set_word_size(0), None);
}
#[test]
fn test_format_number() {
let mut asm = Assembler::from_text("");
assert_eq!(asm.set_word_size(8), None);
assert_eq!(asm.format_number(12), Some(vec![12]));
assert_eq!(asm.format_number(250), Some(vec![250]));
assert_eq!(asm.format_number(260), None);
assert_eq!(asm.set_word_size(16), None);
assert_eq!(asm.format_number(12), Some(vec![12, 0]));
assert_eq!(asm.format_number(250), Some(vec![250, 0]));
assert_eq!(asm.format_number(260), Some(vec![4, 1]));
assert_eq!(asm.format_number(0x10000), None);
}
#[test]
fn format_string_into_number() {
let mut asm = Assembler::from_text("");
assert_eq!(asm.set_word_size(8), None);
assert_eq!(asm.format_string_into_number("12"), Some(vec![12]));
assert_eq!(asm.format_string_into_number("250"), Some(vec![250]));
assert_eq!(asm.format_string_into_number("260"), None);
assert_eq!(asm.format_string_into_number("-129"), None);
assert_eq!(asm.format_string_into_number("-10"), Some(vec![0xF6]));
assert_eq!(asm.set_word_size(16), None);
assert_eq!(asm.format_string_into_number("12"), Some(vec![12, 0]));
assert_eq!(asm.format_string_into_number("250"), Some(vec![250, 0]));
assert_eq!(asm.format_string_into_number("260"), Some(vec![4, 1]));
assert_eq!(asm.format_string_into_number("0x10000"), None);
assert_eq!(asm.format_string_into_number("Potate"), None);
assert_eq!(asm.format_string_into_number("0xFFF"), Some(vec![0xFF, 0xF]));
assert_eq!(asm.format_string_into_number("-10"), Some(vec![0xF6, 0xFF]));
}