rewrote assembler backend to support custom executable format (DSE) and also refactored (moving crates around)

This commit is contained in:
2026-07-16 00:34:13 +01:00
parent 7a84073bc4
commit a4d42bdad6
60 changed files with 1486 additions and 1669 deletions
+1
View File
@@ -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" }
+15 -15
View File
@@ -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));
+123 -30
View File
@@ -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>,
+168
View File
@@ -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)
}
+24
View File
@@ -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,
}
+3 -2
View File
@@ -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;
}
+28 -31
View File
@@ -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 -2
View File
@@ -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>,
-158
View File
@@ -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 -8
View File
@@ -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);
// }
}