Compare commits

..

10 commits

Author SHA1 Message Date
Lumi Kalt
837f611955 Gave up on the idea of colored script visualizations for now 2024-07-27 19:33:31 +01:00
Lumi Kalt
7e7cd5d7b6 Don't commit the produced 'binary' 2024-07-27 19:32:34 +01:00
Lumi Kalt
1913df1f3d Enable nightly feature 2024-05-01 14:51:51 +01:00
Lumi Kalt
386a3b7403 Show the FP reg table and use the new info format 2024-05-01 13:15:29 +01:00
Lumi Kalt
ac07031631 FP instruction implementations 2024-05-01 13:15:03 +01:00
Lumi Kalt
29a7dfa277 Prepare for handling the .data section 2024-05-01 13:14:00 +01:00
Lumi Kalt
fc429dedbf Update deps 2024-04-27 10:25:48 +01:00
Lumi Kalt
98c8e3d4eb There's one more bug with the imm of U-Types, trying to find it 2024-01-26 01:45:36 +00:00
Lumi Kalt
3ee54491be Make output box modular, fix bug in the expansion of li 2024-01-26 01:41:50 +00:00
Lumi Kalt
6d81417701 Fix mem_offset being off by 4, streamlined some helpers, more op impls 2024-01-24 15:02:04 +00:00
14 changed files with 1368 additions and 418 deletions

2
.gitignore vendored
View file

@ -1 +1,3 @@
/target
test.bin

221
Cargo.lock generated
View file

