hence/hence/src/lib/assembler.rs

398 lines
14 KiB
Rust

use anyhow::{bail, Result};
use itertools::Itertools;
use radix_fmt::radix;
use rust_embed::RustEmbed;
use std::collections::{HashMap, HashSet};
use std::fs;
use std::path::Path;
use std::vec;
use crate::arg;
use crate::lexer;
use crate::parser;
pub trait ToCode {
fn to_code(&self) -> String;
}
pub trait ByteResolvable<T> {
fn resolve_number(&self, data: &mut T) -> Result<u16>;
fn resolve_bytes(&self, data: &mut T) -> Result<Vec<u8>>;
fn resolve_string(&self, data: &mut T) -> Result<String>;
}
pub const PROGRAM_SIZE: u16 = 32 * 1024;
pub type Program = [u8; PROGRAM_SIZE as usize];
#[derive(RustEmbed)]
#[folder = "lib/"]
#[prefix = "$lib/"]
pub struct Lib;
#[derive(Debug)]
pub struct Macro {
pub args: Vec<String>,
pub body: parser::ast::Body,
}
#[derive(Debug)]
pub struct Data {
pub dir: String,
pub ast: parser::ast::AST,
pub program: Program,
pub offset: u16,
pub body_stack: Vec<parser::ast::Node>,
pub constants: HashMap<String, arg::Arg>,
pub macros: HashMap<String, Macro>,
pub includes: HashSet<String>,
pub tmp: Vec<u8>,
pub tmp_offset: u16,
pub tmp_enabled: bool,
}
impl Data {
pub fn new(dir: String, ast: parser::ast::AST) -> Self {
Self {
dir,
body_stack: ast.body.iter().rev().cloned().collect(),
ast,
program: [0; PROGRAM_SIZE as usize],
offset: 0,
constants: HashMap::new(),
macros: HashMap::new(),
includes: HashSet::new(),
tmp: vec![],
tmp_offset: 0,
tmp_enabled: false,
}
}
pub fn resolve_node(&mut self) -> Result<Option<Vec<u8>>> {
let node = match self.body_stack.pop() {
Some(x) => x,
None => bail!("Body stack empty. No more nodes to resolve"),
};
match node {
parser::ast::Node::Comment(_) => Ok(None),
parser::ast::Node::Label(x) => {
self.constants.insert(x, arg::Arg::Number(self.offset));
Ok(None)
}
parser::ast::Node::Call { name, arg } => {
let opcode: u8 = match name.as_str() {
"nop" => 0x00,
"push" => 0x01,
"pop" => 0x02,
"ts" => 0x03,
"tsr" => 0x04,
"tss" => 0x05,
"tlr" => 0x06,
"tlrc" => 0x07,
"tls" => 0x08,
"dbg" => 0x09,
"alu" => 0x0a,
"get" => 0x0b,
"set" => 0x0c,
_ => bail!("Unknown opcode: {}", name),
};
let a = match arg {
Some(x) => x.resolve_number(self)?,
None => 0,
};
if a == 0 {
Ok(Some(vec![opcode | 0b10000000]))
} else {
Ok(Some(vec![opcode, (a >> 8) as u8, a as u8]))
}
}
parser::ast::Node::MacroCall { name, args } => match name.as_str() {
"debug" => {
for arg in args {
let assembly = arg.to_code().replace('\n', "\\n");
let num = arg.resolve_number(self)?;
let bytes = arg.resolve_bytes(self)?;
println!("{}", assembly);
let bin_num = radix(num, 2).to_string();
println!(
" => 0b{}{}",
"0000000000000000"[..16 - bin_num.len()].to_owned(),
bin_num
);
println!(" => {}", num);
let hex_num = radix(num, 16).to_string();
println!(
" => 0x{}{}",
"0000"[..4 - hex_num.len()].to_owned(),
hex_num
);
println!(
" => [{}]",
bytes
.iter()
.map(|n| {
let num = radix(*n, 16).to_string();
format!(
"0x{}{}",
"00".chars().take(2 - num.len()).collect::<String>(),
num
)
})
.join(", ")
);
}
Ok(None)
}
"define" => {
let name = match &args[0] {
arg::Arg::Variable(x) | arg::Arg::String(x) => x,
_ => {
bail!("First argument of define macro needs to be a variable")
}
};
self.constants.insert(name.clone(), args[1].clone());
Ok(None)
}
"define_num_nl" => {
let name = match &args[0] {
arg::Arg::Variable(x) => x,
_ => {
bail!("First argument of define macro needs to be a variable")
}
};
let a = arg::Arg::Number(args[1].resolve_number(self)?);
self.constants.insert(name.clone(), a);
Ok(None)
}
"macro" => {
let name = match &args[0] {
arg::Arg::Variable(x) | arg::Arg::String(x) => x,
_ => {
bail!("First argument of define macro needs to be a literal-like value")
}
};
let args = args[1..]
.iter()
.map(|a| match a {
arg::Arg::Variable(x) => Ok(x.clone()),
_ => bail!("Macro arguments need to be variables"),
})
.collect::<Result<Vec<_>>>()?;
let mut depth: usize = 1;
let body: Vec<_> = self
.body_stack
.iter()
.rev()
.take_while(|n| match n {
parser::ast::Node::MacroCall { name, args: _ } => match name.as_str() {
"macro" => {
depth += 1;
true
}
"endmacro" => {
depth -= 1;
depth != 0
}
_ => true,
},
_ => true,
})
.cloned()
.collect();
self.body_stack
.drain((self.body_stack.len() - body.len() - 1)..)
.for_each(drop);
self.macros.insert(name.clone(), Macro { args, body });
Ok(None)
}
"if" => {
let cond = (args[0].resolve_number(self)? & 1) == 1;
dbg!(cond);
let mut if_depth: usize = 1;
let body: Vec<_> = self
.body_stack
.iter()
.rev()
.take_while(|n| match n {
parser::ast::Node::MacroCall { name, args: _ } => match name.as_str() {
"if" => {
if_depth += 1;
true
}
"endif" => {
if_depth -= 1;
if_depth != 0
}
_ => true,
},
_ => true,
})
.cloned()
.collect();
self.body_stack
.drain((self.body_stack.len() - body.len() - 1)..)
.for_each(drop);
dbg!(&body);
let mut iter = body.iter().enumerate();
if let Some(pos) = loop {
if let Some(pos) = iter.next() {
if let parser::ast::Node::MacroCall { name, args: _ } = pos.1 {
match name.as_str() {
"if" => {
break None;
}
"else" => {
break Some(pos);
}
_ => {}
}
}
} else {
break None;
}
} {
self.body_stack.extend(if cond {
body[..pos.0].to_vec()
} else {
body[(pos.0 + 1)..].to_vec()
});
} else if cond {
self.body_stack.extend(body.into_iter().rev());
}
Ok(None)
}
"org" => {
self.offset = args[0].resolve_number(self)?;
Ok(None)
}
"org_add" => {
self.offset = self.offset.wrapping_add(args[0].resolve_number(self)?);
Ok(None)
}
"org_sub" => {
self.offset = self.offset.wrapping_sub(args[0].resolve_number(self)?);
Ok(None)
}
"bytes" => {
let mut data: Vec<u8> = vec![];
for arg in args {
data.append(&mut arg.resolve_bytes(self)?);
}
if data.is_empty() {
Ok(None)
} else {
Ok(Some(data))
}
}
"embed" => {
let arg = args[0].resolve_string(self)?;
let data = if Path::new(&arg).starts_with("$lib") {
match Lib::get(arg.as_str()) {
Some(x) => x.data.to_vec(),
None => bail!("Virtual lib file couldn't be found with path: {}", arg),
}
} else {
let path = Path::new(&self.dir).join(arg);
fs::read(path)?
};
Ok(Some(data))
}
"requires" => {
let arg = args[0].resolve_string(self)?;
if !self.includes.contains(&arg) {
bail!("File requires include of not included file: {}", arg);
}
Ok(None)
}
"include" => {
let arg = args[0].resolve_string(self)?;
let source = if Path::new(&arg).starts_with("$lib") {
match Lib::get(arg.as_str()) {
Some(x) => std::str::from_utf8(&x.data)?.to_string(),
None => bail!("Virtual lib file couldn't be found with path: {}", arg),
}
} else {
let path = Path::new(&self.dir).join(&arg);
fs::read_to_string(path)?
};
self.includes.insert(arg);
let body = parser::parse(lexer::lex(&source)?)?.body;
self.body_stack
.extend(body.into_iter().rev().collect::<Vec<_>>());
Ok(None)
}
_ => match self.macros.get(&name) {
Some(m) => {
if m.args.len() != args.len() {
bail!(
"Macro call argument signature does not match: (.macro {name}{macro_args}) != (.{name}{call_args})",
macro_args = if m.args.is_empty() {
"".to_string()
} else {
format!(" {}", m.args.join(", "))
},
call_args = if args.is_empty() {
"".to_string()
} else {
format!(" {}", args.iter().map(|a| a.to_code()).join(", "))
}
);
}
// dbg!(&args);
for (i, arg) in args.iter().enumerate() {
self.constants.insert(m.args[i].clone(), arg.clone());
}
self.body_stack
.append(&mut m.body.iter().rev().cloned().collect());
Ok(None)
}
None => bail!("Unknown macro: {}", name),
},
},
}
}
pub fn assemble(&mut self) -> Result<Program> {
let offset_name = "OFFSET".to_string();
while !self.body_stack.is_empty() {
self.constants
.insert(offset_name.clone(), arg::Arg::Number(self.offset));
if let Some(x) = self.resolve_node()? {
if self.offset + (x.len() as u16) > 32 * 1024 {
bail!(
"Offset out of bounds: 0x{} > 0x8000",
radix(self.offset, 16)
);
}
for byte in x {
self.program[self.offset as usize] = byte;
self.offset += 1;
}
}
}
Ok(self.program)
}
}