diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..434b2c2 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.rs text=auto diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index f50de2f..8a4706d 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -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" } ] } diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..a6a88d2 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "computer_enhance"] + path = computer_enhance + url = https://github.com/cmuratori/computer_enhance diff --git a/Cargo.lock b/Cargo.lock index 5f697fb..b819e56 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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" diff --git a/Cargo.toml b/Cargo.toml index 2fde269..7d90745 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" diff --git a/README.md b/README.md new file mode 100644 index 0000000..f8ab7f2 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# Performance-Aware Programming coursework + +Me running through [Performance-Aware Programming](https://www.computerenhance.com) in Rust. diff --git a/computer_enhance b/computer_enhance new file mode 160000 index 0000000..00cc753 --- /dev/null +++ b/computer_enhance @@ -0,0 +1 @@ +Subproject commit 00cc753c91f32649b0097657ba10a288b01e754d diff --git a/src/assembly.rs b/src/assembly.rs new file mode 100644 index 0000000..802d65b --- /dev/null +++ b/src/assembly.rs @@ -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>> { + 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) +} diff --git a/src/main.rs b/src/main.rs index e7a11a9..6650c5a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -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 { + 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 +where + T: AsRef<[Instruction]>, +{ + bits: u8, + instructions: T, +} + +impl Display for Program +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 Program +where + T: AsRef<[Instruction]>, +{ + pub fn to_bytes(&self) -> Vec { + if self.bits != 16 { + panic!("Only 16-bits supported"); + } + self.instructions + .as_ref() + .iter() + .flat_map(Instruction::to_bytes) + .collect() + } +} + +fn load_machine_code

(path: P) -> Vec +where + P: AsRef, +{ + 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 + ) + } + } + } } diff --git a/src/register.rs b/src/register.rs new file mode 100644 index 0000000..1d56bbe --- /dev/null +++ b/src/register.rs @@ -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)), + } + } +}