assembler and examples

This commit is contained in:
c0repwn3r 2023-05-08 17:59:54 -04:00
parent f4c78f2b05
commit 6c1cf0f620
Signed by: core
GPG Key ID: FDBF740DADDCEECF
17 changed files with 1088 additions and 14 deletions

338
Cargo.lock generated
View File

@ -2,6 +2,344 @@
# It is not intended for manual editing. # It is not intended for manual editing.
version = 3 version = 3
[[package]]
name = "anstream"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163"
dependencies = [
"anstyle",
"anstyle-parse",
"anstyle-query",
"anstyle-wincon",
"colorchoice",
"is-terminal",
"utf8parse",
]
[[package]]
name = "anstyle"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41ed9a86bf92ae6580e0a31281f65a1b1d867c0cc68d5346e2ae128dddfa6a7d"
[[package]]
name = "anstyle-parse"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e765fd216e48e067936442276d1d57399e37bce53c264d6fefbe298080cb57ee"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-query"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b"
dependencies = [
"windows-sys",
]
[[package]]
name = "anstyle-wincon"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188"
dependencies = [
"anstyle",
"windows-sys",
]
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "cc"
version = "1.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
[[package]]
name = "clap"
version = "4.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34d21f9bf1b425d2968943631ec91202fe5e837264063503708b83013f8fc938"
dependencies = [
"clap_builder",
"clap_derive",
"once_cell",
]
[[package]]
name = "clap_builder"
version = "4.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "914c8c79fb560f238ef6429439a30023c862f7a28e688c58f7203f12b29970bd"
dependencies = [
"anstream",
"anstyle",
"bitflags",
"clap_lex",
"strsim",
]
[[package]]
name = "clap_derive"
version = "4.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9644cd56d6b87dbe899ef8b053e331c0637664e9e21a33dfcdc36093f5c5c4"
dependencies = [
"heck",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "clap_lex"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a2dd5a6fe8c6e3502f568a6353e5273bbb15193ad9a89e457b9970798efbea1"
[[package]]
name = "colorchoice"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
[[package]]
name = "errno"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a"
dependencies = [
"errno-dragonfly",
"libc",
"windows-sys",
]
[[package]]
name = "errno-dragonfly"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
dependencies = [
"cc",
"libc",
]
[[package]]
name = "heck"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
[[package]]
name = "hermit-abi"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286"
[[package]]
name = "io-lifetimes"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220"
dependencies = [
"hermit-abi",
"libc",
"windows-sys",
]
[[package]]
name = "is-terminal"
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f"
dependencies = [
"hermit-abi",
"io-lifetimes",
"rustix",
"windows-sys",
]
[[package]]
name = "libc"
version = "0.2.144"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1"
[[package]]
name = "linux-raw-sys"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ece97ea872ece730aed82664c424eb4c8291e1ff2480247ccf7409044bc6479f"
[[package]]
name = "num-traits"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
dependencies = [
"autocfg",
]
[[package]]
name = "once_cell"
version = "1.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
[[package]]
name = "parse_int"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d695b79916a2c08bcff7be7647ab60d1402885265005a6658ffe6d763553c5a"
dependencies = [
"num-traits",
]
[[package]]
name = "proc-macro2"
version = "1.0.56"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f4f29d145265ec1c483c7c654450edde0bfe043d3938d6972630663356d9500"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rustix"
version = "0.37.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "acf8729d8542766f1b2cf77eb034d52f40d375bb8b615d0b147089946e16613d"
dependencies = [
"bitflags",
"errno",
"io-lifetimes",
"libc",
"linux-raw-sys",
"windows-sys",
]
[[package]]
name = "strsim"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "syn"
version = "2.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]] [[package]]
name = "tmcpu" name = "tmcpu"
version = "0.1.0" version = "0.1.0"
dependencies = [
"clap",
"parse_int",
]
[[package]]
name = "unicode-ident"
version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4"
[[package]]
name = "utf8parse"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
[[package]]
name = "windows-sys"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
[[package]]
name = "windows_aarch64_msvc"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
[[package]]
name = "windows_i686_gnu"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
[[package]]
name = "windows_i686_msvc"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
[[package]]
name = "windows_x86_64_gnu"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
[[package]]
name = "windows_x86_64_msvc"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"

View File

@ -6,3 +6,5 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
clap = { version = "4.2.7", features = ["derive"] }
parse_int = { version = "0.6.0" }

View File

@ -1,15 +1,42 @@
nop
nop
ldi ra, 0x01 ldi ra, 0x01
ldi rb, 0x02 ldi rb, 0x02
add rc, ra rb ; should be 3 add rc, ra rb ; should be 3
out 0x02, rc ; displays 03 on the numeric display out 0x02, rc ; displays 03 on the numeric display
ldi ra, 0b10100000 ; load the num for red and blue into ra ldi ra, 0b10100000 ; load the num for red and blue into ra
mrm ra, 0x001 mrm 0x001, ra
mrm ra, 0x003 mrm 0x003, ra
mrm ra, 0x01e mrm 0x01e, ra
mrm ra, 0x02e mrm 0x02e, ra
mrm ra, 0x02f mrm 0x02f, ra
mrm ra, 0x030 mrm 0x030, ra
mrm ra, 0x031 mrm 0x031, ra
mrm ra, 0x023 mrm 0x023, ra
; Draw a smiley face ; Draw a smiley face
; Test the normalizer
ldi ra, 0b10100101
lsh rb, ra
; Expands to:
; add rb, ra ra
cmp ra rb
; Expands to:
; sub ra, ra rb
cpy rc, rb
; Expands to:
; ldi r0, 0
; add rc, rb r0
not rd, rc
; Expands to:
; ldi r0, 0
; nor rd, rc r0
; test validation
in ra, 0x02
out 0x03, rb

BIN
hello_world.bin Normal file

Binary file not shown.

3
nop.asm Normal file
View File

@ -0,0 +1,3 @@
nop
nop
; comments!

BIN
nop.bin Normal file

Binary file not shown.

4
simple_add.asm Normal file
View File

@ -0,0 +1,4 @@
ldi ra, 0x01 ; set ra = 0x01
ldi rb, 0x02 ; set rb = 0x02
add rc, ra rb ; set rc = ra + rb
out 0x02, rc ; send rc to the numerical display

BIN
simple_add.bin Normal file

Binary file not shown.

11
smiley.asm Normal file
View File

@ -0,0 +1,11 @@
ldi ra, 0b10100000 ; load the num for red and blue into ra
mrm 0x001, ra
mrm 0x003, ra
mrm 0x01e, ra
mrm 0x02e, ra
mrm 0x02f, ra
mrm 0x030, ra
mrm 0x031, ra
mrm 0x023, ra
; Draw a teal smiley face in the top left corner of the framebuffer
; See the spreadsheet (Memory Mapped Framebuffer) for the chart I used to get these values

BIN
smiley.bin Normal file

Binary file not shown.

36
src/asm.rs Normal file
View File

@ -0,0 +1,36 @@
use std::error::Error;
use std::fs;
use std::path::PathBuf;
use crate::opt_sim_asm::SimpleAssemblySimulator;
pub fn assemble(processed: String, out: PathBuf) -> Result<(), Box<dyn Error>> {
let mut simple_sim = SimpleAssemblySimulator {
rom: [0u8; 4096],
ram: [0u8; 4096],
r0: 0,
ra: 0,
rb: 0,
rc: 0,
rd: 0,
re: 0,
pc: 0,
flg: 0,
};
let mut asm = vec![];
for (line_no, line) in processed.lines().enumerate() {
match simple_sim.line(line) {
Ok(mut d) => asm.append(&mut d),
Err(e) => {
eprintln!("{} | {}", line_no, line);
eprintln!("Error on {}: {}", line_no, e);
return Err(e)?;
}
}
}
fs::write(out, asm)?;
Ok(())
}

6
src/disasm.rs Normal file
View File

@ -0,0 +1,6 @@
use std::error::Error;
use std::path::PathBuf;
pub fn disassemble(file: PathBuf, out: PathBuf) -> Result<(), Box<dyn Error>> {
Ok(())
}

View File

@ -1,5 +1,89 @@
use std::path::PathBuf;
use clap::{Parser, ArgAction};
use crate::asm::assemble;
use crate::disasm::disassemble;
use crate::normalizer::normalize;
use crate::preprocessor::preprocess;
pub mod ops; pub mod ops;
pub mod disasm;
pub mod normalizer;
pub mod asm;
pub mod preprocessor;
pub mod opt_sim_asm;
#[derive(Parser)]
pub struct Cli {
// The input assembly file to assemble
pub input: PathBuf,
// The file to write the output to
pub output: PathBuf,
/// Only run the normalizer, and write the normalized code to the output file instead.
#[clap(short, long, action = ArgAction::SetTrue)]
pub normalize_only: bool,
/// Only run up to the preprocessor, and write the normalized and preprocessed code to the output file instead.
#[clap(short, long, action = ArgAction::SetTrue)]
pub preprocess_only: bool,
#[clap(short, long, action = ArgAction::SetTrue)]
pub disassemble: bool
}
fn main() { fn main() {
println!("Hello, world!"); let args = Cli::parse();
if args.disassemble {
match disassemble(args.input.clone(), args.output) {
Ok(_) => std::process::exit(0),
Err(e) => {
eprintln!("{}", e);
std::process::exit(1);
}
}
}
let normalized = match normalize(args.input) {
Ok(s) => s,
Err(e) => {
eprintln!("Normalizer error: {}", e);
std::process::exit(1);
}
};
if args.normalize_only {
match std::fs::write(args.output, normalized) {
Ok(s) => s,
Err(e) => {
eprintln!("Error writing output: {}", e);
std::process::exit(1);
}
}
std::process::exit(0);
}
let preprocessed = match preprocess(normalized) {
Ok(s) => s,
Err(e) => {
eprintln!("Preprocessor error: {}", e);
std::process::exit(1);
}
};
if args.preprocess_only {
match std::fs::write(args.output, preprocessed) {
Ok(s) => s,
Err(e) => {
eprintln!("Error writing output: {}", e);
std::process::exit(1);
}
}
std::process::exit(0);
}
match assemble(preprocessed, args.output) {
Ok(s) => s,
Err(e) => {
eprintln!("Error assembling: {}", e);
std::process::exit(1);
}
}
} }

74
src/normalizer.rs Normal file
View File

@ -0,0 +1,74 @@
use std::error::Error;
use std::fs;
use std::path::PathBuf;
pub fn normalize(file: PathBuf) -> Result<String, Box<dyn Error>> {
let file_str = fs::read_to_string(file)?;
let mut normalized = "; This file has only been normalized. It has not been processed or optimized.\n; This code cannot yet directly be passed into the assembler.\n".to_string();
normalized += "; t+normalizer:tmCPU t+preprocessor:none t+optimizer:none @generated\n";
for (line_num, line) in file_str.lines().enumerate() {
let line_split: Vec<&str> = line.split(' ').collect();
match line_split[0] {
"lsh" => {
if line_split.len() != 3 {
return Err(format!("line {}: expected exactly 2 arguments to 'lsh', instead got {}", line_num, line_split.len() - 1).into());
}
let dest = line_split[1];
if !dest.ends_with(',') {
return Err(format!("line {}: arg 1 to 'lsh' must end in ','", line_num).into());
}
let dest = &dest[..dest.len()-1];
let rg = line_split[2];
normalized += &format!("add {}, {} {} ; normalizer: originally '{}'\n", dest, rg, rg, line);
},
"cmp" => {
if line_split.len() != 3 {
return Err(format!("line {}: expected exactly 2 arguments to 'cmp', instead got {}", line_num, line_split.len() - 1).into());
}
let a = line_split[1];
let b = line_split[2];
normalized += &format!("sub r0, {} {} ; normalizer: originally '{}'\n", a, b, line);
},
"cpy" => {
if line_split.len() != 3 {
return Err(format!("line {}: expected exactly 2 arguments to 'cpy', instead got {}", line_num, line_split.len() - 1).into());
}
let dest = line_split[1];
let a = line_split[2];
if !dest.ends_with(',') {
return Err(format!("line {}: arg 1 to 'lsh' must end in ','", line_num).into());
}
normalized += "ldi r0, 0 ; normalizer: originally below\n";
normalized += &format!("add {}, {} r0 ; normalizer: originally '{}'\n", &dest[..dest.len()-1], a, line);
},
"not" => {
if line_split.len() != 3 {
return Err(format!("line {}: expected exactly 2 arguments to 'not', instead got {}", line_num, line_split.len() - 1).into());
}
let dest = line_split[1];
let a = line_split[2];
if !dest.ends_with(',') {
return Err(format!("line {}: arg 1 to 'not' must end in ','", line_num).into());
}
normalized += "ldi r0, 0 ; normalizer: originally below\n";
normalized += &format!("nor {}, {} r0 ; normalizer: originally '{}'\n", &dest[..dest.len()-1], a, line);
}
_ => {
normalized += line;
normalized += "\n";
}
}
}
Ok(normalized)
}

View File

@ -1,8 +1,8 @@
pub fn nop() -> Vec<u8> { pub fn nop() -> Vec<u8> {
vec![0b1111_1111] vec![0b1111_1111, 0b0000_0000, 0b0000_0000, 0b0000_0000]
} }
pub fn hcf() -> Vec<u8> { pub fn hcf() -> Vec<u8> {
vec![0b1111_1110] vec![0b1111_1110, 0b0000_0000, 0b0000_0000, 0b0000_0000]
} }
pub enum Register { pub enum Register {
@ -183,12 +183,12 @@ mod tests {
#[test] #[test]
fn test_nop() { fn test_nop() {
assert_eq!(nop(), vec![0b1111_1111]); assert_eq!(nop(), vec![0b1111_1111, 0, 0, 0]);
} }
#[test] #[test]
fn test_hcf() { fn test_hcf() {
assert_eq!(hcf(), vec![0b1111_1110]); assert_eq!(hcf(), vec![0b1111_1110, 0, 0, 0]);
} }
#[test] #[test]

466
src/opt_sim_asm.rs Normal file
View File

@ -0,0 +1,466 @@
use std::error::Error;
use crate::ops::{add, and, hcf, ldi, mmr, mrm, nop, nor, or, out, r#in, Register, rmmr, rsh, sub, xor};
#[derive(Clone, Eq, PartialEq, Debug)]
pub struct SimpleAssemblySimulator {
pub rom: [u8; 4096],
pub ram: [u8; 4096],
pub r0: u8,
pub ra: u8,
pub rb: u8,
pub rc: u8,
pub rd: u8,
pub re: u8,
pub pc: u16,
pub flg: u8
}
impl SimpleAssemblySimulator {
pub fn line(&mut self, line: &str) -> Result<Vec<u8>, Box<dyn Error>> {
let line_split: Vec<&str> = line.split(' ').collect();
match line_split[0] {
"nop" => Ok(nop()),
"hcf" => Ok(hcf()), // ignored for the simulator
"add" => {
if line_split.len() != 4 { return Err("add requires exactly 3 arguments".into()) }
let dest = line_split[1];
if !dest.ends_with(',') { return Err("1st argument of add must end in , ".into()) }
let dest = to_reg(&dest[..dest.len() - 1])?;
let a = to_reg(line_split[2])?;
let a_val = match a {
Register::R0 => self.r0,
Register::RA => self.ra,
Register::RB => self.rb,
Register::RC => self.rc,
Register::RD => self.rd,
Register::RE => self.re,
Register::PC => return Err("Cannot use program counter as a register".into()),
Register::FLG => self.flg
};
let b = to_reg(line_split[3])?;
let b_val = match b {
Register::R0 => self.r0,
Register::RA => self.ra,
Register::RB => self.rb,
Register::RC => self.rc,
Register::RD => self.rd,
Register::RE => self.re,
Register::PC => return Err("Cannot use program counter as a register".into()),
Register::FLG => self.flg
};
let mut val = (a_val as u16) + (b_val as u16);
if val > u8::MAX as u16 {
val = u8::MAX as u16; // ignore overflow
}
match dest {
Register::R0 => self.r0 = val as u8,
Register::RA => self.ra = val as u8,
Register::RB => self.rb = val as u8,
Register::RC => self.rc = val as u8,
Register::RD => self.rd = val as u8,
Register::RE => self.re = val as u8,
Register::PC => self.pc = (val as u8) as u16,
Register::FLG => self.flg = val as u8
}
Ok(add(dest, a, b))
},
"sub" => {
if line_split.len() != 4 { return Err("sub requires exactly 3 arguments".into()) }
let dest = line_split[1];
if !dest.ends_with(',') { return Err("1st argument of sub must end in , ".into()) }
let dest = to_reg(&dest[..dest.len() - 1])?;
let a = to_reg(line_split[2])?;
let a_val = match a {
Register::R0 => self.r0,
Register::RA => self.ra,
Register::RB => self.rb,
Register::RC => self.rc,
Register::RD => self.rd,
Register::RE => self.re,
Register::PC => return Err("Cannot use program counter as a register".into()),
Register::FLG => self.flg
};
let b = to_reg(line_split[3])?;
let b_val = match b {
Register::R0 => self.r0,
Register::RA => self.ra,
Register::RB => self.rb,
Register::RC => self.rc,
Register::RD => self.rd,
Register::RE => self.re,
Register::PC => return Err("Cannot use program counter as a register".into()),
Register::FLG => self.flg
};
let val;
if b_val > a_val {
val = 0;
} else {
val = a_val - b_val;
}
match dest {
Register::R0 => self.r0 = val,
Register::RA => self.ra = val,
Register::RB => self.rb = val,
Register::RC => self.rc = val,
Register::RD => self.rd = val,
Register::RE => self.re = val,
Register::PC => self.pc = (val) as u16,
Register::FLG => self.flg = val
}
Ok(sub(dest, a, b))
},
"or" => {
if line_split.len() != 4 { return Err("or requires exactly 3 arguments".into()) }
let dest = line_split[1];
if !dest.ends_with(',') { return Err("1st argument of or must end in , ".into()) }
let dest = to_reg(&dest[..dest.len() - 1])?;
let a = to_reg(line_split[2])?;
let a_val = match a {
Register::R0 => self.r0,
Register::RA => self.ra,
Register::RB => self.rb,
Register::RC => self.rc,
Register::RD => self.rd,
Register::RE => self.re,
Register::PC => return Err("Cannot use program counter as a register".into()),
Register::FLG => self.flg
};
let b = to_reg(line_split[3])?;
let b_val = match b {
Register::R0 => self.r0,
Register::RA => self.ra,
Register::RB => self.rb,
Register::RC => self.rc,
Register::RD => self.rd,
Register::RE => self.re,
Register::PC => return Err("Cannot use program counter as a register".into()),
Register::FLG => self.flg
};
match dest {
Register::R0 => self.r0 = a_val | b_val,
Register::RA => self.ra = a_val | b_val,
Register::RB => self.rb = a_val | b_val,
Register::RC => self.rc = a_val | b_val,
Register::RD => self.rd = a_val | b_val,
Register::RE => self.re = a_val | b_val,
Register::PC => self.pc = (a_val | b_val) as u16,
Register::FLG => self.flg = a_val | b_val
}
Ok(or(dest, a, b))
},
"nor" => {
if line_split.len() != 4 { return Err("nor requires exactly 3 arguments".into()) }
let dest = line_split[1];
if !dest.ends_with(',') { return Err("1st argument of nor must end in , ".into()) }
let dest = to_reg(&dest[..dest.len() - 1])?;
let a = to_reg(line_split[2])?;
let a_val = match a {
Register::R0 => self.r0,
Register::RA => self.ra,
Register::RB => self.rb,
Register::RC => self.rc,
Register::RD => self.rd,
Register::RE => self.re,
Register::PC => return Err("Cannot use program counter as a register".into()),
Register::FLG => self.flg
};
let b = to_reg(line_split[3])?;
let b_val = match b {
Register::R0 => self.r0,
Register::RA => self.ra,
Register::RB => self.rb,
Register::RC => self.rc,
Register::RD => self.rd,
Register::RE => self.re,
Register::PC => return Err("Cannot use program counter as a register".into()),
Register::FLG => self.flg
};
match dest {
Register::R0 => self.r0 = !(a_val | b_val),
Register::RA => self.ra = !(a_val | b_val),
Register::RB => self.rb = !(a_val | b_val),
Register::RC => self.rc = !(a_val | b_val),
Register::RD => self.rd = !(a_val | b_val),
Register::RE => self.re = !(a_val | b_val),
Register::PC => self.pc = !(a_val | b_val) as u16,
Register::FLG => self.flg = !(a_val | b_val)
}
Ok(nor(dest, a, b))
},
"and" => {
if line_split.len() != 4 { return Err("and requires exactly 3 arguments".into()) }
let dest = line_split[1];
if !dest.ends_with(',') { return Err("1st argument of and must end in , ".into()) }
let dest = to_reg(&dest[..dest.len() - 1])?;
let a = to_reg(line_split[2])?;
let a_val = match a {
Register::R0 => self.r0,
Register::RA => self.ra,
Register::RB => self.rb,
Register::RC => self.rc,
Register::RD => self.rd,
Register::RE => self.re,
Register::PC => return Err("Cannot use program counter as a register".into()),
Register::FLG => self.flg
};
let b = to_reg(line_split[3])?;
let b_val = match b {
Register::R0 => self.r0,
Register::RA => self.ra,
Register::RB => self.rb,
Register::RC => self.rc,
Register::RD => self.rd,
Register::RE => self.re,
Register::PC => return Err("Cannot use program counter as a register".into()),
Register::FLG => self.flg
};
match dest {
Register::R0 => self.r0 = a_val & b_val,
Register::RA => self.ra = a_val & b_val,
Register::RB => self.rb = a_val & b_val,
Register::RC => self.rc = a_val & b_val,
Register::RD => self.rd = a_val & b_val,
Register::RE => self.re = a_val & b_val,
Register::PC => self.pc = (a_val & b_val) as u16,
Register::FLG => self.flg = a_val & b_val
}
Ok(and(dest, a, b))
},
"xor" => {
if line_split.len() != 4 { return Err("xor requires exactly 3 arguments".into()) }
let dest = line_split[1];
if !dest.ends_with(',') { return Err("1st argument of xor must end in , ".into()) }
let dest = to_reg(&dest[..dest.len() - 1])?;
let a = to_reg(line_split[2])?;
let a_val = match a {
Register::R0 => self.r0,
Register::RA => self.ra,
Register::RB => self.rb,
Register::RC => self.rc,
Register::RD => self.rd,
Register::RE => self.re,
Register::PC => return Err("Cannot use program counter as a register".into()),
Register::FLG => self.flg
};
let b = to_reg(line_split[3])?;
let b_val = match b {
Register::R0 => self.r0,
Register::RA => self.ra,
Register::RB => self.rb,
Register::RC => self.rc,
Register::RD => self.rd,
Register::RE => self.re,
Register::PC => return Err("Cannot use program counter as a register".into()),
Register::FLG => self.flg
};
match dest {
Register::R0 => self.r0 = a_val ^ b_val,
Register::RA => self.ra = a_val ^ b_val,
Register::RB => self.rb = a_val ^ b_val,
Register::RC => self.rc = a_val ^ b_val,
Register::RD => self.rd = a_val ^ b_val,
Register::RE => self.re = a_val ^ b_val,
Register::PC => self.pc = (a_val ^ b_val) as u16,
Register::FLG => self.flg = a_val ^ b_val
}
Ok(xor(dest, a, b))
},
"rsh" => {
if line_split.len() != 3 { return Err("rsh requires exactly 2 arguments".into()) }
let dest = line_split[1];
if !dest.ends_with(',') { return Err("1st argument of rsh must end in , ".into()) }
let dest = to_reg(&dest[..dest.len() - 1])?;
let a = to_reg(line_split[2])?;
let a_val = match a {
Register::R0 => self.r0,
Register::RA => self.ra,
Register::RB => self.rb,
Register::RC => self.rc,
Register::RD => self.rd,
Register::RE => self.re,
Register::PC => return Err("Cannot use program counter as a register".into()),
Register::FLG => self.flg
};
match dest {
Register::R0 => self.r0 = a_val >> 1,
Register::RA => self.ra = a_val >> 1,
Register::RB => self.rb = a_val >> 1,
Register::RC => self.rc = a_val >> 1,
Register::RD => self.rd = a_val >> 1,
Register::RE => self.re = a_val >> 1,
Register::PC => self.pc = (a_val >> 1) as u16,
Register::FLG => self.flg = a_val >> 1
}
Ok(rsh(dest, a))
},
"ldi" => {
if line_split.len() != 3 { return Err("rsh requires exactly 2 arguments".into()) }
let dest = line_split[1];
if !dest.ends_with(',') { return Err("1st argument of rsh must end in , ".into()) }
let dest = to_reg(&dest[..dest.len() - 1])?;
let data: u8 = parse_int::parse(line_split[2])?;
match dest {
Register::R0 => self.r0 = data,
Register::RA => self.ra = data,
Register::RB => self.rb = data,
Register::RC => self.rc = data,
Register::RD => self.rd = data,
Register::RE => self.re = data,
Register::PC => self.pc = data as u16,
Register::FLG => self.flg = data
}
Ok(ldi(dest, data))
},
"mmr" => {
if line_split.len() != 3 { return Err("mmr requires exactly 2 arguments".into()) }
let dest = line_split[1];
if !dest.ends_with(',') { return Err("1st argument of mmr must end in , ".into()) }
let dest = to_reg(&dest[..dest.len() - 1])?;
let address: u16 = parse_int::parse(line_split[2])?;
match dest {
Register::R0 => self.r0 = self.ram[address as usize],
Register::RA => self.ra = self.ram[address as usize],
Register::RB => self.rb = self.ram[address as usize],
Register::RC => self.rc = self.ram[address as usize],
Register::RD => self.rd = self.ram[address as usize],
Register::RE => self.re = self.ram[address as usize],
Register::PC => self.pc = self.ram[address as usize] as u16,
Register::FLG => self.flg = self.ram[address as usize]
}
Ok(mmr(dest, address))
},
"mrm" => {
if line_split.len() != 3 { return Err("mrm requires exactly 2 arguments".into()) }
let dest = line_split[1];
if !dest.ends_with(',') { return Err("1st argument of mrm must end in , ".into()) }
let src = line_split[2];
let src = to_reg(src)?;
let address: u16 = parse_int::parse(&line_split[1][..line_split[1].len()-1])?;
match src {
Register::R0 => self.ram[address as usize] = self.r0,
Register::RA => self.ram[address as usize] = self.ra,
Register::RB => self.ram[address as usize] = self.rb,
Register::RC => self.ram[address as usize] = self.rc,
Register::RD => self.ram[address as usize] = self.rd,
Register::RE => self.ram[address as usize] = self.re,
Register::PC => self.ram[address as usize] = self.pc.try_into().unwrap(),
Register::FLG => self.ram[address as usize] = self.flg
}
Ok(mrm(src, address))
},
"in" => {
if line_split.len() != 3 { return Err("in requires exactly 2 arguments".into()) }
let dest = line_split[1];
if !dest.ends_with(',') { return Err("1st argument of in must end in , ".into()) }
let dest = to_reg(&dest[..dest.len() - 1])?;
let port: u8 = parse_int::parse(line_split[2])?;
Ok(r#in(dest, port))
},
"out" => {
if line_split.len() != 3 { return Err("in requires exactly 2 arguments".into()) }
let dest = line_split[1];
if !dest.ends_with(',') { return Err("1st argument of in must end in , ".into()) }
let src = line_split[2];
let src = to_reg(src)?;
let port: u8 = parse_int::parse(&line_split[1][..line_split[1].len()-1])?;
Ok(out(src, port))
},
"rmmr" => {
if line_split.len() != 3 { return Err("rmmr requires exactly 2 arguments".into()) }
let dest = line_split[1];
if !dest.ends_with(',') { return Err("1st argument of rmmr must end in , ".into()) }
let dest = to_reg(&dest[..dest.len() - 1])?;
let address: u16 = parse_int::parse(line_split[2])?;
match dest {
Register::R0 => self.r0 = self.ram[address as usize],
Register::RA => self.ra = self.ram[address as usize],
Register::RB => self.rb = self.ram[address as usize],
Register::RC => self.rc = self.ram[address as usize],
Register::RD => self.rd = self.ram[address as usize],
Register::RE => self.re = self.ram[address as usize],
Register::PC => self.pc = self.ram[address as usize] as u16,
Register::FLG => self.flg = self.ram[address as usize]
}
Ok(rmmr(dest, address))
}
_ => Err(format!("Unknown instruction {}", line_split[0]).into())
}
}
}
fn to_reg(s: &str) -> Result<Register, Box<dyn Error>> {
match s {
"r0" => Ok(Register::R0),
"ra" => Ok(Register::RA),
"rb" => Ok(Register::RB),
"rc" => Ok(Register::RC),
"rd" => Ok(Register::RD),
"re" => Ok(Register::RE),
"pc" => Ok(Register::PC),
"flg" => Ok(Register::FLG),
_ => Err(format!("Unknown register {}", s).into())
}
}

23
src/preprocessor.rs Normal file
View File

@ -0,0 +1,23 @@
use std::error::Error;
pub fn preprocess(normalized: String) -> Result<String, Box<dyn Error>> {
let mut processed = String::new();
for (_line_num, line) in normalized.lines().enumerate() {
let mut line_comments_removed = String::new();
for char in line.chars() {
if char == ';' {
break;
} else {
line_comments_removed += char.to_string().as_str();
}
}
if !line_comments_removed.is_empty() {
processed += line_comments_removed.trim_start_matches(' ').trim_end_matches(' ');
processed += "\n";
}
}
Ok(processed)
}