@ -4,15 +4,27 @@ version = 3
[[package]]
name = "anyhow"
version = "1.0.79"
version = "1.0.82"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca"
checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519"
[[package]]
name = "bitfield"
version = "0.14.0"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d7e60934ceec538daadb9d8432424ed043a904d8e0243f3c6446bce549a46ac"
checksum = "c821a6e124197eb56d907ccc2188eab1038fb919c914f47976e64dd8dbc855d1"
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
[[package]]
name = "codespan-reporting"
@ -31,7 +43,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cbf2150cce219b664a8a70df7a1f933836724b503f8a413af9365b4dcc4d90b8"
dependencies = [
"lazy_static",
"windows-sys",
"windows-sys 0.48.0",
]
[[package]]
@ -61,19 +73,29 @@ checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345"
[[package]]
name = "either"
version = "1.9.0"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2"
[[package]]
name = "itertools"
version = "0.12.0"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0"
checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
dependencies = [
"either",
]
[[package]]
name = "kernel32-sys"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
dependencies = [
"winapi",
"winapi-build",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
@ -82,15 +104,32 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.152"
version = "0.2.154"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7"
checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346"
[[package]]
name = "libredox"
version = "0.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3af92c55d7d839293953fcd0fda5ecfe93297cfde6ffbdec13b41d99c0ba6607"
dependencies = [
"bitflags 2.5.0",
"libc",
"redox_syscall",
]
[[package]]
name = "numtoa"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef"
[[package]]
name = "rayon"
version = "1.8.1"
version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa7237101a77a10773db45d62004a272517633fbcc3df19d96455ede1122e051"
checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa"
dependencies = [
"either",
"rayon-core",
@ -107,7 +146,22 @@ dependencies = [
]
[[package]]
name = "riscv_interpreter"
name = "redox_syscall"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
dependencies = [
"bitflags 1.3.2",
]
[[package]]
name = "redox_termios"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20145670ba436b55d91fc92d25e71160fbfbdd57831631c8d7d36377a476f1cb"
[[package]]
name = "rizz-v"
version = "0.1.0"
dependencies = [
"anyhow",
@ -117,14 +171,16 @@ dependencies = [
"itertools",
"rayon",
"term_size",
"termion",
]
[[package]]
name = "term_size"
version = "0.3.2"
version = "1.0.0-beta1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e4129646ca0ed8f45d09b929036bafad5377103edd06e50bf574b353d2b08d9"
checksum = "a8a17d8699e154863becdf18e4fd28bd0be27ca72856f54daf75c00f2566898f"
dependencies = [
"kernel32-sys",
"libc",
"winapi",
]
@ -139,49 +195,60 @@ dependencies = [
]
[[package]]
name = "unicode-width"
version = "0.1.11"
name = "termion"
version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85"
checksum = "417813675a504dfbbf21bfde32c03e5bf9f2413999962b479023c02848c1c7a5"
dependencies = [
"libc",
"libredox",
"numtoa",
"redox_termios",
]
[[package]]
name = "unicode-width"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68f5e5f3158ecfd4b8ff6fe086db7c8467a2dfdac97fe420f2b7c4aa97af66d6"
[[package]]
name = "winapi"
version = "0.3.9"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
name = "winapi-build"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
[[package]]
name = "winapi-util"
version = "0.1.6"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596"
checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b"
dependencies = [
"winapi",
"windows-sys 0.52.0",
]
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-sys"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
dependencies = [
"windows-targets",
"windows-targets 0.48.5",
]
[[package]]
name = "windows-sys"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets 0.52.5",
]
[[package]]
@ -190,13 +257,29 @@ version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
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",
"windows_aarch64_gnullvm 0.48.5",
"windows_aarch64_msvc 0.48.5",
"windows_i686_gnu 0.48.5",
"windows_i686_msvc 0.48.5",
"windows_x86_64_gnu 0.48.5",
"windows_x86_64_gnullvm 0.48.5",
"windows_x86_64_msvc 0.48.5",
]
[[package]]
name = "windows-targets"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb"
dependencies = [
"windows_aarch64_gnullvm 0.52.5",
"windows_aarch64_msvc 0.52.5",
"windows_i686_gnu 0.52.5",
"windows_i686_gnullvm",
"windows_i686_msvc 0.52.5",
"windows_x86_64_gnu 0.52.5",
"windows_x86_64_gnullvm 0.52.5",
"windows_x86_64_msvc 0.52.5",
]
[[package]]
@ -205,38 +288,86 @@ version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263"
[[package]]
name = "windows_aarch64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6"
[[package]]
name = "windows_i686_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
[[package]]
name = "windows_i686_gnu"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9"
[[package]]
name = "windows_i686_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
[[package]]
name = "windows_i686_msvc"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf"
[[package]]
name = "windows_x86_64_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596"
[[package]]
name = "windows_x86_64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"

View file

@ -1,13 +1,14 @@
[package]
name = "riscv_interpreter"
name = "rizz-v"
version = "0.1.0"
edition = "2021"
[dependencies]
anyhow = "1.0.79"
bitfield = "0.14.0"
anyhow = "1.0.82"
bitfield = "0.15.0"
codespan-reporting = "0.11.1"
colored = "2.1.0"
itertools = "0.12.0"
rayon = "1.8.1"
term_size = "0.3.2"
itertools = "0.12.1"
rayon = "1.10.0"
term_size = "1.0.0-beta1"
termion = "3.0.0"

View file

@ -1,28 +0,0 @@
use std::collections::HashMap;
use colored::Colorize;
fn add_color(input: String) -> String {
let map = [
("number", "magenta"),
("green", "blue"),
].iter().cloned().collect::<HashMap<&str, &str>>();
let mut colored = String::new();
for word in input.split(' ') {
if let Some(num) = word.parse::<i32>().ok() {
colored.push_str(&format!("{:032b}", num));
}
}
colored
}
fn handle_color(input: &str, color: &str) -> String {
match color {
"magenta" => input.magenta().to_string(),
"blue" => input.blue().to_string(),
_ => input.to_string(),
}
}

View file

@ -18,7 +18,8 @@ pub enum Variables {
pub struct Env {
register_alias: HashMap<String, usize>,
labels: HashMap<String, u32>,
registers: [u32; 32],
pub registers: [u32; 32],
pub fregisters: [f32; 32],
pub prev_stacks: Vec<Vec<u32>>,
pub stack: Vec<u32>, // TODO: Find the actual size of the stack
pub instructions: Vec<u32>,
@ -29,6 +30,7 @@ impl Env {
pub fn new() -> Self {
// alias -> xN
let register_alias = [
// Integer regs
("zero", 0),
("ra", 1),
("sp", 2),
@ -62,6 +64,39 @@ impl Env {
("t4", 29),
("t5", 30),
("t6", 31),
// Floating point regs
("ft0", 0),
("ft1", 1),
("ft2", 2),
("ft3", 3),
("ft4", 4),
("ft5", 5),
("ft6", 6),
("ft7", 7),
("fs0", 8),
("fs1", 9),
("fa0", 10),
("fa1", 11),
("fa2", 12),
("fa3", 13),
("fa4", 14),
("fa5", 15),
("fa6", 16),
("fa7", 17),
("fs2", 18),
("fs3", 19),
("fs4", 20),
("fs5", 21),
("fs6", 22),
("fs7", 23),
("fs8", 24),
("fs9", 25),
("fs10", 26),
("fs11", 27),
("ft8", 28),
("ft9", 29),
("ft10", 30),
("ft11", 31),
]
.iter()
.map(|(k, v)| (k.to_string(), v.to_owned()))
@ -71,6 +106,7 @@ impl Env {
register_alias,
labels: HashMap::new(),
registers: [0; 32],
fregisters: [0.0; 32],
prev_stacks: Vec::new(),
stack: Vec::from([0; 1024]), // 1024 * 64 = 64 KiB stack
instructions: Vec::new(),
@ -99,6 +135,22 @@ impl Env {
self.register_alias.get(reg).copied()
}
}
pub fn set_fregister(&mut self, reg: usize, value: f32) {
self.fregisters[reg] = value;
}
pub fn get_fregister(&self, reg: usize) -> f32 {
self.fregisters[reg]
}
pub fn str_to_fregister(&self, reg: &str) -> Option<usize> {
if reg.starts_with("f") && !reg[1..].starts_with("0") {
match reg[1..].parse::<usize>() {
Ok(n) if n < 32 => Some(n),
_ => None,
}
} else {
self.register_alias.get(reg).copied()
}
}
pub fn add_label(&mut self, label: &str, value: u32) {
self.labels.insert(label.to_string(), value);
@ -108,7 +160,7 @@ impl Env {
}
pub fn assemble_op(
&self,
&mut self,
op: (Token, Loc),
) -> Result<Vec<u32>, (RuntimeErr, Loc, Option<String>)> {
if let (Token::Op(name, args), loc) = op {
@ -131,98 +183,107 @@ impl Env {
));
}
let _ =
i.1.clone()
.into_iter()
.enumerate()
.try_for_each(|(k, v)| match v {
Arg::Immediate => {
if let Token::Immediate(i) = args[k].0 {
imm = i as u32;
let _ = i
.1
.clone()
.into_iter()
.enumerate()
.try_for_each(|(k, v)| match v {
Arg::Immediate => match args[k].0.clone() {
Token::Immediate(i) => {
imm = i as u32;
Ok(())
}
Token::Symbol(s) => {
if let Some(v) = self.get_label(&s) {
imm = v - loc.mem_offset as u32;
Ok(())
} else {
Err((
RuntimeErr::InvalidType(
Arg::from(args[k].0.clone()).kind(),
v.kind(),
),
RuntimeErr::LabelNotFound,
args[k].1,
None,
))
}
}
Arg::Register(id) => {
if let Token::Register(r) = &args[k].0 {
regs[id] = self.str_to_register(&r).unwrap();
_ => Err((
RuntimeErr::InvalidType(Arg::from(args[k].0.clone()).kind(), v.kind()),
args[k].1,
None,
)),
},
Arg::Register(id) => {
if let Token::Register(r) = &args[k].0 {
regs[id] = self.str_to_register(&r).unwrap();
Ok(())
} else {
Err((
RuntimeErr::InvalidType(
Arg::from(args[k].0.clone()).kind(),
v.kind(),
),
args[k].1,
None,
))
}
}
Arg::Memory => {
if let Token::Memory(i, r) = &args[k].0 {
if r.is_some() {
regs[k] = self
.str_to_register(&if let Token::Register(r) =
*(r.clone().unwrap())
{
r
} else {
unreachable!()
})
.unwrap();
}
imm = if let Token::Immediate(i) = **i {
i as u32
} else {
unreachable!()
};
Ok(())
} else {
Err((
RuntimeErr::InvalidType(
Arg::from(args[k].0.clone()).kind(),
v.kind(),
),
args[k].1,
None,
))
}
}
Arg::Symbol => {
if let Token::Symbol(s) = &args[k].0 {
if let Some(v) = self.get_label(&s) {
imm = (v).wrapping_sub(loc.mem_offset as u32);
Ok(())
} else {
Err((
RuntimeErr::InvalidType(
Arg::from(args[k].0.clone()).kind(),
v.kind(),
),
RuntimeErr::LabelNotFound,
args[k].1,
None,
))
}
} else if let Token::Immediate(i) = &args[k].0 {
imm = *i as u32;
Ok(())
} else {
Err((
RuntimeErr::InvalidType(
Arg::from(args[k].0.clone()).kind(),
v.kind(),
),
args[k].1,
None,
))
}
Arg::Memory => {
if let Token::Memory(i, r) = &args[k].0 {
if r.is_some() {
regs[k] = self
.str_to_register(&if let Token::Register(r) =
*(r.clone().unwrap())
{
r
} else {
unreachable!()
})
.unwrap();
}
imm = if let Token::Immediate(i) = **i {
i as u32
} else {
unreachable!()
};
Ok(())
} else {
Err((
RuntimeErr::InvalidType(
Arg::from(args[k].0.clone()).kind(),
v.kind(),
),
args[k].1,
None,
))
}
}
Arg::Symbol => {
if let Token::Symbol(s) = &args[k].0 {
if let Some(v) = self.get_label(&s) {
imm = (v).wrapping_sub(loc.mem_offset as u32);
Ok(())
} else {
Err((
RuntimeErr::InvalidType(
Arg::from(args[k].0.clone()).kind(),
v.kind(),
),
args[k].1,
None,
))
}
} else {
Err((
RuntimeErr::InvalidType(
Arg::from(args[k].0.clone()).kind(),
v.kind(),
),
args[k].1,
None,
))
}
}
})?;
}
})?;
Ok({
if let Kind::Pseudo(_) = i.0 {
handle_pseudo(i, imm, regs)
@ -250,7 +311,7 @@ impl Env {
Token::Op(name, _) => {
if let Some((kind, args)) = instruction(&name) {
if let Kind::Pseudo(_) = kind {
tokens[id].1.mem_offset = i + 4;
tokens[id].1.mem_offset = i;
handle_pseudo((kind, args), 0, vec![0; 4])
.into_iter()
.for_each(|_| i += 4);
@ -260,7 +321,7 @@ impl Env {
}
}
Token::Label(name) => {
self.add_label(&name, (i + 4) as u32);
self.add_label(&name, i as u32);
}
other => {
dbg!(other);
@ -276,7 +337,7 @@ impl Env {
&mut self,
(op, loc): (Token, Loc),
) -> Result<(), (RuntimeErr, Loc, Option<String>)> {
let (i, args) = if let Token::Op(name, args) = op {
let (_i, _args) = if let Token::Op(name, args) = op {
if let Some(i) = instruction(&name) {
(i, args.clone())
} else {

View file

@ -9,20 +9,30 @@ use crate::instructions::instruction;
#[derive(Debug, Clone)]
pub enum SyntaxErr {
UnexpectedChar,
//.text specific
/// false for '(' true for ')'
UnmatchedParen(bool),
UnexpectedChar,
OutsideOp(String),
OutsideMnemonic(String),
InvalidRegister,
// .data specific
InvalidType,
InvalidVarName,
MalformedData,
}
impl Display for SyntaxErr {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
SyntaxErr::UnmatchedParen(_) => write!(f, "unmatched parenthesis"),
SyntaxErr::UnexpectedChar => write!(f, "unexpected character"),
SyntaxErr::OutsideOp(kind) => write!(f, "`{kind}` before opcode"),
SyntaxErr::UnmatchedParen(_) => write!(f, "unmatched parenthesis"),
SyntaxErr::OutsideMnemonic(kind) => write!(f, "unexpected '{kind}'"),
SyntaxErr::InvalidRegister => write!(f, "invalid register"),
SyntaxErr::InvalidType => write!(f, "invalid type"),
SyntaxErr::InvalidVarName => write!(f, "invalid variable name"),
SyntaxErr::MalformedData => write!(f, "malformed global definition"),
}
}
}
@ -30,37 +40,42 @@ impl Display for SyntaxErr {
impl SyntaxErr {
pub fn note(&self) -> String {
match self {
SyntaxErr::UnmatchedParen(false) => "add `)` after the register name".to_string(),
SyntaxErr::UnmatchedParen(true) => "add `(` before the register name".to_string(),
SyntaxErr::UnexpectedChar => "ensure the input is well-formed".to_string(),
SyntaxErr::OutsideOp(_) => format!("only add arguments after the opcode"),
SyntaxErr::UnmatchedParen(false) => "add `)` after the register".to_string(),
SyntaxErr::UnmatchedParen(true) => "add `(` before the register".to_string(),
SyntaxErr::OutsideMnemonic(_) => format!("only add arguments after the mnemonic"),
SyntaxErr::InvalidRegister => {
"registers are either xN (N < 32 with no leading 0) or the standard aliases"
.to_string()
"registers are either (x|f)N, for N < 32 with no leading 0, or an alias".to_string()
}
SyntaxErr::InvalidType => "check the spec for proper types".to_string(),
SyntaxErr::InvalidVarName => "variable names must be alphanumeric".to_string(),
SyntaxErr::MalformedData => "ensure the global definition is well-formed".to_string(),
}
}
}
#[derive(Debug, Clone)]
pub enum RuntimeErr {
InvalidOp,
/// TODO: only worth using this after all the instructions are implemented!
InvalidMnemonic,
/// op, actual, expected
InvalidOpArity(String, usize, usize),
/// actual, expected
InvalidType(String, String),
TypeMissmatch(String, String),
LabelNotFound,
}
impl Display for RuntimeErr {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
RuntimeErr::InvalidOp => write!(f, "invalid opcode"),
RuntimeErr::InvalidOpArity(op, actual, expected) => {
write!(f, "`{}` expected {} args, got {}", op, expected, actual)
RuntimeErr::InvalidMnemonic => write!(f, "invalid mnemonic"),
RuntimeErr::InvalidOpArity(_, actual, expected) => {
write!(f, "expected {} args, got {}", expected, actual)
}
RuntimeErr::InvalidType(actual, expected) => {
write!(f, "expected `{}`, got `{}`", expected, actual)
RuntimeErr::TypeMissmatch(actual, expected) => {
write!(f, "expected '{}', got '{}'", expected, actual)
}
RuntimeErr::LabelNotFound => write!(f, "label not found"),
}
}
}
@ -68,7 +83,9 @@ impl Display for RuntimeErr {
impl RuntimeErr {
pub fn note(&self) -> String {
match self {
RuntimeErr::InvalidOp => "check the ref sheet for the avaliable opcodes".to_string(),
RuntimeErr::InvalidMnemonic => {
"check the ref sheet for the avaliable mnemonics".to_string()
}
RuntimeErr::InvalidOpArity(op, actual, expected) => {
let args = instruction(op).unwrap().1;
match actual.cmp(expected) {
@ -78,7 +95,7 @@ impl RuntimeErr {
}
Ordering::Greater => "remove the extra arguments".to_string(),
Ordering::Less if expected - actual == 1 => {
format!("add the extra `{}` argument", args.last().unwrap().kind())
format!("add the extra '{}' argument", args.last().unwrap().kind())
}
Ordering::Less => format!(
"add the extra `{}` arguments",
@ -86,11 +103,14 @@ impl RuntimeErr {
.unwrap()
.iter()
.map(|arg| arg.kind())
.join("`, `")
.join("', '")
),
}
}
RuntimeErr::InvalidType(_, _) => "ensure the operation is valid".to_string(),
RuntimeErr::TypeMissmatch(_, _) => {
"ensure the instruction is getting the right arguments".to_string()
}
RuntimeErr::LabelNotFound => "ensure the label is spelled correctly".to_string(),
}
}
}

View file

@ -1,46 +1,182 @@
use std::mem;
use crate::{env::Env, instructions::kind::Kind};
/// Always "safe" because f32 and i32 have the same size.
fn u32_to_f32(i: u32) -> f32 {
unsafe { mem::transmute(i) }
}
/// Always "safe" because f32 and i32 have the same size.
fn _f32_to_u32(f: f32) -> u32 {
unsafe { mem::transmute(f) }
}
/// lui rd, imm
fn lui(env: &mut Env, rd: usize, imm: u32) {
env.set_register(rd, imm);
}
/// add rd, ra, rb
fn add(env: &mut Env, rd: usize, ra: usize, rb: usize) {
env.set_register(rd, env.get_register(ra).wrapping_add(env.get_register(rb)));
}
/// addi rd, ra, imm
fn addi(env: &mut Env, rd: usize, ra: usize, imm: u32) {
env.set_register(rd, env.get_register(ra) + imm);
env.set_register(rd, env.get_register(ra).wrapping_add(imm));
}
/// xor rd, ra, rb
fn xor(env: &mut Env, rd: usize, ra: usize, rb: usize) {
env.set_register(rd, env.get_register(ra) ^ env.get_register(rb));
}
/// mul rd, ra, rb
fn mul(env: &mut Env, rd: usize, ra: usize, rb: usize) {
env.set_register(rd, env.get_register(ra).wrapping_mul(env.get_register(rb)));
}
/// mulh rd, ra, rb (SxS)
fn mulh(env: &mut Env, rd: usize, ra: usize, rb: usize) {
env.set_register(
rd,
(env.get_register(ra) as i64 * env.get_register(rb) as i64 >> 32) as u32,
);
}
/// mulhsu rd, ra, rb (SxU)
fn mulhsu(env: &mut Env, rd: usize, ra: usize, rb: usize) {
env.set_register(
rd,
(env.get_register(ra) as i64 * env.get_register(rb) as i64 >> 32) as u32,
);
}
/// mulhu rd, ra, rb (UxU)
fn mulu(env: &mut Env, rd: usize, ra: usize, rb: usize) {
env.set_register(
rd,
(env.get_register(ra) as u64 * env.get_register(rb) as u64 >> 32) as u32,
);
}
/// beq ra, rb, imm
fn beq(env: &mut Env, ra: usize, rb: usize, imm: u32) -> bool {
if env.get_register(ra) == env.get_register(rb) {
env.pc = env.pc.wrapping_add(imm);
return true;
}
false
}
/// jal rd, imm
fn jal(env: &mut Env, rd: usize, imm: u32) {
env.set_register(rd, env.pc);
env.pc += imm;
env.set_register(rd, env.pc + 4);
env.pc = env.pc.wrapping_add(imm);
}
const fn to_u32<const N: usize>(bits: &[bool; N]) -> u32 {
let mut val = 0;
for i in 0..N {
if bits[i] {
val |= 1 << i;
}
}
val
/// fadd.s fd, fa, fb
fn fadd_s(env: &mut Env, fd: usize, fa: usize, fb: usize) {
env.set_fregister(fd, env.get_fregister(fa) + env.get_fregister(fb));
}
pub fn run_instruction(env: &mut Env, kind: Kind) {
/// fdiv.s fd, fa, fb
fn fdiv_s(env: &mut Env, fd: usize, fa: usize, fb: usize) {
env.set_fregister(fd, env.get_fregister(fa) / env.get_fregister(fb));
}
/// fmadd.s fd, fa, fb, fc
fn fmadd_s(env: &mut Env, fd: usize, fa: usize, fb: usize, fc: usize) {
env.set_fregister(
fd,
env.get_fregister(fa) * env.get_fregister(fb) + env.get_fregister(fc),
);
}
/// fcvt.s.w fd, ra
fn fcvt_s_w(env: &mut Env, fd: usize, ra: usize) {
env.set_fregister(fd, env.get_register(ra) as i32 as f32);
}
/// fmv.w.x fd, ra
fn fmv_w_x(env: &mut Env, fd: usize, ra: usize) {
env.set_fregister(fd, u32_to_f32(env.get_register(ra)));
}
/// Executes the instruction.
///
/// Returns true if the instruction is a jump.
pub fn run_instruction(env: &mut Env, instruction: u32) -> bool {
let (kind, _) = Kind::to_op(instruction);
let mut regs = kind.get_regs().unwrap();
// Ensure all four registers have a value
regs.extend([0].repeat(4 - regs.len()));
let (rd, ra, rb, _rc) = (regs[0], regs[1], regs[2], regs[3]);
let imm = kind.get_imm().unwrap();
let (rd, ra, rb) = (regs[0], regs[1], regs[2]);
let (fd, fa, fb, fc) = (regs[0], regs[1], regs[2], regs[3]);
let imm = kind.get_imm();
let opcode = kind.get_opcode().unwrap();
let funct3 = instruction >> 12 & 0b111;
let funct7 = instruction >> 25 & 0b1111111;
match opcode {
0b0110111 => {
lui(env, rd, imm)
lui(env, rd, imm.unwrap());
false
}
0b0110011 if funct3 == 0b000 && funct7 == 0b0000000 => {
add(env, rd, ra, rb);
false
}
0b0010011 => {
addi(env, rd, ra, imm)
addi(env, rd, ra, imm.unwrap());
false
}
_ => todo!(),
0b0110011 if funct3 == 0b100 && funct7 == 0b0000000 => {
xor(env, rd, ra, rb);
false
}
0b0110011 if funct3 == 0b000 && funct7 == 0b0000001 => {
mul(env, rd, ra, rb);
false
}
0b0110011 if funct3 == 0b001 && funct7 == 0b0000001 => {
mulh(env, rd, ra, rb);
false
}
0b0110011 if funct3 == 0b010 && funct7 == 0b0000001 => {
mulhsu(env, rd, ra, rb);
false
}
0b0110011 if funct3 == 0b011 && funct7 == 0b0000001 => {
mulu(env, rd, ra, rb);
false
}
0b1100011 => beq(env, ra, rb, imm.unwrap()),
0b1101111 => {
jal(env, rd, imm.unwrap());
true
}
0b1010011 if funct7 == 0x00 => {
fadd_s(env, fd, fa, fb);
false
}
0b1010011 if funct7 == 0x0c => {
fdiv_s(env, fd, fa, fb);
false
}
0b1000011 if fb == 0 => {
fmadd_s(env, fd, fa, fb, fc);
false
}
0b1010011 if funct7 == 0x68 => {
fcvt_s_w(env, fd, ra);
false
}
0b1010011 if funct7 == 0x78 => {
fmv_w_x(env, fd, ra);
false
}
_ => todo!("op: {:032b}", instruction),
}
}

View file

@ -2,29 +2,161 @@ use colored::Colorize;
use crate::env::Env;
pub fn info(env: &Env, op: &str, args: Vec<String>) -> Vec<String> {
match op {
"nop" => vec!["Do nothing - wait 1 cycle".to_string()],
"li" => vec![format!("load {} into the register {}", args[1], args[0])],
/// Display a helpful message about an instruction.
///
/// Returns the message and, if needed, tags to highlight specific registers ([x regs...], [fregs...]).
pub fn info(
env: &Env,
op: &str,
args: Vec<String>,
display_mode: char,
) -> (String, (Vec<usize>, Vec<usize>)) {
let args: Vec<_> = args
.into_iter()
.map(|a| {
if let Ok(num) = a.parse::<u32>() {
match display_mode {
'd' => num.to_string(),
's' => (num as i32).to_string(),
'b' => format!("{:032b}", num),
'h' => format!("{:08x}", num),
_ => unreachable!(),
}
} else {
a
}
})
.collect();
let mut tag = (vec![], vec![]);
let msg = match op {
"nop" => vec!["do nothing".to_string()],
"li" => vec![format!(
"load {} into {}",
args[1].italic().yellow(),
args[0].red()
)],
"lui" => {
let imm = format!("{:032b}", args[1].parse::<i32>().unwrap() as u32)
.chars()
.rev()
.collect::<String>();
vec![
format!(
"load the upper 20 bits of {} into {}",
args[1], args[0]
),
format!("load the upper 20 bits of {} into {}", args[1], args[0]),
format!(
"{} = {}{}",
args[1],
imm[12..32].to_string().red(),
imm[0..12].to_string()
args[1].italic().yellow(),
imm[12..32].to_string().green(),
imm[0..12].to_string().strikethrough().black()
),
format!(
"{:>1$} ← {2} << 12",
args[0].blue(),
args[1].to_string().len(),
imm[12..32].to_string(),
),
format!("{:>1$} <- {2}{3:0>12}", args[0], args[1].to_string().len() - 1, imm[12..32].to_string(), "0".to_string().bold()),
]
}
_ => todo!(),
"add" => {
tag = (
vec![
env.str_to_register(&args[1]).unwrap(),
env.str_to_register(&args[2]).unwrap(),
],
vec![],
);
vec![format!(
"add the values of {0} and {1} and store the result in {2}\n{2} ← {0} + {1}",
args[1].blue(),
args[2].blue(),
args[0].blue()
)]
}
"addi" => {
tag = (
vec![
env.str_to_register(&args[1]).unwrap(),
env.str_to_register(&args[2]).unwrap(),
],
vec![],
);
vec![format!(
"add the values of {0} and {1} and store the result in {2}\n{2} ← {0} + {1}",
args[2].blue(),
args[1].italic().yellow(),
args[0].blue()
)]
}
"sub" => vec![format!(
"subtract the value of {} from the value of {} and store the result in {}",
args[1].blue(),
args[2].blue(),
args[0].blue()
)],
"mul" => {
tag = (
vec![],
vec![
env.str_to_fregister(&args[1]).unwrap(),
env.str_to_fregister(&args[2]).unwrap(),
],
);
vec![format!(
"multiply the values of {0} and {1} and store the result in {2}\n{2} ← {0} ✕ {1}",
args[1].blue(),
args[2].blue(),
args[0].blue()
)]
}
"fadd.s" => {
tag = (
vec![],
vec![
env.str_to_fregister(&args[1]).unwrap(),
env.str_to_fregister(&args[2]).unwrap(),
],
);
vec![format!(
"add the values of {0} and {1} and store the result in {2}\n{2} ← {0} + {1}",
args[1].blue(),
args[2].blue(),
args[0].blue()
)]
}
"fdiv.s" => {
tag = (
vec![],
vec![
env.str_to_fregister(&args[1]).unwrap(),
env.str_to_fregister(&args[2]).unwrap(),
],
);
vec![format!(
"divide the value of {0} by the value of {1} and store the result in {2}\n{2} ← {0} ÷ {1}",
args[1].blue(),
args[2].blue(),
args[0].blue()
)]
}
"fcvt.s.w" => {
tag = (vec![env.str_to_register(&args[1]).unwrap()], vec![]);
vec![format!(
"convert the value of {} to a float and store it in {}",
args[1].blue(),
args[0].blue()
)]
}
"fmv.w.x" => {
tag = (vec![env.str_to_register(&args[1]).unwrap()], vec![]);
vec![format!(
"interpret the value of {0} as a float and store it in {1}\n{1} ← {0}",
args[1].blue(),
args[0].blue()
)]
}
op => todo!("{}", op),
}
.join("\n");
(msg, tag)
}

View file

@ -1,16 +1,11 @@
pub mod kind {
use std::{
fmt::{self, Display, Formatter},
mem,
};
use std::fmt::{self, Display, Formatter};
use bitfield::bitfield;
use crate::instructions::{instruction, to_u32};
use super::{to_bits, to_reg};
/// will be converted by the engine to a real instruction
/// A pseudo instruction is an instruction that
/// will be converted by the engine to real instructions
#[derive(Debug)]
pub struct Pseudo(pub &'static str);
bitfield! {
@ -108,6 +103,7 @@ pub mod kind {
pub opcode, set_opcode: 6, 0;
}
#[derive(Debug)]
pub enum Kind {
Pseudo(Pseudo),
R(R),
@ -156,21 +152,31 @@ pub mod kind {
Kind::Pseudo(_) => None,
Kind::R(_) => None,
Kind::R4(_) => None,
Kind::I(i) => Some(i.imm()),
Kind::I2(i2) => Some(i2.imm() >> 20),
Kind::I(i) => Some(if i.imm() >> 11 == 1 {
i.imm() | 0xFFFFF000
} else {
i.imm()
}),
Kind::I2(i2) => Some(if i2.imm() >> 5 == 1 {
i2.imm() | 0xFFFFFFE0
} else {
i2.imm()
}),
Kind::S(s) => Some(s.imm_11_5() | s.imm_4_0()),
Kind::B(b) => Some(
((b.imm_12() as u32) << 12)
| ((b.imm_11() as u32) << 11)
| (b.imm_10_5() << 5)
| (b.imm_4_1() << 1),
| (b.imm_4_1() << 1)
| if b.imm_12() == true { 0xFFFFE000 } else { 0 },
),
Kind::U(u) => Some(u.imm31_12() << 12),
Kind::J(j) => Some(
((j.imm_20() as u32) << 20)
| ((j.imm_19_12() as u32) << 12)
| ((j.imm_11() as u32) << 11)
| (j.imm_10_1() << 1),
| (j.imm_10_1() << 1)
| if j.imm_20() == true { 0xFFE00000 } else { 0 },
),
}
}
@ -196,17 +202,53 @@ pub mod kind {
pub fn to_op(instruction: u32) -> (Kind, String) {
let opcode = instruction & 0b00000000000000000000000001111111;
let funct3 = (instruction & 0b00000000000000000111000000000000) >> 12;
let funct7 = (instruction & 0b11111110000000000000000000000000) >> 25;
match opcode {
0b0110111 => (
Kind::U(unsafe { mem::transmute_copy(&instruction) }),
"lui".into(),
),
0b0010011 => (
Kind::I(unsafe { mem::transmute_copy(&instruction) }),
"addi".into(),
),
_ => todo!(),
0b0110111 => (Kind::U(U(instruction)), "lui".into()),
0b0010011 => (Kind::I(I(instruction)), "addi".into()),
0b0110011 if funct3 == 0b000 && funct7 == 0b0000000 => {
(Kind::R(R(instruction)), "add".into())
}
0b0110011 if funct3 == 0b000 && funct7 == 0b0100000 => {
(Kind::R(R(instruction)), "sub".into())
}
0b0110011 if funct3 == 0b000 && funct7 == 0b0000001 => {
(Kind::R(R(instruction)), "mul".into())
}
0b1100011 => (Kind::B(B(instruction)), "beq".into()),
0b1101111 => (Kind::J(J(instruction)), "jal".into()),
0b1010011 if funct7 == 0x00 => {
(Kind::R(R(instruction)), "fadd.s".into())
}
0b1010011 if funct7 == 0x0c => {
(Kind::R(R(instruction)), "fdiv.s".into())
}
0b1010011 if funct7 == 0x68 => {
(Kind::R(R(instruction)), "fcvt.s.w".into())
}
0b1010011 if funct7 == 0x78 => {
(Kind::R(R(instruction)), "fmv.w.x".into())
}
other => {
println!("todo: opcode={:07b}", other);
todo!()
}
}
}
pub fn to_u32(&self) -> u32 {
match self {
Kind::Pseudo(_) => unreachable!(),
Kind::R(r) => r.0,
Kind::R4(r4) => r4.0,
Kind::I(i) => i.0,
Kind::I2(i2) => i2.0,
Kind::S(s) => s.0,
Kind::B(b) => b.0,
Kind::U(u) => u.0,
Kind::J(j) => j.0,
}
}
}
@ -480,6 +522,52 @@ pub fn instruction(op: &str) -> Option<(Kind, Vec<Arg>)> {
}),
vec![Arg::Register(0), Arg::Symbol],
),
// F Extension - assune rm is 0b000
// Arithmetic
"fadd.s" => (
Kind::R({
let mut r = R(0);
r.set_funct7(0x00);
r.set_funct3(0b000);
r.set_opcode(0b1010011);
r
}),
vec![Arg::Register(0), Arg::Register(1), Arg::Register(2)],
),
"fdiv.s" => (
Kind::R({
let mut r = R(0);
r.set_funct7(0x0c);
r.set_funct3(0b000);
r.set_opcode(0b1010011);
r
}),
vec![Arg::Register(0), Arg::Register(1), Arg::Register(2)],
),
// Move / Convert
"fcvt.s.w" => (
Kind::R({
let mut r = R(0);
r.set_funct7(0x68);
r.set_funct3(0b000);
r.set_opcode(0b1010011);
r
}),
vec![Arg::Register(0), Arg::Register(1)], // rb is not used
),
"fmv.w.x" => (
Kind::R({
let mut r = R(0);
r.set_funct7(0x78);
r.set_funct3(0b000);
r.set_opcode(0b1010011);
r
}),
vec![Arg::Register(0), Arg::Register(1)], // rb is not used
),
op => unimplemented!("{}", op),
})
}
@ -530,7 +618,7 @@ pub fn with((kind, args): (Kind, Vec<Arg>), imm: u32, regs: Vec<usize>) -> (Kind
b.set_imm_12(to_bits::<1>(imm >> 12)[0]);
b.set_imm_11(to_bits::<1>(imm >> 11)[0]);
b.set_imm_10_5(imm >> 5);
b.set_imm_4_1(imm);
b.set_imm_4_1(imm >> 1);
(Kind::B(b), args)
}
Kind::U(mut u) => {
@ -567,28 +655,32 @@ pub fn handle_pseudo(
with(get_instruction("addi"), 0, vec![0, 0]),
],
"li" => {
match imm {
// if the immediate is small enough (12 bits), use addi
_ if imm >> 12 == 0 => {
// addi rd, x0, imm
vec![with(get_instruction("addi"), imm, regs)]
}
// if the immediate is a multiple of 0x1000, use lui
_ if imm & 0xfff == 0 => {
// if the immediate only has the lower 12 bits set, use addi
if imm >> 12 == 0 {
// addi rd, x0, imm
vec![with(get_instruction("addi"), imm, regs)]
}
// if the immediate is a multiple of 0x1000, use lui
else if imm & 0xfff == 0 {
// lui rd, imm
vec![with(get_instruction("lui"), imm, regs)]
}
// otherwise, use lui and addi
else {
vec![
// lui rd, imm
vec![with(get_instruction("lui"), imm, regs)]
}
// otherwise, use lui and addi
_ => vec![
// lui rd, imm
with(get_instruction("lui"), imm & 0xfffff000, regs.clone()),
with(
get_instruction("lui"),
(imm & 0xfffff000) ^ ((imm >> 11) & 0x1) << 12,
regs.clone(),
),
// addi rd, rd, imm
with(
get_instruction("addi"),
imm & 0x00000fff,
vec![regs[0], regs[0]],
),
],
]
}
}
"beqz" => vec![
@ -617,21 +709,3 @@ const fn to_bits<const N: usize>(val: u32) -> [bool; N] {
}
bits
}
const fn reg_bits<const N: usize>(reg: usize) -> [bool; N] {
to_bits(reg as u32)
}
const fn to_u32<const N: usize>(bits: &[bool; N]) -> u32 {
let mut val = 0;
for i in 0..N {
if bits[i] {
val |= 1 << i;
}
}
val
}
const fn to_reg<const N: usize>(bits: &[bool; N]) -> usize {
to_u32(bits) as usize
}

View file

@ -2,8 +2,10 @@
#![feature(const_mut_refs)]
#![feature(const_trait_impl)]
#![feature(effects)]
#![feature(iterator_try_reduce)]
#![feature(try_blocks)]
pub mod colorizer;
// pub mod colorizer;
pub mod env;
pub mod err;
pub mod execution;

View file

@ -1,3 +1,5 @@
use std::io::Write;
use codespan_reporting::{
diagnostic::{Diagnostic, Label},
files::SimpleFile,
@ -7,20 +9,31 @@ use codespan_reporting::{
Config,
},
};
use riscv_interpreter::{
env::Env, execution::run_instruction, info::info, instructions::{instruction, kind::Kind}, parser::{parse, Token}
use colored::Colorize;
use itertools::Itertools;
use rizz_v::{
env::Env,
execution::run_instruction,
info::info,
parser::{parse, Token},
};
use termion::input::TermRead;
fn main() -> anyhow::Result<()> {
let display_mode = 's';
let writer = StandardStream::stderr(ColorChoice::Always);
let config = Config::default();
let input = std::fs::read_to_string("test.s").unwrap();
let term_width = term_size::dimensions().map(|(w, _)| w).unwrap_or(80);
let file = SimpleFile::new("test.s", input.clone());
let mut env = Env::new();
let mut ops: Vec<(Kind, String)> = Vec::new();
let mut toks: Vec<Token> = Vec::new();
let mut ops: Vec<u32> = Vec::new();
let mut parse_asm_result = String::new();
match parse(&env, &input) {
Ok(tokens) => {
@ -33,55 +46,59 @@ fn main() -> anyhow::Result<()> {
let token = token.clone();
match token.clone() {
Token::Op(name, _) => match env.assemble_op((token, loc.clone())) {
Ok(op) => {
let mut formatted = format!(
"{:<1$} {3:02x}: {2:032b}",
lines[loc.line - 1],
size + 3,
op[0],
loc.mem_offset
);
ops.push(Kind::to_op(op[0].clone()));
Token::Mnemonic(..) => {
match env.assemble_op((token.clone(), loc.clone())) {
Ok(op) => {
let mut formatted = format!(
"{:<1$} {3:02x}: {2:032b}",
lines[loc.line - 1],
size + 3,
op[0],
loc.mem_offset
);
ops.push(op[0]);
toks.push(token.clone());
if op.len() > 1 {
for op in op[1..].iter() {
formatted += &format!(
"\n{:<1$} {3:02x}: {2:032b}",
"",
size + 3,
op,
loc.mem_offset
);
ops.push(Kind::to_op(op.clone()));
}
}
println!("{}", formatted);
}
Err(err) => {
let diagnostic = Diagnostic::error()
.with_message("Runtime Error")
.with_labels(vec![Label::primary(
(),
err.1.start..(err.1.end + 1),
)
.with_message(err.0.to_string())])
.with_notes({
let mut notes = Vec::new();
if let Some(note) = &err.2 {
notes.push(note.to_string());
if op.len() > 1 {
for op in op[1..].iter() {
formatted += &format!(
"\n{:<1$} {3:02x}: {2:032b}",
"",
size + 3,
op,
loc.mem_offset
);
ops.push(*op);
toks.push(token.clone());
}
notes.push(err.0.note());
notes
});
}
parse_asm_result += &format!("{}\n", formatted);
}
Err(err) => {
let diagnostic = Diagnostic::error()
.with_message("Engine Error")
.with_labels(vec![Label::primary(
(),
err.1.start..(err.1.end + 1),
)
.with_message(err.0.to_string())])
.with_notes({
let mut notes = Vec::new();
if let Some(note) = &err.2 {
notes.push(note.to_string());
}
notes.push(err.0.note());
notes
});
term::emit(&mut writer.lock(), &config, &file, &diagnostic)
.unwrap();
term::emit(&mut writer.lock(), &config, &file, &diagnostic)
.unwrap();
}
}
},
}
Token::Label(name) => {
println!(
"{:<1$} <{2:02x}>",
parse_asm_result += &format!(
"{:<1$} <{2:02x}>\n",
name.clone() + ":",
size + 3,
env.get_label(&name).unwrap()
@ -116,15 +133,341 @@ fn main() -> anyhow::Result<()> {
}
};
for op in ops {
run_instruction(&mut env, op.0);
let mut file = std::fs::File::create("test.bin")?;
for op in ops.iter() {
let formatted = format!("{:08x}\n", op);
file.write_all(formatted.as_bytes()).unwrap();
}
// Print the register values
while env.pc / 4 < ops.clone().len() as u32 {
let pc = env.pc.clone();
let prev_regs = env.registers.clone();
let prev_fregs = env.fregisters.clone();
env.pc += 4 * !run_instruction(&mut env, ops[pc as usize >> 2]) as u32;
let mut changed = Vec::new();
for (i, _) in prev_regs
.iter()
.zip(env.registers.iter())
.enumerate()
.filter(|(_, (prev, curr))| prev != curr)
{
changed.push(i);
}
let mut fchanged = Vec::new();
for (i, _) in prev_fregs
.iter()
.zip(env.fregisters.iter())
.enumerate()
.filter(|(_, (prev, curr))| prev != curr)
{
fchanged.push(i);
}
println!(
"{:<1$} <{2:02x}>",
op.1,
32,
env.get_register(10));
"{}\n",
parse_asm_result
.lines()
.enumerate()
.map(|(i, line)| {
if i == pc as usize >> 2 {
format!("> {}", line).bright_green()
} else {
format!(" {}", line).normal()
}
})
.join("\n")
);
let (right, tag) = if let Token::Mnemonic(op, args) = &toks[pc as usize >> 2] {
info(
&env,
&op,
args.iter().map(|(token, _)| token.to_string()).collect(),
display_mode,
)
} else {
unreachable!()
};
let left = make_box(
term_width as u32 / 2,
pc as usize,
env.registers.clone().into_iter().collect(),
changed,
display_mode,
true,
tag.clone(),
) + &make_box_fp(
term_width as u32 / 2,
env.fregisters.clone().into_iter().collect(),
fchanged,
display_mode,
true,
tag,
);
println!(
"{}",
left.lines()
.zip(right.lines().chain([""].repeat(left.lines().count())))
.map(|(l, r)| format!("{} {}", l, r))
.join("\n")
);
println!("\nPress enter to continue...");
for c in std::io::stdin().keys() {
match c.unwrap() {
termion::event::Key::Char('\n') => break,
_ => {}
}
}
}
Ok(())
}
const fn round_down_to_power_of_two(n: u32) -> u32 {
1 << (32 - n.leading_zeros() - 1)
}
/// Assuming the terminal is at least 80 characters wide
///
/// Display Mode:
/// - s: signed decimal
/// - u: unsigned decimal
/// - b: binary
/// - h: hex
fn make_box(
width: u32,
pc: usize,
regs: Vec<u32>,
changed: Vec<usize>,
display_mode: char,
first: bool,
tag: (Vec<usize>, Vec<usize>),
) -> String {
let cell_inner_width: u32 = match display_mode {
'b' => 32,
'u' => 10,
'h' => 8,
's' => 11,
_ => unreachable!(),
} + 7;
// Nnumber of boxes that fit horizontally
let num_boxes = round_down_to_power_of_two((width / (cell_inner_width + 2)) as u32);
if num_boxes <= 1 {
return make_one_wide_box(pc, regs, changed, display_mode, first, tag);
}
let mut boxed = String::new();
if first {
boxed += &format!(
"┌─╢ pc = {pc:04x} ╟{:─<1$}┬",
"",
cell_inner_width.saturating_sub(14) as usize
);
for _ in 1..(num_boxes - 1) {
boxed += &format!("{:─<1$}", "", cell_inner_width as usize);
}
boxed += &format!("{:─<1$}\n", "", cell_inner_width as usize);
} else {
boxed += &format!(
"├─╢ pc = {pc:04x} ╟{:─<1$}┼",
"",
cell_inner_width.saturating_sub(14) as usize
);
for _ in 1..(num_boxes - 1) {
boxed += &format!("{:─<1$}", "", cell_inner_width as usize);
}
boxed += &format!("{:─<1$}\n", "", cell_inner_width as usize);
}
for chunk in &regs.iter().enumerate().chunks(num_boxes as usize) {
let chunk = chunk.collect::<Vec<_>>();
let mut formatted = String::from("");
for (i, reg) in chunk {
let reg = match display_mode {
'b' => format!("x{:<3} {1:0>32b}", i.to_string() + ":", reg),
'u' => format!("x{:<3} {1:>10}", i.to_string() + ":", reg),
'h' => format!("x{:<3} {1:0>8x}", i.to_string() + ":", reg),
's' => {
let signed = *reg as i32;
let sign = if signed < 0 { "-" } else { "+" };
format!(
"x{:<3} {:>11}",
i.to_string() + ":",
sign.to_string() + &signed.abs().to_string()
)
}
_ => unreachable!(),
};
let reg = if changed.contains(&i) {
reg.bright_green()
} else {
if tag.0.contains(&i) {
reg.bright_yellow()
} else {
reg.normal()
}
};
formatted += &format!("{}", reg);
}
boxed += &format!("{}\n", formatted);
}
boxed
}
fn make_one_wide_box(
pc: usize,
regs: Vec<u32>,
changed: Vec<usize>,
display_mode: char,
first: bool,
tag: (Vec<usize>, Vec<usize>),
) -> String {
let mut boxed = String::new();
boxed += &if first {
format!("┌─╢ pc = {pc:04x}{:─<1$}\n", "", 32)
} else {
format!("├─╢ pc = {pc:04x}{:─<1$}\n", "", 32)
};
for (i, reg) in regs.iter().enumerate() {
let reg = match display_mode {
'b' => format!("x{:<3} {1:0>32b}", i.to_string() + ":", reg),
'u' => format!("x{:<3} {1:0>10}", i.to_string() + ":", reg),
'h' => format!("x{:<3} {1:0>8x}", i.to_string() + ":", reg),
's' => {
let signed = *reg as i32;
let sign = if signed < 0 { "-" } else { "+" };
format!("x{:<3} {}{:0>10}", i.to_string() + ":", sign, signed.abs())
}
_ => unreachable!(),
};
let reg = if changed.contains(&i) {
reg.bright_green()
} else {
if tag.0.contains(&i) {
reg.bright_yellow()
} else {
reg.normal()
}
};
boxed += &format!("{}\n", reg);
}
boxed
}
/// Assuming the terminal is at least 80 characters wide
/// Display Mode:
/// - s,u: floating point
/// - b: binary
/// - x: hexadecimal
/// - e: scientific
fn make_box_fp(
width: u32,
regs: Vec<f32>,
changed: Vec<usize>,
display_mode: char,
last: bool,
tag: (Vec<usize>, Vec<usize>),
) -> String {
let cell_inner_width: u32 = 11 + 7;
// Nnumber of boxes that fit horizontally
let num_boxes = round_down_to_power_of_two((width / (cell_inner_width + 2)) as u32);
if num_boxes <= 1 {
return make_one_wide_box_fp(regs, changed, last, tag);
}
let mut boxed = String::new();
boxed += &format!("├─{:─<1$}", "", (cell_inner_width - 1) as usize);
for _ in 1..(num_boxes - 1) {
boxed += &format!("{:─<1$}", "", cell_inner_width as usize);
}
boxed += &format!("{:─<1$}\n", "", cell_inner_width as usize);
for chunk in &regs.iter().enumerate().chunks(num_boxes as usize) {
let chunk = chunk.collect::<Vec<_>>();
let mut formatted = String::from("");
for (i, freg) in chunk {
// let freg = format!("f{:<3} {:>11}", i.to_string() + ":", freg);
let freg = match display_mode {
's' | 'u' => format!("f{:<3} {:>11}", i.to_string() + ":", freg),
'b' => format!("f{:<3} {:0>32b}", i.to_string() + ":", freg.to_bits()),
'x' => format!("f{:<3} {:0>8x}", i.to_string() + ":", freg.to_bits()),
'e' => format!("f{:<3} {:>11e}", i.to_string() + ":", freg),
_ => unreachable!(),
};
let reg = if changed.contains(&i) {
freg.bright_green()
} else {
if tag.1.contains(&i) {
freg.bright_yellow()
} else {
freg.normal()
}
};
formatted += &format!("{}", reg);
}
boxed += &format!("{}\n", formatted);
}
if last {
boxed += &format!("{:─<1$}", "", cell_inner_width as usize);
for _ in 1..(num_boxes - 1) {
boxed += &format!("{:─<1$}", "", cell_inner_width as usize);
}
boxed += &format!("{:─<1$}", "", cell_inner_width as usize);
} else {
boxed += &format!("{:─<1$}", "", cell_inner_width as usize);
for _ in 1..(num_boxes - 1) {
boxed += &format!("{:─<1$}", "", cell_inner_width as usize);
}
boxed += &format!("{:─<1$}", "", cell_inner_width as usize);
}
boxed
}
fn make_one_wide_box_fp(
regs: Vec<f32>,
changed: Vec<usize>,
last: bool,
tag: (Vec<usize>, Vec<usize>),
) -> String {
let mut boxed = String::new();
boxed += &format!("├─{:─<1$}\n", "", 32);
for (i, freg) in regs.iter().enumerate() {
let freg = format!("f{:<3} {:0>11}", i.to_string() + ":", freg);
let reg = if changed.contains(&i) {
freg.bright_green()
} else {
if tag.1.contains(&i) {
freg.bright_yellow()
} else {
freg.normal()
}
};
boxed += &format!("{}\n", reg);
}
boxed += &if last {
format!("{:─<1$}", "", 32)
} else {
format!("{:─<1$}", "", 32)
};
boxed
}

View file

@ -83,6 +83,81 @@ fn parse_line(env: &Env, input: &str, loc: &mut Loc) -> Result<Vec<(Token, Loc)>
Spacing
}
'0' if chars.peek() == Some(&'x') => {
chars.next();
loc.end += 1;
let mut num = std::string::String::new();
while let Some('0'..='9') | Some('a'..='f') | Some('A'..='F') = chars.peek() {
num.push(chars.next().unwrap());
loc.end += 1;
}
if let Some('(') | Some(' ') | None = chars.peek() {
Immediate(u32::from_str_radix(&num, 16).unwrap())
} else {
let err = Err((
SyntaxErr::UnexpectedChar,
Loc {
start: loc.end + 1,
end: loc.end + 1,
..*loc
},
tokens.clone(),
None,
));
advance_to_next_line(&mut chars, loc);
return err;
}
}
'0' if chars.peek() == Some(&'b') => {
chars.next();
loc.end += 1;
let mut num = std::string::String::new();
while let Some('0'..='1') = chars.peek() {
num.push(chars.next().unwrap());
loc.end += 1;
}
if let Some('(') | Some(' ') | None = chars.peek() {
Immediate(u32::from_str_radix(&num, 2).unwrap())
} else {
let err = Err((
SyntaxErr::UnexpectedChar,
Loc {
start: loc.end + 1,
end: loc.end + 1,
..*loc
},
tokens.clone(),
None,
));
advance_to_next_line(&mut chars, loc);
return err;
}
}
'0' if chars.peek() == Some(&'o') => {
chars.next();
loc.end += 1;
let mut num = std::string::String::new();
while let Some('0'..='7') = chars.peek() {
num.push(chars.next().unwrap());
loc.end += 1;
}
if let Some('(') | Some(' ') | None = chars.peek() {
Immediate(u32::from_str_radix(&num, 8).unwrap())
} else {
let err = Err((
SyntaxErr::UnexpectedChar,
Loc {
start: loc.end + 1,
end: loc.end + 1,
..*loc
},
tokens.clone(),
None,
));
advance_to_next_line(&mut chars, loc);
return err;
}
}
'0'..='9' => {
let mut num = c.to_string();
while let Some('0'..='9') = chars.peek() {
@ -90,7 +165,7 @@ fn parse_line(env: &Env, input: &str, loc: &mut Loc) -> Result<Vec<(Token, Loc)>
loc.end += 1;
}
if let Some('(') | Some(' ') | None = chars.peek() {
Immediate(num.parse().unwrap())
dbg!(Immediate(num.parse().unwrap()))
} else {
let err = Err((
SyntaxErr::UnexpectedChar,

View file

@ -2,11 +2,10 @@
/// Test values come from Ripes
use crate::{
env::Env,
instructions::{get_instruction, with},
instructions::{get_instruction, handle_pseudo, with},
};
#[test]
#[ignore = "TODO"]
fn nop() {
#[rustfmt::skip]
{
@ -17,21 +16,48 @@ fn nop() {
};
// nop
assert_eq!(
u32::from_str_radix(
&with(
get_instruction("nop"),
0, // imm
vec![]
)
.0
.to_string(),
2
)
.unwrap(),
0b00000000000000000000000000010011u32
handle_pseudo(
get_instruction("nop"),
0, // imm
vec![]
)[0]
.0
.to_u32(),
0b00000000000000000000000000010011
);
}
#[test]
fn li() {
let env = Env::new();
#[rustfmt::skip]
{
// li (pseudo) -> Myriad Sequence (in this case, both addi and lui)
// 1 -> lui
// U-Type
// | imm20 | rd | opcode
// 00000000000000001101 01010 0110111
// 2 -> addi
// I-Type
// | imm12 | ra |f3 | rd | opcode
// 000000101001 01010 000 01010 0010011
};
// li a0 53289
assert!(handle_pseudo(
get_instruction("li"),
53289,
vec![env.str_to_register("a0").unwrap()]
)
.into_iter()
.map(|i| i.0.to_u32())
.eq([
0b00000000000000001101010100110111,
0b00000010100101010000010100010011
]
.into_iter()));
}
#[test]
fn lui() {
let env = Env::new();
@ -40,23 +66,18 @@ fn lui() {
{
// U-Type
// | imm20 | rd | opcode
// 00000000000000000011 01010 0110111
// 00000011010100101001 01010 0110111
};
// lui a0 13609
assert_eq!(
u32::from_str_radix(
&with(
get_instruction("lui"),
13609 << 12,
vec![env.str_to_register("a0").unwrap()]
)
.0
.to_string(),
2
with(
get_instruction("lui"),
13609 << 12,
vec![env.str_to_register("a0").unwrap()]
)
.unwrap(),
0b00000011010100101001010100110111u32
.0
.to_u32(),
0b00000011010100101001010100110111
);
}
@ -72,22 +93,18 @@ fn sb() {
};
// sb t5 -4(sp)
assert_eq!(
u32::from_str_radix(
&with(
get_instruction("sb"),
-4i32 as u32, // imm
vec![
0, // rd
env.str_to_register("sp").unwrap(), // ra
env.str_to_register("t5").unwrap() // rb
],
)
.0
.to_string(),
2
with(
get_instruction("sb"),
-4i32 as u32, // imm
vec![
0, // rd
env.str_to_register("sp").unwrap(), // ra
env.str_to_register("t5").unwrap() // rb
],
)
.unwrap(),
0b11111111111000010000111000100011u32
.0
.to_u32(),
0b11111111111000010000111000100011
);
}
@ -103,22 +120,18 @@ fn add() {
};
// add a0 a0 a1
assert_eq!(
u32::from_str_radix(
&with(
get_instruction("add"),
0, // imm
vec![
env.str_to_register("a0").unwrap(), // rd
env.str_to_register("a0").unwrap(), // ra
env.str_to_register("a1").unwrap() // rb
]
)
.0
.to_string(),
2
with(
get_instruction("add"),
0, // imm
vec![
env.str_to_register("a0").unwrap(), // rd
env.str_to_register("a0").unwrap(), // ra
env.str_to_register("a1").unwrap() // rb
]
)
.unwrap(),
0b00000000101101010000010100110011u32
.0
.to_u32(),
0b00000000101101010000010100110011
);
}
@ -131,26 +144,20 @@ fn addi() {
// I-Type
// | imm12 | ra |f3 | rd | opcode
// 000000000001 01010 000 01010 0010011
// 000000000001 01010 000 01010 0010011 (Ripes)
};
// addi a0 a0 1
assert_eq!(
u32::from_str_radix(
with(
get_instruction("addi"),
1,
vec![
env.str_to_register("a0").unwrap(),
env.str_to_register("a0").unwrap()
],
)
.0
.to_string()
.as_str(),
2
with(
get_instruction("addi"),
1,
vec![
env.str_to_register("a0").unwrap(),
env.str_to_register("a0").unwrap()
],
)
.unwrap(),
0b00000000000101010000010100010011u32
.0
.to_u32(),
0b00000000000101010000010100010011
);
}
@ -163,25 +170,20 @@ fn beq() {
// B-Type
// | imm7 | rb | ra |f3 |imm5 | opcode
// 0000000 01011 01010 000 00100 1100011
// 0000000 01011 01010 000 00100 1100011 (Ripes)
};
// beq a0 a1 4
assert_eq!(
u32::from_str_radix(
&with(
get_instruction("beq"),
4,
vec![
0, // no rd
env.str_to_register("a0").unwrap(),
env.str_to_register("a1").unwrap()
]
)
.0
.to_string(),
2
with(
get_instruction("beq"),
4,
vec![
0, // no rd
env.str_to_register("a0").unwrap(),
env.str_to_register("a1").unwrap()
]
)
.unwrap(),
0b00000000101101010000001001100011u32
.0
.to_u32(),
0b00000000101101010000001001100011
);
}

17
test.s
View file

@ -1,12 +1,11 @@
# li a0 55743235
li a0 5
li a1 1
lui a2 0x3f800000
# 5!
factorial:
# beqz a0 end
# mul a1 a1 a0
# addi a0 a0 -1
# j factorial
add a2 a0 a1
end:
# nop
fcvt.s.w fa0 a0
fcvt.s.w fa1 a1
fadd.s fa2 fa0 fa1
fdiv.s fa3 fa2 fa0