Rewrie assembler with macro support
This commit is contained in:
parent
e82c5bdb90
commit
6adb943754
9 changed files with 435 additions and 264 deletions
|
@ -1,12 +1,281 @@
|
|||
use itertools::Itertools;
|
||||
use radix_fmt::radix;
|
||||
// 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(())
|
||||
// }
|
||||
|
||||
use anyhow::Result;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::arg;
|
||||
use crate::parser;
|
||||
|
||||
pub trait ToCode {
|
||||
fn to_code(&self) -> String;
|
||||
pub trait ToAssembly {
|
||||
fn to_assembly(&self) -> String;
|
||||
}
|
||||
|
||||
pub trait ByteResolvable<T> {
|
||||
|
@ -16,254 +285,70 @@ pub trait ByteResolvable<T> {
|
|||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Macro {
|
||||
pub args: Vec<String>,
|
||||
pub body: Vec<parser::ast::Node>,
|
||||
pub enum ResolveResult {
|
||||
Resolved(Vec<u8>),
|
||||
Partial(u16),
|
||||
Unresolved,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum State {
|
||||
Default,
|
||||
Macro { name: String, depth: usize },
|
||||
impl ResolveResult {
|
||||
pub fn empty() -> Self {
|
||||
ResolveResult::Resolved(vec![])
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub const PROGRAM_SIZE: u16 = 32 * 1024;
|
||||
pub type Program = [u8; PROGRAM_SIZE as usize];
|
||||
|
||||
pub struct Data {
|
||||
pub dir: String,
|
||||
pub body: Vec<parser::ast::Node>,
|
||||
pub program: [u8; 32 * 1024],
|
||||
pub ast: parser::ast::AST,
|
||||
pub program: Program,
|
||||
pub offset: u16,
|
||||
pub contants: HashMap<String, arg::Arg>,
|
||||
pub macros: HashMap<String, Macro>,
|
||||
pub state: State,
|
||||
pub body_stack: Vec<parser::ast::Node>,
|
||||
pub constants: HashMap<String, arg::Arg>,
|
||||
}
|
||||
|
||||
impl Data {
|
||||
pub fn new(dir: String, body: Vec<parser::ast::Node>) -> Self {
|
||||
pub fn new(dir: String, ast: parser::ast::AST) -> Self {
|
||||
Self {
|
||||
dir,
|
||||
body,
|
||||
program: [0; 32 * 1024],
|
||||
body_stack: ast.body.iter().rev().cloned().collect(),
|
||||
ast,
|
||||
program: [0; PROGRAM_SIZE as usize],
|
||||
offset: 0,
|
||||
contants: HashMap::new(),
|
||||
macros: HashMap::new(),
|
||||
state: State::Default,
|
||||
constants: HashMap::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
pub fn resolve_node(&mut self, node: parser::ast::Node) -> ResolveResult {
|
||||
match node {
|
||||
parser::ast::Node::Comment(_) => ResolveResult::empty(),
|
||||
parser::ast::Node::Label(x) => {
|
||||
self.constants.insert(x, arg::Arg::Number(self.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}'")),
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
ResolveResult::empty()
|
||||
}
|
||||
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());
|
||||
}
|
||||
},
|
||||
}
|
||||
parser::ast::Node::Call { name, arg } => {
|
||||
dbg!(name, arg);
|
||||
|
||||
if data.offset > (32 * 1024) {
|
||||
return Err(format!(
|
||||
"Offset out of bounds: 0x{} > 0x8000",
|
||||
radix(data.offset, 16),
|
||||
));
|
||||
ResolveResult::empty()
|
||||
}
|
||||
parser::ast::Node::MacroCall { name, args } => {
|
||||
dbg!(name, args);
|
||||
|
||||
ResolveResult::empty()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
pub fn assemble(&mut self) -> Result<Program> {
|
||||
while let Some(node) = self.body_stack.pop() {
|
||||
let res = self.resolve_node(node);
|
||||
dbg!(res);
|
||||
}
|
||||
|
||||
Ok(self.program)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue