added a create-project system to assembler, and fixed a couple of parsing bugs
This commit is contained in:
@@ -7,6 +7,7 @@ pub fn codegen(nodes: Vec<Node>) -> Result<Vec<Instruction>, AssembleError> {
|
||||
let mut instructions = vec![];
|
||||
|
||||
for node in nodes {
|
||||
println!("node {node}");
|
||||
instructions.push(build_instruction(node)?);
|
||||
}
|
||||
|
||||
@@ -169,6 +170,9 @@ fn build_instruction(node: Node) -> Result<Instruction, AssembleError> {
|
||||
let immediate = expect_token!(args.first().unwrap(), Immediate)?;
|
||||
Ok(Instruction::Segment(immediate))
|
||||
}
|
||||
|
||||
// These pseudo-instructions should have been expanded!
|
||||
// this case being activated suggests the wrong syntax was given, and indicates a bug in the parser
|
||||
Opcode::Db
|
||||
| Opcode::Dh
|
||||
| Opcode::Dw
|
||||
@@ -178,6 +182,10 @@ fn build_instruction(node: Node) -> Result<Instruction, AssembleError> {
|
||||
| Opcode::Push
|
||||
| Opcode::Pop
|
||||
| Opcode::Lwi
|
||||
| Opcode::Include => todo!(),
|
||||
| Opcode::Include
|
||||
| Opcode::Call
|
||||
| Opcode::Return
|
||||
| Opcode::Pusha
|
||||
| Opcode::Popa => Err(AssembleError::InvalidArg),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,10 @@ fn try_expand(
|
||||
match node.opcode() {
|
||||
Opcode::Push => expand_push(node.clone(), result)?,
|
||||
Opcode::Pop => expand_pop(node.clone(), result)?,
|
||||
Opcode::Pusha => expand_pusha(node.clone(), result)?,
|
||||
Opcode::Popa => expand_popa(node.clone(), result)?,
|
||||
Opcode::Call => expand_call(node.clone(), result)?,
|
||||
Opcode::Return => expand_return(node.clone(), result)?,
|
||||
Opcode::Ldb | Opcode::Ldbs | Opcode::Ldh | Opcode::Ldhs | Opcode::Ldw => {
|
||||
expand_ldx(node.clone(), result)?
|
||||
}
|
||||
@@ -42,22 +46,105 @@ fn try_expand(
|
||||
fn expand_push(current: Node, nodes: &mut Vec<Node>) -> Result<(), AssembleError> {
|
||||
let label = current.label();
|
||||
let reg = expect_type!(current.arg(0).unwrap(), Register)?;
|
||||
let spr = Token::Register(Register::Spr);
|
||||
|
||||
nodes.extend(vec![
|
||||
node!(
|
||||
label,
|
||||
Opcode::SubI,
|
||||
Token::Register(Register::Spr),
|
||||
Token::Immediate(4),
|
||||
Token::Register(Register::Spr)
|
||||
),
|
||||
node!(
|
||||
None,
|
||||
Opcode::Stw,
|
||||
reg,
|
||||
Token::Register(Register::Spr),
|
||||
Token::Immediate(0)
|
||||
),
|
||||
node!(label, Opcode::SubI, spr, 4, spr),
|
||||
node!(None, Opcode::Stw, reg, spr, 0),
|
||||
]);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn expand_pusha(current: Node, nodes: &mut Vec<Node>) -> Result<(), AssembleError> {
|
||||
let label = current.label();
|
||||
let count = expect_token!(current.arg(0).unwrap(), Immediate)?;
|
||||
let spr = Token::Register(Register::Spr);
|
||||
let registers: Vec<Register> = Register::general();
|
||||
|
||||
nodes.push(node!(
|
||||
label,
|
||||
Opcode::SubI,
|
||||
spr,
|
||||
Token::Immediate(count * 4),
|
||||
spr
|
||||
));
|
||||
|
||||
nodes.extend(
|
||||
(0..count)
|
||||
.rev()
|
||||
.map(|i| {
|
||||
node!(
|
||||
None,
|
||||
Opcode::Stw,
|
||||
Token::Register(registers[i as usize]),
|
||||
spr,
|
||||
Token::Immediate(i * 4)
|
||||
)
|
||||
})
|
||||
.collect::<Vec<Node>>(),
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn expand_popa(current: Node, nodes: &mut Vec<Node>) -> Result<(), AssembleError> {
|
||||
let label = current.label();
|
||||
let count = expect_token!(current.arg(0).unwrap(), Immediate)?;
|
||||
let spr = Token::Register(Register::Spr);
|
||||
let registers: Vec<Register> = Register::general();
|
||||
|
||||
nodes.extend(
|
||||
(0..count)
|
||||
.rev()
|
||||
.map(|i| {
|
||||
node!(
|
||||
{ if i == 0 { label.clone() } else { None } },
|
||||
Opcode::Ldw,
|
||||
spr,
|
||||
Token::Register(registers[i as usize]),
|
||||
Token::Immediate(i * 4)
|
||||
)
|
||||
})
|
||||
.collect::<Vec<Node>>(),
|
||||
);
|
||||
|
||||
nodes.push(node!(
|
||||
None,
|
||||
Opcode::AddI,
|
||||
spr,
|
||||
Token::Immediate(count * 4),
|
||||
spr
|
||||
));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn expand_call(current: Node, nodes: &mut Vec<Node>) -> Result<(), AssembleError> {
|
||||
let label = current.label();
|
||||
let addr = expect_type!(current.arg(0).unwrap(), Symbol)?;
|
||||
let spr = Token::Register(Register::Spr);
|
||||
let pcx = Token::Register(Register::Pcx);
|
||||
let zero = Token::Register(Register::Zero);
|
||||
|
||||
nodes.extend(vec![
|
||||
node!(label, Opcode::SubI, spr, 4, spr),
|
||||
node!(None, Opcode::Stw, pcx, spr, 0),
|
||||
node!(None, Opcode::Jmp, addr, zero),
|
||||
]);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn expand_return(current: Node, nodes: &mut Vec<Node>) -> Result<(), AssembleError> {
|
||||
let label = current.label();
|
||||
let spr = Token::Register(Register::Spr);
|
||||
let ret = Token::Register(Register::Ret);
|
||||
|
||||
nodes.extend(vec![
|
||||
node!(label, Opcode::Ldw, spr, ret, 0),
|
||||
node!(None, Opcode::AddI, spr, 4, spr),
|
||||
node!(None, Opcode::Jmp, 4, ret),
|
||||
]);
|
||||
|
||||
Ok(())
|
||||
@@ -66,22 +153,11 @@ fn expand_push(current: Node, nodes: &mut Vec<Node>) -> Result<(), AssembleError
|
||||
fn expand_pop(current: Node, nodes: &mut Vec<Node>) -> Result<(), AssembleError> {
|
||||
let label = current.label();
|
||||
let reg = expect_type!(current.arg(0).unwrap(), Register)?;
|
||||
let spr = Token::Register(Register::Spr);
|
||||
|
||||
nodes.extend(vec![
|
||||
node!(
|
||||
label,
|
||||
Opcode::Ldw,
|
||||
Token::Register(Register::Spr),
|
||||
reg,
|
||||
Token::Immediate(0)
|
||||
),
|
||||
node!(
|
||||
None,
|
||||
Opcode::AddI,
|
||||
Token::Register(Register::Spr),
|
||||
Token::Immediate(4),
|
||||
Token::Register(Register::Spr)
|
||||
),
|
||||
node!(label, Opcode::Ldw, spr, reg, 0),
|
||||
node!(None, Opcode::AddI, spr, 4, spr),
|
||||
]);
|
||||
|
||||
Ok(())
|
||||
@@ -94,9 +170,9 @@ fn expand_ldx(current: Node, nodes: &mut Vec<Node>) -> Result<(), AssembleError>
|
||||
let offset = expect_type!(current.arg(2).unwrap(), Immediate)?;
|
||||
|
||||
nodes.extend(vec![
|
||||
node!(current.label(), Opcode::Lli, name.clone(), reg.clone()),
|
||||
node!(None, Opcode::Lui, name.clone(), reg.clone()),
|
||||
node!(None, opcode, reg.clone(), reg, offset),
|
||||
node!(current.label(), Opcode::Lli, name, reg),
|
||||
node!(None, Opcode::Lui, name, reg),
|
||||
node!(None, opcode, reg, reg, offset),
|
||||
]);
|
||||
|
||||
Ok(())
|
||||
@@ -107,20 +183,12 @@ fn expand_stx(current: Node, nodes: &mut Vec<Node>) -> Result<(), AssembleError>
|
||||
let base = expect_type!(current.arg(0).unwrap(), Register)?;
|
||||
let dest = expect_type!(current.arg(1).unwrap(), Symbol)?;
|
||||
let offset = expect_type!(current.arg(2).unwrap(), Immediate)?;
|
||||
let temp = Token::Register(Register::Rgf);
|
||||
|
||||
nodes.extend(vec![
|
||||
node!(
|
||||
current.label(),
|
||||
Opcode::Lli,
|
||||
dest.clone(),
|
||||
Token::Register(Register::Rgf)
|
||||
),
|
||||
node!(
|
||||
None,
|
||||
Opcode::Lui,
|
||||
dest.clone(),
|
||||
Token::Register(Register::Rgf)
|
||||
),
|
||||
node!(None, opcode, base, Token::Register(Register::Rgf), offset),
|
||||
node!(current.label(), Opcode::Lli, dest, temp),
|
||||
node!(None, Opcode::Lui, dest, temp),
|
||||
node!(None, opcode, base, temp, offset),
|
||||
]);
|
||||
|
||||
Ok(())
|
||||
@@ -131,8 +199,8 @@ fn expand_lwi(current: Node, nodes: &mut Vec<Node>) -> Result<(), AssembleError>
|
||||
let reg = expect_type!(current.arg(1).unwrap(), Register)?;
|
||||
|
||||
nodes.extend(vec![
|
||||
node!(current.label(), Opcode::Lli, val.clone(), reg.clone()),
|
||||
node!(None, Opcode::Lui, val.clone(), reg.clone()),
|
||||
node!(current.label(), Opcode::Lli, val, reg),
|
||||
node!(None, Opcode::Lui, val, reg),
|
||||
]);
|
||||
|
||||
Ok(())
|
||||
@@ -153,7 +221,7 @@ fn expand_resx(current: Node, nodes: &mut Vec<Node>) -> Result<(), AssembleError
|
||||
// push the inital node with the label
|
||||
for _ in 0..size.div_ceil(units_per) {
|
||||
// push the rest of the nodes
|
||||
buffer.push(node!(None, Opcode::Data, Token::Immediate(0)));
|
||||
buffer.push(node!(None, Opcode::Data, 0));
|
||||
}
|
||||
buffer[0].symbol = Some(region_label);
|
||||
nodes.extend(buffer);
|
||||
|
||||
@@ -18,7 +18,19 @@ macro_rules! node {
|
||||
};
|
||||
|
||||
($symbol: expr, $opcode: expr, $($tokens: expr),+) => {
|
||||
Node::new($symbol.clone(), $opcode.clone(), vec![$($tokens.clone()),+])
|
||||
Node::new(
|
||||
$symbol.clone(),
|
||||
$opcode.clone(),
|
||||
vec![$(node!(@convert_token $tokens)),+]
|
||||
)
|
||||
};
|
||||
|
||||
(@convert_token $token: literal) => {
|
||||
Token::Immediate($token)
|
||||
};
|
||||
|
||||
(@convert_token $token: expr) => {
|
||||
$token.clone()
|
||||
};
|
||||
}
|
||||
|
||||
@@ -36,7 +48,7 @@ impl Node {
|
||||
}
|
||||
|
||||
pub fn opcode(&self) -> Opcode {
|
||||
self.opcode.clone()
|
||||
self.opcode
|
||||
}
|
||||
|
||||
pub fn args(&self) -> Vec<Token> {
|
||||
@@ -134,11 +146,13 @@ impl fmt::Display for Opcode {
|
||||
Opcode::Push => write!(f, "push"),
|
||||
Opcode::Pop => write!(f, "pop"),
|
||||
Opcode::Lwi => write!(f, "lwi"),
|
||||
Opcode::Call => write!(f, "call"),
|
||||
Opcode::Return => write!(f, "return"),
|
||||
Opcode::Pusha => write!(f, "pusha"),
|
||||
Opcode::Popa => write!(f, "popa"),
|
||||
|
||||
// utility - removed at compile time
|
||||
// meta instructions
|
||||
Opcode::Include => write!(f, "include"),
|
||||
|
||||
// special - generated by assembler
|
||||
Opcode::Data => write!(f, "data"),
|
||||
Opcode::Segment => write!(f, "[SEGMENT]"),
|
||||
}
|
||||
@@ -200,7 +214,7 @@ impl TokenType {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum Opcode {
|
||||
// Real instructions (0x00-0x26)
|
||||
Nop,
|
||||
@@ -242,6 +256,7 @@ pub enum Opcode {
|
||||
Hlt,
|
||||
AddI,
|
||||
SubI,
|
||||
|
||||
// Pseudo-instructions
|
||||
Db,
|
||||
Dh,
|
||||
@@ -251,10 +266,14 @@ pub enum Opcode {
|
||||
Resw,
|
||||
Push,
|
||||
Pop,
|
||||
Pusha,
|
||||
Popa,
|
||||
Lwi,
|
||||
Include,
|
||||
Call,
|
||||
Return,
|
||||
|
||||
// fake instructions (these aren't present in the binary as instructions)
|
||||
// meta instructions (these aren't present in the binary as instructions)
|
||||
Include,
|
||||
Data,
|
||||
Segment,
|
||||
}
|
||||
@@ -328,6 +347,10 @@ impl FromStr for Opcode {
|
||||
"pop" => Ok(Self::Pop),
|
||||
"lwi" => Ok(Self::Lwi),
|
||||
"include" => Ok(Self::Include),
|
||||
"call" => Ok(Self::Call),
|
||||
"return" => Ok(Self::Return),
|
||||
"pusha" => Ok(Self::Push),
|
||||
"popa" => Ok(Self::Pop),
|
||||
_ => Err(OpcodeFromStrError::InvalidRegister("unknown opcode")),
|
||||
}
|
||||
}
|
||||
@@ -340,7 +363,9 @@ impl Opcode {
|
||||
"lli", "lui", "jmp", "jeq", "jne", "jgt", "jge", "jlt", "jle", "cmp", "inc",
|
||||
"dec", "shl", "shr", "add", "sub", "and", "or", "not", "xor", "nand", "nor",
|
||||
"xnor", "int", "irt", "hlt", "addi", "subi", // Pseudo-instructions
|
||||
"db", "dh", "dw", "resb", "resh", "resw", "push", "pop", "lwi", "include",
|
||||
"db", "dh", "dw", "resb", "resh", "resw", "push", "pop", "lwi", "call", "return",
|
||||
"pusha", "popa", // meta instructions
|
||||
"include",
|
||||
];
|
||||
|
||||
pub fn to_opcode_value(&self) -> Option<u8> {
|
||||
@@ -385,6 +410,7 @@ impl Opcode {
|
||||
Self::AddI => Some(0x25),
|
||||
Self::SubI => Some(0x26),
|
||||
Self::Segment => Some(0x27),
|
||||
|
||||
// Pseudo-instructions don't have opcode values
|
||||
_ => None,
|
||||
}
|
||||
|
||||
@@ -40,10 +40,12 @@ impl Default for Program {
|
||||
impl Parser {
|
||||
pub fn parse_nodes(tokens: Vec<Token>) -> Result<Vec<Node>, AssembleError> {
|
||||
let mut self_ = Parser {
|
||||
tokens: tokens.into_iter().rev().collect(),
|
||||
tokens: tokens.clone().into_iter().rev().collect(),
|
||||
nodes: vec![],
|
||||
};
|
||||
|
||||
println!("{:#?}", tokens);
|
||||
|
||||
while !self_.tokens.is_empty() {
|
||||
let ins = self_.parse_instruction()?;
|
||||
self_.nodes.push(ins);
|
||||
@@ -68,8 +70,6 @@ impl Parser {
|
||||
unreachable!();
|
||||
}
|
||||
|
||||
println!("{:?}", self.peek_next()?);
|
||||
|
||||
// check if the Node starts with a label
|
||||
let label = expect_token!(self.peek_next()?, Symbol).ok();
|
||||
if label.is_some() {
|
||||
@@ -158,14 +158,24 @@ impl Parser {
|
||||
| Opcode::Jlt
|
||||
| Opcode::Jle => {
|
||||
let imm = expect_type!(self.next()?, Immediate, Symbol)?;
|
||||
let offset = if expect_type!(self.peek_next()?, Register).is_ok() {
|
||||
self.next()?
|
||||
} else {
|
||||
Token::Register(Register::Zero)
|
||||
let offset = match self.peek_next() {
|
||||
Ok(token) => {
|
||||
if expect_type!(token, Register).is_ok() {
|
||||
self.next()?
|
||||
} else {
|
||||
Token::Register(Register::Zero)
|
||||
}
|
||||
}
|
||||
Err(_) => Token::Register(Register::Zero),
|
||||
};
|
||||
args = vec![imm, offset];
|
||||
}
|
||||
|
||||
Opcode::Call => {
|
||||
let addr = expect_type!(self.next()?, Symbol)?;
|
||||
args = vec![addr];
|
||||
}
|
||||
|
||||
// I-type instructions
|
||||
Opcode::Lui | Opcode::Lli | Opcode::Lwi => {
|
||||
let imm = expect_type!(self.next()?, Immediate, Symbol)?;
|
||||
@@ -193,18 +203,19 @@ impl Parser {
|
||||
}
|
||||
|
||||
Opcode::Db | Opcode::Dh | Opcode::Dw => {
|
||||
args = self.parse_data_definition(opcode.clone())?;
|
||||
args = self.parse_data_definition(opcode)?;
|
||||
}
|
||||
|
||||
// E-type pseudoinstructions (stack operations)
|
||||
Opcode::Push => {
|
||||
Opcode::Push | Opcode::Pop => {
|
||||
let reg = expect_type!(self.next()?, Register, Symbol)?;
|
||||
args = vec![reg];
|
||||
}
|
||||
|
||||
Opcode::Pop => {
|
||||
let reg = expect_type!(self.next()?, Register, Symbol)?;
|
||||
args = vec![reg];
|
||||
Opcode::Pusha | Opcode::Popa => {
|
||||
let count =
|
||||
expect_type!(self.next()?, Immediate).unwrap_or(Token::Immediate(8));
|
||||
args = vec![count];
|
||||
}
|
||||
|
||||
// Special instructions
|
||||
@@ -214,7 +225,7 @@ impl Parser {
|
||||
}
|
||||
|
||||
// Instructions with no arguments
|
||||
Opcode::Hlt | Opcode::Nop | Opcode::Irt => {
|
||||
Opcode::Hlt | Opcode::Nop | Opcode::Irt | Opcode::Return => {
|
||||
args = vec![];
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +1,8 @@
|
||||
use assembler::codegen::codegen;
|
||||
use assembler::expand::expand_pseudo_ops;
|
||||
use assembler::model::{Node, Opcode, Symbol, Token, TokenType};
|
||||
use assembler::parser::{Parser, Program};
|
||||
use assembler::resolver::{create_sections, resolve_dependencies, resolve_symbols};
|
||||
use common::prelude::*;
|
||||
use core::fmt;
|
||||
|
||||
pub mod assembler;
|
||||
|
||||
use crate::assembler::lexer;
|
||||
pub mod tooling;
|
||||
mod util;
|
||||
|
||||
pub mod prelude {
|
||||
pub use crate::assembler::assemble;
|
||||
|
||||
@@ -3,6 +3,12 @@ use std::{fs, io::Write, path::PathBuf};
|
||||
fn main() {
|
||||
// parse args:
|
||||
let args: Vec<String> = std::env::args().collect();
|
||||
|
||||
if args.len() == 2 && args[1] == "init" {
|
||||
assembler::tooling::project::tool_libcreate();
|
||||
std::process::exit(0);
|
||||
}
|
||||
|
||||
if args.len() != 5 || args[1] != "-i" || args[3] != "-o" {
|
||||
eprintln!("Usage: binary_name -i input_path -o output_path");
|
||||
std::process::exit(1);
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
pub mod project;
|
||||
@@ -0,0 +1,93 @@
|
||||
use crate::util::input;
|
||||
|
||||
pub fn tool_libcreate() {
|
||||
let mut ptype: String;
|
||||
loop {
|
||||
ptype = input("Enter project type (bin|lib)");
|
||||
if ptype == "bin" || ptype == "lib" {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let project_name = input("Enter project name");
|
||||
let project_path = input("Enter Directory to create project in");
|
||||
|
||||
println!("[ Creating new {ptype} project {project_name} in {project_path} ]");
|
||||
|
||||
let template = match ptype.as_str() {
|
||||
"bin" => generate_bin_template(&project_name),
|
||||
"lib" => generate_lib_template(&project_name),
|
||||
_ => panic!("Invalid project type"),
|
||||
};
|
||||
|
||||
let path = format!("{}/{}.dsa", project_path, project_name);
|
||||
std::fs::write(path, template).expect("Unable to write file");
|
||||
}
|
||||
|
||||
fn generate_lib_template(module_name: &str) -> String {
|
||||
format!(
|
||||
r#"// {module_name}.dsa
|
||||
// usage:
|
||||
//
|
||||
// include {module_name} "<relative path>"
|
||||
//
|
||||
// usage for {module_name}_main:
|
||||
// push (arg1)
|
||||
// push (arg0)
|
||||
// call {module_name}::{module_name}_main
|
||||
// pop (arg0)
|
||||
// pop (arg1)
|
||||
|
||||
// Example data declarations
|
||||
// dw example_data: 0x0000
|
||||
|
||||
// Main function template
|
||||
{module_name}_main:
|
||||
// the correct way to start a function as defined by the calling convention
|
||||
push bpr
|
||||
mov spr, bpr
|
||||
|
||||
// explanation of how to access args
|
||||
ldw bpr, rg0, 8 // arg 0
|
||||
ldw bpr, rg0, 12 // arg 1
|
||||
|
||||
// your code goes here
|
||||
// Example: load example_data into rg1
|
||||
// ldw example_data, rg1
|
||||
|
||||
// the correct way to end a function as defined by the calling convention
|
||||
mov bpr, spr
|
||||
pop bpr
|
||||
return
|
||||
"#,
|
||||
)
|
||||
}
|
||||
|
||||
fn generate_bin_template(project_name: &str) -> String {
|
||||
format!(
|
||||
r#"// {project_name}.dsa
|
||||
// Binary executable project
|
||||
|
||||
// Example Dependencies
|
||||
// include math "libs/math/math.dsa"
|
||||
include print "../resources/dsa/print.dsa"
|
||||
|
||||
// Data declarations - It is best practice to include these before any code!
|
||||
dw message: "Hello from {project_name}.dsa!" // strings are automatically null terminated!
|
||||
|
||||
// Program entry point - execution starts at the first non-definition line
|
||||
{project_name}:
|
||||
// Getting started: Calling external functions
|
||||
// Syntax: push (arg1), push (arg0), call namespace::function, pop (arg0), pop (arg1)
|
||||
|
||||
// Example: Print a string (if print library is included)
|
||||
ldw message, rg0 // load address of message
|
||||
push rg0 // push argument
|
||||
call print::print // call the print function
|
||||
pop rg0 // clean up stack
|
||||
|
||||
// Program must end with halt instruction
|
||||
halt
|
||||
"#,
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
use std::io::Write;
|
||||
|
||||
pub fn input(prompt: &str) -> String {
|
||||
print!("{}\n > ", prompt);
|
||||
std::io::stdout().flush().unwrap();
|
||||
let mut input = String::new();
|
||||
std::io::stdin().read_line(&mut input).unwrap();
|
||||
input.trim().to_string()
|
||||
}
|
||||
Reference in New Issue
Block a user