hence/src/lib/assembler.rs
2022-08-26 15:02:48 +02:00

270 lines
11 KiB
Rust

use itertools::Itertools;
use radix_fmt::radix;
use std::collections::HashMap;
use crate::arg;
use crate::parser;
pub trait ToCode {
fn to_code(&self) -> String;
}
pub trait ByteResolvable<T> {
fn resolve_number(&self, data: &mut T) -> Result<u16, String>;
fn resolve_bytes(&self, data: &mut T) -> Result<Vec<u8>, String>;
}
#[derive(Debug)]
pub struct Macro {
pub args: Vec<String>,
pub body: Vec<parser::ast::Node>,
}
#[derive(Debug)]
pub enum State {
Default,
Macro { name: String, depth: usize },
}
#[derive(Debug)]
pub struct Data {
pub dir: String,
pub body: Vec<parser::ast::Node>,
pub program: [u8; 32 * 1024],
pub offset: u16,
pub contants: HashMap<String, arg::Arg>,
pub macros: HashMap<String, Macro>,
pub state: State,
}
impl Data {
pub fn new(dir: String, body: Vec<parser::ast::Node>) -> Self {
Self {
dir,
body,
program: [0; 32 * 1024],
offset: 0,
contants: HashMap::new(),
macros: HashMap::new(),
state: State::Default,
}
}
}
pub fn assemble(data: &mut Data) -> Result<(), String> {
for node in data.body.clone() {
data.contants
.insert("OFFSET".to_string(), arg::Arg::Number(data.offset));
match &data.state {
State::Default => {
match node {
parser::ast::Node::Comment(_) => {}
parser::ast::Node::Label(x) => {
if data.contants.contains_key(&x) {
return Err(format!("Label already exists: '{x}'"));
}
data.contants
.insert(x.to_string(), arg::Arg::Number(data.offset));
}
parser::ast::Node::Call { name, arg } => {
let arg_num = match arg {
Some(x) => x.resolve_number(data).unwrap(),
_ => 0,
};
data.program[data.offset as usize] = match name.as_str() {
"nop" => 0x00,
"push" => 0x01,
"pop" => 0x02,
"ts" => 0x03,
"tsr" => 0x04,
"tss" => 0x05,
"tlr" => 0x06,
"tlrc" => 0x07,
"tls" => 0x08,
"ld" => 0x09,
"dbg" => 0x0a,
"alu" => 0x0b,
"get" => 0x0c,
"set" => 0x0d,
_ => return Err(format!("Unknown opcode: '{name}'")),
};
if arg_num == 0 {
data.program[data.offset as usize] |= 0b10000000;
}
data.offset += 1;
if arg_num != 0 {
data.program[data.offset as usize] = (arg_num >> 8) as u8;
data.offset += 1;
data.program[data.offset as usize] = arg_num as u8;
data.offset += 1;
}
}
parser::ast::Node::MacroCall { name, args } => {
match name.as_str() {
"debug" => {
for arg in args {
let bytes = arg.resolve_bytes(data).unwrap();
println!("{}", arg.to_code().replace('\n', "\\n"));
println!(" => {}", arg.resolve_number(data).unwrap());
println!(
" => [{}]",
bytes
.iter()
.map(|n| {
let num = radix(*n, 16).to_string();
format!(
"0x{}{}",
"00".chars()
.take(2 - num.len())
.collect::<String>(),
num
)
})
.join(", ")
);
println!(
" => \"{}\"",
String::from_utf8(bytes).unwrap().replace('\n', "\\n")
);
}
println!("==========");
}
"define" => {
let name = match &args[0] {
arg::Arg::Variable(x) | arg::Arg::String(x) => x,
_ => return Err(
"First argument of define macro needs to be a literal-like"
.to_string(),
),
};
data.contants.insert(name.to_string(), (&args[1]).clone());
}
"macro" => {
let name = match &args[0] {
arg::Arg::Variable(x) | arg::Arg::String(x) => x,
_ => return Err(
"First argument of define macro needs to be a literal-like"
.to_string(),
),
};
let args = match (&args[1..])
.iter()
// .map(|a| match a {
// arg::Arg::Variable(x) => Ok(x.clone()),
// __ => {
// Err("Macro arguments need to be variables".to_string())
// }
// })
.map(|a| {
if let arg::Arg::Variable(x) = a {
Ok(x.clone())
} else {
Err("Macro arguments need to be variables".to_string())
}
})
.collect::<Result<Vec<_>, _>>()
{
Ok(x) => x,
Err(x) => return Err(x),
};
data.macros.insert(
name.clone(),
Macro {
args,
body: Vec::new(),
},
);
data.state = State::Macro {
name: name.clone(),
depth: 1,
};
}
"macroend" => return Err("Unexpected macro end".to_string()),
"org" => {
data.offset = args[0].resolve_number(data).unwrap();
}
"org_add" => {
data.offset += args[0].resolve_number(data).unwrap();
}
"org_sub" => {
data.offset -= args[0].resolve_number(data).unwrap();
}
"bytes" => {
for arg in args {
for n in arg.resolve_bytes(data).unwrap() {
data.program[data.offset as usize] = n;
data.offset += 1;
}
}
}
"bw" => {
let string_arg = args[0].resolve_bytes(data).unwrap();
let string =
String::from_utf8(string_arg).unwrap().replace("\\n", "\n");
for n in string.bytes() {
data.program[data.offset as usize] = n;
data.offset += 1;
}
}
_ => match data.macros.get(&name) {
Some(m) => {
dbg!(name, m);
}
None => return Err(format!("Unknown macro: '{name}'")),
},
};
}
}
}
State::Macro { name, depth } => match &node {
parser::ast::Node::MacroCall {
name: node_name,
args: _,
} => match node_name.as_str() {
"macro" => {
data.state = State::Macro {
name: name.clone(),
depth: depth + 1,
};
}
"macroend" => {
if *depth - 1 == 0 {
data.state = State::Default;
} else {
data.state = State::Macro {
name: name.clone(),
depth: depth - 1,
};
}
}
_ => {
data.macros.get_mut(name).unwrap().body.push(node.clone());
}
},
_ => {
data.macros.get_mut(name).unwrap().body.push(node.clone());
}
},
}
if data.offset > (32 * 1024) {
return Err(format!(
"Offset out of bounds: 0x{} > 0x8000",
radix(data.offset, 16),
));
}
}
Ok(())
}