Disassembler and assembler for register-register mov

This commit is contained in:
Smaug123
2023-04-08 12:25:00 +01:00
parent 6b5124442c
commit 62126f5ba4
10 changed files with 854 additions and 7 deletions

1
.gitattributes vendored Normal file
View File

@@ -0,0 +1 @@
*.rs text=auto

View File

@@ -15,8 +15,13 @@ jobs:
"runs-on": "ubuntu-latest",
"steps": [
{
"uses": "actions/checkout@v3",
"name": "Checkout"
"name": "Checkout",
"uses": "actions/checkout@v3"
},
{
"name": "Checkout submodules",
"shell": "bash",
"run": "auth_header=\"$(git config --local --get http.https://github.com/.extraheader)\" git submodule sync --recursive && git -c \"http.extraheader=$auth_header\" -c protocol.version=2 submodule update --init --force --recursive --depth=1"
},
{
"name": "Install Nix",
@@ -35,8 +40,12 @@ jobs:
"runs-on": "ubuntu-latest",
"steps": [
{
"uses": "actions/checkout@v3",
"name": "Checkout"
"name": "Checkout",
"uses": "actions/checkout@v3"
},
{
"name": "Checkout submodules",
"run": "auth_header=\"$(git config --local --get http.https://github.com/.extraheader)\" git submodule sync --recursive && git -c \"http.extraheader=$auth_header\" -c protocol.version=2 submodule update --init --force --recursive --depth=1"
},
{
"name": "Install Nix",
@@ -143,6 +152,10 @@ jobs:
"uses": "actions/checkout@v3",
"name": "Checkout"
},
{
"name": "Checkout submodules",
"run": "auth_header=\"$(git config --local --get http.https://github.com/.extraheader)\" git submodule sync --recursive && git -c \"http.extraheader=$auth_header\" -c protocol.version=2 submodule update --init --force --recursive --depth=1"
},
{
"name": "Install Nix",
"uses": "cachix/install-nix-action@v17",
@@ -150,7 +163,7 @@ jobs:
},
{
"name": "Run app",
"run": "nix run"
"run": "nix run . -- computer_enhance/perfaware/part1/listing_0038_many_register_mov computer_enhance/perfaware/part1/listing_0038_many_register_mov.asm"
}
]
}

3
.gitmodules vendored Normal file
View File

@@ -0,0 +1,3 @@
[submodule "computer_enhance"]
path = computer_enhance
url = https://github.com/cmuratori/computer_enhance

402
Cargo.lock generated
View File

@@ -2,6 +2,408 @@
# It is not intended for manual editing.
version = 3
[[package]]
name = "anstream"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "342258dd14006105c2b75ab1bd7543a03bdf0cfc94383303ac212a04939dff6f"
dependencies = [
"anstyle",
"anstyle-parse",
"anstyle-wincon",
"concolor-override",
"concolor-query",
"is-terminal",
"utf8parse",
]
[[package]]
name = "anstyle"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23ea9e81bd02e310c216d080f6223c179012256e5151c41db88d12c88a1684d2"
[[package]]
name = "anstyle-parse"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7d1bb534e9efed14f3e5f44e7dd1a4f709384023a4165199a4241e18dff0116"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-wincon"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3127af6145b149f3287bb9a0d10ad9c5692dba8c53ad48285e5bec4063834fa"
dependencies = [
"anstyle",
"windows-sys 0.45.0",
]
[[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.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "046ae530c528f252094e4a77886ee1374437744b2bff1497aa898bbddbbb29b3"
dependencies = [
"clap_builder",
"clap_derive",
"once_cell",
]
[[package]]
name = "clap_builder"
version = "4.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "223163f58c9a40c3b0a43e1c4b50a9ce09f007ea2cb1ec258a687945b4b7929f"
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 = "concolor-override"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a855d4a1978dc52fb0536a04d384c2c0c1aa273597f08b77c8c4d3b2eec6037f"
[[package]]
name = "concolor-query"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88d11d52c3d7ca2e6d0040212be9e4dbbcd78b6447f535b6b561f449427944cf"
dependencies = [
"windows-sys 0.45.0",
]
[[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 0.48.0",
]
[[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 0.48.0",
]
[[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 0.48.0",
]
[[package]]
name = "libc"
version = "0.2.141"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3304a64d199bb964be99741b7a14d26972741915b3649639149b2479bb46f4b5"
[[package]]
name = "linux-raw-sys"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d59d8c75012853d2e872fb56bc8a2e53718e2cafe1a4c823143141c6d90c322f"
[[package]]
name = "memchr"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
[[package]]
name = "minimal-lexical"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]]
name = "nom"
version = "7.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
dependencies = [
"memchr",
"minimal-lexical",
]
[[package]]
name = "once_cell"
version = "1.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
[[package]]
name = "performance_aware_programming"
version = "0.1.0"
dependencies = [
"clap",
"nom",
]
[[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.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rustix"
version = "0.37.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85597d61f83914ddeba6a47b3b8ffe7365107221c2e557ed94426489fefb5f77"
dependencies = [
"bitflags",
"errno",
"io-lifetimes",
"libc",
"linux-raw-sys",
"windows-sys 0.48.0",
]
[[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.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c9da457c5285ac1f936ebd076af6dac17a61cfe7826f2076b4d015cf47bc8ec"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[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.45.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
dependencies = [
"windows-targets 0.42.2",
]
[[package]]
name = "windows-sys"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
dependencies = [
"windows-targets 0.48.0",
]
[[package]]
name = "windows-targets"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
dependencies = [
"windows_aarch64_gnullvm 0.42.2",
"windows_aarch64_msvc 0.42.2",
"windows_i686_gnu 0.42.2",
"windows_i686_msvc 0.42.2",
"windows_x86_64_gnu 0.42.2",
"windows_x86_64_gnullvm 0.42.2",
"windows_x86_64_msvc 0.42.2",
]
[[package]]
name = "windows-targets"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5"
dependencies = [
"windows_aarch64_gnullvm 0.48.0",
"windows_aarch64_msvc 0.48.0",
"windows_i686_gnu 0.48.0",
"windows_i686_msvc 0.48.0",
"windows_x86_64_gnu 0.48.0",
"windows_x86_64_gnullvm 0.48.0",
"windows_x86_64_msvc 0.48.0",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
[[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.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
[[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.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
[[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.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
[[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.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
[[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.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
[[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.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
[[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
[dependencies]
clap = { version = "4.2.1", features = [ "derive" ] }
nom = "7.1.3"

3
README.md Normal file
View File

@@ -0,0 +1,3 @@
# Performance-Aware Programming coursework
Me running through [Performance-Aware Programming](https://www.computerenhance.com) in Rust.

1
computer_enhance Submodule

Submodule computer_enhance added at 00cc753c91

108
src/assembly.rs Normal file
View File

@@ -0,0 +1,108 @@
use nom::{
branch::alt,
bytes::complete::tag,
character::complete::{char, digit1, line_ending, not_line_ending, one_of},
combinator::map_res,
multi::many0,
sequence::{preceded, separated_pair, terminated, tuple},
IResult,
};
use crate::{
register::{ByteRegisterSubset, GeneralRegister, Register, RegisterSubset, SpecialRegister},
Instruction, MoveInstruction, Program,
};
fn comment(input: &str) -> IResult<&str, &str> {
preceded(char(';'), terminated(not_line_ending, line_ending))(input)
}
fn bits(input: &str) -> IResult<&str, u8> {
let p = preceded(tag("bits "), terminated(digit1, line_ending));
map_res(p, str::parse)(input)
}
fn general_register(input: &str) -> IResult<&str, GeneralRegister> {
map_res(one_of("abcd"), |c| match c {
'a' => Ok::<_, ()>(GeneralRegister::A),
'b' => Ok(GeneralRegister::B),
'c' => Ok(GeneralRegister::C),
'd' => Ok(GeneralRegister::D),
_ => panic!("cannot hit"),
})(input)
}
fn byte_register_subset(input: &str) -> IResult<&str, ByteRegisterSubset> {
map_res(one_of("hl"), |c| match c {
'h' => Ok::<_, ()>(ByteRegisterSubset::High),
'l' => Ok(ByteRegisterSubset::Low),
_ => panic!("cannot hit"),
})(input)
}
fn register_subset(input: &str) -> IResult<&str, RegisterSubset> {
alt((
map_res(byte_register_subset, |x| {
Ok::<_, ()>(RegisterSubset::Subset(x))
}),
map_res(char('x'), |_| Ok::<_, ()>(RegisterSubset::All)),
))(input)
}
fn special_register(input: &str) -> IResult<&str, SpecialRegister> {
alt((
map_res(tag("si"), |_| Ok::<_, ()>(SpecialRegister::SourceIndex)),
map_res(tag("bp"), |_| Ok::<_, ()>(SpecialRegister::BasePointer)),
map_res(tag("sp"), |_| Ok::<_, ()>(SpecialRegister::StackPointer)),
map_res(tag("di"), |_| Ok::<_, ()>(SpecialRegister::DestIndex)),
))(input)
}
fn register(input: &str) -> IResult<&str, Register> {
alt((
map_res(tuple((general_register, register_subset)), |(reg, sub)| {
Ok::<_, ()>(Register::General(reg, sub))
}),
map_res(special_register, |r| Ok::<_, ()>(Register::Special(r))),
))(input)
}
fn move_instruction(input: &str) -> IResult<&str, MoveInstruction> {
map_res(
preceded(
tag("mov "),
tuple((register, tag(", "), register, line_ending)),
),
|(dest, _, source, _)| Ok::<_, ()>(MoveInstruction { dest, source }),
)(input)
}
fn instruction(input: &str) -> IResult<&str, Instruction> {
map_res(move_instruction, |v| Ok::<_, ()>(Instruction::Move(v)))(input)
}
fn trivia(input: &str) -> IResult<&str, &str> {
alt((comment, line_ending))(input)
}
pub fn program(input: &str) -> IResult<&str, Program<Vec<Instruction>>> {
map_res(
preceded(
many0(trivia),
separated_pair(
bits,
many0(trivia),
many0(alt((
map_res(instruction, |i| Ok::<_, ()>(Some(i))),
map_res(trivia, |_| Ok::<_, ()>(None)),
))),
),
),
|(bits, instructions)| {
Ok::<_, ()>(Program {
bits,
instructions: instructions.into_iter().flatten().collect(),
})
},
)(input)
}

View File

@@ -1,3 +1,218 @@
fn main() {
println!("Hello, world!");
mod assembly;
mod register;
use std::{fmt::Display, fs, path::Path};
use clap::Parser;
use register::{ByteRegisterSubset, Register, RegisterSubset};
#[derive(Eq, PartialEq)]
pub struct MoveInstruction {
source: Register,
dest: Register,
}
#[derive(Eq, PartialEq)]
pub enum Instruction {
Move(MoveInstruction),
}
impl Display for Instruction {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Instruction::Move(mov) => f.write_fmt(format_args!("mov {}, {}", mov.dest, mov.source)),
}
}
}
impl Instruction {
fn to_bytes(&self) -> Vec<u8> {
match self {
Instruction::Move(mov) => {
let mut result = Vec::with_capacity(2);
let instruction1 = 0b10001000u8;
let mut is_wide = 1u8;
// We always pick the 0 direction, so REG indicates the source.
let d: u8 = 0;
// Register-to-register move
let mode = 0b11000000u8;
match (&mov.dest, &mov.source) {
(
Register::General(dest, RegisterSubset::Subset(dest_subset)),
Register::General(source, RegisterSubset::Subset(source_subset)),
) => {
is_wide = 0;
result.push(instruction1 + 2 * d + is_wide);
let dest_offset: u8 = 4 * match dest_subset {
ByteRegisterSubset::Low => 0,
ByteRegisterSubset::High => 1,
};
let rm: u8 = dest_offset + dest.to_id();
let source_offset: u8 = 4 * match source_subset {
ByteRegisterSubset::Low => 0,
ByteRegisterSubset::High => 1,
};
let reg: u8 = source_offset + source.to_id();
result.push(mode + reg * 8 + rm);
}
(
Register::General(dest, RegisterSubset::All),
Register::General(source, RegisterSubset::All),
) => {
result.push(instruction1 + 2 * d + is_wide);
let reg = source.to_id();
let rm = dest.to_id();
result.push(mode + reg * 8 + rm);
}
(Register::General(dest, RegisterSubset::All), Register::Special(source)) => {
result.push(instruction1 + 2 * d + is_wide);
let reg = source.to_id();
let rm = dest.to_id();
result.push(mode + reg * 8 + rm);
}
(Register::Special(dest), Register::General(source, RegisterSubset::All)) => {
result.push(instruction1 + 2 * d + is_wide);
let reg = source.to_id();
let rm = dest.to_id();
result.push(mode + reg * 8 + rm);
}
(Register::Special(dest), Register::Special(source)) => {
result.push(instruction1 + 2 * d + is_wide);
let reg = source.to_id();
let rm = dest.to_id();
result.push(mode + reg * 8 + rm);
}
(
Register::General(_, RegisterSubset::Subset(_)),
Register::General(_, RegisterSubset::All),
) => {
panic!("tried to move wide into narrow")
}
(Register::General(_, RegisterSubset::Subset(_)), Register::Special(_)) => {
panic!("tried to move wide into narrow")
}
(Register::Special(_), Register::General(_, RegisterSubset::Subset(_))) => {
panic!("tried to move narrow into wide")
}
(
Register::General(_, RegisterSubset::All),
Register::General(_, RegisterSubset::Subset(_)),
) => {
panic!("tried to move narrow into wide")
}
}
result
}
}
}
}
pub struct Program<T>
where
T: AsRef<[Instruction]>,
{
bits: u8,
instructions: T,
}
impl<T> Display for Program<T>
where
T: AsRef<[Instruction]>,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_fmt(format_args!("bits {}\n", self.bits))?;
for i in self.instructions.as_ref().iter() {
f.write_fmt(format_args!("{}\n", i))?;
}
std::fmt::Result::Ok(())
}
}
impl<T> Program<T>
where
T: AsRef<[Instruction]>,
{
pub fn to_bytes(&self) -> Vec<u8> {
if self.bits != 16 {
panic!("Only 16-bits supported");
}
self.instructions
.as_ref()
.iter()
.flat_map(Instruction::to_bytes)
.collect()
}
}
fn load_machine_code<P>(path: P) -> Vec<u8>
where
P: AsRef<Path>,
{
fs::read(path).unwrap()
}
#[derive(Parser)]
struct Args {
#[arg(value_name = "COMPILED_PATH")]
compiled_path: std::path::PathBuf,
#[arg(value_name = "ASM_PATH")]
asm_path: std::path::PathBuf,
}
fn main() {
let args = Args::parse();
let expected_bytecode = load_machine_code(args.compiled_path);
let asm = fs::read_to_string(args.asm_path).unwrap();
let (remaining, compiled) = assembly::program(&asm).unwrap();
if !remaining.is_empty() {
println!(
"Failed to parse, as there was remaining code:\n{}",
remaining
);
std::process::exit(2)
}
let actual_bytecode = compiled.to_bytes();
if expected_bytecode != actual_bytecode {
println!(
"Expected: {:?}\nActual: {:?}",
expected_bytecode, actual_bytecode
);
std::process::exit(1)
}
}
#[cfg(test)]
mod test_program {
use super::assembly::program;
#[test]
fn test_parser() {
let input_asm = include_str!(
"../computer_enhance/perfaware/part1/listing_0037_single_register_mov.asm"
);
let input_bytecode =
include_bytes!("../computer_enhance/perfaware/part1/listing_0037_single_register_mov");
let (remaining, parsed) = program(input_asm).unwrap();
assert_eq!(remaining, "");
assert_eq!(parsed.bits, 16);
for (i, (actual, expected)) in parsed
.to_bytes()
.iter()
.zip(input_bytecode.iter())
.enumerate()
{
if actual != expected {
panic!(
"Failed assertion: expected {}, got {}, at position {}",
expected, actual, i
)
}
}
}
}

99
src/register.rs Normal file
View File

@@ -0,0 +1,99 @@
use std::fmt::{Display, Write};
#[derive(Eq, PartialEq)]
pub enum GeneralRegister {
A,
B,
C,
D,
}
impl GeneralRegister {
pub fn to_id(&self) -> u8 {
match self {
GeneralRegister::A => 0b00,
GeneralRegister::B => 0b11,
GeneralRegister::C => 0b01,
GeneralRegister::D => 0b10,
}
}
}
impl Display for GeneralRegister {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
GeneralRegister::A => f.write_char('a'),
GeneralRegister::B => f.write_char('b'),
GeneralRegister::C => f.write_char('c'),
GeneralRegister::D => f.write_char('d'),
}
}
}
#[derive(Eq, PartialEq)]
pub enum SpecialRegister {
StackPointer,
BasePointer,
SourceIndex,
DestIndex,
}
impl SpecialRegister {
pub fn to_id(&self) -> u8 {
// These are all wide.
4 + match self {
SpecialRegister::StackPointer => 0,
SpecialRegister::BasePointer => 1,
SpecialRegister::SourceIndex => 2,
SpecialRegister::DestIndex => 3,
}
}
}
impl Display for SpecialRegister {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
SpecialRegister::StackPointer => f.write_str("sp"),
SpecialRegister::BasePointer => f.write_str("bp"),
SpecialRegister::SourceIndex => f.write_str("si"),
SpecialRegister::DestIndex => f.write_str("di"),
}
}
}
#[derive(Eq, PartialEq)]
pub enum ByteRegisterSubset {
High,
Low,
}
#[derive(Eq, PartialEq)]
pub enum RegisterSubset {
All,
Subset(ByteRegisterSubset),
}
impl Display for RegisterSubset {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
RegisterSubset::All => f.write_char('x'),
RegisterSubset::Subset(ByteRegisterSubset::Low) => f.write_char('l'),
RegisterSubset::Subset(ByteRegisterSubset::High) => f.write_char('h'),
}
}
}
#[derive(Eq, PartialEq)]
pub enum Register {
General(GeneralRegister, RegisterSubset),
Special(SpecialRegister),
}
impl Display for Register {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Register::General(r, subset) => f.write_fmt(format_args!("{}{}", r, subset)),
Register::Special(special) => f.write_fmt(format_args!("{}", special)),
}
}
}