281 lines
8.7 KiB
Rust
281 lines
8.7 KiB
Rust
use std::{fs, path::Path};
|
|
|
|
use common::prelude::*;
|
|
|
|
use crate::{
|
|
assembler::{
|
|
Module, Node, Opcode, Symbol, Token, codegen, create_sections, expand_pseudo_ops,
|
|
resolve_symbols,
|
|
},
|
|
node,
|
|
};
|
|
|
|
#[must_use]
|
|
pub fn build(src: &Path) -> Vec<Instruction> {
|
|
let src = fs::read_to_string(src).expect("Failed to read source file");
|
|
let mut nodes = parse(&src);
|
|
|
|
// we need to expand pseudoinstructions etc now
|
|
nodes = expand_pseudo_ops(nodes, 0).expect("Failed to expand pseudo-operations");
|
|
|
|
create_sections(&mut nodes).expect("Failed to create sections");
|
|
|
|
for n in &nodes {
|
|
println!("{n}");
|
|
}
|
|
|
|
resolve_symbols(&mut nodes).expect("Failed to resolve symbols");
|
|
|
|
codegen(nodes).expect("Failed to generate code from nodes")
|
|
}
|
|
|
|
#[must_use]
|
|
#[expect(clippy::too_many_lines)]
|
|
pub fn parse(src: &str) -> Vec<Node> {
|
|
let stack = Token::Immediate(0x10000);
|
|
let acc = Token::Register(Register::Acc);
|
|
let rga = Token::Register(Register::Rga);
|
|
|
|
let bpr = Token::Register(Register::Bpr);
|
|
let spr = Token::Register(Register::Spr);
|
|
let mut nodes = Vec::<Node>::new();
|
|
|
|
// Define symbols
|
|
let print_start = Symbol {
|
|
name: "print".to_string(),
|
|
module: Module::Resolved(0),
|
|
};
|
|
|
|
let tokens = lex(src);
|
|
|
|
// let _id = 0;
|
|
let mut idstack = Vec::<u32>::new();
|
|
|
|
nodes.extend(vec![
|
|
// set up a stack
|
|
node!(None, Opcode::Lwi, stack, bpr),
|
|
node!(None, Opcode::Mov, bpr, spr),
|
|
// set up the data pointer
|
|
node!(
|
|
Some(Symbol {
|
|
name: "main".to_string(),
|
|
module: Module::Resolved(0)
|
|
}),
|
|
Opcode::Lwi,
|
|
Token::Immediate(0x30000),
|
|
rga
|
|
),
|
|
]);
|
|
|
|
for (id, tok) in tokens.iter().enumerate() {
|
|
match tok {
|
|
BfToken::Inc => {
|
|
// inc acc
|
|
nodes.extend(vec![node!(None, Opcode::Inc, acc)]);
|
|
}
|
|
BfToken::Dec => {
|
|
// dec acc
|
|
nodes.extend(vec![node!(None, Opcode::Dec, acc)]);
|
|
}
|
|
BfToken::IncPtr => {
|
|
// stb acc, rga
|
|
// add rga, 4
|
|
// ldb rga, acc
|
|
nodes.extend(vec![
|
|
node!(None, Opcode::Stw, acc, rga, 0),
|
|
node!(None, Opcode::AddI, rga, 4, rga),
|
|
node!(None, Opcode::Ldw, rga, acc, 0),
|
|
]);
|
|
}
|
|
BfToken::DecPtr => {
|
|
// stb acc, rga
|
|
// sub rga, 4
|
|
// ldb rga, acc
|
|
nodes.extend(vec![
|
|
node!(None, Opcode::Stw, acc, rga, 0),
|
|
node!(None, Opcode::SubI, rga, 4, rga),
|
|
node!(None, Opcode::Ldw, rga, acc, 0),
|
|
]);
|
|
}
|
|
BfToken::Out => {
|
|
// push rga
|
|
// call print
|
|
// pop zero
|
|
nodes.extend(vec![
|
|
node!(None, Opcode::Push, acc),
|
|
node!(None, Opcode::Call, Token::Symbol(print_start.clone())),
|
|
node!(None, Opcode::Pop, Token::Register(Register::Zero)),
|
|
]);
|
|
}
|
|
BfToken::In => {
|
|
// Read a byte from input and store it at the current data pointer
|
|
// Assuming we have an input function mapped to a specific memory location or I/O port
|
|
nodes.extend(vec![
|
|
// Read input (assuming input is mapped to memory address 0x40000)
|
|
node!(None, Opcode::Ldw, Token::Immediate(0x40000), acc, 0),
|
|
// Store the input byte at the current data pointer
|
|
]);
|
|
}
|
|
BfToken::Forward => {
|
|
// Start of loop [
|
|
let loop_start = format!("loop_start_{id}");
|
|
let loop_end = format!("loop_end_{id}");
|
|
|
|
// Push the current position for the matching ]
|
|
idstack.push(id as u32);
|
|
|
|
// Load current cell value and check if zero
|
|
nodes.extend(vec![
|
|
// Compare with zero
|
|
node!(None, Opcode::Cmp, acc, Token::Register(Register::Zero)),
|
|
// If zero, jump to end of loop
|
|
node!(
|
|
None,
|
|
Opcode::Jeq,
|
|
Token::Symbol(Symbol {
|
|
name: loop_end,
|
|
module: Module::Resolved(0),
|
|
}),
|
|
Token::Register(Register::Zero)
|
|
),
|
|
]);
|
|
|
|
// Add label for loop start
|
|
nodes.push(node!(
|
|
Some(Symbol {
|
|
name: loop_start,
|
|
module: Module::Resolved(0),
|
|
}),
|
|
Opcode::Nop
|
|
));
|
|
}
|
|
BfToken::Back => {
|
|
// End of loop ]
|
|
if let Some(start_id) = idstack.pop() {
|
|
let loop_start = format!("loop_start_{start_id}");
|
|
let loop_end = format!("loop_end_{start_id}");
|
|
|
|
// Jump back to the start of the loop
|
|
nodes.extend(vec![
|
|
// Compare with zero
|
|
node!(None, Opcode::Cmp, acc, Token::Register(Register::Zero)),
|
|
// If not zero, jump back to start of loop
|
|
node!(
|
|
None,
|
|
Opcode::Jne,
|
|
Token::Symbol(Symbol {
|
|
name: loop_start,
|
|
module: Module::Resolved(0),
|
|
}),
|
|
Token::Register(Register::Zero)
|
|
),
|
|
// Add label for loop end
|
|
node!(
|
|
Some(Symbol {
|
|
name: loop_end,
|
|
module: Module::Resolved(0),
|
|
}),
|
|
Opcode::Nop
|
|
),
|
|
]);
|
|
} else {
|
|
// Unmatched closing bracket - could add error handling here
|
|
eprintln!("Warning: Unmatched ']' at position {id}");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
nodes.push(node!(None, Opcode::Hlt));
|
|
|
|
insert_lib(&mut nodes);
|
|
|
|
nodes
|
|
}
|
|
|
|
fn insert_lib(nodes: &mut Vec<Node>) {
|
|
let bpr = Token::Register(Register::Bpr);
|
|
let spr = Token::Register(Register::Spr);
|
|
let rg0 = Token::Register(Register::Rg0);
|
|
let rg1 = Token::Register(Register::Rg1);
|
|
|
|
let print_start = Symbol {
|
|
name: "print".to_string(),
|
|
module: Module::Resolved(0),
|
|
};
|
|
let current = Symbol {
|
|
name: "current".to_string(),
|
|
module: Module::Resolved(0),
|
|
};
|
|
// set up the program framework.
|
|
nodes.extend(vec![
|
|
// set display to 0x20000
|
|
node!(
|
|
None,
|
|
Opcode::Dw,
|
|
Token::Symbol(current.clone()),
|
|
Token::Immediate(0x20000)
|
|
),
|
|
// print function
|
|
// initialisation
|
|
node!(Some(print_start), Opcode::Push, bpr),
|
|
node!(None, Opcode::Mov, spr, bpr),
|
|
// function body
|
|
node!(None, Opcode::Ldw, bpr, rg0, Token::Immediate(8)),
|
|
node!(
|
|
None,
|
|
Opcode::Ldw,
|
|
Token::Symbol(current.clone()), // Load address of current
|
|
rg1,
|
|
Token::Immediate(0)
|
|
),
|
|
node!(None, Opcode::Stb, rg0, rg1, Token::Immediate(0)),
|
|
node!(None, Opcode::AddI, rg1, Token::Immediate(1), rg1),
|
|
// function return according to spec.
|
|
node!(
|
|
None,
|
|
Opcode::Stw,
|
|
rg1,
|
|
Token::Symbol(current), // Store back to current
|
|
Token::Immediate(0)
|
|
),
|
|
node!(None, Opcode::Mov, bpr, spr),
|
|
node!(None, Opcode::Pop, bpr),
|
|
node!(None, Opcode::Return),
|
|
]);
|
|
}
|
|
|
|
enum BfToken {
|
|
Inc,
|
|
Dec,
|
|
IncPtr,
|
|
DecPtr,
|
|
Out,
|
|
In,
|
|
Forward,
|
|
Back,
|
|
}
|
|
|
|
fn lex(src: &str) -> Vec<BfToken> {
|
|
src.chars()
|
|
.filter_map(|c| match c {
|
|
'+' => Some(BfToken::Inc),
|
|
'-' => Some(BfToken::Dec),
|
|
'>' => Some(BfToken::IncPtr),
|
|
'<' => Some(BfToken::DecPtr),
|
|
'.' => Some(BfToken::Out),
|
|
',' => Some(BfToken::In),
|
|
'[' => Some(BfToken::Forward),
|
|
']' => Some(BfToken::Back),
|
|
_ => None,
|
|
})
|
|
.collect()
|
|
}
|
|
|
|
fn _create_symbol(id: u32) -> Symbol {
|
|
Symbol {
|
|
name: format!("label_{id}"),
|
|
module: Module::Resolved(0),
|
|
}
|
|
}
|