misc: fix merge conflict
This commit is contained in:
Generated
+16
@@ -659,6 +659,12 @@ dependencies = [
|
|||||||
"unicode-width",
|
"unicode-width",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "colorful"
|
||||||
|
version = "0.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ffb474a9c3219a8254ead020421ecf1b90427f29b55f6aae9a2471fa62c126ef"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "combine"
|
name = "combine"
|
||||||
version = "4.6.7"
|
version = "4.6.7"
|
||||||
@@ -903,6 +909,16 @@ version = "0.1.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76"
|
checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dsa_editor"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"colorful",
|
||||||
|
"eframe",
|
||||||
|
"egui",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ecolor"
|
name = "ecolor"
|
||||||
version = "0.31.1"
|
version = "0.31.1"
|
||||||
|
|||||||
+27
-20
@@ -1,20 +1,12 @@
|
|||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use crate::{AssembleError, parser::Opcode};
|
use crate::{
|
||||||
|
AssembleError,
|
||||||
|
model::{Module, Opcode, Symbol, Token},
|
||||||
|
};
|
||||||
use common::prelude::Register;
|
use common::prelude::Register;
|
||||||
|
|
||||||
pub type Symbol = String;
|
pub fn lexer(mut program: String, module: u64) -> Result<Vec<Token>, AssembleError> {
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub enum Token {
|
|
||||||
Symbol(Symbol),
|
|
||||||
Register(Register),
|
|
||||||
Immediate(u32),
|
|
||||||
StringLit(String),
|
|
||||||
Opcode(Opcode),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn lexer(mut program: String) -> Result<Vec<Token>, AssembleError> {
|
|
||||||
let mut tokens = Vec::new();
|
let mut tokens = Vec::new();
|
||||||
|
|
||||||
program = program.replace(",", "");
|
program = program.replace(",", "");
|
||||||
@@ -30,7 +22,6 @@ pub fn lexer(mut program: String) -> Result<Vec<Token>, AssembleError> {
|
|||||||
|
|
||||||
if let Some(stripped) = token.strip_prefix('"') {
|
if let Some(stripped) = token.strip_prefix('"') {
|
||||||
literal.push_str(stripped);
|
literal.push_str(stripped);
|
||||||
println!("literal: {literal}");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if !literal.is_empty() {
|
if !literal.is_empty() {
|
||||||
@@ -61,9 +52,9 @@ pub fn lexer(mut program: String) -> Result<Vec<Token>, AssembleError> {
|
|||||||
tokens.push(token);
|
tokens.push(token);
|
||||||
} else if let Some(token) = parse_decimal(token)? {
|
} else if let Some(token) = parse_decimal(token)? {
|
||||||
tokens.push(token);
|
tokens.push(token);
|
||||||
} else if let Some(token) = parse_label(token)? {
|
} else if let Some(token) = parse_label(token, module)? {
|
||||||
tokens.push(token);
|
tokens.push(token);
|
||||||
} else if let Some(token) = parse_symbol(token)? {
|
} else if let Some(token) = parse_symbol(token, module)? {
|
||||||
tokens.push(token);
|
tokens.push(token);
|
||||||
} else {
|
} else {
|
||||||
return Err(AssembleError::Generic);
|
return Err(AssembleError::Generic);
|
||||||
@@ -126,18 +117,34 @@ pub fn parse_decimal(token: &str) -> Result<Option<Token>, AssembleError> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_label(token: &str) -> Result<Option<Token>, AssembleError> {
|
pub fn parse_label(token: &str, module: u64) -> Result<Option<Token>, AssembleError> {
|
||||||
if !token.ends_with(":") {
|
if !token.ends_with(":") {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
} else {
|
} else {
|
||||||
Ok(Some(Token::Symbol(token[0..token.len() - 1].to_string())))
|
Ok(Some(Token::Symbol(Symbol {
|
||||||
|
name: token[0..token.len() - 1].to_string(),
|
||||||
|
module: Module::Resolved(module),
|
||||||
|
})))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_symbol(token: &str) -> Result<Option<Token>, AssembleError> {
|
pub fn parse_symbol(token: &str, module: u64) -> Result<Option<Token>, AssembleError> {
|
||||||
if token.chars().next().unwrap().is_numeric() {
|
if token.chars().next().unwrap().is_numeric() {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Some(Token::Symbol(token.to_string())))
|
let mut split = token.splitn(2, "::");
|
||||||
|
let symbol1 = split.next().unwrap().to_string();
|
||||||
|
|
||||||
|
if let Some(symbol2) = split.next() {
|
||||||
|
Ok(Some(Token::Symbol(Symbol {
|
||||||
|
name: symbol2.to_string(),
|
||||||
|
module: Module::Unresolved(symbol1),
|
||||||
|
})))
|
||||||
|
} else {
|
||||||
|
Ok(Some(Token::Symbol(Symbol {
|
||||||
|
name: symbol1,
|
||||||
|
module: Module::Resolved(module),
|
||||||
|
})))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+92
-3
@@ -1,14 +1,91 @@
|
|||||||
use core::fmt;
|
use core::fmt;
|
||||||
|
use std::{
|
||||||
|
collections::HashSet,
|
||||||
|
fs,
|
||||||
|
hash::{DefaultHasher, Hash, Hasher},
|
||||||
|
path::PathBuf,
|
||||||
|
};
|
||||||
|
|
||||||
use common::prelude::Instruction;
|
use common::prelude::Instruction;
|
||||||
|
|
||||||
use crate::{lexer::Token, parser::TokenType};
|
use crate::{
|
||||||
|
model::{Node, Token, TokenType},
|
||||||
|
parser::{Parser, Program},
|
||||||
|
};
|
||||||
|
|
||||||
pub mod lexer;
|
pub mod lexer;
|
||||||
|
pub mod model;
|
||||||
pub mod parser;
|
pub mod parser;
|
||||||
|
|
||||||
pub fn assemble(_src: &str) -> Vec<Instruction> {
|
pub fn assemble(src: &PathBuf) -> Vec<Instruction> {
|
||||||
todo!()
|
let mut modules = HashSet::<u64>::new();
|
||||||
|
let mut program = Program::new();
|
||||||
|
|
||||||
|
let hash = quick_hash(src);
|
||||||
|
modules.insert(hash);
|
||||||
|
|
||||||
|
match prepare_dependency(src.clone(), &mut modules, &mut program) {
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(err) => println!("BIG ERROR {:?}", err),
|
||||||
|
}
|
||||||
|
|
||||||
|
for node in program.nodes {
|
||||||
|
println!("{:?}", node);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec![]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn prepare_dependency(
|
||||||
|
path: PathBuf,
|
||||||
|
modules: &mut HashSet<u64>,
|
||||||
|
program: &mut Program,
|
||||||
|
) -> Result<(), AssembleError> {
|
||||||
|
let filename = path.file_name().unwrap().to_str().unwrap();
|
||||||
|
if let Ok(path) = path.canonicalize() {
|
||||||
|
log(&format!(
|
||||||
|
"{:20} {:20} [{}]",
|
||||||
|
"Building",
|
||||||
|
filename,
|
||||||
|
path.display()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let src = fs::read_to_string(&path)
|
||||||
|
.map_err(|_| AssembleError::InvalidFile(path.clone()))?;
|
||||||
|
let file_hash = quick_hash(&path);
|
||||||
|
|
||||||
|
log(&format!("{:20} {:20}", "Tokenising", filename));
|
||||||
|
let tokens = lexer::lexer(src, file_hash)?;
|
||||||
|
|
||||||
|
log(&format!("{:20} {:20}", "Parsing", filename));
|
||||||
|
let mut parser = Parser::new(tokens);
|
||||||
|
|
||||||
|
log(&format!("{:20} {:20}", "Resolving Deps", filename));
|
||||||
|
let deps = parser
|
||||||
|
.parse_nodes()?
|
||||||
|
.resolve_dependencies()?
|
||||||
|
.get_dependencies()?;
|
||||||
|
program.add_module(parser.get());
|
||||||
|
|
||||||
|
for dep in deps {
|
||||||
|
log(&format!(
|
||||||
|
"{:20} {:20}",
|
||||||
|
"Including",
|
||||||
|
dep.file_name().unwrap().to_str().unwrap()
|
||||||
|
));
|
||||||
|
|
||||||
|
if !modules.contains(&quick_hash(&dep)) {
|
||||||
|
modules.insert(quick_hash(&dep));
|
||||||
|
prepare_dependency(dep, modules, program)?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build(src: Vec<Node>) -> Result<Vec<Instruction>, AssembleError> {
|
||||||
|
Ok(vec![])
|
||||||
}
|
}
|
||||||
|
|
||||||
/// TODO: disassembling functionality
|
/// TODO: disassembling functionality
|
||||||
@@ -23,6 +100,7 @@ pub fn disassemble(_: Vec<Instruction>) -> String {
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum AssembleError {
|
pub enum AssembleError {
|
||||||
Generic,
|
Generic,
|
||||||
|
InvalidFile(PathBuf),
|
||||||
UnexpectedToken(Token, TokenType),
|
UnexpectedToken(Token, TokenType),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,6 +111,17 @@ impl fmt::Display for AssembleError {
|
|||||||
AssembleError::UnexpectedToken(tok, expected) => {
|
AssembleError::UnexpectedToken(tok, expected) => {
|
||||||
write!(f, "Unexpected token {tok:?}, expected {expected:?}")
|
write!(f, "Unexpected token {tok:?}, expected {expected:?}")
|
||||||
}
|
}
|
||||||
|
AssembleError::InvalidFile(path) => write!(f, "Invalid file {path:?}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn quick_hash(value: &PathBuf) -> u64 {
|
||||||
|
let mut hasher = DefaultHasher::new();
|
||||||
|
value.canonicalize().unwrap().to_str().hash(&mut hasher);
|
||||||
|
hasher.finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn log(message: &str) {
|
||||||
|
println!("\x1b[32mINFO:\x1b[0m {}", message);
|
||||||
|
}
|
||||||
|
|||||||
+19
-10
@@ -1,16 +1,25 @@
|
|||||||
use std::fs;
|
use std::{fs, io::Write, path::PathBuf};
|
||||||
|
|
||||||
use assembler::{lexer, parser::Parser};
|
use assembler::{lexer, parser::Parser};
|
||||||
|
use common::prelude::{ITypeArgs, Instruction, RTypeArgs, Register};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let program = fs::read_to_string("../resources/dsa/print.dsa").unwrap();
|
// parse args:
|
||||||
let tokens = lexer::lexer(program).unwrap();
|
let args: Vec<String> = std::env::args().collect();
|
||||||
|
if args.len() != 5 || args[1] != "-i" || args[3] != "-o" {
|
||||||
println!("{tokens:?}");
|
eprintln!("Usage: binary_name -i input_path -o output_path");
|
||||||
|
std::process::exit(1);
|
||||||
let parser = Parser::new(tokens);
|
|
||||||
|
|
||||||
for node in parser {
|
|
||||||
println!("{node:?}");
|
|
||||||
}
|
}
|
||||||
|
let input_path = &args[2];
|
||||||
|
let output_path = &args[4];
|
||||||
|
|
||||||
|
let src = PathBuf::from(input_path);
|
||||||
|
let mut output_file = fs::File::create(output_path).unwrap();
|
||||||
|
|
||||||
|
let res = assembler::assemble(&src)
|
||||||
|
.iter()
|
||||||
|
.map(|i| i.encode())
|
||||||
|
.for_each(|i| {
|
||||||
|
output_file.write_all(&i.to_be_bytes()).unwrap();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,282 @@
|
|||||||
|
use std::{fmt, str::FromStr};
|
||||||
|
|
||||||
|
use common::prelude::Register;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
#[expect(dead_code)]
|
||||||
|
pub struct Node(pub Option<Symbol>, pub Opcode, pub Vec<Token>);
|
||||||
|
|
||||||
|
impl fmt::Display for Node {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
let symbol = match &self.0 {
|
||||||
|
Some(symbol) => format!("{}", symbol),
|
||||||
|
None => "".to_string(),
|
||||||
|
};
|
||||||
|
|
||||||
|
write!(f, "Node: {} {} {:?}", symbol, self.1, self.2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Symbol {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "{}::{}", self.module, self.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Module {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Module::Unresolved(name) => write!(f, "{}", name),
|
||||||
|
Module::Resolved(name) => write!(f, "{}", name),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Opcode {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "{}", self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Symbol {
|
||||||
|
pub name: String,
|
||||||
|
pub module: Module,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum Module {
|
||||||
|
Resolved(u64),
|
||||||
|
Unresolved(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum Token {
|
||||||
|
Symbol(Symbol),
|
||||||
|
Register(Register),
|
||||||
|
Immediate(u32),
|
||||||
|
StringLit(String),
|
||||||
|
Opcode(Opcode),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Copy, Clone)]
|
||||||
|
pub enum TokenType {
|
||||||
|
Symbol,
|
||||||
|
Register,
|
||||||
|
Immediate,
|
||||||
|
StringLit,
|
||||||
|
Opcode,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TokenType {
|
||||||
|
pub fn from_token(token: &Token) -> TokenType {
|
||||||
|
match token {
|
||||||
|
Token::Symbol(_) => TokenType::Symbol,
|
||||||
|
Token::Register(_) => TokenType::Register,
|
||||||
|
Token::Immediate(_) => TokenType::Immediate,
|
||||||
|
Token::StringLit(_) => TokenType::StringLit,
|
||||||
|
Token::Opcode(_) => TokenType::Opcode,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub enum Opcode {
|
||||||
|
// Real instructions (0x00-0x26)
|
||||||
|
Nop,
|
||||||
|
Mov,
|
||||||
|
Movs,
|
||||||
|
Ldb,
|
||||||
|
Ldbs,
|
||||||
|
Ldh,
|
||||||
|
Ldhs,
|
||||||
|
Ldw,
|
||||||
|
Stb,
|
||||||
|
Sth,
|
||||||
|
Stw,
|
||||||
|
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,
|
||||||
|
Iadd,
|
||||||
|
Isub,
|
||||||
|
// Pseudo-instructions
|
||||||
|
Db,
|
||||||
|
Dh,
|
||||||
|
Dw,
|
||||||
|
Resb,
|
||||||
|
Resh,
|
||||||
|
Resw,
|
||||||
|
Push,
|
||||||
|
Pop,
|
||||||
|
Lwi,
|
||||||
|
Include,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum OpcodeFromStrError {
|
||||||
|
InvalidRegister(&'static str),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for OpcodeFromStrError {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::InvalidRegister(reg) => write!(f, "register does not exist: {reg}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::error::Error for OpcodeFromStrError {}
|
||||||
|
|
||||||
|
impl FromStr for Opcode {
|
||||||
|
type Err = OpcodeFromStrError;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
match s.to_lowercase().as_str() {
|
||||||
|
"nop" => Ok(Self::Nop),
|
||||||
|
"mov" => Ok(Self::Mov),
|
||||||
|
"movs" => Ok(Self::Movs),
|
||||||
|
"ldb" => Ok(Self::Ldb),
|
||||||
|
"ldbs" => Ok(Self::Ldbs),
|
||||||
|
"ldh" => Ok(Self::Ldh),
|
||||||
|
"ldhs" => Ok(Self::Ldhs),
|
||||||
|
"ldw" => Ok(Self::Ldw),
|
||||||
|
"stb" => Ok(Self::Stb),
|
||||||
|
"sth" => Ok(Self::Sth),
|
||||||
|
"stw" => Ok(Self::Stw),
|
||||||
|
"lli" => Ok(Self::Lli),
|
||||||
|
"lui" => Ok(Self::Lui),
|
||||||
|
"jmp" => Ok(Self::Jmp),
|
||||||
|
"jeq" => Ok(Self::Jeq),
|
||||||
|
"jne" => Ok(Self::Jne),
|
||||||
|
"jgt" => Ok(Self::Jgt),
|
||||||
|
"jge" => Ok(Self::Jge),
|
||||||
|
"jlt" => Ok(Self::Jlt),
|
||||||
|
"jle" => Ok(Self::Jle),
|
||||||
|
"cmp" => Ok(Self::Cmp),
|
||||||
|
"inc" => Ok(Self::Inc),
|
||||||
|
"dec" => Ok(Self::Dec),
|
||||||
|
"shl" => Ok(Self::Shl),
|
||||||
|
"shr" => Ok(Self::Shr),
|
||||||
|
"add" => Ok(Self::Add),
|
||||||
|
"sub" => Ok(Self::Sub),
|
||||||
|
"and" => Ok(Self::And),
|
||||||
|
"or" => Ok(Self::Or),
|
||||||
|
"not" => Ok(Self::Not),
|
||||||
|
"xor" => Ok(Self::Xor),
|
||||||
|
"nand" => Ok(Self::Nand),
|
||||||
|
"nor" => Ok(Self::Nor),
|
||||||
|
"xnor" => Ok(Self::Xnor),
|
||||||
|
"int" => Ok(Self::Int),
|
||||||
|
"irt" => Ok(Self::Irt),
|
||||||
|
"hlt" => Ok(Self::Hlt),
|
||||||
|
"iadd" => Ok(Self::Iadd),
|
||||||
|
"isub" => Ok(Self::Isub),
|
||||||
|
"db" => Ok(Self::Db),
|
||||||
|
"dh" => Ok(Self::Dh),
|
||||||
|
"dw" => Ok(Self::Dw),
|
||||||
|
"resb" => Ok(Self::Resb),
|
||||||
|
"resh" => Ok(Self::Resh),
|
||||||
|
"resw" => Ok(Self::Resw),
|
||||||
|
"push" => Ok(Self::Push),
|
||||||
|
"pop" => Ok(Self::Pop),
|
||||||
|
"lwi" => Ok(Self::Lwi),
|
||||||
|
"include" => Ok(Self::Include),
|
||||||
|
_ => Err(OpcodeFromStrError::InvalidRegister("unknown opcode")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Opcode {
|
||||||
|
pub const OPCODES: &[&str] = &[
|
||||||
|
// Real instructions (0x00-0x26)
|
||||||
|
"nop", "mov", "movs", "ldb", "ldbs", "ldh", "ldhs", "ldw", "stb", "sth", "stw",
|
||||||
|
"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", "iadd", "isub", // Pseudo-instructions
|
||||||
|
"db", "dh", "dw", "resb", "resh", "resw", "push", "pop", "lwi", "include",
|
||||||
|
];
|
||||||
|
|
||||||
|
pub fn to_opcode_value(&self) -> Option<u8> {
|
||||||
|
match self {
|
||||||
|
Self::Nop => Some(0x00),
|
||||||
|
Self::Mov => Some(0x01),
|
||||||
|
Self::Movs => Some(0x02),
|
||||||
|
Self::Ldb => Some(0x03),
|
||||||
|
Self::Ldbs => Some(0x04),
|
||||||
|
Self::Ldh => Some(0x05),
|
||||||
|
Self::Ldhs => Some(0x06),
|
||||||
|
Self::Ldw => Some(0x07),
|
||||||
|
Self::Stb => Some(0x08),
|
||||||
|
Self::Sth => Some(0x09),
|
||||||
|
Self::Stw => Some(0x0A),
|
||||||
|
Self::Lli => Some(0x0B),
|
||||||
|
Self::Lui => Some(0x0C),
|
||||||
|
Self::Jmp => Some(0x0D),
|
||||||
|
Self::Jeq => Some(0x0E),
|
||||||
|
Self::Jne => Some(0x0F),
|
||||||
|
Self::Jgt => Some(0x10),
|
||||||
|
Self::Jge => Some(0x11),
|
||||||
|
Self::Jlt => Some(0x12),
|
||||||
|
Self::Jle => Some(0x13),
|
||||||
|
Self::Cmp => Some(0x14),
|
||||||
|
Self::Inc => Some(0x15),
|
||||||
|
Self::Dec => Some(0x16),
|
||||||
|
Self::Shl => Some(0x17),
|
||||||
|
Self::Shr => Some(0x18),
|
||||||
|
Self::Add => Some(0x19),
|
||||||
|
Self::Sub => Some(0x1A),
|
||||||
|
Self::And => Some(0x1B),
|
||||||
|
Self::Or => Some(0x1C),
|
||||||
|
Self::Not => Some(0x1D),
|
||||||
|
Self::Xor => Some(0x1E),
|
||||||
|
Self::Nand => Some(0x1F),
|
||||||
|
Self::Nor => Some(0x20),
|
||||||
|
Self::Xnor => Some(0x21),
|
||||||
|
Self::Int => Some(0x22),
|
||||||
|
Self::Irt => Some(0x23),
|
||||||
|
Self::Hlt => Some(0x24),
|
||||||
|
Self::Iadd => Some(0x25),
|
||||||
|
Self::Isub => Some(0x26),
|
||||||
|
// Pseudo-instructions don't have opcode values
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_pseudo_instruction(&self) -> bool {
|
||||||
|
matches!(
|
||||||
|
self,
|
||||||
|
Self::Db
|
||||||
|
| Self::Dh
|
||||||
|
| Self::Dw
|
||||||
|
| Self::Resb
|
||||||
|
| Self::Resh
|
||||||
|
| Self::Resw
|
||||||
|
| Self::Push
|
||||||
|
| Self::Pop
|
||||||
|
| Self::Lwi
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
+144
-256
@@ -1,73 +1,35 @@
|
|||||||
|
use core::fmt;
|
||||||
|
use std::path::PathBuf;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use crate::AssembleError;
|
use common::prelude::{Instruction, Register};
|
||||||
use crate::lexer::{Symbol, Token};
|
|
||||||
|
use crate::model::{Module, Node, Opcode, Symbol, Token, TokenType};
|
||||||
|
use crate::{AssembleError, quick_hash};
|
||||||
|
|
||||||
pub struct Parser {
|
pub struct Parser {
|
||||||
tokens: Vec<Token>,
|
tokens: Vec<Token>,
|
||||||
|
nodes: Vec<Node>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Copy, Clone)]
|
pub struct Program {
|
||||||
pub enum TokenType {
|
pub nodes: Vec<Node>,
|
||||||
Symbol,
|
|
||||||
Register,
|
|
||||||
Immediate,
|
|
||||||
StringLit,
|
|
||||||
Opcode,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TokenType {
|
impl Program {
|
||||||
fn from_token(token: &Token) -> TokenType {
|
pub fn new() -> Program {
|
||||||
match token {
|
Program { nodes: vec![] }
|
||||||
Token::Symbol(_) => TokenType::Symbol,
|
|
||||||
Token::Register(_) => TokenType::Register,
|
|
||||||
Token::Immediate(_) => TokenType::Immediate,
|
|
||||||
Token::StringLit(_) => TokenType::StringLit,
|
|
||||||
Token::Opcode(_) => TokenType::Opcode,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: MAKE SURE I DO THE BIT SHIFT FOR LUI CODEGEN
|
pub fn add_module(&mut self, module: Vec<Node>) {
|
||||||
// TODO: MAKE SURE I DO THE BIT SHIFT FOR LUI CODEGEN
|
self.nodes.extend(module);
|
||||||
// TODO: MAKE SURE I DO THE BIT SHIFT FOR LUI CODEGEN
|
}
|
||||||
// TODO: MAKE SURE I DO THE BIT SHIFT FOR LUI CODEGEN
|
|
||||||
// TODO: MAKE SURE I DO THE BIT SHIFT FOR LUI CODEGEN
|
|
||||||
// TODO: MAKE SURE I DO THE BIT SHIFT FOR LUI CODEGEN
|
|
||||||
// TODO: MAKE SURE I DO THE BIT SHIFT FOR LUI CODEGEN
|
|
||||||
// TODO: MAKE SURE I DO THE BIT SHIFT FOR LUI CODEGEN
|
|
||||||
// TODO: MAKE SURE I DO THE BIT SHIFT FOR LUI CODEGEN
|
|
||||||
// TODO: MAKE SURE I DO THE BIT SHIFT FOR LUI CODEGEN
|
|
||||||
// TODO: MAKE SURE I DO THE BIT SHIFT FOR LUI CODEGEN
|
|
||||||
// TODO: MAKE SURE I DO THE BIT SHIFT FOR LUI CODEGEN
|
|
||||||
// TODO: MAKE SURE I DO THE BIT SHIFT FOR LUI CODEGEN
|
|
||||||
// TODO: MAKE SURE I DO THE BIT SHIFT FOR LUI CODEGEN
|
|
||||||
// TODO: MAKE SURE I DO THE BIT SHIFT FOR LUI CODEGEN
|
|
||||||
// TODO: MAKE SURE I DO THE BIT SHIFT FOR LUI CODEGEN
|
|
||||||
// TODO: MAKE SURE I DO THE BIT SHIFT FOR LUI CODEGEN
|
|
||||||
// TODO: MAKE SURE I DO THE BIT SHIFT FOR LUI CODEGEN
|
|
||||||
// TODO: MAKE SURE I DO THE BIT SHIFT FOR LUI CODEGEN
|
|
||||||
// TODO: MAKE SURE I DO THE BIT SHIFT FOR LUI CODEGEN
|
|
||||||
// TODO: MAKE SURE I DO THE BIT SHIFT FOR LUI CODEGEN
|
|
||||||
// TODO: MAKE SURE I DO THE BIT SHIFT FOR LUI CODEGEN
|
|
||||||
// TODO: MAKE SURE I DO THE BIT SHIFT FOR LUI CODEGEN
|
|
||||||
// TODO: MAKE SURE I DO THE BIT SHIFT FOR LUI CODEGEN
|
|
||||||
// TODO: MAKE SURE I DO THE BIT SHIFT FOR LUI CODEGEN
|
|
||||||
// TODO: MAKE SURE I DO THE BIT SHIFT FOR LUI CODEGEN
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
pub fn parser(&mut self) -> Parser {
|
||||||
#[expect(dead_code)]
|
Parser {
|
||||||
pub struct Node(Option<Symbol>, Opcode, Vec<Token>);
|
tokens: vec![],
|
||||||
|
nodes: self.nodes.clone(),
|
||||||
impl Iterator for Parser {
|
|
||||||
type Item = Result<Node, AssembleError>;
|
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Result<Node, AssembleError>> {
|
|
||||||
if self.tokens.is_empty() {
|
|
||||||
return None;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(self.parse_instruction())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,9 +37,128 @@ impl Parser {
|
|||||||
pub fn new(tokens: Vec<Token>) -> Parser {
|
pub fn new(tokens: Vec<Token>) -> Parser {
|
||||||
Parser {
|
Parser {
|
||||||
tokens: tokens.into_iter().rev().collect(),
|
tokens: tokens.into_iter().rev().collect(),
|
||||||
|
nodes: vec![],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn parse_nodes(&mut self) -> Result<&mut Self, AssembleError> {
|
||||||
|
while !self.tokens.is_empty() {
|
||||||
|
let ins = self.parse_instruction()?;
|
||||||
|
self.nodes.push(ins);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_dependencies(&mut self) -> Result<Vec<PathBuf>, AssembleError> {
|
||||||
|
let mut dependencies = Vec::new();
|
||||||
|
for node in &self.nodes {
|
||||||
|
if let Opcode::Include = node.1 {
|
||||||
|
if let Token::StringLit(path) = node.2.get(1).unwrap() {
|
||||||
|
dependencies.push(PathBuf::from(path));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(dependencies)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolve_dependencies(&mut self) -> Result<&mut Self, AssembleError> {
|
||||||
|
// first we get a list of imports
|
||||||
|
let mut dependencies = Vec::new();
|
||||||
|
for node in &self.nodes {
|
||||||
|
if let Opcode::Include = node.1 {
|
||||||
|
// we want the path, and the name
|
||||||
|
let name = if let Token::Symbol(name) = node.2.get(0).unwrap() {
|
||||||
|
name.name.clone()
|
||||||
|
} else {
|
||||||
|
unreachable!()
|
||||||
|
}; //node.2.get(0).unwrap()
|
||||||
|
let path = if let Token::StringLit(path) = node.2.get(1).unwrap() {
|
||||||
|
path
|
||||||
|
} else {
|
||||||
|
unreachable!()
|
||||||
|
};
|
||||||
|
let hash = quick_hash(&PathBuf::from(path).canonicalize().unwrap());
|
||||||
|
|
||||||
|
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 self.nodes.clone().iter().enumerate() {
|
||||||
|
let Node(_, _, 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 {
|
||||||
|
self.nodes[i as usize].2[j as usize] = Token::Symbol(symbol);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(&self) -> Vec<Node> {
|
||||||
|
self.nodes.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn expand_pseudo_ops(&mut self) -> Result<&mut Self, AssembleError> {
|
||||||
|
for node in self.nodes.iter_mut() {
|
||||||
|
match node.1 {
|
||||||
|
Opcode::Db | Opcode::Dh | Opcode::Dw => todo!(),
|
||||||
|
Opcode::Resb | Opcode::Resh | Opcode::Resw => todo!(),
|
||||||
|
|
||||||
|
Opcode::Push => {
|
||||||
|
// inc SPR
|
||||||
|
// STW reg, SPR
|
||||||
|
let label = node.0.clone();
|
||||||
|
let reg = node.2.get(0).unwrap();
|
||||||
|
|
||||||
|
vec![
|
||||||
|
Node(
|
||||||
|
label.clone(),
|
||||||
|
Opcode::Inc,
|
||||||
|
vec![Token::Register(Register::Spr)],
|
||||||
|
),
|
||||||
|
Node(
|
||||||
|
label.clone(),
|
||||||
|
Opcode::Stw,
|
||||||
|
vec![reg.clone(), Token::Register(Register::Spr)],
|
||||||
|
),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
_ => continue,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolve_symbols(&mut self) -> Result<&mut Self, AssembleError> {
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn instructions(&mut self) -> Vec<Instruction> {
|
||||||
|
vec![]
|
||||||
|
}
|
||||||
|
|
||||||
fn parse_instruction(&mut self) -> Result<Node, AssembleError> {
|
fn parse_instruction(&mut self) -> Result<Node, AssembleError> {
|
||||||
if self.tokens.is_empty() {
|
if self.tokens.is_empty() {
|
||||||
unreachable!();
|
unreachable!();
|
||||||
@@ -170,6 +251,12 @@ impl Parser {
|
|||||||
args = vec![reg];
|
args = vec![reg];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Opcode::Include => {
|
||||||
|
let mod_name = self.expect(TokenType::Symbol)?;
|
||||||
|
let path = self.expect(TokenType::StringLit)?;
|
||||||
|
args = vec![mod_name, path];
|
||||||
|
}
|
||||||
|
|
||||||
// J-type instructions
|
// J-type instructions
|
||||||
Opcode::Jmp
|
Opcode::Jmp
|
||||||
| Opcode::Jeq
|
| Opcode::Jeq
|
||||||
@@ -312,202 +399,3 @@ impl Parser {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
|
||||||
pub enum Opcode {
|
|
||||||
// Real instructions (0x00-0x26)
|
|
||||||
Nop,
|
|
||||||
Mov,
|
|
||||||
Movs,
|
|
||||||
Ldb,
|
|
||||||
Ldbs,
|
|
||||||
Ldh,
|
|
||||||
Ldhs,
|
|
||||||
Ldw,
|
|
||||||
Stb,
|
|
||||||
Sth,
|
|
||||||
Stw,
|
|
||||||
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,
|
|
||||||
Iadd,
|
|
||||||
Isub,
|
|
||||||
// Pseudo-instructions
|
|
||||||
Db,
|
|
||||||
Dh,
|
|
||||||
Dw,
|
|
||||||
Resb,
|
|
||||||
Resh,
|
|
||||||
Resw,
|
|
||||||
Push,
|
|
||||||
Pop,
|
|
||||||
Lwi,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum OpcodeFromStrError {
|
|
||||||
InvalidRegister(&'static str),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::fmt::Display for OpcodeFromStrError {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
match self {
|
|
||||||
Self::InvalidRegister(reg) => write!(f, "register does not exist: {reg}"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::error::Error for OpcodeFromStrError {}
|
|
||||||
|
|
||||||
impl FromStr for Opcode {
|
|
||||||
type Err = OpcodeFromStrError;
|
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
||||||
match s.to_lowercase().as_str() {
|
|
||||||
"nop" => Ok(Self::Nop),
|
|
||||||
"mov" => Ok(Self::Mov),
|
|
||||||
"movs" => Ok(Self::Movs),
|
|
||||||
"ldb" => Ok(Self::Ldb),
|
|
||||||
"ldbs" => Ok(Self::Ldbs),
|
|
||||||
"ldh" => Ok(Self::Ldh),
|
|
||||||
"ldhs" => Ok(Self::Ldhs),
|
|
||||||
"ldw" => Ok(Self::Ldw),
|
|
||||||
"stb" => Ok(Self::Stb),
|
|
||||||
"sth" => Ok(Self::Sth),
|
|
||||||
"stw" => Ok(Self::Stw),
|
|
||||||
"lli" => Ok(Self::Lli),
|
|
||||||
"lui" => Ok(Self::Lui),
|
|
||||||
"jmp" => Ok(Self::Jmp),
|
|
||||||
"jeq" => Ok(Self::Jeq),
|
|
||||||
"jne" => Ok(Self::Jne),
|
|
||||||
"jgt" => Ok(Self::Jgt),
|
|
||||||
"jge" => Ok(Self::Jge),
|
|
||||||
"jlt" => Ok(Self::Jlt),
|
|
||||||
"jle" => Ok(Self::Jle),
|
|
||||||
"cmp" => Ok(Self::Cmp),
|
|
||||||
"inc" => Ok(Self::Inc),
|
|
||||||
"dec" => Ok(Self::Dec),
|
|
||||||
"shl" => Ok(Self::Shl),
|
|
||||||
"shr" => Ok(Self::Shr),
|
|
||||||
"add" => Ok(Self::Add),
|
|
||||||
"sub" => Ok(Self::Sub),
|
|
||||||
"and" => Ok(Self::And),
|
|
||||||
"or" => Ok(Self::Or),
|
|
||||||
"not" => Ok(Self::Not),
|
|
||||||
"xor" => Ok(Self::Xor),
|
|
||||||
"nand" => Ok(Self::Nand),
|
|
||||||
"nor" => Ok(Self::Nor),
|
|
||||||
"xnor" => Ok(Self::Xnor),
|
|
||||||
"int" => Ok(Self::Int),
|
|
||||||
"irt" => Ok(Self::Irt),
|
|
||||||
"hlt" => Ok(Self::Hlt),
|
|
||||||
"iadd" => Ok(Self::Iadd),
|
|
||||||
"isub" => Ok(Self::Isub),
|
|
||||||
"db" => Ok(Self::Db),
|
|
||||||
"dh" => Ok(Self::Dh),
|
|
||||||
"dw" => Ok(Self::Dw),
|
|
||||||
"resb" => Ok(Self::Resb),
|
|
||||||
"resh" => Ok(Self::Resh),
|
|
||||||
"resw" => Ok(Self::Resw),
|
|
||||||
"push" => Ok(Self::Push),
|
|
||||||
"pop" => Ok(Self::Pop),
|
|
||||||
"lwi" => Ok(Self::Lwi),
|
|
||||||
_ => Err(OpcodeFromStrError::InvalidRegister("unknown opcode")),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Opcode {
|
|
||||||
pub const OPCODES: &[&str] = &[
|
|
||||||
// Real instructions (0x00-0x26)
|
|
||||||
"nop", "mov", "movs", "ldb", "ldbs", "ldh", "ldhs", "ldw", "stb", "sth", "stw",
|
|
||||||
"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", "iadd", "isub", // Pseudo-instructions
|
|
||||||
"db", "dh", "dw", "resb", "resh", "resw", "push", "pop", "lwi",
|
|
||||||
];
|
|
||||||
|
|
||||||
pub fn to_opcode_value(&self) -> Option<u8> {
|
|
||||||
match self {
|
|
||||||
Self::Nop => Some(0x00),
|
|
||||||
Self::Mov => Some(0x01),
|
|
||||||
Self::Movs => Some(0x02),
|
|
||||||
Self::Ldb => Some(0x03),
|
|
||||||
Self::Ldbs => Some(0x04),
|
|
||||||
Self::Ldh => Some(0x05),
|
|
||||||
Self::Ldhs => Some(0x06),
|
|
||||||
Self::Ldw => Some(0x07),
|
|
||||||
Self::Stb => Some(0x08),
|
|
||||||
Self::Sth => Some(0x09),
|
|
||||||
Self::Stw => Some(0x0A),
|
|
||||||
Self::Lli => Some(0x0B),
|
|
||||||
Self::Lui => Some(0x0C),
|
|
||||||
Self::Jmp => Some(0x0D),
|
|
||||||
Self::Jeq => Some(0x0E),
|
|
||||||
Self::Jne => Some(0x0F),
|
|
||||||
Self::Jgt => Some(0x10),
|
|
||||||
Self::Jge => Some(0x11),
|
|
||||||
Self::Jlt => Some(0x12),
|
|
||||||
Self::Jle => Some(0x13),
|
|
||||||
Self::Cmp => Some(0x14),
|
|
||||||
Self::Inc => Some(0x15),
|
|
||||||
Self::Dec => Some(0x16),
|
|
||||||
Self::Shl => Some(0x17),
|
|
||||||
Self::Shr => Some(0x18),
|
|
||||||
Self::Add => Some(0x19),
|
|
||||||
Self::Sub => Some(0x1A),
|
|
||||||
Self::And => Some(0x1B),
|
|
||||||
Self::Or => Some(0x1C),
|
|
||||||
Self::Not => Some(0x1D),
|
|
||||||
Self::Xor => Some(0x1E),
|
|
||||||
Self::Nand => Some(0x1F),
|
|
||||||
Self::Nor => Some(0x20),
|
|
||||||
Self::Xnor => Some(0x21),
|
|
||||||
Self::Int => Some(0x22),
|
|
||||||
Self::Irt => Some(0x23),
|
|
||||||
Self::Hlt => Some(0x24),
|
|
||||||
Self::Iadd => Some(0x25),
|
|
||||||
Self::Isub => Some(0x26),
|
|
||||||
// Pseudo-instructions don't have opcode values
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_pseudo_instruction(&self) -> bool {
|
|
||||||
matches!(
|
|
||||||
self,
|
|
||||||
Self::Db
|
|
||||||
| Self::Dh
|
|
||||||
| Self::Dw
|
|
||||||
| Self::Resb
|
|
||||||
| Self::Resh
|
|
||||||
| Self::Resw
|
|
||||||
| Self::Push
|
|
||||||
| Self::Pop
|
|
||||||
| Self::Lwi
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -344,7 +344,9 @@ impl std::fmt::Display for Instruction {
|
|||||||
write!(f, "{}", self.mnemonic())?;
|
write!(f, "{}", self.mnemonic())?;
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
Self::Mov(args) | Self::MovSigned(args) => write!(f, " {}, {}", args.sr1, args.dr),
|
Self::Mov(args) | Self::MovSigned(args) => {
|
||||||
|
write!(f, " {}, {}", args.sr1, args.dr)
|
||||||
|
}
|
||||||
Self::LoadByte(args)
|
Self::LoadByte(args)
|
||||||
| Self::LoadByteSigned(args)
|
| Self::LoadByteSigned(args)
|
||||||
| Self::LoadHalfword(args)
|
| Self::LoadHalfword(args)
|
||||||
@@ -365,9 +367,11 @@ impl std::fmt::Display for Instruction {
|
|||||||
write!(f, " ({:x}){}", args.immediate, args.r1)
|
write!(f, " ({:x}){}", args.immediate, args.r1)
|
||||||
}
|
}
|
||||||
Self::LoadLowerImmediate(args) | Self::LoadUpperImmediate(args) => {
|
Self::LoadLowerImmediate(args) | Self::LoadUpperImmediate(args) => {
|
||||||
write!(f, " {}, {}", args.r1, args.r2)
|
write!(f, " {}, {}, {}", args.immediate, args.r1, args.r2)
|
||||||
|
}
|
||||||
|
Self::Compare(args) | Self::Not(args) => {
|
||||||
|
write!(f, " {}, {}", args.sr1, args.sr2)
|
||||||
}
|
}
|
||||||
Self::Compare(args) | Self::Not(args) => write!(f, " {}, {}", args.sr1, args.sr2),
|
|
||||||
|
|
||||||
Self::Add(args)
|
Self::Add(args)
|
||||||
| Self::Sub(args)
|
| Self::Sub(args)
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ path = "src/lib.rs"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
common = { path = "../common" }
|
common = { path = "../common" }
|
||||||
assembler = { path = "../assembler" }
|
assembler = { path = "../assembler" }
|
||||||
|
dsa_editor = { path = "../dsa_editor" }
|
||||||
eframe = "0.31.1"
|
eframe = "0.31.1"
|
||||||
egui = "0.31.1"
|
egui = "0.31.1"
|
||||||
rfd = "0.15.3"
|
rfd = "0.15.3"
|
||||||
|
|||||||
@@ -86,6 +86,9 @@ impl MemoryUnit for MainStore {
|
|||||||
for i in 0..size {
|
for i in 0..size {
|
||||||
data.push(self.read_byte(addr + i));
|
data.push(self.read_byte(addr + i));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// println!("reading {data:?} from {addr:x?}");
|
||||||
|
|
||||||
data
|
data
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,8 +108,10 @@ impl MemoryUnit for MainStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn write_range(&mut self, addr: u32, value: Vec<u8>) {
|
fn write_range(&mut self, addr: u32, value: Vec<u8>) {
|
||||||
for byte in value {
|
// println!("writing {value:?} to {addr:x?}");
|
||||||
let (block_addr, offset) = Self::segment_addr(addr);
|
|
||||||
|
for (i, byte) in value.into_iter().enumerate() {
|
||||||
|
let (block_addr, offset) = Self::segment_addr(addr + i as u32);
|
||||||
let block = self.mut_block(block_addr);
|
let block = self.mut_block(block_addr);
|
||||||
block.data[offset as usize] = byte;
|
block.data[offset as usize] = byte;
|
||||||
}
|
}
|
||||||
|
|||||||
+104
-108
@@ -1,15 +1,18 @@
|
|||||||
use std::sync::mpsc::Sender;
|
use std::{ffi::OsStr, path::PathBuf, sync::mpsc::Sender};
|
||||||
|
|
||||||
use egui::{Align, Context, Layout, Ui};
|
use common::prelude::Instruction;
|
||||||
|
use egui::{Align, Context, Key, Layout, Ui};
|
||||||
use rfd::FileDialog;
|
use rfd::FileDialog;
|
||||||
|
|
||||||
|
use dsa_editor::{CodeEditor, ColorTheme, Syntax};
|
||||||
|
|
||||||
use crate::emulator::{
|
use crate::emulator::{
|
||||||
system::model::{Command, State},
|
system::model::{Command, State},
|
||||||
ui::interface::Component,
|
ui::interface::Component,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct Editor {
|
pub struct Editor {
|
||||||
filename: String,
|
path: PathBuf,
|
||||||
text: String,
|
text: String,
|
||||||
output: Vec<u8>,
|
output: Vec<u8>,
|
||||||
sender: Sender<Command>,
|
sender: Sender<Command>,
|
||||||
@@ -37,6 +40,11 @@ impl Component for Editor {
|
|||||||
fn render(&mut self, state: &mut State, ui: &mut Ui, ctx: &Context) {
|
fn render(&mut self, state: &mut State, ui: &mut Ui, ctx: &Context) {
|
||||||
ui.vertical(|ui| {
|
ui.vertical(|ui| {
|
||||||
// Top bar
|
// Top bar
|
||||||
|
|
||||||
|
if ui.input(|i| i.key_pressed(Key::S) && i.modifiers.ctrl) {
|
||||||
|
self.save();
|
||||||
|
};
|
||||||
|
|
||||||
self.render_toolbar(state, ui, ctx);
|
self.render_toolbar(state, ui, ctx);
|
||||||
|
|
||||||
ui.add_space(4.0); // Add some spacing instead of just a separator
|
ui.add_space(4.0); // Add some spacing instead of just a separator
|
||||||
@@ -61,9 +69,9 @@ impl Component for Editor {
|
|||||||
|
|
||||||
impl Editor {
|
impl Editor {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn new(sender: Sender<Command>) -> Self {
|
pub fn new(sender: Sender<Command>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
filename: String::new(),
|
path: PathBuf::new(),
|
||||||
text: String::new(),
|
text: String::new(),
|
||||||
output: Vec::new(),
|
output: Vec::new(),
|
||||||
sender,
|
sender,
|
||||||
@@ -76,11 +84,68 @@ impl Editor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn filename(&self) -> &str {
|
||||||
|
self.path
|
||||||
|
.file_name()
|
||||||
|
.unwrap_or(OsStr::new("Unnamed!"))
|
||||||
|
.to_str()
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extension(&self) -> &str {
|
||||||
|
self.path
|
||||||
|
.extension()
|
||||||
|
.unwrap_or(OsStr::new("Unknown!"))
|
||||||
|
.to_str()
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn save(&mut self) {
|
||||||
|
let work_dir = std::env::current_dir().unwrap_or_else(|_| {
|
||||||
|
dirs::home_dir().expect(
|
||||||
|
"Couldn't get your current working directory or your home directory.",
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
if let Some(path) = FileDialog::new()
|
||||||
|
.add_filter("damn simple files", &["dsa", "dsb", "dsc", "dsd"])
|
||||||
|
.add_filter("all", &["*"])
|
||||||
|
.set_directory(&work_dir)
|
||||||
|
.save_file()
|
||||||
|
{
|
||||||
|
if let Err(why) = std::fs::write(&path, &self.text) {
|
||||||
|
self.error = Some(format!("Failed to save file: {why}"));
|
||||||
|
} else {
|
||||||
|
self.path = path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn open(&mut self) {
|
||||||
|
let work_dir = std::env::current_dir().unwrap_or_else(|_| {
|
||||||
|
dirs::home_dir().expect(
|
||||||
|
"Couldn't get your current working directory or your home directory.",
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
if let Some(path) = FileDialog::new()
|
||||||
|
.add_filter("damn simple files", &["dsa", "dsb", "dsc", "dsd"])
|
||||||
|
.add_filter("all", &["*"])
|
||||||
|
.set_directory(&work_dir)
|
||||||
|
.pick_file()
|
||||||
|
{
|
||||||
|
if let Ok(contents) = std::fs::read_to_string(&path) {
|
||||||
|
self.path = path;
|
||||||
|
self.text = contents;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn render_output(&self, _state: &mut State, ui: &mut Ui, _ctx: &Context) {
|
fn render_output(&self, _state: &mut State, ui: &mut Ui, _ctx: &Context) {
|
||||||
// Output area with synchronized scrolling
|
// Output area with synchronized scrolling
|
||||||
egui::ScrollArea::vertical()
|
egui::ScrollArea::vertical()
|
||||||
.id_salt("output_scroll")
|
.id_salt("output_scroll")
|
||||||
.max_width(300.0)
|
.max_width(350.0)
|
||||||
.show(ui, |ui| {
|
.show(ui, |ui| {
|
||||||
if self.output.is_empty() {
|
if self.output.is_empty() {
|
||||||
ui.label(
|
ui.label(
|
||||||
@@ -92,8 +157,8 @@ impl Editor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
egui::Grid::new("output_grid")
|
egui::Grid::new("output_grid")
|
||||||
|
.spacing([5.0, 2.0]) // Horizontal and vertical spacing
|
||||||
.num_columns(4)
|
.num_columns(4)
|
||||||
.spacing([20.0, 2.0]) // Horizontal and vertical spacing
|
|
||||||
.striped(false)
|
.striped(false)
|
||||||
.show(ui, |ui| {
|
.show(ui, |ui| {
|
||||||
// Process bytes in chunks of 4
|
// Process bytes in chunks of 4
|
||||||
@@ -107,7 +172,7 @@ impl Editor {
|
|||||||
bytes[i] = byte;
|
bytes[i] = byte;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let value = u32::from_le_bytes(bytes);
|
let value = u32::from_be_bytes(bytes);
|
||||||
|
|
||||||
// Address column
|
// Address column
|
||||||
ui.with_layout(
|
ui.with_layout(
|
||||||
@@ -144,9 +209,14 @@ impl Editor {
|
|||||||
.color(egui::Color32::from_rgb(255, 200, 200)),
|
.color(egui::Color32::from_rgb(255, 200, 200)),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Decimal column
|
// Instruction column
|
||||||
|
let instruction = match Instruction::decode(value) {
|
||||||
|
Ok(instruction) => instruction.to_string(),
|
||||||
|
Err(_) => format!("{value:10}"),
|
||||||
|
};
|
||||||
|
|
||||||
ui.label(
|
ui.label(
|
||||||
egui::RichText::new(format!("{value:10}"))
|
egui::RichText::new(instruction)
|
||||||
.font(egui::FontId::monospace(12.0))
|
.font(egui::FontId::monospace(12.0))
|
||||||
.color(egui::Color32::from_rgb(200, 255, 200)),
|
.color(egui::Color32::from_rgb(200, 255, 200)),
|
||||||
);
|
);
|
||||||
@@ -159,75 +229,26 @@ impl Editor {
|
|||||||
|
|
||||||
fn render_editor(&mut self, _state: &mut State, ui: &mut Ui, _ctx: &Context) {
|
fn render_editor(&mut self, _state: &mut State, ui: &mut Ui, _ctx: &Context) {
|
||||||
let available_width = ui.available_width();
|
let available_width = ui.available_width();
|
||||||
|
let syntax = match self.extension() {
|
||||||
|
"dsa" => Syntax::dsa(),
|
||||||
|
_ => Syntax::dsa(),
|
||||||
|
};
|
||||||
|
|
||||||
// Main editor area with synchronized scrolling
|
CodeEditor::default()
|
||||||
egui::ScrollArea::vertical()
|
.id_source("editor")
|
||||||
.max_width(available_width - 400.0)
|
.with_fontsize(12.0)
|
||||||
.id_salt("editor_scroll")
|
.with_rows(0)
|
||||||
.show(ui, |ui| {
|
.with_theme(ColorTheme::default())
|
||||||
ui.horizontal(|ui| {
|
.with_syntax(syntax)
|
||||||
// Line numbers column
|
.with_numlines(true)
|
||||||
let line_count = self.text.lines().count();
|
.desired_width(available_width - 450.0)
|
||||||
ui.vertical(|ui| {
|
.show(ui, &mut self.text);
|
||||||
ui.set_width(50.0);
|
|
||||||
ui.style_mut().visuals.widgets.inactive.bg_fill =
|
|
||||||
egui::Color32::from_gray(30);
|
|
||||||
|
|
||||||
// Calculate line height to match text editor
|
|
||||||
let line_height =
|
|
||||||
ui.text_style_height(&egui::TextStyle::Monospace);
|
|
||||||
|
|
||||||
for line_num in 1..=line_count {
|
|
||||||
let line_response = ui.allocate_response(
|
|
||||||
egui::vec2(50.0, line_height),
|
|
||||||
egui::Sense::hover(),
|
|
||||||
);
|
|
||||||
|
|
||||||
ui.painter().text(
|
|
||||||
line_response.rect.left_center() + egui::vec2(5.0, 0.0),
|
|
||||||
egui::Align2::LEFT_CENTER,
|
|
||||||
format!("{line_num:3}"),
|
|
||||||
egui::FontId::monospace(12.0),
|
|
||||||
ui.style().visuals.text_color(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
ui.separator();
|
|
||||||
|
|
||||||
// Text editor area
|
|
||||||
ui.vertical(|ui| {
|
|
||||||
let available_size = ui.available_size();
|
|
||||||
let response = ui.add_sized(
|
|
||||||
available_size,
|
|
||||||
egui::TextEdit::multiline(&mut self.text)
|
|
||||||
.font(egui::TextStyle::Monospace)
|
|
||||||
.margin(egui::vec2(5.0, 0.0))
|
|
||||||
.code_editor(),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Update cursor position when text changes
|
|
||||||
if response.changed() {
|
|
||||||
// Simple but functional cursor tracking
|
|
||||||
let lines = self.text.lines().collect::<Vec<_>>();
|
|
||||||
self.cursor_line = lines.len().max(1);
|
|
||||||
|
|
||||||
// Get the length of the last line for column position
|
|
||||||
if let Some(last_line) = lines.last() {
|
|
||||||
self.cursor_col = last_line.chars().count() + 1;
|
|
||||||
} else {
|
|
||||||
self.cursor_col = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_toolbar(&mut self, _state: &mut State, ui: &mut Ui, _ctx: &Context) {
|
fn render_toolbar(&mut self, _state: &mut State, ui: &mut Ui, _ctx: &Context) {
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
// current filename
|
ui.label(format!("File type: {}", self.extension()));
|
||||||
ui.label(format!("File: {}", self.filename));
|
ui.label(format!("Filename: {}", self.filename()));
|
||||||
|
|
||||||
// error display
|
// error display
|
||||||
ui.label(
|
ui.label(
|
||||||
@@ -243,52 +264,21 @@ impl Editor {
|
|||||||
});
|
});
|
||||||
|
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
let work_dir = std::env::current_dir().unwrap_or_else(|_| {
|
|
||||||
dirs::home_dir().expect(
|
|
||||||
"Couldn't get your current working directory or your home directory.",
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
ui.spacing_mut().button_padding = egui::vec2(8.0, 4.0);
|
ui.spacing_mut().button_padding = egui::vec2(8.0, 4.0);
|
||||||
ui.spacing_mut().item_spacing.x = 6.0;
|
ui.spacing_mut().item_spacing.x = 6.0;
|
||||||
|
|
||||||
// Opens a file
|
// Opens a file
|
||||||
if ui.button("Open").clicked() {
|
if ui.button("Open").clicked() {
|
||||||
if let Some(path) = FileDialog::new()
|
self.open();
|
||||||
.add_filter("dsafiles", &["dsa", "dsb", "dsc", "dsd"])
|
|
||||||
.add_filter("all", &["*"])
|
|
||||||
.set_directory(&work_dir)
|
|
||||||
.pick_file()
|
|
||||||
{
|
|
||||||
if let Ok(content) = std::fs::read_to_string(&path) {
|
|
||||||
self.text = content;
|
|
||||||
self.filename = path.display().to_string();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.output = Vec::new();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Saves the current file
|
// Saves the current file
|
||||||
if ui.button("Save").clicked() {
|
if ui.button("Save").clicked() {
|
||||||
if let Some(path) = FileDialog::new()
|
self.save();
|
||||||
.add_filter("dsafiles", &["dsa", "dsb", "dsc", "dsd"])
|
|
||||||
.add_filter("all", &["*"])
|
|
||||||
.set_directory(&work_dir)
|
|
||||||
.save_file()
|
|
||||||
{
|
|
||||||
if let Err(why) = std::fs::write(&path, &self.text) {
|
|
||||||
self.error = Some(format!("Failed to save file: {why}"));
|
|
||||||
} else {
|
|
||||||
self.filename = path.display().to_string();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// builds the current file
|
// builds the current file
|
||||||
if ui.button("Build").clicked() {
|
if ui.button("Build").clicked() {
|
||||||
let _instructions = assembler::assemble(&self.text);
|
|
||||||
|
|
||||||
// TODO: uncomment this once assembler works!!!
|
// TODO: uncomment this once assembler works!!!
|
||||||
// let instructions = assembler::assemble(&self.text);
|
// let instructions = assembler::assemble(&self.text);
|
||||||
// self.output = instructions
|
// self.output = instructions
|
||||||
@@ -296,7 +286,13 @@ impl Editor {
|
|||||||
// .flat_map(|i| i.encode().to_le_bytes().to_vec())
|
// .flat_map(|i| i.encode().to_le_bytes().to_vec())
|
||||||
// .collect();
|
// .collect();
|
||||||
|
|
||||||
self.output = vec![0x00; 256];
|
self.output = vec![
|
||||||
|
0x2e, 0xe0, 0x00, 0x00, 0x2e, 0xe1, 0x00, 0x01, 0x2e, 0xe2, 0x00,
|
||||||
|
0x06, 0x2e, 0xf6, 0x00, 0x00, 0x64, 0x01, 0x18, 0x00, 0x04, 0x37,
|
||||||
|
0x00, 0x00, 0x04, 0x77, 0x08, 0x00, 0x58, 0x57, 0x10, 0x00, 0x50,
|
||||||
|
0x56, 0xb8, 0x00, 0x42, 0xf7, 0x00, 0x04, 0x04, 0x37, 0x80, 0x00,
|
||||||
|
0x92, 0xf7, 0xb8, 0x00,
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Loads the generated binary into the assembler at the provided offset
|
// Loads the generated binary into the assembler at the provided offset
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
include print "../resources/dsa/print.dsa"
|
||||||
|
// Fibonacci sequence calculator in DSA assembly
|
||||||
|
// Calculates the first 8 Fibonacci numbers: 0, 1, 1, 2, 3, 5, 8, 13
|
||||||
|
dw fib_count: 6 // How many more numbers to calculate after F(0) and F(1)
|
||||||
|
|
||||||
|
init:
|
||||||
|
// Initialize the first two Fibonacci numbers
|
||||||
|
lli 0, rg0 // F(0) = 0
|
||||||
|
lli 1, rg1 // F(1) = 1
|
||||||
|
|
||||||
|
// Load loop counter
|
||||||
|
ldw fib_count, zero, rg2 // Load number of iterations remaining
|
||||||
|
|
||||||
|
fibonacci_loop:
|
||||||
|
// Calculate next Fibonacci number: F(n) = F(n-1) + F(n-2)
|
||||||
|
add rg0, rg1, rg4 // rg4 = rg0 + rg1 (new Fibonacci number)
|
||||||
|
|
||||||
|
// Shift the sequence forward
|
||||||
|
mov rg1, rg0 // rg0 = previous rg1 (F(n-2) = F(n-1))
|
||||||
|
mov rg4, rg1 // rg1 = rg4 (F(n-1) = F(n))
|
||||||
|
|
||||||
|
// Decrement loop counter
|
||||||
|
dec rg2 // rg2 = rg2 - 1
|
||||||
|
|
||||||
|
// Check if we should continue looping
|
||||||
|
cmp rg2, zero // Compare counter with 0
|
||||||
|
jgt fibonacci_loop // Jump back if counter > 0
|
||||||
|
|
||||||
|
finish:
|
||||||
|
mov rg1, acc // Final Fibonacci number is in acc
|
||||||
|
hlt
|
||||||
|
|
||||||
|
jmp print::run
|
||||||
|
|
||||||
Reference in New Issue
Block a user