rewrote assembler backend to support custom executable format (DSE) and also refactored (moving crates around)
This commit is contained in:
@@ -13,6 +13,7 @@ name = "assembler"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[dependencies]
|
||||
binrw = "0.15.1"
|
||||
clap = { version = "4.6.0", features = ["derive"] }
|
||||
common = { path = "../common" }
|
||||
|
||||
|
||||
@@ -78,7 +78,7 @@ fn build_instruction(node: &Node) -> Result<Instruction, AssembleError> {
|
||||
|
||||
fn ins_mov(
|
||||
opcode: Opcode,
|
||||
args: &[crate::assembler::model::Token],
|
||||
args: &[Token],
|
||||
) -> Result<Instruction, AssembleError> {
|
||||
let Some(src_token) = args.first() else {
|
||||
return Err(AssembleError::MissingArgument(0));
|
||||
@@ -98,7 +98,7 @@ fn ins_mov(
|
||||
|
||||
fn ins_cmov(
|
||||
opcode: Opcode,
|
||||
args: &[crate::assembler::model::Token],
|
||||
args: &[Token],
|
||||
) -> Result<Instruction, AssembleError> {
|
||||
let Some(src_token) = args.first() else {
|
||||
return Err(AssembleError::MissingArgument(0));
|
||||
@@ -122,7 +122,7 @@ fn ins_cmov(
|
||||
|
||||
fn ins_ldx_stx(
|
||||
opcode: Opcode,
|
||||
args: &[crate::assembler::model::Token],
|
||||
args: &[Token],
|
||||
) -> Result<Instruction, AssembleError> {
|
||||
let Some(src_token) = args.first() else {
|
||||
return Err(AssembleError::MissingArgument(0));
|
||||
@@ -153,7 +153,7 @@ fn ins_ldx_stx(
|
||||
|
||||
fn ins_stack(
|
||||
opcode: Opcode,
|
||||
args: &[crate::assembler::model::Token],
|
||||
args: &[Token],
|
||||
) -> Result<Instruction, AssembleError> {
|
||||
let Some(reg_token) = args.first() else {
|
||||
return Err(AssembleError::MissingArgument(0));
|
||||
@@ -169,7 +169,7 @@ fn ins_stack(
|
||||
|
||||
fn ins_load_imm(
|
||||
opcode: Opcode,
|
||||
args: &[crate::assembler::model::Token],
|
||||
args: &[Token],
|
||||
) -> Result<Instruction, AssembleError> {
|
||||
let Some(value_token) = args.first() else {
|
||||
return Err(AssembleError::MissingArgument(0));
|
||||
@@ -190,7 +190,7 @@ fn ins_load_imm(
|
||||
|
||||
fn ins_jump_unconditional(
|
||||
opcode: Opcode,
|
||||
args: &[crate::assembler::model::Token],
|
||||
args: &[Token],
|
||||
) -> Result<Instruction, AssembleError> {
|
||||
let Some(addr_imm_token) = args.first() else {
|
||||
return Err(AssembleError::MissingArgument(0));
|
||||
@@ -212,7 +212,7 @@ fn ins_jump_unconditional(
|
||||
|
||||
fn ins_jump_conditional(
|
||||
opcode: Opcode,
|
||||
args: &[crate::assembler::model::Token],
|
||||
args: &[Token],
|
||||
) -> Result<Instruction, AssembleError> {
|
||||
let Some(condition_token) = args.first() else {
|
||||
return Err(AssembleError::MissingArgument(0));
|
||||
@@ -239,7 +239,7 @@ fn ins_jump_conditional(
|
||||
|
||||
fn ins_comparison(
|
||||
opcode: Opcode,
|
||||
args: &[crate::assembler::model::Token],
|
||||
args: &[Token],
|
||||
) -> Result<Instruction, AssembleError> {
|
||||
let Some(left_token) = args.first() else {
|
||||
return Err(AssembleError::MissingArgument(0));
|
||||
@@ -267,7 +267,7 @@ fn ins_comparison(
|
||||
|
||||
fn ins_bitshift(
|
||||
opcode: Opcode,
|
||||
args: &[crate::assembler::model::Token],
|
||||
args: &[Token],
|
||||
) -> Result<Instruction, AssembleError> {
|
||||
let Some(src_reg) = args.first() else {
|
||||
return Err(AssembleError::MissingArgument(0));
|
||||
@@ -296,7 +296,7 @@ fn ins_bitshift(
|
||||
|
||||
fn ins_arithmetic(
|
||||
opcode: Opcode,
|
||||
args: &[crate::assembler::model::Token],
|
||||
args: &[Token],
|
||||
) -> Result<Instruction, AssembleError> {
|
||||
let Some(left_token) = args.first() else {
|
||||
return Err(AssembleError::MissingArgument(0));
|
||||
@@ -327,7 +327,7 @@ fn ins_arithmetic(
|
||||
|
||||
fn ins_imm_arithmetic(
|
||||
opcode: Opcode,
|
||||
args: &[crate::assembler::model::Token],
|
||||
args: &[Token],
|
||||
) -> Result<Instruction, AssembleError> {
|
||||
let Some(reg_token) = args.first() else {
|
||||
return Err(AssembleError::MissingArgument(0));
|
||||
@@ -350,7 +350,7 @@ fn ins_imm_arithmetic(
|
||||
})
|
||||
}
|
||||
|
||||
fn ins_not(args: &[crate::assembler::model::Token]) -> Result<Instruction, AssembleError> {
|
||||
fn ins_not(args: &[Token]) -> Result<Instruction, AssembleError> {
|
||||
let Some(reg_token) = args.first() else {
|
||||
return Err(AssembleError::MissingArgument(0));
|
||||
};
|
||||
@@ -363,7 +363,7 @@ fn ins_not(args: &[crate::assembler::model::Token]) -> Result<Instruction, Assem
|
||||
Ok(Instruction::not(src, dest))
|
||||
}
|
||||
|
||||
fn ins_interrupt(args: &[crate::assembler::model::Token]) -> Result<Instruction, AssembleError> {
|
||||
fn ins_interrupt(args: &[Token]) -> Result<Instruction, AssembleError> {
|
||||
let Some(code_token) = args.first() else {
|
||||
return Err(AssembleError::MissingArgument(0));
|
||||
};
|
||||
@@ -373,7 +373,7 @@ fn ins_interrupt(args: &[crate::assembler::model::Token]) -> Result<Instruction,
|
||||
}
|
||||
|
||||
fn build_data_instruction(
|
||||
args: &[crate::assembler::model::Token],
|
||||
args: &[Token],
|
||||
) -> Result<Instruction, AssembleError> {
|
||||
let Some(immediate_token) = args.first() else {
|
||||
return Err(AssembleError::MissingArgument(0));
|
||||
@@ -384,7 +384,7 @@ fn build_data_instruction(
|
||||
}
|
||||
|
||||
fn build_segment_instruction(
|
||||
args: &[crate::assembler::model::Token],
|
||||
args: &[Token],
|
||||
) -> Result<Instruction, AssembleError> {
|
||||
let Some(immediate_token) = args.first() else {
|
||||
return Err(AssembleError::MissingArgument(0));
|
||||
|
||||
@@ -11,8 +11,11 @@ use std::{
|
||||
},
|
||||
thread,
|
||||
};
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::io::Cursor;
|
||||
use common::formats::binary_dse::{DseExecutable, SymbolEntry};
|
||||
use common::prelude::Instruction;
|
||||
use crate::assembler::resolver2::{resolve_address, resolve_dependencies, resolve_symbols, split_sections, SectionId};
|
||||
|
||||
// Module declarations
|
||||
#[macro_use]
|
||||
@@ -21,12 +24,14 @@ pub mod macros;
|
||||
#[allow(clippy::module_inception)]
|
||||
pub mod codegen;
|
||||
pub mod expand;
|
||||
pub mod lexer;
|
||||
pub mod model;
|
||||
pub mod parser;
|
||||
pub mod resolver;
|
||||
|
||||
use crate::assemblerv2::lexer::Lexer;
|
||||
mod lexer;
|
||||
mod model;
|
||||
pub mod parser;
|
||||
// pub mod resolver;
|
||||
pub mod resolver2;
|
||||
|
||||
|
||||
|
||||
// Re-exports
|
||||
pub use self::{
|
||||
@@ -35,7 +40,7 @@ pub use self::{
|
||||
lexer::lexer,
|
||||
model::{Module, Node, Opcode, Symbol, Token, TokenType},
|
||||
parser::{Parser, Program},
|
||||
resolver::{create_sections, resolve_dependencies, resolve_symbols},
|
||||
// resolver::{create_sections, resolve_dependencies, resolve_symbols},
|
||||
};
|
||||
|
||||
pub struct Assembler {
|
||||
@@ -67,23 +72,33 @@ impl Assembler {
|
||||
let tx = self.result_tx.clone();
|
||||
|
||||
thread::spawn(move || match assemble(&src) {
|
||||
Ok(res) => {
|
||||
let buffer: Vec<u8> = res
|
||||
.iter()
|
||||
.flat_map(|instruction| instruction.to_le_bytes())
|
||||
.collect();
|
||||
tx.send(Ok(buffer))
|
||||
.expect("Failed to send compilation result from worker thread");
|
||||
}
|
||||
Err(err) => {
|
||||
tx.send(Err(err))
|
||||
.expect("Failed to send compilation error from worker thread");
|
||||
Ok(exe) => {
|
||||
let mut buffer = Cursor::new(Vec::new());
|
||||
match exe.write(&mut buffer) {
|
||||
Ok(()) => {
|
||||
tx.send(Ok(buffer.into_inner()))
|
||||
.expect("Failed to send compilation result from worker thread");
|
||||
}
|
||||
Err(e) => {
|
||||
tx.send(Err(AssembleError::Generic))
|
||||
.expect("...");
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(err) => { /* unchanged */ }
|
||||
});
|
||||
|
||||
self.is_running = true;
|
||||
}
|
||||
|
||||
/// Polls the result channel to check if a compilation result is ready.
|
||||
///
|
||||
/// This method attempts a non-blocking read (`try_recv`) from the result receiver.
|
||||
/// It returns `Some(Ok(Vec<u8>))` if the compilation successfully finished and
|
||||
/// the binary output is available, or `None` if no result was received yet.
|
||||
///
|
||||
/// If it receives an error via `mpsc::TryRecvError::Disconnected`, it indicates that
|
||||
/// the worker thread terminated unexpectedly, returning a specific error string.
|
||||
pub fn poll(&mut self) -> Option<Result<Vec<u8>, String>> {
|
||||
if !self.is_running {
|
||||
return None;
|
||||
@@ -130,36 +145,114 @@ impl Assembler {
|
||||
|
||||
impl Assembler {}
|
||||
|
||||
fn assemble(src: &Path) -> Result<Vec<Instruction>, AssembleError> {
|
||||
// fn assemble(src: &Path) -> Result<Vec<Instruction>, AssembleError> {
|
||||
// let mut modules = HashSet::new();
|
||||
// let mut program = Program::new();
|
||||
//
|
||||
// let hash = quick_hash(src);
|
||||
//
|
||||
// if modules.contains(&hash) {
|
||||
// return Ok(vec![]);
|
||||
// }
|
||||
//
|
||||
// prepare_dependency(src, &mut modules, &mut program)?;
|
||||
//
|
||||
// let mut nodes = program.nodes.clone();
|
||||
//
|
||||
// println!("PRE LINK");
|
||||
// for node in &nodes {
|
||||
// println!("{:?}", node);
|
||||
// }
|
||||
//
|
||||
// create_sections(&mut nodes)?;
|
||||
// resolve_symbols(&mut nodes)?;
|
||||
//
|
||||
// println!("Generating assembly output...");
|
||||
//
|
||||
// let instructions = codegen(nodes)?;
|
||||
//
|
||||
// println!("Compilation Successful");
|
||||
// Ok(instructions)
|
||||
// }
|
||||
|
||||
fn assemble(src: &Path) -> Result<DseExecutable, AssembleError> {
|
||||
let mut modules = HashSet::new();
|
||||
let mut program = Program::new();
|
||||
|
||||
let hash = quick_hash(src);
|
||||
|
||||
if modules.contains(&hash) {
|
||||
return Ok(vec![]);
|
||||
}
|
||||
|
||||
prepare_dependency(src, &mut modules, &mut program)?;
|
||||
|
||||
let mut nodes = program.nodes.clone();
|
||||
let nodes = program.nodes.clone();
|
||||
|
||||
println!("PRE LINK");
|
||||
for node in &nodes {
|
||||
println!("{:?}", node);
|
||||
}
|
||||
|
||||
create_sections(&mut nodes)?;
|
||||
resolve_symbols(&mut nodes)?;
|
||||
let (data_nodes, mut text_nodes) = split_sections(nodes);
|
||||
let symbol_table = resolve_symbols(&data_nodes, &mut text_nodes)?;
|
||||
let text_len = text_nodes.len() as u32;
|
||||
|
||||
println!("Generating assembly output...");
|
||||
let instructions = codegen(text_nodes)?;
|
||||
let text: Vec<u32> = instructions
|
||||
.iter()
|
||||
.map(|i| i.0).collect();
|
||||
|
||||
let instructions = codegen(nodes)?;
|
||||
let data = build_data_bytes(&data_nodes)?;
|
||||
|
||||
// Build string table + SymbolEntry list from the resolved symbol table
|
||||
let mut string_table = Vec::new();
|
||||
let mut symbols = Vec::new();
|
||||
for (symbol, entry) in &symbol_table {
|
||||
let name_offset = string_table.len() as u32;
|
||||
string_table.extend_from_slice(symbol.name.as_bytes());
|
||||
string_table.push(0); // null terminator
|
||||
|
||||
symbols.push(SymbolEntry {
|
||||
name_offset,
|
||||
value: resolve_address(*entry, text_len),
|
||||
section: match entry.1 {
|
||||
SectionId::Text => 2,
|
||||
SectionId::Data => 1,
|
||||
},
|
||||
binding: 1, // global — refine later if adding local/weak
|
||||
_pad: 0,
|
||||
});
|
||||
}
|
||||
|
||||
// Entry point: first symbol to jump to is '_init'
|
||||
let entry_point = symbol_table
|
||||
.iter()
|
||||
.find(|(sym, _)| sym.name == "_init")
|
||||
.map(|(_, entry)| resolve_address(*entry, text_len))
|
||||
.ok_or(AssembleError::UndefinedSymbol(Symbol {
|
||||
name: "_init".to_string(),
|
||||
module: Module::Resolved(0),
|
||||
}))?;
|
||||
|
||||
println!("Compilation Successful");
|
||||
Ok(instructions)
|
||||
|
||||
Ok(DseExecutable::new(
|
||||
symbols.as_slice(),
|
||||
&string_table,
|
||||
&data,
|
||||
&text,
|
||||
entry_point,
|
||||
).unwrap())
|
||||
}
|
||||
|
||||
fn build_data_bytes(data_nodes: &[Node]) -> Result<Vec<u8>, AssembleError> {
|
||||
let mut bytes = Vec::with_capacity(data_nodes.len() * 4);
|
||||
for node in data_nodes {
|
||||
let Token::Immediate(value) = node.arg(0)? else {
|
||||
return Err(AssembleError::InvalidArg);
|
||||
};
|
||||
bytes.extend_from_slice(&value.to_le_bytes());
|
||||
}
|
||||
Ok(bytes)
|
||||
}
|
||||
|
||||
|
||||
fn prepare_dependency(
|
||||
path: &Path,
|
||||
modules: &mut HashSet<u64>,
|
||||
|
||||
@@ -0,0 +1,168 @@
|
||||
use std::collections::HashMap;
|
||||
use std::path::Path;
|
||||
|
||||
use crate::assembler::model::{Module, Node, Opcode, Symbol, Token};
|
||||
use crate::assembler::{quick_hash, AssembleError};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum SectionId {
|
||||
Data,
|
||||
Text,
|
||||
}
|
||||
|
||||
/// Splits a flat (possibly multi-module) node list into separate data and
|
||||
/// text streams. Segment/Include markers are dropped here — module
|
||||
/// disambiguation lives on `Symbol.module`, not node position, so we don't
|
||||
/// need them past this point.
|
||||
pub fn split_sections(nodes: Vec<Node>) -> (Vec<Node>, Vec<Node>) {
|
||||
let mut data = Vec::new();
|
||||
let mut text = Vec::new();
|
||||
|
||||
for node in nodes {
|
||||
match node.opcode() {
|
||||
Opcode::Data => data.push(node),
|
||||
Opcode::Segment | Opcode::Include => {}
|
||||
_ => text.push(node),
|
||||
}
|
||||
}
|
||||
|
||||
(data, text)
|
||||
}
|
||||
|
||||
pub fn generate_symbol_table(
|
||||
data_nodes: &[Node],
|
||||
text_nodes: &[Node],
|
||||
) -> HashMap<Symbol, (u32, SectionId)> {
|
||||
let mut table = HashMap::new();
|
||||
|
||||
for (i, node) in data_nodes.iter().enumerate() {
|
||||
if let Some(symbol) = node.label() {
|
||||
table.insert(symbol, (4 * i as u32, SectionId::Data));
|
||||
}
|
||||
}
|
||||
for (i, node) in text_nodes.iter().enumerate() {
|
||||
if let Some(symbol) = node.label() {
|
||||
table.insert(symbol, (4 * i as u32, SectionId::Text));
|
||||
}
|
||||
}
|
||||
|
||||
table
|
||||
}
|
||||
|
||||
/// v1 loader convention: TEXT is placed at address 0, DATA immediately
|
||||
/// follows it. This is a stopgap until real relocations exist — it's the
|
||||
/// same assumption your current jmp-hack already makes, just explicit now.
|
||||
pub fn resolve_address((offset, section): (u32, SectionId), text_len_words: u32) -> u32 {
|
||||
match section {
|
||||
SectionId::Text => offset,
|
||||
SectionId::Data => (text_len_words * 4) + offset,
|
||||
}
|
||||
}
|
||||
|
||||
/// Patches every symbol reference in `text_nodes` with its resolved absolute
|
||||
/// address, and returns the symbol table so the caller can build the
|
||||
/// executable's symbol/string tables from it.
|
||||
pub fn resolve_symbols(
|
||||
data_nodes: &[Node],
|
||||
text_nodes: &mut [Node],
|
||||
) -> Result<HashMap<Symbol, (u32, SectionId)>, AssembleError> {
|
||||
let symbol_table = generate_symbol_table(data_nodes, text_nodes);
|
||||
let text_len = text_nodes.len() as u32;
|
||||
|
||||
for node in text_nodes.iter_mut() {
|
||||
match node.opcode() {
|
||||
Opcode::Jmp | Opcode::Call | Opcode::Jic | Opcode::Jnc | Opcode::Lli | Opcode::Lui => {
|
||||
if let Token::Symbol(symbol) = node
|
||||
.arg(0)
|
||||
.expect("Expected argument 0 for jump-like opcode")
|
||||
{
|
||||
let Some(entry) = symbol_table.get(&symbol) else {
|
||||
return Err(AssembleError::UndefinedSymbol(symbol));
|
||||
};
|
||||
node.tokens[0] = Token::Immediate(resolve_address(*entry, text_len));
|
||||
}
|
||||
}
|
||||
Opcode::Jez | Opcode::Jnz => {
|
||||
if let Token::Symbol(symbol) = node
|
||||
.arg(1)
|
||||
.expect("Expected argument 1 for jump-like opcode")
|
||||
{
|
||||
let Some(entry) = symbol_table.get(&symbol) else {
|
||||
return Err(AssembleError::UndefinedSymbol(symbol));
|
||||
};
|
||||
node.tokens[1] = Token::Immediate(resolve_address(*entry, text_len));
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(symbol_table)
|
||||
}
|
||||
|
||||
// resolve_dependencies is unchanged — keep exactly as you have it.
|
||||
pub fn resolve_dependencies(
|
||||
mut nodes: Vec<Node>,
|
||||
base_dir: &Path,
|
||||
) -> Result<Vec<Node>, AssembleError> {
|
||||
// First we get a list of imports.
|
||||
let mut dependencies = Vec::new();
|
||||
for node in &nodes {
|
||||
if node.opcode() == Opcode::Include {
|
||||
// we want the path, and the name
|
||||
let name = if let Token::Symbol(name) = node
|
||||
.arg(0)
|
||||
.expect("Expected argument #0 for Include directive.")
|
||||
{
|
||||
name.name.clone()
|
||||
} else {
|
||||
unreachable!()
|
||||
}; //node.2.get(0).unwrap()
|
||||
|
||||
let Ok(Token::StringLit(path)) = node.arg(1) else {
|
||||
unreachable!()
|
||||
};
|
||||
|
||||
let full_path = base_dir.join(path);
|
||||
let canonical_path = full_path
|
||||
.canonicalize()
|
||||
.map_err(|_| AssembleError::InvalidFile(full_path.clone()))?;
|
||||
|
||||
let hash = quick_hash(&canonical_path);
|
||||
|
||||
dependencies.push((name, hash));
|
||||
}
|
||||
}
|
||||
|
||||
let mut changes = Vec::<(u32, u32, Symbol)>::new();
|
||||
// now we resolve the symbols on all the nodes
|
||||
// we need to check all operands for unresolved signals
|
||||
for (i, node) in nodes.clone().iter().enumerate() {
|
||||
let Node {
|
||||
tokens: operands, ..
|
||||
} = node;
|
||||
for (j, token) in operands.iter().enumerate() {
|
||||
if let Token::Symbol(symbol) = token {
|
||||
for d in &dependencies {
|
||||
if let Module::Unresolved(name) = symbol.module.clone() {
|
||||
if name != d.0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
let symbol = Symbol {
|
||||
name: symbol.name.clone(),
|
||||
module: Module::Resolved(d.1),
|
||||
};
|
||||
changes.push((i as u32, j as u32, symbol));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (i, j, symbol) in changes {
|
||||
nodes[i as usize].tokens[j as usize] = Token::Symbol(symbol);
|
||||
}
|
||||
|
||||
Ok(nodes)
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
pub struct BuildContext {
|
||||
filename: PathBuf,
|
||||
stage: BuildStage,
|
||||
// Logging
|
||||
}
|
||||
|
||||
impl BuildContext {
|
||||
pub fn new(filename: PathBuf) -> Self {
|
||||
Self {
|
||||
filename,
|
||||
stage: BuildStage::Lexing,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum BuildStage {
|
||||
Lexing,
|
||||
Parsing,
|
||||
PseudoExpansion,
|
||||
Linking,
|
||||
CodeGeneration,
|
||||
}
|
||||
@@ -2,7 +2,8 @@ use std::{str::FromStr, thread, time::Duration};
|
||||
|
||||
use common::{asm::AsmOpcode, prelude::Register};
|
||||
|
||||
use crate::assembler::{AssembleError, Module, Opcode, Symbol, Token, lexer::parse_opcode};
|
||||
use crate::assembler::{AssembleError, Module, Symbol};
|
||||
use crate::assemblerv2::Token;
|
||||
|
||||
pub struct Lexer {
|
||||
src: Vec<u8>,
|
||||
@@ -75,7 +76,7 @@ impl Lexer {
|
||||
buffer.push(self.src.pop().unwrap() as char);
|
||||
}
|
||||
|
||||
if let Ok(opcode) = Opcode::from_str(&buffer) {
|
||||
if let Ok(opcode) = AsmOpcode::from_str(&buffer) {
|
||||
tokens.push(Token::Opcode(opcode));
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1,46 +1,43 @@
|
||||
// mod error;
|
||||
pub mod lexer;
|
||||
// pub mod parser;
|
||||
mod error;
|
||||
mod lexer;
|
||||
mod parser;
|
||||
|
||||
use core::fmt;
|
||||
|
||||
use crate::assembler::{AssembleError, Symbol};
|
||||
// use common::{asm::AsmOpcode, prelude::Register};
|
||||
use common::{asm::AsmOpcode, prelude::Register};
|
||||
use lexer::Lexer;
|
||||
// use parser::Parser;
|
||||
use parser::Parser;
|
||||
|
||||
pub fn asm(input: &str) -> Result<Vec<u8>, AssembleError> {
|
||||
let mut lexer = Lexer::new(input.to_string(), 0);
|
||||
|
||||
let tokens = lexer.run()?;
|
||||
|
||||
// let ast = Parser::new(tokens).parse().unwrap();
|
||||
let ast = Parser::new(tokens).parse().unwrap();
|
||||
|
||||
// println!("{:#?}", ast);
|
||||
|
||||
// let ast = parser::parse(tokens)?;
|
||||
println!("{:#?}", ast);
|
||||
Ok(vec![])
|
||||
}
|
||||
|
||||
// #[derive(Debug, Clone)]
|
||||
// pub enum Token {
|
||||
// Symbol(Symbol),
|
||||
// Register(Register),
|
||||
// Immediate(u32),
|
||||
// StringLit(String),
|
||||
// CharLit(char),
|
||||
// Opcode(AsmOpcode),
|
||||
// }
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Token {
|
||||
Symbol(Symbol),
|
||||
Register(Register),
|
||||
Immediate(u32),
|
||||
StringLit(String),
|
||||
CharLit(char),
|
||||
Opcode(AsmOpcode),
|
||||
}
|
||||
|
||||
// impl fmt::Display for Token {
|
||||
// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
// match self {
|
||||
// Self::Symbol(symbol) => write!(f, "{symbol}"),
|
||||
// Self::Register(register) => write!(f, "{register}",),
|
||||
// Self::Immediate(immediate) => write!(f, "{immediate}",),
|
||||
// Self::StringLit(string_lit) => write!(f, "{string_lit}",),
|
||||
// Self::CharLit(char_lit) => write!(f, "{char_lit}",),
|
||||
// Self::Opcode(opcode) => write!(f, "{opcode}",),
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
impl fmt::Display for Token {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::Symbol(symbol) => write!(f, "{symbol}"),
|
||||
Self::Register(register) => write!(f, "{register}",),
|
||||
Self::Immediate(immediate) => write!(f, "{immediate}",),
|
||||
Self::StringLit(string_lit) => write!(f, "{string_lit}",),
|
||||
Self::CharLit(char_lit) => write!(f, "{char_lit}",),
|
||||
Self::Opcode(opcode) => write!(f, "{opcode}",),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,11 +2,11 @@ use std::path::PathBuf;
|
||||
|
||||
use common::asm::{AsmInstruction, AsmOpcode};
|
||||
|
||||
use crate::assembler::Symbol;
|
||||
use crate::assembler::{Symbol};
|
||||
|
||||
use crate::{expect_tt, expect_value};
|
||||
|
||||
use super::{Token, error::AssembleError};
|
||||
use super::{error::AssembleError, Token};
|
||||
|
||||
pub struct AsmNode {
|
||||
pub label: Option<Symbol>,
|
||||
|
||||
@@ -1,158 +0,0 @@
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
use std::io::{Cursor, Read, Seek, SeekFrom, Write};
|
||||
use std::fs::File;
|
||||
use binrw::{binrw, BinRead, binwrite, NullString, BinReaderExt, BinWriterExt, BinWrite};
|
||||
|
||||
use common::prelude::Instruction;
|
||||
use crate::assembler::Symbol;
|
||||
|
||||
pub fn disassemble(in_path: PathBuf, out_path: PathBuf) -> Result<(), String> {
|
||||
|
||||
let Ok(content) = fs::read(in_path) else {
|
||||
return Err(String::new())
|
||||
};
|
||||
|
||||
for (line_num, chunk) in content.chunks(4).enumerate() {
|
||||
let address = line_num * 4;
|
||||
|
||||
let value = u32::from_le_bytes(*chunk.as_array().unwrap());
|
||||
println!("{:#?}", Instruction(value));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
#[binrw]
|
||||
#[brw(magic = b"DSAX", little)]
|
||||
#[derive(Debug, Clone)]
|
||||
struct DseHeader {
|
||||
version: u16,
|
||||
flags: u16,
|
||||
entry_point: u32,
|
||||
|
||||
symbol_table_offset: u32,
|
||||
symbol_table_size: u32,
|
||||
|
||||
string_table_offset: u32,
|
||||
string_table_size: u32,
|
||||
|
||||
data_offset: u32,
|
||||
data_size: u32,
|
||||
|
||||
text_offset: u32,
|
||||
text_size: u32
|
||||
}
|
||||
|
||||
#[binrw]
|
||||
#[brw(little)]
|
||||
#[derive(Debug, Clone)]
|
||||
struct SymbolEntry {
|
||||
name_offset: u32,
|
||||
value: u32,
|
||||
section: u8,
|
||||
binding: u8,
|
||||
_pad: u16
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct DseExecutable {
|
||||
header: DseHeader,
|
||||
symbols: Vec<SymbolEntry>,
|
||||
string_table: Vec<u8>,
|
||||
data: Vec<u8>,
|
||||
text: Vec<u32>
|
||||
}
|
||||
|
||||
impl DseExecutable {
|
||||
fn load(path: PathBuf) -> binrw::BinResult<Self> {
|
||||
let mut file = File::open(path).map_err(|e| binrw::Error::Io(e))?;
|
||||
Self::read(&mut file)
|
||||
}
|
||||
|
||||
fn read<R: Read + Seek>(reader: &mut R) -> binrw::BinResult<Self> {
|
||||
let header = DseHeader::read(reader)?;
|
||||
|
||||
reader.seek(SeekFrom::Start(header.symbol_table_offset as u64))?;
|
||||
let symbols: Vec<SymbolEntry> = (0..header.symbol_table_size)
|
||||
.map(|_| SymbolEntry::read(reader))
|
||||
.collect::<binrw::BinResult<_>>()?;
|
||||
|
||||
reader.seek(SeekFrom::Start(header.string_table_offset as u64))?;
|
||||
let mut string_table = vec![0u8; header.string_table_size as usize];
|
||||
reader.read_exact(&mut string_table).map_err(|e| binrw::Error::Io(e))?;
|
||||
|
||||
reader.seek(SeekFrom::Start(header.data_offset as u64))?;
|
||||
let mut data = vec![0u8; header.data_size as usize];
|
||||
reader.read_exact(&mut data).map_err(|e| binrw::Error::Io(e))?;
|
||||
|
||||
reader.seek(SeekFrom::Start(header.text_offset as u64))?;
|
||||
let text: Vec<u32> = (0..header.text_size)
|
||||
.map(|_| reader.read_le::<u32>())
|
||||
.collect::<binrw::BinResult<_>>()?;
|
||||
|
||||
Ok(DseExecutable { header, symbols, string_table, data, text })
|
||||
}
|
||||
|
||||
fn save(&self, path: PathBuf) -> binrw::BinResult<()> {
|
||||
let mut file = File::create(path)
|
||||
.map_err(|e| binrw::Error::Io(e))?;
|
||||
self.write(&mut file)
|
||||
}
|
||||
|
||||
fn write<W: Write + Seek>(&self, writer: &mut W) -> binrw::BinResult<()> {
|
||||
self.header.write(writer)?;
|
||||
|
||||
for symbol in &self.symbols {
|
||||
symbol.write(writer)?;
|
||||
}
|
||||
|
||||
writer.write_all(&self.string_table)?;
|
||||
writer.write_all(&self.data)?;
|
||||
|
||||
for word in &self.text {
|
||||
writer.write_le(word)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn new(
|
||||
symbols: &[SymbolEntry],
|
||||
string_table: &[u8],
|
||||
data: &[u8],
|
||||
text: &[u32],
|
||||
entry_point: u32,
|
||||
) -> DseExecutable {
|
||||
const HEADER_SIZE: u32 = 4 + 2 + 2 + 4 + 4*2 + 4*2 + 4*2 + 4*2; // adjust to actual packed size
|
||||
|
||||
let symbol_table_offset = HEADER_SIZE;
|
||||
let symbol_table_size = symbols.len() as u32 * 8; // 4+4+1+1+2 padded = 8 bytes each
|
||||
let string_table_offset = symbol_table_offset + symbol_table_size;
|
||||
let data_offset = string_table_offset + string_table.len() as u32;
|
||||
let text_offset = data_offset + data.len() as u32;
|
||||
|
||||
let header = DseHeader {
|
||||
version: 1,
|
||||
flags: 0,
|
||||
entry_point,
|
||||
symbol_table_offset,
|
||||
symbol_table_size: symbols.len() as u32,
|
||||
string_table_offset,
|
||||
string_table_size: string_table.len() as u32,
|
||||
data_offset,
|
||||
data_size: data.len() as u32,
|
||||
text_offset,
|
||||
text_size: text.len() as u32,
|
||||
};
|
||||
|
||||
DseExecutable {
|
||||
header,
|
||||
symbols: symbols.to_vec(),
|
||||
string_table: string_table.to_vec(),
|
||||
data: data.to_vec(),
|
||||
text: text.to_vec(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use assembler::prelude::*;
|
||||
use clap::{Parser, arg, command};
|
||||
use clap::{Parser, arg};
|
||||
use std::{fs, path::PathBuf};
|
||||
|
||||
#[derive(Parser, Debug, Clone)]
|
||||
@@ -23,11 +23,4 @@ fn main() {
|
||||
eprintln!("Failed to write to output file: {e}");
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
// let input = fs::read_to_string("../../dsa_resources/framebuffer.dsa").unwrap();
|
||||
// let output = asm(&input).unwrap();
|
||||
|
||||
// for token in output {
|
||||
// println!("{:?}", token);
|
||||
// }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user