diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 7998a30d5..059b24387 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -7,7 +7,7 @@ on: workflow_call: env: - PROJECT_TRUNK_VERSION: 'v0.16.0' + PROJECT_TRUNK_VERSION: 'v0.18.8' jobs: check: @@ -37,7 +37,7 @@ jobs: - name: Build run: trunk build if: success() || failure() - + - name: Check documentation run: cargo doc --no-deps if: success() || failure() diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index bce49114a..8bf35783f 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -34,6 +34,9 @@ jobs: uses: jetli/trunk-action@v0.1.0 with: version: ${{ env.PROJECT_TRUNK_VERSION }} + + - name: Install Tailwind CSS + run: npm install -g tailwindcss - name: Build run: trunk build --release --public-url /${{ github.event.repository.name }} diff --git a/Cargo.lock b/Cargo.lock index 353001015..4f7920416 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,21 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + [[package]] name = "akin" version = "0.4.0" @@ -16,9 +31,24 @@ checksum = "d301b3b94cb4b2f23d7917810addbbaff90738e0ca2be692bd027e70d7e0330c" [[package]] name = "autocfg" -version = "1.1.0" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" + +[[package]] +name = "backtrace" +version = "0.3.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" +dependencies = [ + "addr2line", + "cc", + "cfg-if 1.0.0", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] [[package]] name = "bincode" @@ -37,9 +67,27 @@ checksum = "cfa8873f51c92e232f9bac4065cddef41b714152812bfc5f7672ba16d6ef8cd9" [[package]] name = "bumpalo" -version = "3.12.0" +version = "3.15.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa" + +[[package]] +name = "bytes" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" + +[[package]] +name = "cc" +version = "1.0.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5" + +[[package]] +name = "cfg-if" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" [[package]] name = "cfg-if" @@ -53,27 +101,51 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "wasm-bindgen", ] +[[package]] +name = "console_log" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be8aed40e4edbf4d3b4431ab260b63fdc40f5780a4766824329ea0f1eefe3c0f" +dependencies = [ + "log", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + [[package]] name = "form_urlencoded" -version = "1.1.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" dependencies = [ "percent-encoding", ] [[package]] name = "futures" -version = "0.3.26" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13e2792b0ff0340399d58445b88fd9770e3489eff258a4cbc1523418f12abf84" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" dependencies = [ "futures-channel", "futures-core", + "futures-executor", "futures-io", "futures-sink", "futures-task", @@ -82,9 +154,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.26" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e5317663a9089767a1ec00a487df42e0ca174b61b4483213ac24448e4664df5" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" dependencies = [ "futures-core", "futures-sink", @@ -92,44 +164,55 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.26" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + +[[package]] +name = "futures-executor" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec90ff4d0fe1f57d600049061dc6bb68ed03c7d2fbd697274c41805dcb3f8608" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] [[package]] name = "futures-io" -version = "0.3.26" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfb8371b6fb2aeb2d280374607aeabfc99d95c72edfe51692e42d3d7f0d08531" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" [[package]] name = "futures-macro" -version = "0.3.26" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95a73af87da33b5acf53acfebdc339fe592ecf5357ac7c0a7734ab9d8c876a70" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.58", ] [[package]] name = "futures-sink" -version = "0.3.26" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f310820bb3e8cfd46c80db4d7fb8353e15dfff853a127158425f31e0be6c8364" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" [[package]] name = "futures-task" -version = "0.3.26" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcf79a1bf610b10f42aea489289c5a2c478a786509693b80cd39c44ccd936366" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" [[package]] name = "futures-util" -version = "0.3.26" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c1d6de3acfef38d2be4b1f543f553131788603495be83da675e180c8d6b7bd1" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" dependencies = [ "futures-channel", "futures-core", @@ -143,23 +226,61 @@ dependencies = [ "slab", ] +[[package]] +name = "getrandom" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" +dependencies = [ + "cfg-if 1.0.0", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "gimli" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" + +[[package]] +name = "gloo" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28999cda5ef6916ffd33fb4a7b87e1de633c47c0dc6d97905fee1cdaa142b94d" +dependencies = [ + "gloo-console 0.2.3", + "gloo-dialogs 0.1.1", + "gloo-events 0.1.2", + "gloo-file 0.2.3", + "gloo-history 0.1.5", + "gloo-net 0.3.1", + "gloo-render 0.1.1", + "gloo-storage 0.2.2", + "gloo-timers 0.2.6", + "gloo-utils 0.1.7", + "gloo-worker 0.2.1", +] + [[package]] name = "gloo" -version = "0.8.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a4bef6b277b3ab073253d4bca60761240cf8d6998f4bd142211957b69a61b20" +checksum = "cd35526c28cc55c1db77aed6296de58677dbab863b118483a27845631d870249" dependencies = [ - "gloo-console", - "gloo-dialogs", - "gloo-events", - "gloo-file", - "gloo-history", - "gloo-net", - "gloo-render", - "gloo-storage", - "gloo-timers", - "gloo-utils", - "gloo-worker", + "gloo-console 0.3.0", + "gloo-dialogs 0.2.0", + "gloo-events 0.2.0", + "gloo-file 0.3.0", + "gloo-history 0.2.2", + "gloo-net 0.4.0", + "gloo-render 0.2.0", + "gloo-storage 0.3.0", + "gloo-timers 0.3.0", + "gloo-utils 0.2.0", + "gloo-worker 0.4.0", ] [[package]] @@ -168,7 +289,20 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "82b7ce3c05debe147233596904981848862b068862e9ec3e34be446077190d3f" dependencies = [ - "gloo-utils", + "gloo-utils 0.1.7", + "js-sys", + "serde", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-console" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a17868f56b4a24f677b17c8cb69958385102fa879418052d60b50bc1727e261" +dependencies = [ + "gloo-utils 0.2.0", "js-sys", "serde", "wasm-bindgen", @@ -185,6 +319,16 @@ dependencies = [ "web-sys", ] +[[package]] +name = "gloo-dialogs" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf4748e10122b01435750ff530095b1217cf6546173459448b83913ebe7815df" +dependencies = [ + "wasm-bindgen", + "web-sys", +] + [[package]] name = "gloo-events" version = "0.1.2" @@ -195,6 +339,16 @@ dependencies = [ "web-sys", ] +[[package]] +name = "gloo-events" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27c26fb45f7c385ba980f5fa87ac677e363949e065a083722697ef1b2cc91e41" +dependencies = [ + "wasm-bindgen", + "web-sys", +] + [[package]] name = "gloo-file" version = "0.2.3" @@ -202,22 +356,51 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8d5564e570a38b43d78bdc063374a0c3098c4f0d64005b12f9bbe87e869b6d7" dependencies = [ "futures-channel", - "gloo-events", + "gloo-events 0.1.2", "js-sys", "wasm-bindgen", "web-sys", ] +[[package]] +name = "gloo-file" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97563d71863fb2824b2e974e754a81d19c4a7ec47b09ced8a0e6656b6d54bd1f" +dependencies = [ + "gloo-events 0.2.0", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-history" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85725d90bf0ed47063b3930ef28e863658a7905989e9929a8708aab74a1d5e7f" +dependencies = [ + "gloo-events 0.1.2", + "gloo-utils 0.1.7", + "serde", + "serde-wasm-bindgen 0.5.0", + "serde_urlencoded", + "thiserror", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "gloo-history" -version = "0.1.3" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd451019e0b7a2b8a7a7b23e74916601abf1135c54664e57ff71dcc26dfcdeb7" +checksum = "903f432be5ba34427eac5e16048ef65604a82061fe93789f2212afc73d8617d6" dependencies = [ - "gloo-events", - "gloo-utils", + "getrandom", + "gloo-events 0.2.0", + "gloo-utils 0.2.0", "serde", - "serde-wasm-bindgen", + "serde-wasm-bindgen 0.6.5", "serde_urlencoded", "thiserror", "wasm-bindgen", @@ -226,14 +409,36 @@ dependencies = [ [[package]] name = "gloo-net" -version = "0.2.6" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a66b4e3c7d9ed8d315fd6b97c8b1f74a7c6ecbbc2320e65ae7ed38b7068cc620" +dependencies = [ + "futures-channel", + "futures-core", + "futures-sink", + "gloo-utils 0.1.7", + "http", + "js-sys", + "pin-project", + "serde", + "serde_json", + "thiserror", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "gloo-net" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9902a044653b26b99f7e3693a42f171312d9be8b26b5697bd1e43ad1f8a35e10" +checksum = "8ac9e8288ae2c632fa9f8657ac70bfe38a1530f345282d7ba66a1f70b72b7dc4" dependencies = [ "futures-channel", "futures-core", "futures-sink", - "gloo-utils", + "gloo-utils 0.2.0", + "http", "js-sys", "pin-project", "serde", @@ -254,13 +459,38 @@ dependencies = [ "web-sys", ] +[[package]] +name = "gloo-render" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56008b6744713a8e8d98ac3dcb7d06543d5662358c9c805b4ce2167ad4649833" +dependencies = [ + "wasm-bindgen", + "web-sys", +] + [[package]] name = "gloo-storage" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d6ab60bf5dbfd6f0ed1f7843da31b41010515c745735c970e821945ca91e480" dependencies = [ - "gloo-utils", + "gloo-utils 0.1.7", + "js-sys", + "serde", + "serde_json", + "thiserror", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-storage" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbc8031e8c92758af912f9bc08fbbadd3c6f3cfcbf6b64cdf3d6a81f0139277a" +dependencies = [ + "gloo-utils 0.2.0", "js-sys", "serde", "serde_json", @@ -281,11 +511,34 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "gloo-timers" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbb143cf96099802033e0d4f4963b19fd2e0b728bcf076cd9cf7f6634f092994" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "gloo-utils" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8e8fc851e9c7b9852508bc6e3f690f452f474417e8545ec9857b7f7377036b5" +checksum = "037fcb07216cb3a30f7292bd0176b050b7b9a052ba830ef7d5d65f6dc64ba58e" +dependencies = [ + "js-sys", + "serde", + "serde_json", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-utils" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b5555354113b18c547c1d3a98fbf7fb32a9ff4f6fa112ce823a21641a0ba3aa" dependencies = [ "js-sys", "serde", @@ -302,21 +555,58 @@ checksum = "13471584da78061a28306d1359dd0178d8d6fc1c7c80e5e35d27260346e0516a" dependencies = [ "anymap2", "bincode", - "gloo-console", - "gloo-utils", + "gloo-console 0.2.3", + "gloo-utils 0.1.7", + "js-sys", + "serde", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "gloo-worker" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76495d3dd87de51da268fa3a593da118ab43eb7f8809e17eb38d3319b424e400" +dependencies = [ + "bincode", + "futures", + "gloo-utils 0.2.0", + "gloo-worker-macros", "js-sys", + "pinned", "serde", + "thiserror", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", ] +[[package]] +name = "gloo-worker-macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "956caa58d4857bc9941749d55e4bd3000032d8212762586fa5705632967140e7" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.58", +] + [[package]] name = "hashbrown" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +[[package]] +name = "hashbrown" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" + [[package]] name = "heck" version = "0.4.1" @@ -325,43 +615,99 @@ checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "hermit-abi" -version = "0.2.6" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "http" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" dependencies = [ - "libc", + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "implicit-clone" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfd6201e7c30ccb24773cac7efa6fec1e06189d414b7439ce756a481c8bfbf53" +dependencies = [ + "indexmap 1.9.3", ] [[package]] name = "implicit-clone" -version = "0.3.5" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8a9aa791c7b5a71b636b7a68207fdebf171ddfc593d9c8506ec4cbc527b6a84" +dependencies = [ + "implicit-clone-derive", + "indexmap 2.2.6", +] + +[[package]] +name = "implicit-clone-derive" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40fc102e70475c320b185cd18c1e48bba2d7210b63970a4d581ef903e4368ef7" +checksum = "9311685eb9a34808bbb0608ad2fcab9ae216266beca5848613e95553ac914e3b" dependencies = [ - "indexmap", + "quote", + "syn 2.0.58", ] [[package]] name = "indexmap" -version = "1.9.2" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", - "hashbrown", + "hashbrown 0.12.3", +] + +[[package]] +name = "indexmap" +version = "2.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +dependencies = [ + "equivalent", + "hashbrown 0.14.3", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if 1.0.0", + "js-sys", + "wasm-bindgen", + "web-sys", ] [[package]] name = "itoa" -version = "1.0.5" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "js-sys" -version = "0.3.61" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" dependencies = [ "wasm-bindgen", ] @@ -374,90 +720,105 @@ checksum = "db13adb97ab515a3691f56e4dbab09283d0b86cb45abd991d8634a9d6f501760" [[package]] name = "libc" -version = "0.2.139" +version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "log" -version = "0.4.17" +version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if", -] +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" [[package]] name = "memchr" -version = "2.5.0" +version = "2.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" + +[[package]] +name = "miniz_oxide" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" +dependencies = [ + "adler", +] [[package]] name = "monaco" version = "0.3.0" -source = "git+https://github.com/SWIM-ucf/rust-monaco?rev=c9586e4af77131a15daf53e91e1ad5161a5265e8#c9586e4af77131a15daf53e91e1ad5161a5265e8" +source = "git+https://github.com/SWIM-ucf/rust-monaco?rev=630610e915e3c9742001f0fbe6f90e115a9e31e0#630610e915e3c9742001f0fbe6f90e115a9e31e0" dependencies = [ "js-sys", "paste", "wasm-bindgen", "web-sys", - "yew", + "yew 0.20.0", ] [[package]] name = "num_cpus" -version = "1.15.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ "hermit-abi", "libc", ] +[[package]] +name = "object" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +dependencies = [ + "memchr", +] + [[package]] name = "once_cell" -version = "1.17.1" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "paste" -version = "1.0.11" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d01a5bd0424d00070b0098dd17ebca6f961a959dead1dbcbbbc1d1cd8d3deeba" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" [[package]] name = "percent-encoding" -version = "2.2.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pin-project" -version = "1.0.12" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc" +checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.0.12" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" +checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.58", ] [[package]] name = "pin-project-lite" -version = "0.2.9" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" [[package]] name = "pin-utils" @@ -478,12 +839,32 @@ dependencies = [ [[package]] name = "prettyplease" -version = "0.1.23" +version = "0.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8646e95016a7a6c4adea95bafa8a16baab64b583356217f2c85db4a39d9a86" +dependencies = [ + "proc-macro2", + "syn 1.0.109", +] + +[[package]] +name = "prettyplease" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e97e3215779627f01ee256d2fad52f3d95e8e1c11e9fc6fd08f7cd455d5d5c78" +checksum = "8d3928fb5db768cb86f891ff014f0144589297e3c6a1aba6ed7cecfdace270c7" dependencies = [ "proc-macro2", - "syn", + "syn 2.0.58", +] + +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit", ] [[package]] @@ -495,7 +876,7 @@ dependencies = [ "proc-macro-error-attr", "proc-macro2", "quote", - "syn", + "syn 1.0.109", "version_check", ] @@ -512,9 +893,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.51" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6" +checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" dependencies = [ "unicode-ident", ] @@ -526,7 +907,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "03b55e106e5791fa5a13abd13c85d6127312e8e09098059ca2bc9b03ca4cf488" dependencies = [ "futures", - "gloo", + "gloo 0.8.1", "num_cpus", "once_cell", "pin-project", @@ -538,39 +919,56 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.23" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" dependencies = [ "proc-macro2", ] +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + [[package]] name = "rustversion" -version = "1.0.11" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5583e89e108996506031660fe09baa5011b9dd0341b89029313006d1fb508d70" +checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" [[package]] name = "ryu" -version = "1.0.12" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" +checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" [[package]] name = "serde" -version = "1.0.152" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" +checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" dependencies = [ "serde_derive", ] [[package]] name = "serde-wasm-bindgen" -version = "0.4.5" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3b4c031cd0d9014307d82b8abf653c0290fbdaeb4c02d00c63cf52f728628bf" +checksum = "f3b143e2833c57ab9ad3ea280d21fd34e285a42837aeb0ee301f4f41890fa00e" +dependencies = [ + "js-sys", + "serde", + "wasm-bindgen", +] + +[[package]] +name = "serde-wasm-bindgen" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8302e169f0eddcc139c70f139d19d6467353af16f9fce27e8c30158036a1e16b" dependencies = [ "js-sys", "serde", @@ -579,20 +977,20 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.152" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" +checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.58", ] [[package]] name = "serde_json" -version = "1.0.93" +version = "1.0.115" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cad406b69c91885b5107daf2c29572f6c8cdb3c66826821e286c533490c0bc76" +checksum = "12dc5c46daa8e9fdf4f5e71b6cf9a53f2487da0e86e55808e2d35539666497dd" dependencies = [ "itoa", "ryu", @@ -613,9 +1011,9 @@ dependencies = [ [[package]] name = "slab" -version = "0.4.7" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" dependencies = [ "autocfg", ] @@ -636,7 +1034,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn", + "syn 1.0.109", ] [[package]] @@ -644,27 +1042,46 @@ name = "swim" version = "0.1.0" dependencies = [ "akin", - "gloo", - "gloo-console", - "gloo-events", - "gloo-utils", + "cfg-if 0.1.10", + "console_log", + "futures", + "gloo 0.8.1", + "gloo-console 0.2.3", + "gloo-events 0.1.2", + "gloo-utils 0.1.7", + "humantime", + "instant", "js-sys", "levenshtein", + "log", "monaco", + "serde", "strum", "strum_macros", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "yew", + "yew 0.20.0", + "yew-agent", "yew-hooks", ] [[package]] name = "syn" -version = "1.0.107" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" +checksum = "44cfb93f38070beee36b3fef7d4f5a16f27751d94b187b666a5cc5e9b0d30687" dependencies = [ "proc-macro2", "quote", @@ -673,53 +1090,68 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.38" +version = "1.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" +checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.38" +version = "1.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" +checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.58", ] [[package]] name = "tokio" -version = "1.25.0" +version = "1.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8e00990ebabbe4c14c08aca901caed183ecd5c09562a12c824bb53d3c3fd3af" +checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" dependencies = [ - "autocfg", + "backtrace", "pin-project-lite", - "windows-sys", ] [[package]] name = "tokio-stream" -version = "0.1.11" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d660770404473ccd7bc9f8b28494a811bc18542b915c0855c51e8f419d5223ce" +checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" dependencies = [ "futures-core", "pin-project-lite", "tokio", ] +[[package]] +name = "toml_datetime" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" + +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap 2.2.6", + "toml_datetime", + "winnow", +] + [[package]] name = "tracing" -version = "0.1.37" +version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ - "cfg-if", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -727,29 +1159,29 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.23" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.58", ] [[package]] name = "tracing-core" -version = "0.1.30" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ "once_cell", ] [[package]] name = "unicode-ident" -version = "1.0.6" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "version_check" @@ -757,38 +1189,44 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + [[package]] name = "wasm-bindgen" -version = "0.2.84" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.84" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn", + "syn 2.0.58", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.34" +version = "0.4.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454" +checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "js-sys", "wasm-bindgen", "web-sys", @@ -796,9 +1234,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.84" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -806,101 +1244,78 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.84" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.58", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.84" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" [[package]] name = "web-sys" -version = "0.3.61" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" +checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" dependencies = [ "js-sys", "wasm-bindgen", ] [[package]] -name = "windows-sys" -version = "0.42.0" +name = "winnow" +version = "0.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" 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", + "memchr", ] [[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" - -[[package]] -name = "windows_i686_gnu" -version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" - -[[package]] -name = "windows_i686_msvc" -version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.1" +name = "yew" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" +checksum = "5dbecfe44343b70cc2932c3eb445425969ae21754a8ab3a0966981c1cf7af1cc" +dependencies = [ + "console_error_panic_hook", + "futures", + "gloo 0.8.1", + "implicit-clone 0.3.9", + "indexmap 1.9.3", + "js-sys", + "prokio", + "rustversion", + "serde", + "slab", + "thiserror", + "tokio", + "tracing", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "yew-macro 0.20.0", +] [[package]] name = "yew" -version = "0.20.0" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dbecfe44343b70cc2932c3eb445425969ae21754a8ab3a0966981c1cf7af1cc" +checksum = "5f1a03f255c70c7aa3e9c62e15292f142ede0564123543c1cc0c7a4f31660cac" dependencies = [ "console_error_panic_hook", "futures", - "gloo", - "implicit-clone", - "indexmap", + "gloo 0.10.0", + "implicit-clone 0.4.9", + "indexmap 2.2.6", "js-sys", "prokio", "rustversion", @@ -912,7 +1327,32 @@ dependencies = [ "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "yew-macro", + "yew-macro 0.21.0", +] + +[[package]] +name = "yew-agent" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e27eaca61ea9d0e1a6589dce592283b0e62ced527d8a8b28447957295d588f5" +dependencies = [ + "futures", + "gloo-worker 0.4.0", + "serde", + "wasm-bindgen", + "yew 0.21.0", + "yew-agent-macro", +] + +[[package]] +name = "yew-agent-macro" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ad00f6c9436d25c9225ed0fd8eea27e6d2886c1387bf934afdf91e9131b8b77" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", ] [[package]] @@ -921,14 +1361,14 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "268e2367720311f19582235f5c021702d6be8ded13b7ee8dcacc71019d055d15" dependencies = [ - "gloo", + "gloo 0.8.1", "js-sys", "log", "serde", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "yew", + "yew 0.20.0", ] [[package]] @@ -939,9 +1379,24 @@ checksum = "b64c253c1d401f1ea868ca9988db63958cfa15a69f739101f338d6f05eea8301" dependencies = [ "boolinator", "once_cell", - "prettyplease", + "prettyplease 0.1.25", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "yew-macro" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02fd8ca5166d69e59f796500a2ce432ff751edecbbb308ca59fd3fe4d0343de2" +dependencies = [ + "boolinator", + "once_cell", + "prettyplease 0.2.17", "proc-macro-error", "proc-macro2", "quote", - "syn", + "syn 2.0.58", ] diff --git a/Cargo.toml b/Cargo.toml index c78e1ac3c..b5bcfe976 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ name = "swim" version = "0.1.0" edition = "2021" -rust-version = "1.67" +rust-version = "1.77" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -17,15 +17,25 @@ gloo-console = "0.2.3" gloo-events = "0.1.2" gloo-utils = "0.1.6" js-sys = "0.3.61" -monaco = { git = "https://github.com/SWIM-ucf/rust-monaco", rev = "c9586e4af77131a15daf53e91e1ad5161a5265e8", features = ["yew-components"] } +monaco = { git = "https://github.com/SWIM-ucf/rust-monaco", rev = "630610e915e3c9742001f0fbe6f90e115a9e31e0", features = ["yew-components"] } wasm-bindgen = "0.2.83" wasm-bindgen-futures = "0.4.33" -web-sys = {version = "0.3.60", features = ["CssStyleDeclaration", "Event", "HtmlCollection", "HtmlElement", "HtmlInputElement", "HtmlObjectElement", "SvgElement"]} +web-sys = {version = "0.3.60", features = ["CssStyleDeclaration", "Event", "HtmlCollection", "HtmlElement", "HtmlInputElement", "HtmlObjectElement", "SvgElement", "CanvasRenderingContext2d", "Document", "HtmlCanvasElement", "EventTarget", "InputEvent", "ScrollLogicalPosition", "ScrollIntoViewOptions"]} yew = {version = "0.20.0", features = ["csr"] } yew-hooks = "0.2.0" +yew-agent = "0.3.0" +serde = "1.0.193" +futures = "0.3.29" # Parser / Assembler levenshtein = "1.0.5" # Tests -akin = "0.4" \ No newline at end of file +akin = "0.4" + +# Logging +cfg-if = "0.1" +log = "0.4" +console_log = { version = "1", features = ["color"] } +humantime = "2.1.0" +instant = { version = "0.1.12" , features = ["wasm-bindgen"] } diff --git a/README.md b/README.md index 5bbe24caa..4ca1a95e1 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,22 @@ -# SWIM (Simple Web Interface for MIPS) +# SWIM (Simple Web Interface for eMulation) -This was originally developed by Kevin Cahalan, Jerrett Longworth, Huy Nguyen, Evan Raiford, and Jimmie Smith at UCF as a senior design project. +This was originally developed by Kevin Cahalan, Jerrett Longworth, Huy Nguyen, Evan Raiford, and Jimmie Smith at UCF as a senior design project. Version 2 was developed by Ryan Harding, Cay Henning, Cameron McDougal, and Brooks McKinley as their senior design project. -![Screenshot of Swim V1](media/swim-screenshot.png) +![Screenshot of Swim V1](media/swimv2-screenshot.png) + +A web-based emulator for MIPS64/RISC-V made for educational purposes. The user interface that provides the following features: -A web-based emulator for MIPS64 made for educational purposes. Its emulation core supports over 60 real instructions and 20 pseudo-instuctions and an user interface that provides the following features: - Step execute and execute code down to the individual [stages](https://en.wikipedia.org/wiki/Instruction_cycle) -- Upload files to SWIM and Copy code to the user's clipboard to be saved locally - - Note: For Chromium-based browsers on Mac, the user will have to manually copy-paste the code onto a text editor. This is done as followed: - 1. Click on the Editor window. - 2. Press `Cmd + A` to select all text. - 3. Press `Cmd + C` to copy the text on your clipboard. - 4. Press `Cmd + V` to paste the text in your text editor to save the code. +- Execution speed control and breakpoints +- Upload files to SWIM and Copy code to the user's clipboard to be saved locally - A register viewer that displays General Purpose and Floating Point registers with toggling to different views (decimal, binary, hexadecimal, float, double) + - A register editor that allows changing the values of registers while a program is executing - A console viewer to display errors and suggestions on fixing them + - Syscalls and IO to interact with the program through the console when running - A memory viewer to see the code compiled and updated as it executes + - A hex editor for the memory to change memory while a program executes +- Text/data segment viewer for viewing assembled code in memory +- Stack frame and stack segment viewer for tracking function calls in a program while it executes - A visualization of the datapath to see the individual parts that make up the general and floating-point coprocessors and the values inside each wire - Utilizes the [Monaco Editor](https://microsoft.github.io/monaco-editor/) code library to provide: - Syntax highlighting of our custom language @@ -22,7 +24,12 @@ A web-based emulator for MIPS64 made for educational purposes. Its emulation cor - Providing mouse hover information on instructions and errors - Expands the pseudo-instructions into their hardware equivalent upon assembling code -Supported Instructions: +All of this wholly developed with the [Rust](https://www.rust-lang.org/) language with the interface built with the [Yew](https://yew.rs/) framework which uses [WebAssembly](https://webassembly.org/) and JavaScript to house the emulation core and parser/assembler. + +## MIPS Support + +The following instructions are supported on the MIPS emulator core: + - Conventional Instructions: - add - addi @@ -115,7 +122,182 @@ Supported Instructions: - subi - sw `(followed by a label)` -Supported .data directives: +## RISC-V Support + +The RISC-V core supports the RV32I, RV64I, RV32M, RV64M, RV32F, and RV64F extensions. The following instructions are supported in the RISC-V core: + +- RV32I: + - lui + - auipc + - addi + - slti + - xori + - ori + - andi + - slli + - srli + - srai + - add + - sub + - sll + - slt + - sltu + - cor + - srl + - sra + - or + - and + - fence + - fence.i + - csrrw + - csrrs + - csrrc + - csrrwi + - csrrsi + - csrrci + - ecall + - ebreak + - uret + - sret + - mret + - wfi + - sfence.vma + - lb + - lh + - lw + - lbu + - lhu + - sb + - sh + - sw + - jal + - jalr + - beq + - bne + - blt + - bge + - bltu + - bgeu +- RV64I: + - addiw + - slliw + - srliw + - addw + - subw + - sllw + - srlw + - sraw + - lwu + - ld + - sd +- RV32M: + - mul + - mulh + - mulhsu + - mulhu + - div + - divu + - rem + - remu +- RV64M: + - mulw + - divw + - divuw + - remw + - remuw +- RV32F: + - fmadd.s + - fmsub.s + - fnmsub.s + - fnmadd.s + - fadd.s + - fsub.s + - fmul.s + - fdiv.s + - fsqrt.s + - fsgnj.s + - fsgnjn.s + - fsgnjx.s + - fmin.s + - fmax.s + - fcvt.w.s + - fcvt.wu.s + - fmv.x.w + - feq.s + - flt.s + - fle.s + - fclass.s + - fcvt.s.w + - fcvt.s.wu + - fmv.w.x + - fmadd.d + - fmsub.d + - fnmadd.d + - fnmsub.d + - fadd.d + - fsub.d + - fmul.d + - fdiv.d + - fsqrt.d + - fsgnj.d + - fsgnjn.d + - fsgnjx.d + - fmin.d + - fmax.d + - fcvt.s.d + - fcvt.d.s + - feq.d + - flt.d + - fle.d + - fclass.d + - fcvt.w.d + - fcvt.wu.d + - fcvt.d.w + - fcvt.d.wu + - flw + - fsw + - fld + - fsd +- RV64F: + - fcvt.l.s + - fcvt.lu.s + - fcvt.s.l + - fcvt.s.lu +- Pseudo-instructions: + - li + - call + - tail + - mv + - not + - neg + - negw + - sext.w + - seqz + - snez + - sltz + - sgtz + - beqz + - bnez + - blez + - bgez + - bltz + - bgtz + - bgt + - ble + - bgtu + - bleu + - j + - jr + - jalr + - ret + - fmv.s + - fabs.s + - fneg.s + +## Data Directives + +The following directives are supported in the .data segment: + - .ascii - .asciiz - .byte @@ -125,7 +307,38 @@ Supported .data directives: - .space - .word -All of this wholly developed with the [Rust](https://www.rust-lang.org/) language with the interface built with the [Yew](https://yew.rs/) framework which uses [WebAssembly](https://webassembly.org/) and JavaScript to house the emulation core and parser/assembler. +## System Calls and IO + +SWIM supports IO through the console. Upon executing the `syscall` instruction in MIPS or the `ecall` instruction in RISC-V, SWIM will attempt to perform a syscall based on the values of the argument registers. + +In both MIPS and RISC-V, the call number is extracted from the `a0` register, and the integer/memory address argument are extracted from the `a1` register. For floating point arguments, the value is extracted from the `f0` register for MIPS and `f10` for RISC-V. + +For return values, in MIPS, the return value for a syscall is placed in the `v0` register while in RISC-V it's placed in the `a1` register. For floating point return values, the result is returned to `f0` for MIPS and `f10` for RISC-V. + +The following table shows a list of the supported syscalls and their arguments. + +| Name | Description | Call Number | Argument Type | Return Type | +|--------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------|----------------|-------------| +| exit | Halts the emulator core | 0 | None | None | +| print_int | Prints the integer value in the argument register | 1 | Integer | None | +| print_float | Prints the float value in the argument register | 2 | Float | None | +| print_double | Prints the double value in the argument register | 3 | Double | None | +| print_string | Prints a string to console, starting at the memory address in the register and ending at a null byte | 4 | Memory Address | None | +| read_int | Reads the next int from the console and stores it in the argument register | 5 | None | Integer | +| read_float | Reads the next float from the console and stores it in the argument register | 6 | None | Float | +| read_double | Reads the next double from the console and stores it in the argument register | 7 | None | Double | +| read_string | Reads from the console until a newline character is encountered and stores it in the provided memory address with a null terminator. Returns the number of bytes read. | 8 | Memory Address | Integer | + +Each of the syscall names are also pseudo-instructions that translate into the instructions to set the `a0` register to the correct value and perform the syscall instruction. + +As an example, the following RISC-V assembly reads an integer typed by the user, doubles it, and then prints it out. + +``` +read_int +add a1, a1, a1 +print_int +exit +``` ## Compiling diff --git a/index.html b/index.html index 7d62cbe60..9fd7457b5 100644 --- a/index.html +++ b/index.html @@ -2,19 +2,23 @@ + + - - + + + SWIM diff --git a/media/datapath-full.drawio b/media/datapath-full.drawio index 9b9e89f2e..2f6572d75 100644 --- a/media/datapath-full.drawio +++ b/media/datapath-full.drawio @@ -1,9 +1,24 @@ - + - + + + + + + + + + + + + + + + + @@ -20,7 +35,7 @@ - + @@ -110,7 +125,7 @@ - + @@ -174,7 +189,7 @@ - + @@ -182,7 +197,7 @@ - + @@ -190,7 +205,7 @@ - + @@ -207,7 +222,7 @@ - + @@ -217,16 +232,11 @@ - - - - - - + @@ -235,7 +245,7 @@ - + @@ -243,7 +253,7 @@ - + @@ -252,7 +262,7 @@ - + @@ -263,7 +273,7 @@ - + @@ -283,7 +293,7 @@ - + @@ -291,7 +301,7 @@ - + @@ -299,7 +309,7 @@ - + @@ -319,7 +329,7 @@ - + @@ -327,7 +337,7 @@ - + @@ -338,7 +348,7 @@ - + @@ -358,13 +368,8 @@ - - - - - - + @@ -372,7 +377,7 @@ - + @@ -383,7 +388,7 @@ - + @@ -398,7 +403,7 @@ - + @@ -409,7 +414,7 @@ - + @@ -431,7 +436,7 @@ - + @@ -439,7 +444,7 @@ - + @@ -478,7 +483,7 @@ - + @@ -499,7 +504,7 @@ - + @@ -605,7 +610,7 @@ - + @@ -616,7 +621,7 @@ - + @@ -640,7 +645,7 @@ - + @@ -654,7 +659,7 @@ - + @@ -672,7 +677,7 @@ - + @@ -700,7 +705,7 @@ - + @@ -708,7 +713,7 @@ - + @@ -793,7 +798,7 @@ - + @@ -808,7 +813,7 @@ - + @@ -816,7 +821,7 @@ - + @@ -826,7 +831,7 @@ - + @@ -834,7 +839,7 @@ - + @@ -849,7 +854,7 @@ - + @@ -865,7 +870,7 @@ - + @@ -960,7 +965,7 @@ - + @@ -973,12 +978,12 @@ - + - + @@ -995,7 +1000,7 @@ - + @@ -1005,7 +1010,7 @@ - + @@ -1018,7 +1023,7 @@ - + @@ -1031,7 +1036,7 @@ - + @@ -1059,7 +1064,7 @@ - + @@ -1075,7 +1080,7 @@ - + @@ -1095,7 +1100,7 @@ - + @@ -1110,7 +1115,7 @@ - + @@ -1118,7 +1123,7 @@ - + @@ -1134,7 +1139,7 @@ - + @@ -1156,7 +1161,7 @@ - + @@ -1185,7 +1190,7 @@ - + @@ -1193,7 +1198,7 @@ - + @@ -1209,7 +1214,7 @@ - + @@ -1218,7 +1223,7 @@ - + @@ -1246,7 +1251,7 @@ - + @@ -1262,7 +1267,7 @@ - + @@ -1274,7 +1279,7 @@ - + @@ -1309,7 +1314,7 @@ - + @@ -1317,7 +1322,7 @@ - + @@ -1334,7 +1339,7 @@ - + @@ -1347,7 +1352,7 @@ - + @@ -1355,7 +1360,7 @@ - + @@ -1369,7 +1374,7 @@ - + @@ -1397,7 +1402,7 @@ - + @@ -1412,7 +1417,7 @@ - + @@ -1420,7 +1425,7 @@ - + @@ -1433,7 +1438,7 @@ - + @@ -1447,7 +1452,7 @@ - + @@ -1455,7 +1460,7 @@ - + @@ -1472,7 +1477,7 @@ - + @@ -1516,7 +1521,7 @@ - + @@ -1532,7 +1537,7 @@ - + @@ -1546,7 +1551,7 @@ - + @@ -1563,7 +1568,7 @@ - + @@ -1575,7 +1580,7 @@ - + @@ -1589,7 +1594,7 @@ - + @@ -1604,7 +1609,7 @@ - + @@ -1621,7 +1626,7 @@ - + @@ -1663,7 +1668,7 @@ - + @@ -1679,7 +1684,7 @@ - + @@ -1795,7 +1800,7 @@ - + @@ -1809,7 +1814,7 @@ - + @@ -1817,7 +1822,7 @@ - + @@ -1879,7 +1884,7 @@ - + @@ -1915,7 +1920,7 @@ - + @@ -1927,22 +1932,17 @@ - + - + - - - - - - + @@ -1960,7 +1960,7 @@ - + @@ -1971,7 +1971,7 @@ - + @@ -1990,7 +1990,7 @@ - + @@ -2006,7 +2006,7 @@ - + @@ -2024,7 +2024,7 @@ - + diff --git a/media/datapath-riscv.drawio b/media/datapath-riscv.drawio new file mode 100644 index 000000000..adaa32fc0 --- /dev/null +++ b/media/datapath-riscv.drawio @@ -0,0 +1,1097 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/media/datapath-simple.drawio b/media/datapath-simple.drawio index ca5307789..dc0364d1f 100644 --- a/media/datapath-simple.drawio +++ b/media/datapath-simple.drawio @@ -1,9 +1,19 @@ - + - + + + + + + + + + + + @@ -47,7 +57,7 @@ - + @@ -80,13 +90,16 @@ + + + - + @@ -94,7 +107,7 @@ - + @@ -102,7 +115,7 @@ - + @@ -119,7 +132,7 @@ - + @@ -142,7 +155,7 @@ - + @@ -161,7 +174,7 @@ - + @@ -175,7 +188,7 @@ - + @@ -183,7 +196,7 @@ - + @@ -196,7 +209,7 @@ - + @@ -207,7 +220,7 @@ - + @@ -225,7 +238,7 @@ - + @@ -235,7 +248,7 @@ - + @@ -261,13 +274,8 @@ - - - - - - + @@ -275,7 +283,7 @@ - + @@ -287,7 +295,7 @@ - + @@ -302,7 +310,7 @@ - + @@ -313,7 +321,7 @@ - + @@ -335,7 +343,7 @@ - + @@ -343,7 +351,7 @@ - + @@ -364,7 +372,7 @@ - + @@ -385,7 +393,7 @@ - + @@ -486,7 +494,7 @@ - + @@ -508,7 +516,7 @@ - + @@ -522,7 +530,7 @@ - + @@ -539,7 +547,7 @@ - + @@ -569,7 +577,7 @@ - + @@ -577,7 +585,7 @@ - + @@ -632,7 +640,7 @@ - + @@ -645,7 +653,7 @@ - + @@ -659,7 +667,7 @@ - + @@ -673,7 +681,7 @@ - + @@ -689,7 +697,7 @@ - + @@ -701,7 +709,7 @@ - + @@ -715,7 +723,7 @@ - + @@ -726,7 +734,7 @@ - + @@ -777,7 +785,7 @@ - + @@ -824,7 +832,7 @@ - + @@ -836,7 +844,7 @@ - + @@ -862,13 +870,8 @@ - - - - - - + @@ -884,7 +887,7 @@ - + diff --git a/media/swimv2-screenshot.png b/media/swimv2-screenshot.png new file mode 100644 index 000000000..36f6ce26d Binary files /dev/null and b/media/swimv2-screenshot.png differ diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 2d2656bde..35dd0253c 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,5 +1,5 @@ [toolchain] -channel = "1.67" +channel = "1.77" components = [ "rustc", "cargo", "rustfmt", "rust-std", "rust-docs", "clippy" ] targets = [ "wasm32-unknown-unknown" ] profile = "default" \ No newline at end of file diff --git a/src/agent.rs b/src/agent.rs new file mode 100644 index 000000000..eac63e229 --- /dev/null +++ b/src/agent.rs @@ -0,0 +1,539 @@ +//! The agent responsible for running the emulator core on the worker thread and communication functionalities. + +use crate::agent::messages::MipsStateUpdate; +use crate::agent::messages::{Command, RiscStateUpdate, SystemUpdate}; +use crate::agent::system_scanner::Scanner; +use crate::emulation_core::architectures::{AvailableDatapaths, DatapathRef}; +use crate::emulation_core::datapath::{Datapath, DatapathUpdateSignal, Syscall, UPDATE_EVERYTHING}; +use crate::emulation_core::mips::datapath::MipsDatapath; +use crate::emulation_core::riscv::datapath::RiscDatapath; +use futures::{FutureExt, SinkExt, StreamExt}; +use instant::Instant; +use messages::DatapathUpdate; +use std::collections::HashSet; +use std::time::Duration; +use yew::platform::time::sleep; +use yew_agent::prelude::*; + +pub mod datapath_communicator; +pub mod datapath_reducer; +pub mod messages; +pub mod system_scanner; + +macro_rules! send_update { + ($scope:expr, $condition:expr, $value:expr) => { + if $condition { + $scope + .send($value) + .await + .expect("ReactorScope's send() function should not fail.") + } + }; +} + +macro_rules! send_update_mips { + ($scope:expr, $cond:expr, $data:expr) => { + send_update!($scope, $cond, DatapathUpdate::MIPS($data)) + }; +} + +macro_rules! send_update_riscv { + ($scope:expr, $cond:expr, $data:expr) => { + send_update!($scope, $cond, DatapathUpdate::RISCV($data)) + }; +} + +const UPDATE_INTERVAL: Duration = Duration::from_millis(250); + +/// The main logic for the emulation core agent. All code within this function runs on a worker thread as opposed to +/// the UI thread. +#[reactor(EmulationCoreAgent)] +pub async fn emulation_core_agent(scope: ReactorScope) { + let mut state = EmulatorCoreAgentState::new(scope); + loop { + let execution_delay = state.get_delay(); + + // Save the previous state of the emulator core's execution and initialization status + let is_executing = state.executing; + let is_initialized = state.initialized; + let curr_speed = state.speed; + + // Part 1: Delay/Command Handling + if state.executing { + futures::select! { + // If we get a message, handle the command before attempting to execute. + msg = state.scope.next() => match msg { + Some(msg) => state.handle_command(msg).await, + None => return, + }, + // Delay to slow execution down to the intended speed. + _ = sleep(Duration::from_millis(execution_delay)).fuse() => {}, + } + } else { + // If we're not currently executing, wait indefinitely until the next message comes in. + match state.scope.next().await { + Some(msg) => state.handle_command(msg).await, + None => return, + } + } + + // Part 2: Execution + // Execute a single instruction if the emulator core should be executing. + state.execute(); + + // Part 3: Performing Syscalls + state.execute_syscall_stage().await; + + // Part 4: Processing State/Sending Updates to UI + if state.should_send_datapath_update() { + match state.current_datapath.as_datapath_ref() { + DatapathRef::MIPS(datapath) => { + // Stage always updates + send_update_mips!( + state.scope, + true, + MipsStateUpdate::UpdateStage(datapath.current_stage) + ); + + // Send all other updates based on the state.updates variable. + send_update_mips!( + state.scope, + state.updates.changed_state, + MipsStateUpdate::UpdateState(datapath.state.clone()) + ); + send_update_mips!( + state.scope, + state.updates.changed_registers, + MipsStateUpdate::UpdateRegisters(datapath.registers) + ); + send_update_mips!( + state.scope, + state.updates.changed_coprocessor_state, + MipsStateUpdate::UpdateCoprocessorState(datapath.coprocessor.state.clone()) + ); + send_update_mips!( + state.scope, + state.updates.changed_coprocessor_registers, + MipsStateUpdate::UpdateCoprocessorRegisters(datapath.coprocessor.registers) + ); + send_update_mips!( + state.scope, + state.updates.changed_memory, + MipsStateUpdate::UpdateMemory(datapath.memory.clone()) + ); + send_update_mips!( + state.scope, + state.updates.changed_stack, + MipsStateUpdate::UpdateStack(datapath.stack.clone()) + ); + } + DatapathRef::RISCV(datapath) => { + // Stage always updates + send_update_riscv!( + state.scope, + true, + RiscStateUpdate::UpdateStage(datapath.current_stage) + ); + + // Send all other updates based on the state.updates variable. + send_update_riscv!( + state.scope, + state.updates.changed_state, + RiscStateUpdate::UpdateState(datapath.state.clone()) + ); + send_update_riscv!( + state.scope, + state.updates.changed_registers, + RiscStateUpdate::UpdateRegisters(datapath.registers) + ); + send_update_riscv!( + state.scope, + state.updates.changed_memory, + RiscStateUpdate::UpdateMemory(datapath.memory.clone()) + ); + send_update_riscv!( + state.scope, + state.updates.changed_stack, + RiscStateUpdate::UpdateStack(datapath.stack.clone()) + ); + send_update_riscv!( + state.scope, + state.updates.changed_coprocessor_registers, + RiscStateUpdate::UpdateCoprocessorRegisters(datapath.coprocessor.registers) + ); + } + } + state.updates = Default::default(); + state.last_update = Some(Instant::now()); + } + + // Part 5: Sending Non-Syscall System Updates to UI + send_update!( + state.scope, + state.executing != is_executing, + DatapathUpdate::System(SystemUpdate::UpdateExecuting(state.executing)) + ); + send_update!( + state.scope, + state.initialized != is_initialized, + DatapathUpdate::System(SystemUpdate::UpdateInitialized(state.initialized)) + ); + send_update!( + state.scope, + state.speed != curr_speed, + DatapathUpdate::System(SystemUpdate::UpdateSpeed(state.speed)) + ); + } +} + +#[derive(Clone, PartialEq, Default)] +enum BlockedOn { + #[default] + Nothing, + Syscall(Syscall), +} + +struct EmulatorCoreAgentState { + current_datapath: Box>, + /// The changes to the emulator core's memory/registers/etc. are tracked in this variable. When + /// it's time to send updates back to the main thread, this variable determines which updates + /// get sent. + pub updates: DatapathUpdateSignal, + pub scope: ReactorScope, + speed: u32, + last_update: Option, + executing: bool, + initialized: bool, + messages: Vec, + scanner: Scanner, + blocked_on: BlockedOn, + breakpoints: HashSet, +} + +impl EmulatorCoreAgentState { + pub fn new(scope: ReactorScope) -> EmulatorCoreAgentState { + EmulatorCoreAgentState { + current_datapath: Box::::default(), + updates: DatapathUpdateSignal::default(), + scope, + speed: 0, + last_update: None, + executing: false, + initialized: false, + messages: Vec::new(), + scanner: Scanner::new(), + blocked_on: BlockedOn::Nothing, + breakpoints: HashSet::default(), + } + } + + pub async fn handle_command(&mut self, command: Command) { + match command { + Command::SetCore(architecture) => { + match architecture { + AvailableDatapaths::MIPS => { + self.current_datapath = Box::::default(); + } + AvailableDatapaths::RISCV => { + self.current_datapath = Box::::default(); + } + } + self.reset_system().await; + } + Command::Initialize(initial_pc, mem) => { + self.current_datapath.initialize(initial_pc, mem).unwrap(); + self.reset_system().await; + self.initialized = true; + } + Command::SetExecuteSpeed(speed) => { + self.speed = speed; + } + Command::SetRegister(register, value) => { + self.current_datapath.set_register_by_str(®ister, value); + self.updates.changed_registers = true; + } + Command::SetFPRegister(register, value) => { + self.current_datapath + .set_fp_register_by_str(®ister, value); + } + Command::SetMemory(ptr, data) => { + self.current_datapath.set_memory(ptr, data); + self.updates.changed_memory = true; + } + Command::Execute => { + self.executing = true; + } + Command::ExecuteInstruction => { + if self.blocked_on == BlockedOn::Nothing { + self.updates |= self.current_datapath.execute_instruction(); + } + } + Command::ExecuteStage => { + if self.blocked_on == BlockedOn::Nothing { + self.updates |= self.current_datapath.execute_stage(); + } + } + Command::Pause => { + self.executing = false; + } + Command::Reset => { + self.current_datapath.reset(); + self.reset_system().await; + } + Command::Input(line) => { + self.add_message(format!("> {}", line)).await; + self.scanner.feed(line); + } + Command::SetBreakpoint(address) => { + self.breakpoints.insert(address); + } + Command::RemoveBreakpoint(address) => { + self.breakpoints.remove(&address); + } + } + } + + pub fn execute(&mut self) { + // Skip the execution phase if the emulator core is not currently executing. + if !self.executing || matches!(self.blocked_on, BlockedOn::Syscall(_)) { + return; + } + + self.updates |= self.current_datapath.execute_instruction(); + + // Extract the current program counter and break if there's a breakpoint set here. + let current_pc = match self.current_datapath.as_datapath_ref() { + DatapathRef::MIPS(datapath) => datapath.registers.pc, + DatapathRef::RISCV(datapath) => datapath.registers.pc, + }; + if self.breakpoints.contains(¤t_pc) || self.updates.hit_breakpoint { + self.executing = false; + // Unset the hit_breakpoint flag after processing + self.updates.hit_breakpoint = false; + } + } + + /// Returns the delay between CPU cycles in milliseconds for the current execution speed. Will return zero if the + /// execution speed is zero. + pub fn get_delay(&self) -> u64 { + if self.speed == 0 { + 0 + } else { + (1000 / self.speed).into() + } + } + + pub async fn execute_syscall_stage(&mut self) { + if !self.updates.hit_syscall && !matches!(self.blocked_on, BlockedOn::Syscall(_)) { + return; + } + + // Determine if we should attempt to execute a new syscall or poll on a previous syscall + // the processor blocked on. + let syscall = match &self.blocked_on { + BlockedOn::Nothing => self.current_datapath.get_syscall_arguments(), + BlockedOn::Syscall(syscall) => syscall.clone(), + }; + + match syscall { + Syscall::Exit => { + self.current_datapath.halt(); + self.executing = false; + } + Syscall::PrintInt(val) => { + self.add_message(val.to_string()).await; + } + Syscall::PrintFloat(val) => { + self.add_message(val.to_string()).await; + } + Syscall::PrintDouble(val) => { + self.add_message(val.to_string()).await; + } + Syscall::PrintString(addr) => { + let memory = self.current_datapath.get_memory_mut(); + let mut buffer = Vec::new(); + 'outer: for i in 0.. { + let word = memory.load_word(addr + (i * 4)); + match word { + Ok(word) => { + for byte in word.to_be_bytes() { + if byte == 0 { + // Break on null terminator + break 'outer; + } else { + buffer.push(byte); + } + } + } + Err(_) => break, + } + } + + let message = String::from_utf8(buffer); + match message { + Ok(message) => self.add_message(message).await, + Err(_) => { + self.add_message("Error: Attempted to print invalid string".to_string()) + .await + } + } + } + Syscall::ReadInt => { + let scan_result = self.scanner.next_int(); + match scan_result { + None => { + self.blocked_on = BlockedOn::Syscall(syscall); + } + Some(scan_result) => { + self.blocked_on = BlockedOn::Nothing; + match self.current_datapath.as_datapath_ref() { + DatapathRef::MIPS(_) => { + self.current_datapath.set_register_by_str("v0", scan_result); + } + DatapathRef::RISCV(_) => { + self.current_datapath + .set_register_by_str("x11", scan_result); + } + } + self.updates.changed_registers = true; + } + } + } + Syscall::ReadFloat => { + let scan_result = self.scanner.next_float(); + match scan_result { + None => { + self.blocked_on = BlockedOn::Syscall(syscall); + } + Some(scan_result) => { + self.blocked_on = BlockedOn::Nothing; + match self.current_datapath.as_datapath_ref() { + DatapathRef::MIPS(_) => { + self.current_datapath + .set_fp_register_by_str("f0", f32::to_bits(scan_result) as u64); + } + DatapathRef::RISCV(_) => { + self.current_datapath.set_fp_register_by_str( + "f10", + f32::to_bits(scan_result) as u64, + ); + } + } + self.updates.changed_coprocessor_registers = true; + } + } + } + Syscall::ReadDouble => { + let scan_result = self.scanner.next_double(); + match scan_result { + None => { + self.blocked_on = BlockedOn::Syscall(syscall); + } + Some(scan_result) => { + self.blocked_on = BlockedOn::Nothing; + match self.current_datapath.as_datapath_ref() { + DatapathRef::MIPS(_) => { + self.current_datapath + .set_fp_register_by_str("f0", f64::to_bits(scan_result)); + } + DatapathRef::RISCV(_) => { + self.current_datapath + .set_fp_register_by_str("f10", f64::to_bits(scan_result)); + } + } + self.updates.changed_coprocessor_registers = true; + } + } + } + Syscall::ReadString(addr) => { + let scan_result = self.scanner.next_line(); + match scan_result { + None => { + self.blocked_on = BlockedOn::Syscall(syscall); + } + Some(scan_result) => { + self.blocked_on = BlockedOn::Nothing; + + let bytes = scan_result.as_bytes().to_vec(); + let memory = self.current_datapath.get_memory_mut(); + let mut failed_store = false; + for (i, chunk) in bytes.chunks(4).enumerate() { + // Attempt to store the byte in memory, but if the store process fails, + // end the syscall and return to normal operation. + let mut word = [0u8; 4]; + for (i, byte) in chunk.iter().enumerate() { + word[i] = *byte; + } + let result = + memory.store_word(addr + (4 * i as u64), u32::from_be_bytes(word)); + if result.is_err() { + failed_store = true; + break; + } + } + match self.current_datapath.as_datapath_ref() { + DatapathRef::MIPS(_) => { + if failed_store { + self.current_datapath.set_register_by_str("v0", 0); + } else { + self.current_datapath + .set_register_by_str("v0", bytes.len() as u64); + } + } + DatapathRef::RISCV(_) => { + if failed_store { + self.current_datapath.set_register_by_str("x11", 0); + } else { + self.current_datapath + .set_register_by_str("x11", bytes.len() as u64); + } + } + } + self.updates.changed_registers = true; + self.updates.changed_memory = true; + } + } + } + } + + // Now that the syscall is processed, unset the update signal + self.updates.hit_syscall = false; + } + + /// Determines of datapath updates should be sent. Datapath updates should be sent at most once + /// per second when executing as fast as possible. If the last cycle was executed using the + /// debug buttons or we're going at at a specific speed, always send an update. + pub fn should_send_datapath_update(&self) -> bool { + if self.executing && self.speed == 0 { + self.last_update.is_none() + || Instant::now().duration_since(self.last_update.unwrap()) > UPDATE_INTERVAL + } else { + true + } + } + + async fn reset_system(&mut self) { + self.scanner = Scanner::new(); + self.blocked_on = BlockedOn::Nothing; + self.initialized = false; + self.messages = Vec::new(); + self.scope + .send(DatapathUpdate::System(SystemUpdate::UpdateMessages( + self.messages.clone(), + ))) + .await + .unwrap(); + self.updates |= UPDATE_EVERYTHING; + self.breakpoints = HashSet::default(); + } + + async fn add_message(&mut self, msg: String) { + self.messages.push(msg); + self.scope + .send(DatapathUpdate::System(SystemUpdate::UpdateMessages( + self.messages.clone(), + ))) + .await + .unwrap(); + } +} diff --git a/src/agent/datapath_communicator.rs b/src/agent/datapath_communicator.rs new file mode 100644 index 000000000..091d277f6 --- /dev/null +++ b/src/agent/datapath_communicator.rs @@ -0,0 +1,159 @@ +use crate::agent::datapath_reducer::DatapathReducer; +use crate::agent::messages::Command; +use crate::agent::EmulationCoreAgent; +use crate::emulation_core::architectures::AvailableDatapaths; +use futures::stream::{SplitSink, SplitStream}; +use futures::FutureExt; +use futures::SinkExt; +use futures::StreamExt; +use gloo_console::warn; +use std::cell::RefCell; +use yew::UseReducerDispatcher; +use yew_agent::reactor::ReactorBridge; + +/// This struct provides an abstraction over all communication with the worker thread. Any commands to the worker +/// thread should be sent by calling a function on this struct. +/// +/// The DatapathCommunicator will also handle receiving information about the state of the emulation core and maintain +/// internal state that can be displayed by the UI. +pub struct DatapathCommunicator { + writer: RefCell, Command>>, + reader: RefCell>>, +} + +// Check references for equality by memory address. +impl PartialEq for &DatapathCommunicator { + fn eq(&self, other: &Self) -> bool { + let self_ptr: *const DatapathCommunicator = *self; + let other_ptr: *const DatapathCommunicator = *other; + self_ptr == other_ptr + } +} + +impl DatapathCommunicator { + // General operational functions + + /// Initialize the DatapathCommunicator using a bridge. + pub fn new(bridge: ReactorBridge) -> DatapathCommunicator { + let (write, read) = bridge.split(); + DatapathCommunicator { + writer: RefCell::new(write), + reader: RefCell::new(read), + } + } + + /// Listen for updates from the worker thread and update internal state accordingly. This function should be called + /// from the main app component. After updating internal state, the component this was called from will be force + /// updated. + #[allow(clippy::await_holding_refcell_ref)] + pub async fn listen_for_updates( + &self, + dispatcher_handle: UseReducerDispatcher, + ) { + let mut reader = match self.reader.try_borrow_mut() { + Ok(reader) => reader, + Err(_) => { + warn!("Warning: Attempted to listen for updates multiple times"); + return; + } + }; + + loop { + let update = reader.next().await; + match update { + None => return, + Some(update) => dispatcher_handle.dispatch(update), + } + } + } + + /// Sends a test message to the worker thread. + fn send_message(&self, command: Command) { + let mut writer = self.writer.borrow_mut(); + writer + .send(command) + // The logic for sending a message is synchronous but the API for writing to a SplitSink is asynchronous, + // so we attempt to resolve the future immediately, so we can expose a synchronous API for sending commands. + // If the future doesn't return immediately, there's serious logic changes that need to happen, so we just + // log an error message and panic. + .now_or_never() + .expect("Send function did not immediately return, async logic needed.") + .expect("Sending test message error") + } + + // Wrapper functions for commands + + /// Sets the current emulation core to the provided architecture. + pub fn set_core(&self, architecture: AvailableDatapaths) { + self.send_message(Command::SetCore(architecture)); + } + + /// Resets and loads the parsed/assembled instructions provided into the current emulator core. + pub fn initialize(&self, initial_pc: usize, instructions: Vec) { + self.send_message(Command::Initialize(initial_pc, instructions)); + } + + /// Sets the execution speed of the emulator core to the provided speed in hz. If set to zero, the emulator core + /// will execute as fast as possible. + pub fn set_execute_speed(&self, speed: u32) { + self.send_message(Command::SetExecuteSpeed(speed)); + } + + /// Sets the register with the provided name to the provided value. + pub fn set_register(&self, register: String, data: u64) { + self.send_message(Command::SetRegister(register, data)); + } + + // Sets the FP register with the provided name to the provided value. + pub fn set_fp_register(&self, register: String, data: u64) { + self.send_message(Command::SetFPRegister(register, data)); + } + + /// Copies the contents of `data` to the emulator core's memory at `ptr`. Copies until either the end of `data` or + /// the end of the emulator core's memory. + pub fn set_memory(&self, ptr: u64, data: u32) { + self.send_message(Command::SetMemory(ptr, data)); + } + + /// Sends a line of input to the emulator core, to be read using syscalls. + pub fn send_input(&self, line: String) { + self.send_message(Command::Input(line)); + } + + /// Executes the emulator core at the current set speed. + pub fn execute(&self) { + self.send_message(Command::Execute); + } + + /// Executes a single instruction on the emulator core and pauses. + pub fn execute_instruction(&self) { + self.send_message(Command::ExecuteInstruction); + } + + /// Executes a single stage on the emulator core and pauses. + pub fn execute_stage(&self) { + self.send_message(Command::ExecuteStage); + } + + /// Pauses the core. Does nothing if the emulator core is already paused. + pub fn pause_core(&self) { + self.send_message(Command::Pause); + } + + /// Resets the current core to its default state. + pub fn reset(&self) { + self.send_message(Command::Reset); + } + + pub fn set_breakpoint(&self, address: u64) { + self.send_message(Command::SetBreakpoint(address)); + } + + pub fn remove_breakpoint(&self, address: u64) { + self.send_message(Command::RemoveBreakpoint(address)); + } + + pub fn get_accepting_input(&self) -> bool { + todo!() + } +} diff --git a/src/agent/datapath_reducer.rs b/src/agent/datapath_reducer.rs new file mode 100644 index 000000000..e01d32f91 --- /dev/null +++ b/src/agent/datapath_reducer.rs @@ -0,0 +1,213 @@ +use crate::agent::messages::{DatapathUpdate, MipsStateUpdate, RiscStateUpdate, SystemUpdate}; +use crate::emulation_core::architectures::AvailableDatapaths; +use crate::emulation_core::architectures::AvailableDatapaths::{MIPS, RISCV}; +use crate::emulation_core::mips::coprocessor::FpuState; +use crate::emulation_core::mips::datapath::{DatapathState, Stage}; +use crate::emulation_core::mips::fp_registers::FpRegisters; +use crate::emulation_core::mips::gp_registers::{GpRegisterType, GpRegisters}; +use crate::emulation_core::mips::memory::Memory; +use crate::emulation_core::register::{RegisterType, Registers}; +use crate::emulation_core::riscv::datapath::{RiscDatapathState, RiscStage}; +use crate::emulation_core::riscv::registers::{ + RiscFpRegisters, RiscGpRegisterType, RiscGpRegisters, +}; +use crate::emulation_core::stack::Stack; +use std::rc::Rc; +use yew::Reducible; + +#[derive(PartialEq, Clone)] +pub struct DatapathReducer { + pub current_architecture: AvailableDatapaths, + pub mips: MipsCoreState, + pub riscv: RiscCoreState, + pub messages: Vec, + pub speed: u32, + pub executing: bool, + pub initialized: bool, +} + +#[derive(Default, PartialEq, Clone)] +pub struct MipsCoreState { + pub state: DatapathState, + pub registers: GpRegisters, + pub coprocessor_state: FpuState, + pub coprocessor_registers: FpRegisters, + pub memory: Memory, + pub current_stage: Stage, + pub stack: Stack, +} + +#[derive(Default, PartialEq, Clone)] +pub struct RiscCoreState { + pub state: RiscDatapathState, + pub registers: RiscGpRegisters, + pub coprocessor_registers: RiscFpRegisters, + pub memory: Memory, + pub current_stage: RiscStage, + pub stack: Stack, +} + +impl Default for DatapathReducer { + fn default() -> Self { + Self { + current_architecture: MIPS, + mips: MipsCoreState::default(), + riscv: RiscCoreState::default(), + messages: Vec::new(), + speed: 0, + executing: false, + initialized: false, + } + } +} + +impl Reducible for DatapathReducer { + type Action = DatapathUpdate; + + fn reduce(self: Rc, action: Self::Action) -> Rc { + Rc::from(match action { + DatapathUpdate::MIPS(update) => Self { + current_architecture: MIPS, + mips: match update { + MipsStateUpdate::UpdateState(state) => MipsCoreState { + state, + ..self.mips.clone() + }, + MipsStateUpdate::UpdateRegisters(registers) => MipsCoreState { + registers, + ..self.mips.clone() + }, + MipsStateUpdate::UpdateCoprocessorState(coprocessor_state) => MipsCoreState { + coprocessor_state, + ..self.mips.clone() + }, + MipsStateUpdate::UpdateCoprocessorRegisters(coprocessor_registers) => { + MipsCoreState { + coprocessor_registers, + ..self.mips.clone() + } + } + MipsStateUpdate::UpdateMemory(memory) => MipsCoreState { + memory, + ..self.mips.clone() + }, + MipsStateUpdate::UpdateStage(stage) => MipsCoreState { + current_stage: stage, + ..self.mips.clone() + }, + MipsStateUpdate::UpdateStack(stack) => MipsCoreState { + stack, + ..self.mips.clone() + }, + }, + ..(*self).clone() + }, + DatapathUpdate::System(update) => match update { + SystemUpdate::UpdateMessages(messages) => Self { + messages, + ..(*self).clone() + }, + SystemUpdate::UpdateExecuting(executing) => Self { + executing, + ..(*self).clone() + }, + SystemUpdate::UpdateInitialized(initialized) => Self { + initialized, + ..(*self).clone() + }, + SystemUpdate::UpdateSpeed(speed) => Self { + current_architecture: self.current_architecture, + mips: self.mips.clone(), + messages: self.messages.clone(), + speed, + executing: self.executing, + initialized: self.initialized, + riscv: self.riscv.clone(), + }, + }, + DatapathUpdate::RISCV(update) => Self { + current_architecture: RISCV, + riscv: match update { + RiscStateUpdate::UpdateState(state) => RiscCoreState { + state, + ..self.riscv.clone() + }, + RiscStateUpdate::UpdateRegisters(registers) => RiscCoreState { + registers, + ..self.riscv.clone() + }, + RiscStateUpdate::UpdateMemory(memory) => RiscCoreState { + memory, + ..self.riscv.clone() + }, + RiscStateUpdate::UpdateStage(current_stage) => RiscCoreState { + current_stage, + ..self.riscv.clone() + }, + RiscStateUpdate::UpdateStack(stack) => RiscCoreState { + stack, + ..self.riscv.clone() + }, + RiscStateUpdate::UpdateCoprocessorRegisters(coprocessor_registers) => { + RiscCoreState { + coprocessor_registers, + ..self.riscv.clone() + } + } + }, + ..(*self).clone() + }, + }) + } +} + +impl DatapathReducer { + pub fn get_pc(&self) -> u64 { + match self.current_architecture { + MIPS => self.mips.registers.pc, + RISCV => self.riscv.registers.pc, + } + } + + pub fn get_sp(&self) -> u64 { + match self.current_architecture { + MIPS => self.mips.registers[GpRegisterType::Sp], + RISCV => self.riscv.registers[RiscGpRegisterType::X2], + } + } + + pub fn get_dyn_gp_registers(&self) -> Vec<(Rc, u64)> { + match self.current_architecture { + MIPS => self.mips.registers.get_dyn_register_list(), + RISCV => self.riscv.registers.get_dyn_register_list(), + } + } + + pub fn get_dyn_fp_registers(&self) -> Vec<(Rc, u64)> { + match self.current_architecture { + MIPS => self.mips.coprocessor_registers.get_dyn_register_list(), + RISCV => self.riscv.coprocessor_registers.get_dyn_register_list(), + } + } + + pub fn get_memory(&self) -> &Memory { + match self.current_architecture { + MIPS => &self.mips.memory, + RISCV => &self.riscv.memory, + } + } + + pub fn get_current_stage(&self) -> String { + match self.current_architecture { + MIPS => self.mips.current_stage.into(), + RISCV => self.riscv.current_stage.into(), + } + } + + pub fn get_stack(&self) -> &Stack { + match self.current_architecture { + MIPS => &self.mips.stack, + RISCV => &self.riscv.stack, + } + } +} diff --git a/src/agent/messages.rs b/src/agent/messages.rs new file mode 100644 index 000000000..51a8dbbfd --- /dev/null +++ b/src/agent/messages.rs @@ -0,0 +1,69 @@ +use crate::emulation_core::mips::coprocessor::FpuState; +use crate::emulation_core::mips::datapath::DatapathState; +use crate::emulation_core::mips::fp_registers::FpRegisters; +use crate::emulation_core::mips::gp_registers::GpRegisters; +use crate::emulation_core::mips::memory::Memory; +use crate::emulation_core::riscv::datapath::{RiscDatapathState, RiscStage}; +use crate::emulation_core::riscv::registers::{RiscFpRegisters, RiscGpRegisters}; +use crate::emulation_core::stack::Stack; +use crate::emulation_core::{architectures::AvailableDatapaths, mips::datapath::Stage}; +use serde::{Deserialize, Serialize}; + +/// Commands sent from the UI thread to the worker thread. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub enum Command { + SetCore(AvailableDatapaths), + Initialize(usize, Vec), + SetExecuteSpeed(u32), + SetRegister(String, u64), + SetFPRegister(String, u64), + SetMemory(u64, u32), + Execute, + ExecuteInstruction, + ExecuteStage, + Pause, + Reset, + SetBreakpoint(u64), + RemoveBreakpoint(u64), + Input(String), +} + +/// Information about the emulator core's state sent from the worker thread to the UI thread. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub enum MipsStateUpdate { + UpdateState(DatapathState), + UpdateRegisters(GpRegisters), + UpdateCoprocessorState(FpuState), + UpdateCoprocessorRegisters(FpRegisters), + UpdateMemory(Memory), + UpdateStage(Stage), + UpdateStack(Stack), +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub enum RiscStateUpdate { + UpdateState(RiscDatapathState), + UpdateRegisters(RiscGpRegisters), + UpdateCoprocessorRegisters(RiscFpRegisters), + UpdateMemory(Memory), + UpdateStage(RiscStage), + UpdateStack(Stack), +} + +/// Information about the effects of system calls sent from the worker thread to the UI thread. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub enum SystemUpdate { + UpdateMessages(Vec), + UpdateExecuting(bool), + UpdateInitialized(bool), + UpdateSpeed(u32), +} + +/// Enum containing all types of updates sent from the worker thread to the UI thread. +#[allow(clippy::large_enum_variant)] // Temporary allow while the System enum is tiny +#[derive(Clone, Debug, Serialize, Deserialize)] +pub enum DatapathUpdate { + MIPS(MipsStateUpdate), + RISCV(RiscStateUpdate), + System(SystemUpdate), +} diff --git a/src/agent/system_scanner.rs b/src/agent/system_scanner.rs new file mode 100644 index 000000000..e9a68e389 --- /dev/null +++ b/src/agent/system_scanner.rs @@ -0,0 +1,185 @@ +use std::collections::VecDeque; + +#[derive(Default)] +pub struct Scanner { + input: VecDeque, +} + +enum ScannerState { + /// State when the Scanner is waiting for the first matching character + Waiting, + /// State when the Scanner is reading the integer component of a decimal number or just an + /// integer. + ReadingInt, + /// State when the Scanner is reading the decimal point or the remainder of a decimal number + /// in a float. + ReadingDecimalPoint, + /// State when the Scanner has finished its read. + Finished, +} + +/// The Scanner is a data structure that pulls information (i.e. strings or ints) from a stream of +/// characters. To use, feed a string into the Scanner and use the next_x() functions to pull data +/// out. These functions are designed so they cannot fail, so any input that does not match the type +/// trying to be read will be ignored. +impl Scanner { + /// Constructs a new, empty scanner. + pub fn new() -> Scanner { + Scanner { + input: VecDeque::new(), + } + } + + /// Feeds a line into the Scanner. + pub fn feed(&mut self, line: String) { + for character in line.chars() { + self.input.push_back(character); + } + self.input.push_back('\n'); + } + + /// Attempts to read an int from the Scanner. The read effectively finds the first match to the + /// following regular expression: `[0-9]+`. Any characters before the match are discarded and + /// the Scanner is advanced to the character immediately after the last character of the match. + /// If the number exceeds the maximum possible size of a u64, u64::MAX is returned. + /// + /// If no digits are found in the entire remainder of the unscanned input, the function will + /// return None. + pub fn next_int(&mut self) -> Option { + let mut state = ScannerState::Waiting; + let mut result = String::new(); + + // Process the Scanner's queue character by character + while !self.input.is_empty() { + let character = self.input.pop_front().unwrap(); + + match state { + ScannerState::Waiting => { + if character.is_ascii_digit() { + result.push(character); + state = ScannerState::ReadingInt; + } + } + ScannerState::ReadingInt => { + if character.is_ascii_digit() { + result.push(character); + } else { + state = ScannerState::Finished; + // Put the character back since we never actually utilized it. + self.input.push_front(character); + } + } + ScannerState::ReadingDecimalPoint => { + // Should be unreachable + unimplemented!() + } + ScannerState::Finished => { + // Put the character back in the queue to avoid consuming it and break out of + // the loop to return the int to the user. + self.input.push_front(character); + break; + } + } + } + + let parsed = result.parse(); + if !result.is_empty() { + match parsed { + Ok(res) => Some(res), + Err(_) => Some(u64::MAX), + } + } else { + None + } + } + + /// Attempts to read a double from the Scanner. The read effectively finds the first match to + /// the following regular expression: `[0-9]+((\.[0-9]+)|)`. Any characters before the match are + /// discarded and the Scanner is advanced to the character immediately after the last character + /// of the match. + /// + /// If no matches are found in the entire remainder of the unscanned input, the function will + /// return None. + pub fn next_double(&mut self) -> Option { + let mut state = ScannerState::Waiting; + let mut result = String::new(); + + // Process the Scanner's queue character by character + while !self.input.is_empty() { + let character = self.input.pop_front().unwrap(); + + match state { + ScannerState::Waiting => { + if character.is_ascii_digit() { + result.push(character); + state = ScannerState::ReadingInt; + } + } + ScannerState::ReadingInt => { + if character.is_ascii_digit() { + result.push(character); + } else if character == '.' { + state = ScannerState::ReadingDecimalPoint; + result.push(character); + } else { + state = ScannerState::Finished; + // Put the character back since we never actually utilized it. + self.input.push_front(character); + } + } + ScannerState::ReadingDecimalPoint => { + if character.is_ascii_digit() { + result.push(character); + } else { + state = ScannerState::Finished; + // Put the character back since we never actually utilized it. + self.input.push_front(character); + } + } + ScannerState::Finished => { + // Put the character back in the queue to avoid consuming it and break out of + // the loop to return the int to the user. + self.input.push_front(character); + break; + } + } + } + + let parsed = result.parse(); + if !result.is_empty() { + match parsed { + Ok(res) => Some(res), + Err(_) => Some(f64::INFINITY), + } + } else { + None + } + } + + /// Identical to next_double(), but it returns an f32 instead. + pub fn next_float(&mut self) -> Option { + self.next_double().map(|val| val as f32) + } + + /// Returns the remainder of the current line in the Scanner. If the Scanner is empty, this + /// function will return None. + pub fn next_line(&mut self) -> Option { + let mut result = String::new(); + + // If the feed is empty, block here. Checking for an empty buffer here allows the function + // to return an empty string if the only thing left in the queue is a newline character. + if self.input.is_empty() { + return None; + } + + while !self.input.is_empty() { + let character = self.input.pop_front().unwrap(); + if character == '\n' { + break; + } + result.push(character); + } + + Some(result) + } +} diff --git a/src/bin/main.rs b/src/bin/main.rs new file mode 100644 index 000000000..079c21d46 --- /dev/null +++ b/src/bin/main.rs @@ -0,0 +1,756 @@ +use gloo::file::FileList; +use gloo_console::warn; +use js_sys::Object; +use monaco::{ + api::TextModel, + sys::{editor::IMarkerData, MarkerSeverity}, +}; +use std::collections::HashMap; +use std::collections::HashSet; +use std::rc::Rc; +use swim::agent::datapath_communicator::DatapathCommunicator; +use swim::agent::datapath_reducer::DatapathReducer; +use swim::agent::EmulationCoreAgent; +use swim::emulation_core::mips::datapath::Stage; +use swim::parser::parser_assembler_main::parser; +use swim::parser::parser_structs_and_enums::ProgramInfo; +use swim::ui::footer::component::Footer; +use swim::ui::regview::component::Regview; +use swim::ui::swim_editor::component::SwimEditor; +use swim::{ + emulation_core::{ + architectures::AvailableDatapaths, mips::instruction::MipsInstruction, + riscv::instruction::RiscInstruction, + }, + ui::{ + hex_editor::component::{parse_hexdump, UpdatedLine}, + swim_editor::tab::TabState, + }, +}; +use wasm_bindgen::{JsCast, JsValue}; +use wasm_bindgen_futures::spawn_local; +use web_sys::HtmlInputElement; +use yew::prelude::*; +use yew::{html, Html, Properties}; + +use swim::emulation_core::riscv::datapath::RiscStage; +use yew_agent::Spawnable; + +// To load in the Fibonacci example, uncomment the CONTENT and fib_model lines +// and comment the code, language, and text_model lines. IMPORTANT: +// rename fib_model to text_model to have it work. +const CONTENT_MIPS: &str = include_str!("../../static/assembly_examples/fibonacci.asm"); +const CONTENT_RISCV: &str = include_str!("../../static/assembly_examples/riscv_fib_recursive.asm"); + +#[derive(Properties, Clone, PartialEq)] +struct AppProps { + communicator: &'static DatapathCommunicator, +} + +#[function_component(App)] +fn app(props: &AppProps) -> Html { + // This contains the binary representation of "ori $s0, $zero, 12345", which + // stores 12345 in register $s0. + // let code = String::from("ori $s0, $zero, 12345\n"); + // let language = String::from("mips"); + + // This is the initial text model with default text contents. The + // use_state_eq hook is created so that the component can be updated + // when the text model changes. + //let text_model = use_mut_ref(|| TextModel::create(&code, Some(&language), None).unwrap()); + let text_model = use_state_eq(|| TextModel::create(CONTENT_MIPS, Some("mips"), None).unwrap()); + + // Store the currently executed line in code editor and hex editor + let editor_curr_line = use_state_eq(|| 0.0); + let memory_curr_instr = use_state_eq(|| 0); + + // Output strings for the console and memory viewers. + let parser_text_output = use_state_eq(String::new); + let memory_text_output = use_state_eq(String::new); + let pc_limit = use_state(|| 0); + + // Breakpoints for the text segment viewer + let breakpoints = use_state(HashSet::default); + + // Input strings from the code editor + let lines_content = use_mut_ref(Vec::::new); + + let program_info_ref = use_mut_ref(ProgramInfo::default); + let binary_ref = use_mut_ref(Vec::::new); + let labels_ref = use_mut_ref(HashMap::::new); + + let memory_text_model = + use_state_eq(|| TextModel::create(&memory_text_output, Some("ini"), None).unwrap()); + + // Show input + let show_input = use_state_eq(bool::default); + show_input.set(true); + + // Store the currently selected tabs in windows + let console_active_tab = use_state_eq(|| TabState::Console); + let editor_active_tab = use_state_eq(|| TabState::Editor); + + let datapath_state = use_reducer(DatapathReducer::default); + + let parser_found_errors = use_state(|| false); + + // Start listening for messages from the communicator. This effectively links the worker thread to the main thread + // and will force updates whenever its internal state changes. + { + let dispatcher = datapath_state.dispatcher(); + use_effect_with_deps( + move |communicator: &&DatapathCommunicator| { + spawn_local(communicator.listen_for_updates(dispatcher)); + }, + props.communicator, + ); + } + + // Sync the language/example in the text model with the current architecture set in state. + use_effect_with_deps( + |(current_architecture, text_model)| match current_architecture { + AvailableDatapaths::MIPS => { + text_model.set_language("mips"); + text_model.set_value(CONTENT_MIPS); + } + AvailableDatapaths::RISCV => { + text_model.set_language("riscv"); + text_model.set_value(CONTENT_RISCV); + } + }, + (datapath_state.current_architecture, text_model.clone()), + ); + + // This is where code is assembled and loaded into the emulation core's memory. + let on_assemble_clicked = { + let text_model = text_model.clone(); + let memory_curr_instr = memory_curr_instr.clone(); + let datapath_state = datapath_state.clone(); + let parser_text_output = parser_text_output.clone(); + let trigger = use_force_update(); + let editor_curr_line = editor_curr_line.clone(); + let breakpoints = breakpoints.clone(); + let communicator = props.communicator; + let parser_found_errors = parser_found_errors.clone(); + + // Clone the value before moving it into the closure + let pc_limit = pc_limit.clone(); + let program_info_ref = Rc::clone(&program_info_ref); + let binary_ref = Rc::clone(&binary_ref); + let labels_ref = Rc::clone(&labels_ref); + + use_callback( + move |_, + ( + text_model, + editor_curr_line, + memory_curr_instr, + datapath_state, + parser_found_errors, + )| { + let text_model = text_model.clone(); + // parses through the code to assemble the binary and retrieves programinfo for error marking and mouse hover + let (program_info, assembled, labels) = + parser(text_model.get_value(), datapath_state.current_architecture); + *program_info_ref.borrow_mut() = program_info.clone(); + *binary_ref.borrow_mut() = assembled.clone(); + *labels_ref.borrow_mut() = labels.clone(); + pc_limit.set(assembled.len() * 4); + parser_text_output.set(program_info.console_out_post_assembly); + + let mut markers: Vec = vec![]; + + // Parse output from parser and create an instance of IMarkerData for each error. + for (line_number, line_information) in + program_info.monaco_line_info.iter().enumerate() + { + for error in &line_information.errors { + let new_marker: IMarkerData = new_object().into(); + new_marker.set_message(&error.message); + new_marker.set_severity(MarkerSeverity::Error); + new_marker.set_start_line_number((line_number + 1) as f64); + new_marker.set_start_column((error.start_end_columns.0 + 1) as f64); + new_marker.set_end_line_number((line_number + 1) as f64); + new_marker.set_end_column((error.start_end_columns.1 + 1) as f64); + markers.push(new_marker); + } + } + + // Convert Vec to Javascript array + let marker_jsarray = js_sys::Array::new(); + for marker in markers { + marker_jsarray.push(&marker); + } + + monaco::sys::editor::set_model_markers( + text_model.as_ref(), + "owner", + &marker_jsarray, + ); + + // Reset highlighted line to 0 + editor_curr_line.set(0.0); + + // Proceed with loading into memory and expand pseudo-instructions if there are no errors. + if marker_jsarray.length() == 0 { + // Send the binary over to the emulation core thread + communicator.initialize(program_info.pc_starting_point, assembled); + memory_curr_instr.set(datapath_state.get_pc()); + breakpoints.set(HashSet::default()); + + text_model.set_value(&program_info.updated_monaco_string); // Expands pseudo-instructions to their hardware counterpart. + + // After adding pseudo instructions, update program info + let (program_info, assembled, labels) = + parser(text_model.get_value(), datapath_state.current_architecture); + *program_info_ref.borrow_mut() = program_info.clone(); + *binary_ref.borrow_mut() = assembled.clone(); + *labels_ref.borrow_mut() = labels.clone(); + parser_found_errors.set(false); + } else { + parser_found_errors.set(true); + } + + trigger.force_update(); + }, + ( + text_model, + editor_curr_line, + memory_curr_instr, + datapath_state, + parser_found_errors, + ), + ) + }; + + // This is where the code will get executed. If you execute further + // than when the code ends, the program crashes. This is remedied via the + // syscall instruction, which will halt the datapath. As you execute the + // code, the previously executed line is highlighted. + let on_execute_clicked = { + let datapath_state = datapath_state.clone(); + let text_model = text_model.clone(); + + // Code editor + let editor_curr_line = editor_curr_line.clone(); + let memory_curr_instr = memory_curr_instr.clone(); + + let trigger = use_force_update(); + let communicator = props.communicator; + + let program_info_ref = Rc::clone(&program_info_ref); + let binary_ref = Rc::clone(&binary_ref); + let labels_ref = Rc::clone(&labels_ref); + + use_callback( + move |_, (editor_curr_line, memory_curr_instr, text_model, datapath_state)| { + // Get the current line and convert it to f64 + let (program_info, assembled, labels) = + parser(text_model.get_value(), datapath_state.current_architecture); + *program_info_ref.borrow_mut() = program_info.clone(); + *binary_ref.borrow_mut() = assembled.clone(); + *labels_ref.borrow_mut() = labels.clone(); + + let list_of_line_numbers = program_info.address_to_line_number; + let index = datapath_state.get_pc() as usize / 4; + editor_curr_line.set(match list_of_line_numbers.get(index) { + Some(val) => (val + 1) as f64, + None => 0f64, + }); + memory_curr_instr.set(datapath_state.get_pc()); + + // Execute instruction + communicator.execute_instruction(); + + trigger.force_update(); + }, + ( + editor_curr_line, + memory_curr_instr, + text_model, + datapath_state, + ), + ) + }; + + let on_execute_stage_clicked = { + let datapath_state = datapath_state.clone(); + let text_model = text_model.clone(); + let communicator = props.communicator; + + // Code editor + let editor_curr_line = editor_curr_line.clone(); + + // Hex editor + let memory_curr_instr = memory_curr_instr.clone(); + + let trigger = use_force_update(); + + let program_info_ref = Rc::clone(&program_info_ref); + let binary_ref = Rc::clone(&binary_ref); + let labels_ref = Rc::clone(&labels_ref); + + use_callback( + move |_, (editor_curr_line, memory_curr_instr, text_model, datapath_state)| { + let is_instruction_decode = match datapath_state.current_architecture { + AvailableDatapaths::MIPS => { + datapath_state.mips.current_stage == Stage::InstructionDecode + } + AvailableDatapaths::RISCV => { + datapath_state.riscv.current_stage == RiscStage::InstructionDecode + } + }; + + if is_instruction_decode { + // highlight on InstructionDecode since syscall stops at that stage. + + // highlight on InstructionDecode since syscall stops at that stage. + let (program_info, assembled, labels) = + parser(text_model.get_value(), datapath_state.current_architecture); + *program_info_ref.borrow_mut() = program_info.clone(); + *binary_ref.borrow_mut() = assembled.clone(); + *labels_ref.borrow_mut() = labels.clone(); + + let list_of_line_numbers = program_info.address_to_line_number; + let index = datapath_state.get_pc() as usize / 4; + editor_curr_line.set(match list_of_line_numbers.get(index) { + Some(val) => (val + 1) as f64, + None => 0f64, + }); + memory_curr_instr.set(datapath_state.get_pc()); + communicator.execute_stage(); + } else { + communicator.execute_stage(); + } + + trigger.force_update(); + }, + ( + editor_curr_line, + memory_curr_instr, + text_model, + datapath_state, + ), + ) + }; + + let on_continue_execution = { + let communicator = props.communicator; + let program_info_ref = Rc::clone(&program_info_ref); + let binary_ref = Rc::clone(&binary_ref); + let labels_ref = Rc::clone(&labels_ref); + + let text_model = text_model.clone(); + + let datapath_state = datapath_state.clone(); + use_callback( + move |_, (text_model, datapath_state)| { + let (program_info, assembled, labels) = + parser(text_model.get_value(), datapath_state.current_architecture); + *program_info_ref.borrow_mut() = program_info.clone(); + *binary_ref.borrow_mut() = assembled.clone(); + *labels_ref.borrow_mut() = labels.clone(); + + communicator.execute(); + }, + (text_model, datapath_state), + ) + }; + + let on_pause_execution = { + let communicator = props.communicator; + use_callback( + move |_, _| { + communicator.pause_core(); + }, + (), + ) + }; + + let on_memory_clicked = { + // Code editor + let text_model = text_model.clone(); + + // Hex editor + let memory_text_model = memory_text_model.clone(); + + let trigger = use_force_update(); + let communicator = props.communicator; + let datapath_state = datapath_state.clone(); + + let program_info_ref = Rc::clone(&program_info_ref); + let labels_ref = Rc::clone(&labels_ref); + + use_callback( + move |_, datapath_state| { + let text_model = text_model.clone(); + + // Update memory + let memory_text_model = memory_text_model.clone(); + + let current_memory_text_model_value = memory_text_model.get_value(); + + match parse_hexdump(¤t_memory_text_model_value) { + Ok((hex_instructions, ascii_instructions)) => { + let mut changed_lines: Vec = vec![]; + for (i, data) in hex_instructions.iter().enumerate() { + let address = i as u64; + let curr_word = match datapath_state.get_memory().load_word(address * 4) + { + Ok(data) => data, + Err(e) => { + warn!("{:?}", e); + 0 + } + }; + + let mut new_word = 0; + let mut differs = false; + // hex portion gets priority when checking for changes + if curr_word != *data { + // hex portion was changed + differs = true; + new_word = *data; + } else if i < ascii_instructions.len() + && curr_word != ascii_instructions[i] + && ascii_instructions[i] != 0 + { + // ascii portion was changed + differs = true; + new_word = ascii_instructions[i]; + } + + if differs { + // change string version based on architecture + let string_version = match datapath_state.current_architecture { + AvailableDatapaths::MIPS => { + match MipsInstruction::get_string_version( + new_word, + labels_ref.borrow().clone(), + i, + ) { + Ok(string) => string, + Err(string) => string, + } + } + AvailableDatapaths::RISCV => { + match RiscInstruction::get_string_version( + new_word, + labels_ref.borrow().clone(), + ) { + Ok(string) => string, + Err(string) => string, + } + } + }; + + changed_lines.push(UpdatedLine::new(string_version, i)); + communicator.set_memory(address * 4, new_word); + } + } + // Memory updated successfully + let (program_info, _assembled, _labels) = + parser(text_model.get_value(), datapath_state.current_architecture); + let mut lines_beyond_counter = program_info.address_to_line_number.len(); + let mut curr_value = text_model.get_value(); + let mut add_new_lines = false; + for line in changed_lines { + // Check if we're updating or appending instruction + if line.line_number < program_info.address_to_line_number.len() { + let updated_line = + program_info.address_to_line_number[line.line_number] as f64 + + 1.0; + let curr_model = text_model.as_ref(); + + // Get the current line's contents in the code editor + let line_to_replace = curr_model.get_line_content(updated_line); + // Create the range to replace + let mut start_line_column = 0.0; + let end_line_column = line_to_replace.len() as f64 + 2.0; + for (i, c) in line_to_replace.chars().enumerate() { + if c.is_alphanumeric() { + start_line_column = i as f64 + 1.0; + break; + } + } + let edit_range = monaco::sys::Range::new( + updated_line, + start_line_column, + updated_line, + end_line_column, + ); + let before_cursor_state = monaco::sys::Selection::new( + updated_line, + start_line_column, + updated_line, + end_line_column, + ); + // Create the edit operation using the range and new text + let edit_operations: monaco::sys::editor::IIdentifiedSingleEditOperation = Object::new().unchecked_into(); + edit_operations.set_range(&edit_range); + edit_operations.set_text(Some(&line.text)); + // Append it to JavaScript Array + let edit_operations_array = js_sys::Array::new(); + edit_operations_array.push(&edit_operations); + let before_cursor_state_array = js_sys::Array::new(); + before_cursor_state_array.push(&before_cursor_state); + // Do the edit! + curr_model.push_edit_operations( + &before_cursor_state_array, + &edit_operations_array, + None, + ); + } else if line.line_number == lines_beyond_counter { + // Append instruction + if !add_new_lines { + // If we've added new lines already, + // start adding new lines by getting a copy of the current text model to append to + add_new_lines = true; + curr_value = text_model.get_value(); + } + curr_value.push('\n'); + curr_value.push_str(&line.text); + lines_beyond_counter += 1; + } + } + if add_new_lines { + text_model.set_value(&curr_value); + } + } + Err(err) => { + warn!("Error updating memory: {}", err) + } + } + + // Update the parsed info for text and data segment views + let (program_info, _, _) = + parser(text_model.get_value(), datapath_state.current_architecture); + *program_info_ref.borrow_mut() = program_info; + + trigger.force_update(); + }, + datapath_state, + ) + }; + + // This is how we will reset the datapath. + // This will also clear any highlight on the editor. + let on_reset_clicked = { + let trigger = use_force_update(); + let parser_text_output = parser_text_output.clone(); + let communicator = props.communicator; + + // Code editor + let parser_text_output = parser_text_output; + let editor_curr_line = editor_curr_line.clone(); + let breakpoints = breakpoints.clone(); + + // Hex editor + let memory_curr_instr = memory_curr_instr.clone(); + + use_callback( + move |_, (editor_curr_line, program_info_ref, binary_ref, labels_ref)| { + // Set highlighted line to 0 + editor_curr_line.set(0.0); + memory_curr_instr.set(0); + + parser_text_output.set("".to_string()); + + *program_info_ref.borrow_mut() = ProgramInfo::default(); + *binary_ref.borrow_mut() = vec![]; + *labels_ref.borrow_mut() = HashMap::::new(); + + communicator.reset(); + breakpoints.set(HashSet::default()); + trigger.force_update(); + }, + ( + editor_curr_line, + program_info_ref.clone(), + binary_ref.clone(), + labels_ref.clone(), + ), + ) + }; + + // This is where we will have the user prompted to load in a file + let upload_clicked_callback = use_callback( + move |e: MouseEvent, _| { + e.stop_propagation(); + on_upload_file_clicked(); + }, + (), + ); + + // This is the callback to get the file's contents and load it onto the Editor + let file_picked_callback = { + let text_model = text_model.clone(); + use_callback( + move |e: Event, _| { + let text_model = text_model.clone(); + let input: HtmlInputElement = e.target_unchecked_into(); + // gloo making the code readable and easy to implement + let filelist = FileList::from(input.files().unwrap()); + let file = filelist.first().unwrap(); + let contents = gloo::file::futures::read_as_text(file); + spawn_local(async move { + let contents = contents.await; + + let contents = contents.expect("File contains invalid utf8"); // TODO: implement a file checker, will load in anything + + text_model.set_value(&contents); + }) + }, + (), + ) + }; + + html! { +
+ // button tied to the input file element, which is hidden to be more clean + +
+ // Left column +
+ // Top buttons +
+
+ + + + + + + +
+
+ + // Editor +
+ +
+ + // Console +
+
+ + // Right column + +
+ +
+ } +} + +/// Creates a new `JsValue`. +fn new_object() -> JsValue { + js_sys::Object::new().into() +} + +/********************** File I/O Function ***********************/ +pub fn on_upload_file_clicked() { + let window = web_sys::window().expect("should have a window in this context"); + let document = window.document().expect("window should have a document"); + + let file_input_elem = document + .get_element_by_id("file_input") + .expect("File input element with id \"file_input\" should exist."); + + let file_input_elem = file_input_elem + .dyn_into::() + .expect("Element should be an HtmlInputElement"); + + // workaround for https://github.com/yewstack/yew/pull/3037 since it's not in 0.20 + spawn_local(async move { + file_input_elem.click(); + }); +} + +fn main() { + // Initialize and leak the communicator to ensure that the thread spawns immediately and the bridge to it lives + // for the remainder of the program. + let bridge = EmulationCoreAgent::spawner().spawn("./worker.js"); + let communicator = Box::new(DatapathCommunicator::new(bridge)); + yew::Renderer::::with_props(AppProps { + communicator: Box::leak(communicator), + }) + .render(); +} diff --git a/src/bin/worker.rs b/src/bin/worker.rs new file mode 100644 index 000000000..587377ae1 --- /dev/null +++ b/src/bin/worker.rs @@ -0,0 +1,6 @@ +use swim::agent::EmulationCoreAgent; +use yew_agent::Registrable; + +fn main() { + EmulationCoreAgent::registrar().register(); +} diff --git a/src/emulation_core.rs b/src/emulation_core.rs index e62a37920..d5aca5cd4 100644 --- a/src/emulation_core.rs +++ b/src/emulation_core.rs @@ -1,4 +1,9 @@ //! The emulation core for the project. +pub mod architectures; pub mod datapath; +pub mod line_info; pub mod mips; +pub mod register; +pub mod riscv; +pub mod stack; diff --git a/src/emulation_core/architectures.rs b/src/emulation_core/architectures.rs new file mode 100644 index 000000000..99e121989 --- /dev/null +++ b/src/emulation_core/architectures.rs @@ -0,0 +1,36 @@ +use crate::emulation_core::mips::datapath::MipsDatapath; +use crate::emulation_core::riscv::datapath::RiscDatapath; +use core::fmt; +use serde::{Deserialize, Serialize}; +use strum_macros::EnumIter; + +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, EnumIter, Copy)] +pub enum AvailableDatapaths { + MIPS, + RISCV, +} + +impl fmt::Display for AvailableDatapaths { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + AvailableDatapaths::MIPS => write!(f, "MIPS"), + AvailableDatapaths::RISCV => write!(f, "RISCV"), + } + } +} + +// from string +impl From<&str> for AvailableDatapaths { + fn from(s: &str) -> Self { + match s { + "MIPS" => AvailableDatapaths::MIPS, + "RISCV" => AvailableDatapaths::RISCV, + _ => panic!("Invalid datapath type"), + } + } +} + +pub enum DatapathRef<'a> { + MIPS(&'a MipsDatapath), + RISCV(&'a RiscDatapath), +} diff --git a/src/emulation_core/datapath.rs b/src/emulation_core/datapath.rs index b3d1c8deb..a391c37e7 100644 --- a/src/emulation_core/datapath.rs +++ b/src/emulation_core/datapath.rs @@ -1,5 +1,13 @@ //! Module for the API of a generic datapath. +use crate::emulation_core::architectures::DatapathRef; +use crate::emulation_core::datapath::Syscall::{ + Exit, PrintDouble, PrintFloat, PrintInt, PrintString, ReadDouble, ReadFloat, ReadInt, + ReadString, +}; +use crate::emulation_core::mips::memory::Memory; +use std::ops::BitOrAssign; + /// A generic datapath. /// /// This has the ability to execute instructions, and to interface with @@ -16,55 +24,166 @@ pub trait Datapath { /// registers, respectively.) type RegisterData; - /// The enum used to describe all available registers used in the - /// datapath. This must be defined separately, and at minimum simply - /// contain a list of registers. Further implementation details are - /// at the discretion of the developer. - type RegisterEnum; - - /// The data type that describes memory for this datapath. This must be - /// defined separately. This allows raw access to any parts of memory - /// or its own interface at will. - type MemoryType; - /// Execute a single instruction based on the current state of the /// datapath. Should the datapath support stages, if the datapath is /// midway through a stage, the current instruction will be finished /// instead of executing a new instruction. Should the datapath be in /// a "halted" state, behavior is undefined. - fn execute_instruction(&mut self); + fn execute_instruction(&mut self) -> DatapathUpdateSignal; /// Execute a single stage of execution based on the current state of /// the datapath. Should the datapath not support stages, assume the /// same behavior as [`Self::execute_instruction()`]. Should the /// datapath be in a "halted" state, behavior is undefined. - fn execute_stage(&mut self); + fn execute_stage(&mut self) -> DatapathUpdateSignal; + + /// Sets the data in the GP register indicated by the provided string. If it doesn't exist, + /// this function returns Err. + fn set_register_by_str(&mut self, register: &str, data: Self::RegisterData); + + // Set the data in the FP register indicated by the provided string + fn set_fp_register_by_str(&mut self, register: &str, data: Self::RegisterData); - /// Retrieve the data in the register indicated by the provided enum. - /// It can be assumed valid data will be retrieved since any valid - /// registers should be listed within [`Self::RegisterEnum`]. - fn get_register_by_enum(&self, register: Self::RegisterEnum) -> Self::RegisterData; + /// Reset the datapath, load instructions into memory, and un-sets the `is_halted` + /// flag. If the process fails, an [`Err`] is returned. + fn initialize(&mut self, initial_pc: usize, instructions: Vec) -> Result<(), String>; /// Retrieve all memory as-is. - fn get_memory(&self) -> &Self::MemoryType; + fn get_memory(&self) -> &Memory; + + /// Retrieve a mutable reference to memory. + fn get_memory_mut(&mut self) -> &mut Memory; + + // Store word in memory at ptr + fn set_memory(&mut self, ptr: u64, data: u32); /// Returns if the datapath is in a "halted" or "stopped" state. This may /// be true in the case where an error had occurred previously. fn is_halted(&self) -> bool; + /// Halts the datapath. In order to un-halt the datapath, either call reset() or initialize(). + fn halt(&mut self); + /// Restore the datapath to its default state. fn reset(&mut self); + + /// Obtain a reference to the concrete datapath type. Used when datapath-specific logic is + /// needed while dealing with a datapath as a trait object. + fn as_datapath_ref(&self) -> DatapathRef; + + /// Returns the type of syscall and its arguments (if any exist for that syscall). See the + /// syscall enum for a list of syscalls and what they do. + fn get_syscall_arguments(&self) -> Syscall; } /// A datapath that supports a visual diagram component. /// /// This requires a corresponding visual diagram with labels that can be mapped /// to the datapath. -pub trait VisualDatapath { - /// The information about a piece of the diagram that is returned from the datapath. - type LineInformation; +pub trait VisualDatapath {} + +/// Enum describing all syscalls that can be executed. The register used for indicating the syscall +/// (and its argument) is different for each architecture. +#[derive(Clone, Debug, PartialEq)] +pub enum Syscall { + /// Halts the emulator core. This should generally be the default syscall if the syscall number + /// does not match any of the other variants. + /// + /// Call number: 0 + Exit, + /// Prints the integer value of the argument register. + /// + /// Call number: 1 + PrintInt(u64), + /// Prints the float value of the argument register. + /// + /// Call number: 2 + PrintFloat(f32), + /// Prints the double value of the argument register. + /// + /// Call number: 3 + PrintDouble(f64), + /// Prints a string to console, starting at the memory address in the argument and ending at a + /// null byte. + /// + /// Call number: 4 + PrintString(u64), + /// Reads the next int from the console. + /// + /// Call number: 5 + ReadInt, + /// Reads the next double from the console. + /// + /// Call number: 6 + ReadFloat, + /// Reads the next float from the console. + /// + /// Call number 7 + ReadDouble, + /// Reads from the console until a newline character and stores it at the provided memory + /// address with a null terminator. + /// + /// Call number 8 + ReadString(u64), +} + +impl Syscall { + pub fn from_register_data( + syscall: u64, + integer_arg: u64, + float_arg: f32, + double_arg: f64, + ) -> Syscall { + match syscall { + 1 => PrintInt(integer_arg), + 2 => PrintFloat(float_arg), + 3 => PrintDouble(double_arg), + 4 => PrintString(integer_arg), + 5 => ReadInt, + 6 => ReadFloat, + 7 => ReadDouble, + 8 => ReadString(integer_arg), + _ => Exit, + } + } +} + +/// Struct used for signalling the results of execution. This can then be used to determine which +/// additional actions the emulator core thread needs to perform after it executes a cycle/stage. +#[derive(Default, Debug, Copy, Clone, PartialEq)] +pub struct DatapathUpdateSignal { + pub changed_state: bool, + pub changed_registers: bool, + pub changed_coprocessor_state: bool, + pub changed_coprocessor_registers: bool, + pub changed_memory: bool, + pub changed_stack: bool, + pub hit_syscall: bool, + pub hit_breakpoint: bool, +} + +/// Constant used to easily trigger an update for everything but to avoid triggering any other +/// execution (i.e. breakpoints or syscalls). +pub const UPDATE_EVERYTHING: DatapathUpdateSignal = DatapathUpdateSignal { + changed_state: true, + changed_registers: true, + changed_coprocessor_state: true, + changed_coprocessor_registers: true, + changed_memory: true, + changed_stack: true, + hit_syscall: false, + hit_breakpoint: false, +}; - /// Return the information from the datapath corresponding to the `variable` attribute on a - /// part of the visual datapath diagram. - fn visual_line_to_data(&self, variable: &str) -> Self::LineInformation; +impl BitOrAssign for DatapathUpdateSignal { + fn bitor_assign(&mut self, rhs: Self) { + self.changed_state |= rhs.changed_state; + self.changed_registers |= rhs.changed_registers; + self.changed_coprocessor_state |= rhs.changed_coprocessor_state; + self.changed_coprocessor_registers |= rhs.changed_coprocessor_registers; + self.changed_memory |= rhs.changed_memory; + self.changed_stack |= rhs.changed_stack; + self.hit_syscall |= rhs.hit_syscall; + self.hit_breakpoint |= rhs.hit_breakpoint; + } } diff --git a/src/emulation_core/line_info.rs b/src/emulation_core/line_info.rs new file mode 100644 index 000000000..a3cf7b3ba --- /dev/null +++ b/src/emulation_core/line_info.rs @@ -0,0 +1,16 @@ +//! Module for mapping lines in the visual datapath to information +//! and variables in the coded datapath. + +/// A collection of data surrounding a line in the visual datapath. +pub struct LineInformation { + pub title: String, + pub description: String, + + /// The value stored in a line. This may not be a 64-bit value, but should + /// refer to the `bits` field to determine how many bits on the line are + /// relevant to be displayed. + pub value: u64, + + /// The number of bits on a given line. + pub bits: u64, +} diff --git a/src/emulation_core/mips.rs b/src/emulation_core/mips.rs index 9eef52183..739e7731b 100644 --- a/src/emulation_core/mips.rs +++ b/src/emulation_core/mips.rs @@ -6,7 +6,7 @@ pub mod control_signals; pub mod coprocessor; pub mod datapath; pub mod datapath_signals; +pub mod fp_registers; +pub mod gp_registers; pub mod instruction; -pub mod line_info; pub mod memory; -pub mod registers; diff --git a/src/emulation_core/mips/constants.rs b/src/emulation_core/mips/constants.rs index 158bd7d88..9fc9ebb1e 100644 --- a/src/emulation_core/mips/constants.rs +++ b/src/emulation_core/mips/constants.rs @@ -2,6 +2,7 @@ use super::control_signals::RegWidth; pub const FUNCT_SYSCALL: u8 = 0b001100; +pub const FUNCT_BREAK: u8 = 0b001101; pub const FUNCT_SLL: u8 = 0b000000; pub const FUNCT_ADD: u8 = 0b100000; pub const FUNCT_ADDU: u8 = 0b100001; diff --git a/src/emulation_core/mips/control_signals.rs b/src/emulation_core/mips/control_signals.rs index 5ff0ad324..e7f11de77 100644 --- a/src/emulation_core/mips/control_signals.rs +++ b/src/emulation_core/mips/control_signals.rs @@ -279,8 +279,9 @@ pub enum RegWrite { pub mod floating_point { use super::super::constants::*; + use serde::{Deserialize, Serialize}; - #[derive(Clone, Default, PartialEq)] + #[derive(Clone, Default, PartialEq, Serialize, Deserialize, Debug)] pub struct FpuControlSignals { pub cc: Cc, pub cc_write: CcWrite, @@ -300,7 +301,7 @@ pub mod floating_point { /// /// For the sake of this project, it will usually be assumed that this will /// be 0, however the functionality is available to be extended. - #[derive(Clone, Default, PartialEq)] + #[derive(Clone, Default, PartialEq, Serialize, Deserialize, Debug)] pub enum Cc { /// Use condition code register 0. Default in most operations. Can be /// additionally used in the case where the condition code register is @@ -310,7 +311,7 @@ pub mod floating_point { } /// Determines if the condition code register file should be written to. - #[derive(Clone, Default, PartialEq)] + #[derive(Clone, Default, PartialEq, Serialize, Deserialize, Debug)] pub enum CcWrite { #[default] NoWrite = 0, @@ -321,7 +322,7 @@ pub mod floating_point { /// /// This is a special intermediary register that facilitates passing data between /// the main processing unit and the floating-point unit. - #[derive(Clone, Default, PartialEq)] + #[derive(Clone, Default, PartialEq, Serialize, Deserialize, Debug)] pub enum DataSrc { /// Use data from the main processing unit. Specifically, the data from register /// `rt` from a given instruction. This value can additionally be used in the cases @@ -342,7 +343,7 @@ pub mod floating_point { /// For the latter two functions, it is imperative to unset the [`RegWrite`](super::RegWrite) and /// [`FpuRegWrite`] control signals in cases where registers should not be modified /// with unintended data. - #[derive(Clone, Default, PartialEq)] + #[derive(Clone, Default, PartialEq, Serialize, Deserialize, Debug)] pub enum DataWrite { /// - Do not write to the data register. /// - Source data to write to the main processing unit register file from the main @@ -373,7 +374,7 @@ pub mod floating_point { /// /// *Implementation note:* The bits set for the comparator are intended to match /// the bits used in the `cond` field of a `c.cond.fmt` instruction. - #[derive(Clone, Debug, Default, PartialEq)] + #[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)] pub enum FpuAluOp { #[default] /// `_0000` (0): @@ -437,7 +438,7 @@ pub mod floating_point { /// /// This directly overrides any branch decisions decided by the main processing unit. /// The [`Branch`](super::Branch) control signal should not be set in addition to this signal. - #[derive(Clone, Default, PartialEq)] + #[derive(Clone, Default, PartialEq, Serialize, Deserialize, Debug)] pub enum FpuBranch { /// Do not consider branching. #[default] @@ -451,7 +452,7 @@ pub mod floating_point { /// register's new data will be. /// /// This decision, if set, overrides the decision from the [`DataWrite`] control signal. - #[derive(Clone, Default, PartialEq)] + #[derive(Clone, Default, PartialEq, Serialize, Deserialize, Debug)] pub enum FpuMemToReg { /// Do not use data from memory. Use the result of the [`DataWrite`] control signal. #[default] @@ -463,7 +464,7 @@ pub mod floating_point { /// Determines, given that [`FpuRegWrite`] is set, which destination register to write /// to, which largely depends on the instruction format. - #[derive(Clone, Default, PartialEq)] + #[derive(Clone, Default, PartialEq, Serialize, Deserialize, Debug)] pub enum FpuRegDst { /// Use register `ft`. Reg1 = 0, @@ -480,7 +481,7 @@ pub mod floating_point { /// /// While all buses carrying information are 64-bits wide, some bits of the bus may be /// ignored in the case of this control signal. - #[derive(Clone, Default, PartialEq)] + #[derive(Clone, Default, PartialEq, Serialize, Deserialize, Debug)] pub enum FpuRegWidth { /// Use words (32 bits). Equivalent to a single-precision floating-point value. Word = 0, @@ -503,7 +504,7 @@ pub mod floating_point { } /// Determines if the floating-point register file should be written to. - #[derive(Clone, Default, PartialEq)] + #[derive(Clone, Default, PartialEq, Serialize, Deserialize, Debug)] pub enum FpuRegWrite { /// Do not write to the floating-point register file. #[default] @@ -517,7 +518,7 @@ pub mod floating_point { /// to follow through with a branch. /// /// This signal is what is sent to the main processor. - #[derive(Clone, Default, PartialEq)] + #[derive(Clone, Default, PartialEq, Serialize, Deserialize, Debug)] pub enum FpuTakeBranch { #[default] NoBranch = 0, diff --git a/src/emulation_core/mips/coprocessor.rs b/src/emulation_core/mips/coprocessor.rs index c0c40fe85..9789c5a4f 100644 --- a/src/emulation_core/mips/coprocessor.rs +++ b/src/emulation_core/mips/coprocessor.rs @@ -2,25 +2,26 @@ use super::constants::*; use super::control_signals::floating_point::*; -use super::instruction::Instruction; +use super::fp_registers::FpRegisters; +use super::instruction::MipsInstruction; +use serde::{Deserialize, Serialize}; /// An implementation of a floating-point coprocessor for the MIPS64 ISA. /// /// Different from the main processor, much of the functionality of the coprocessor /// is controlled remotely using its available API calls. -#[derive(Clone, Default, PartialEq)] +#[derive(Clone, PartialEq, Default, Debug, Serialize, Deserialize)] pub struct MipsFpCoprocessor { - instruction: Instruction, + instruction: MipsInstruction, pub signals: FpuControlSignals, pub state: FpuState, pub is_halted: bool, - - pub fpr: [u64; 32], + pub registers: FpRegisters, pub condition_code: u64, pub data: u64, } -#[derive(Clone, Default, PartialEq)] +#[derive(Clone, Default, PartialEq, Serialize, Deserialize, Debug)] pub struct FpuState { pub instruction: u32, pub op: u32, @@ -96,7 +97,7 @@ impl MipsFpCoprocessor { /// does not fetch instructions. pub fn set_instruction(&mut self, instruction_bits: u32) { self.state.instruction = instruction_bits; - if let Ok(instruction) = Instruction::try_from(self.state.instruction) { + if let Ok(instruction) = MipsInstruction::try_from(self.state.instruction) { self.instruction = instruction; } } @@ -127,13 +128,37 @@ impl MipsFpCoprocessor { self.state.fp_register_to_memory } + pub fn set_register(&mut self, _register: usize, _data: u64) -> Result<(), String> { + if _register >= 32 { + return Err(format!("Register index out of bounds: {}", _register)); + } + + let register = &mut self.registers.fpr[_register]; + *register = _data; + + Ok(()) + } + + pub fn register_from_str(&self, _register: &str) -> Option { + // Check if register matches a register between f0 and f31 + if _register.len() >= 2 && &_register[0..1] == "f" && _register.len() <= 3 { + let register = &_register[1..]; + if let Ok(register) = register.parse::() { + if register < 32 { + return Some(register); + } + } + } + None + } + // ================== Instruction Decode (ID) ================== /// Decode an instruction into its individual fields. fn instruction_decode(&mut self) { // Set the data lines based on the contents of the instruction. // Some lines will hold uninitialized values as a result. match self.instruction { - Instruction::FpuRType(r) => { + MipsInstruction::FpuRType(r) => { self.state.op = r.op as u32; self.state.fmt = r.fmt as u32; self.state.fs = r.fs as u32; @@ -141,33 +166,33 @@ impl MipsFpCoprocessor { self.state.fd = r.fd as u32; self.state.function = r.function as u32; } - Instruction::FpuIType(i) => { + MipsInstruction::FpuIType(i) => { self.state.ft = i.ft as u32; } - Instruction::FpuRegImmType(i) => { + MipsInstruction::FpuRegImmType(i) => { self.state.op = i.op as u32; self.state.fmt = 0; // Not applicable self.state.fs = i.fs as u32; self.state.ft = 0; // Not applicable self.state.fd = 0; // Not applicable } - Instruction::FpuCompareType(c) => { + MipsInstruction::FpuCompareType(c) => { self.state.op = c.op as u32; self.state.fmt = c.fmt as u32; self.state.ft = c.ft as u32; self.state.fs = c.fs as u32; self.state.function = c.function as u32; } - Instruction::FpuBranchType(b) => { + MipsInstruction::FpuBranchType(b) => { self.state.op = b.op as u32; self.state.fmt = b.bcc1 as u32; self.state.branch_flag = b.tf == 1; } // These types do not use the floating-point unit so they can be ignored. - Instruction::RType(_) - | Instruction::IType(_) - | Instruction::JType(_) - | Instruction::SyscallType(_) => (), + MipsInstruction::RType(_) + | MipsInstruction::IType(_) + | MipsInstruction::JType(_) + | MipsInstruction::SyscallType(_) => (), } } @@ -175,7 +200,7 @@ impl MipsFpCoprocessor { /// control signals. fn set_control_signals(&mut self) { match self.instruction { - Instruction::FpuRType(r) => { + MipsInstruction::FpuRType(r) => { match r.op { OPCODE_COP1 => match r.function { FUNCTION_ADD => { @@ -263,7 +288,7 @@ impl MipsFpCoprocessor { )), } } - Instruction::FpuIType(i) => match i.op { + MipsInstruction::FpuIType(i) => match i.op { OPCODE_SWC1 => { self.signals = FpuControlSignals { cc_write: CcWrite::NoWrite, @@ -291,7 +316,7 @@ impl MipsFpCoprocessor { i.op )), }, - Instruction::FpuRegImmType(i) => match i.sub { + MipsInstruction::FpuRegImmType(i) => match i.sub { SUB_MT => { self.signals = FpuControlSignals { cc_write: CcWrite::NoWrite, @@ -345,7 +370,7 @@ impl MipsFpCoprocessor { i.sub )), }, - Instruction::FpuCompareType(c) => { + MipsInstruction::FpuCompareType(c) => { self.signals = FpuControlSignals { // All floating-point branch instructions are forced to use the same // one condition code register, regardless of the CC field in the @@ -373,7 +398,7 @@ impl MipsFpCoprocessor { ..Default::default() } } - Instruction::FpuBranchType(_) => { + MipsInstruction::FpuBranchType(_) => { self.signals = FpuControlSignals { // All floating-point branch instructions are forced to use the same // one condition code register, regardless of the CC field in the @@ -385,10 +410,10 @@ impl MipsFpCoprocessor { } } // These types do not use the floating-point unit so they can be ignored. - Instruction::RType(_) - | Instruction::IType(_) - | Instruction::JType(_) - | Instruction::SyscallType(_) => self.signals = FpuControlSignals::default(), + MipsInstruction::RType(_) + | MipsInstruction::IType(_) + | MipsInstruction::JType(_) + | MipsInstruction::SyscallType(_) => self.signals = FpuControlSignals::default(), } } @@ -398,13 +423,13 @@ impl MipsFpCoprocessor { let reg1 = self.state.fs as usize; let reg2 = self.state.ft as usize; - self.state.read_data_1 = self.fpr[reg1]; - self.state.read_data_2 = self.fpr[reg2]; + self.state.read_data_1 = self.registers.fpr[reg1]; + self.state.read_data_2 = self.registers.fpr[reg2]; // Truncate the variable data if a 32-bit word is requested. if let FpuRegWidth::Word = self.signals.fpu_reg_width { - self.state.read_data_1 = self.fpr[reg1] as u32 as u64; - self.state.read_data_2 = self.fpr[reg2] as u32 as u64; + self.state.read_data_1 = self.registers.fpr[reg1] as u32 as u64; + self.state.read_data_2 = self.registers.fpr[reg2] as u32 as u64; } } @@ -613,7 +638,6 @@ impl MipsFpCoprocessor { FpuMemToReg::UseDataWrite => self.state.register_write_mux_to_mux, FpuMemToReg::UseMemory => self.state.fp_register_data_from_main_processor, }; - - self.fpr[self.state.destination] = self.state.register_write_data; + self.registers.fpr[self.state.destination] = self.state.register_write_data; } } diff --git a/src/emulation_core/mips/datapath.rs b/src/emulation_core/mips/datapath.rs index 16ac9c9d0..c61efb505 100644 --- a/src/emulation_core/mips/datapath.rs +++ b/src/emulation_core/mips/datapath.rs @@ -50,8 +50,15 @@ use super::super::datapath::Datapath; use super::constants::*; use super::control_signals::{floating_point::*, *}; use super::datapath_signals::*; +use super::gp_registers::GpRegisterType; use super::instruction::*; -use super::{coprocessor::MipsFpCoprocessor, memory::Memory, registers::GpRegisters}; +use super::{coprocessor::MipsFpCoprocessor, gp_registers::GpRegisters, memory::Memory}; +use crate::emulation_core::architectures::DatapathRef; +use crate::emulation_core::datapath::{DatapathUpdateSignal, Syscall}; +use crate::emulation_core::mips::fp_registers::FpRegisterType; +use crate::emulation_core::mips::gp_registers::GpRegisterType::{A0, A1}; +use crate::emulation_core::stack::{Stack, StackFrame}; +use serde::{Deserialize, Serialize}; /// An implementation of a datapath for the MIPS64 ISA. #[derive(Clone, PartialEq)] @@ -60,7 +67,7 @@ pub struct MipsDatapath { pub memory: Memory, pub coprocessor: MipsFpCoprocessor, - pub instruction: Instruction, + pub instruction: MipsInstruction, pub signals: ControlSignals, pub datapath_signals: DatapathSignals, pub state: DatapathState, @@ -68,6 +75,9 @@ pub struct MipsDatapath { /// The currently-active stage in the datapath. pub current_stage: Stage, + /// The stack of instructions that have been executed + pub stack: Stack, + /// Boolean value that states whether the datapath has halted. /// /// This is set in the event of any `syscall` instruction. To unset this, @@ -76,7 +86,7 @@ pub struct MipsDatapath { } /// A collection of all the data lines and wires in the datapath. -#[derive(Clone, Default, PartialEq)] +#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)] pub struct DatapathState { /// *Data line.* The currently loaded instruction. Initialized after the /// Instruction Fetch stage. @@ -161,7 +171,7 @@ pub struct DatapathState { } /// The possible stages the datapath could be in during execution. -#[derive(Clone, Copy, Default, Eq, PartialEq)] +#[derive(Clone, Copy, Default, Eq, PartialEq, Debug, Serialize, Deserialize)] pub enum Stage { #[default] InstructionFetch, @@ -185,17 +195,30 @@ impl Stage { } } +impl From for String { + fn from(val: Stage) -> String { + String::from(match val { + Stage::InstructionFetch => "writeback", + Stage::InstructionDecode => "instruction_fetch", + Stage::Execute => "instruction_decode", + Stage::Memory => "execute", + Stage::WriteBack => "memory", + }) + } +} + impl Default for MipsDatapath { fn default() -> Self { let mut datapath = MipsDatapath { registers: GpRegisters::default(), memory: Memory::default(), coprocessor: MipsFpCoprocessor::default(), - instruction: Instruction::default(), + instruction: MipsInstruction::default(), signals: ControlSignals::default(), datapath_signals: DatapathSignals::default(), state: DatapathState::default(), current_stage: Stage::default(), + stack: Stack::default(), is_halted: true, }; @@ -209,17 +232,16 @@ impl Default for MipsDatapath { impl Datapath for MipsDatapath { type RegisterData = u64; - type RegisterEnum = super::registers::GpRegisterType; - type MemoryType = Memory; - fn execute_instruction(&mut self) { + fn execute_instruction(&mut self) -> DatapathUpdateSignal { + let mut result_signals = DatapathUpdateSignal::default(); loop { // Stop early if the datapath has halted. if self.is_halted { break; } - self.execute_stage(); + result_signals |= self.execute_stage(); // This instruction is finished when the datapath has returned // to the IF stage. @@ -227,21 +249,22 @@ impl Datapath for MipsDatapath { break; } } + result_signals } - fn execute_stage(&mut self) { + fn execute_stage(&mut self) -> DatapathUpdateSignal { // If the datapath is halted, do nothing. if self.is_halted { - return; + return DatapathUpdateSignal::default(); } - match self.current_stage { + let res = match self.current_stage { Stage::InstructionFetch => self.stage_instruction_fetch(), Stage::InstructionDecode => self.stage_instruction_decode(), Stage::Execute => self.stage_execute(), Stage::Memory => self.stage_memory(), Stage::WriteBack => self.stage_writeback(), - } + }; // If the FPU has halted, reflect this in the main unit. if self.coprocessor.is_halted { @@ -249,30 +272,70 @@ impl Datapath for MipsDatapath { } self.current_stage = Stage::get_next_stage(self.current_stage); + res + } + + fn set_register_by_str(&mut self, register: &str, data: Self::RegisterData) { + let register = &mut self.registers[register]; + *register = data; } - fn get_register_by_enum(&self, register: Self::RegisterEnum) -> u64 { - self.registers[register] + fn set_fp_register_by_str(&mut self, register: &str, data: Self::RegisterData) { + let register = &mut self.coprocessor.registers[register]; + *register = data; + } + + fn initialize(&mut self, initial_pc: usize, instructions: Vec) -> Result<(), String> { + self.reset(); + self.load_instructions(instructions)?; + self.registers.pc = initial_pc as u64; + self.is_halted = false; + + Ok(()) } - fn get_memory(&self) -> &Self::MemoryType { + fn get_memory(&self) -> &Memory { &self.memory } + fn get_memory_mut(&mut self) -> &mut Memory { + &mut self.memory + } + + fn set_memory(&mut self, _ptr: u64, _data: u32) { + self.memory.store_word(_ptr, _data).unwrap_or_default(); + } + fn is_halted(&self) -> bool { self.is_halted } + fn halt(&mut self) { + self.is_halted = true; + } + fn reset(&mut self) { std::mem::take(self); } + + fn as_datapath_ref(&self) -> DatapathRef { + DatapathRef::MIPS(self) + } + + fn get_syscall_arguments(&self) -> Syscall { + Syscall::from_register_data( + self.registers[A0], + self.registers[A1], + f32::from_bits(self.coprocessor.registers[FpRegisterType::F0] as u32), + f64::from_bits(self.coprocessor.registers[FpRegisterType::F0]), + ) + } } impl MipsDatapath { // ===================== General Functions ===================== - /// Reset the datapath, load instructions into memory, and un-sets the `is_halted` - /// flag. If the process fails, an [`Err`] is returned. - pub fn initialize(&mut self, instructions: Vec) -> Result<(), String> { + /// Legacy initialize function, to be removed later. + pub fn initialize_legacy(&mut self, instructions: Vec) -> Result<(), String> { self.reset(); self.load_instructions(instructions)?; self.is_halted = false; @@ -300,13 +363,19 @@ impl MipsDatapath { /// /// Fetch the current instruction based on the given PC and load it /// into the datapath. - fn stage_instruction_fetch(&mut self) { + fn stage_instruction_fetch(&mut self) -> DatapathUpdateSignal { self.instruction_fetch(); // Upper part of datapath, PC calculation self.pc_plus_4(); - self.coprocessor.set_instruction(self.state.instruction); + + // Both state and coprocessor state always update + DatapathUpdateSignal { + changed_state: true, + changed_coprocessor_state: true, + ..Default::default() + } } /// Stage 2 of 5: Instruction Decode (ID) @@ -315,7 +384,7 @@ impl MipsDatapath { /// /// If the instruction is determined to be a `syscall`, immediately /// finish the instruction and set the `is_halted` flag. - fn stage_instruction_decode(&mut self) { + fn stage_instruction_decode(&mut self) -> DatapathUpdateSignal { self.instruction_decode(); self.sign_extend(); self.set_control_signals(); @@ -330,26 +399,45 @@ impl MipsDatapath { self.coprocessor .set_data_from_main_processor(self.state.read_data_2); - // Finish this instruction out of the datapath and halt if this is a syscall. - if let Instruction::SyscallType(_) = self.instruction { - self.is_halted = true; + // Check if we hit a syscall or breakpoint and signal it to the caller. + let (hit_syscall, hit_breakpoint) = match self.instruction { + MipsInstruction::SyscallType(instruction) => ( + instruction.funct == FUNCT_SYSCALL, + instruction.funct == FUNCT_BREAK, + ), + _ => (false, false), + }; + + // Instruction decode always involves a state update + DatapathUpdateSignal { + changed_state: true, + changed_coprocessor_state: true, + hit_syscall, + hit_breakpoint, + ..Default::default() } } /// Stage 3 of 5: Execute (EX) /// /// Execute the current instruction with some arithmetic operation. - fn stage_execute(&mut self) { + fn stage_execute(&mut self) -> DatapathUpdateSignal { self.alu(); self.calc_relative_pc_branch(); self.calc_cpu_branch_signal(); self.coprocessor.stage_execute(); + + DatapathUpdateSignal { + changed_state: true, + changed_coprocessor_state: true, + ..Default::default() + } } /// Stage 4 of 5: Memory (MEM) /// /// Read or write to memory. - fn stage_memory(&mut self) { + fn stage_memory(&mut self) -> DatapathUpdateSignal { if let MemRead::YesRead = self.signals.mem_read { self.memory_read(); } @@ -372,18 +460,78 @@ impl MipsDatapath { self.calc_general_branch_signal(); self.pick_pc_plus_4_or_relative_branch_addr_mux1(); self.set_new_pc_mux2(); + + let mut changed_stack = false; + + // If this is the first instruction, push the initial frame to the stack + if self.stack.is_empty() { + let frame = StackFrame::new( + self.state.instruction, + self.registers.pc, + self.state.pc_plus_4, + self.registers[GpRegisterType::Sp], + // self.registers[GpRegisterType::Sp], + self.registers.pc, + ); + self.stack.push(frame); + changed_stack = true; + } + + let hit_jump = matches!(self.signals.jump, Jump::YesJump); + if hit_jump { + // Add current line to stack if we call a function + let frame = StackFrame::new( + self.state.instruction, + self.registers.pc, + self.state.pc_plus_4, + self.registers[GpRegisterType::Sp], + self.state.new_pc, + ); + self.stack.push(frame); + changed_stack = true; + } + + DatapathUpdateSignal { + changed_state: true, + changed_memory: self.signals.mem_write == MemWrite::YesWrite, + changed_coprocessor_state: true, + changed_stack, + ..Default::default() + } } /// Stage 5 of 5: Writeback (WB) /// /// Write the result of the instruction's operation to a register, /// if desired. Additionally, set the PC for the next instruction. - fn stage_writeback(&mut self) { + fn stage_writeback(&mut self) -> DatapathUpdateSignal { self.coprocessor .set_fp_register_data_from_main_processor(self.state.data_result); self.register_write(); self.set_pc(); self.coprocessor.stage_writeback(); + + // check if we are writing to the stack pointer + let mut changed_stack = false; + if self.state.write_register_destination == GpRegisterType::Sp as usize { + if let Some(last_frame) = self.stack.peek() { + if self.state.register_write_data >= last_frame.frame_pointer { + // dellocating stack space + self.stack.pop(); + changed_stack = true; + } + } + } + + DatapathUpdateSignal { + changed_state: true, + changed_coprocessor_state: true, + changed_registers: true, // Always true because pc always gets updated + changed_coprocessor_registers: self.coprocessor.signals.fpu_reg_write + == FpuRegWrite::YesWrite, + changed_stack, + ..Default::default() + } } // ================== Instruction Fetch (IF) ================== @@ -407,7 +555,7 @@ impl MipsDatapath { // ================== Instruction Decode (ID) ================== /// Decode an instruction into its individual fields. fn instruction_decode(&mut self) { - match Instruction::try_from(self.state.instruction) { + match MipsInstruction::try_from(self.state.instruction) { Ok(instruction) => self.instruction = instruction, Err(message) => { self.error(&message); @@ -418,7 +566,7 @@ impl MipsDatapath { // Set the data lines based on the contents of the instruction. // Some lines will hold uninitialized values as a result. match self.instruction { - Instruction::RType(r) => { + MipsInstruction::RType(r) => { self.state.rs = r.rs as u32; self.state.rt = r.rt as u32; self.state.rd = r.rd as u32; @@ -426,19 +574,19 @@ impl MipsDatapath { self.state.shamt = r.shamt as u32; self.state.funct = r.funct as u32; } - Instruction::IType(i) => { + MipsInstruction::IType(i) => { self.state.rs = i.rs as u32; self.state.rt = i.rt as u32; self.state.rd = 0; // Placeholder self.state.imm = i.immediate as u32; } - Instruction::FpuRegImmType(i) => { + MipsInstruction::FpuRegImmType(i) => { self.state.rs = 0; // Not applicable wire self.state.rt = i.rt as u32; self.state.rd = 0; // Not applicable self.state.imm = 0; // Not applicable } - Instruction::SyscallType(s) => { + MipsInstruction::SyscallType(s) => { self.state.funct = s.funct as u32; // Not applicable: self.state.rs = 0; @@ -449,15 +597,15 @@ impl MipsDatapath { } // R-type and comparison FPU instructions exclusively use the // FPU, so these data lines do not need to be used. - Instruction::FpuRType(_) | Instruction::FpuCompareType(_) => (), - Instruction::FpuIType(i) => { + MipsInstruction::FpuRType(_) | MipsInstruction::FpuCompareType(_) => (), + MipsInstruction::FpuIType(i) => { self.state.rs = i.base as u32; self.state.imm = i.offset as u32; } - Instruction::JType(i) => { + MipsInstruction::JType(i) => { self.state.lower_26 = i.addr; } - Instruction::FpuBranchType(b) => { + MipsInstruction::FpuBranchType(b) => { self.state.imm = b.offset as u32; self.state.funct = 0; // Not applicable self.state.rs = 0; // Not applicable @@ -478,23 +626,23 @@ impl MipsDatapath { /// instruction's opcode. fn set_control_signals(&mut self) { match self.instruction { - Instruction::RType(r) => { + MipsInstruction::RType(r) => { self.set_rtype_control_signals(r); } - Instruction::IType(i) => { + MipsInstruction::IType(i) => { self.set_itype_control_signals(i); } - Instruction::JType(j) => { + MipsInstruction::JType(j) => { self.set_jtype_control_signals(j); } - Instruction::FpuRegImmType(i) => { + MipsInstruction::FpuRegImmType(i) => { self.set_fpu_reg_imm_control_signals(i); } // Main processor does nothing. - Instruction::FpuRType(_) - | Instruction::FpuCompareType(_) - | Instruction::SyscallType(_) - | Instruction::FpuBranchType(_) => { + MipsInstruction::FpuRType(_) + | MipsInstruction::FpuCompareType(_) + | MipsInstruction::SyscallType(_) + | MipsInstruction::FpuBranchType(_) => { self.signals = ControlSignals { branch: Branch::NoBranch, jump: Jump::NoJump, @@ -504,7 +652,7 @@ impl MipsDatapath { ..Default::default() }; } - Instruction::FpuIType(i) => { + MipsInstruction::FpuIType(i) => { self.set_fpu_itype_control_signals(i); } } diff --git a/src/emulation_core/mips/fp_registers.rs b/src/emulation_core/mips/fp_registers.rs new file mode 100644 index 000000000..f8660b2b5 --- /dev/null +++ b/src/emulation_core/mips/fp_registers.rs @@ -0,0 +1,172 @@ +//! Register structure and API. + +use crate::emulation_core::register::{RegisterType, Registers}; +use serde::{Deserialize, Serialize}; +use std::ops::{Index, IndexMut}; +use std::rc::Rc; +use std::str::FromStr; +use strum::IntoEnumIterator; +use strum_macros::{Display, EnumIter, EnumString}; + +/// Collection of general-purpose registers used by the datapath. +#[derive(Clone, Copy, Debug, Default, PartialEq, Serialize, Deserialize)] +pub struct FpRegisters { + pub fpr: [u64; 32], +} + +/// Specifies all of the valid registers accessible in an instance +/// of [`FpRegisters`]. +#[derive(Clone, Copy, Debug, Display, EnumIter, EnumString, Eq, PartialEq)] +#[strum(ascii_case_insensitive)] +#[strum(serialize_all = "lowercase")] +pub enum FpRegisterType { + F0 = 0, + F1 = 1, + F2 = 2, + F3 = 3, + F4 = 4, + F5 = 5, + F6 = 6, + F7 = 7, + F8 = 8, + F9 = 9, + F10 = 10, + F11 = 11, + F12 = 12, + F13 = 13, + F14 = 14, + F15 = 15, + F16 = 16, + F17 = 17, + F18 = 18, + F19 = 19, + F20 = 20, + F21 = 21, + F22 = 22, + F23 = 23, + F24 = 24, + F25 = 25, + F26 = 26, + F27 = 27, + F28 = 28, + F29 = 29, + F30 = 30, + F31 = 31, +} + +impl RegisterType for FpRegisterType { + fn get_register_name(&self) -> String { + format!("f{}", *self as u32) + } + fn is_valid_register_value(&self, _value: u64, _pc_limit: usize) -> bool { + true + } +} + +impl Registers for FpRegisters { + fn get_dyn_register_list(&self) -> Vec<(Rc, u64)> { + self.into_iter() + .map(|(register, val)| { + let register: Rc = Rc::new(register); + (register, val) + }) + .collect() + } +} + +impl ToString for FpRegisters { + fn to_string(&self) -> String { + let mut output = String::new(); + + let fpr_registers = self + .fpr + .iter() + .enumerate() + .map(|(i, inst)| format!("fpr[{i}] = {inst}")) + .collect::>() + .join("\n"); + output.push_str(&fpr_registers); + + output + } +} + +impl Index<&str> for FpRegisters { + type Output = u64; + + // Convert string to the corresponding RegistersEnum value and use this to index. + // If this is an invalid string, no enum will be returned, causing a panic as desired. + fn index(&self, index: &str) -> &Self::Output { + match FpRegisterType::from_str(index) { + Ok(register) => &self[register], + _ => panic!("{index} is not a valid register"), + } + } +} + +impl IndexMut<&str> for FpRegisters { + // Convert string to the corresponding RegistersEnum value and use this to index. + // If this is an invalid string, no enum will be returned, causing a panic as desired. + fn index_mut(&mut self, index: &str) -> &mut Self::Output { + match FpRegisterType::from_str(index) { + Ok(register) => &mut self[register], + _ => panic!("{index} is not a valid register"), + } + } +} + +impl Index for FpRegisters { + type Output = u64; + + fn index(&self, index: FpRegisterType) -> &Self::Output { + &self.fpr[index as usize] + } +} + +impl IndexMut for FpRegisters { + fn index_mut(&mut self, index: FpRegisterType) -> &mut Self::Output { + &mut self.fpr[index as usize] + } +} + +/// Iterator that is used to view each register in the register file. +/// +/// This contains a copy of all the registers and their values, and a [`FpRegisterTypeIter`], +/// as generated by [`strum::IntoEnumIterator`]. In other iterator implementations, +/// the internal state might be data like a [`FpRegisterType`]. However, since we can't +/// normally just "add 1" to get to the next register, we use an internal iterator +/// that can track the progression of one [`FpRegisterType`] to the next. +pub struct FpRegistersIter { + registers: FpRegisters, + register_iter: FpRegisterTypeIter, +} + +/// This implementation of the [`Iterator`] trait essentially wraps the existing +/// [`FpRegisterTypeIter`] so that the register type can be paired with register data. +impl Iterator for FpRegistersIter { + type Item = (FpRegisterType, u64); + + fn next(&mut self) -> Option { + match self.register_iter.next() { + Some(register_type) => Some((register_type, self.registers[register_type])), + None => None, + } + } +} + +/// [`IntoIterator`] is a standard library trait that can convert any type into +/// an [`Iterator`]. In this case, this is an instance of [`FpRegistersIter`] with all the +/// data in the registers and a new [`FpRegisterTypeIter`]. +impl IntoIterator for FpRegisters { + type Item = (FpRegisterType, u64); + type IntoIter = FpRegistersIter; + + /// Consumes the [`FpRegisters`] struct to create a new [`FpRegistersIter`] that can + /// be iterated over. + fn into_iter(self) -> Self::IntoIter { + FpRegistersIter { + registers: self, + register_iter: FpRegisterType::iter(), + } + } +} diff --git a/src/emulation_core/mips/registers.rs b/src/emulation_core/mips/gp_registers.rs similarity index 84% rename from src/emulation_core/mips/registers.rs rename to src/emulation_core/mips/gp_registers.rs index efd218a83..8d96a329c 100644 --- a/src/emulation_core/mips/registers.rs +++ b/src/emulation_core/mips/gp_registers.rs @@ -1,12 +1,17 @@ //! Register structure and API. +use crate::emulation_core::register::{RegisterType, Registers}; +use serde::{Deserialize, Serialize}; use std::ops::{Index, IndexMut}; +use std::rc::Rc; use std::str::FromStr; use strum::IntoEnumIterator; use strum_macros::{Display, EnumIter, EnumString}; +use super::memory::CAPACITY_BYTES; + /// Collection of general-purpose registers used by the datapath. -#[derive(Clone, Copy, Debug, Default, PartialEq)] +#[derive(Clone, Copy, Debug, Default, PartialEq, Serialize, Deserialize)] pub struct GpRegisters { pub pc: u64, pub gpr: [u64; 32], @@ -53,6 +58,40 @@ pub enum GpRegisterType { Ra = 31, } +impl RegisterType for GpRegisterType { + fn get_register_name(&self) -> String { + match self { + GpRegisterType::Pc => self.to_string(), + _ => format!("{} (r{})", self, *self as u32), + } + } + fn is_valid_register_value(&self, value: u64, pc_limit: usize) -> bool { + match self { + GpRegisterType::Zero => false, // Zero register is immutable + GpRegisterType::Pc => { + // Check if PC is more than the number of instructions or not word-aligned + value <= pc_limit as u64 && value % 4 == 0 + } + GpRegisterType::Sp => { + // Check if SP is more than memory capacity or not word-aligned + value <= CAPACITY_BYTES as u64 && value % 4 == 0 + } + _ => true, // Other registers are always considered valid + } + } +} + +impl Registers for GpRegisters { + fn get_dyn_register_list(&self) -> Vec<(Rc, u64)> { + self.into_iter() + .map(|(register, val)| { + let register: Rc = Rc::new(register); + (register, val) + }) + .collect() + } +} + impl ToString for GpRegisters { fn to_string(&self) -> String { let mut output = String::new(); diff --git a/src/emulation_core/mips/instruction.rs b/src/emulation_core/mips/instruction.rs index 126212d4a..f9c641dec 100644 --- a/src/emulation_core/mips/instruction.rs +++ b/src/emulation_core/mips/instruction.rs @@ -1,5 +1,10 @@ //! Abstract representation of an instruction. +use std::collections::HashMap; + +use crate::parser::parser_structs_and_enums::{FP_REGISTERS, GP_REGISTERS}; +use serde::{Deserialize, Serialize}; + use super::constants::*; /// Register (R-Type) Instruction @@ -22,7 +27,7 @@ use super::constants::*; /// determining the type of instruction executed (in `mul`, `dmul`, `dmulu`, `div`, `ddiv`, /// `ddivu`), or be used as a "hint" field for certain instructions (of note are `jr` and `jalr`). /// - function: Secondary field for determining the type of instruction executed. -#[derive(Clone, Copy, Debug, Default, PartialEq)] +#[derive(Clone, Copy, Debug, Default, PartialEq, Serialize, Deserialize)] pub struct RType { pub op: u8, pub rs: u8, @@ -32,7 +37,7 @@ pub struct RType { pub funct: u8, } -#[derive(Clone, Copy, Debug, Default, PartialEq)] +#[derive(Clone, Copy, Debug, Default, PartialEq, Serialize, Deserialize)] pub struct IType { pub op: u8, pub rs: u8, @@ -40,7 +45,7 @@ pub struct IType { pub immediate: u16, } -#[derive(Clone, Copy, Debug, Default, PartialEq)] +#[derive(Clone, Copy, Debug, Default, PartialEq, Serialize, Deserialize)] pub struct JType { pub op: u8, pub addr: u32, @@ -59,15 +64,15 @@ pub struct JType { /// /// - opcode: SPECIAL (`000000`) /// - code: Available for use as software parameters. -/// - funct: SYSCALL (`001100`) -#[derive(Clone, Copy, Debug, Default, PartialEq)] +/// - funct: SYSCALL (`001100`), BREAK (`001101`) +#[derive(Clone, Copy, Debug, Default, PartialEq, Serialize, Deserialize)] pub struct SyscallType { pub op: u8, pub code: u32, pub funct: u8, } -#[derive(Clone, Copy, Debug, Default, PartialEq)] +#[derive(Clone, Copy, Debug, Default, PartialEq, Serialize, Deserialize)] pub struct FpuRType { pub op: u8, pub fmt: u8, @@ -77,7 +82,7 @@ pub struct FpuRType { pub function: u8, } -#[derive(Clone, Copy, Debug, PartialEq)] +#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)] pub struct FpuIType { pub op: u8, pub base: u8, @@ -103,7 +108,7 @@ pub struct FpuIType { /// - sub: Operation subcode field for COP1 register immediate-mode instructions. /// - rt: CPU register - can be either source or destination. /// - fs: FPU register - can be either source or destination. -#[derive(Clone, Copy, Debug, PartialEq)] +#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)] pub struct FpuRegImmType { pub op: u8, pub sub: u8, @@ -111,7 +116,7 @@ pub struct FpuRegImmType { pub fs: u8, } -#[derive(Clone, Copy, Debug, PartialEq)] +#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)] pub struct FpuCompareType { pub op: u8, pub fmt: u8, @@ -140,7 +145,7 @@ pub struct FpuCompareType { /// - nd: Nullify delay. If set, the branch is Likely, and the delay slot instruction is not executed. (Not necessary for this project.) /// - tf: True/False. The type of condition for a comparison. /// - offset: Signed offset field used in address calculations. -#[derive(Clone, Copy, Debug, PartialEq)] +#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)] pub struct FpuBranchType { pub op: u8, pub bcc1: u8, @@ -150,8 +155,8 @@ pub struct FpuBranchType { pub offset: u16, } -#[derive(Clone, Debug, PartialEq)] -pub enum Instruction { +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub enum MipsInstruction { RType(RType), IType(IType), JType(JType), @@ -163,13 +168,13 @@ pub enum Instruction { FpuBranchType(FpuBranchType), } -impl Default for Instruction { +impl Default for MipsInstruction { fn default() -> Self { - Instruction::RType(RType::default()) + MipsInstruction::RType(RType::default()) } } -impl TryFrom for Instruction { +impl TryFrom for MipsInstruction { type Error = String; /// Based on the opcode, convert a binary instruction into a struct representation. @@ -190,12 +195,17 @@ impl TryFrom for Instruction { let funct = (value & 0x3F) as u8; match funct { - FUNCT_SYSCALL => Ok(Instruction::SyscallType(SyscallType { + FUNCT_SYSCALL => Ok(MipsInstruction::SyscallType(SyscallType { op: ((value >> 26) & 0x3F) as u8, code: ((value >> 6) & 0xFFFFF), funct: (value & 0x3F) as u8, })), - _ => Ok(Instruction::RType(RType { + FUNCT_BREAK => Ok(MipsInstruction::SyscallType(SyscallType { + op: ((value >> 26) & 0x3F) as u8, + code: (value >> 6) & 0xFFFFF, + funct: (value & 0x3F) as u8, + })), + _ => Ok(MipsInstruction::RType(RType { op: ((value >> 26) & 0x3F) as u8, rs: ((value >> 21) & 0x1F) as u8, rt: ((value >> 16) & 0x1F) as u8, @@ -219,7 +229,7 @@ impl TryFrom for Instruction { match function { // add.fmt, sub.fmt, mul.fmt, div.fmt FUNCTION_ADD | FUNCTION_SUB | FUNCTION_MUL | FUNCTION_DIV => { - Ok(Instruction::FpuRType(FpuRType { + Ok(MipsInstruction::FpuRType(FpuRType { op: ((value >> 26) & 0x3F) as u8, fmt: ((value >> 21) & 0x1F) as u8, ft: ((value >> 16) & 0x1F) as u8, @@ -231,14 +241,16 @@ impl TryFrom for Instruction { // Comparison instructions: // c.eq.fmt, c.lt.fmt, c.le.fmt, c.ngt.fmt, c.nge.fmt FUNCTION_C_EQ | FUNCTION_C_LT | FUNCTION_C_NGE | FUNCTION_C_LE - | FUNCTION_C_NGT => Ok(Instruction::FpuCompareType(FpuCompareType { - op: ((value >> 26) & 0x3F) as u8, - fmt: ((value >> 21) & 0x1F) as u8, - ft: ((value >> 16) & 0x1F) as u8, - fs: ((value >> 11) & 0x1F) as u8, - cc: ((value >> 8) & 0x7) as u8, - function: (value & 0x3F) as u8, - })), + | FUNCTION_C_NGT => { + Ok(MipsInstruction::FpuCompareType(FpuCompareType { + op: ((value >> 26) & 0x3F) as u8, + fmt: ((value >> 21) & 0x1F) as u8, + ft: ((value >> 16) & 0x1F) as u8, + fs: ((value >> 11) & 0x1F) as u8, + cc: ((value >> 8) & 0x7) as u8, + function: (value & 0x3F) as u8, + })) + } _ => Err(format!( "function `{function}` not supported for opcode {op}" )), @@ -250,7 +262,7 @@ impl TryFrom for Instruction { // Move word from coprocessor 1 (mfc1) // Move doubleword from coprocessor 1 (dmfc1) SUB_MT | SUB_DMT | SUB_MF | SUB_DMF => { - Ok(Instruction::FpuRegImmType(FpuRegImmType { + Ok(MipsInstruction::FpuRegImmType(FpuRegImmType { op: ((value >> 26) & 0x3F) as u8, sub: ((value >> 21) & 0x1F) as u8, rt: ((value >> 16) & 0x1F) as u8, @@ -260,7 +272,7 @@ impl TryFrom for Instruction { // Branch on coprocessor 1 true (bc1t) // Branch on coprocessor 1 false (bc1f) - SUB_BC => Ok(Instruction::FpuBranchType(FpuBranchType { + SUB_BC => Ok(MipsInstruction::FpuBranchType(FpuBranchType { op: ((value >> 26) & 0x3F) as u8, bcc1: ((value >> 21) & 0x1F) as u8, cc: ((value >> 18) & 0x7) as u8, @@ -276,7 +288,7 @@ impl TryFrom for Instruction { // I-Type instructions: OPCODE_ADDI | OPCODE_ADDIU | OPCODE_DADDI | OPCODE_DADDIU | OPCODE_LW | OPCODE_SW | OPCODE_LUI | OPCODE_ORI | OPCODE_ANDI | OPCODE_REGIMM | OPCODE_BEQ | OPCODE_BNE => { - Ok(Instruction::IType(IType { + Ok(MipsInstruction::IType(IType { op: ((value >> 26) & 0x3F) as u8, rs: ((value >> 21) & 0x1F) as u8, rt: ((value >> 16) & 0x1F) as u8, @@ -285,14 +297,14 @@ impl TryFrom for Instruction { } // Store/load word to Coprocessor 1 - OPCODE_SWC1 | OPCODE_LWC1 => Ok(Instruction::FpuIType(FpuIType { + OPCODE_SWC1 | OPCODE_LWC1 => Ok(MipsInstruction::FpuIType(FpuIType { op: ((value >> 26) & 0x3F) as u8, base: ((value >> 21) & 0x1F) as u8, ft: ((value >> 16) & 0x1F) as u8, offset: (value & 0xFFFF) as u16, })), - OPCODE_J | OPCODE_JAL => Ok(Instruction::JType(JType { + OPCODE_J | OPCODE_JAL => Ok(MipsInstruction::JType(JType { op: ((value >> 26) & 0x3F) as u8, addr: value & 0x03ffffff, })), @@ -301,3 +313,522 @@ impl TryFrom for Instruction { } } } + +impl MipsInstruction { + pub fn get_string_version( + value: u32, + labels: HashMap, + instruction_number: usize, + ) -> Result { + let struct_representation = match MipsInstruction::try_from(value) { + Ok(struct_representation) => struct_representation, + Err(_) => return Ok("nop".to_string()), + }; + let mut string_version = String::new(); + + log::debug!("struct_representation: {:?}", struct_representation); + // log all the fields of the struct_representation + + match struct_representation { + MipsInstruction::RType(r_type) => { + // R-type instructions: + // add, sub, mul, div + // addu + // dadd, dsub, dmul, ddiv + // daddu, dsubu, dmulu, ddivu + // or, and, sll + // slt, sltu + // jalr, jr + + let str_rs = find_register_name(r_type.rs).unwrap_or("##"); + let str_rt = find_register_name(r_type.rt).unwrap_or("##"); + let str_rd = find_register_name(r_type.rd).unwrap_or("##"); + let shamt_binary_str = format!("{:?}", r_type.shamt); + let str_shamt = shamt_binary_str.as_str(); + + match r_type.op { + OPCODE_SPECIAL => match r_type.funct { + FUNCT_SYSCALL => { + string_version.push_str("syscall"); + } + FUNCT_BREAK => { + string_version.push_str("break"); + } + FUNCT_ADD => { + string_version + .push_str(&format!("add {}, {}, {}", str_rd, str_rs, str_rt)); + } + FUNCT_ADDU => { + string_version + .push_str(&format!("addu {}, {}, {}", str_rd, str_rs, str_rt)); + } + FUNCT_DADD => { + string_version + .push_str(&format!("dadd {}, {}, {}", str_rd, str_rs, str_rt)); + } + FUNCT_DADDU => { + string_version + .push_str(&format!("daddu {}, {}, {}", str_rd, str_rs, str_rt)); + } + FUNCT_SUB => { + string_version + .push_str(&format!("sub {}, {}, {}", str_rd, str_rs, str_rt)); + } + FUNCT_DSUB => { + string_version + .push_str(&format!("dsub {}, {}, {}", str_rd, str_rs, str_rt)); + } + FUNCT_DSUBU => { + string_version + .push_str(&format!("dsubu {}, {}, {}", str_rd, str_rs, str_rt)); + } + FUNCT_AND => { + string_version + .push_str(&format!("and {}, {}, {}", str_rd, str_rs, str_rt)); + } + FUNCT_OR => { + string_version + .push_str(&format!("or {}, {}, {}", str_rd, str_rs, str_rt)); + } + FUNCT_SLL => { + // if all the fields are 0, then it is a nop + if r_type.rd == 0 && r_type.rt == 0 && r_type.shamt == 0 { + string_version.push_str("nop"); + } else { + string_version.push_str(&format!( + "sll {}, {}, {}", + str_rd, str_rt, str_shamt + )); + } + } + FUNCT_SLT => { + string_version + .push_str(&format!("slt {}, {}, {}", str_rd, str_rs, str_rt)); + } + FUNCT_SLTU => { + string_version + .push_str(&format!("sltu {}, {}, {}", str_rd, str_rs, str_rt)); + } + FUNCT_SOP30 => match r_type.shamt { + ENC_MUL => { + string_version + .push_str(&format!("mul {}, {}, {}", str_rd, str_rs, str_rt)); + } + _ => { + string_version.push_str("###"); + } + }, + FUNCT_SOP31 => match r_type.shamt { + ENC_MULU => { + string_version + .push_str(&format!("mulu {}, {}, {}", str_rd, str_rs, str_rt)); + } + _ => { + string_version.push_str("###"); + } + }, + FUNCT_SOP32 => { + string_version + .push_str(&format!("div {}, {}, {}", str_rd, str_rs, str_rt)); + } + FUNCT_SOP33 => match r_type.shamt { + ENC_DIVU => { + string_version + .push_str(&format!("divu {}, {}, {}", str_rd, str_rs, str_rt)); + } + _ => { + string_version.push_str("###"); + } + }, + FUNCT_SOP34 => match r_type.shamt { + ENC_DMUL => { + string_version + .push_str(&format!("dmul {}, {}, {}", str_rd, str_rs, str_rt)); + } + _ => { + string_version.push_str("###"); + } + }, + FUNCT_SOP35 => match r_type.shamt { + ENC_DMULU => { + string_version + .push_str(&format!("dmulu {}, {}, {}", str_rd, str_rs, str_rt)); + } + _ => { + string_version.push_str("###"); + } + }, + FUNCT_SOP36 => match r_type.shamt { + ENC_DIV => { + string_version + .push_str(&format!("ddiv {}, {}, {}", str_rd, str_rs, str_rt)); + } + _ => { + string_version.push_str("###"); + } + }, + FUNCT_SOP37 => match r_type.shamt { + ENC_DIVU => { + string_version + .push_str(&format!("ddivu {}, {}, {}", str_rd, str_rs, str_rt)); + } + _ => { + string_version.push_str("###"); + } + }, + FUNCT_JR => { + string_version.push_str(&format!("jr {}", str_rs)); + } + _ => { + string_version.push_str("###"); + } + }, + _ => { + string_version.push_str("###"); + } + } + } + MipsInstruction::IType(i_type) => { + // I-Type instructions: + // addi, addiu, daddi, daddiu + // lw, sw + // lui + // ori, andi + // regimm + // beq, bne + + let str_rs = find_register_name(i_type.rs).unwrap_or("##"); + let str_rt = find_register_name(i_type.rt).unwrap_or("##"); + + // Check if immediate is negative + let mut str_immediate = format!("{}", i_type.immediate); + if i_type.immediate & 0x800 != 0 { + str_immediate = format!( + "-{}", + (!(i_type.immediate) + 1) & 0b00000000000000000000111111111111 + ); + } + + match i_type.op { + OPCODE_ADDI => { + string_version + .push_str(&format!("addi {}, {}, {}", str_rt, str_rs, str_immediate)); + } + OPCODE_ADDIU => { + string_version + .push_str(&format!("addiu {}, {}, {}", str_rt, str_rs, str_immediate)); + } + OPCODE_DADDI => { + string_version + .push_str(&format!("daddi {}, {}, {}", str_rt, str_rs, str_immediate)); + } + OPCODE_DADDIU => { + string_version + .push_str(&format!("daddiu {}, {}, {}", str_rt, str_rs, str_immediate)); + } + OPCODE_LW => { + string_version + .push_str(&format!("lw {}, {}({})", str_rt, str_immediate, str_rs)); + } + OPCODE_SW => { + string_version + .push_str(&format!("sw {}, {}({})", str_rt, str_immediate, str_rs)); + } + OPCODE_LUI => { + let str_immediate = i_type.immediate as u32; + string_version.push_str(&format!("lui {}, 0x{:x}", str_rt, str_immediate)); + } + OPCODE_ORI => { + string_version + .push_str(&format!("ori {}, {}, {}", str_rt, str_rs, str_immediate)); + } + OPCODE_ANDI => { + string_version + .push_str(&format!("andi {}, {}, {}", str_rt, str_rs, str_immediate)); + } + OPCODE_REGIMM => { + // rt field is used as the register immediate subcode + match i_type.rt { + RMSUB_DAHI => { + string_version + .push_str(&format!("dahi {}, {}", str_rs, str_immediate)); + } + RMSUB_DATI => { + string_version + .push_str(&format!("dati {}, {}", str_rs, str_immediate)); + } + _ => { + string_version.push_str("###"); + } + } + } + OPCODE_BEQ => { + let mut str_label = String::new(); + for label in labels { + if label.1 == (i_type.immediate as usize) + instruction_number * 4 { + str_label = label.0; + } + } + string_version + .push_str(&format!("beq {}, {}, {}", str_rs, str_rt, str_label)); + } + OPCODE_BNE => { + let mut str_label = String::new(); + for label in labels { + if label.1 == (i_type.immediate as usize) + instruction_number * 4 { + str_label = label.0; + } + } + string_version + .push_str(&format!("bne {}, {}, {}", str_rs, str_rt, str_label)); + } + _ => { + string_version.push_str("###"); + } + } + } + MipsInstruction::JType(j_type) => { + // J-Type instructions: + // j, jal + let addr = j_type.addr; + + let mut str_addr = addr.to_string(); + + for label in labels { + if label.1 == (addr as usize) * 4 { + str_addr = label.0; + } + } + + match j_type.op { + OPCODE_J => { + string_version.push_str(&format!("j {}", str_addr)); + } + OPCODE_JAL => { + string_version.push_str(&format!("jal {}", str_addr)); + } + _ => { + string_version.push_str("###"); + } + } + } + MipsInstruction::SyscallType(syscall_type) => { + // Syscall ("System Call") Instruction + match syscall_type.funct { + FUNCT_SYSCALL => { + string_version.push_str("syscall"); + } + // FUNCT_BREAK => { + // string_version.push_str("break"); + // } + _ => { + string_version.push_str("###"); + } + } + } + MipsInstruction::FpuRType(fpu_r_type) => { + // FPU R-Type instructions: + // add.fmt, sub.fmt, mul.fmt, div.fmt + + let str_fs = find_register_name_fp(fpu_r_type.fs).unwrap_or("##"); + let str_ft = find_register_name_fp(fpu_r_type.ft).unwrap_or("##"); + let str_fd = find_register_name_fp(fpu_r_type.fd).unwrap_or("##"); + + match fpu_r_type.fmt { + FMT_SINGLE => match fpu_r_type.function { + FUNCTION_ADD => { + string_version + .push_str(&format!("add.s {}, {}, {}", str_fd, str_fs, str_ft)); + } + FUNCTION_SUB => { + string_version + .push_str(&format!("sub.s {}, {}, {}", str_fd, str_fs, str_ft)); + } + FUNCTION_MUL => { + string_version + .push_str(&format!("mul.s {}, {}, {}", str_fd, str_fs, str_ft)); + } + FUNCTION_DIV => { + string_version + .push_str(&format!("div.s {}, {}, {}", str_fd, str_fs, str_ft)); + } + _ => { + string_version.push_str("###"); + } + }, + FMT_DOUBLE => match fpu_r_type.function { + FUNCTION_ADD => { + string_version + .push_str(&format!("add.d {}, {}, {}", str_fd, str_fs, str_ft)); + } + FUNCTION_SUB => { + string_version + .push_str(&format!("sub.d {}, {}, {}", str_fd, str_fs, str_ft)); + } + FUNCTION_MUL => { + string_version + .push_str(&format!("mul.d {}, {}, {}", str_fd, str_fs, str_ft)); + } + FUNCTION_DIV => { + string_version + .push_str(&format!("div.d {}, {}, {}", str_fd, str_fs, str_ft)); + } + _ => { + string_version.push_str("###"); + } + }, + _ => { + string_version.push_str("###"); + } + } + } + MipsInstruction::FpuIType(fpu_i_type) => { + // FPU I-Type instructions: + // swc1, lwc1 + let str_base = find_register_name(fpu_i_type.base).unwrap_or("##"); // base is a GPRegister + let str_ft = find_register_name_fp(fpu_i_type.ft).unwrap_or("##"); + let str_offset = fpu_i_type.offset.to_string(); + let str_offset = str_offset.as_str(); + + match fpu_i_type.op { + OPCODE_SWC1 => { + string_version + .push_str(&format!("swc1 {}, {}({})", str_ft, str_offset, str_base)); + } + OPCODE_LWC1 => { + string_version + .push_str(&format!("lwc1 {}, {}({})", str_ft, str_offset, str_base)); + } + _ => { + string_version.push_str("###"); + } + } + } + MipsInstruction::FpuRegImmType(fpu_reg_imm_type) => { + // FPU Register-Immediate instructions: + // mtc1, dmtc1, mfc1, dmfc1 + + let str_rt = find_register_name(fpu_reg_imm_type.rt).unwrap_or("##"); + let str_fs = find_register_name_fp(fpu_reg_imm_type.fs).unwrap_or("##"); + + match fpu_reg_imm_type.sub { + SUB_MT => { + string_version.push_str(&format!("mtc1 {}, {}", str_rt, str_fs)); + } + SUB_DMT => { + string_version.push_str(&format!("dmtc1 {}, {}", str_rt, str_fs)); + } + SUB_MF => { + string_version.push_str(&format!("mfc1 {}, {}", str_rt, str_fs)); + } + SUB_DMF => { + string_version.push_str(&format!("dmfc1 {}, {}", str_rt, str_fs)); + } + _ => { + string_version.push_str("###"); + } + } + } + MipsInstruction::FpuBranchType(fpu_branch_type) => { + // FPU Branching instructions: + // bc1t, bc1f + // TODO add support for labels + + match fpu_branch_type.bcc1 { + SUB_BC => { + let str_offset = fpu_branch_type.offset.to_string(); + let str_offset = str_offset.as_str(); + + match fpu_branch_type.tf { + 1 => { + string_version.push_str(&format!("bc1t {}", str_offset)); + } + _ => { + string_version.push_str(&format!("bc1f {}", str_offset)); + } + } + } + _ => { + string_version.push_str("###"); + } + } + } + MipsInstruction::FpuCompareType(fpu_compare_type) => { + // FPU Comparison instructions: + // c.eq.fmt, c.lt.fmt, c.le.fmt, c.ngt.fmt, c.nge.fmt + + let str_fs = find_register_name_fp(fpu_compare_type.fs).unwrap_or("##"); + let str_ft = find_register_name_fp(fpu_compare_type.ft).unwrap_or("##"); + + match fpu_compare_type.fmt { + FMT_SINGLE => match fpu_compare_type.function { + FUNCTION_C_EQ => { + string_version.push_str(&format!("c.eq.s {}, {}", str_fs, str_ft)); + } + FUNCTION_C_LT => { + string_version.push_str(&format!("c.lt.s {}, {}", str_fs, str_ft)); + } + FUNCTION_C_NGE => { + string_version.push_str(&format!("c.nge.s {}, {}", str_fs, str_ft)); + } + FUNCTION_C_LE => { + string_version.push_str(&format!("c.le.s {}, {}", str_fs, str_ft)); + } + FUNCTION_C_NGT => { + string_version.push_str(&format!("c.ngt.s {}, {}", str_fs, str_ft)); + } + _ => { + string_version.push_str("###"); + } + }, + FMT_DOUBLE => match fpu_compare_type.function { + FUNCTION_C_EQ => { + string_version.push_str(&format!("c.eq.d {}, {}", str_fs, str_ft)); + } + FUNCTION_C_LT => { + string_version.push_str(&format!("c.lt.d {}, {}", str_fs, str_ft)); + } + FUNCTION_C_NGE => { + string_version.push_str(&format!("c.nge.d {}, {}", str_fs, str_ft)); + } + FUNCTION_C_LE => { + string_version.push_str(&format!("c.le.d {}, {}", str_fs, str_ft)); + } + FUNCTION_C_NGT => { + string_version.push_str(&format!("c.ngt.d {}, {}", str_fs, str_ft)); + } + _ => { + string_version.push_str("###"); + } + }, + _ => { + string_version.push_str("###"); + } + } + } + } + Ok(string_version) + } +} + +pub fn find_register_name(binary: u8) -> Option<&'static str> { + for register in GP_REGISTERS { + if register.binary == binary { + // If a match is found, return the first name in the names array + return Some(register.names[0]); + } + } + // If no match is found, return None + None +} + +pub fn find_register_name_fp(binary: u8) -> Option<&'static str> { + for register in FP_REGISTERS { + if register.binary == binary { + // If a match is found, return the first name in the names array + return Some(register.name); + } + } + // If no match is found, return None + None +} diff --git a/src/emulation_core/mips/line_info.rs b/src/emulation_core/mips/line_info.rs deleted file mode 100644 index c28c0a8aa..000000000 --- a/src/emulation_core/mips/line_info.rs +++ /dev/null @@ -1,334 +0,0 @@ -//! Module for mapping lines in the visual datapath to information -//! and variables in the coded datapath. - -use super::super::datapath::VisualDatapath; -use super::datapath::MipsDatapath; - -/// A collection of data surrounding a line in the visual datapath. -pub struct LineInformation { - pub title: String, - pub description: String, - - /// The value stored in a line. This may not be a 64-bit value, but should - /// refer to the `bits` field to determine how many bits on the line are - /// relevant to be displayed. - pub value: u64, - - /// The number of bits on a given line. - pub bits: u64, -} - -impl VisualDatapath for MipsDatapath { - type LineInformation = LineInformation; - - fn visual_line_to_data(&self, variable: &str) -> LineInformation { - match variable { - "alu_input2" => LineInformation { - title: String::from("ALU Input 2"), - description: String::from("The second input to the ALU. This is determined by the ALUSrc control signal to select between register data, a sign-extended and left-shifted immediate value, or a zero-extended immediate value."), - value: self.state.alu_input2, - bits: 64, - }, - "alu_result" => LineInformation { - title: String::from("ALU Result"), - description: String::from("The result of the calculation performed by the ALU. This is used either as an address to access memory or as a value that is saved into a register."), - value: self.state.alu_result, - bits: 64, - }, - "data_result" => LineInformation { - title: String::from("Writeback Data"), - description: String::from("After finishing processing the instruction, this will either be the ALU result, data from memory, or PC + 4, based on the MemToReg control signal. This data is saved into registers."), - value: self.state.data_result, - bits: 64, - }, - "fpu_alu_result" => LineInformation { - title: String::from("Floating-Point ALU Result"), - description: String::from("The result of the calculation performed by the floating-point ALU. This is used as an option to be written to a floating-point register, based on the DataWrite and FpuMemToReg control signals."), - value: self.coprocessor.state.alu_result, - bits: 64, - }, - "fpu_branch_decision" => LineInformation { - title: String::from("FPU Branch Decision"), - description: String::from("Based on the true/false branch flag, determines whether to branch. (The FpuBranch control signal must also be set.)"), - value: self.coprocessor.state.condition_code_mux as u64, - bits: 1, - }, - "fpu_branch_flag" => LineInformation { - title: String::from("Instruction [16] (True/False Branch Flag)"), - description: String::from("The true/false branch flag of branching coprocessor instructions. This flag specifies whether a floating-point branch instruction is BC1T or BC1F."), - value: self.coprocessor.state.branch_flag as u64, - bits: 1, - }, - "fpu_comparator_result" => LineInformation { - title: String::from("Floating-Point Comparator Result"), - description: String::from("The result of the comparison of two floating-point values. This is routed to the \"Condition Code\" (cc) register, and will be written there if the CcWrite control signal is set."), - value: self.coprocessor.state.comparator_result, - bits: 64, - }, - "fpu_condition_code" => LineInformation { - title: String::from("Condition Code Value"), - description: String::from("Data retrieved from the \"Condition Code\" (cc) register. This specifies whether a previous conditional instruction was true or false."), - value: self.coprocessor.state.condition_code_bit as u64, - bits: 1, - }, - "fpu_condition_code_inverted" => LineInformation { - title: String::from("Condition Code Value (Inverted)"), - description: String::from("Inverted form of the condition code register value."), - value: self.coprocessor.state.condition_code_bit_inverted as u64, - bits: 1, - }, - "fpu_data" => LineInformation { - title: String::from("Floating-Point Data Register Value"), - description: String::from("Data retrieved from the \"Data\" register. This register acts as a means to communicate data between the main processor and floating-point coprocessor in MTC1 and MFC1 instructions."), - value: self.coprocessor.state.fmt as u64, - bits: 64, - }, - "fpu_data_writeback" => LineInformation { - title: String::from("Floating-Point Data Writeback"), - description: String::from("The value from the floating-point unit's \"Data\" register. Depending on the FpuRegWidth control signal, this will be 64-bit data or sign-extended 32-bit data."), - value: self.coprocessor.state.data_writeback, - bits: 64, - }, - "fpu_destination" => LineInformation { - title: String::from("Floating-Point Write Register"), - description: String::from("The register that will be written to, assuming FpuRegWrite is set. Depending on the FpuRegDst control signal, this will consist of the fs, ft, or fd register."), - value: self.coprocessor.state.destination as u64, - bits: 5, - }, - "fpu_fd" => LineInformation { - title: String::from("Instruction [10-6] (fd)"), - description: String::from("The fd field. Depending on the FpuRegDst control signal, this will be the register written to in a floating-point operation. This register is used as the destination for most floating-point arithmetic instructions."), - value: self.coprocessor.state.fd as u64, - bits: 5, - }, - "fpu_fmt" => LineInformation { - title: String::from("Instruction [25-21] (fmt)"), - description: String::from("The fmt field. This is used to distinguish between single-precision and double-precision floating-point instructions."), - value: self.coprocessor.state.fmt as u64, - bits: 5, - }, - "fpu_fp_register_data_from_main_processor" => LineInformation { - title: String::from("Writeback Data (To Floating-Point Coprocessor)"), - description: String::from("This data is written to a floating-point register, given FpuMemToReg is set. This line allows data to load from memory to a floating-point register, specifically in the case of the LWC1 instruction."), - value: self.coprocessor.state.fp_register_data_from_main_processor, - bits: 64, - }, - "fpu_fp_register_to_memory" => LineInformation { - title: String::from("Memory Write Data (from FPU)"), - description: String::from("If the MemWriteSrc control signal is set, this data will be written to memory. This is used for the SWC1 instruction."), - value: self.coprocessor.state.fp_register_to_memory, - bits: 64, - }, - "fpu_fs" => LineInformation { - title: String::from("Instruction [15-11] (fs)"), - description: String::from("The fs field. Contains the first register to be read for a floating-point instruction."), - value: self.coprocessor.state.fs as u64, - bits: 5, - }, - "fpu_ft" => LineInformation { - title: String::from("Instruction [20-16] (ft)"), - description: String::from("The ft field. Contains the second register to be read for a floating-point instruction."), - value: self.coprocessor.state.ft as u64, - bits: 5, - }, - "fpu_new_data" => LineInformation { - title: String::from("New Floating-Point Data Register Value"), - description: String::from("Data sent to the \"Data\" register. Depending on the DataSrc control signal, this will either be data from the main processor or the floating-point coprocessor. This register acts as a means to communicate data between the main processor and floating-point coprocessor in MTC1 and MFC1 instructions."), - value: self.coprocessor.state.fmt as u64, - bits: 64, - }, - "fpu_read_data_1" => LineInformation { - title: String::from("FPU Read Data 1"), - description: String::from("Data retrieved from the register specified by the fs instruction field. This is used as the first inputs to the floating-point ALU and comparator. This can additionally be written to the \"Data\" register, based on the DataSrc and DataWrite control signals."), - value: self.coprocessor.state.read_data_1, - bits: 64, - }, - "fpu_read_data_2" => LineInformation { - title: String::from("FPU Read Data 2"), - description: String::from("Data retrieved from the register specified by the ft instruction field. This is used as the second inputs to the floating-point ALU and comparator. This can additionally be used as data to be written to memory, based on the MemWriteSrc control signal."), - value: self.coprocessor.state.read_data_2, - bits: 64, - }, - "fpu_register_write_data" => LineInformation { - title: String::from("FPU Register Write Data"), - description: String::from("Data that will be written to a floating-point register, given that FpuRegWrite is set."), - value: self.coprocessor.state.register_write_data, - bits: 64, - }, - "fpu_register_write_mux_to_mux" => LineInformation { - title: String::from("FPU Register Write Data (When FpuMemToReg is Unset)"), - description: String::from("Based on the DataWrite control signal, this will either be the result of the floating-point ALU or the contents of the \"Data\" register. (The \"Data\" register is used for transferring data between the processor and floating-point coprocessor.)"), - value: self.coprocessor.state.register_write_mux_to_mux, - bits: 64, - }, - "fpu_sign_extend_data" => LineInformation { - title: String::from("Floating-Point Data Register Value (Sign-Extended)"), - description: String::from("In the case where FpuRegWidth indicates a 32-bit width, this is the bottom 32 bits of the value from the \"Data\" register, then sign-extended to 64 bits."), - value: self.coprocessor.state.sign_extend_data, - bits: 64, - }, - "funct" => LineInformation { - title: String::from("Instruction [5-0] (funct)"), - description: String::from("The funct field. Contains the type of operation to execute for R-type instructions."), - value: self.state.funct as u64, - bits: 6, - }, - "imm" => LineInformation { - title: String::from("Instruction [15-0] (immediate)"), - description: String::from("The immediate field. Contains the 16-bit constant value used for I-type instructions."), - value: self.state.imm as u64, - bits: 16, - }, - "instruction" => LineInformation { - title: String::from("Instruction"), - description: String::from("The currently-loaded instruction. This is broken down into different fields, where each field serves a different purpose in identifying what the instruction does."), - value: self.state.instruction as u64, - bits: 32, - }, - "jump_address" => LineInformation { - title: String::from("Jump Address"), - description: String::from("The concatenation of the upper 36 bits of PC + 4 with the lower 26 bits of the instruction, shifted left by 2. This is used as the new PC value for J-type instructions."), - value: self.state.jump_address, - bits: 64, - }, - "lower_26" => LineInformation { - title: String::from("Instruction [25-0]"), - description: String::from("The lower 26 bits of instruction. This is used as part of the new PC value for J-type instructions."), - value: self.state.lower_26 as u64, - bits: 26, - }, - "lower_26_shifted_left_by_2" => LineInformation { - title: String::from("Instruction [25-0] << 2"), - description: String::from("The lower 26 bits of instruction, shifted left by 2. This is used as part of the new PC value for J-type instructions."), - value: self.state.lower_26_shifted_left_by_2 as u64, - bits: 28, - }, - "mem_mux1_to_mem_mux2" => LineInformation { - title: String::from("Relative PC Address"), - description: String::from("Based on the control signals for branching and jumping, this address may be the next PC value. This is used for general non-branching instructions or branch-type instructions."), - value: self.state.mem_mux1_to_mem_mux2, - bits: 64, - }, - "memory_data" => LineInformation { - title: String::from("Memory Data"), - description: String::from("The data retrieved from memory, given that the MemRead control signal is set. This may be 32 bits or 64 bits, depending on the RegWidth control signal."), - value: self.state.memory_data, - bits: 64, - }, - "new_pc" => LineInformation { - title: String::from("New Program Counter"), - description: String::from("The address of the next instruction to execute. In other words, the next value of the program counter (PC) register."), - value: self.state.new_pc, - bits: 64, - }, - "pc" => LineInformation { - title: String::from("Program Counter"), - description: String::from("The address of the currently-executing instruction."), - value: self.registers.pc, - bits: 64, - }, - "pc_plus_4" => LineInformation { - title: String::from("PC + 4"), - description: String::from("The address of the currently-executing instruction, plus 4. By default, this will become the next value of the PC register. However, a different address may be used in the case of a branch or jump instruction."), - value: self.state.pc_plus_4, - bits: 64, - }, - "pc_plus_4_upper" => LineInformation { - title: String::from("PC + 4 [63-28]"), - description: String::from("The upper 36 bits of PC + 4. This is to be concatenated with the lower 26 bits of the instruction to calculate a jump address."), - value: self.state.pc_plus_4 & 0xffff_ffff_f000_0000 >> 28, - bits: 36, - }, - "ra_id" => LineInformation { - title: String::from("Return Address Register Index"), - description: String::from("The value 31. This represents the thirty-second register, the return address register ($ra)."), - value: 31, - bits: 5, - }, - "rd" => LineInformation { - title: String::from("Instruction [15-11] (rd)"), - description: String::from("The rd field. Depending on the RegDst control signal, this will be the register written to for an instruction. This register is used as the destination for most R-type instructions."), - value: self.state.rd as u64, - bits: 5, - }, - "read_data_1" => LineInformation { - title: String::from("Read Data 1"), - description: String::from("Data retrieved from the register specified by the rs instruction field. Based on the instruction, this may be used as the first input to the ALU, or the next value of the PC register."), - value: self.state.read_data_1, - bits: 64, - }, - "read_data_2" => LineInformation { - title: String::from("Read Data 2"), - description: String::from("Data retrieved from the register specified by the rt instruction field. Based on the instruction, this may be used as the second input to the ALU, data written to memory, or data transferred to the floating-point coprocessor."), - value: self.state.read_data_2, - bits: 64, - }, - "register_write_data" => LineInformation { - title: String::from("Register Write Data"), - description: String::from("Data that will be written to a general-purpose register, given that RegWrite is set."), - value: self.state.register_write_data, - bits: 64, - }, - "relative_pc_branch" => LineInformation { - title: String::from("Relative PC Branch Address"), - description: String::from("The relative address used in branch instructions. This is the sum of PC + 4 and the sign-extended immediate value, shifted left by 2."), - value: self.state.relative_pc_branch, - bits: 64, - }, - "rs" => LineInformation { - title: String::from("Instruction [25-21] (rs)"), - description: String::from("The rs field. Contains the first register to be read for an instruction."), - value: self.state.rs as u64, - bits: 5, - }, - "rt" => LineInformation { - title: String::from("Instruction [20-16] (rt)"), - description: String::from("The rt field. Contains the second register to be read for an instruction."), - value: self.state.rt as u64, - bits: 5, - }, - "shamt" => LineInformation { - title: String::from("Instruction [10-6] (shamt)"), - description: String::from("The shamt (\"shift amount\") field. Specifies the number of bits to shift for those instructions that perform bit-shifting."), - value: self.state.shamt as u64, - bits: 5, - }, - "sign_extend" => LineInformation { - title: String::from("Sign-Extended Immediate"), - description: String::from("The immediate field, sign-extended to a 64-bit value."), - value: self.state.sign_extend, - bits: 64, - }, - "sign_extend_shift_left_by_2" => LineInformation { - title: String::from("Sign-Extended Immediate << 2"), - description: String::from("The immediate field, sign-extended to a 64-bit value, then shifted left by 2."), - value: self.state.sign_extend_shift_left_by_2, - bits: 64, - }, - "write_data" => LineInformation { - title: String::from("Memory Write Data"), - description: String::from("Given that the MemWrite control signal is set, this data will be written to memory."), - value: self.state.write_data, - bits: 64, - }, - "write_register" => LineInformation { - title: String::from("Write Register"), - description: String::from("The register that will be written to, assuming RegWrite is set. Depending on the RegDst control signal, this will consist of the rs, rt, or rd register, or 31 (indicating the $ra register)."), - value: self.state.write_register_destination as u64, - bits: 5, - }, - "zero_extended_immediate" => LineInformation { - title: String::from("Zero-Extended Immediate"), - description: String::from("The immediate field, zero-extended to a 64-bit value."), - value: self.state.imm as u64, - bits: 64, - }, - _ => LineInformation { - title: String::from("[Title]"), - description: String::from("[Description]"), - value: 0, - bits: 0, - }, - } - } -} diff --git a/src/emulation_core/mips/memory.rs b/src/emulation_core/mips/memory.rs index 2abcf1921..7c2d00efd 100644 --- a/src/emulation_core/mips/memory.rs +++ b/src/emulation_core/mips/memory.rs @@ -1,9 +1,11 @@ //! Data and instruction memory implementation and API. +use serde::{Deserialize, Serialize}; + // pub const CAPACITY_BYTES: usize = 2^12; // 4KB pub const CAPACITY_BYTES: usize = 64 * 1024; // 64 KB -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct Memory { pub memory: Vec, } @@ -46,6 +48,29 @@ impl Memory { } } + // A byte is 8 bits. + pub fn store_byte(&mut self, address: u64, data: u8) -> Result<(), String> { + let address = address as usize; + + self.check_valid_address(address)?; + + self.memory[address] = data; + + Ok(()) + } + + // A word is 32 bits. + pub fn store_half(&mut self, address: u64, data: u16) -> Result<(), String> { + let address = address as usize; + + self.check_valid_address(address)?; + + self.memory[address] = ((data >> 8) & 0b11111111) as u8; + self.memory[address + 1] = (data & 0b11111111) as u8; + + Ok(()) + } + // A word is 32 bits. pub fn store_word(&mut self, address: u64, data: u32) -> Result<(), String> { let address = address as usize; @@ -71,6 +96,31 @@ impl Memory { Ok(()) } + // A byte is 8 bits. + pub fn load_byte(&self, address: u64) -> Result { + let address = address as usize; + + self.check_valid_address(address)?; + + let mut result: u8 = 0; + result |= self.memory[address]; + + Ok(result) + } + + // A half-word is 16 bits. + pub fn load_half(&self, address: u64) -> Result { + let address = address as usize; + + self.check_valid_address(address)?; + + let mut result: u16 = 0; + result |= (self.memory[address] as u16) << 8; + result |= self.memory[address + 1] as u16; + + Ok(result) + } + // A word is 32 bits. pub fn load_word(&self, address: u64) -> Result { let address = address as usize; @@ -109,37 +159,74 @@ impl Memory { Ok(result) } - pub fn generate_formatted_hex(&self) -> String { + pub fn generate_formatted_hex(&self, end_address: usize) -> String { + if end_address == 0 { + return "".to_string(); + } + let iterator = MemoryIter::new(self, 0, end_address); + let mut string: String = "".to_string(); - let mut base = 0; - while base < self.memory.len() { - string.push_str(&format!("0x{base:04x}:\t\t")); + for (address, words) in iterator { + string.push_str(&format!("0x{address:04x}:\t\t")); let mut char_version: String = "".to_string(); - for offset in 0..4 { - let word_address = base as u64 + (offset * 4); - if let Ok(word) = self.load_word(word_address) { - string.push_str(&format!("{word:08x}\t")); - char_version.push_str(&convert_word_to_chars(word)) - }; + for word in words { + string.push_str(&format!("{:08x}\t", word)); + char_version.push_str(&Self::convert_word_to_chars(word)); } + string.push_str(&format!("{char_version}\n")); - base += 16; } + string } + + pub fn convert_word_to_chars(word: u32) -> String { + let mut chars = "".to_string(); + for shift in (0..4).rev() { + let byte = (word >> (shift * 8)) as u8; + if byte > 32 && byte < 127 { + chars.push(byte as char); + } else { + chars.push('.'); + } + } + chars + } +} + +pub struct MemoryIter<'a> { + memory: &'a Memory, + current_address: usize, + end_address: usize, +} + +impl<'a> MemoryIter<'a> { + pub fn new(memory: &'a Memory, current_address: usize, end_address: usize) -> MemoryIter<'a> { + MemoryIter { + memory, + current_address, + end_address, + } + } } -fn convert_word_to_chars(word: u32) -> String { - let mut chars = "".to_string(); - for shift in (0..4).rev() { - let byte = (word >> (shift * 8)) as u8; - if byte > 32 && byte < 127 { - chars.push(byte as char); +impl<'a> Iterator for MemoryIter<'a> { + // Words are 32 bits + type Item = (usize, Vec); + fn next(&mut self) -> Option { + self.current_address = (self.current_address + 3) & !3; + if self.current_address + 16 <= self.end_address { + let address = self.current_address; + let words = (0..4) + .map(|i| self.memory.load_word(address as u64 + (i * 4)).unwrap()) + .collect(); + + self.current_address += 16; + Some((address, words)) } else { - chars.push('.'); + None } } - chars } diff --git a/src/emulation_core/register.rs b/src/emulation_core/register.rs new file mode 100644 index 000000000..5b38e59c8 --- /dev/null +++ b/src/emulation_core/register.rs @@ -0,0 +1,19 @@ +use std::fmt::Display; +use std::rc::Rc; + +pub trait RegisterType: ToString + Display { + fn get_register_name(&self) -> String; + + fn is_valid_register_value(&self, value: u64, pc_limit: usize) -> bool; +} + +pub trait Registers { + fn get_dyn_register_list(&self) -> Vec<(Rc, u64)>; +} + +impl PartialEq for dyn RegisterType { + fn eq(&self, other: &Self) -> bool { + // The register names are unique, so use them to compare for equality + self.get_register_name() == other.get_register_name() + } +} diff --git a/src/emulation_core/riscv.rs b/src/emulation_core/riscv.rs new file mode 100644 index 000000000..8e83c2b45 --- /dev/null +++ b/src/emulation_core/riscv.rs @@ -0,0 +1,10 @@ +//! All facets of this project's implementation of the MIPS64 ISA, including +//! the datapath, control signals, registers, and memory. + +pub mod constants; +pub mod control_signals; +pub mod coprocessor; +pub mod datapath; +pub mod datapath_signals; +pub mod instruction; +pub mod registers; diff --git a/src/emulation_core/riscv/constants.rs b/src/emulation_core/riscv/constants.rs new file mode 100644 index 000000000..0cb9f0a44 --- /dev/null +++ b/src/emulation_core/riscv/constants.rs @@ -0,0 +1,44 @@ +/// Used for R-type instructions. +pub const OPCODE_OP: u8 = 0b0110011; +pub const OPCODE_OP_32: u8 = 0b0111011; +pub const OPCODE_OP_FP: u8 = 0b1010011; + +/// Used for I-type instructions. +pub const OPCODE_IMM: u8 = 0b0010011; +pub const OPCODE_IMM_32: u8 = 0b0011011; +// JALR +pub const OPCODE_JALR: u8 = 0b1100111; +// LOAD +pub const OPCODE_LOAD: u8 = 0b0000011; +pub const OPCODE_LOAD_FP: u8 = 0b0000111; +// SYSTEM +pub const OPCODE_SYSTEM: u8 = 0b1110011; + +/// Used for S-type instructions. +pub const OPCODE_STORE: u8 = 0b0100011; +pub const OPCODE_STORE_FP: u8 = 0b0100111; + +/// Used for B-type instructions. +pub const OPCODE_BRANCH: u8 = 0b1100011; + +/// Used for U-type instructions. +// LUI +pub const OPCODE_LUI: u8 = 0b0110111; +// AUIPC +pub const OPCODE_AUIPC: u8 = 0b0010111; + +/// Used for J-type instructions. +pub const OPCODE_JAL: u8 = 0b1101111; + +/// Used for R4-type instructions. +// FMADD.S +pub const OPCODE_MADD: u8 = 0b1000011; +// FMSUB.S +pub const OPCODE_MSUB: u8 = 0b1000111; +// FNMSUB.S +pub const OPCODE_NMSUB: u8 = 0b1001011; +// FNMADD.S +pub const OPCODE_NMADD: u8 = 0b1001111; + +/// Not a Number +pub const RISC_NAN: u32 = 0x7fc00000; diff --git a/src/emulation_core/riscv/control_signals.rs b/src/emulation_core/riscv/control_signals.rs new file mode 100644 index 000000000..1bf71d8ab --- /dev/null +++ b/src/emulation_core/riscv/control_signals.rs @@ -0,0 +1,409 @@ +//! Internal datapath control signals. + +/// Full collection of control signals. +#[derive(Clone, Default, PartialEq)] +pub struct ControlSignals { + pub imm_select: ImmSelect, + pub op1_select: OP1Select, + pub op2_select: OP2Select, + pub alu_op: AluOp, + pub sys_op: SysOp, + pub branch_jump: BranchJump, + pub read_write: ReadWrite, + pub wb_sel: WBSel, + pub mem_write_src: MemWriteSrc, + pub reg_dst: RegDst, + pub reg_write_en: RegWriteEn, +} + +/// Selection of different Immediate forms. +#[derive(Clone, Default, PartialEq)] +pub enum ImmSelect { + UType, + JType, + SType, + BType, + #[default] + ISigned, + IShamt, + IUnsigned, +} + +/// Selection of different sources for the first operand. +#[derive(Clone, Default, PartialEq)] +pub enum OP1Select { + #[default] + DATA1, + PC, + IMM, +} + +/// Selection of different sources for the second operand. +#[derive(Clone, Default, PartialEq)] +pub enum OP2Select { + DATA2, + #[default] + IMM, +} + +/// The output of the ALU control unit that directly controls the ALU. +#[derive(Clone, Default, PartialEq)] +pub enum AluOp { + /// `_0000` (0) - Perform an addition. (Also used in cases where the ALU result does not matter.) + #[default] + Addition, + + /// `_0001` (1) - Perform a subtraction. Will not set any underflow signal on underflow. + Subtraction, + + /// `_0010` (2) - Perform a shift left logical operation by `shamt` bits. + ShiftLeftLogical(u32), + + /// `_0011` (3) - Perform a "set on less than" operation. + SetOnLessThanSigned, + + /// `_0100` (4) - Perform a "set on less than unsigned" operation. + SetOnLessThanUnsigned, + + /// `_0101` (5) - Perform a bitwise "Xor" operation. + Xor, + + /// `_0110` (6) - Perform a shift right logical operation by `shamt` bits. + ShiftRightLogical(u32), + + /// `_0111` (7) - Perform a shift right arithmetic operation by `shamt` bits. + ShiftRightArithmetic(u32), + + /// `_1000` (8) - Perform a bitwise "OR" operation. + Or, + + /// `_1001` (9) - Perform a bitwise "AND" operation. + And, + + /// `_1010` (10) - Left shift the sign-extended immediate value 16 bits. + LeftShift16, + + /// `_1011` (11) - Perform signed multiplication. + MultiplicationSigned, + + /// `_1100` (12) - Perform unsigned multiplication. + MultiplicationUnsigned, + + /// `_1101` (13) - Perform signed integer division. (Returns the integer quotient.) + DivisionSigned, + + /// `_1110` (14) - Perform unsigned integer division. (Returns the integer quotient.) + DivisionUnsigned, + + MultiplicationSignedUpper, + + MultiplicationSignedUnsignedUpper, + + MultiplicationUnsignedSignedUpper, + + RemainderSigned, + + RemainderUnsigned, +} + +/// Selection of System Operations. +#[derive(Clone, Default, PartialEq)] +pub enum SysOp { + #[default] + None, + ECALL, + EBREAK, + CSRReadWrite, + CSRReadSet, + CSRReadClear, +} + +/// Selection of Branch Operations. +#[derive(Clone, Default, PartialEq)] +pub enum BranchJump { + Beq, + Bne, + #[default] + NoBranch, + J, + Blt, + Bge, + Bltu, + Bgeu, +} + +/// Selection of Read and Write Operations. +#[derive(Clone, Default, PartialEq)] +pub enum ReadWrite { + #[default] + NoLoadStore, + LoadByte, + LoadHalf, + LoadWord, + LoadDouble, + LoadByteUnsigned, + LoadHalfUnsigned, + LoadWordUnsigned, + StoreByte, + StoreHalf, + StoreWord, + StoreDouble, +} + +/// Determines, given [`RegWriteEn`] is set, what the source of a +/// register's new data will be. +/// +/// The decision can be completely overridden by the floating point +/// unit's [`DataWrite`](floating_point::DataWrite) control signal. +/// +/// This control signal also applies to what data is sent to the +/// floating-point unit to be stored in its registers. +#[derive(Clone, Default, PartialEq)] +pub enum WBSel { + #[default] + UseAlu = 0, + UseMemory = 1, + UseImmediate = 2, + UsePcPlusFour = 3, +} + +/// Determines, given that a write value in [`ReadWrite`] is set, the source of the data +/// will be written to memory. +#[derive(Clone, Default, PartialEq)] +pub enum MemWriteSrc { + /// Source the write data from the main processing unit. Specifically, this means the data read from the register `rs1` from a given instruction. + #[default] + PrimaryUnit = 0, + + /// Source the write data from the floating-point unit. + FloatingPointUnit = 1, +} + +/// Determines, given that [`RegWriteEn`] is set, which destination +/// register to write to, which largely depends on the instruction format. +#[derive(Clone, Default, PartialEq)] +pub enum RegDst { + /// Use register `rs1`. + Reg1 = 0, + + /// Use register `rs2`. + Reg2 = 1, + + /// Use register `rd`. + #[default] + Reg3 = 2, + + /// Write to general-purpose register 31 ($ra). This is the return address + /// used in `jal` instructions. + ReturnRegister = 3, +} + +/// Determines if the register file should be written to. +#[derive(Clone, Default, Eq, PartialEq)] +pub enum RegWriteEn { + #[default] + NoWrite = 0, + YesWrite = 1, +} + +/// Floating Point Control Signals Module. +pub mod floating_point { + use serde::{Deserialize, Serialize}; + + #[derive(Clone, Default, PartialEq, Serialize, Deserialize, Debug)] + pub struct FpuControlSignals { + pub round_mode: RoundingMode, + pub data_src: DataSrc, + pub data_write: DataWrite, + pub fpu_alu_op: FpuAluOp, + pub fpu_mem_to_reg: FpuMemToReg, + pub fpu_reg_dst: FpuRegDst, + pub fpu_reg_write: FpuRegWrite, + } + + /// Determines the source of the `Data` register in the floating-point unit. + /// + /// This is a special intermediary register that facilitates passing data between + /// the main processing unit and the floating-point unit. + #[derive(Clone, Default, PartialEq, Serialize, Deserialize, Debug)] + pub enum DataSrc { + /// Use data from the main processing unit. Specifically, the data from register + /// `rs1` from a given instruction. This value can additionally be used in the cases + /// where this register is not written to. + MainProcessorUnit = 0, + + /// Use data from the floating-point unit. Specifically, the data from register `rs1` + /// from a given instruction. + #[default] + FloatingPointUnitRS1 = 1, + + /// Use data from the floating-point unit. Specifically, the data from the comparator. + FloatingPointUnitComp = 2, + + /// Use data from the floating-point unit. Specifically, the Classify Mask. + FloatingPointUnitMask = 3, + + /// Use the un-altered bits from the floating-point unit. + FloatingPointBits = 4, + + /// Use the un-altered bits from the main unit. + MainProcessorBits = 5, + } + + /// Determines whether to write to the `Data` register in the floating-point unit. + /// + /// This acts as a toggle for the source of data to the main processing unit register + /// file. Additionally, it acts as a toggle for a source to the floating-point unit + /// register file (this could be overridden by the [`FpuMemToReg`] control signal). + /// For the latter two functions, it is imperative to unset the [`RegWriteEn`](super::RegWriteEn) and + /// [`FpuRegWrite`] control signals in cases where registers should not be modified + /// with unintended data. + #[derive(Clone, Default, PartialEq, Serialize, Deserialize, Debug)] + pub enum DataWrite { + /// - Do not write to the data register. + /// - Source data to write to the main processing unit register file from the main + /// processing unit. This implies either the ALU result or the data read from memory + /// - Source data to write to the floating-point register file from the floating-point + /// ALU. + #[default] + NoWrite = 0, + + /// - Write to the data register. + /// - Source data to write to the main processing unit register file from the + /// floating-point unit. Specifically, this is the data stored in the `Data` register + /// in the FPU, likely from register `rs1` from a given instruction. This data source + /// overrides the decision given by the [`WBSel`](super::WBSel) control signal. + /// - Source data to write to the floating-point register file from the `Data` register + /// in the FPU, likely from register `rs1` from a given instruction. + YesWrite = 1, + } + + /// This doubly determines the operations sent to the floating-point ALU and the + /// floating-point comparator. + /// + /// Only one of these units are effectively utilized in any given instruction. + #[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)] + pub enum FpuAluOp { + #[default] + /// `_00000` (0): + /// - ALU: Perform an addition. + Addition = 0, + + /// `_00001` (1): + /// - ALU: Perform a subtraction. + Subtraction = 1, + + /// `_00010` (2): + /// - ALU: Perform a multiplication. + /// - Comparator: Set if equal. + MultiplicationOrEqual = 2, + + /// `_00011` (3): + /// - ALU: Perform a division. + Division = 3, + + /// `_00100` (4): + /// - ALU: Perform a Square Root. + Sqrt = 4, + + /// `_00101` (5): + /// - ALU: Take the Minimum value. + Min = 5, + + /// `_00110` (6): + /// - ALU: Take the Maximum value. + Max = 6, + + /// `_00111` (7): + /// - ALU: Sign-Injection. + SGNJ = 7, + + /// `_01000` (8): + /// - ALU: Negative Sign-Injection. + SGNJN = 8, + + /// `_01001` (9): + /// - ALU: Xor Sign-Injection. + SGNJX = 9, + + /// `_01010` (10): + /// - ALU: Classification Mask. + Class = 10, + + /// `_01011` (11): + /// - ALU: Fused Multiplication-Addition. + MAdd = 11, + + /// `_01100` (12): + /// - ALU: Fused Multiplication-Subtraction. + MSub = 12, + + /// `_01101` (13): + /// - ALU: Fused Negated Multiplication-Subtraction. + NMSub = 13, + + /// `_01110` (14): + /// - ALU: Fused Negated Multiplication-Addition. + NMAdd = 14, + + /// `_10000` (16): + /// - Comparator: Set if less than. + Slt = 16, + + /// `_10001` (17): + /// - Comparator: Set if less than or equal. + Sle = 17, + } + + /// Selection of Rounding Modes + #[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)] + pub enum RoundingMode { + RNE = 0, + RTZ = 1, + RDN = 2, + RUP = 3, + RMM = 4, + #[default] + DRM = 7, + } + + /// Determines, given that [`FpuRegWrite`] is set, what the source of a floating-point + /// register's new data will be. + /// + /// This decision, if set, overrides the decision from the [`DataWrite`] control signal. + #[derive(Clone, Default, PartialEq, Serialize, Deserialize, Debug)] + pub enum FpuMemToReg { + /// Do not use data from memory. Use the result of the [`DataWrite`] control signal. + #[default] + UseDataWrite = 0, + + /// Use data from memory. + UseMemory = 1, + } + + /// Determines, given that [`FpuRegWrite`] is set, which destination register to write + /// to, which largely depends on the instruction format. + #[derive(Clone, Default, PartialEq, Serialize, Deserialize, Debug)] + pub enum FpuRegDst { + /// Use register `rs1`. + Reg1 = 0, + + /// Use register `rs2`. + Reg2 = 1, + + /// Use register `rd`. + #[default] + Reg3 = 2, + } + + /// Determines if the floating-point register file should be written to. + #[derive(Clone, Default, PartialEq, Serialize, Deserialize, Debug)] + pub enum FpuRegWrite { + /// Do not write to the floating-point register file. + #[default] + NoWrite = 0, + + /// Write to the floating-point register file. + YesWrite = 1, + } +} diff --git a/src/emulation_core/riscv/coprocessor.rs b/src/emulation_core/riscv/coprocessor.rs new file mode 100644 index 000000000..e799aaa74 --- /dev/null +++ b/src/emulation_core/riscv/coprocessor.rs @@ -0,0 +1,628 @@ +//! Implementation of a RISC-V floating-point coprocessor. + +use std::ops::Neg; + +use super::constants::*; +use super::instruction::RiscInstruction; +use super::registers::RiscFpRegisters; +use super::{constants::RISC_NAN, control_signals::floating_point::*}; +use serde::{Deserialize, Serialize}; + +/// An implementation of a floating-point coprocessor for the RISC-V ISA. +/// +/// Different from the main processor, much of the functionality of the coprocessor +/// is controlled remotely using its available API calls. +#[derive(Clone, PartialEq, Default, Debug, Serialize, Deserialize)] +pub struct RiscFpCoprocessor { + instruction: RiscInstruction, + pub signals: FpuControlSignals, + pub state: RiscFpuState, + pub is_halted: bool, + pub registers: RiscFpRegisters, + pub data: u64, +} + +#[derive(Clone, Default, PartialEq, Serialize, Deserialize, Debug)] +pub struct RiscFpuState { + pub instruction: u32, + pub rs1: u32, + pub rs2: u32, + pub rs3: u32, + pub rd: u32, + pub shamt: u32, + pub funct2: u32, + pub funct3: u32, + pub funct7: u32, + pub imm: u32, + pub imm1: u32, + pub imm2: u32, + pub branch_flag: bool, + + pub data_from_main_processor: u64, + pub data_writeback: u64, + pub destination: usize, + pub fp_register_data_from_main_processor: u64, + pub read_data_1: u64, + pub read_data_2: u64, + pub read_data_3: u64, + pub register_write_data: u64, + pub register_write_mux_to_mux: u64, + pub sign_extend_data: u64, + + /// Data line that goes from `Read Data 2` to the multiplexer in the main processor + /// controlled by [`MemWriteSrc`](super::control_signals::MemWriteSrc). + /// This variable in a way in just a copy of read_data_2 + pub fp_register_to_memory: u64, + + pub alu_result: u64, + pub comparator_result: u64, +} + +impl RiscFpCoprocessor { + // ========================== Stages ========================== + pub fn stage_instruction_decode(&mut self) { + self.instruction_decode(); + self.set_control_signals(); + self.read_registers(); + } + + pub fn stage_execute(&mut self) { + self.alu(); + self.comparator(); + self.write_fp_register_to_memory(); + } + + pub fn stage_memory(&mut self) { + self.write_data(); + self.set_data_writeback(); + } + + pub fn stage_writeback(&mut self) { + self.register_write(); + } + + // ===================== General Functions ===================== + /// Handle an otherwise irrecoverable error within the datapath. + pub fn error(&mut self, _message: &str) { + self.is_halted = true; + } + + // =================== API For Main Processor =================== + /// Set the internally-stored copy of the current instruction. This effectively + /// operates in lieu of any "instruction fetch" functionality since the coprocessor + /// does not fetch instructions. + pub fn set_instruction(&mut self, instruction_bits: u32) { + self.state.instruction = instruction_bits; + if let Ok(instruction) = RiscInstruction::try_from(self.state.instruction) { + self.instruction = instruction; + } + } + + /// Sets the data line between the main processor and the `Data` register. This + /// is then used if deciding data from the main processor should go into the `Data` + /// register. + pub fn set_data_from_main_processor(&mut self, data: u64) { + self.state.data_from_main_processor = data; + } + + /// Gets the contents of the data line between the `Data` register and the multiplexer + /// in the main processor controlled by the [`DataWrite`] control signal. + pub fn get_data_writeback(&mut self) -> u64 { + self.state.data_writeback + } + + /// Sets the data line between the multiplexer controlled by [`WBSel`](super::control_signals::WBSel) + /// in the main processor and the multiplexer controlled by [`FpuMemToReg`] in the + /// floating-point coprocessor. + pub fn set_fp_register_data_from_main_processor(&mut self, data: u64) { + self.state.fp_register_data_from_main_processor = data; + } + + /// Gets the contents of the data line that goes from `Read Data 2` to the multiplexer + /// in the main processor controlled by [`MemWriteSrc`](super::control_signals::MemWriteSrc). + pub fn get_fp_register_to_memory(&mut self) -> u64 { + self.state.fp_register_to_memory + } + + pub fn set_register(&mut self, _register: usize, _data: u64) -> Result<(), String> { + if _register >= 32 { + return Err(format!("Register index out of bounds: {}", _register)); + } + + let register = &mut self.registers.fpr[_register]; + *register = _data; + + Ok(()) + } + + pub fn register_from_str(&self, _register: &str) -> Option { + // Check if register matches a register between f0 and f31 + if _register.len() >= 2 && &_register[0..1] == "f" && _register.len() <= 3 { + let register = &_register[1..]; + if let Ok(register) = register.parse::() { + if register < 32 { + return Some(register); + } + } + } + None + } + + // ================== Instruction Decode (ID) ================== + /// Decode an instruction into its individual fields. + fn instruction_decode(&mut self) { + // Set the data lines based on the contents of the instruction. + // Some lines will hold uninitialized values as a result. + match self.instruction { + RiscInstruction::RType(r) => { + self.state.rs1 = r.rs1 as u32; + self.state.rs2 = r.rs2 as u32; + self.state.rd = r.rd as u32; + self.state.shamt = r.rs2 as u32; + self.state.funct3 = r.funct3 as u32; + self.state.funct7 = r.funct7 as u32; + } + RiscInstruction::IType(i) => { + self.state.rs1 = i.rs1 as u32; + self.state.funct3 = i.funct3 as u32; + self.state.rd = i.rd as u32; + self.state.imm = i.imm as u32; + self.state.shamt = (i.imm & 0x003f) as u32; + } + RiscInstruction::SType(s) => { + self.state.rs2 = s.rs2 as u32; + self.state.rs1 = s.rs1 as u32; + self.state.funct3 = s.funct3 as u32; + self.state.imm1 = s.imm1 as u32; + self.state.imm2 = s.imm2 as u32; + } + RiscInstruction::R4Type(r4) => { + self.state.rs3 = r4.rs3 as u32; + self.state.funct2 = r4.funct2 as u32; + self.state.rs2 = r4.rs2 as u32; + self.state.rs1 = r4.rs1 as u32; + self.state.funct3 = r4.funct3 as u32; + self.state.rd = r4.rd as u32; + } + _ => (), + } + } + + /// Set the control signals of the processor based on the instruction opcode and function + /// control signals. + fn set_control_signals(&mut self) { + match self.instruction { + RiscInstruction::RType(r) => { + self.signals = FpuControlSignals { + data_write: DataWrite::NoWrite, + fpu_mem_to_reg: FpuMemToReg::UseDataWrite, + fpu_reg_write: FpuRegWrite::YesWrite, + fpu_reg_dst: FpuRegDst::Reg3, + ..Default::default() + }; + + if r.op != OPCODE_OP_FP { + self.signals.fpu_reg_write = FpuRegWrite::NoWrite; + return; + } + + match r.funct7 >> 2 { + 0 => self.signals.fpu_alu_op = FpuAluOp::Addition, + 1 => self.signals.fpu_alu_op = FpuAluOp::Subtraction, + 2 => self.signals.fpu_alu_op = FpuAluOp::MultiplicationOrEqual, + 3 => self.signals.fpu_alu_op = FpuAluOp::Division, + 4 => match r.funct3 { + 0 => self.signals.fpu_alu_op = FpuAluOp::SGNJ, + 1 => self.signals.fpu_alu_op = FpuAluOp::SGNJN, + 2 => self.signals.fpu_alu_op = FpuAluOp::SGNJX, + _ => self.error("Unsupported Instruction!"), + }, + 5 => match r.funct3 { + 0 => self.signals.fpu_alu_op = FpuAluOp::Min, + 1 => self.signals.fpu_alu_op = FpuAluOp::Max, + _ => self.error("Unsupported Instruction!"), + }, + 11 => self.signals.fpu_alu_op = FpuAluOp::Sqrt, + 20 => { + self.signals.fpu_reg_write = FpuRegWrite::NoWrite; + self.signals.data_write = DataWrite::YesWrite; + self.signals.data_src = DataSrc::FloatingPointUnitComp; + match r.funct3 { + 0 => self.signals.fpu_alu_op = FpuAluOp::Sle, + 1 => self.signals.fpu_alu_op = FpuAluOp::Slt, + 2 => self.signals.fpu_alu_op = FpuAluOp::MultiplicationOrEqual, + _ => self.error("Unsupported Instruction!"), + } + } + 24 => { + self.signals.data_write = DataWrite::YesWrite; + self.signals.fpu_reg_write = FpuRegWrite::NoWrite; + } + 26 => { + self.signals.data_write = DataWrite::YesWrite; + self.signals.data_src = DataSrc::MainProcessorUnit; + } + 28 => { + self.signals.data_write = DataWrite::YesWrite; + self.signals.fpu_reg_write = FpuRegWrite::NoWrite; + match r.funct3 { + 0 => self.signals.data_src = DataSrc::FloatingPointBits, + 1 => { + self.signals.fpu_alu_op = FpuAluOp::Class; + self.signals.data_src = DataSrc::FloatingPointUnitMask; + } + _ => self.error("Unsupported Instruction!"), + } + } + 30 => { + self.signals.data_write = DataWrite::YesWrite; + self.signals.fpu_reg_write = FpuRegWrite::YesWrite; + self.signals.data_src = DataSrc::MainProcessorBits; + } + _ => self.error("Unsupported Instruction!"), + } + + match r.funct3 { + 0 => self.signals.round_mode = RoundingMode::RNE, + 1 => self.signals.round_mode = RoundingMode::RTZ, + 2 => self.signals.round_mode = RoundingMode::RDN, + 3 => self.signals.round_mode = RoundingMode::RUP, + 4 => self.signals.round_mode = RoundingMode::RMM, + 7 => self.signals.round_mode = RoundingMode::DRM, + _ => self.error("Unsupported Rounding Mode!"), + } + } + RiscInstruction::IType(i) => { + self.signals = FpuControlSignals { + data_write: DataWrite::NoWrite, + fpu_mem_to_reg: FpuMemToReg::UseMemory, + fpu_reg_dst: FpuRegDst::Reg3, + fpu_reg_write: FpuRegWrite::YesWrite, + ..Default::default() + }; + + if i.op != OPCODE_LOAD_FP { + self.signals.fpu_reg_write = FpuRegWrite::NoWrite; + } + } + RiscInstruction::SType(_s) => { + self.signals = FpuControlSignals { + data_write: DataWrite::NoWrite, + fpu_reg_write: FpuRegWrite::NoWrite, + ..Default::default() + }; + } + RiscInstruction::R4Type(r4) => { + self.signals = FpuControlSignals { + data_write: DataWrite::NoWrite, + fpu_mem_to_reg: FpuMemToReg::UseDataWrite, + fpu_reg_dst: FpuRegDst::Reg3, + fpu_reg_write: FpuRegWrite::YesWrite, + ..Default::default() + }; + + self.signals.fpu_alu_op = match r4.op { + OPCODE_MADD => FpuAluOp::MAdd, + OPCODE_MSUB => FpuAluOp::MSub, + OPCODE_NMSUB => FpuAluOp::NMSub, + OPCODE_NMADD => FpuAluOp::NMAdd, + _ => { + self.error("Unsupported Instruction!"); + FpuAluOp::Addition + } + }; + } + _ => (), + } + } + + /// Read the registers as specified from the instruction and pass + /// the data into the datapath. + fn read_registers(&mut self) { + let reg1 = self.state.rs1 as usize; + let reg2 = self.state.rs2 as usize; + let reg3 = self.state.rs3 as usize; + + self.state.read_data_1 = self.registers.fpr[reg1]; + self.state.read_data_2 = self.registers.fpr[reg2]; + self.state.read_data_3 = self.registers.fpr[reg3]; + } + + // ======================= Execute (EX) ======================= + /// Perform an ALU operation. + fn alu(&mut self) { + let input1 = self.state.read_data_1 as u32; + let input2 = self.state.read_data_2 as u32; + let input3 = self.state.read_data_3 as u32; + let input1_f32 = f32::from_bits(input1); + let input2_f32 = f32::from_bits(input2); + let input3_f32 = f32::from_bits(input3); + let mut input_mask = 0b0000000000; + let input1_wo_sign = input1 & 0x7fffffff; + let mut sign_bit = 0; + + let result_f32: f32 = match self.signals.fpu_alu_op { + FpuAluOp::Addition => input1_f32 + input2_f32, + FpuAluOp::Subtraction => input1_f32 - input2_f32, + FpuAluOp::MultiplicationOrEqual => input1_f32 * input2_f32, + FpuAluOp::Division => { + if input2_f32 == 0.0 { + 0.0 + } else { + input1_f32 / input2_f32 + } + } + FpuAluOp::Sqrt => input1_f32.sqrt(), + FpuAluOp::Min => { + if input1_f32 < input2_f32 { + input1_f32 + } else { + input2_f32 + } + } + FpuAluOp::Max => { + if input1_f32 > input2_f32 { + input1_f32 + } else { + input2_f32 + } + } + FpuAluOp::SGNJ => { + sign_bit = input2 & 0x80000000; + 0.0 + } + FpuAluOp::SGNJN => { + sign_bit = !(input2 | 0x7fffffff); + 0.0 + } + FpuAluOp::SGNJX => { + sign_bit = (input1 ^ input2) & 0x80000000; + 0.0 + } + FpuAluOp::Class => { + if input1_f32.is_sign_negative() { + if input1_f32.is_infinite() { + input_mask = 0b1; + } else if input1_f32.is_normal() { + input_mask = 0b10; + } else if input1_f32.is_subnormal() { + input_mask = 0b100; + } else { + input_mask = 0b1000; + } + } else if input1_f32.is_sign_positive() { + if input1_f32.is_infinite() { + input_mask = 0b10000000; + } else if input1_f32.is_normal() { + input_mask = 0b1000000; + } else if input1_f32.is_subnormal() { + input_mask = 0b100000; + } else { + input_mask = 0b10000; + } + } else if input1_f32.is_nan() { + input_mask = 0b100000000; + } else { + input_mask = 0b1000000000; + } + 0.0 + } + FpuAluOp::MAdd => input1_f32 * input2_f32 + input3_f32, + FpuAluOp::MSub => input1_f32 * input2_f32 - input3_f32, + FpuAluOp::NMSub => input1_f32.neg() * input2_f32 + input3_f32, + FpuAluOp::NMAdd => input1_f32.neg() * input2_f32 - input3_f32, + // No operation. + FpuAluOp::Slt | FpuAluOp::Sle => 0.0, + }; + + if result_f32.is_nan() { + self.state.alu_result = RISC_NAN as u64; + return; + } + + if (self.signals.fpu_alu_op == FpuAluOp::SGNJ) + | (self.signals.fpu_alu_op == FpuAluOp::SGNJN) + | (self.signals.fpu_alu_op == FpuAluOp::SGNJX) + { + self.state.alu_result = (input1_wo_sign | sign_bit) as i32 as u64; + return; + } + + if self.signals.fpu_alu_op == FpuAluOp::Class { + self.state.alu_result = input_mask as u64; + return; + } + + self.state.alu_result = match self.signals.round_mode { + RoundingMode::RNE => f32::to_bits( + if (result_f32.ceil() - result_f32).abs() == (result_f32 - result_f32.floor()).abs() + { + if result_f32.ceil() % 2.0 == 0.0 { + result_f32.ceil() + } else { + result_f32.floor() + } + } else { + result_f32.round() + }, + ), + RoundingMode::RTZ => f32::to_bits(result_f32.trunc()), + RoundingMode::RDN => f32::to_bits(result_f32.floor()), + RoundingMode::RUP => f32::to_bits(result_f32.ceil()), + RoundingMode::RMM => f32::to_bits(result_f32.round()), + _ => f32::to_bits(result_f32), + } as i32 as u64; + } + + /// Perform a comparison. + fn comparator(&mut self) { + let input1 = self.state.read_data_1 as u32; + let input2 = self.state.read_data_2 as u32; + let input1_f32 = f32::from_bits(input1); + let input2_f32 = f32::from_bits(input2); + + self.state.comparator_result = match self.signals.fpu_alu_op { + FpuAluOp::MultiplicationOrEqual => (input1_f32 == input2_f32) as u64, + FpuAluOp::Slt => (input1_f32 < input2_f32) as u64, + FpuAluOp::Sle => (input1_f32 <= input2_f32) as u64, + _ => 0, + } + } + + /// Write to the `Data` register. This register is used to transfer data between + /// the main processor and the coprocessor. + fn write_data(&mut self) { + if let DataWrite::NoWrite = self.signals.data_write { + return; + } + + self.data = match self.signals.data_src { + DataSrc::FloatingPointUnitRS1 => self.state.read_data_1, + DataSrc::FloatingPointUnitComp => self.state.comparator_result, + DataSrc::FloatingPointUnitMask => self.state.alu_result, + DataSrc::FloatingPointBits => self.state.read_data_1 as i32 as u64, + _ => self.state.data_from_main_processor, + }; + } + + /// Set the data line that goes from `Read Data 2` to the multiplexer in the main processor + /// controlled by [`MemWriteSrc`](super::control_signals::MemWriteSrc). + fn write_fp_register_to_memory(&mut self) { + self.state.fp_register_to_memory = self.state.read_data_2; + } + + // ======================= Memory (MEM) ======================= + /// Set the data line between the multiplexer after the `Data` register and the + /// multiplexer in the main processor controlled by the [`DataWrite`] control signal. + fn set_data_writeback(&mut self) { + if let DataWrite::NoWrite = self.signals.data_write { + return; + } + + self.state.data_writeback = match self.signals.data_src { + DataSrc::MainProcessorUnit => match self.state.rs2 { + 0 => f32::to_bits(self.data as i32 as f32) as i32 as u64, + 1 => f32::to_bits(self.data as u32 as f32) as i32 as u64, + 2 => f32::to_bits(self.data as i64 as f32) as i32 as u64, + 3 => f32::to_bits(self.data as f32) as u64, + _ => { + self.error(&format!( + "Unsupported Register Width `{:?}`", + self.state.rs2 + )); + 0 + } + }, + DataSrc::FloatingPointUnitRS1 => { + let data_unrounded = f32::from_bits(self.data as u32); + let data_rounded = match self.signals.round_mode { + RoundingMode::RNE => { + if (data_unrounded.ceil() - data_unrounded).abs() + == (data_unrounded - data_unrounded.floor()).abs() + { + if data_unrounded.ceil() % 2.0 == 0.0 { + data_unrounded.ceil() + } else { + data_unrounded.floor() + } + } else { + data_unrounded.round() + } + } + RoundingMode::RTZ => data_unrounded.trunc(), + RoundingMode::RDN => data_unrounded.floor(), + RoundingMode::RUP => data_unrounded.ceil(), + _ => data_unrounded.round(), + }; + + match self.state.rs2 { + 0 => { + if (data_rounded <= (-(2_i64.pow(31))) as f32) + | (data_rounded == f32::NEG_INFINITY) + { + -(2_i64.pow(31)) as u64 + } else if (data_rounded >= (2_i64.pow(31) - 1) as f32) + | (data_rounded == f32::INFINITY) + | (data_rounded.is_nan()) + { + (2_i64.pow(31) - 1) as u64 + } else { + data_rounded as i32 as u64 + } + } + 1 => { + if (data_rounded <= 0.0) | (data_rounded == f32::NEG_INFINITY) { + 0 + } else if (data_rounded >= (2_u64.pow(32) - 1) as f32) + | (data_rounded == f32::INFINITY) + | (data_rounded.is_nan()) + { + 2_u64.pow(32) - 1 + } else { + data_rounded as i32 as u64 + } + } + 2 => { + if (data_rounded <= (-(2_i64.pow(63))) as f32) + | (data_rounded == f32::NEG_INFINITY) + { + -(2_i64.pow(63)) as u64 + } else if (data_rounded >= (2_i64.pow(63) - 1) as f32) + | (data_rounded == f32::INFINITY) + | (data_rounded.is_nan()) + { + (2_i64.pow(63) - 1) as u64 + } else { + data_rounded as i32 as u64 + } + } + 3 => { + if (data_rounded <= 0.0) | (data_rounded == f32::NEG_INFINITY) { + 0 + } else if (data_rounded >= (0xffffffffffffffff_u64) as f32) + | (data_rounded == f32::INFINITY) + | (data_rounded.is_nan()) + { + 0xffffffffffffffff + } else { + data_rounded as i32 as u64 + } + } + _ => { + self.error(&format!( + "Unsupported Register Width `{:?}`", + self.state.rs2 + )); + 0 + } + } + } + DataSrc::MainProcessorBits => self.data as i32 as u64, + _ => self.data, + } + } + + // ====================== Writeback (WB) ====================== + /// Write data to the floating-point register file. + fn register_write(&mut self) { + if let FpuRegWrite::NoWrite = self.signals.fpu_reg_write { + return; + } + + self.state.destination = match self.signals.fpu_reg_dst { + FpuRegDst::Reg1 => self.state.rs1 as usize, + FpuRegDst::Reg2 => self.state.rs2 as usize, + FpuRegDst::Reg3 => self.state.rd as usize, + }; + + self.state.register_write_mux_to_mux = match self.signals.data_write { + DataWrite::NoWrite => self.state.alu_result, + DataWrite::YesWrite => self.state.data_writeback, + }; + self.state.register_write_data = match self.signals.fpu_mem_to_reg { + FpuMemToReg::UseDataWrite => self.state.register_write_mux_to_mux, + FpuMemToReg::UseMemory => self.state.fp_register_data_from_main_processor, + }; + self.registers.fpr[self.state.destination] = self.state.register_write_data; + } +} diff --git a/src/emulation_core/riscv/datapath.rs b/src/emulation_core/riscv/datapath.rs new file mode 100644 index 000000000..767780995 --- /dev/null +++ b/src/emulation_core/riscv/datapath.rs @@ -0,0 +1,1226 @@ +//! Implementation of a RISC-V datapath. +//! +//! It is assumed that while moving through stages, only one +//! instruction will be active any any given point in time. Due to this, +//! we consider the datapath to be a "pseudo-single-cycle datapath." +//! +//! For the most part, this datapath is an implementation of RISC-V Spec 2.2 with extensions: +//! RV32I, RV64I, M, and F. +//! # Notes on `is_halted` +//! +//! - The datapath starts with the `is_halted` flag set. +//! - [`RiscDatapath::initialize()`] should be used to un-set `is_halted`. +//! - The `EBREAK` instruction simply performs a no-operation instruction, except for +//! setting the boolean flag `is_halted`. +//! - Invalid instructions will cause the datapath to set the `is_halted` flag. + +use super::super::datapath::Datapath; +use super::constants::*; +use super::control_signals::floating_point::*; +use super::control_signals::*; +use super::coprocessor::RiscFpCoprocessor; +use super::datapath_signals::*; +use super::instruction::*; +use super::registers::FpRegisterType; +use super::registers::RiscGpRegisterType; +use super::{super::mips::memory::Memory, registers::RiscGpRegisters}; +use crate::emulation_core::architectures::DatapathRef; +use crate::emulation_core::datapath::{DatapathUpdateSignal, Syscall}; +use crate::emulation_core::riscv::registers::RiscGpRegisterType::{X10, X11}; +use crate::emulation_core::stack::Stack; +use crate::emulation_core::stack::StackFrame; +use serde::{Deserialize, Serialize}; + +/// An implementation of a datapath for the MIPS64 ISA. +#[derive(Clone, PartialEq)] +pub struct RiscDatapath { + pub registers: RiscGpRegisters, + pub memory: Memory, + pub coprocessor: RiscFpCoprocessor, + pub stack: Stack, + + pub instruction: RiscInstruction, + pub signals: ControlSignals, + pub datapath_signals: DatapathSignals, + pub state: RiscDatapathState, + + /// The currently-active stage in the datapath. + pub current_stage: RiscStage, + + /// Boolean value that states whether the datapath has halted. + /// + /// This is set in the event of any `syscall` instruction. To unset this, + /// [`Self::initialize()`] should be used. + is_halted: bool, +} + +/// A collection of all the data lines and wires in the datapath. +#[derive(Clone, Default, PartialEq, Debug, Serialize, Deserialize)] +pub struct RiscDatapathState { + /// *Data line.* The currently loaded instruction. Initialized after the + /// Instruction Fetch stage. + pub instruction: u32, + pub rs1: u32, + pub rs2: u32, + pub rs3: u32, + pub rd: u32, + pub shamt: u32, + pub funct2: u32, + pub funct3: u32, + pub funct7: u32, + pub imm: i32, + pub imm1: i32, + pub imm2: i32, + + /// *Data line.* The first input of the ALU. + pub alu_input1: u64, + + /// *Data line.* The second input of the ALU. + pub alu_input2: u64, + + /// *Data line.* The final result as provided by the ALU. + /// Initialized after the Execute stage. + pub alu_result: u64, + + /// *Data line.* The data after the `MemToReg` multiplexer, but + /// before the `DataWrite` multiplexer in the main processor. + pub data_result: u64, + + // *Data line.* This line carries the idenfication number for the + // register register-write will write to. + pub write_register_destination: usize, + + /// *Jump address line.* This line carries the concatenation of + /// the high 36 bits of the PC, and `lower_26_shifted_left_by_2`. + pub jump_address: u64, + + /// *Jump 26 bit line.* The lower 26 bits of the instruction reserved + /// for the location used by a J-type instruction. + pub lower_26: u32, + + /// *Lower 26 << 2 line.* This line carries the lower 28 bits of the + /// jump address. + pub lower_26_shifted_left_by_2: u32, + + /// *Data line.* Determines the next value of the PC, given that the + /// current instruction is not a jump. + pub mem_mux1_to_mem_mux2: u64, + + /// *Data line.* The data retrieved from memory. Initialized after + /// the Memory stage. + pub memory_data: u64, + + /// *New PC line.* In the WB stage, this line is written to the PC. + pub new_pc: u64, + + /// *Data line.* Contains PC + 4. + pub pc_plus_4: u64, + + /// *Data line.* Data read from the register file based on the `rs1` + /// field of the instruction. Initialized after the Instruction + /// Decode stage. + pub read_data_1: u64, + + /// *Data line.* Data read from the register file based on the `rd` + /// field of the instruction. Initialized after the Instruction + /// Decode stage. + pub read_data_2: u64, + + /// *Data line.* The data after the `DataWrite` multiplexer in the main + /// processor and the main processor register file. + pub register_write_data: u64, + + /// *Data line.* New PC value used if branching is set for an instruction. + pub relative_pc_branch: u64, + + /// *Data line.* The instruction's immediate value sign-extended to + /// 64 bits. Initialized after the Instruction Decode stage. + pub sign_extend: u64, + + /// *Data line.* The `sign_extend` line, shifted left by two bits. + pub sign_extend_shift_left_by_2: u64, + + /// *Data line.* The data that will be written to memory. + pub write_data: u64, + + pub imm_input: u64, + pub i_type_jump: u64, +} + +/// The possible stages the datapath could be in during execution. +#[derive(Clone, Copy, Default, Eq, PartialEq, Debug, Serialize, Deserialize)] +pub enum RiscStage { + #[default] + InstructionFetch, + InstructionDecode, + Execute, + Memory, + WriteBack, +} + +impl RiscStage { + /// Given a stage, return the next consecutive stage. If the last + /// stage is given, return the first stage. + fn get_next_stage(current_stage: RiscStage) -> RiscStage { + match current_stage { + RiscStage::InstructionFetch => RiscStage::InstructionDecode, + RiscStage::InstructionDecode => RiscStage::Execute, + RiscStage::Execute => RiscStage::Memory, + RiscStage::Memory => RiscStage::WriteBack, + RiscStage::WriteBack => RiscStage::InstructionFetch, + } + } +} + +impl From for String { + fn from(val: RiscStage) -> String { + String::from(match val { + RiscStage::InstructionFetch => "writeback", + RiscStage::InstructionDecode => "instruction_fetch", + RiscStage::Execute => "instruction_decode", + RiscStage::Memory => "execute", + RiscStage::WriteBack => "memory", + }) + } +} + +impl Default for RiscDatapath { + fn default() -> Self { + let mut datapath = RiscDatapath { + registers: RiscGpRegisters::default(), + memory: Memory::default(), + coprocessor: RiscFpCoprocessor::default(), + stack: Stack::default(), + instruction: RiscInstruction::default(), + signals: ControlSignals::default(), + datapath_signals: DatapathSignals::default(), + state: RiscDatapathState::default(), + current_stage: RiscStage::default(), + is_halted: true, + }; + + // Set the stack pointer ($sp) to initially start at the end + // of memory. + datapath.registers.gpr[2] = super::super::mips::memory::CAPACITY_BYTES as u64; + + datapath + } +} + +impl Datapath for RiscDatapath { + type RegisterData = u64; + + /// Reset the datapath, load instructions into memory, and un-sets the `is_halted` + /// flag. If the process fails, an [`Err`] is returned. + fn initialize(&mut self, initial_pc: usize, instructions: Vec) -> Result<(), String> { + self.reset(); + self.load_instructions(instructions)?; + self.registers.pc = initial_pc as u64; + self.is_halted = false; + + Ok(()) + } + + fn execute_instruction(&mut self) -> DatapathUpdateSignal { + let mut result_signals = DatapathUpdateSignal::default(); + loop { + // Stop early if the datapath has halted. + if self.is_halted { + break; + } + + result_signals |= self.execute_stage(); + + // This instruction is finished when the datapath has returned + // to the IF stage. + if self.current_stage == RiscStage::InstructionFetch { + break; + } + } + result_signals + } + + fn execute_stage(&mut self) -> DatapathUpdateSignal { + // If the datapath is halted, do nothing. + if self.is_halted { + return DatapathUpdateSignal::default(); + } + + let res = match self.current_stage { + RiscStage::InstructionFetch => self.stage_instruction_fetch(), + RiscStage::InstructionDecode => self.stage_instruction_decode(), + RiscStage::Execute => self.stage_execute(), + RiscStage::Memory => self.stage_memory(), + RiscStage::WriteBack => self.stage_writeback(), + }; + + // If the FPU has halted, reflect this in the main unit. + if self.coprocessor.is_halted { + self.is_halted = true; + } + + self.current_stage = RiscStage::get_next_stage(self.current_stage); + res + } + + fn set_register_by_str(&mut self, register: &str, data: Self::RegisterData) { + let register = &mut self.registers[register]; + *register = data; + } + + fn get_memory(&self) -> &Memory { + &self.memory + } + + fn set_memory(&mut self, _ptr: u64, _data: u32) { + self.memory.store_word(_ptr, _data).unwrap_or_default(); + } + + fn is_halted(&self) -> bool { + self.is_halted + } + + fn reset(&mut self) { + std::mem::take(self); + } + + fn as_datapath_ref(&self) -> DatapathRef { + DatapathRef::RISCV(self) + } + + fn set_fp_register_by_str(&mut self, register: &str, data: Self::RegisterData) { + let register = &mut self.coprocessor.registers[register]; + *register = data; + } + + fn get_memory_mut(&mut self) -> &mut Memory { + &mut self.memory + } + + fn halt(&mut self) { + self.is_halted = true; + } + + fn get_syscall_arguments(&self) -> Syscall { + Syscall::from_register_data( + self.registers[X10], + self.registers[X11], + f32::from_bits(self.coprocessor.registers[FpRegisterType::F10] as u32), + f64::from_bits(self.coprocessor.registers[FpRegisterType::F10]), + ) + } +} + +impl RiscDatapath { + // ===================== General Functions ===================== + /// Load a vector of 32-bit instructions into memory. If the process fails, + /// from a lack of space or otherwise, an [`Err`] is returned. + fn load_instructions(&mut self, instructions: Vec) -> Result<(), String> { + for (i, data) in instructions.iter().enumerate() { + self.memory.store_word((i as u64) * 4, *data)? + } + + Ok(()) + } + + /// Handle an otherwise irrecoverable error within the datapath. + pub fn error(&mut self, _message: &str) { + self.is_halted = true; + } + + // ========================== Stages ========================== + /// Stage 1 of 5: Instruction Fetch (IF) + /// + /// Fetch the current instruction based on the given PC and load it + /// into the datapath. + fn stage_instruction_fetch(&mut self) -> DatapathUpdateSignal { + self.instruction_fetch(); + + // Upper part of datapath, PC calculation + self.pc_plus_4(); + self.coprocessor.set_instruction(self.state.instruction); + + // Both state and coprocessor state always update + DatapathUpdateSignal { + changed_state: true, + changed_coprocessor_state: true, + ..Default::default() + } + } + + /// Stage 2 of 5: Instruction Decode (ID) + /// + /// Parse the instruction, set control signals, and read registers. + /// + /// If the instruction is determined to be an `EBREAK`, immediately + /// finish the instruction and set the `is_halted` flag. + fn stage_instruction_decode(&mut self) -> DatapathUpdateSignal { + self.instruction_decode(); + self.set_control_signals(); + self.set_immediate(); + self.read_registers(); + self.construct_jump_address(); + self.coprocessor.stage_instruction_decode(); + self.coprocessor + .set_data_from_main_processor(self.state.read_data_1); + + // Check if we hit a syscall or breakpoint and signal it to the caller. + let (hit_syscall, hit_breakpoint) = ( + self.signals.sys_op == SysOp::ECALL, + self.signals.sys_op == SysOp::EBREAK, + ); + + // Instruction decode always involves a state update + DatapathUpdateSignal { + changed_state: true, + changed_coprocessor_state: true, + hit_syscall, + hit_breakpoint, + ..Default::default() + } + } + + /// Stage 3 of 5: Execute (EX) + /// + /// Execute the current instruction with some arithmetic operation. + fn stage_execute(&mut self) -> DatapathUpdateSignal { + self.alu(); + self.calc_relative_pc_branch(); + self.calc_cpu_branch_signal(); + self.coprocessor.stage_execute(); + + DatapathUpdateSignal { + changed_state: true, + changed_coprocessor_state: true, + ..Default::default() + } + } + + /// Stage 4 of 5: Memory (MEM) + /// + /// Read or write to memory. + fn stage_memory(&mut self) -> DatapathUpdateSignal { + match self.signals.read_write { + ReadWrite::LoadByte => self.memory_read(), + ReadWrite::LoadByteUnsigned => self.memory_read(), + ReadWrite::LoadHalf => self.memory_read(), + ReadWrite::LoadHalfUnsigned => self.memory_read(), + ReadWrite::LoadWord => self.memory_read(), + ReadWrite::LoadWordUnsigned => self.memory_read(), + ReadWrite::LoadDouble => self.memory_read(), + ReadWrite::NoLoadStore => (), + ReadWrite::StoreByte => self.memory_write(), + ReadWrite::StoreHalf => self.memory_write(), + ReadWrite::StoreWord => self.memory_write(), + ReadWrite::StoreDouble => self.memory_write(), + } + + self.coprocessor.stage_memory(); + + // PC calculation stuff from upper part of datapath + self.calc_general_branch_signal(); + self.pick_pc_plus_4_or_relative_branch_addr_mux1(); + self.set_new_pc_mux2(); + + let mut changed_stack = false; + + // If this is the first instruction, push the initial frame to the stack + if self.stack.is_empty() { + let frame = StackFrame::new( + self.state.instruction, + self.registers.pc, + self.state.pc_plus_4, + self.registers[RiscGpRegisterType::X2], + // self.registers[GpRegisterType::Sp], + self.registers.pc, + ); + self.stack.push(frame); + changed_stack = true; + } + + let hit_jump = matches!(self.signals.branch_jump, BranchJump::J) + // Evil hack to stop JALR from appearing in the stack frame viewer + && ((self.state.instruction & 0b1111111) as u8 != OPCODE_JALR); + if hit_jump { + // Add current line to stack if we call a function + let frame = StackFrame::new( + self.state.instruction, + self.registers.pc, + self.state.pc_plus_4, + self.registers[RiscGpRegisterType::X2], + self.state.new_pc, + ); + self.stack.push(frame); + changed_stack = true; + } + + DatapathUpdateSignal { + changed_state: true, + changed_memory: ((self.signals.read_write == ReadWrite::StoreByte) + | (self.signals.read_write == ReadWrite::StoreDouble) + | (self.signals.read_write == ReadWrite::StoreHalf) + | (self.signals.read_write == ReadWrite::StoreWord)), + changed_coprocessor_state: true, + changed_stack, + ..Default::default() + } + } + + /// Stage 5 of 5: Writeback (WB) + /// + /// Write the result of the instruction's operation to a register, + /// if desired. Additionally, set the PC for the next instruction. + fn stage_writeback(&mut self) -> DatapathUpdateSignal { + self.register_write(); + self.coprocessor + .set_fp_register_data_from_main_processor(self.state.data_result); + self.set_pc(); + self.coprocessor.stage_writeback(); + + // check if we are writing to the stack pointer + let mut changed_stack = false; + if self.state.write_register_destination == RiscGpRegisterType::X2 as usize { + if let Some(last_frame) = self.stack.peek() { + if self.state.register_write_data >= last_frame.frame_pointer { + // dellocating stack space + self.stack.pop(); + changed_stack = true; + } + } + } + + DatapathUpdateSignal { + changed_state: true, + changed_coprocessor_state: true, + changed_registers: true, // Always true because pc always gets updated + changed_coprocessor_registers: self.coprocessor.signals.fpu_reg_write + == FpuRegWrite::YesWrite, + changed_stack, + ..Default::default() + } + } + + // ================== Instruction Fetch (IF) ================== + /// Load the raw binary instruction from memory and into the + /// datapath. If there is an error with loading the word, assume + /// the instruction to be bitwise zero and error. + fn instruction_fetch(&mut self) { + self.state.instruction = match self.memory.load_word(self.registers.pc) { + Ok(data) => data, + Err(e) => { + self.error(e.as_str()); + 0 + } + } + } + + fn pc_plus_4(&mut self) { + self.state.pc_plus_4 = self.registers.pc + 4; + } + + // ================== Instruction Decode (ID) ================== + /// Decode an instruction into its individual fields. + fn instruction_decode(&mut self) { + match RiscInstruction::try_from(self.state.instruction) { + Ok(instruction) => self.instruction = instruction, + Err(message) => { + self.error(&message); + return; + } + } + + // Set the data lines based on the contents of the instruction. + // Some lines will hold uninitialized values as a result. + match self.instruction { + RiscInstruction::RType(r) => { + self.state.rs1 = r.rs1 as u32; + self.state.rs2 = r.rs2 as u32; + self.state.rd = r.rd as u32; + self.state.shamt = (self.registers.gpr[r.rs2 as usize] & 0b11111) as u32; + self.state.funct3 = r.funct3 as u32; + self.state.funct7 = r.funct7 as u32; + } + RiscInstruction::IType(i) => { + self.state.rs1 = i.rs1 as u32; + self.state.funct3 = i.funct3 as u32; + self.state.rd = i.rd as u32; + self.state.imm = i.imm as i32; + self.state.shamt = (i.imm & 0x003f) as u32; + } + RiscInstruction::SType(s) => { + self.state.rs2 = s.rs2 as u32; + self.state.rs1 = s.rs1 as u32; + self.state.funct3 = s.funct3 as u32; + self.state.imm1 = s.imm1 as i32; + self.state.imm2 = s.imm2 as i32; + } + RiscInstruction::BType(b) => { + self.state.rs1 = b.rs1 as u32; + self.state.rs2 = b.rs2 as u32; + self.state.funct3 = b.funct3 as u32; + self.state.imm = b.imm as i32; + } + RiscInstruction::UType(u) => { + self.state.imm = u.imm; + self.state.rd = u.rd as u32; + } + RiscInstruction::JType(j) => { + self.state.imm = j.imm; + self.state.rd = j.rd as u32; + } + RiscInstruction::R4Type(r4) => { + self.state.rs3 = r4.rs3 as u32; + self.state.funct2 = r4.funct2 as u32; + self.state.rs2 = r4.rs2 as u32; + self.state.rs1 = r4.rs1 as u32; + self.state.funct3 = r4.funct3 as u32; + self.state.rd = r4.rd as u32; + } + } + + // Extract the first 20 bits from the instruction for the imm decoding input + self.state.imm_input = ((self.state.instruction as u64) & (0xfffff << 12)) >> 12; + } + + fn set_immediate(&mut self) { + let mut signed_imm = 0x0000; + + signed_imm = match self.signals.imm_select { + ImmSelect::ISigned => { + let mask = 0b100000000000; + if self.state.imm & mask != 0 { + !(!self.state.imm & 0xFFF) + } else { + self.state.imm + } + } + ImmSelect::IShamt => (signed_imm << 12) | self.state.imm, + ImmSelect::IUnsigned => self.state.imm, + ImmSelect::SType => { + let uimm = ((signed_imm << 7) | self.state.imm1) << 5 | self.state.imm2; + let mask = 0b100000000000; + if uimm & mask != 0 { + !(!uimm & 0xFFF) + } else { + uimm + } + } + ImmSelect::BType => self.state.imm, + ImmSelect::UType => ((signed_imm << 20) | self.state.imm) << 12, + ImmSelect::JType => self.state.imm, + }; + + self.state.imm = signed_imm; + } + + /// Set the control signals for the datapath based on the + /// instruction's opcode. + fn set_control_signals(&mut self) { + match self.instruction { + RiscInstruction::RType(r) => { + self.set_rtype_control_signals(r); + } + RiscInstruction::IType(i) => { + self.set_itype_control_signals(i); + } + RiscInstruction::SType(s) => { + self.set_stype_control_signals(s); + } + RiscInstruction::BType(b) => { + self.set_btype_control_signals(b); + } + RiscInstruction::UType(u) => { + self.set_utype_control_signals(u); + } + RiscInstruction::JType(j) => { + self.set_jtype_control_signals(j); + } + RiscInstruction::R4Type(r4) => { + self.set_r4type_control_signals(r4); + } + } + } + + /// Set the control signals for the datapath, specifically in the + /// case where the instruction is an R-type. + fn set_rtype_control_signals(&mut self, r: RType) { + self.signals = ControlSignals { + op2_select: OP2Select::DATA2, + branch_jump: BranchJump::NoBranch, + read_write: ReadWrite::NoLoadStore, + wb_sel: WBSel::UseAlu, + reg_dst: RegDst::Reg3, + reg_write_en: RegWriteEn::YesWrite, + ..Default::default() + }; + + if r.op == OPCODE_OP_32 { + self.datapath_signals.reg_width = RegisterWidth::HalfWidth; + } + + if r.op == OPCODE_OP_FP { + match r.funct7 >> 2 { + 20 => self.signals.reg_write_en = RegWriteEn::YesWrite, + 24 => self.signals.reg_write_en = RegWriteEn::YesWrite, + 28 => self.signals.reg_write_en = RegWriteEn::YesWrite, + _ => self.signals.reg_write_en = RegWriteEn::NoWrite, + } + } + + match r.funct3 { + 0 => match r.funct7 { + 0b0000000 => self.signals.alu_op = AluOp::Addition, + 0b0100000 => self.signals.alu_op = AluOp::Subtraction, + 0b0000001 => self.signals.alu_op = AluOp::MultiplicationSigned, + _ => (), + }, + 1 => match r.funct7 { + 0b0000000 => self.signals.alu_op = AluOp::ShiftLeftLogical(self.state.shamt), + 0b0000001 => self.signals.alu_op = AluOp::MultiplicationSignedUpper, + _ => (), + }, + 2 => match r.funct7 { + 0b0000000 => self.signals.alu_op = AluOp::SetOnLessThanSigned, + 0b0000001 => self.signals.alu_op = AluOp::MultiplicationSignedUnsignedUpper, + _ => (), + }, + 3 => match r.funct7 { + 0b0000000 => self.signals.alu_op = AluOp::SetOnLessThanUnsigned, + 0b0000001 => self.signals.alu_op = AluOp::MultiplicationUnsignedSignedUpper, + _ => (), + }, + 4 => match r.funct7 { + 0b0000000 => self.signals.alu_op = AluOp::Xor, + 0b0000001 => self.signals.alu_op = AluOp::DivisionSigned, + _ => (), + }, + 5 => match r.funct7 { + 0b0000000 => self.signals.alu_op = AluOp::ShiftRightLogical(self.state.shamt), + 0b0100000 => self.signals.alu_op = AluOp::ShiftRightArithmetic(self.state.shamt), + 0b0000001 => self.signals.alu_op = AluOp::DivisionUnsigned, + _ => (), + }, + 6 => match r.funct7 { + 0b0000000 => self.signals.alu_op = AluOp::Or, + 0b0000001 => self.signals.alu_op = AluOp::RemainderSigned, + _ => (), + }, + 7 => match r.funct7 { + 0b0000000 => self.signals.alu_op = AluOp::And, + 0b0000001 => self.signals.alu_op = AluOp::RemainderUnsigned, + _ => (), + }, + _ => (), + } + } + + /// Set the control signals for the datapath, specifically in the + /// case where the instruction is an I-type. + fn set_itype_control_signals(&mut self, i: IType) { + self.signals = ControlSignals { + reg_dst: RegDst::Reg3, + reg_write_en: RegWriteEn::YesWrite, + ..Default::default() + }; + + if i.op == OPCODE_IMM_32 { + self.datapath_signals.reg_width = RegisterWidth::HalfWidth; + if self.state.shamt >> 5 != 0 { + self.error("Unsupported Instruction!"); + } + } + + match i.op { + OPCODE_IMM | OPCODE_IMM_32 => match i.funct3 { + 0 => self.signals.alu_op = AluOp::Addition, + 1 => self.signals.alu_op = AluOp::ShiftLeftLogical(self.state.shamt), + + 2 => self.signals.alu_op = AluOp::SetOnLessThanSigned, + 3 => self.signals.alu_op = AluOp::SetOnLessThanUnsigned, + 4 => self.signals.alu_op = AluOp::Xor, + 5 => { + match i.imm >> 6 { + 0b000000 => { + self.signals.alu_op = AluOp::ShiftRightLogical(self.state.shamt) + } + 0b010000 => { + self.signals.alu_op = AluOp::ShiftRightArithmetic(self.state.shamt) + } + _ => (), + }; + } + 6 => self.signals.alu_op = AluOp::Or, + 7 => self.signals.alu_op = AluOp::And, + _ => (), + }, + OPCODE_JALR => { + self.signals.imm_select = ImmSelect::IUnsigned; + self.signals.wb_sel = WBSel::UsePcPlusFour; + self.signals.branch_jump = BranchJump::J; + } + OPCODE_LOAD => { + self.signals.wb_sel = WBSel::UseMemory; + match i.funct3 { + 0 => self.signals.read_write = ReadWrite::LoadByte, + 1 => self.signals.read_write = ReadWrite::LoadHalf, + 2 => self.signals.read_write = ReadWrite::LoadWord, + 3 => self.signals.read_write = ReadWrite::LoadDouble, + 4 => self.signals.read_write = ReadWrite::LoadByteUnsigned, + 5 => self.signals.read_write = ReadWrite::LoadHalfUnsigned, + 6 => self.signals.read_write = ReadWrite::LoadWordUnsigned, + _ => (), + } + } + OPCODE_LOAD_FP => { + self.signals.wb_sel = WBSel::UseMemory; + self.signals.reg_write_en = RegWriteEn::NoWrite; + match i.funct3 { + 0 => self.signals.read_write = ReadWrite::LoadByte, + 1 => self.signals.read_write = ReadWrite::LoadHalf, + 2 => self.signals.read_write = ReadWrite::LoadWord, + 3 => self.signals.read_write = ReadWrite::LoadDouble, + 4 => self.signals.read_write = ReadWrite::LoadByteUnsigned, + 5 => self.signals.read_write = ReadWrite::LoadHalfUnsigned, + 6 => self.signals.read_write = ReadWrite::LoadWordUnsigned, + _ => (), + } + } + OPCODE_SYSTEM => { + self.signals.imm_select = ImmSelect::IUnsigned; + self.signals.wb_sel = WBSel::UseAlu; + + match i.funct3 { + 0 => { + self.signals.sys_op = match i.imm { + 0 => SysOp::ECALL, + 1 => SysOp::EBREAK, + _ => SysOp::None, + }; + self.signals.reg_write_en = RegWriteEn::NoWrite; + } + 1 => self.signals.sys_op = SysOp::CSRReadWrite, + 2 => self.signals.sys_op = SysOp::CSRReadSet, + 3 => self.signals.sys_op = SysOp::CSRReadClear, + 5 => { + self.signals.sys_op = SysOp::CSRReadWrite; + self.signals.op1_select = OP1Select::IMM; + } + 6 => { + self.signals.sys_op = SysOp::CSRReadSet; + self.signals.op1_select = OP1Select::IMM; + } + 7 => { + self.signals.sys_op = SysOp::CSRReadClear; + self.signals.op1_select = OP1Select::IMM; + } + _ => { + self.error("Unsupported Instruction!"); + } + } + } + _ => (), + } + } + + /// Set the control signals for the datapath, specifically in the + /// case where the instruction is an S-type. + fn set_stype_control_signals(&mut self, s: SType) { + self.signals = ControlSignals { + imm_select: ImmSelect::SType, + reg_write_en: RegWriteEn::NoWrite, + ..Default::default() + }; + + if s.op == OPCODE_STORE_FP { + self.signals.mem_write_src = MemWriteSrc::FloatingPointUnit; + } + + match s.funct3 { + 0 => self.signals.read_write = ReadWrite::StoreByte, + 1 => self.signals.read_write = ReadWrite::StoreHalf, + 2 => self.signals.read_write = ReadWrite::StoreWord, + 3 => self.signals.read_write = ReadWrite::StoreDouble, + _ => (), + } + } + + /// Set the control signals for the datapath, specifically in the + /// case where the instruction is an B-type. + fn set_btype_control_signals(&mut self, b: BType) { + self.signals = ControlSignals { + imm_select: ImmSelect::BType, + reg_write_en: RegWriteEn::NoWrite, + ..Default::default() + }; + + match b.funct3 { + 0 => self.signals.branch_jump = BranchJump::Beq, + 1 => self.signals.branch_jump = BranchJump::Bne, + 4 => self.signals.branch_jump = BranchJump::Blt, + 5 => self.signals.branch_jump = BranchJump::Bge, + 6 => self.signals.branch_jump = BranchJump::Bltu, + 7 => self.signals.branch_jump = BranchJump::Bgeu, + _ => (), + } + } + + /// Set the control signals for the datapath, specifically in the + /// case where the instruction is an U-type. + fn set_utype_control_signals(&mut self, u: UType) { + self.signals = ControlSignals { + imm_select: ImmSelect::UType, + op1_select: OP1Select::PC, + alu_op: AluOp::Addition, + reg_dst: RegDst::Reg3, + reg_write_en: RegWriteEn::YesWrite, + ..Default::default() + }; + + match u.op { + OPCODE_AUIPC => self.signals.wb_sel = WBSel::UseAlu, + OPCODE_LUI => self.signals.wb_sel = WBSel::UseImmediate, + _ => (), + } + } + + /// Set control signals for J-Type instructions + fn set_jtype_control_signals(&mut self, _j: JType) { + self.signals = ControlSignals { + imm_select: ImmSelect::JType, + branch_jump: BranchJump::J, + wb_sel: WBSel::UsePcPlusFour, + reg_write_en: RegWriteEn::YesWrite, + ..Default::default() + }; + } + + /// Set the control signals for the datapath, specifically in the + /// case where the instruction is an R4-type. + fn set_r4type_control_signals(&mut self, _r4: R4Type) { + self.signals = ControlSignals { + op2_select: OP2Select::DATA2, + branch_jump: BranchJump::NoBranch, + read_write: ReadWrite::NoLoadStore, + wb_sel: WBSel::UseAlu, + reg_write_en: RegWriteEn::NoWrite, + ..Default::default() + }; + } + + /// Read the registers as specified from the instruction and pass + /// the data into the datapath. + fn read_registers(&mut self) { + self.state.read_data_1 = self.registers.gpr[self.state.rs1 as usize]; + self.state.read_data_2 = self.registers.gpr[self.state.rs2 as usize]; + } + + // ======================= Execute (EX) ======================= + /// Perform an ALU operation. + fn alu(&mut self) { + // Specify the inputs for the operation. The first will always + // be the first register, but the second may be either the + // second register, the sign-extended immediate value, or the + // zero-extended immediate value. + self.state.alu_input1 = match self.signals.op1_select { + OP1Select::PC => self.registers.pc, + OP1Select::DATA1 => self.state.read_data_1, + OP1Select::IMM => self.state.rs1 as u64, + }; + + self.state.alu_input2 = match self.signals.op2_select { + OP2Select::DATA2 => self.state.read_data_2, + OP2Select::IMM => self.state.imm as i64 as u64, + }; + + if self.signals.sys_op != SysOp::None { + self.csr_handler(); + return; + } + + if self.datapath_signals.reg_width == RegisterWidth::HalfWidth { + self.state.alu_input1 = self.state.alu_input1 as u32 as i64 as u64; + self.state.alu_input2 = self.state.alu_input2 as u32 as i64 as u64; + } + + // Set the result. + self.state.alu_result = match self.signals.alu_op { + AluOp::Addition => self.state.alu_input1.wrapping_add(self.state.alu_input2), + AluOp::Subtraction => { + (self.state.alu_input1 as i64).wrapping_sub(self.state.alu_input2 as i64) as u64 + } + AluOp::SetOnLessThanSigned => { + ((self.state.alu_input1 as i64) < (self.state.alu_input2 as i64)) as u64 + } + AluOp::SetOnLessThanUnsigned => (self.state.alu_input1 < self.state.alu_input2) as u64, + AluOp::And => self.state.alu_input1 & self.state.alu_input2, + AluOp::Or => self.state.alu_input1 | self.state.alu_input2, + AluOp::Xor => self.state.alu_input1 ^ self.state.alu_input2, + AluOp::ShiftLeftLogical(shamt) => self.state.alu_input1 << shamt, + AluOp::ShiftRightLogical(shamt) => self.state.alu_input1 >> shamt, + AluOp::ShiftRightArithmetic(shamt) => ((self.state.alu_input1 as i64) >> shamt) as u64, + AluOp::MultiplicationSigned => { + ((self.state.alu_input1 as i128) * (self.state.alu_input2 as i128)) as u64 + } + AluOp::MultiplicationUnsigned => { + ((self.state.alu_input1 as u128) * (self.state.alu_input2 as u128)) as u64 + } + AluOp::MultiplicationSignedUpper => { + (((self.state.alu_input1 as i128) * (self.state.alu_input2 as i128)) >> 64) as u64 + } + AluOp::MultiplicationSignedUnsignedUpper => { + (((self.state.alu_input1 as i128) * (self.state.alu_input2 as u128 as i128)) >> 64) + as u64 + } + AluOp::MultiplicationUnsignedSignedUpper => { + (((self.state.alu_input1 as u128 as i128) * (self.state.alu_input2 as i128)) >> 64) + as u64 + } + AluOp::DivisionSigned => { + if self.state.alu_input2 == 0 { + 0 + } else { + ((self.state.alu_input1 as i64) / (self.state.alu_input2 as i64)) as u64 + } + } + AluOp::DivisionUnsigned => { + if self.state.alu_input2 == 0 { + 0 + } else { + self.state.alu_input1 / self.state.alu_input2 + } + } + AluOp::RemainderSigned => { + if self.state.alu_input2 == 0 { + 0 + } else { + ((self.state.alu_input1 as i64) % (self.state.alu_input2 as i64)) as u64 + } + } + AluOp::RemainderUnsigned => { + if self.state.alu_input2 == 0 { + 0 + } else { + self.state.alu_input1 % self.state.alu_input2 + } + } + _ => 0, + }; + + if self.datapath_signals.reg_width == RegisterWidth::HalfWidth { + self.state.alu_result = self.state.alu_result as u32 as i64 as u64; + } + + // Set the zero bit/signal. + self.datapath_signals.alu_z = match self.state.alu_result { + 0 => AluZ::YesZero, + _ => AluZ::NoZero, + }; + } + + fn csr_handler(&mut self) { + match self.signals.sys_op { + SysOp::CSRReadWrite => { + if self.registers["X0"] != self.registers.gpr[self.state.rd as usize] { + self.state.alu_result = self.registers.gpr[self.state.imm as usize]; + } else { + self.state.alu_result = self.registers.gpr[self.state.rd as usize]; + } + self.registers.gpr[self.state.imm as usize] = self.state.alu_input1; + } + SysOp::CSRReadSet => { + self.state.alu_result = self.registers.gpr[self.state.imm as usize]; + if self.registers["X0"] != self.state.alu_input1 { + self.registers.gpr[self.state.imm as usize] |= self.state.alu_input1; + } + } + SysOp::CSRReadClear => { + self.state.alu_result = self.registers.gpr[self.state.imm as usize]; + if self.registers["X0"] != self.state.alu_input1 { + self.registers.gpr[self.state.imm as usize] &= !self.state.alu_input1; + } + } + SysOp::None => self.error("Impossible/Unsupported Instruction!"), + _ => self.state.alu_result = 0, + } + } + + fn construct_jump_address(&mut self) { + self.state.i_type_jump = + ((self.state.imm as u64).wrapping_add(self.state.read_data_1)) & 0xfffffffffffffff0; + self.state.jump_address = match self.instruction { + RiscInstruction::IType(_i) => { + (self.state.imm as u64).wrapping_add(self.state.read_data_1) + } + RiscInstruction::JType(_j) => (self.state.imm as u64).wrapping_shl(2), + _ => self.state.jump_address, + } + } + + fn calc_relative_pc_branch(&mut self) { + if self.state.imm >= 0 { + self.state.relative_pc_branch = self.state.imm as u64 * 4; + } + } + + /// Determine the value of the [`CpuBranch`] signal. + fn calc_cpu_branch_signal(&mut self) { + // Start by assuming there is no branch. + self.datapath_signals.cpu_branch = CpuBranch::NoBranch; + + // condition_is_true is based on the ALU and the BranchType. This + // is the line between the multiplexer and the AND gate, where the + // AND gate has as input the Branch control signal and said + // multiplexer. + // + // Depending on the branch type, this may use the ALU's Zero signal + // as-is or inverted. + let condition_is_true = match self.signals.branch_jump { + BranchJump::Beq => self.state.read_data_1 == self.state.read_data_2, + BranchJump::Bne => self.state.read_data_1 != self.state.read_data_2, + BranchJump::Bge => self.state.read_data_1 as i64 >= self.state.read_data_2 as i64, + BranchJump::Bgeu => self.state.read_data_1 >= self.state.read_data_2, + BranchJump::Blt => (self.state.read_data_1 as i64) < (self.state.read_data_2 as i64), + BranchJump::Bltu => self.state.read_data_1 < self.state.read_data_2, + _ => false, + }; + + if condition_is_true { + self.datapath_signals.cpu_branch = CpuBranch::YesBranch; + } + } + + // ======================= Memory (MEM) ======================= + /// Read from memory based on the address provided by the ALU in + /// [`RiscDatapathState::alu_result`]. Returns the result to [`RiscDatapathState::memory_data`]. + /// Should the address be invalid or otherwise memory cannot be + /// read at the given address, bitwise 0 will be used in lieu of + /// any data. + fn memory_read(&mut self) { + let address = self.state.alu_result; + + // Load memory, first choosing the correct load function by the + // RegWidth control signal, then reading the result from this + // memory access. + self.state.memory_data = match self.signals.read_write { + ReadWrite::LoadByte => self.memory.load_byte(address).unwrap_or(0) as i64 as u64, + ReadWrite::LoadByteUnsigned => self.memory.load_byte(address).unwrap_or(0) as u64, + ReadWrite::LoadHalf => self.memory.load_half(address).unwrap_or(0) as i64 as u64, + ReadWrite::LoadHalfUnsigned => self.memory.load_half(address).unwrap_or(0) as u64, + ReadWrite::LoadWord => self.memory.load_word(address).unwrap_or(0) as i64 as u64, + ReadWrite::LoadWordUnsigned => self.memory.load_word(address).unwrap_or(0) as u64, + ReadWrite::LoadDouble => self.memory.load_double_word(address).unwrap_or(0), + _ => 0, + }; + } + + /// Write to memory based on the address provided by the ALU in + /// [`RiscDatapathState::alu_result`]. The source of the data being written to + /// memory is determined by [`MemWriteSrc`]. + fn memory_write(&mut self) { + let address = self.state.alu_result; + + self.state.write_data = match self.signals.mem_write_src { + MemWriteSrc::PrimaryUnit => self.state.read_data_2, + MemWriteSrc::FloatingPointUnit => self.coprocessor.get_fp_register_to_memory(), + }; + + // Choose the correct store function based on the RegWidth + // control signal. + match self.signals.read_write { + ReadWrite::StoreByte => { + self.memory + .store_byte(address, self.state.write_data as u8) + .ok(); + } + ReadWrite::StoreHalf => { + self.memory + .store_half(address, self.state.write_data as u16) + .ok(); + } + ReadWrite::StoreWord => { + self.memory + .store_word(address, self.state.write_data as u32) + .ok(); + } + ReadWrite::StoreDouble => { + self.memory + .store_double_word(address, self.state.write_data) + .ok(); + } + _ => (), + }; + } + + fn calc_general_branch_signal(&mut self) { + // Assume there is no branch initially. + self.datapath_signals.general_branch = GeneralBranch::NoBranch; + + if let CpuBranch::YesBranch = self.datapath_signals.cpu_branch { + self.datapath_signals.general_branch = GeneralBranch::YesBranch; + } + } + + fn pick_pc_plus_4_or_relative_branch_addr_mux1(&mut self) { + if let GeneralBranch::YesBranch = self.datapath_signals.general_branch { + self.state.mem_mux1_to_mem_mux2 = self.state.relative_pc_branch; + } else { + self.state.mem_mux1_to_mem_mux2 = self.state.pc_plus_4; + } + } + + fn set_new_pc_mux2(&mut self) { + self.state.new_pc = match self.signals.branch_jump { + BranchJump::J => self.state.jump_address, + _ => self.state.mem_mux1_to_mem_mux2, + }; + } + + // ====================== Writeback (WB) ====================== + /// Write to a register. This will only write if the RegWrite + /// control signal is set. + fn register_write(&mut self) { + // Determine what data will be sent to the register: either + // the result from the ALU, or data retrieved from memory. + self.state.data_result = match self.signals.wb_sel { + WBSel::UseAlu => self.state.alu_result, + WBSel::UseMemory => self.state.memory_data, + WBSel::UsePcPlusFour => self.state.pc_plus_4, + WBSel::UseImmediate => self.state.imm as i64 as u64, + }; + + // Decide to retrieve data either from the main processor or the coprocessor. + self.state.register_write_data = match self.coprocessor.signals.data_write { + DataWrite::NoWrite => self.state.data_result, + DataWrite::YesWrite => self.coprocessor.get_data_writeback(), + }; + + // Abort if the RegWrite signal is not set. + if self.signals.reg_write_en == RegWriteEn::NoWrite { + return; + } + + // Determine the destination for the data to write. This is + // determined by the RegDst control signal. + self.state.write_register_destination = match self.signals.reg_dst { + RegDst::Reg1 => self.state.rs1 as usize, + RegDst::Reg2 => self.state.rs2 as usize, + RegDst::Reg3 => self.state.rd as usize, + RegDst::ReturnRegister => 31_usize, + }; + + // If we are attempting to write to register $zero, stop. + if self.state.write_register_destination == 0 { + return; + } + + // Write. + self.registers.gpr[self.state.write_register_destination] = self.state.register_write_data; + } + + /// Update the program counter register. + /// + /// This function is called from the WB stage. + fn set_pc(&mut self) { + self.registers.pc = self.state.new_pc; + } +} diff --git a/src/emulation_core/riscv/datapath_signals.rs b/src/emulation_core/riscv/datapath_signals.rs new file mode 100644 index 000000000..cccde994f --- /dev/null +++ b/src/emulation_core/riscv/datapath_signals.rs @@ -0,0 +1,61 @@ +//! Internal datapath signals. + +#[derive(Clone, Default, PartialEq)] +pub struct DatapathSignals { + pub alu_z: AluZ, + pub cpu_branch: CpuBranch, + pub general_branch: GeneralBranch, + pub reg_width: RegisterWidth, +} + +/// The "Zero" line that comes out of the ALU. +/// +/// Indicates whether or not the result of the last arithmetic +/// operation was equal to 0. +#[derive(Clone, Default, PartialEq)] +pub enum AluZ { + /// The result of the ALU is bitwise zero. + #[default] + YesZero = 0, + + /// The result of the ALU is non-zero. + NoZero = 1, +} + +/// CPU branch signal. This is the final determined branch signal from the CPU. +/// +/// This signal uses as input the [`BranchJump`](super::control_signals::BranchJump) +/// and [`AluZ`] signals to determine its value. This signal is set in the EX stage. +#[derive(Clone, Default, PartialEq)] +pub enum CpuBranch { + /// Do not branch. + /// Based on the following formula: `(BranchJump == NoBranch) || (AluZ != YesZero)` + #[default] + NoBranch = 0, + + /// Branch. + /// Based on the following formula: `(Branch != NoBranch) && (AluZ == YesZero)` + YesBranch = 1, +} + +/// General branch signal. This is the final determined branch signal from +/// the CPU and FPU combined. +/// +/// This signal uses as input the [`CpuBranch`] signal. +/// This signal is set in the MEM stage. +/// +/// The following formula is considered: [`GeneralBranch`] = [`CpuBranch`] +#[derive(Clone, Default, PartialEq)] +pub enum GeneralBranch { + #[default] + NoBranch = 0, + YesBranch = 1, +} + +/// If in RV64, set instructions to only operate in RV32 mode. +#[derive(Clone, Default, PartialEq)] +pub enum RegisterWidth { + #[default] + DoubleWidth = 0, + HalfWidth = 1, +} diff --git a/src/emulation_core/riscv/instruction.rs b/src/emulation_core/riscv/instruction.rs new file mode 100644 index 000000000..12e51f045 --- /dev/null +++ b/src/emulation_core/riscv/instruction.rs @@ -0,0 +1,1227 @@ +//! Abstract representation of an instruction. + +use std::collections::HashMap; + +use serde::{Deserialize, Serialize}; + +use crate::parser::parser_structs_and_enums::{RISCV_FP_REGISTERS, RISCV_GP_REGISTERS}; + +use super::constants::*; + +/// Register (R-Type) RiscInstruction +/// +/// ```text +/// 31 25 24 20 19 15 14 12 11 7 6 0 +/// ┌───────────────┬─────────────┬─────────────┬───────────┬─────────────┬───────────────┐ +/// │ funct7 │ rs2 │ rs1 │ funct3 │ rd │ opcode │ +/// │ │ │ │ │ │ │ +/// └───────────────┴─────────────┴─────────────┴───────────┴─────────────┴───────────────┘ +/// 7 5 5 3 5 7 +/// ``` +/// + +/// - funct7: 7-bit Function field uses to specify options or operations. +/// - rs2: CPU register - used as a source to read from in the register file. +/// - rs1: CPU register - used as a source to read from in the register file. +/// - funct3: 3-bit Function field uses to specify options or operations. +/// - rd: CPU register - can be used as a destination for the result of executed instructions. +/// - opcode: Determines the type of instruction executed. +#[derive(Clone, Copy, Debug, Default, PartialEq, Serialize, Deserialize)] +pub struct RType { + pub funct7: u8, + pub rs2: u8, + pub rs1: u8, + pub funct3: u8, + pub rd: u8, + pub op: u8, +} + +/// Immediate (I-Type) Instruction +/// +/// ```text +/// 31 20 19 15 14 12 11 7 6 0 +/// ┌─────────────────────────────┬─────────────┬───────────┬─────────────┬───────────────┐ +/// │ imm[11:0] │ rs1 │ funct3 │ rd │ opcode │ +/// │ │ │ │ │ │ +/// └─────────────────────────────┴─────────────┴───────────┴─────────────┴───────────────┘ +/// 12 5 3 5 7 +/// ``` +/// + +/// - imm[11:0]: 12-bit immediate value, sign-extended by 20 bits. +/// - rs1: CPU register - used as a source to read from in the register file. +/// - funct3: 3-bit Function field uses to specify options or operations. +/// - rd: CPU register - can be used as a destination for the result of executed instructions. +/// - opcode: Determines the type of instruction executed. +#[derive(Clone, Copy, Debug, Default, PartialEq, Serialize, Deserialize)] +pub struct IType { + pub imm: u16, + pub rs1: u8, + pub funct3: u8, + pub rd: u8, + pub op: u8, +} + +/// Store (S-Type) Instruction +/// +/// ```text +/// 31 25 24 20 19 15 14 12 11 7 6 0 +/// ┌───────────────┬─────────────┬─────────────┬───────────┬─────────────┬───────────────┐ +/// │ imm[11:5] │ rs2 │ rs1 │ funct3 │ imm[4:0] │ opcode │ +/// │ │ │ │ │ │ │ +/// └───────────────┴─────────────┴─────────────┴───────────┴─────────────┴───────────────┘ +/// 7 5 5 3 5 7 +/// ``` +/// + +/// - imm[11:5]: Upper 7-bits of the 12-bit immediate. +/// - rs2: CPU register - used as a source to read from in the register file. +/// - rs1: CPU register - used as a source to read from in the register file. +/// - funct3: 3-bit Function field uses to specify options or operations. +/// - imm[4:0]: Lower 5-bits of the 12-bit immediate +/// - opcode: Determines the type of instruction executed. +#[derive(Clone, Copy, Debug, Default, PartialEq, Serialize, Deserialize)] +pub struct SType { + pub imm1: u8, + pub rs2: u8, + pub rs1: u8, + pub funct3: u8, + pub imm2: u8, + pub op: u8, +} + +/// Branch (B-Type) Instruction +/// +/// ```text +/// 31 20 19 15 14 12 11 7 6 0 +/// ┌─────────────────────────────┬─────────────┬───────────┬─────────────┬───────────────┐ +/// │ imm[11:0] │ rs2 │ funct3 │ rs1 │ opcode │ +/// │ │ │ │ │ │ +/// └─────────────────────────────┴─────────────┴───────────┴─────────────┴───────────────┘ +/// 12 5 3 5 7 +/// ``` +/// + +/// - imm[11:0]: 12-bit immediate value, sign-extended by 20 bits. +/// - rs1: CPU register - used as a source to read from in the register file. +/// - funct3: 3-bit Function field uses to specify options or operations. +/// - rs2: CPU register - used as a source to read from in the register file. +/// - opcode: Determines the type of instruction executed. +#[derive(Clone, Copy, Debug, Default, PartialEq, Serialize, Deserialize)] +pub struct BType { + pub imm: u16, + pub rs2: u8, + pub rs1: u8, + pub funct3: u8, + pub op: u8, +} + +/// Upper Immediate (U-Type) Instruction +/// +/// ```text +/// 31 12 11 7 6 0 +/// ┌───────────────────────────────────────────────────────┬─────────────┬───────────────┐ +/// │ imm[31:12] │ rd │ opcode │ +/// │ │ │ │ +/// └───────────────────────────────────────────────────────┴─────────────┴───────────────┘ +/// 20 5 7 +/// ``` +/// + +/// - imm[31:12]: 20-bit upper immediate. +/// - rd: CPU register - can be used as a destination for the result of executed instructions. +/// - opcode: Determines the type of instruction executed. +#[derive(Clone, Copy, Debug, Default, PartialEq, Serialize, Deserialize)] +pub struct UType { + pub imm: i32, + pub rd: u8, + pub op: u8, +} + +/// Jump (J-Type) Instruction +/// +/// ```text +/// 31 12 11 7 6 0 +/// ┌───────────────────────────────────────────────────────┬─────────────┬───────────────┐ +/// │ imm │ rd │ opcode │ +/// │ │ │ │ +/// └───────────────────────────────────────────────────────┴─────────────┴───────────────┘ +/// 20 5 7 +/// ``` +/// + +/// - imm: 20-bit immediate. +/// - rd: CPU register - can be used as a destination for the result of executed instructions. +/// - opcode: Determines the type of instruction executed. +#[derive(Clone, Copy, Debug, Default, PartialEq, Serialize, Deserialize)] +pub struct JType { + pub imm: i32, + pub rd: u8, + pub op: u8, +} + +/// Fused-Register (R4-Type) Instruction +/// +/// ```text +/// 31 27 26 25 24 20 19 15 14 12 11 7 6 0 +/// ┌────────┬──────┬─────────────┬─────────────┬───────────┬─────────────┬───────────────┐ +/// │ rs3 │funct2│ rs2 │ rs1 │ funct3 │ rd │ opcode │ +/// │ │ │ │ │ │ │ │ +/// └────────┴──────┴─────────────┴─────────────┴───────────┴─────────────┴───────────────┘ +/// 5 2 5 5 3 5 7 +/// ``` +/// + +/// - rs3: CPU register - used as a source to read from in the register file. +/// - funct2: 2-bit Function field uses to specify options or operations. +/// - rs2: CPU register - used as a source to read from in the register file. +/// - rs1: CPU register - used as a source to read from in the register file. +/// - funct3: 3-bit Function field uses to specify options or operations. +/// - rd: CPU register - can be used as a destination for the result of executed instructions. +/// - opcode: Determines the type of instruction executed. +#[derive(Clone, Copy, Debug, Default, PartialEq, Serialize, Deserialize)] +pub struct R4Type { + pub rs3: u8, + pub funct2: u8, + pub rs2: u8, + pub rs1: u8, + pub funct3: u8, + pub rd: u8, + pub op: u8, +} + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub enum RiscInstruction { + RType(RType), + IType(IType), + SType(SType), + BType(BType), + UType(UType), + JType(JType), + R4Type(R4Type), +} + +impl Default for RiscInstruction { + fn default() -> Self { + RiscInstruction::RType(RType::default()) + } +} + +impl TryFrom for RiscInstruction { + type Error = String; + + /// Based on the opcode, convert a binary instruction into a struct representation. + fn try_from(value: u32) -> Result { + let op = (value & 0x7f) as u8; + match op { + // R-type instructions: + OPCODE_OP | OPCODE_OP_32 | OPCODE_OP_FP => Ok(RiscInstruction::RType(RType { + funct7: (value >> 25) as u8, + rs2: ((value >> 20) & 0x1f) as u8, + rs1: ((value >> 15) & 0x1f) as u8, + funct3: ((value >> 12) & 0x07) as u8, + rd: ((value >> 7) & 0x1f) as u8, + op: (value & 0x7f) as u8, + })), + + // I-type instructions: + OPCODE_IMM | OPCODE_IMM_32 | OPCODE_JALR | OPCODE_LOAD | OPCODE_SYSTEM + | OPCODE_LOAD_FP => Ok(RiscInstruction::IType(IType { + imm: (value >> 20) as u16, + rs1: ((value >> 15) & 0x1f) as u8, + funct3: ((value >> 12) & 0x07) as u8, + rd: ((value >> 7) & 0x1f) as u8, + op: (value & 0x7f) as u8, + })), + + // S-type instruction: + OPCODE_STORE | OPCODE_STORE_FP => Ok(RiscInstruction::SType(SType { + imm1: (value >> 25) as u8, + rs2: ((value >> 20) & 0x1f) as u8, + rs1: ((value >> 15) & 0x1f) as u8, + funct3: ((value >> 12) & 0x07) as u8, + imm2: ((value >> 7) & 0x1f) as u8, + op: (value & 0x7f) as u8, + })), + + // B-type instruction: + OPCODE_BRANCH => Ok(RiscInstruction::BType(BType { + imm: (value >> 20) as u16, + rs1: ((value >> 7) & 0x1f) as u8, + funct3: ((value >> 12) & 0x07) as u8, + rs2: ((value >> 15) & 0x1f) as u8, + op: (value & 0x7f) as u8, + })), + + // U-type instruction: + OPCODE_LUI | OPCODE_AUIPC => Ok(RiscInstruction::UType(UType { + imm: (value >> 12) as i32, + rd: ((value >> 7) & 0x1f) as u8, + op: (value & 0x7f) as u8, + })), + + // J-type instruction: + OPCODE_JAL => Ok(RiscInstruction::JType(JType { + imm: (value >> 12) as i32, + rd: ((value >> 7) & 0x1f) as u8, + op: (value & 0x7f) as u8, + })), + + // R4-type instruction: + OPCODE_MADD | OPCODE_MSUB | OPCODE_NMSUB | OPCODE_NMADD => { + Ok(RiscInstruction::R4Type(R4Type { + rs3: (value >> 27) as u8, + funct2: ((value >> 25) & 0x3) as u8, + rs2: ((value >> 20) & 0x1f) as u8, + rs1: ((value >> 15) & 0x1f) as u8, + funct3: ((value >> 12) & 0x07) as u8, + rd: ((value >> 7) & 0x1f) as u8, + op: (value & 0x7f) as u8, + })) + } + + _ => Err(format!("opcode `{op}` not supported")), + } + } +} + +impl RiscInstruction { + pub fn get_string_version( + value: u32, + labels: HashMap, + ) -> Result { + let mut string_version = String::new(); + + let struct_representation = RiscInstruction::try_from(value)?; + + match struct_representation { + RiscInstruction::RType(r_type) => { + let mut rs1 = find_register_name(r_type.rs1).unwrap(); + let mut rs2 = find_register_name(r_type.rs2).unwrap(); + let mut rd = find_register_name(r_type.rd).unwrap(); + match r_type.op { + OPCODE_OP => match r_type.funct3 { + 0 => match r_type.funct7 { + 0b0000000 => { + string_version + .push_str(&format!("{} {}, {}, {}", "add", rd, rs1, rs2)); + } + 0b0100000 => { + string_version + .push_str(&format!("{} {}, {}, {}", "sub", rd, rs1, rs2)); + } + 0b0000001 => { + string_version + .push_str(&format!("{} {}, {}, {}", "mul", rd, rs1, rs2)); + } + _ => (), + }, + 1 => match r_type.funct7 { + 0b0000000 => { + string_version + .push_str(&format!("{} {}, {}, {}", "sll", rd, rs1, rs2)); + } + 0b0000001 => { + string_version + .push_str(&format!("{} {}, {}, {}", "mulh", rd, rs1, rs2)); + } + _ => (), + }, + 2 => match r_type.funct7 { + 0b0000000 => { + string_version + .push_str(&format!("{} {}, {}, {}", "slt", rd, rs1, rs2)); + } + 0b0000001 => { + string_version + .push_str(&format!("{} {}, {}, {}", "mulhsu", rd, rs1, rs2)); + } + _ => (), + }, + 3 => match r_type.funct7 { + 0b0000000 => { + string_version + .push_str(&format!("{} {}, {}, {}", "sltu", rd, rs1, rs2)); + } + 0b0000001 => { + string_version + .push_str(&format!("{} {}, {}, {}", "mulhu", rd, rs1, rs2)); + } + _ => (), + }, + 4 => match r_type.funct7 { + 0b0000000 => { + string_version + .push_str(&format!("{} {}, {}, {}", "xor", rd, rs1, rs2)); + } + 0b0000001 => { + string_version + .push_str(&format!("{} {}, {}, {}", "div", rd, rs1, rs2)); + } + _ => (), + }, + 5 => match r_type.funct7 { + 0b0000000 => { + string_version + .push_str(&format!("{} {}, {}, {}", "srl", rd, rs1, rs2)); + } + 0b0000001 => { + string_version + .push_str(&format!("{} {}, {}, {}", "divu", rd, rs1, rs2)); + } + 0b0100000 => { + string_version + .push_str(&format!("{} {}, {}, {}", "sra", rd, rs1, rs2)); + } + _ => (), + }, + 6 => match r_type.funct7 { + 0b0000000 => { + string_version + .push_str(&format!("{} {}, {}, {}", "or", rd, rs1, rs2)); + } + 0b0000001 => { + string_version + .push_str(&format!("{} {}, {}, {}", "rem", rd, rs1, rs2)); + } + _ => (), + }, + 7 => match r_type.funct7 { + 0b0000000 => { + string_version + .push_str(&format!("{} {}, {}, {}", "and", rd, rs1, rs2)); + } + 0b0000001 => { + string_version + .push_str(&format!("{} {}, {}, {}", "remu", rd, rs1, rs2)); + } + _ => (), + }, + _ => (), + }, + // RISCV64I + OPCODE_OP_32 => match r_type.funct3 { + 0 => match r_type.funct7 { + 0b0000000 => { + string_version + .push_str(&format!("{} {}, {}, {}", "addw", rd, rs1, rs2)); + } + 0b0100000 => { + string_version + .push_str(&format!("{} {}, {}, {}", "subw", rd, rs1, rs2)); + } + 0b0000001 => { + string_version + .push_str(&format!("{} {}, {}, {}", "mulw", rd, rs1, rs2)); + } + _ => (), + }, + 1 => { + string_version + .push_str(&format!("{} {}, {}, {}", "sllw", rd, rs1, rs2)); + } + 4 => { + string_version + .push_str(&format!("{} {}, {}, {}", "divw", rd, rs1, rs2)); + } + 5 => match r_type.funct7 { + 0b0000000 => { + string_version + .push_str(&format!("{} {}, {}, {}", "srlw", rd, rs1, rs2)); + } + 0b0100000 => { + string_version + .push_str(&format!("{} {}, {}, {}", "sraw", rd, rs1, rs2)); + } + 0b0000001 => { + string_version + .push_str(&format!("{} {}, {}, {}", "divuw", rd, rs1, rs2)); + } + _ => (), + }, + 6 => { + string_version + .push_str(&format!("{} {}, {}, {}", "remw", rd, rs1, rs2)); + } + 7 => { + string_version + .push_str(&format!("{} {}, {}, {}", "remuw", rd, rs1, rs2)); + } + _ => (), + }, + // RISCV64F + OPCODE_OP_FP => { + rd = find_register_name_fp(r_type.rd).unwrap(); + rs1 = find_register_name_fp(r_type.rs1).unwrap(); + rs2 = find_register_name_fp(r_type.rs2).unwrap(); + + match r_type.funct7 >> 2 { + 0 => match r_type.funct7 & 0b11 { + 0 => { + string_version.push_str(&format!( + "{} {}, {}, {}", + "fadd.s", rd, rs1, rs2 + )); + } + 1 => { + string_version.push_str(&format!( + "{} {}, {}, {}", + "fadd.d", rd, rs1, rs2 + )); + } + _ => (), + }, + 1 => match r_type.funct7 & 0b11 { + 0 => { + string_version.push_str(&format!( + "{} {}, {}, {}", + "fsub.s", rd, rs1, rs2 + )); + } + 1 => { + string_version.push_str(&format!( + "{} {}, {}, {}", + "fsub.d", rd, rs1, rs2 + )); + } + _ => (), + }, + 2 => match r_type.funct7 & 0b11 { + 0 => { + string_version.push_str(&format!( + "{} {}, {}, {}", + "fmul.s", rd, rs1, rs2 + )); + } + 1 => { + string_version.push_str(&format!( + "{} {}, {}, {}", + "fmul.d", rd, rs1, rs2 + )); + } + _ => (), + }, + 3 => match r_type.funct7 & 0b11 { + 0 => { + string_version.push_str(&format!( + "{} {}, {}, {}", + "fdiv.s", rd, rs1, rs2 + )); + } + 1 => { + string_version.push_str(&format!( + "{} {}, {}, {}", + "fdiv.d", rd, rs1, rs2 + )); + } + _ => (), + }, + 4 => match r_type.funct3 { + 0 => match r_type.funct7 & 0b11 { + 0 => { + string_version.push_str(&format!( + "{} {}, {}, {}", + "fsgnj.s", rd, rs1, rs2 + )); + } + 1 => { + string_version.push_str(&format!( + "{} {}, {}, {}", + "fsgnj.d", rd, rs1, rs2 + )); + } + _ => (), + }, + 1 => match r_type.funct7 & 0b11 { + 0 => { + string_version.push_str(&format!( + "{} {}, {}, {}", + "fsgnjn.s", rd, rs1, rs2 + )); + } + 1 => { + string_version.push_str(&format!( + "{} {}, {}, {}", + "fsgnjn.d", rd, rs1, rs2 + )); + } + _ => (), + }, + 2 => match r_type.funct7 & 0b11 { + 0 => { + string_version.push_str(&format!( + "{} {}, {}, {}", + "fsgnjx.s", rd, rs1, rs2 + )); + } + 1 => { + string_version.push_str(&format!( + "{} {}, {}, {}", + "fsgnjx.d", rd, rs1, rs2 + )); + } + _ => (), + }, + _ => (), + }, + 5 => match r_type.funct3 { + 0 => match r_type.funct7 & 0b11 { + 0 => { + string_version.push_str(&format!( + "{} {}, {}, {}", + "fmin.s", rd, rs1, rs2 + )); + } + 1 => { + string_version.push_str(&format!( + "{} {}, {}, {}", + "fmin.d", rd, rs1, rs2 + )); + } + _ => (), + }, + 1 => match r_type.funct7 & 0b11 { + 0 => { + string_version.push_str(&format!( + "{} {}, {}, {}", + "fmax.s", rd, rs1, rs2 + )); + } + 1 => { + string_version.push_str(&format!( + "{} {}, {}, {}", + "fmax.d", rd, rs1, rs2 + )); + } + _ => (), + }, + _ => (), + }, + 8 => match r_type.rs2 { + 0 => { + string_version.push_str(&format!( + "{} {}, {}, {}", + "fcvt.d.s", rd, rs1, rs2 + )); + } + 1 => { + string_version.push_str(&format!( + "{} {}, {}, {}", + "fcvt.s.d", rd, rs1, rs2 + )); + } + _ => (), + }, + 11 => match r_type.funct7 & 0b11 { + 0 => { + string_version.push_str(&format!( + "{} {}, {}, {}", + "fsqrt.s", rd, rs1, rs2 + )); + } + 1 => { + string_version.push_str(&format!( + "{} {}, {}, {}", + "fsqrt.d", rd, rs1, rs2 + )); + } + _ => (), + }, + 20 => { + rd = find_register_name(r_type.rd).unwrap(); + match r_type.funct3 { + 0 => match r_type.funct7 & 0b11 { + 0 => { + string_version.push_str(&format!( + "{} {}, {}, {}", + "fle.s", rd, rs1, rs2 + )); + } + 1 => { + string_version.push_str(&format!( + "{} {}, {}, {}", + "fle.d", rd, rs1, rs2 + )); + } + _ => (), + }, + 1 => match r_type.funct7 & 0b11 { + 0 => { + string_version.push_str(&format!( + "{} {}, {}, {}", + "flt.s", rd, rs1, rs2 + )); + } + 1 => { + string_version.push_str(&format!( + "{} {}, {}, {}", + "flt.d", rd, rs1, rs2 + )); + } + _ => (), + }, + 2 => match r_type.funct7 & 0b11 { + 0 => { + string_version.push_str(&format!( + "{} {}, {}, {}", + "feq.s", rd, rs1, rs2 + )); + } + 1 => { + string_version.push_str(&format!( + "{} {}, {}, {}", + "feq.d", rd, rs1, rs2 + )); + } + _ => (), + }, + _ => (), + } + } + 24 => { + rd = find_register_name(r_type.rd).unwrap(); + match r_type.rs2 { + 0 => match r_type.funct7 & 0b11 { + 0 => { + string_version.push_str(&format!( + "{} {}, {}", + "fcvt.w.s", rd, rs1 + )); + } + 1 => { + string_version.push_str(&format!( + "{} {}, {}", + "fcvt.w.d", rd, rs1 + )); + } + _ => (), + }, + 1 => match r_type.funct7 & 0b11 { + 0 => { + string_version.push_str(&format!( + "{} {}, {}", + "fcvt.wu.s", rd, rs1 + )); + } + 1 => { + string_version.push_str(&format!( + "{} {}, {}", + "fcvt.wu.d", rd, rs1 + )); + } + _ => (), + }, + 2 => { + string_version + .push_str(&format!("{} {}, {}", "fcvt.l.s", rd, rs1)); + } + 3 => { + string_version + .push_str(&format!("{} {}, {}", "fcvt.lu.s", rd, rs1)); + } + _ => (), + } + } + 26 => { + rs1 = find_register_name(r_type.rs1).unwrap(); + match r_type.rs2 { + 0 => match r_type.funct7 & 0b11 { + 0 => { + string_version.push_str(&format!( + "{} {}, {}", + "fcvt.s.w", rd, rs1 + )); + } + 1 => { + string_version.push_str(&format!( + "{} {}, {}", + "fcvt.d.w", rd, rs1 + )); + } + _ => (), + }, + 1 => match r_type.funct7 & 0b11 { + 0 => { + string_version.push_str(&format!( + "{} {}, {}", + "fcvt.s.wu", rd, rs1 + )); + } + 1 => { + string_version.push_str(&format!( + "{} {}, {}", + "fcvt.d.wu", rd, rs1 + )); + } + _ => (), + }, + 2 => { + string_version + .push_str(&format!("{} {}, {}", "fcvt.s.l", rd, rs1)); + } + 3 => { + string_version + .push_str(&format!("{} {}, {}", "fcvt.s.lu", rd, rs1)); + } + _ => (), + } + } + 28 => { + rd = find_register_name(r_type.rd).unwrap(); + match r_type.funct3 { + 0 => { + string_version + .push_str(&format!("{} {}, {}", "fmv.x.w", rd, rs1)); + } + 1 => match r_type.funct7 & 0b11 { + 0 => { + string_version.push_str(&format!( + "{} {}, {}", + "fclass.s", rd, rs1 + )); + } + 1 => { + string_version.push_str(&format!( + "{} {}, {}", + "fclass.d", rd, rs1 + )); + } + _ => (), + }, + _ => (), + } + } + 30 => { + rs1 = find_register_name(r_type.rs1).unwrap(); + string_version.push_str(&format!("{} {}, {}", "fmv.w.x", rd, rs1)); + } + _ => (), + } + } + _ => (), + } + } + RiscInstruction::IType(i_type) => { + let rs1 = find_register_name(i_type.rs1).unwrap(); + let mut rd = find_register_name(i_type.rd).unwrap(); + let imm = i_type.imm as i32; + + // Check if immediate is negative + let mut str_imm = format!("{}", imm); + if imm & 0x800 != 0 { + str_imm = format!("-{}", (!(imm) + 1) & 0b00000000000000000000111111111111); + } + + match i_type.op { + OPCODE_IMM => match i_type.funct3 { + 0 => { + string_version + .push_str(&format!("{} {}, {}, {}", "addi", rd, rs1, str_imm)); + } + 1 => { + string_version.push_str(&format!( + "{} {}, {}, {}", + "slli", + rd, + rs1, + i_type.imm & 0x003f + )); + } + 2 => { + string_version.push_str(&format!( + "{} {}, {}, {}", + "slti", + rd, + rs1, + i_type.imm & 0x003f + )); + } + 3 => { + string_version.push_str(&format!( + "{} {}, {}, {}", + "sltiu", + rd, + rs1, + i_type.imm & 0x003f + )); + } + 4 => { + string_version + .push_str(&format!("{} {}, {}, {}", "xori", rd, rs1, i_type.imm)); + } + // match first 6 bits of imm + 5 => match i_type.imm >> 6 { + 0b000000 => { + string_version.push_str(&format!( + "{} {}, {}, {}", + "srli", + rd, + rs1, + i_type.imm & 0x003f + )); + } + 0b010000 => { + string_version.push_str(&format!( + "{} {}, {}, {}", + "srai", + rd, + rs1, + i_type.imm & 0x003f + )); + } + + _ => (), + }, + 6 => { + string_version + .push_str(&format!("{} {}, {}, {}", "ori", rd, rs1, i_type.imm)); + } + 7 => { + string_version + .push_str(&format!("{} {}, {}, {}", "andi", rd, rs1, str_imm)); + } + _ => (), + }, + OPCODE_IMM_32 => { + match i_type.funct3 { + 0 => { + string_version + .push_str(&format!("{} {}, {}, {}", "addiw", rd, rs1, str_imm)); + } + 1 => { + // shift lower 5 bits of imm + string_version.push_str(&format!( + "{} {}, {}, {}", + "slliw", + rd, + rs1, + i_type.imm & 0x001f + )); + } + 5 => { + // match first 5 bits of imm + let imm = i_type.imm >> 5; + match imm { + 0b000000 => { + string_version.push_str(&format!( + "{} {}, {}, {}", + "srliw", + rd, + rs1, + i_type.imm & 0x001f + )); + } + 0b010000 => { + string_version.push_str(&format!( + "{} {}, {}, {}", + "sraiw", + rd, + rs1, + i_type.imm & 0x001f + )); + } + _ => (), + } + } + _ => (), + } + } + OPCODE_JALR => { + string_version + .push_str(&format!("{} {}, {}, {}", "jalr", rd, rs1, i_type.imm)); + } + OPCODE_LOAD => match i_type.funct3 { + 0 => { + string_version.push_str(&format!("{} {}, {}({})", "lb", rd, imm, rs1)); + } + 1 => { + string_version.push_str(&format!("{} {}, {}({})", "lh", rd, imm, rs1)); + } + 2 => { + string_version.push_str(&format!("{} {}, {}({})", "lw", rd, imm, rs1)); + } + 3 => { + string_version.push_str(&format!("{} {}, {}({})", "ld", rd, imm, rs1)); + } + 4 => { + string_version.push_str(&format!("{} {}, {}({})", "lbu", rd, imm, rs1)); + } + 5 => { + string_version.push_str(&format!("{} {}, {}({})", "lhu", rd, imm, rs1)); + } + 6 => { + string_version.push_str(&format!("{} {}, {}({})", "lwu", rd, imm, rs1)); + } + _ => (), + }, + OPCODE_LOAD_FP => { + rd = find_register_name_fp(i_type.rd).unwrap(); + match i_type.funct3 { + 2 => { + string_version + .push_str(&format!("{} {}, {}({})", "flw", rd, imm, rs1)); + } + 3 => { + string_version + .push_str(&format!("{} {}, {}({})", "fld", rd, imm, rs1)); + } + _ => (), + } + } + OPCODE_SYSTEM => match i_type.funct3 { + 0 => match i_type.imm { + 0 => { + string_version.push_str("ecall"); + } + 1 => { + string_version.push_str("ebreak"); + } + 2 => { + string_version.push_str("uret"); + } + 0b000100000010 => { + string_version.push_str("sret"); + } + 0b001100000010 => { + string_version.push_str("mret"); + } + 0b000100000101 => { + string_version.push_str("wfi"); + } + _ => (), + }, + 2 => { + string_version + .push_str(&format!("{} {}, {}, {}", "csrrw", rd, rs1, i_type.imm)); + } + 3 => { + string_version + .push_str(&format!("{} {}, {}, {}", "csrrs", rd, rs1, i_type.imm)); + } + 4 => { + string_version + .push_str(&format!("{} {}, {}, {}", "csrrc", rd, rs1, i_type.imm)); + } + 5 => { + string_version + .push_str(&format!("{} {}, {}, {}", "csrrwi", rd, rs1, i_type.imm)); + } + 6 => { + string_version + .push_str(&format!("{} {}, {}, {}", "csrrsi", rd, rs1, i_type.imm)); + } + 7 => { + string_version + .push_str(&format!("{} {}, {}, {}", "csrrci", rd, rs1, i_type.imm)); + } + _ => (), + }, + _ => (), + } + } + RiscInstruction::SType(s_type) => { + let rs1 = find_register_name(s_type.rs1).unwrap(); + let mut rs2 = find_register_name(s_type.rs2).unwrap(); + let offset = (s_type.imm1 as i32) << 5 | s_type.imm2 as i32; + match s_type.op { + OPCODE_STORE => match s_type.funct3 { + 0 => { + string_version + .push_str(&format!("{} {}, {}({})", "sb", rs2, offset, rs1)); + } + 1 => { + string_version + .push_str(&format!("{} {}, {}({})", "sh", rs2, offset, rs1)); + } + 2 => { + string_version + .push_str(&format!("{} {}, {}({})", "sw", rs2, offset, rs1)); + } + 3 => { + string_version + .push_str(&format!("{} {}, {}({})", "sd", rs2, offset, rs1)); + } + _ => (), + }, + OPCODE_STORE_FP => { + rs2 = find_register_name_fp(s_type.rs2).unwrap(); + match s_type.funct3 { + 2 => { + string_version + .push_str(&format!("{} {}, {}({})", "fsw", rs2, offset, rs1)); + } + 3 => { + string_version + .push_str(&format!("{} {}, {}({})", "fsd", rs2, offset, rs1)); + } + _ => (), + } + } + _ => (), + } + } + RiscInstruction::BType(b_type) => { + let rs1 = find_register_name(b_type.rs1).unwrap(); + let rs2 = find_register_name(b_type.rs2).unwrap(); + if b_type.op == OPCODE_BRANCH { + let mut str_label = format!("0x{:x}", b_type.imm); + + for label in labels { + if label.1 == (b_type.imm as usize) * 4 { + str_label = label.0; + } + } + match b_type.funct3 { + 0 => { + string_version + .push_str(&format!("{} {}, {}, {}", "beq", rs1, rs2, str_label)); + } + 1 => { + string_version + .push_str(&format!("{} {}, {}, {}", "bne", rs1, rs2, str_label)); + } + 4 => { + string_version + .push_str(&format!("{} {}, {}, {}", "blt", rs1, rs2, str_label)); + } + 5 => { + string_version + .push_str(&format!("{} {}, {}, {}", "bge", rs1, rs2, str_label)); + } + 6 => { + string_version + .push_str(&format!("{} {}, {}, {}", "bltu", rs1, rs2, str_label)); + } + 7 => { + string_version + .push_str(&format!("{} {}, {}, {}", "bgeu", rs1, rs2, str_label)); + } + _ => (), + } + } + } + RiscInstruction::UType(u_type) => { + let rd = find_register_name(u_type.rd).unwrap(); + match u_type.op { + OPCODE_AUIPC => { + string_version.push_str(&format!("{} {}, {}", "auipc", rd, u_type.imm)); + } + OPCODE_LUI => { + string_version.push_str(&format!("{} {}, {}", "lui", rd, u_type.imm)); + } + _ => (), + } + } + RiscInstruction::JType(j_type) => { + // TODO get current address + let rd = find_register_name(j_type.rd).unwrap(); + let mut str_label = format!("0x{:x}", j_type.imm.wrapping_shl(2)); + for label in labels { + if label.1 == (j_type.imm as usize) * 4 { + str_label = label.0; + } + } + if j_type.op == OPCODE_JAL { + string_version.push_str(&format!("{} {}, {}", "jal", rd, str_label)); + } + } + RiscInstruction::R4Type(r4_type) => { + let rs1 = find_register_name_fp(r4_type.rs1).unwrap(); + let rs2 = find_register_name_fp(r4_type.rs2).unwrap(); + let rs3 = find_register_name_fp(r4_type.rs3).unwrap(); + let rd = find_register_name_fp(r4_type.rd).unwrap(); + match r4_type.op { + OPCODE_MADD => match r4_type.funct2 { + 0 => { + string_version.push_str(&format!( + "{} {}, {}, {}, {}", + "fmadd.s", rd, rs1, rs2, rs3 + )); + } + 1 => { + string_version.push_str(&format!( + "{} {}, {}, {}, {}", + "fmadd.d", rd, rs1, rs2, rs3 + )); + } + _ => (), + }, + OPCODE_MSUB => match r4_type.funct2 { + 0 => { + string_version.push_str(&format!( + "{} {}, {}, {}, {}", + "fmsub.s", rd, rs1, rs2, rs3 + )); + } + 1 => { + string_version.push_str(&format!( + "{} {}, {}, {}, {}", + "fmsub.d", rd, rs1, rs2, rs3 + )); + } + _ => (), + }, + OPCODE_NMSUB => match r4_type.funct2 { + 0 => { + string_version.push_str(&format!( + "{} {}, {}, {}, {}", + "fnmsub.s", rd, rs1, rs2, rs3 + )); + } + 1 => { + string_version.push_str(&format!( + "{} {}, {}, {}, {}", + "fnmsub.d", rd, rs1, rs2, rs3 + )); + } + _ => (), + }, + OPCODE_NMADD => match r4_type.funct2 { + 0 => { + string_version.push_str(&format!( + "{} {}, {}, {}, {}", + "fnmadd.s", rd, rs1, rs2, rs3 + )); + } + 1 => { + string_version.push_str(&format!( + "{} {}, {}, {}, {}", + "fnmadd.d", rd, rs1, rs2, rs3 + )); + } + _ => (), + }, + _ => (), + } + } + } + + if string_version.is_empty() { + Err(format!("instruction `{}` not supported", value)) + } else { + Ok(string_version) + } + } +} + +pub fn find_register_name(binary: u8) -> Option<&'static str> { + for register in RISCV_GP_REGISTERS { + if register.binary == binary { + // If a match is found, return the first name in the names array + return Some(register.names[0]); + } + } + // If no match is found, return None + None +} + +pub fn find_register_name_fp(binary: u8) -> Option<&'static str> { + for register in RISCV_FP_REGISTERS { + if register.binary == binary { + // If a match is found, return the first name in the names array + return Some(register.names[0]); + } + } + // If no match is found, return None + None +} diff --git a/src/emulation_core/riscv/registers.rs b/src/emulation_core/riscv/registers.rs new file mode 100644 index 000000000..aeb28bcc7 --- /dev/null +++ b/src/emulation_core/riscv/registers.rs @@ -0,0 +1,422 @@ +//! Register structure and API. + +use crate::emulation_core::mips::memory::CAPACITY_BYTES; +use crate::emulation_core::register::{RegisterType, Registers}; +use serde::{Deserialize, Serialize}; +use std::ops::{Index, IndexMut}; +use std::rc::Rc; +use std::str::FromStr; +use strum::IntoEnumIterator; +use strum_macros::{Display, EnumIter, EnumString}; + +/// Collection of general-purpose registers used by the datapath. +#[derive(Clone, Copy, Debug, Default, PartialEq, Serialize, Deserialize)] +pub struct RiscGpRegisters { + pub pc: u64, + pub gpr: [u64; 32], +} + +/// Specifies all of the valid registers accessible in an instance +/// of [`RiscGpRegisters`]. +#[derive(Clone, Copy, Debug, Display, EnumIter, EnumString, Eq, PartialEq)] +#[strum(ascii_case_insensitive)] +#[strum(serialize_all = "lowercase")] +pub enum RiscGpRegisterType { + Pc = -1, + X0 = 0, + X1 = 1, + X2 = 2, + X3 = 3, + X4 = 4, + X5 = 5, + X6 = 6, + X7 = 7, + X8 = 8, + X9 = 9, + X10 = 10, + X11 = 11, + X12 = 12, + X13 = 13, + X14 = 14, + X15 = 15, + X16 = 16, + X17 = 17, + X18 = 18, + X19 = 19, + X20 = 20, + X21 = 21, + X22 = 22, + X23 = 23, + X24 = 24, + X25 = 25, + X26 = 26, + X27 = 27, + X28 = 28, + X29 = 29, + X30 = 30, + X31 = 31, +} + +impl RegisterType for RiscGpRegisterType { + fn get_register_name(&self) -> String { + match self { + RiscGpRegisterType::Pc => self.to_string(), + _ => format!("{}", self), + } + } + fn is_valid_register_value(&self, value: u64, pc_limit: usize) -> bool { + match self { + RiscGpRegisterType::X0 => false, // Zero register is immutable + RiscGpRegisterType::Pc => { + // Check if PC is more than the number of instructions or not word-aligned + value <= pc_limit as u64 && value % 4 == 0 + } + RiscGpRegisterType::X2 => { + // Check if SP is more than memory capacity or not word-aligned + value <= CAPACITY_BYTES as u64 && value % 4 == 0 + } + _ => true, // Other registers are always considered valid + } + } +} + +impl Registers for RiscGpRegisters { + fn get_dyn_register_list(&self) -> Vec<(Rc, u64)> { + self.into_iter() + .map(|(register, val)| { + let register: Rc = Rc::new(register); + (register, val) + }) + .collect() + } +} + +impl ToString for RiscGpRegisters { + fn to_string(&self) -> String { + let mut output = String::new(); + + output.push_str(&format!("PC = {}\n", self.pc)); + + let gpr_registers = self + .gpr + .iter() + .enumerate() + .map(|(i, inst)| format!("gpr[{i}] = {inst}")) + .collect::>() + .join("\n"); + output.push_str(&gpr_registers); + + output + } +} + +impl Index<&str> for RiscGpRegisters { + type Output = u64; + + // Convert string to the corresponding RegistersEnum value and use this to index. + // If this is an invalid string, no enum will be returned, causing a panic as desired. + fn index(&self, index: &str) -> &Self::Output { + match RiscGpRegisterType::from_str(index) { + Ok(register) => &self[register], + _ => panic!("{index} is not a valid register"), + } + } +} + +impl IndexMut<&str> for RiscGpRegisters { + // Convert string to the corresponding RegistersEnum value and use this to index. + // If this is an invalid string, no enum will be returned, causing a panic as desired. + fn index_mut(&mut self, index: &str) -> &mut Self::Output { + match RiscGpRegisterType::from_str(index) { + Ok(register) => &mut self[register], + _ => panic!("{index} is not a valid register"), + } + } +} + +impl Index for RiscGpRegisters { + type Output = u64; + + fn index(&self, index: RiscGpRegisterType) -> &Self::Output { + match index { + RiscGpRegisterType::Pc => &self.pc, + RiscGpRegisterType::X0 => &self.gpr[0], + RiscGpRegisterType::X1 => &self.gpr[1], + RiscGpRegisterType::X2 => &self.gpr[2], + RiscGpRegisterType::X3 => &self.gpr[3], + RiscGpRegisterType::X4 => &self.gpr[4], + RiscGpRegisterType::X5 => &self.gpr[5], + RiscGpRegisterType::X6 => &self.gpr[6], + RiscGpRegisterType::X7 => &self.gpr[7], + RiscGpRegisterType::X8 => &self.gpr[8], + RiscGpRegisterType::X9 => &self.gpr[9], + RiscGpRegisterType::X10 => &self.gpr[10], + RiscGpRegisterType::X11 => &self.gpr[11], + RiscGpRegisterType::X12 => &self.gpr[12], + RiscGpRegisterType::X13 => &self.gpr[13], + RiscGpRegisterType::X14 => &self.gpr[14], + RiscGpRegisterType::X15 => &self.gpr[15], + RiscGpRegisterType::X16 => &self.gpr[16], + RiscGpRegisterType::X17 => &self.gpr[17], + RiscGpRegisterType::X18 => &self.gpr[18], + RiscGpRegisterType::X19 => &self.gpr[19], + RiscGpRegisterType::X20 => &self.gpr[20], + RiscGpRegisterType::X21 => &self.gpr[21], + RiscGpRegisterType::X22 => &self.gpr[22], + RiscGpRegisterType::X23 => &self.gpr[23], + RiscGpRegisterType::X24 => &self.gpr[24], + RiscGpRegisterType::X25 => &self.gpr[25], + RiscGpRegisterType::X26 => &self.gpr[26], + RiscGpRegisterType::X27 => &self.gpr[27], + RiscGpRegisterType::X28 => &self.gpr[28], + RiscGpRegisterType::X29 => &self.gpr[29], + RiscGpRegisterType::X30 => &self.gpr[30], + RiscGpRegisterType::X31 => &self.gpr[31], + } + } +} + +impl IndexMut for RiscGpRegisters { + fn index_mut(&mut self, index: RiscGpRegisterType) -> &mut Self::Output { + match index { + RiscGpRegisterType::Pc => &mut self.pc, + RiscGpRegisterType::X0 => panic!("The $zero register cannot be accessed as mutable"), + RiscGpRegisterType::X1 => &mut self.gpr[1], + RiscGpRegisterType::X2 => &mut self.gpr[2], + RiscGpRegisterType::X3 => &mut self.gpr[3], + RiscGpRegisterType::X4 => &mut self.gpr[4], + RiscGpRegisterType::X5 => &mut self.gpr[5], + RiscGpRegisterType::X6 => &mut self.gpr[6], + RiscGpRegisterType::X7 => &mut self.gpr[7], + RiscGpRegisterType::X8 => &mut self.gpr[8], + RiscGpRegisterType::X9 => &mut self.gpr[9], + RiscGpRegisterType::X10 => &mut self.gpr[10], + RiscGpRegisterType::X11 => &mut self.gpr[11], + RiscGpRegisterType::X12 => &mut self.gpr[12], + RiscGpRegisterType::X13 => &mut self.gpr[13], + RiscGpRegisterType::X14 => &mut self.gpr[14], + RiscGpRegisterType::X15 => &mut self.gpr[15], + RiscGpRegisterType::X16 => &mut self.gpr[16], + RiscGpRegisterType::X17 => &mut self.gpr[17], + RiscGpRegisterType::X18 => &mut self.gpr[18], + RiscGpRegisterType::X19 => &mut self.gpr[19], + RiscGpRegisterType::X20 => &mut self.gpr[20], + RiscGpRegisterType::X21 => &mut self.gpr[21], + RiscGpRegisterType::X22 => &mut self.gpr[22], + RiscGpRegisterType::X23 => &mut self.gpr[23], + RiscGpRegisterType::X24 => &mut self.gpr[24], + RiscGpRegisterType::X25 => &mut self.gpr[25], + RiscGpRegisterType::X26 => &mut self.gpr[26], + RiscGpRegisterType::X27 => &mut self.gpr[27], + RiscGpRegisterType::X28 => &mut self.gpr[28], + RiscGpRegisterType::X29 => &mut self.gpr[29], + RiscGpRegisterType::X30 => &mut self.gpr[30], + RiscGpRegisterType::X31 => &mut self.gpr[31], + } + } +} + +/// Iterator that is used to view each register in the register file. +/// +/// This contains a copy of all the registers and their values, and a [`GpRegisterTypeIter`], +/// as generated by [`strum::IntoEnumIterator`]. In other iterator implementations, +/// the internal state might be data like a [`RiscGpRegisterType`]. However, since we can't +/// normally just "add 1" to get to the next register, we use an internal iterator +/// that can track the progression of one [`RiscGpRegisterType`] to the next. +pub struct GpRegistersIter { + registers: RiscGpRegisters, + register_iter: RiscGpRegisterTypeIter, +} + +/// This implementation of the [`Iterator`] trait essentially wraps the existing +/// [`GpRegisterTypeIter`] so that the register type can be paired with register data. +impl Iterator for GpRegistersIter { + type Item = (RiscGpRegisterType, u64); + + fn next(&mut self) -> Option { + match self.register_iter.next() { + Some(register_type) => Some((register_type, self.registers[register_type])), + None => None, + } + } +} + +/// [`IntoIterator`] is a standard library trait that can convert any type into +/// an [`Iterator`]. In this case, this is an instance of [`GpRegistersIter`] with all the +/// data in the registers and a new [`GpRegisterTypeIter`]. +impl IntoIterator for RiscGpRegisters { + type Item = (RiscGpRegisterType, u64); + type IntoIter = GpRegistersIter; + + /// Consumes the [`RiscGpRegisters`] struct to create a new [`GpRegistersIter`] that can + /// be iterated over. + fn into_iter(self) -> Self::IntoIter { + GpRegistersIter { + registers: self, + register_iter: RiscGpRegisterType::iter(), + } + } +} + +/// Collection of general-purpose registers used by the datapath. +#[derive(Clone, Copy, Debug, Default, PartialEq, Serialize, Deserialize)] +pub struct RiscFpRegisters { + pub fpr: [u64; 32], +} + +/// Specifies all of the valid registers accessible in an instance +/// of [`RiscFpRegisters`]. +#[derive(Clone, Copy, Debug, Display, EnumIter, EnumString, Eq, PartialEq)] +#[strum(ascii_case_insensitive)] +#[strum(serialize_all = "lowercase")] +pub enum FpRegisterType { + F0 = 0, + F1 = 1, + F2 = 2, + F3 = 3, + F4 = 4, + F5 = 5, + F6 = 6, + F7 = 7, + F8 = 8, + F9 = 9, + F10 = 10, + F11 = 11, + F12 = 12, + F13 = 13, + F14 = 14, + F15 = 15, + F16 = 16, + F17 = 17, + F18 = 18, + F19 = 19, + F20 = 20, + F21 = 21, + F22 = 22, + F23 = 23, + F24 = 24, + F25 = 25, + F26 = 26, + F27 = 27, + F28 = 28, + F29 = 29, + F30 = 30, + F31 = 31, +} + +impl RegisterType for FpRegisterType { + fn get_register_name(&self) -> String { + format!("f{}", *self as u32) + } + fn is_valid_register_value(&self, _value: u64, _pc_limit: usize) -> bool { + true + } +} + +impl Registers for RiscFpRegisters { + fn get_dyn_register_list(&self) -> Vec<(Rc, u64)> { + self.into_iter() + .map(|(register, val)| { + let register: Rc = Rc::new(register); + (register, val) + }) + .collect() + } +} + +impl ToString for RiscFpRegisters { + fn to_string(&self) -> String { + let mut output = String::new(); + + let fpr_registers = self + .fpr + .iter() + .enumerate() + .map(|(i, inst)| format!("fpr[{i}] = {inst}")) + .collect::>() + .join("\n"); + output.push_str(&fpr_registers); + + output + } +} + +impl Index<&str> for RiscFpRegisters { + type Output = u64; + + // Convert string to the corresponding RegistersEnum value and use this to index. + // If this is an invalid string, no enum will be returned, causing a panic as desired. + fn index(&self, index: &str) -> &Self::Output { + match FpRegisterType::from_str(index) { + Ok(register) => &self[register], + _ => panic!("{index} is not a valid register"), + } + } +} + +impl IndexMut<&str> for RiscFpRegisters { + // Convert string to the corresponding RegistersEnum value and use this to index. + // If this is an invalid string, no enum will be returned, causing a panic as desired. + fn index_mut(&mut self, index: &str) -> &mut Self::Output { + match FpRegisterType::from_str(index) { + Ok(register) => &mut self[register], + _ => panic!("{index} is not a valid register"), + } + } +} + +impl Index for RiscFpRegisters { + type Output = u64; + + fn index(&self, index: FpRegisterType) -> &Self::Output { + &self.fpr[index as usize] + } +} + +impl IndexMut for RiscFpRegisters { + fn index_mut(&mut self, index: FpRegisterType) -> &mut Self::Output { + &mut self.fpr[index as usize] + } +} + +/// Iterator that is used to view each register in the register file. +/// +/// This contains a copy of all the registers and their values, and a [`FpRegisterTypeIter`], +/// as generated by [`strum::IntoEnumIterator`]. In other iterator implementations, +/// the internal state might be data like a [`FpRegisterType`]. However, since we can't +/// normally just "add 1" to get to the next register, we use an internal iterator +/// that can track the progression of one [`FpRegisterType`] to the next. +pub struct FpRegistersIter { + registers: RiscFpRegisters, + register_iter: FpRegisterTypeIter, +} + +/// This implementation of the [`Iterator`] trait essentially wraps the existing +/// [`FpRegisterTypeIter`] so that the register type can be paired with register data. +impl Iterator for FpRegistersIter { + type Item = (FpRegisterType, u64); + + fn next(&mut self) -> Option { + match self.register_iter.next() { + Some(register_type) => Some((register_type, self.registers[register_type])), + None => None, + } + } +} + +/// [`IntoIterator`] is a standard library trait that can convert any type into +/// an [`Iterator`]. In this case, this is an instance of [`FpRegistersIter`] with all the +/// data in the registers and a new [`FpRegisterTypeIter`]. +impl IntoIterator for RiscFpRegisters { + type Item = (FpRegisterType, u64); + type IntoIter = FpRegistersIter; + + /// Consumes the [`RiscFpRegisters`] struct to create a new [`FpRegistersIter`] that can + /// be iterated over. + fn into_iter(self) -> Self::IntoIter { + FpRegistersIter { + registers: self, + register_iter: FpRegisterType::iter(), + } + } +} diff --git a/src/emulation_core/stack.rs b/src/emulation_core/stack.rs new file mode 100644 index 000000000..34141dd7d --- /dev/null +++ b/src/emulation_core/stack.rs @@ -0,0 +1,76 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct StackFrame { + pub call_instruction: u32, + pub call_address: u32, + pub return_address: u64, + pub frame_pointer: u64, + pub jump_address: u64, +} + +impl StackFrame { + pub fn new( + call_instruction: u32, + call_address: u64, + return_address: u64, + frame_pointer: u64, + jump_address: u64, + ) -> Self { + Self { + call_instruction, + call_address: call_address as u32, + return_address, + frame_pointer, + jump_address, + } + } +} +#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)] +pub struct Stack { + pub stack: Vec, +} + +impl Stack { + pub fn push(&mut self, frame: StackFrame) { + self.stack.push(frame); + } + + pub fn pop(&mut self) -> Option { + self.stack.pop() + } + + pub fn peek(&self) -> Option<&StackFrame> { + self.stack.last() + } + + pub fn is_empty(&self) -> bool { + self.stack.is_empty() + } +} + +pub struct StackIter<'a> { + stack: &'a Stack, + current_address: u32, +} + +impl<'a> StackIter<'a> { + pub fn new(stack: &'a Stack, current_address: u32) -> StackIter<'a> { + StackIter { + stack, + current_address, + } + } +} + +impl<'a> Iterator for StackIter<'a> { + type Item = &'a StackFrame; + fn next(&mut self) -> Option { + self.current_address = (self.current_address + 3) & !3; + if self.current_address < self.stack.stack.len() as u32 { + Some(&self.stack.stack[self.current_address as usize]) + } else { + None + } + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 000000000..825d617f2 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,6 @@ +pub mod agent; +pub mod emulation_core; +pub mod parser; +#[cfg(test)] +pub mod tests; +pub mod ui; diff --git a/src/main.rs b/src/main.rs deleted file mode 100644 index a59835a88..000000000 --- a/src/main.rs +++ /dev/null @@ -1,542 +0,0 @@ -pub mod emulation_core; -pub mod parser; -#[cfg(test)] -pub mod tests; -pub mod ui; - -use emulation_core::datapath::Datapath; -use emulation_core::mips::datapath::MipsDatapath; -use emulation_core::mips::datapath::Stage; -use gloo::{dialogs::alert, file::FileList}; -use js_sys::Object; -use monaco::{ - api::TextModel, - sys::{ - editor::{ - IEditorMinimapOptions, IEditorScrollbarOptions, IMarkerData, IModelDecorationOptions, - IModelDeltaDecoration, IStandaloneEditorConstructionOptions, ISuggestOptions, - }, - IMarkdownString, MarkerSeverity, - }, - yew::CodeEditor, -}; -use parser::parser_assembler_main::parser; -use std::rc::Rc; -use ui::console::component::Console; -use ui::regview::component::Regview; -use wasm_bindgen::{JsCast, JsValue}; -use wasm_bindgen_futures::spawn_local; -use web_sys::HtmlInputElement; -use yew::prelude::*; -use yew::{html, Html, Properties}; -use yew_hooks::prelude::*; - -// To load in the Fibonacci example, uncomment the CONTENT and fib_model lines -// and comment the code, language, and text_model lines. IMPORTANT: -// rename fib_model to text_model to have it work. -const CONTENT: &str = include_str!("../static/assembly_examples/fibonacci.asm"); - -#[function_component(App)] -fn app() -> Html { - // This contains the binary representation of "ori $s0, $zero, 12345", which - // stores 12345 in register $s0. - // let code = String::from("ori $s0, $zero, 12345\n"); - // let language = String::from("mips"); - - // This is the initial text model with default text contents. The - // use_state_eq hook is created so that the component can be updated - // when the text model changes. - //let text_model = use_mut_ref(|| TextModel::create(&code, Some(&language), None).unwrap()); - let text_model = use_mut_ref(|| TextModel::create(CONTENT, Some("mips"), None).unwrap()); - - // Setup the array that would store decorations applied to the - // text model and initialize the options for it. - let hover_jsarray = js_sys::Array::new(); - let hover_decor_array = use_mut_ref(js_sys::Array::new); - - // Setup the highlight stacks that would store which line - // was executed after the execute button is pressed. - let executed_line = js_sys::Array::new(); - let not_highlighted = js_sys::Array::new(); - - // Setting up the options/parameters which - // will highlight the previously executed line. - // The highlight decor does not need to be changed, - // the only parameter that will change is the range. - let highlight_decor = use_mut_ref(monaco::sys::editor::IModelDecorationOptions::default); - (*highlight_decor) - .borrow_mut() - .set_is_whole_line(true.into()); - (*highlight_decor) - .borrow_mut() - .set_inline_class_name("myInlineDecoration".into()); - - // Output strings for the console and memory viewers. - let parser_text_output = use_state_eq(String::new); - let memory_text_output = use_state_eq(String::new); - - // Since we want the Datapath to be independent from all the - // events within the app, we will create it when the app loads. This is also done - // since the scope will be open across all events involved with it. To achieve this, - // we use interior mutability to have the reference to the Datapath immutable, but - // the ability to access and change its contents be mutable. - let datapath = use_mut_ref(MipsDatapath::default); - - // This is where code is assembled and loaded into the emulation core's memory. - let on_assemble_clicked = { - let text_model = Rc::clone(&text_model); - let datapath = Rc::clone(&datapath); - let parser_text_output = parser_text_output.clone(); - let trigger = use_force_update(); - - let executed_line = executed_line.clone(); - let not_highlighted = not_highlighted.clone(); - - use_callback( - move |_, text_model| { - let mut datapath = datapath.borrow_mut(); - let text_model = text_model.borrow_mut(); - - // parses through the code to assemble the binary and retrieves programinfo for error marking and mouse hover - let (program_info, assembled) = parser(text_model.get_value()); - parser_text_output.set(program_info.console_out_post_assembly); - - let mut markers: Vec = vec![]; - - // Parse output from parser and create an instance of IMarkerData for each error. - for (line_number, line_information) in - program_info.monaco_line_info.iter().enumerate() - { - for error in &line_information.errors { - let new_marker: IMarkerData = new_object().into(); - new_marker.set_message(&error.message); - new_marker.set_severity(MarkerSeverity::Error); - new_marker.set_start_line_number((line_number + 1) as f64); - new_marker.set_start_column((error.start_end_columns.0 + 1) as f64); - new_marker.set_end_line_number((line_number + 1) as f64); - new_marker.set_end_column((error.start_end_columns.1 + 1) as f64); - markers.push(new_marker); - } - } - - // Convert Vec to Javascript array - let marker_jsarray = js_sys::Array::new(); - for marker in markers { - marker_jsarray.push(&marker); - } - - monaco::sys::editor::set_model_markers( - text_model.as_ref(), - "owner", - &marker_jsarray, - ); - // Acts like reset and clears the highlight - let curr_model = text_model.as_ref(); - executed_line.pop(); - not_highlighted.set( - 0, - curr_model - .delta_decorations(¬_highlighted, &executed_line, None) - .into(), - ); - - // Proceed with loading into memory and expand pseudo-instructions if there are no errors. - if marker_jsarray.length() == 0 { - // Load the binary into the datapath's memory - match datapath.initialize(assembled) { - Ok(_) => (), - Err(msg) => { - // In the case of an error, note this and stop early. - parser_text_output.set(format!("This program failed to load into the datapath. Message returned by datapath: {msg}")); - } - } - // log!(datapath.memory.to_string()); - text_model.set_value(&program_info.updated_monaco_string); // Expands pseudo-instructions to their hardware counterpart. - datapath.registers.pc = program_info.pc_starting_point as u64; - } - - trigger.force_update(); - }, - text_model, - ) - }; - - // This is where the code will get executed. If you execute further - // than when the code ends, the program crashes. This is remedied via the - // syscall instruction, which will halt the datapath. As you execute the - // code, the previously executed line is highlighted. - let on_execute_clicked = { - let text_model = Rc::clone(&text_model); - let datapath = Rc::clone(&datapath); - let trigger = use_force_update(); - - let executed_line = executed_line.clone(); - let not_highlighted = not_highlighted.clone(); - let highlight_decor = highlight_decor.clone(); - - use_callback( - move |_, _| { - let mut datapath = datapath.borrow_mut(); - let text_model = text_model.borrow_mut(); - let highlight_decor = highlight_decor.borrow_mut(); - - // Pull ProgramInfo from the parser - let (programinfo, _) = parser(text_model.get_value()); - - // Get the current line and convert it to f64 - let list_of_line_numbers = programinfo.address_to_line_number; - let index = datapath.registers.pc as usize / 4; - let curr_line = *list_of_line_numbers.get(index).unwrap_or(&0) as f64 + 1.0; // add one to account for the editor's line numbers - - // Setup the range - let curr_model = text_model.as_ref(); - let curr_range = monaco::sys::Range::new(curr_line, 0.0, curr_line, 0.0); - - // element to be stored in the stack to highlight the line - let highlight_line: monaco::sys::editor::IModelDeltaDecoration = - Object::new().unchecked_into(); - highlight_line.set_options(&highlight_decor); - let range_js = curr_range - .dyn_into::() - .expect("Range is not found."); - highlight_line.set_range(&monaco::sys::IRange::from(range_js)); - let highlight_js = highlight_line - .dyn_into::() - .expect("Highlight is not found."); - - // log!("These are the stacks before the push"); - // log!(executed_line.at(0)); - // log!(not_highlighted.at(0)); - - // push the decoration onto the executed_line stack - executed_line.push(&highlight_js); - - // it may look ugly, but it makes sense. Uncomment debug statements to see why. - not_highlighted.set( - 0, - curr_model - .delta_decorations(¬_highlighted, &executed_line, None) - .into(), - ); - - // log!("These are the stacks after the push"); - // log!(executed_line.at(0)); - // log!(not_highlighted.at(0)); - - datapath.execute_instruction(); - - // done with the highlight, prepare for the next one. - executed_line.pop(); - - // log!("These are the stacks after the pop"); - // log!(executed_line.at(0)); - // log!(not_highlighted.at(0)); - - trigger.force_update(); - }, - (), - ) - }; - - let on_execute_stage_clicked = { - let datapath = Rc::clone(&datapath); - let text_model = Rc::clone(&text_model); - let executed_line = executed_line.clone(); - let not_highlighted = not_highlighted.clone(); - let highlight_decor = highlight_decor; - let trigger = use_force_update(); - - use_callback( - move |_, _| { - let mut datapath = datapath.borrow_mut(); - let highlight_decor = highlight_decor.borrow_mut(); - if datapath.current_stage == Stage::InstructionDecode { - // highlight on InstructionDecode since syscall stops at that stage. - let text_model = text_model.borrow_mut(); - let (programinfo, _) = parser(text_model.get_value()); - let list_of_line_numbers = programinfo.address_to_line_number; - let index = datapath.registers.pc as usize / 4; - let curr_line = *list_of_line_numbers.get(index).unwrap_or(&0) as f64 + 1.0; - let curr_model = text_model.as_ref(); - let curr_range = monaco::sys::Range::new(curr_line, 0.0, curr_line, 0.0); - let highlight_line: monaco::sys::editor::IModelDeltaDecoration = - Object::new().unchecked_into(); - highlight_line.set_options(&highlight_decor); - let range_js = curr_range - .dyn_into::() - .expect("Range is not found."); - highlight_line.set_range(&monaco::sys::IRange::from(range_js)); - let highlight_js = highlight_line - .dyn_into::() - .expect("Highlight is not found."); - executed_line.push(&highlight_js); - not_highlighted.set( - 0, - curr_model - .delta_decorations(¬_highlighted, &executed_line, None) - .into(), - ); - datapath.execute_stage(); - executed_line.pop(); - } else { - datapath.execute_stage(); - } - trigger.force_update(); - }, - (), - ) - }; - - // This is how we will reset the datapath. - // This will also clear any highlight on the editor. - let on_reset_clicked = { - let text_model = Rc::clone(&text_model); - let datapath = Rc::clone(&datapath); - let trigger = use_force_update(); - let parser_text_output = parser_text_output.clone(); - - let executed_line = executed_line; - let not_highlighted = not_highlighted; - - use_callback( - move |_, _| { - let mut datapath = datapath.borrow_mut(); - let text_model = text_model.borrow_mut(); - let curr_model = text_model.as_ref(); - executed_line.pop(); - not_highlighted.set( - 0, - curr_model - .delta_decorations(¬_highlighted, &executed_line, None) - .into(), - ); - parser_text_output.set("".to_string()); - datapath.reset(); - trigger.force_update(); - }, - (), - ) - }; - - // Copies text to the user's clipboard - let on_clipboard_clicked = { - let text_model = Rc::clone(&text_model); - let clipboard = use_clipboard(); - Callback::from(move |_: _| { - let text_model = text_model.borrow_mut(); - clipboard.write_text(text_model.get_value()); - alert("Your code is saved to the clipboard.\nPaste it onto a text file to save it.\n(Ctrl/Cmd + V)"); - }) - }; - - // We'll have the Mouse Hover event running at all times. - { - let text_model = Rc::clone(&text_model); - use_event_with_window("mouseover", move |_: MouseEvent| { - let hover_jsarray = hover_jsarray.clone(); - let hover_decor_array = hover_decor_array.clone(); - let text_model = text_model.borrow_mut(); - let curr_model = text_model.as_ref(); - let (program_info, _) = parser(text_model.get_value()); - - // Parse output from parser and create an instance of IModelDeltaDecoration for each line. - for (line_number, line_information) in program_info.monaco_line_info.iter().enumerate() - { - let decoration: IModelDeltaDecoration = new_object().into(); - - let hover_range = monaco::sys::Range::new( - (line_number + 1) as f64, - 0.0, - (line_number + 1) as f64, - 0.0, - ); - let hover_range_js = hover_range - .dyn_into::() - .expect("Range is not found."); - decoration.set_range(&monaco::sys::IRange::from(hover_range_js)); - - let hover_opts: IModelDecorationOptions = new_object().into(); - hover_opts.set_is_whole_line(true.into()); - let hover_message: IMarkdownString = new_object().into(); - js_sys::Reflect::set( - &hover_message, - &JsValue::from_str("value"), - &JsValue::from_str(&line_information.mouse_hover_string), - ) - .unwrap(); - hover_opts.set_hover_message(&hover_message); - decoration.set_options(&hover_opts); - let hover_js = decoration - .dyn_into::() - .expect("Hover is not found."); - hover_jsarray.push(&hover_js); - } - - // log!("This is the array after the push"); - // log!(hover_jsarray.clone()); - - // properly pass the handlers onto the array - let new_hover_decor_array = - curr_model.delta_decorations(&hover_decor_array.borrow_mut(), &hover_jsarray, None); - *hover_decor_array.borrow_mut() = new_hover_decor_array; - - // log!("These are the arrays after calling Delta Decorations"); - // log!(hover_jsarray.clone()); - // log!(hover_decor_array.borrow_mut().clone()); - - // empty out the array that hold the decorations - hover_jsarray.set_length(0); - - // log!("These are the arrays after calling popping the hover_jsarray"); - // log!(hover_jsarray.clone()); - // log!(hover_decor_array.borrow_mut().clone()); - }); - }; - - // This is where we will have the user prompted to load in a file - let upload_clicked_callback = use_callback( - move |e: MouseEvent, _| { - e.stop_propagation(); - on_upload_file_clicked(); - }, - (), - ); - - // This is the callback to get the file's contents and load it onto the Editor - let file_picked_callback = { - let text_model = Rc::clone(&text_model); - use_callback( - move |e: Event, _| { - let text_model = text_model.borrow_mut().clone(); - let input: HtmlInputElement = e.target_unchecked_into(); - // gloo making the code readable and easy to implement - let filelist = FileList::from(input.files().unwrap()); - let file = filelist.first().unwrap(); - let contents = gloo::file::futures::read_as_text(file); - spawn_local(async move { - let contents = contents.await; - - let contents = contents.expect("File contains invalid utf8"); // TODO: implement a file checker, will load in anything - - text_model.set_value(&contents); - }) - }, - (), - ) - }; - - html! { - <> - // button tied to the input file element, which is hidden to be more clean - -
- // Left column -
- // Top buttons -
-
- - - - - // - - // - -
-
- - // Editor -
- -
- - // Console - -
- - // Right column - -
- - } -} - -/// Creates a new `JsValue`. -fn new_object() -> JsValue { - js_sys::Object::new().into() -} - -/********************** Editor Component **********************/ - -#[derive(PartialEq, Properties)] -pub struct SwimEditorProps { - pub text_model: TextModel, -} - -fn get_options() -> IStandaloneEditorConstructionOptions { - let options = IStandaloneEditorConstructionOptions::default(); - options.set_theme("vs-dark".into()); - options.set_language("mips".into()); - options.set_scroll_beyond_last_line(false.into()); - options.set_automatic_layout(true.into()); - - let minimap = IEditorMinimapOptions::default(); - minimap.set_enabled(false.into()); - options.set_minimap(Some(&minimap)); - - let scrollbar = IEditorScrollbarOptions::default(); - scrollbar.set_always_consume_mouse_wheel(false.into()); - options.set_scrollbar(Some(&scrollbar)); - - let suggest = ISuggestOptions::default(); - suggest.set_show_keywords(false.into()); - suggest.set_show_variables(false.into()); - suggest.set_show_icons(false.into()); - suggest.set_show_words(false.into()); - suggest.set_filter_graceful(false.into()); - options.set_suggest(Some(&suggest)); - - options -} - -#[function_component] -pub fn SwimEditor(props: &SwimEditorProps) -> Html { - html! { - - } -} - -/********************** "Console" Component **********************/ -#[derive(PartialEq, Properties)] -pub struct Consoleprops { - pub parsermsg: String, -} - -/********************** File I/O Function ***********************/ -pub fn on_upload_file_clicked() { - // log!("Upload clicked!"); - - let window = web_sys::window().expect("should have a window in this context"); - let document = window.document().expect("window should have a document"); - - let file_input_elem = document - .get_element_by_id("file_input") - .expect("File input element with id \"file_input\" should exist."); - - let file_input_elem = file_input_elem - .dyn_into::() - .expect("Element should be an HtmlInputElement"); - - // log!("Before click"); - // workaround for https://github.com/yewstack/yew/pull/3037 since it's not in 0.20 - spawn_local(async move { - file_input_elem.click(); - }); - // log!("After click"); -} - -fn main() { - yew::Renderer::::new().render(); -} diff --git a/src/parser/assembling.rs b/src/parser/assembling.rs index 38f7d3c1d..0d2e32617 100644 --- a/src/parser/assembling.rs +++ b/src/parser/assembling.rs @@ -6,7 +6,8 @@ use crate::parser::parser_structs_and_enums::ErrorType::{ NonIntImmediate, UnrecognizedDataType, UnrecognizedFPRegister, UnrecognizedGPRegister, }; use crate::parser::parser_structs_and_enums::OperandType::{ - Immediate, LabelAbsolute, LabelRelative, MemoryAddress, RegisterFP, RegisterGP, + Immediate, LabelAbsolute, LabelRelative, MemoryAddress, RegisterFP, RegisterGP, ShiftAmount, + UpperImmediate, }; use crate::parser::parser_structs_and_enums::RegisterType::{FloatingPoint, GeneralPurpose}; use crate::parser::parser_structs_and_enums::TokenType::{ @@ -17,6 +18,8 @@ use crate::parser::parser_structs_and_enums::{ }; use std::collections::HashMap; +use super::parser_structs_and_enums::{RISCV_FP_REGISTERS, RISCV_GP_REGISTERS}; + ///This function takes an instruction whose operands it is supposed to read, the order of expected operand types and then ///the order these operands should be concatenated onto the binary representation of the string ///the function returns the instruction it was given with any errors and the binary of the operands added on. @@ -37,10 +40,9 @@ pub fn read_operands( return instruction; } - let labels = if let Some(..) = labels_option { - labels_option.unwrap() - } else { - HashMap::new() + let labels = match labels_option { + Some(labels_option) => labels_option, + None => HashMap::new(), }; //operands aren't represented in the binary in the order they're read so the vec allows us to concatenate them in the proper order after they're all read. @@ -87,6 +89,9 @@ pub fn read_operands( instruction.errors.push(immediate_results.1.unwrap()); } } + UpperImmediate => { + // Don't need to handle for MIPS + } MemoryAddress => { instruction.operands[i].token_type = TokenType::MemoryAddress; @@ -152,7 +157,7 @@ pub fn read_operands( instruction.errors.push(label_relative_results.1.unwrap()); } } - OperandType::ShiftAmount => { + ShiftAmount => { instruction.operands[i].token_type = TokenType::Immediate; bit_lengths.push(5); @@ -181,6 +186,194 @@ pub fn read_operands( instruction } +///This function takes an instruction whose operands it is supposed to read, the order of expected operand types and then +///the order these operands should be concatenated onto the binary representation of the string +///the function returns the instruction it was given with any errors and the binary of the operands added on. +pub fn read_operands_riscv( + instruction: &mut Instruction, + expected_operands: Vec, + concat_order: Vec, + labels_option: Option>, + funct3: Option, + fmt: Option, +) -> &mut Instruction { + //if the number of operands in the instruction does not match the expected number, there is an error + if instruction.operands.len() != expected_operands.len() { + instruction.errors.push(Error { + error_name: IncorrectNumberOfOperands, + token_causing_error: instruction.operator.token_name.clone(), + start_end_columns: instruction.operator.start_end_columns, + message: "".to_string(), + }); + return instruction; + } + + let labels = match labels_option { + Some(labels_option) => labels_option, + None => HashMap::new(), + }; + + //operands aren't represented in the binary in the order they're read so the vec allows us to concatenate them in the proper order after they're all read. + let mut binary_representation: Vec = Vec::new(); + let mut bit_lengths: Vec = Vec::new(); + //goes through once for each expected operand + for (i, operand_type) in expected_operands.iter().enumerate() { + //break if there are no more operands to read. Should only occur if IncorrectNumberOfOperands occurs above + if i >= instruction.operands.len() { + break; + }; + + //match case calls the proper functions based on the expected operand type. The data returned from these functions is always + //the binary of the read operand and the option for any errors encountered while reading the operand. If there were no errors, + //the binary is pushed to the string representations vec. Otherwise, the errors are pushed to the instruction.errors vec. + match operand_type { + RegisterGP => { + instruction.operands[i].token_type = TokenType::RegisterGP; + bit_lengths.push(5); + + let register_results = read_register_riscv( + &instruction.operands[i].token_name, + instruction.operands[i].start_end_columns, + GeneralPurpose, + ); + + // Vector holding all register arguments + binary_representation.push(register_results.0 as u32); + if register_results.1.is_some() { + instruction.errors.push(register_results.1.unwrap()); + } + } + Immediate => { + instruction.operands[i].token_type = TokenType::Immediate; + bit_lengths.push(12); // 12 bits to represent immediates + + let immediate_results = read_immediate( + &instruction.operands[i].token_name, + instruction.operands[i].start_end_columns, + 12, + ); + + binary_representation.push(immediate_results.0); + if immediate_results.1.is_some() { + instruction.errors.push(immediate_results.1.unwrap()); + } + } + UpperImmediate => { + instruction.operands[i].token_type = TokenType::Immediate; + bit_lengths.push(20); // 20 bits to represent upper immediates + + let immediate_results = read_immediate( + &instruction.operands[i].token_name, + instruction.operands[i].start_end_columns, + 20, + ); + + binary_representation.push(immediate_results.0); + if immediate_results.1.is_some() { + instruction.errors.push(immediate_results.1.unwrap()); + } + } + MemoryAddress => { + instruction.operands[i].token_type = TokenType::MemoryAddress; + + bit_lengths.push(12); + bit_lengths.push(5); + //memory address works a bit differently because it really amounts to two operands: the offset and base + //meaning there are two values to push and the possibility of errors on both operands + let memory_results = read_memory_address_riscv( + &instruction.operands[i].token_name, + instruction.operands[i].start_end_columns, + ); + + binary_representation.push(memory_results.0); + binary_representation.push(memory_results.1); + if memory_results.2.is_some() { + for error in memory_results.2.unwrap() { + instruction.errors.push(error); + } + } + } + RegisterFP => { + instruction.operands[i].token_type = TokenType::RegisterFP; + + bit_lengths.push(5); + let register_results = read_register_riscv( + &instruction.operands[i].token_name, + instruction.operands[i].start_end_columns, + FloatingPoint, + ); + + binary_representation.push(register_results.0 as u32); + if register_results.1.is_some() { + instruction.errors.push(register_results.1.unwrap()); + } + } + LabelAbsolute => { + instruction.operands[i].token_type = TokenType::LabelOperand; + + bit_lengths.push(20); + let label_absolute_results = read_label_absolute( + &instruction.operands[i].token_name, + instruction.operands[i].start_end_columns, + labels.clone(), + ); + + binary_representation.push(label_absolute_results.0); + if label_absolute_results.1.is_some() { + instruction.errors.push(label_absolute_results.1.unwrap()); + } + } + LabelRelative => { + instruction.operands[i].token_type = TokenType::LabelOperand; + + bit_lengths.push(12); + let label_relative_results = read_label_relative( + &instruction.operands[i].token_name, + instruction.operands[i].start_end_columns, + instruction.instruction_number, + labels.clone(), + ); + binary_representation.push(label_relative_results.0); + if label_relative_results.1.is_some() { + instruction.errors.push(label_relative_results.1.unwrap()); + } + } + ShiftAmount => { + instruction.operands[i].token_type = TokenType::Immediate; + bit_lengths.push(7); + + let immediate_results = read_immediate( + &instruction.operands[i].token_name, + instruction.operands[i].start_end_columns, + 7, + ); + + binary_representation.push(immediate_results.0); + if immediate_results.1.is_some() { + instruction.errors.push(immediate_results.1.unwrap()); + } + } + } + } + //once all operands are read, we can append them onto the instruction + for (index, element) in concat_order.iter().rev().enumerate() { + instruction.binary = append_binary( + instruction.binary, + binary_representation[element - 1], + bit_lengths[element - 1], + ); + if index == concat_order.len() - 2 && funct3.is_some() + // Set funct3 value before the final argument + { + instruction.binary = append_binary(instruction.binary, funct3.unwrap_or(0), 3) + } else if index == 0 && fmt.is_some() { + instruction.binary = append_binary(instruction.binary, fmt.unwrap_or(0), 2) + } + } + + instruction +} + ///Returns distance to a labeled instruction relative to the instruction after the current instruction. /// The value represents instruction numbers NOT bytes. pub fn read_label_relative( @@ -300,6 +493,75 @@ pub fn read_memory_address( (immediate_results.0, register_results.0 as u32, None) } +///Takes in a memory address and token number and returns the binary for the offset value, base register value, and any errors. +/// If the string given matches a label, that address is returned instead +pub fn read_memory_address_riscv( + orig_string: &str, + start_end_columns: (usize, usize), +) -> (u32, u32, Option>) { + //the indices of the open and close parentheses are checked. + //If either are missing or they are in the wrong order, an error is returned + let open_index = orig_string.find('('); + let close_index = orig_string.find(')'); + if close_index.is_none() || open_index.is_none() || close_index < open_index { + return ( + 0, + 0, + Some(vec![Error { + error_name: InvalidMemorySyntax, + token_causing_error: orig_string.to_string(), + start_end_columns, + message: "".to_string(), + }]), + ); + } + + //splits the string at the index of the open parenthesis to isolate the base and offset + let (offset_str, base_str) = orig_string.split_at(open_index.unwrap()); + + let mut base: Vec = base_str.chars().collect(); + + //returns an error if there are any characters after the close parenthesis + if base[base.len() - 1] != ')' { + return ( + 0, + 0, + Some(vec![Error { + error_name: InvalidMemorySyntax, + token_causing_error: orig_string.to_string(), + start_end_columns, + message: "".to_string(), + }]), + ); + } + + //removes the open and close parentheses characters and then turns it into a string + base = base[1..base.len() - 1].to_owned(); + let mut cleaned_base: String = base.into_iter().collect(); + cleaned_base = cleaned_base.to_string(); + + //offset is an immediate while base is a register so the read functions for those operands + //will confirm they are properly formatted + let immediate_results = read_immediate(offset_str, start_end_columns, 16); + let register_results = read_register_riscv(&cleaned_base, start_end_columns, GeneralPurpose); + + //any errors found in the read_immediate or read_register functions are collected into a vec + //if there were any errors, those are returned + let mut return_errors: Vec = Vec::new(); + if immediate_results.1.is_some() { + return_errors.push(immediate_results.1.unwrap()) + } + if register_results.1.is_some() { + return_errors.push(register_results.1.unwrap()); + } + if !return_errors.is_empty() { + return (0, 0, Some(return_errors)); + } + + //if the function reaches here and hasn't already returned, there aren't any errors + (immediate_results.0, register_results.0 as u32, None) +} + ///read_register takes the string of the register name, the token number the register is from the corresponding instruction ///and the expected register type. It calls the corresponding functions holding the match cases for the different register types. pub fn read_register( @@ -310,54 +572,130 @@ pub fn read_register( if register_type == GeneralPurpose { //this section is for matching general purpose registers let general_result = match_gp_register(register); - if let Some(..) = general_result { - (general_result.unwrap(), None) - } else if match_fp_register(register).is_some() { - ( - 0, - Some(Error { - error_name: IncorrectRegisterTypeFP, - token_causing_error: register.to_string(), - start_end_columns, - message: "".to_string(), - }), - ) - } else { - ( - 0, - Some(Error { - error_name: UnrecognizedGPRegister, - token_causing_error: register.to_string(), - start_end_columns, - message: "".to_string(), - }), - ) + match general_result { + Some(general_result) => (general_result, None), + None => { + if match_fp_register(register).is_some() { + // Creates Error if supplied a fp register for gp + ( + 0, + Some(Error { + error_name: IncorrectRegisterTypeFP, + token_causing_error: register.to_string(), + start_end_columns, + message: "".to_string(), + }), + ) + } else { + ( + 0, + Some(Error { + error_name: UnrecognizedGPRegister, + token_causing_error: register.to_string(), + start_end_columns, + message: "".to_string(), + }), + ) + } + } } } else { //this section is for matching floating point registers let floating_result = match_fp_register(register); - if let Some(..) = floating_result { - (floating_result.unwrap(), None) - } else if match_gp_register(register).is_some() { - ( - 0, - Some(Error { - error_name: IncorrectRegisterTypeGP, - token_causing_error: register.to_string(), - start_end_columns, - message: "".to_string(), - }), - ) - } else { - ( - 0, - Some(Error { - error_name: UnrecognizedFPRegister, - token_causing_error: register.to_string(), - start_end_columns, - message: "".to_string(), - }), - ) + match floating_result { + Some(floating_result) => (floating_result, None), + None => { + if match_gp_register(register).is_some() { + ( + 0, + Some(Error { + error_name: IncorrectRegisterTypeGP, + token_causing_error: register.to_string(), + start_end_columns, + message: "".to_string(), + }), + ) + } else { + ( + 0, + Some(Error { + error_name: UnrecognizedFPRegister, + token_causing_error: register.to_string(), + start_end_columns, + message: "".to_string(), + }), + ) + } + } + } + } +} + +///read_register takes the string of the register name, the token number the register is from the corresponding instruction +///and the expected register type. It calls the corresponding functions holding the match cases for the different register types. +pub fn read_register_riscv( + register: &str, + start_end_columns: (usize, usize), + register_type: RegisterType, +) -> (u8, Option) { + if register_type == GeneralPurpose { + //this section is for matching general purpose registers + let general_result = match_gp_register_riscv(register); + match general_result { + Some(general_result) => (general_result, None), + None => { + if match_fp_register_riscv(register).is_some() { + // Creates Error if supplied a fp register for gp + ( + 0, + Some(Error { + error_name: IncorrectRegisterTypeFP, + token_causing_error: register.to_string(), + start_end_columns, + message: "".to_string(), + }), + ) + } else { + ( + 0, + Some(Error { + error_name: UnrecognizedGPRegister, + token_causing_error: register.to_string(), + start_end_columns, + message: "".to_string(), + }), + ) + } + } + } + } else { + //this section is for matching floating point registers + let floating_result = match_fp_register_riscv(register); + match floating_result { + Some(floating_result) => (floating_result, None), + None => { + if match_gp_register_riscv(register).is_some() { + ( + 0, + Some(Error { + error_name: IncorrectRegisterTypeGP, + token_causing_error: register.to_string(), + start_end_columns, + message: "".to_string(), + }), + ) + } else { + ( + 0, + Some(Error { + error_name: UnrecognizedFPRegister, + token_causing_error: register.to_string(), + start_end_columns, + message: "".to_string(), + }), + ) + } + } } } } @@ -375,6 +713,19 @@ pub fn match_gp_register(given_string: &str) -> Option { None } +///This function takes a register string as an argument and returns the string of the binary of the matching +///general register or none if there is not one that matches. +pub fn match_gp_register_riscv(given_string: &str) -> Option { + for register in RISCV_GP_REGISTERS { + for name in register.names { + if &given_string.to_lowercase().as_str() == name { + return Some(register.binary); + } + } + } + None +} + ///This function takes a register string as an argument and returns the string of the binary of the matching ///floating point register or none if there is not one that matches. pub fn match_fp_register(given_string: &str) -> Option { @@ -386,6 +737,19 @@ pub fn match_fp_register(given_string: &str) -> Option { None } +///This function takes a register string as an argument and returns the string of the binary of the matching +///floating point register or none if there is not one that matches. +pub fn match_fp_register_riscv(given_string: &str) -> Option { + for register in RISCV_FP_REGISTERS { + for name in register.names { + if &given_string.to_lowercase().as_str() == name { + return Some(register.binary); + } + } + } + None +} + ///This function takes a string representation of an immediate value and the number of bits available to represent it /// and attempts to translate it to an actual integer. If the value cannot be cast to int or is too big to be represented /// by the available bits, an error is returned. @@ -400,8 +764,8 @@ pub fn read_immediate( //if that results in an error, try to read it as hex if parse_results.is_err() { let removed_prefix = given_text.strip_prefix("0x"); - if let Some(..) = removed_prefix { - parse_results = i64::from_str_radix(removed_prefix.unwrap(), 16); + if let Some(removed_prefix) = removed_prefix { + parse_results = i64::from_str_radix(removed_prefix, 16); } } @@ -538,23 +902,24 @@ pub fn assemble_data_binary(data_list: &mut [Data]) -> Vec { for value in datum.data_entries.iter_mut() { value.token_type = Float; let parse_results = value.token_name.parse::(); - if let Ok(..) = parse_results { - let float_bits = parse_results.unwrap().to_bits(); - vec_of_data.push((float_bits >> 56) as u8); - vec_of_data.push((float_bits >> 48) as u8); - vec_of_data.push((float_bits >> 40) as u8); - vec_of_data.push((float_bits >> 32) as u8); - vec_of_data.push((float_bits >> 24) as u8); - vec_of_data.push((float_bits >> 16) as u8); - vec_of_data.push((float_bits >> 8) as u8); - vec_of_data.push(float_bits as u8); - } else { - datum.errors.push(Error { + match parse_results { + Ok(..) => { + let float_bits = parse_results.unwrap().to_bits(); + vec_of_data.push((float_bits >> 56) as u8); + vec_of_data.push((float_bits >> 48) as u8); + vec_of_data.push((float_bits >> 40) as u8); + vec_of_data.push((float_bits >> 32) as u8); + vec_of_data.push((float_bits >> 24) as u8); + vec_of_data.push((float_bits >> 16) as u8); + vec_of_data.push((float_bits >> 8) as u8); + vec_of_data.push(float_bits as u8); + } + Err(_) => datum.errors.push(Error { error_name: NonFloatImmediate, token_causing_error: value.token_name.to_string(), start_end_columns: value.start_end_columns, message: "".to_string(), - }) + }), } } } @@ -563,19 +928,20 @@ pub fn assemble_data_binary(data_list: &mut [Data]) -> Vec { for value in datum.data_entries.iter_mut() { value.token_type = Float; let parse_results = value.token_name.parse::(); - if let Ok(..) = parse_results { - let float_bits = parse_results.unwrap().to_bits(); - vec_of_data.push((float_bits >> 24) as u8); - vec_of_data.push((float_bits >> 16) as u8); - vec_of_data.push((float_bits >> 8) as u8); - vec_of_data.push(float_bits as u8); - } else { - datum.errors.push(Error { + match parse_results { + Ok(..) => { + let float_bits = parse_results.unwrap().to_bits(); + vec_of_data.push((float_bits >> 24) as u8); + vec_of_data.push((float_bits >> 16) as u8); + vec_of_data.push((float_bits >> 8) as u8); + vec_of_data.push(float_bits as u8); + } + Err(_) => datum.errors.push(Error { error_name: NonFloatImmediate, token_causing_error: value.token_name.to_string(), start_end_columns: value.start_end_columns, message: "".to_string(), - }) + }), } } } diff --git a/src/parser/parser_assembler_main.rs b/src/parser/parser_assembler_main.rs index 482468b89..161ea968a 100644 --- a/src/parser/parser_assembler_main.rs +++ b/src/parser/parser_assembler_main.rs @@ -1,4 +1,5 @@ -use crate::parser::assembling::{assemble_data_binary, read_operands}; +use crate::emulation_core::architectures::AvailableDatapaths; +use crate::parser::assembling::{assemble_data_binary, read_operands, read_operands_riscv}; use crate::parser::parser_structs_and_enums::ErrorType::*; use crate::parser::parser_structs_and_enums::OperandType::*; use crate::parser::parser_structs_and_enums::ProgramInfo; @@ -6,76 +7,143 @@ use crate::parser::parser_structs_and_enums::*; use crate::parser::parsing::*; use crate::parser::pseudo_instruction_parsing::{ complete_lw_sw_pseudo_instructions, expand_pseudo_instructions_and_assign_instruction_numbers, + expand_pseudo_instructions_and_assign_instruction_numbers_riscv, }; use std::collections::HashMap; -///Parser is the starting function of the parser / assembler process. It takes a string representation of a MIPS +///Parser is the starting function of the parser / assembler process. It takes a string representation of a MIPS/RISC-V /// program and builds the binary of the instructions while cataloging any errors that are found. -pub fn parser(file_string: String) -> (ProgramInfo, Vec) { - let mut program_info = ProgramInfo { - monaco_line_info: tokenize_program(file_string), - ..Default::default() - }; +pub fn parser( + file_string: String, + arch: AvailableDatapaths, +) -> (ProgramInfo, Vec, HashMap) { + match arch { + AvailableDatapaths::MIPS => { + let mut program_info = ProgramInfo { + monaco_line_info: tokenize_program(file_string), + ..Default::default() + }; + + (program_info.instructions, program_info.data) = + separate_data_and_text(&mut program_info.monaco_line_info); + + expand_pseudo_instructions_and_assign_instruction_numbers( + &mut program_info.instructions, + &program_info.data, + &mut program_info.monaco_line_info, + ); + + let vec_of_data = assemble_data_binary(&mut program_info.data); + + let labels: HashMap = + create_label_map(&mut program_info.instructions, &mut program_info.data); + let labels_clone = labels.clone(); + + complete_lw_sw_pseudo_instructions( + &mut program_info.instructions, + &labels, + &mut program_info.monaco_line_info, + ); + + read_instructions( + &mut program_info.instructions, + &labels, + &mut program_info.monaco_line_info, + ); + + program_info.console_out_post_assembly = suggest_error_corrections( + &mut program_info.instructions, + &mut program_info.data, + &labels, + &mut program_info.monaco_line_info, + arch, + ); + + let (binary, data_starting_point) = + create_binary_vec(program_info.instructions.clone(), vec_of_data); + + for entry in &program_info.monaco_line_info { + program_info + .updated_monaco_string + .push_str(&format!("{}\n", entry.updated_monaco_string)); + } - (program_info.instructions, program_info.data) = - separate_data_and_text(&mut program_info.monaco_line_info); - - expand_pseudo_instructions_and_assign_instruction_numbers( - &mut program_info.instructions, - &program_info.data, - &mut program_info.monaco_line_info, - ); - - let vec_of_data = assemble_data_binary(&mut program_info.data); - - let labels: HashMap = - create_label_map(&mut program_info.instructions, &mut program_info.data); - - complete_lw_sw_pseudo_instructions( - &mut program_info.instructions, - &labels, - &mut program_info.monaco_line_info, - ); - - read_instructions( - &mut program_info.instructions, - &labels, - &mut program_info.monaco_line_info, - ); - - program_info.console_out_post_assembly = suggest_error_corrections( - &mut program_info.instructions, - &mut program_info.data, - &labels, - &mut program_info.monaco_line_info, - ); - - let binary = create_binary_vec(program_info.instructions.clone(), vec_of_data); - - for entry in &program_info.monaco_line_info { - program_info - .updated_monaco_string - .push_str(&format!("{}\n", entry.updated_monaco_string)); - } + for instruction in program_info.instructions.clone() { + program_info + .address_to_line_number + .push(instruction.line_number); + } - for instruction in program_info.instructions.clone() { - program_info - .address_to_line_number - .push(instruction.line_number); - } + program_info.pc_starting_point = determine_pc_starting_point(labels); + program_info.data_starting_point = data_starting_point; + + (program_info.clone(), binary, labels_clone) + } + AvailableDatapaths::RISCV => { + let mut program_info = ProgramInfo { + monaco_line_info: tokenize_program(file_string), + ..Default::default() + }; + + (program_info.instructions, program_info.data) = + separate_data_and_text(&mut program_info.monaco_line_info); + + expand_pseudo_instructions_and_assign_instruction_numbers_riscv( + &mut program_info.instructions, + &program_info.data, + &mut program_info.monaco_line_info, + ); + + let vec_of_data = assemble_data_binary(&mut program_info.data); + + let labels: HashMap = + create_label_map(&mut program_info.instructions, &mut program_info.data); + let labels_clone = labels.clone(); + + read_instructions_riscv( + &mut program_info.instructions, + &labels, + &mut program_info.monaco_line_info, + ); + + program_info.console_out_post_assembly = suggest_error_corrections( + &mut program_info.instructions, + &mut program_info.data, + &labels, + &mut program_info.monaco_line_info, + arch, + ); + + let (binary, data_starting_point) = + create_binary_vec(program_info.instructions.clone(), vec_of_data); + + for entry in &program_info.monaco_line_info { + program_info + .updated_monaco_string + .push_str(&format!("{}\n", entry.updated_monaco_string)); + } - program_info.pc_starting_point = determine_pc_starting_point(labels); + for instruction in program_info.instructions.clone() { + program_info + .address_to_line_number + .push(instruction.line_number); + } + + program_info.pc_starting_point = determine_pc_starting_point(labels); + program_info.data_starting_point = data_starting_point; - (program_info.clone(), binary) + (program_info.clone(), binary, labels_clone) + } + } } -///Takes the vector of instructions and assembles the binary for them. +///Takes the vector of MIPS instructions and assembles the binary for them. pub fn read_instructions( instruction_list: &mut [Instruction], labels: &HashMap, monaco_line_info: &mut [MonacoLineInfo], ) { - for mut instruction in &mut instruction_list.iter_mut() { + for instruction in &mut instruction_list.iter_mut() { //this match case is the heart of the parser and figures out which instruction type it is //then it can call the proper functions for that specific instruction match &*instruction.operator.token_name.to_lowercase() { @@ -918,7 +986,7 @@ pub fn read_instructions( //this instruction is not used in pseudo-instructions so we can push it to mouse_hover_string without checking if mouse_hover_string is empty let info = InstructionDescription{ - syntax: "lwc1 ft offset(base)".to_string(), + syntax: "lwc1 ft, offset(base)".to_string(), description: "Loads the contents of the 32-bit word at the specified memory address into `ft`.\n\nMemory address is calculated as the sum of `offset` and the contents of the `base` register.".to_string(), }; monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); @@ -1412,8 +1480,8 @@ pub fn read_instructions( //this instruction is not used in pseudo-instructions so we can push it to mouse_hover_string without checking if mouse_hover_string is empty let info = InstructionDescription{ - syntax: "sll rt, rs, sa".to_string(), - description: "Shifts the lower 32-bit word in `rs` to the left by sa number of bits and placing the sign-extended result into `rt`.".to_string(), + syntax: "sll rd, rt, sa".to_string(), + description: "Shifts the lower 32-bit word in `rt` to the left by sa number of bits and placing the sign-extended result into `rd`.".to_string(), }; monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); } @@ -1460,7 +1528,7 @@ pub fn read_instructions( } _ => { - if UNSUPPORTED_INSTRUCTIONS.contains(&&*instruction.operator.token_name) { + if UNSUPPORTED_INSTRUCTIONS_MIPS.contains(&&*instruction.operator.token_name) { instruction.errors.push(Error { error_name: UnsupportedInstruction, token_causing_error: instruction.operator.token_name.to_string(), @@ -1480,63 +1548,3559 @@ pub fn read_instructions( } } -///This function takes two numbers and inserts the binary of the second at a given index in the binary of the first. -///All binary values at and past the insertion index of the original string will be moved to the end of the resultant string. -///Since binary is sign extended on the left to 32 bits, insertion index must be the index from the end of the string. -pub fn place_binary_in_middle_of_another( - wrapper: u32, - middle: u32, - middle_length: usize, - index_from_right: usize, -) -> u32 { - //Step 1: Remove End Bits from Wrapper to make New Binary - //Step 2: Move New Binary Left By length of Middle - //Step 3: Or with Middle - //Step 4: Move New Binary Left by length of End - //Step 5: Shift Wrapper Left 32 - Length of End to get End - //Step 6: Shift End Right by 32 - Length of End - //Step 7: Or with New Binary - //Step 8: Return New Binary - let end_length = index_from_right + 1; - let mut new_binary = wrapper >> end_length; - new_binary <<= middle_length; - new_binary |= middle; - new_binary <<= end_length; - let mut end = wrapper << (32 - end_length); - end >>= 32 - end_length; - new_binary |= end; - new_binary -} +///Takes the vector of RISC-V instructions and assembles the binary for them. +pub fn read_instructions_riscv( + instruction_list: &mut [Instruction], + labels: &HashMap, + monaco_line_info: &mut [MonacoLineInfo], +) { + for instruction in &mut instruction_list.iter_mut() { + match &*instruction.operator.token_name.to_lowercase() { + // Start of RV32I + "add" => { + // Funct7 + instruction.binary = append_binary(instruction.binary, 0b0000000, 7); -///Append binary takes two numbers, shifts the first by a specified amount and then bitwise ors the -/// two numbers together effectively appending the second onto the first. -pub fn append_binary(mut first: u32, mut second: u32, shift_amount: u8) -> u32 { - second <<= 32 - shift_amount; - second >>= 32 - shift_amount; - first <<= shift_amount; - first |= second; - first -} + read_operands_riscv( + instruction, + vec![RegisterGP, RegisterGP, RegisterGP], + vec![1, 2, 3], + None, + Some(0b000), + None, + ); -///returns the address of the labelled main instruction. If none exists, returns address of labelled start instruction. -///Otherwise returns 0. -pub fn determine_pc_starting_point(labels: HashMap) -> usize { - return match labels.get("main") { - Some(main_address) => *main_address, - None => match labels.get("start") { - Some(start_address) => *start_address, - None => 0, - }, - }; -} + // Opcode + instruction.binary = append_binary(instruction.binary, 0b0110011, 7); -///Creates a vector of u32 from the data found in the parser / assembler to put into memory. -pub fn create_binary_vec(instructions: Vec, mut vec_of_data: Vec) -> Vec { - //push all instructions - let mut binary: Vec = Vec::new(); - for instruction in instructions { - binary.push(instruction.binary); - } + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "add rd, rs1, rs2".to_string(), + description: "Adds the registers rs1 and rs2 and stores the result in rd.\n\nArithmetic overflow is ignored and the result is simply the low XLEN bits of the result.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "sub" => { + // Funct7 + instruction.binary = append_binary(instruction.binary, 0b0100000, 7); + + read_operands_riscv( + instruction, + vec![RegisterGP, RegisterGP, RegisterGP], + vec![1, 2, 3], + None, + Some(0b000), + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b0110011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "sub rd, rs1, rs2".to_string(), + description: "Subs the register rs2 from rs1 and stores the result in rd.\n\nArithmetic overflow is ignored and the result is simply the low XLEN bits of the result.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "sll" => { + // Funct7 + instruction.binary = append_binary(instruction.binary, 0b0000000, 7); + + read_operands_riscv( + instruction, + vec![RegisterGP, RegisterGP, RegisterGP], + vec![1, 2, 3], + None, + Some(0b001), + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b0110011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "sll rd, rs1, rs2".to_string(), + description: "Performs logical left shift on the value in register rs1 by the shift amount held in the lower 5 bits of register rs2.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "slt" => { + // Funct7 + instruction.binary = append_binary(instruction.binary, 0b0000000, 7); + + read_operands_riscv( + instruction, + vec![RegisterGP, RegisterGP, RegisterGP], + vec![1, 2, 3], + None, + Some(0b010), + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b0110011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "slt rd, rs1, rs2".to_string(), + description: "Place the value 1 in register rd if register rs1 is less than register rs2 when both are treated as signed numbers, else 0 is written to rd.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "sltu" => { + // Funct7 + instruction.binary = append_binary(instruction.binary, 0b0000000, 7); + + read_operands_riscv( + instruction, + vec![RegisterGP, RegisterGP, RegisterGP], + vec![1, 2, 3], + None, + Some(0b011), + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b0110011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "sltu rd, rs1, rs2".to_string(), + description: "Place the value 1 in register rd if register rs1 is less than register rs2 when both are treated as unsigned numbers, else 0 is written to rd.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "xor" => { + // Funct7 + instruction.binary = append_binary(instruction.binary, 0b0000000, 7); + + read_operands_riscv( + instruction, + vec![RegisterGP, RegisterGP, RegisterGP], + vec![1, 2, 3], + None, + Some(0b100), + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b0110011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "xor rd, rs1, rs2".to_string(), + description: "Performs bitwise XOR on registers rs1 and rs2 and place the result in rd.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "srl" => { + // Funct7 + instruction.binary = append_binary(instruction.binary, 0b0000000, 7); + + read_operands_riscv( + instruction, + vec![RegisterGP, RegisterGP, RegisterGP], + vec![1, 2, 3], + None, + Some(0b101), + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b0110011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "srl rd, rs1, rs2".to_string(), + description: "Logical right shift on the value in register rs1 by the shift amount held in the lower 5 bits of register rs2.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "sra" => { + // Funct7 + instruction.binary = append_binary(instruction.binary, 0b0100000, 7); + + read_operands_riscv( + instruction, + vec![RegisterGP, RegisterGP, RegisterGP], + vec![1, 2, 3], + None, + Some(0b101), + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b0110011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "sra rd, rs1, rs2".to_string(), + description: "Performs arithmetic right shift on the value in register rs1 by the shift amount held in the lower 5 bits of register rs2.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "or" => { + // Funct7 + instruction.binary = append_binary(instruction.binary, 0b0000000, 7); + + read_operands_riscv( + instruction, + vec![RegisterGP, RegisterGP, RegisterGP], + vec![1, 2, 3], + None, + Some(0b110), + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b0110011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "or rd, rs1, rs2".to_string(), + description: "Performs bitwise OR on registers rs1 and rs2 and place the result in rd.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "and" => { + // Funct7 + instruction.binary = append_binary(instruction.binary, 0b0000000, 7); + + read_operands_riscv( + instruction, + vec![RegisterGP, RegisterGP, RegisterGP], + vec![1, 2, 3], + None, + Some(0b111), + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b0110011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "and rd, rs1, rs2".to_string(), + description: "Performs bitwise AND on registers rs1 and rs2 and place the result in rd.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "addi" => + // This instruction requires the 12-bit immediate to be sign extended before moving to the emulator's register + { + read_operands_riscv( + instruction, + vec![RegisterGP, RegisterGP, Immediate], + vec![1, 2, 3], + None, + Some(0), + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b0010011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "addi rd, rs1, imm".to_string(), + description: "Adds the sign-extended 12-bit immediate to register rs1.\n\nArithmetic overflow is ignored and the result is simply the low XLEN bits of the result.\n\naddi rd, rs1, 0 is used to implement the MV rd, rs1 assembler pseudo-instruction.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "slti" => { + read_operands_riscv( + instruction, + vec![RegisterGP, RegisterGP, Immediate], + vec![1, 2, 3], + None, + Some(0b010), + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b0010011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "slti rd, rs1, imm".to_string(), + description: "Place the value 1 in register rd if register rs1 is less than the signextended immediate when both are treated as signed numbers, else 0 is written to rd.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "sltiu" => { + read_operands_riscv( + instruction, + vec![RegisterGP, RegisterGP, Immediate], + vec![1, 2, 3], + None, + Some(0b011), + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b0010011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "sltiu rd, rs1, imm".to_string(), + description: "Place the value 1 in register rd if register rs1 is less than the immediate when both are treated as unsigned numbers, else 0 is written to rd.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "xori" => { + read_operands_riscv( + instruction, + vec![RegisterGP, RegisterGP, Immediate], + vec![1, 2, 3], + None, + Some(0b100), + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b0010011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "xori rd, rs1, imm".to_string(), + description: "Performs bitwise XOR on register rs1 and the sign-extended 12-bit immediate and place the result in rd.\n\nNote, “xori rd, rs1, -1” performs a bitwise logical inversion of register rs1(assembler pseudo-instruction NOT rd, rs)".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "ori" => { + read_operands_riscv( + instruction, + vec![RegisterGP, RegisterGP, Immediate], + vec![1, 2, 3], + None, + Some(0b110), + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b0010011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "ori rd, rs1, imm".to_string(), + description: "Performs bitwise OR on register rs1 and the sign-extended 12-bit immediate and place the result in rd.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "andi" => { + read_operands_riscv( + instruction, + vec![RegisterGP, RegisterGP, Immediate], + vec![1, 2, 3], + None, + Some(0b111), + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b0010011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "andi rd, rs1, imm".to_string(), + description: "Performs bitwise AND on register rs1 and the sign-extended 12-bit immediate and place the result in rd.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "slli" => { + // Funct7 + instruction.binary = append_binary(instruction.binary, 0b00000, 5); // Check if the next 2 bits are needed + + read_operands_riscv( + instruction, + vec![RegisterGP, RegisterGP, ShiftAmount], + vec![1, 2, 3], + None, + Some(0b001), + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b0010011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "slli rd, rs1, shamt".to_string(), + description: "Performs logical left shift on the value in register rs1 by the shift amount held in the lower 5 bits of the immediate.\n\nIn RV64, bit-25 is used to shamt[5].".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "srli" => { + // Funct7 + instruction.binary = append_binary(instruction.binary, 0b00000, 5); // Check if the next 2 bits are needed + + read_operands_riscv( + instruction, + vec![RegisterGP, RegisterGP, ShiftAmount], + vec![1, 2, 3], + None, + Some(0b101), + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b0010011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "srli rd, rs1, shamt".to_string(), + description: "Performs logical right shift on the value in register rs1 by the shift amount held in the lower 5 bits of the immediate.\n\nIn RV64, bit-25 is used to shamt[5].".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "srai" => { + // Funct7 + instruction.binary = append_binary(instruction.binary, 0b01000, 5); // Check if the next 2 bits are needed + + read_operands_riscv( + instruction, + vec![RegisterGP, RegisterGP, ShiftAmount], + vec![1, 2, 3], + None, + Some(0b101), + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b0010011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "srai rd, rs1, shamt".to_string(), + description: "Performs arithmetic right shift on the value in register rs1 by the shift amount held in the lower 5 bits of the immediate.\n\nIn RV64, bit-25 is used to shamt[5].".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "lb" => { + read_operands_riscv( + instruction, + vec![RegisterGP, MemoryAddress], + vec![1, 3, 2], + None, + Some(0b000), + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b0000011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "lb rd, offset(rs1)".to_string(), + description: "Loads a 8-bit value from memory and sign-extends this to XLEN bits before storing it in register rd.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "lh" => { + read_operands_riscv( + instruction, + vec![RegisterGP, MemoryAddress], + vec![1, 3, 2], + None, + Some(0b001), + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b0000011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "lh rd, offset(rs1)".to_string(), + description: "Loads a 16-bit value from memory and sign-extends this to XLEN bits before storing it in register rd.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "lw" => { + read_operands_riscv( + instruction, + vec![RegisterGP, MemoryAddress], + vec![1, 3, 2], + None, + Some(0b010), + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b0000011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "lw rd, offset(rs1)".to_string(), + description: "Loads a 32-bit value from memory and sign-extends this to XLEN bits before storing it in register rd.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "lbu" => { + read_operands_riscv( + instruction, + vec![RegisterGP, MemoryAddress], + vec![1, 3, 2], + None, + Some(0b100), + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b0000011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "lbu rd, offset(rs1)".to_string(), + description: "Loads a 8-bit value from memory and zero-extends this to XLEN bits before storing it in register rd.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "lhu" => { + read_operands_riscv( + instruction, + vec![RegisterGP, MemoryAddress], + vec![1, 3, 2], + None, + Some(0b101), + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b0000011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "lhu rd, offset(rs1)".to_string(), + description: "Loads a 16-bit value from memory and zero-extends this to XLEN bits before storing it in register rd.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "sb" => { + read_operands_riscv( + instruction, + vec![RegisterGP, MemoryAddress], + vec![1, 3, 2], + None, + Some(0b000), + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b0100011, 7); + + // Encoded as I-type, but needs reordering for S-type + instruction.binary = immediate_to_stored(instruction.binary); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription { + syntax: "sb rs2, offset(rs1)".to_string(), + description: + "Store 8-bit, values from the low bits of register rs2 to memory." + .to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "sh" => { + read_operands_riscv( + instruction, + vec![RegisterGP, MemoryAddress], + vec![1, 3, 2], + None, + Some(0b001), + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b0100011, 7); + + // Encoded as I-type, but needs reordering for S-type + instruction.binary = immediate_to_stored(instruction.binary); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription { + syntax: "sh rs2, offset(rs1)".to_string(), + description: + "Store 16-bit, values from the low bits of register rs2 to memory." + .to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "sw" => { + read_operands_riscv( + instruction, + vec![RegisterGP, MemoryAddress], + vec![1, 3, 2], + None, + Some(0b010), + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b0100011, 7); + + // Encoded as I-type, but needs reordering for S-type + instruction.binary = immediate_to_stored(instruction.binary); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription { + syntax: "sw rs2, offset(rs1)".to_string(), + description: + "Store 32-bit, values from the low bits of register rs2 to memory." + .to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "jal" => { + // Read as U-type instruction and reorder immediate value after + read_operands_riscv( + instruction, + vec![RegisterGP, LabelAbsolute], + vec![1, 2], + Some(labels.clone()), + None, + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b1101111, 7); + + // Reorder immediate + //instruction.binary = upper_to_jump(instruction.binary); + + // This isn't accurate to the risc-v spec, but we can make the user aware of this change in the instruction description. Unsure how useful this may be to the end user. + + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription { + syntax: "jal rd, offset".to_string(), + description: "Jump to address and place return address in rd.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "jalr" => { + read_operands_riscv( + instruction, + vec![RegisterGP, RegisterGP, Immediate], + vec![1, 2, 3], + None, + Some(0b000), + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b1100111, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription { + syntax: "jalr rd, rs1, offset".to_string(), + description: "Jump to address and place return address in rd.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "beq" => { + read_operands_riscv( + instruction, + vec![RegisterGP, RegisterGP, LabelAbsolute], + vec![1, 2, 3], + Some(labels.clone()), + Some(0b000), + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b1100011, 7); + + // May be easier to treat branch instructions as i-type instructions due to how labels get resolved + // This isn't accurate to the risc-v spec, but we can make the user aware of this change in the instruction description + + //instruction.binary = immediate_to_branch(instruction.binary); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription { + syntax: "beq rs1, rs2, label".to_string(), + description: "Take the branch if registers rs1 and rs2 are equal." + .to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "bne" => { + read_operands_riscv( + instruction, + vec![RegisterGP, RegisterGP, LabelAbsolute], + vec![1, 2, 3], + Some(labels.clone()), + Some(0b001), + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b1100011, 7); + + //instruction.binary = immediate_to_branch(instruction.binary); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription { + syntax: "bne rs1, rs2, label".to_string(), + description: "Take the branch if registers rs1 and rs2 are not equal." + .to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "blt" => { + read_operands_riscv( + instruction, + vec![RegisterGP, RegisterGP, LabelAbsolute], + vec![1, 2, 3], + Some(labels.clone()), + Some(0b100), + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b1100011, 7); + + //instruction.binary = immediate_to_branch(instruction.binary); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "blt rs1, rs2, label".to_string(), + description: "Take the branch if registers rs1 is less than rs2, using signed comparison.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "bge" => { + read_operands_riscv( + instruction, + vec![RegisterGP, RegisterGP, LabelAbsolute], + vec![1, 2, 3], + Some(labels.clone()), + Some(0b101), + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b1100011, 7); + + //instruction.binary = immediate_to_branch(instruction.binary); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "bge rs1, rs2, offset".to_string(), + description: "Take the branch if registers rs1 is greater than or equal to rs2, using signed comparison.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "bltu" => { + read_operands_riscv( + instruction, + vec![RegisterGP, RegisterGP, LabelAbsolute], + vec![1, 2, 3], + Some(labels.clone()), + Some(0b110), + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b1100011, 7); + + //instruction.binary = immediate_to_branch(instruction.binary); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "bltu rs1, rs2, label".to_string(), + description: "Take the branch if registers rs1 is less than rs2, using unsigned comparison.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "bgeu" => { + read_operands_riscv( + instruction, + vec![RegisterGP, RegisterGP, LabelAbsolute], + vec![1, 2, 3], + Some(labels.clone()), + Some(0b111), + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b1100011, 7); + + //instruction.binary = immediate_to_branch(instruction.binary); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "bgeu rs1, rs2, label".to_string(), + description: "Take the branch if registers rs1 is greater than or equal to rs2, using unsigned comparison.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "ecall" => { + // ecall instruction encoding does not change + instruction.binary = 0b00000000000000000000000001110011; + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "ecall".to_string(), + description: "Make a request to the supporting execution environment.\n\nWhen executed in U-mode, S-mode, or M-mode, it generates an environment-call-from-U-mode exception, environment-call-from-S-mode exception, or environment-call-from-M-mode exception, respectively, and performs no other operation.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "ebreak" => { + // ebreak instruction encoding does not change + instruction.binary = 0b00000000000100000000000001110011; + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "ebreak".to_string(), + description: "Used by debuggers to cause control to be transferred back to a debugging environment.\n\nIt generates a breakpoint exception and performs no other operation.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "uret" => { + // uret instruction encoding does not change + instruction.binary = 0b00000000001000000000000001110011; + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "uret".to_string(), + description: "Return from traps in U-mode, and URET copies UPIE into UIE, then sets UPIE.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "sret" => { + // uret instruction encoding does not change + instruction.binary = 0b00010000001000000000000001110011; + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "uret".to_string(), + description: "Return from traps in U-mode, and URET copies UPIE into UIE, then sets UPIE.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "mret" => { + // uret instruction encoding does not change + instruction.binary = 0b00110000001000000000000001110011; + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "mret".to_string(), + description: "Return from traps in M-mode, and MRET copies MPIE into MIE, then sets MPIE.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "wfi" => { + // uret instruction encoding does not change + instruction.binary = 0b00010000010100000000000001110011; + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "wfi".to_string(), + description: "Provides a hint to the implementation that the current hart can be stalled until an interrupt might need servicing.\n\nExecution of the WFI instruction can also be used to inform the hardware platform that suitable interrupts should preferentially be routed to this hart.\n\nWFI is available in all privileged modes, and optionally available to U-mode.\n\nThis instruction may raise an illegal instruction exception when TW=1 in mstatus.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "fence.i" => { + // fence.i instruction encoding does not change + instruction.binary = 0b00000000000000000001000000001111; + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "fence.i".to_string(), + description: "Provides explicit synchronization between writes to instruction memory and instruction fetches on the same hart.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "lui" => { + read_operands_riscv( + instruction, + vec![RegisterGP, UpperImmediate], + vec![1, 2], + None, + None, + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b0110111, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "lui rd, imm".to_string(), + description: "Build 32-bit constants and uses the U-type format. LUI places the U-immediate value in the top 20 bits of the destination register rd, filling in the lowest 12 bits with zeros.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "auipc" => { + read_operands_riscv( + instruction, + vec![RegisterGP, UpperImmediate], + vec![1, 2], + None, + None, + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b0010111, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "auipc rd, imm".to_string(), + description: "Build pc-relative addresses and uses the U-type format.\n\nAUIPC forms a 32-bit offset from the 20-bit U-immediate, filling in the lowest 12 bits with zeros, adds this offset to the pc, then places the result in register rd.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + // Start of RV64I I nstructions + "addiw" => { + read_operands_riscv( + instruction, + vec![RegisterGP, RegisterGP, Immediate], + vec![1, 2, 3], + None, + Some(0b000), + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b0011011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "addiw rd, rs1, imm".to_string(), + description: "Adds the sign-extended 12-bit immediate to register rs1 and produces the proper sign-extension of a 32-bit result in rd.\n\nOverflows are ignored and the result is the low 32 bits of the result sign-extended to 64 bits.\n\nNote, ADDIW rd, rs1, 0 writes the sign-extension of the lower 32 bits of register rs1 into register rd (assembler pseudoinstruction SEXT.W).".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "slliw" => { + // Funct7 + instruction.binary = append_binary(instruction.binary, 0b0000000, 7); + + read_operands_riscv( + instruction, + vec![RegisterGP, RegisterGP, ShiftAmount], + vec![1, 2, 3], + None, + Some(0b001), + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b0011011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "slliw rd, rs1, shamt".to_string(), + description: "Performs logical left shift on the 32-bit of value in register rs1 by the shift amount held in the lower 5 bits of the immediate.\n\nEncodings with $imm[5] neq 0$ are reserved.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "srliw" => { + // Funct7 + instruction.binary = append_binary(instruction.binary, 0b0000000, 7); + + read_operands_riscv( + instruction, + vec![RegisterGP, RegisterGP, ShiftAmount], + vec![1, 2, 3], + None, + Some(0b101), + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b0011011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "srliw rd, rs1, shamt".to_string(), + description: "Performs logical right shift on the 32-bit of value in register rs1 by the shift amount held in the lower 5 bits of the immediate.\n\nEncodings with $imm[5] neq 0$ are reserved.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "sraiw" => { + // Funct7 + instruction.binary = append_binary(instruction.binary, 0b01000, 7); + + read_operands_riscv( + instruction, + vec![RegisterGP, RegisterGP, ShiftAmount], + vec![1, 2, 3], + None, + Some(0b101), + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b0011011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "sraiw rd, rs1, shamt".to_string(), + description: "Performs arithmetic right shift on the 32-bit of value in register rs1 by the shift amount held in the lower 5 bits of the immediate.\n\nEncodings with $imm[5] neq 0$ are reserved.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "addw" => { + // Funct7 + instruction.binary = append_binary(instruction.binary, 0b0000000, 7); + + read_operands_riscv( + instruction, + vec![RegisterGP, RegisterGP, RegisterGP], + vec![1, 2, 3], + None, + Some(0b000), + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b0111011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "addw rd, rs1, rs2".to_string(), + description: "Adds the 32-bit of registers rs1 and 32-bit of register rs2 and stores the result in rd.\n\nArithmetic overflow is ignored and the low 32-bits of the result is sign-extended to 64-bits and written to the destination register.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "subw" => { + // Funct7 + instruction.binary = append_binary(instruction.binary, 0b0100000, 7); + + read_operands_riscv( + instruction, + vec![RegisterGP, RegisterGP, RegisterGP], + vec![1, 2, 3], + None, + Some(0b000), + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b0111011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "subw rd, rs1, rs2".to_string(), + description: "Subtract the 32-bit of registers rs1 and 32-bit of register rs2 and stores the result in rd.\n\nArithmetic overflow is ignored and the low 32-bits of the result is sign-extended to 64-bits and written to the destination register.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "sllw" => { + // Funct7 + instruction.binary = append_binary(instruction.binary, 0b0000000, 7); + + read_operands_riscv( + instruction, + vec![RegisterGP, RegisterGP, RegisterGP], + vec![1, 2, 3], + None, + Some(0b001), + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b0111011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "sllw rd, rs1, rs2".to_string(), + description: "Performs logical left shift on the low 32-bits value in register rs1 by the shift amount held in the lower 5 bits of register rs2 and produce 32-bit results and written to the destination register rd.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "srlw" => { + // Funct7 + instruction.binary = append_binary(instruction.binary, 0b0000000, 7); + + read_operands_riscv( + instruction, + vec![RegisterGP, RegisterGP, RegisterGP], + vec![1, 2, 3], + None, + Some(0b101), + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b0111011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "srlw rd, rs1, rs2".to_string(), + description: "Performs logical right shift on the low 32-bits value in register rs1 by the shift amount held in the lower 5 bits of register rs2 and produce 32-bit results and written to the destination register rd.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "sraw" => { + // Funct7 + instruction.binary = append_binary(instruction.binary, 0b0100000, 7); + + read_operands_riscv( + instruction, + vec![RegisterGP, RegisterGP, RegisterGP], + vec![1, 2, 3], + None, + Some(0b101), + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b0111011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "sraw rd, rs1, rs2".to_string(), + description: "Performs arithmetic right shift on the low 32-bits value in register rs1 by the shift amount held in the lower 5 bits of register rs2 and produce 32-bit results and written to the destination register rd.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "lwu" => { + read_operands_riscv( + instruction, + vec![RegisterGP, MemoryAddress], + vec![1, 3, 2], + None, + Some(0b110), + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b0000011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "lwu rd, offset(rs1)".to_string(), + description: "Loads a 32-bit value from memory and zero-extends this to 64 bits before storing it in register rd.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "ld" => { + read_operands_riscv( + instruction, + vec![RegisterGP, MemoryAddress], + vec![1, 3, 2], + None, + Some(0b011), + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b0000011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription { + syntax: "ld rd, offset(rs1)".to_string(), + description: "Loads a 64-bit value from memory into register rd for RV64I." + .to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "sd" => { + read_operands_riscv( + instruction, + vec![RegisterGP, MemoryAddress], + vec![1, 3, 2], + None, + Some(0b011), + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b0100011, 7); + + // Encoded as I-type, but needs reordering for S-type + instruction.binary = immediate_to_stored(instruction.binary); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription { + syntax: "sd rs2, offset(rs1)".to_string(), + description: "Store 64-bit, values from register rs2 to memory." + .to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + // Start of RV32M + "mul" => { + // Funct7 + instruction.binary = append_binary(instruction.binary, 0b0000001, 7); + + read_operands_riscv( + instruction, + vec![RegisterGP, RegisterGP, RegisterGP], + vec![1, 2, 3], + None, + Some(0b000), + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b0110011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "mul rd, rs1, rs2".to_string(), + description: "Performs an XLEN-bit * XLEN-bit multiplication of signed rs1 by signed rs2 and places the lower XLEN bits in the destination register.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "mulh" => { + // Funct7 + instruction.binary = append_binary(instruction.binary, 0b0000001, 7); + + read_operands_riscv( + instruction, + vec![RegisterGP, RegisterGP, RegisterGP], + vec![1, 2, 3], + None, + Some(0b001), + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b0110011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "mulh rd, rs1, rs2".to_string(), + description: "Performs an XLEN-bit * XLEN-bit multiplication of signed rs1 by signed rs2 and places the upper XLEN bits in the destination register. + ".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "mulhsu" => { + // Funct7 + instruction.binary = append_binary(instruction.binary, 0b0000001, 7); + + read_operands_riscv( + instruction, + vec![RegisterGP, RegisterGP, RegisterGP], + vec![1, 2, 3], + None, + Some(0b010), + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b0110011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "mulhsu rd, rs1, rs2".to_string(), + description: "Performs an XLEN-bit * XLEN-bit multiplication of signed rs1 by unsigned rs2 and places the upper XLEN bits in the destination register.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "mulhu" => { + // Funct7 + instruction.binary = append_binary(instruction.binary, 0b0000001, 7); + + read_operands_riscv( + instruction, + vec![RegisterGP, RegisterGP, RegisterGP], + vec![1, 2, 3], + None, + Some(0b011), + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b0110011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "mulhu rd, rs1, rs2".to_string(), + description: "Performs an XLEN-bit * XLEN-bit multiplication of unsigned rs1 by unsigned rs2 and places the upper XLEN bits in the destination register.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "div" => { + // Funct7 + instruction.binary = append_binary(instruction.binary, 0b0000001, 7); + + read_operands_riscv( + instruction, + vec![RegisterGP, RegisterGP, RegisterGP], + vec![1, 2, 3], + None, + Some(0b100), + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b0110011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "div rd, rs1, rs2".to_string(), + description: "Perform an XLEN bits by XLEN bits signed integer division of rs1 by rs2, rounding towards zero.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "divu" => { + // Funct7 + instruction.binary = append_binary(instruction.binary, 0b0000001, 7); + + read_operands_riscv( + instruction, + vec![RegisterGP, RegisterGP, RegisterGP], + vec![1, 2, 3], + None, + Some(0b101), + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b0110011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "divu rd, rs1, rs2".to_string(), + description: "Perform an XLEN bits by XLEN bits unsigned integer division of rs1 by rs2, rounding towards zero.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "rem" => { + // Funct7 + instruction.binary = append_binary(instruction.binary, 0b0000001, 7); + + read_operands_riscv( + instruction, + vec![RegisterGP, RegisterGP, RegisterGP], + vec![1, 2, 3], + None, + Some(0b110), + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b0110011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "rem rd, rs1, rs2".to_string(), + description: "Perform an XLEN bits by XLEN bits signed integer remainder of rs1 by rs2.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "remu" => { + // Funct7 + instruction.binary = append_binary(instruction.binary, 0b0000001, 7); + + read_operands_riscv( + instruction, + vec![RegisterGP, RegisterGP, RegisterGP], + vec![1, 2, 3], + None, + Some(0b111), + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b0110011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "remu rd, rs1, rs2".to_string(), + description: "Perform an XLEN bits by XLEN bits unsigned integer remainder of rs1 by rs2.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + // Start of RV64M + "mulw" => { + // Funct7 + instruction.binary = append_binary(instruction.binary, 0b0000001, 7); + + read_operands_riscv( + instruction, + vec![RegisterGP, RegisterGP, RegisterGP], + vec![1, 2, 3], + None, + Some(0b000), + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b0111011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "mulw rd, rs1, rs2".to_string(), + description: "Multiplies the lower 32 bits of the source registers, placing the sign-extension of the lower 32 bits of the result into the destination register.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "divw" => { + // Funct7 + instruction.binary = append_binary(instruction.binary, 0b0000001, 7); + + read_operands_riscv( + instruction, + vec![RegisterGP, RegisterGP, RegisterGP], + vec![1, 2, 3], + None, + Some(0b100), + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b0111011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "divw rd, rs1, rs2".to_string(), + description: "Perform an XLEN bits by XLEN bits signed integer division of rs1 by rs2.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "divuw" => { + // Funct7 + instruction.binary = append_binary(instruction.binary, 0b0000001, 7); + + read_operands_riscv( + instruction, + vec![RegisterGP, RegisterGP, RegisterGP], + vec![1, 2, 3], + None, + Some(0b101), + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b0111011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "divuw rd, rs1, rs2".to_string(), + description: "Perform an XLEN bits by XLEN bits unsigned integer division of rs1 by rs2.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "remw" => { + // Funct7 + instruction.binary = append_binary(instruction.binary, 0b0000001, 7); + + read_operands_riscv( + instruction, + vec![RegisterGP, RegisterGP, RegisterGP], + vec![1, 2, 3], + None, + Some(0b110), + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b0111011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "remw rd, rs1, rs2".to_string(), + description: "Perform an XLEN bits by XLEN bits signed integer remainder of rs1 by rs2.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "remuw" => { + // Funct7 + instruction.binary = append_binary(instruction.binary, 0b0000001, 7); + + read_operands_riscv( + instruction, + vec![RegisterGP, RegisterGP, RegisterGP], + vec![1, 2, 3], + None, + Some(0b111), + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b0111011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "remuw rd, rs1, rs2".to_string(), + description: "Perform an XLEN bits by XLEN bits unsigned integer reminder of rs1 by rs2.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + // Start of RV32F + "fmadd.s" => { + read_operands_riscv( + instruction, + vec![RegisterFP, RegisterFP, RegisterFP, RegisterFP], + vec![1, 2, 3, 4], + None, + Some(0b111), // This funct3 is used for the rm value + Some(0b00), + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b1000011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "fmadd.s rd, rs1, rs2, rs3".to_string(), + description: "Multiplies the values in rs1 and rs2, adds the value in rs3, and writes the final result to rd.\n\nFMADD.S computes (rs1 * rs2) + rs3".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "fmsub.s" => { + read_operands_riscv( + instruction, + vec![RegisterFP, RegisterFP, RegisterFP, RegisterFP], + vec![1, 2, 3, 4], + None, + Some(0b111), // This funct3 is used for the rm value + Some(0b00), + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b1000111, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "fmsub.s rd, rs1, rs2, rs3".to_string(), + description: "Multiplies the values in rs1 and rs2, subtracts the value in rs3, and writes the final result to rd.\n\nFMSUB.S computes (rs1×rs2) - rs3.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "fnmsub.s" => { + read_operands_riscv( + instruction, + vec![RegisterFP, RegisterFP, RegisterFP, RegisterFP], + vec![1, 2, 3, 4], + None, + Some(0b111), // This funct3 is used for the rm value + Some(0b00), + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b1001011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "fnmsub.s rd, rs1, rs2, rs3".to_string(), + description: "Multiplies the values in rs1 and rs2, negates the product, adds the value in rs3, and writes the final result to rd.\n\nFNMSUB.S computes -(rs1 * rs2) + rs3.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "fnmadd.s" => { + read_operands_riscv( + instruction, + vec![RegisterFP, RegisterFP, RegisterFP, RegisterFP], + vec![1, 2, 3, 4], + None, + Some(0b111), // This funct3 is used for the rm value + Some(0b00), + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b1001111, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "fnmadd.s rd, rs1, rs2, rs3".to_string(), + description: "multiplies the values in rs1 and rs2, negates the product, subtracts the value in rs3, and writes the final result to rd.\n\nFNMADD.S computes -(rs1 * rs2) - rs3.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "fadd.s" => { + // Funct5 + fmt + instruction.binary = append_binary(instruction.binary, 0b0000000, 7); + + read_operands_riscv( + instruction, + vec![RegisterFP, RegisterFP, RegisterFP], + vec![1, 2, 3], + None, + Some(0b111), // This funct3 is used for the rm value + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b1010011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription { + syntax: "fadd.s rd, rs1, rs2".to_string(), + description: "Perform single-precision floating-point addition." + .to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "fsub.s" => { + // Funct5 + fmt + instruction.binary = append_binary(instruction.binary, 0b0000100, 7); + + read_operands_riscv( + instruction, + vec![RegisterFP, RegisterFP, RegisterFP], + vec![1, 2, 3], + None, + Some(0b111), // This funct3 is used for the rm value + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b1010011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription { + syntax: "fsub.s rd, rs1, rs2".to_string(), + description: "Perform single-precision floating-point substraction." + .to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "fmul.s" => { + // Funct5 + fmt + instruction.binary = append_binary(instruction.binary, 0b0001000, 7); + + read_operands_riscv( + instruction, + vec![RegisterFP, RegisterFP, RegisterFP], + vec![1, 2, 3], + None, + Some(0b111), // This funct3 is used for the rm value + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b1010011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription { + syntax: "fmul.s rd, rs1, rs2".to_string(), + description: "Perform single-precision floating-point multiplication." + .to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "fdiv.s" => { + // Funct5 + fmt + instruction.binary = append_binary(instruction.binary, 0b0001100, 7); + + read_operands_riscv( + instruction, + vec![RegisterFP, RegisterFP, RegisterFP], + vec![1, 2, 3], + None, + Some(0b111), // This funct3 is used for the rm value + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b1010011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription { + syntax: "fdiv.s rd, rs1, rs2".to_string(), + description: "Perform single-precision floating-point division." + .to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "fsqrt.s" => { + // Funct5 + fmt + padding for absent register + instruction.binary = append_binary(instruction.binary, 0b010110000000, 12); + + read_operands_riscv( + instruction, + vec![RegisterFP, RegisterFP], + vec![1, 2], + None, + Some(0b111), // This funct3 is used for the rm value + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b1010011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription { + syntax: "fsqrt.s rd, rs1".to_string(), + description: "Perform single-precision square root.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "fsgnj.s" => { + // Funct5 + fmt + instruction.binary = append_binary(instruction.binary, 0b0010000, 7); + + read_operands_riscv( + instruction, + vec![RegisterFP, RegisterFP, RegisterFP], + vec![1, 2, 3], + None, + Some(0b000), // This funct3 is used for the rm value + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b1010011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "fsgnj.s rd, rs1, rs2".to_string(), + description: "Produce a result that takes all bits except the sign bit from rs1.\n\nThe result's sign bit is rs2's sign bit.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "fsgnjn.s" => { + // Funct5 + fmt + instruction.binary = append_binary(instruction.binary, 0b0010000, 7); + + read_operands_riscv( + instruction, + vec![RegisterFP, RegisterFP, RegisterFP], + vec![1, 2, 3], + None, + Some(0b001), // This funct3 is used for the rm value + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b1010011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "fsgnjn.s rd, rs1, rs2".to_string(), + description: "Produce a result that takes all bits except the sign bit from rs1.\n\nThe result's sign bit is opposite of rs2's sign bit.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "fsgnjx.s" => { + // Funct5 + fmt + instruction.binary = append_binary(instruction.binary, 0b0010000, 7); + + read_operands_riscv( + instruction, + vec![RegisterFP, RegisterFP, RegisterFP], + vec![1, 2, 3], + None, + Some(0b010), // This funct3 is used for the rm value + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b1010011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "fsgnjx.s rd, rs1, rs2".to_string(), + description: "Produce a result that takes all bits except the sign bit from rs1.\n\nThe result's sign bit is XOR of sign bit of rs1 and rs2.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "fmin.s" => { + // Funct5 + fmt + instruction.binary = append_binary(instruction.binary, 0b0010100, 7); + + read_operands_riscv( + instruction, + vec![RegisterFP, RegisterFP, RegisterFP], + vec![1, 2, 3], + None, + Some(0b000), // This funct3 is used for the rm value + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b1010011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription { + syntax: "fmin.s rd, rs1, rs2".to_string(), + description: + "Write the smaller of single precision data in rs1 and rs2 to rd." + .to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "fmax.s" => { + // Funct5 + fmt + instruction.binary = append_binary(instruction.binary, 0b0010100, 7); + + read_operands_riscv( + instruction, + vec![RegisterFP, RegisterFP, RegisterFP], + vec![1, 2, 3], + None, + Some(0b001), // This funct3 is used for the rm value + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b1010011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription { + syntax: "fmax.s rd, rs1, rs2".to_string(), + description: + "Write the larger of single precision data in rs1 and rs2 to rd." + .to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "fcvt.w.s" => { + // Funct5 + fmt + padding for absent register + instruction.binary = append_binary(instruction.binary, 0b110000000000, 12); + + read_operands_riscv( + instruction, + vec![RegisterGP, RegisterFP], + vec![1, 2], + None, + Some(0b111), // This funct3 is used for the rm value + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b1010011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "fcvt.w.s rd, rs1".to_string(), + description: "Convert a floating-point number in floating-point register rs1 to a signed 32-bit in integer register rd.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "fcvt.wu.s" => { + // Funct5 + fmt + rs2 + instruction.binary = append_binary(instruction.binary, 0b110000000001, 12); + + read_operands_riscv( + instruction, + vec![RegisterGP, RegisterFP], + vec![1, 2], + None, + Some(0b111), // This funct3 is used for the rm value + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b1010011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "fcvt.wu.s rd, rs1".to_string(), + description: "Convert a floating-point number in floating-point register rs1 to a signed 32-bit in unsigned integer register rd.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "fmv.x.w" => { + // Funct5 + fmt + rs2 + instruction.binary = append_binary(instruction.binary, 0b111000000000, 12); + + read_operands_riscv( + instruction, + vec![RegisterGP, RegisterFP], + vec![1, 2], + None, + Some(0b000), // This funct3 is used for the rm value + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b1010011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "fmv.x.w rd, rs1".to_string(), + description: "Move the single-precision value in floating-point register rs1 represented in IEEE 754-2008 encoding to the lower 32 bits of integer register rd.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "feq.s" => { + // Funct5 + fmt + instruction.binary = append_binary(instruction.binary, 0b1010000, 7); + + read_operands_riscv( + instruction, + vec![RegisterGP, RegisterFP, RegisterFP], + vec![1, 2, 3], + None, + Some(0b010), // This funct3 is used for the rm value + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b1010011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "feq.s rd, rs1, rs2".to_string(), + description: "Performs a quiet equal comparison between floating-point registers rs1 and rs2 and record the Boolean result in integer register rd.\n\nOnly signaling NaN inputs cause an Invalid Operation exception.\n\nThe result is 0 if either operand is NaN.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "flt.s" => { + // Funct5 + fmt + instruction.binary = append_binary(instruction.binary, 0b1010000, 7); + + read_operands_riscv( + instruction, + vec![RegisterGP, RegisterFP, RegisterFP], + vec![1, 2, 3], + None, + Some(0b001), // This funct3 is used for the rm value + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b1010011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "flt.s rd, rs1, rs2".to_string(), + description: "Performs a quiet less comparison between floating-point registers rs1 and rs2 and record the Boolean result in integer register rd.\n\nOnly signaling NaN inputs cause an Invalid Operation exception.\n\nThe result is 0 if either operand is NaN.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "fle.s" => { + // Funct5 + fmt + instruction.binary = append_binary(instruction.binary, 0b1010000, 7); + + read_operands_riscv( + instruction, + vec![RegisterGP, RegisterFP, RegisterFP], + vec![1, 2, 3], + None, + Some(0b000), // This funct3 is used for the rm value + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b1010011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "fle.s rd, rs1, rs2".to_string(), + description: "Performs a quiet less or equal comparison between floating-point registers rs1 and rs2 and record the Boolean result in integer register rd.\n\nOnly signaling NaN inputs cause an Invalid Operation exception.\n\nThe result is 0 if either operand is NaN.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "fclass.s" => { + // Funct5 + fmt + rs2 + instruction.binary = append_binary(instruction.binary, 0b111000000000, 12); + + read_operands_riscv( + instruction, + vec![RegisterGP, RegisterFP], + vec![1, 2], + None, + Some(0b001), // This funct3 is used for the rm value + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b1010011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "fclass.s rd, rs1".to_string(), + description: "Examines the value in floating-point register rs1 and writes to integer register rd a 10-bit mask that indicates the class of the floating-point number.\n\nThe corresponding bit in rd will be set if the property is true and clear otherwise. All other bits in rd are cleared.\n\nNote that exactly one bit in rd will be set.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "fcvt.s.w" => { + // Funct5 + fmt + rs2 + instruction.binary = append_binary(instruction.binary, 0b110100000000, 12); + + read_operands_riscv( + instruction, + vec![RegisterFP, RegisterGP], + vec![1, 2], + None, + Some(0b111), // This funct3 is used for the rm value + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b1010011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "fcvt.s.w rd, rs1".to_string(), + description: "Converts a 32-bit signed integer, in integer register rs1 into a floating-point number in floating-point register rd.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "fcvt.s.wu" => { + // Funct5 + fmt + rs2 + instruction.binary = append_binary(instruction.binary, 0b110100000001, 12); + + read_operands_riscv( + instruction, + vec![RegisterFP, RegisterGP], + vec![1, 2], + None, + Some(0b111), // This funct3 is used for the rm value + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b1010011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "fcvt.s.wu rd, rs1".to_string(), + description: "Converts a 32-bit unsigned integer, in integer register rs1 into a floating-point number in floating-point register rd.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "fmv.w.x" => { + // Funct5 + fmt + rs2 + instruction.binary = append_binary(instruction.binary, 0b111100000000, 12); + + read_operands_riscv( + instruction, + vec![RegisterFP, RegisterGP], + vec![1, 2], + None, + Some(0b000), // This funct3 is used for the rm value + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b1010011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "fmv.w.x rd, rs1".to_string(), + description: "Move the single-precision value encoded in IEEE 754-2008 standard encoding from the lower 32 bits of integer register rs1 to the floating-point register rd.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "fmadd.d" => { + read_operands_riscv( + instruction, + vec![RegisterFP, RegisterFP, RegisterFP, RegisterFP], + vec![1, 2, 3, 4], + None, + Some(0b111), // This funct3 is used for the rm value + Some(0b01), + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b1000011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "fmadd.d rd, rs1, rs2, rs3".to_string(), + description: "Multiplies the values in rs1 and rs2, adds the value in rs3, and writes the final result tord.\n\nFMADD.D computes (rs1 * rs2) + rs3.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "fmsub.d" => { + read_operands_riscv( + instruction, + vec![RegisterFP, RegisterFP, RegisterFP, RegisterFP], + vec![1, 2, 3, 4], + None, + Some(0b111), // This funct3 is used for the rm value + Some(0b01), + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b1000111, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "fmsub.d rd, rs1, rs2, rs3".to_string(), + description: "Multiplies the values in rs1 and rs2, subtracts the value in rs3, and writes the final result to rd. FMSUB.D computes (rs1 * rs2) - rs3".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "fnmsub.d" => { + read_operands_riscv( + instruction, + vec![RegisterFP, RegisterFP, RegisterFP, RegisterFP], + vec![1, 2, 3, 4], + None, + Some(0b111), // This funct3 is used for the rm value + Some(0b01), + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b1001011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "fnmsub.d rd, rs1, rs2, rs3".to_string(), + description: "Multiplies the values in rs1 and rs2, negates the product, adds the value in rs3, and writes the final result to rd.\n\nFNMSUB.D computes -(rs1 * rs2) + rs3.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "fnmadd.d" => { + read_operands_riscv( + instruction, + vec![RegisterFP, RegisterFP, RegisterFP, RegisterFP], + vec![1, 2, 3, 4], + None, + Some(0b111), // This funct3 is used for the rm value + Some(0b01), + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b1001111, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "fnmadd.d rd, rs1, rs2, rs3".to_string(), + description: "Multiplies the values in rs1 and rs2, negates the product, subtracts the value in rs3, and writes the final result to rd.\n\nFNMADD.D computes -(rs1 * rs2) - rs3".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "fadd.d" => { + // Funct5 + fmt + instruction.binary = append_binary(instruction.binary, 0b0000001, 7); + + read_operands_riscv( + instruction, + vec![RegisterFP, RegisterFP, RegisterFP], + vec![1, 2, 3], + None, + Some(0b111), // This funct3 is used for the rm value + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b1010011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription { + syntax: "fadd.d rd, rs1, rs2".to_string(), + description: "Perform single-precision floating-point addition." + .to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "fsub.d" => { + // Funct5 + fmt + instruction.binary = append_binary(instruction.binary, 0b0000101, 7); + + read_operands_riscv( + instruction, + vec![RegisterFP, RegisterFP, RegisterFP], + vec![1, 2, 3], + None, + Some(0b111), // This funct3 is used for the rm value + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b1010011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription { + syntax: "fsub.d rd, rs1, rs2".to_string(), + description: "Perform single-precision floating-point subtraction." + .to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "fmul.d" => { + // Funct5 + fmt + instruction.binary = append_binary(instruction.binary, 0b0001001, 7); + + read_operands_riscv( + instruction, + vec![RegisterFP, RegisterFP, RegisterFP], + vec![1, 2, 3], + None, + Some(0b111), // This funct3 is used for the rm value + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b1010011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription { + syntax: "fmul.d rd, rs1, rs2".to_string(), + description: "Perform single-precision floating-point multiplication." + .to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "fdiv.d" => { + // Funct5 + fmt + instruction.binary = append_binary(instruction.binary, 0b0001101, 7); + + read_operands_riscv( + instruction, + vec![RegisterFP, RegisterFP, RegisterFP], + vec![1, 2, 3], + None, + Some(0b111), // This funct3 is used for the rm value + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b1010011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription { + syntax: "fdiv.d rd, rs1, rs2".to_string(), + description: "Perform single-precision floating-point division." + .to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "fsqrt.d" => { + // Funct5 + fmt + rs2 + instruction.binary = append_binary(instruction.binary, 0b010110100000, 12); + + read_operands_riscv( + instruction, + vec![RegisterFP, RegisterFP], + vec![1, 2], + None, + Some(0b111), // This funct3 is used for the rm value + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b1010011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription { + syntax: "fsqrt.d rd, rs1".to_string(), + description: "Perform single-precision square root.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "fsgnj.d" => { + // Funct5 + fmt + instruction.binary = append_binary(instruction.binary, 0b0010001, 7); + + read_operands_riscv( + instruction, + vec![RegisterFP, RegisterFP, RegisterFP], + vec![1, 2, 3], + None, + Some(0b000), // This funct3 is used for the rm value + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b1010011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "fsgnj.d rd, rs1, rs2".to_string(), + description: "Produce a result that takes all bits except the sign bit from rs1.\n\nThe result's sign bit is rs2's sign bit.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "fsgnjn.d" => { + // Funct5 + fmt + instruction.binary = append_binary(instruction.binary, 0b0010001, 7); + + read_operands_riscv( + instruction, + vec![RegisterFP, RegisterFP, RegisterFP], + vec![1, 2, 3], + None, + Some(0b001), // This funct3 is used for the rm value + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b1010011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "fsgnjn.d rd, rs1, rs2".to_string(), + description: "Produce a result that takes all bits except the sign bit from rs1.\n\nThe result's sign bit is opposite of rs2's sign bit.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "fsgnjx.d" => { + // Funct5 + fmt + instruction.binary = append_binary(instruction.binary, 0b0010001, 7); + + read_operands_riscv( + instruction, + vec![RegisterFP, RegisterFP, RegisterFP], + vec![1, 2, 3], + None, + Some(0b010), // This funct3 is used for the rm value + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b1010011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "fsgnjx.d rd, rs1, rs2".to_string(), + description: "Produce a result that takes all bits except the sign bit from rs1.\n\nThe result's sign bit is XOR of sign bit of rs1 and rs2.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "fmin.d" => { + // Funct5 + fmt + instruction.binary = append_binary(instruction.binary, 0b0010101, 7); + + read_operands_riscv( + instruction, + vec![RegisterFP, RegisterFP, RegisterFP], + vec![1, 2, 3], + None, + Some(0b000), // This funct3 is used for the rm value + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b1010011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription { + syntax: "fmin.d rd, rs1, rs2".to_string(), + description: + "Write the smaller of single precision data in rs1 and rs2 to rd." + .to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "fmax.d" => { + // Funct5 + fmt + instruction.binary = append_binary(instruction.binary, 0b0010101, 7); + + read_operands_riscv( + instruction, + vec![RegisterFP, RegisterFP, RegisterFP], + vec![1, 2, 3], + None, + Some(0b001), // This funct3 is used for the rm value + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b1010011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription { + syntax: "fmax.d rd, rs1, rs2".to_string(), + description: + "Write the larger of single precision data in rs1 and rs2 to rd." + .to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "fcvt.s.d" => { + // Funct5 + fmt + rs2 + instruction.binary = append_binary(instruction.binary, 0b010000000001, 12); + + read_operands_riscv( + instruction, + vec![RegisterFP, RegisterFP], + vec![1, 2], + None, + Some(0b111), // This funct3 is used for the rm value + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b1010011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "fcvt.s.d rd, rs1".to_string(), + description: "Converts double floating-point register in rs1 into a floating-point number in floating-point register rd.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "fcvt.d.s" => { + // Funct5 + fmt + rs2 + instruction.binary = append_binary(instruction.binary, 0b010000100000, 12); + + read_operands_riscv( + instruction, + vec![RegisterFP, RegisterFP], + vec![1, 2], + None, + Some(0b111), // This funct3 is used for the rm value + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b1010011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "fcvt.d.s rd, rs1".to_string(), + description: "Converts single floating-point register in rs1 into a double floating-point number in floating-point register rd.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "feq.d" => { + // Funct5 + fmt + instruction.binary = append_binary(instruction.binary, 0b1010001, 7); + + read_operands_riscv( + instruction, + vec![RegisterGP, RegisterFP, RegisterFP], + vec![1, 2, 3], + None, + Some(0b010), // This funct3 is used for the rm value + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b1010011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "feq.d rd, rs1, rs2".to_string(), + description: "Performs a quiet equal comparison between floating-point registers rs1 and rs2 and record the Boolean result in integer register rd.\n\nOnly signaling NaN inputs cause an Invalid Operation exception.\n\nThe result is 0 if either operand is NaN.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "flt.d" => { + // Funct5 + fmt + instruction.binary = append_binary(instruction.binary, 0b1010001, 7); + + read_operands_riscv( + instruction, + vec![RegisterGP, RegisterFP, RegisterFP], + vec![1, 2, 3], + None, + Some(0b001), // This funct3 is used for the rm value + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b1010011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "flt.d rd, rs1, rs2".to_string(), + description: "Performs a quiet less comparison between floating-point registers rs1 and rs2 and record the Boolean result in integer register rd.\n\nOnly signaling NaN inputs cause an Invalid Operation exception.\n\nThe result is 0 if either operand is NaN.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "fle.d" => { + // Funct5 + fmt + instruction.binary = append_binary(instruction.binary, 0b1010001, 7); + + read_operands_riscv( + instruction, + vec![RegisterGP, RegisterFP, RegisterFP], + vec![1, 2, 3], + None, + Some(0b000), // This funct3 is used for the rm value + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b1010011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "fle.d rd, rs1, rs2".to_string(), + description: "Performs a quiet less or equal comparison between floating-point registers rs1 and rs2 and record the Boolean result in integer register rd.\n\nOnly signaling NaN inputs cause an Invalid Operation exception.\n\nThe result is 0 if either operand is NaN.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "fclass.d" => { + // Funct5 + fmt + rs2 + instruction.binary = append_binary(instruction.binary, 0b111000100000, 12); + + read_operands_riscv( + instruction, + vec![RegisterGP, RegisterFP], + vec![1, 2], + None, + Some(0b001), // This funct3 is used for the rm value + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b1010011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "fclass.d rd, rs1".to_string(), + description: "Examines the value in floating-point register rs1 and writes to integer register rd a 10-bit mask that indicates the class of the floating-point number.\n\nThe corresponding bit in rd will be set if the property is true and clear otherwise.\nAll other bits in rd are cleared.\n\nNote that exactly one bit in rd will be set.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "fcvt.w.d" => { + // Funct5 + fmt + rs2 + instruction.binary = append_binary(instruction.binary, 0b110000100000, 12); + + read_operands_riscv( + instruction, + vec![RegisterGP, RegisterFP], + vec![1, 2], + None, + Some(0b111), // This funct3 is used for the rm value + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b1010011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "fcvt.w.d rd, rs1".to_string(), + description: "Converts a double-precision floating-point number in floating-point register rs1 to a signed 32-bit integer, in integer register rd.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "fcvt.wu.d" => { + // Funct5 + fmt + rs2 + instruction.binary = append_binary(instruction.binary, 0b110000100001, 12); + + read_operands_riscv( + instruction, + vec![RegisterGP, RegisterFP], + vec![1, 2], + None, + Some(0b111), // This funct3 is used for the rm value + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b1010011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "fcvt.wu.d rd, rs1".to_string(), + description: "Converts a double-precision floating-point number in floating-point register rs1 to a unsigned 32-bit integer, in integer register rd.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "fcvt.d.w" => { + // Funct5 + fmt + rs2 + instruction.binary = append_binary(instruction.binary, 0b110100100000, 12); + + read_operands_riscv( + instruction, + vec![RegisterFP, RegisterGP], + vec![1, 2], + None, + Some(0b111), // This funct3 is used for the rm value + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b1010011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "fcvt.d.w rd, rs1".to_string(), + description: "Converts a 32-bit signed integer, in integer register rs1 into a double-precision floating-point number in floating-point register rd.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "fcvt.d.wu" => { + // Funct5 + fmt + rs2 + instruction.binary = append_binary(instruction.binary, 0b110100100001, 12); + + read_operands_riscv( + instruction, + vec![RegisterFP, RegisterGP], + vec![1, 2], + None, + Some(0b111), // This funct3 is used for the rm value + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b1010011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "fcvt.d.wu rd, rs1".to_string(), + description: "Converts a 32-bit unsigned integer, in integer register rs1 into a double-precision floating-point number in floating-point register rd.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "flw" => { + read_operands_riscv( + instruction, + vec![RegisterFP, MemoryAddress], + vec![1, 3, 2], + None, + Some(0b010), + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b0000111, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "flw rd, offset(rs1)".to_string(), + description: "Load a single-precision floating-point value from memory into floating-point register rd.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "fsw" => { + read_operands_riscv( + instruction, + vec![RegisterFP, MemoryAddress], + vec![1, 3, 2], + None, + Some(0b010), + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b0100111, 7); + + // Encoded as I-type, but needs reordering for S-type + instruction.binary = immediate_to_stored(instruction.binary); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "fsw rs2, offset(rs1)".to_string(), + description: "Store a single-precision value from floating-point register rs2 to memory.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "fld" => { + read_operands_riscv( + instruction, + vec![RegisterFP, MemoryAddress], + vec![1, 3, 2], + None, + Some(0b011), + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b0000111, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "fld rd, rs1, offset".to_string(), + description: "Load a double-precision floating-point value from memory into floating-point register rd.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "fsd" => { + read_operands_riscv( + instruction, + vec![RegisterFP, MemoryAddress], + vec![1, 3, 2], + None, + Some(0b011), + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b0100111, 7); + + // Encoded as I-type, but needs reordering for S-type + instruction.binary = immediate_to_stored(instruction.binary); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "fsd rs2, offset(rs1)".to_string(), + description: "Store a double-precision value from the floating-point registers to memory.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "fcvt.l.s" => { + // Funct5 + fmt + rs2 + instruction.binary = append_binary(instruction.binary, 0b110000000010, 12); + + read_operands_riscv( + instruction, + vec![RegisterGP, RegisterFP], + vec![1, 2], + None, + Some(0b111), // This funct3 is used for the rm value + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b1010011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "fcvt.l.s rd, rs1".to_string(), + description: "Converts the floating-point value in register rs1 (interpreted as a single-precision floating-point number) to a signed 32-bit integer.\n\nThe result is then stored in integer register rd.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "fcvt.lu.s" => { + // Funct5 + fmt + rs2 + instruction.binary = append_binary(instruction.binary, 0b110000000011, 12); + + read_operands_riscv( + instruction, + vec![RegisterGP, RegisterFP], + vec![1, 2], + None, + Some(0b111), // This funct3 is used for the rm value + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b1010011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "fcvt.lu.s rd, rs1".to_string(), + description: "This instruction converts the floating-point value in register rs1 (interpreted as a single-precision floating-point number) to an unsigned 32-bit integer.\n\nThe result is then stored in integer register rd.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "fcvt.s.l" => { + // Funct5 + fmt + rs2 + instruction.binary = append_binary(instruction.binary, 0b110100000010, 12); + + read_operands_riscv( + instruction, + vec![RegisterFP, RegisterGP], + vec![1, 2], + None, + Some(0b111), // This funct3 is used for the rm value + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b1010011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "fcvt.s.l rd, rs1".to_string(), + description: "Converts the signed 32-bit integer value in register rs1 to a single-precision floating-point number.\n\nThe result is then stored in floating-point register rd.".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + "fcvt.s.lu" => { + // Funct5 + fmt + rs2 + instruction.binary = append_binary(instruction.binary, 0b110100000011, 12); + + read_operands_riscv( + instruction, + vec![RegisterFP, RegisterGP], + vec![1, 2], + None, + Some(0b111), // This funct3 is used for the rm value + None, + ); + + // Opcode + instruction.binary = append_binary(instruction.binary, 0b1010011, 7); + + //Pseudo-instructions already have text in mouse_hover_string so we check if there's text there already before adding in the blurb + if monaco_line_info[instruction.line_number] + .mouse_hover_string + .is_empty() + { + let info = InstructionDescription{ + syntax: "fcvt.s.lu rd, rs1".to_string(), + description: "Converts a single-precision floating-point value to an unsigned 32-bit integer.\n\nTakes the value in a floating-point register (f-register), performs the conversion, and stores the result in an integer register (x-register).".to_string(), + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + } + } + _ => { + if UNSUPPORTED_INSTRUCTIONS_RISCV.contains(&&*instruction.operator.token_name) { + instruction.errors.push(Error { + error_name: UnsupportedInstruction, + token_causing_error: instruction.operator.token_name.to_string(), + start_end_columns: instruction.operator.start_end_columns, + message: "\n\n".to_string(), + }) + } else { + instruction.errors.push(Error { + error_name: UnrecognizedInstruction, + token_causing_error: instruction.operator.token_name.clone(), + start_end_columns: instruction.operator.start_end_columns, + message: "\n\n".to_string(), + }); + } + } + } + } +} + +/// Reorder store (S-type) instructions from I-type to the correct S-type format +fn immediate_to_stored(mut bin: u32) -> u32 { + // Extract bits 24-20 from the first segment + let lower_imm = (bin >> 20) & 0b11111; + + // Extract bits 11-7 from the second segment + let rs2 = (bin >> 7) & 0b11111; + + // Clear bits 24-20 and 11-7 + bin &= !((0b11111 << 20) | (0b111111 << 6)); + + // Move bits 24-20 to positions 11-7 + let moved_imm = lower_imm << 7; + + // Move bits 11-7 to positions 24-20 + let moved_rs2 = rs2 << 20; + + // Combine the manipulated bits + bin |= moved_imm | moved_rs2; + + bin +} + +// Converts an I-type instruction to B-type instruction +// This is currently not used as B-type instructions concatenate the least significant bit +// We need this bit because we are referencing the instruction index, not a memory address +// and therefore, not always an even number +fn _immediate_to_branch(mut bin: u32) -> u32 { + // Extract bits imm[4:1] from the immediate, last bit is ignored + let lower_imm = (bin >> 21) & 0b1111; + + // Extract imm[10:5] + let upper_imm = (bin >> 25) & 0b111111; + + // Extract bit 11 and bit 12 + let bit_11 = (bin >> 30) & 0b1; + let bit_12 = (bin >> 31) & 0b1; + + // Extract rs1 and rs2 + let rs1 = (bin >> 7) & 0b11111; + let rs2 = (bin >> 15) & 0b11111; + + // Clear bits 24-20, rs1, and rs2 + bin &= !((0b11111 << 20) | (0b11111 << 15) | (0b11111 << 7)); + + // Move bits imm[4:1] to positions 10-8 + let moved_lower = lower_imm << 8; + + let moved_upper = upper_imm << 25; + + let moved_bit_11 = bit_11 << 7; + + let moved_bit_12 = bit_12 << 31; + + // Move rs2 to positions 24-20 + let moved_rs2 = rs2 << 20; + + // Move rs1 to positions 15-19 + let moved_rs1 = rs1 << 15; + + // Combine the manipulated bits + bin |= moved_bit_12 | moved_upper | moved_rs2 | moved_rs1 | moved_lower | moved_bit_11; + + bin +} + +/// Reorder the immediate value to comply with J-type format +/// This is currently not used as J-type instructions concatenate the least significant bit +/// We need this bit because we are referencing the instruction index, not a memory address +/// and therefore, not always an even number +fn _upper_to_jump(mut bin: u32) -> u32 { + // Extract bits immediate + let imm = bin >> 12; + + // Extract bits imm[1-10] + let lower_imm = (imm >> 1) & 0b1111111111; + + // Extracts bits imm[12:19] + let upper_imm = imm >> 12; + + // Extract bit imm[11] + let bit_11 = (imm >> 11) & 1; + + // Extract bit imm[20] + let bit_20 = (imm >> 20) & 1; + + // Clear bits [12-31] + bin &= !(0b11111111111111111111 << 12); + + bin |= (upper_imm << 12) | (bit_11 << 20) | (lower_imm << 21) | (bit_20 << 31); + + bin +} + +///This function takes two numbers and inserts the binary of the second at a given index in the binary of the first. +///All binary values at and past the insertion index of the original string will be moved to the end of the resultant string. +///Since binary is sign extended on the left to 32 bits, insertion index must be the index from the end of the string. +pub fn place_binary_in_middle_of_another( + wrapper: u32, + middle: u32, + middle_length: usize, + index_from_right: usize, +) -> u32 { + //Step 1: Remove End Bits from Wrapper to make New Binary + //Step 2: Move New Binary Left By length of Middle + //Step 3: Or with Middle + //Step 4: Move New Binary Left by length of End + //Step 5: Shift Wrapper Left 32 - Length of End to get End + //Step 6: Shift End Right by 32 - Length of End + //Step 7: Or with New Binary + //Step 8: Return New Binary + let end_length = index_from_right + 1; + let mut new_binary = wrapper >> end_length; + new_binary <<= middle_length; + new_binary |= middle; + new_binary <<= end_length; + let mut end = wrapper << (32 - end_length); + end >>= 32 - end_length; + new_binary |= end; + new_binary +} + +///Append binary takes two numbers, shifts the first by a specified amount and then bitwise ors the +/// two numbers together effectively appending the second onto the first. +pub fn append_binary(mut first: u32, mut second: u32, shift_amount: u8) -> u32 { + second <<= 32 - shift_amount; + second >>= 32 - shift_amount; + first <<= shift_amount; + first |= second; + first +} + +///returns the address of the labelled main instruction. If none exists, returns address of labelled start instruction. +///Otherwise returns 0. +pub fn determine_pc_starting_point(labels: HashMap) -> usize { + return match labels.get("main") { + Some(main_address) => *main_address, + None => match labels.get("start") { + Some(start_address) => *start_address, + None => 0, + }, + }; +} + +///Creates a vector of u32 from the data found in the parser / assembler to put into memory. +pub fn create_binary_vec( + instructions: Vec, + mut vec_of_data: Vec, +) -> (Vec, usize) { + //push all instructions + let mut binary: Vec = Vec::new(); + for instruction in instructions { + binary.push(instruction.binary); + } + + let data_starting_point = binary.len(); //makes sure the byte array length is a multiple of 4 let mut mod4 = 4 - (vec_of_data.len() % 4); @@ -1563,5 +5127,5 @@ pub fn create_binary_vec(instructions: Vec, mut vec_of_data: Vec, pub data: Vec, pub pc_starting_point: usize, + pub data_starting_point: usize, } #[derive(Clone, Debug, Default, Eq, PartialEq)] @@ -127,8 +130,11 @@ impl Instruction { for operand in &self.operands { recreated_string.push_str(&format!(" {},", operand.token_name.clone())); } + //pop the extra comma - recreated_string.pop(); + if recreated_string.ends_with(',') { + recreated_string.pop(); + } recreated_string } @@ -145,6 +151,23 @@ pub struct Data { pub data_entries: Vec, } +impl Data { + ///Takes the operator, operands, and label(optional) associated with an instruction and recreates the string version + pub fn recreate_string(&self) -> String { + let mut recreated_string = "".to_string(); + recreated_string.push_str(&format!("{}: ", self.label.clone().token_name)); + recreated_string.push_str(&self.data_type.token_name.to_string()); + + for token in &self.data_entries { + recreated_string.push_str(&format!(" {},", token.token_name.clone())); + } + //pop the extra comma + recreated_string.pop(); + + recreated_string + } +} + #[derive(Clone, Debug, Default, Eq, PartialEq)] pub struct LabelInstance { pub token_line: usize, @@ -215,6 +238,7 @@ pub enum ErrorType { NonASCIIString, //One or multiple characters within the given string cannot be represented in ASCII NonASCIIChar, //The given char cannot be represented in ASCII JALRRDRegisterZero, //The destination address for JALR cannot be the zero register + IncorrectImmediateValue, //Ensure immediate value for li is valid } impl fmt::Display for ErrorType { @@ -228,13 +252,15 @@ pub enum OperandType { RegisterGP, RegisterFP, Immediate, + UpperImmediate, MemoryAddress, LabelAbsolute, LabelRelative, ShiftAmount, } -pub const SUPPORTED_INSTRUCTIONS: [&str; 64] = [ +pub const SUPPORTED_INSTRUCTIONS_MIPS: [&str; 64] = [ + // MIPS Instructions "add", "add.d", "add.s", "addi", "addiu", "addu", "and", "andi", "aui", "b", "bc1f", "bc1t", "beq", "bne", "c.eq.d", "c.eq.s", "c.le.d", "c.le.s", "c.lt.d", "c.lt.s", "c.nge.d", "c.nge.s", "c.ngt.d", "c.ngt.s", "dadd", "daddi", "daddiu", "daddu", "dahi", "dati", "ddiv", "ddivu", @@ -243,7 +269,147 @@ pub const SUPPORTED_INSTRUCTIONS: [&str; 64] = [ "sll", "slt", "sltu", "sub", "sub.d", "sub.s", "sw", "swc1", ]; -pub const UNSUPPORTED_INSTRUCTIONS: [&str; 408] = [ +pub const SUPPORTED_INSTRUCTIONS_RISCV: [&str; 131] = [ + // RV32I + "lui", + "auipc", + "addi", + "slti", + "xori", + "ori", + "andi", + "slli", + "srli", + "srai", + "add", + "sub", + "sll", + "slt", + "sltu", + "cor", + "srl", + "sra", + "or", + "and", + "fence", + "fence.i", + "csrrw", + "csrrs", + "csrrc", + "csrrwi", + "csrrsi", + "csrrci", + "ecall", + "ebreak", + "uret", + "sret", + "mret", + "wfi", + "sfence.vma", + "lb", + "lh", + "lw", + "lbu", + "lhu", + "sb", + "sh", + "sw", + "jal", + "jalr", + "beq", + "bne", + "blt", + "bge", + "bltu", + "bgeu", + // RV64I + "addiw", + "slliw", + "srliw", + "addw", + "subw", + "sllw", + "srlw", + "sraw", + "lwu", + "ld", + "sd", + // RV32M + "mul", + "mulh", + "mulhsu", + "mulhu", + "div", + "divu", + "rem", + "remu", + // RV64M + "mulw", + "divw", + "divuw", + "remw", + "remuw", + // RV32F + "fmadd.s", + "fmsub.s", + "fnmsub.s", + "fnmadd.s", + "fadd.s", + "fsub.s", + "fmul.s", + "fdiv.s", + "fsqrt.s", + "fsgnj.s", + "fsgnjn.s", + "fsgnjx.s", + "fmin.s", + "fmax.s", + "fcvt.w.s", + "fcvt.wu.s", + "fmv.x.w", + "feq.s", + "flt.s", + "fle.s", + "fclass.s", + "fcvt.s.w", + "fcvt.s.wu", + "fmv.w.x", + "fmadd.d", + "fmsub.d", + "fnmadd.d", + "fnmsub.d", + "fadd.d", + "fsub.d", + "fmul.d", + "fdiv.d", + "fsqrt.d", + "fsgnj.d", + "fsgnjn.d", + "fsgnjx.d", + "fmin.d", + "fmax.d", + "fcvt.s.d", + "fcvt.d.s", + "feq.d", + "flt.d", + "fle.d", + "fclass.d", + "fcvt.w.d", + "fcvt.wu.d", + "fcvt.d.w", + "fcvt.d.wu", + "flw", + "fsw", + "fld", + "fsd", + // RV64F + "fcvt.l.s", + "fcvt.lu.s", + "fcvt.s.l", + "fcvt.s.lu", +]; + +pub const UNSUPPORTED_INSTRUCTIONS_MIPS: [&str; 408] = [ "abs.d", "abs.ps", "abs.s", @@ -654,7 +820,79 @@ pub const UNSUPPORTED_INSTRUCTIONS: [&str; 408] = [ "xori", ]; -///Contains every general purpose register's binary value and the various names they are recognized as. Any reference to gp registers throughout the parser/assembler should reference this array +pub const UNSUPPORTED_INSTRUCTIONS_RISCV: [&str; 69] = [ + "lr.w", + "sc.w", + "amoswap.w", + "amoadd.w", + "amoxor.w", + "amoand.w", + "amomin.w", + "amomax.w", + "amominu.w", + "amomaxu.w", + "lr.d", + "sc.d", + "amoswap.d", + "amoadd.d", + "amoxor.d", + "amoand.d", + "amoor.d", + "amomin.d", + "amomax.d", + "amominu.d", + "amomaxu.d", + "fcvt.l.d", + "scvt.lu.d", + "fmv.x.d", + "fcvt.d.l", + "fcvt.d.lu", + "fmv.d.x", + "c.addi4spn", + "c.fld", + "c.lw", + "c.flw", + "c.ld", + "c.fsd", + "c.sw", + "c.fsw", + "c.sd", + "c.nop", + "c.addi", + "c.jal", + "c.addiw", + "c.li", + "c.addi16sp", + "c.lui", + "c.srli", + "c.srai", + "c.andi", + "c.sub", + "c.xor", + "c.or", + "c.and", + "c.subw", + "c.addw", + "c.j", + "c.beqz", + "c.bnez", + "c.slli", + "c.fldsp", + "c.lwsp", + "c.flwsp", + "c.ldsp", + "c.jr", + "c.mv", + "c.ebreak", + "c.jalr", + "c.add", + "c.fsdsp", + "c.swsp", + "c.fswsp", + "c.sdsp", +]; + +///Contains every MIPS general purpose register's binary value and the various names they are recognized as. Any reference to gp registers throughout the parser/assembler should reference this array pub const GP_REGISTERS: &[GPRegister; 32] = &[ GPRegister { names: &["$zero", "r0", "$0", "zero"], @@ -790,7 +1028,7 @@ pub struct GPRegister<'a> { pub binary: u8, } -///Contains every floating point register name and binary value. Any reference to fp registers throughout the parser/assembler should reference this array +///Contains every MIPS floating point register name and binary value. Any reference to fp registers throughout the parser/assembler should reference this array pub const FP_REGISTERS: &[FPRegister] = &[ FPRegister { name: "$f0", @@ -927,6 +1165,280 @@ pub struct FPRegister<'a> { pub binary: u8, } +pub struct GPRegisterRiscv<'a> { + pub names: &'a [&'a str], + pub binary: u8, +} + +///Contains every RISC-V general purpose register's binary value and the various names they are recognized as. Any reference to gp registers throughout the parser/assembler should reference this array +pub const RISCV_GP_REGISTERS: &[GPRegisterRiscv; 32] = &[ + GPRegisterRiscv { + names: &["x0", "zero"], + binary: 0b00000, + }, + GPRegisterRiscv { + names: &["x1", "ra"], + binary: 0b00001, + }, + GPRegisterRiscv { + names: &["x2", "sp"], + binary: 0b00010, + }, + GPRegisterRiscv { + names: &["x3", "gp"], + binary: 0b00011, + }, + GPRegisterRiscv { + names: &["x4", "tp"], + binary: 0b00100, + }, + GPRegisterRiscv { + names: &["x5", "t0"], + binary: 0b00101, + }, + GPRegisterRiscv { + names: &["x6", "t1"], + binary: 0b00110, + }, + GPRegisterRiscv { + names: &["x7", "t2"], + binary: 0b00111, + }, + GPRegisterRiscv { + names: &["x8", "s0", "fp"], + binary: 0b01000, + }, + GPRegisterRiscv { + names: &["x9", "s1"], + binary: 0b01001, + }, + GPRegisterRiscv { + names: &["x10", "a0"], + binary: 0b01010, + }, + GPRegisterRiscv { + names: &["x11", "a1"], + binary: 0b01011, + }, + GPRegisterRiscv { + names: &["x12", "a2"], + binary: 0b01100, + }, + GPRegisterRiscv { + names: &["x13", "a3"], + binary: 0b01101, + }, + GPRegisterRiscv { + names: &["x14", "a4"], + binary: 0b01110, + }, + GPRegisterRiscv { + names: &["x15", "a5"], + binary: 0b01111, + }, + GPRegisterRiscv { + names: &["x16", "a6"], + binary: 0b10000, + }, + GPRegisterRiscv { + names: &["x17", "a7"], + binary: 0b10001, + }, + GPRegisterRiscv { + names: &["x18", "s2"], + binary: 0b10010, + }, + GPRegisterRiscv { + names: &["x19", "s3"], + binary: 0b10011, + }, + GPRegisterRiscv { + names: &["x20", "s4"], + binary: 0b10100, + }, + GPRegisterRiscv { + names: &["x21", "s5"], + binary: 0b10101, + }, + GPRegisterRiscv { + names: &["x22", "s6"], + binary: 0b10110, + }, + GPRegisterRiscv { + names: &["x23", "s7"], + binary: 0b10111, + }, + GPRegisterRiscv { + names: &["x24", "s8"], + binary: 0b11000, + }, + GPRegisterRiscv { + names: &["x25", "s9"], + binary: 0b11001, + }, + GPRegisterRiscv { + names: &["x26", "s10"], + binary: 0b11010, + }, + GPRegisterRiscv { + names: &["x27", "s11"], + binary: 0b11011, + }, + GPRegisterRiscv { + names: &["x28", "t3"], + binary: 0b11100, + }, + GPRegisterRiscv { + names: &["x29", "t4"], + binary: 0b11101, + }, + GPRegisterRiscv { + names: &["x30", "t5"], + binary: 0b11110, + }, + GPRegisterRiscv { + names: &["x31", "t6"], + binary: 0b11111, + }, +]; + +pub struct FPRegisterRiscv<'a> { + pub names: &'a [&'a str], + pub binary: u8, +} + +///Contains every RISC-V floating point register name and binary value. Any reference to fp registers throughout the parser/assembler should reference this array +pub const RISCV_FP_REGISTERS: &[FPRegisterRiscv; 32] = &[ + FPRegisterRiscv { + names: &["f0", "ft0"], + binary: 0b00000, + }, + FPRegisterRiscv { + names: &["f1", "ft1"], + binary: 0b00001, + }, + FPRegisterRiscv { + names: &["f2", "ft2"], + binary: 0b00010, + }, + FPRegisterRiscv { + names: &["f3", "ft3"], + binary: 0b00011, + }, + FPRegisterRiscv { + names: &["f4", "ft4"], + binary: 0b00100, + }, + FPRegisterRiscv { + names: &["f5", "ft5"], + binary: 0b00101, + }, + FPRegisterRiscv { + names: &["f6", "ft6"], + binary: 0b00110, + }, + FPRegisterRiscv { + names: &["f7", "ft7"], + binary: 0b00111, + }, + FPRegisterRiscv { + names: &["f8", "fs0"], + binary: 0b01000, + }, + FPRegisterRiscv { + names: &["f9", "fs1"], + binary: 0b01001, + }, + FPRegisterRiscv { + names: &["f10", "fa0"], + binary: 0b01010, + }, + FPRegisterRiscv { + names: &["f11", "fa1"], + binary: 0b01011, + }, + FPRegisterRiscv { + names: &["f12", "fa2"], + binary: 0b01100, + }, + FPRegisterRiscv { + names: &["f13", "fa3"], + binary: 0b01101, + }, + FPRegisterRiscv { + names: &["f14", "fa4"], + binary: 0b01110, + }, + FPRegisterRiscv { + names: &["f15", "fa5"], + binary: 0b01111, + }, + FPRegisterRiscv { + names: &["f16", "fa6"], + binary: 0b10000, + }, + FPRegisterRiscv { + names: &["f17", "fa7"], + binary: 0b10001, + }, + FPRegisterRiscv { + names: &["f18", "fs2"], + binary: 0b10010, + }, + FPRegisterRiscv { + names: &["f19", "fs3"], + binary: 0b10011, + }, + FPRegisterRiscv { + names: &["f20", "fs4"], + binary: 0b10100, + }, + FPRegisterRiscv { + names: &["f21", "fs5"], + binary: 0b10101, + }, + FPRegisterRiscv { + names: &["f22", "fs6"], + binary: 0b10110, + }, + FPRegisterRiscv { + names: &["f23", "fs7"], + binary: 0b10111, + }, + FPRegisterRiscv { + names: &["f24", "fs8"], + binary: 0b11000, + }, + FPRegisterRiscv { + names: &["f25", "fs9"], + binary: 0b11001, + }, + FPRegisterRiscv { + names: &["f26", "fs10"], + binary: 0b11010, + }, + FPRegisterRiscv { + names: &["f27", "fs11"], + binary: 0b11011, + }, + FPRegisterRiscv { + names: &["f28", "ft8"], + binary: 0b11100, + }, + FPRegisterRiscv { + names: &["f29", "ft9"], + binary: 0b11101, + }, + FPRegisterRiscv { + names: &["f30", "ft10"], + binary: 0b11110, + }, + FPRegisterRiscv { + names: &["f31", "ft11"], + binary: 0b11111, + }, +]; + //This enum is just for the read_register_function to determine which register type it should expect #[derive(Eq, PartialEq)] pub enum RegisterType { @@ -949,19 +1461,21 @@ pub fn print_vec_of_data(data: Vec) { } pub fn print_instruction_contents(instruction: Instruction) { - println!("Operator: {}", instruction.operator.token_name); - print!("Operands: "); + log!("Operator: ", instruction.operator.token_name); + log!("Operands: "); for operand in instruction.operands { - print!("{} ", operand.token_name); + log!(operand.token_name); } - println!(); + log!(""); for label in instruction.labels { - println!("Label: {}", label.token.token_name); + log!("Label: ", label.token.token_name); } - print!("Errors: "); + log!("Errors: "); for error in instruction.errors { - print!("{:?} ", error.error_name); + log!(error.error_name.to_string()); } + + log!("Binary ", instruction.binary); } pub fn print_data_contents(data: Data) { diff --git a/src/parser/parsing.rs b/src/parser/parsing.rs index 5fe39244b..1b3939ad9 100644 --- a/src/parser/parsing.rs +++ b/src/parser/parsing.rs @@ -1,12 +1,15 @@ +use crate::emulation_core::architectures::AvailableDatapaths; use crate::parser::parser_structs_and_enums::ErrorType::*; use crate::parser::parser_structs_and_enums::TokenType::{Directive, Label, Operator, Unknown}; use crate::parser::parser_structs_and_enums::{ Data, Error, Instruction, LabelInstance, MonacoLineInfo, Token, FP_REGISTERS, GP_REGISTERS, - SUPPORTED_INSTRUCTIONS, + RISCV_FP_REGISTERS, RISCV_GP_REGISTERS, SUPPORTED_INSTRUCTIONS_MIPS, }; use levenshtein::levenshtein; use std::collections::HashMap; +use super::parser_structs_and_enums::SUPPORTED_INSTRUCTIONS_RISCV; + ///Takes the initial string of the program given by the editor and turns it into a vector of Line, /// a struct that holds tokens and the original line number. pub fn tokenize_program(program: String) -> Vec { @@ -188,7 +191,7 @@ pub fn remove_commas(line: &mut MonacoLineInfo) { ///This function takes the vector of lines created by tokenize program and turns them into instructions ///assigning labels, operators, operands, and line numbers and data assigning labels, data types, and values -pub fn separate_data_and_text(lines: &mut Vec) -> (Vec, Vec) { +pub fn separate_data_and_text(lines: &mut [MonacoLineInfo]) -> (Vec, Vec) { let mut instruction_list: Vec = Vec::new(); let mut data_list: Vec = Vec::new(); let mut labels: Vec = Vec::new(); @@ -208,8 +211,7 @@ pub fn separate_data_and_text(lines: &mut Vec) -> (Vec) -> (Vec) -> (Vec ((last_instruction.instruction_number + 1) << 2) as u32, + None => 0_u32, + }; - let offset_for_instructions: u32 = if let Some(..) = last_instruction { - (last_instruction.unwrap().instruction_number + 1) << 2 - } else { - 0 - } as u32; - - for (_i, data) in data_list.iter_mut().enumerate() { + for data in data_list.iter_mut() { //if the given label name is already used, an error is generated if labels.contains_key(&*data.label.clone().token_name) { data.errors.push(Error { @@ -402,6 +398,7 @@ pub fn suggest_error_corrections( data: &mut [Data], labels: &HashMap, monaco_line_info: &mut [MonacoLineInfo], + arch: AvailableDatapaths, ) -> String { let levenshtein_threshold = 2_f32 / 3_f32; let mut console_out_string: String = "".to_string(); @@ -424,12 +421,26 @@ pub fn suggest_error_corrections( let given_string = &error.token_causing_error; let mut closest: (usize, String) = (usize::MAX, "".to_string()); - for register in GP_REGISTERS { - if levenshtein(given_string, register.names[0]) < closest.0 { - closest.0 = levenshtein(given_string, register.names[0]); - closest.1 = register.names[0].to_string(); + match arch { + AvailableDatapaths::MIPS => { + for register in GP_REGISTERS { + if levenshtein(given_string, register.names[0]) < closest.0 { + closest.0 = levenshtein(given_string, register.names[0]); + closest.1 = register.names[0].to_string(); + } + } + } + + AvailableDatapaths::RISCV => { + for register in RISCV_GP_REGISTERS { + if levenshtein(given_string, register.names[0]) < closest.0 { + closest.0 = levenshtein(given_string, register.names[0]); + closest.1 = register.names[0].to_string(); + } + } } } + let mut message = "GP register is not recognized.".to_string(); //only suggest a different register if the ratio of chars needed to change vs chars in string is under a threshold if (closest.0 as f32 / given_string.len() as f32) < levenshtein_threshold { @@ -446,12 +457,25 @@ pub fn suggest_error_corrections( let given_string = &error.token_causing_error; let mut closest: (usize, String) = (usize::MAX, "".to_string()); - for register in FP_REGISTERS { - if levenshtein(given_string, register.name) < closest.0 { - closest.0 = levenshtein(given_string, register.name); - closest.1 = register.name.to_string(); + match arch { + AvailableDatapaths::MIPS => { + for register in FP_REGISTERS { + if levenshtein(given_string, register.name) < closest.0 { + closest.0 = levenshtein(given_string, register.name); + closest.1 = register.name.to_string(); + } + } + } + AvailableDatapaths::RISCV => { + for register in RISCV_FP_REGISTERS { + if levenshtein(given_string, register.names[0]) < closest.0 { + closest.0 = levenshtein(given_string, register.names[0]); + closest.1 = register.names[0].to_string(); + } + } } } + let mut message = "FP register is not recognized.".to_string(); //only suggest a different register if the ratio of chars needed to change vs chars in string is under a threshold if (closest.0 as f32 / given_string.len() as f32) < levenshtein_threshold { @@ -468,10 +492,22 @@ pub fn suggest_error_corrections( let given_string = &instruction.operator.token_name; let mut closest: (usize, String) = (usize::MAX, "".to_string()); - for instruction in SUPPORTED_INSTRUCTIONS { - if levenshtein(given_string, instruction) < closest.0 { - closest.0 = levenshtein(given_string, instruction); - closest.1 = instruction.to_string(); + match arch { + AvailableDatapaths::MIPS => { + for instruction in SUPPORTED_INSTRUCTIONS_MIPS { + if levenshtein(given_string, instruction) < closest.0 { + closest.0 = levenshtein(given_string, instruction); + closest.1 = instruction.to_string(); + } + } + } + AvailableDatapaths::RISCV => { + for instruction in SUPPORTED_INSTRUCTIONS_RISCV { + if levenshtein(given_string, instruction) < closest.0 { + closest.0 = levenshtein(given_string, instruction); + closest.1 = instruction.to_string(); + } + } } } let mut message = "Instruction is not recognized.".to_string(); diff --git a/src/parser/pseudo_instruction_parsing.rs b/src/parser/pseudo_instruction_parsing.rs index 6bb384d9c..3c9ff3684 100644 --- a/src/parser/pseudo_instruction_parsing.rs +++ b/src/parser/pseudo_instruction_parsing.rs @@ -1,4 +1,6 @@ -use crate::parser::parser_structs_and_enums::ErrorType::IncorrectNumberOfOperands; +use crate::parser::parser_structs_and_enums::ErrorType::{ + IncorrectImmediateValue, IncorrectNumberOfOperands, +}; use crate::parser::parser_structs_and_enums::TokenType::Operator; use crate::parser::parser_structs_and_enums::{ Data, Error, Instruction, MonacoLineInfo, PseudoDescription, Token, @@ -31,7 +33,7 @@ pub fn expand_pseudo_instructions_and_assign_instruction_numbers( let mut vec_of_added_instructions: Vec = Vec::new(); //iterate through every instruction and check if the operator is a pseudo-instruction - for (i, mut instruction) in &mut instructions.iter_mut().enumerate() { + for (i, instruction) in &mut instructions.iter_mut().enumerate() { instruction.instruction_number = i + vec_of_added_instructions.len(); match &*instruction.operator.token_name.to_lowercase() { "li" => { @@ -1149,132 +1151,2537 @@ pub fn expand_pseudo_instructions_and_assign_instruction_numbers( monaco_line_info[instruction.line_number] .update_pseudo_string(vec![&mut extra_instruction, instruction]); } - _ => {} - } - } + "exit" => { + let info = PseudoDescription { + name: "exit".to_string(), + syntax: "exit".to_string(), + translation_lines: vec!["ori $a0, $zero, 0".to_string(), "syscall".to_string()], + }; - //insert all new new instructions - for instruction in vec_of_added_instructions { - instructions.insert(instruction.instruction_number, instruction); - } + // Set up syscall instruction + let mut syscall_instruction = Instruction { + operator: Token { + token_name: "syscall".to_string(), + start_end_columns: (0, 0), + token_type: Operator, + }, + operands: vec![], + binary: 0, + instruction_number: instruction.instruction_number + 1, + line_number: instruction.line_number, + errors: vec![], + labels: Vec::new(), + }; - //if there aren't any instructions, add a syscall to monaco's updated string so the emulation core does not try to run data as an instruction - if instructions.is_empty() { - //try to find an instance of .text - let mut dot_text_index: Option = None; - for (i, monaco_line) in monaco_line_info.iter_mut().enumerate() { - if !monaco_line.tokens.is_empty() && monaco_line.tokens[0].token_name == ".text" { - dot_text_index = Some(i); - break; + if !check_operands(instruction, 0) { + continue; + } + + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + + // Adjust exit for io syscall + instruction.operator.token_name = "ori".to_string(); + instruction.operator.start_end_columns = (0, 0); + instruction.operands.insert( + 0, + Token { + token_name: "$a0".to_string(), + token_type: Operator, + start_end_columns: (0, 0), + }, + ); + instruction.operands.insert( + 1, + Token { + token_name: "$zero".to_string(), + token_type: Default::default(), + start_end_columns: (0, 0), + }, + ); + instruction.operands.insert( + 2, + Token { + token_name: "0".to_string(), + token_type: Default::default(), + start_end_columns: (0, 0), + }, + ); + instruction.labels = Vec::new(); + + vec_of_added_instructions.push(syscall_instruction.clone()); + + monaco_line_info[instruction.line_number] + .update_pseudo_string(vec![instruction, &mut syscall_instruction]); } - } - if let Some(dot_text_index) = dot_text_index { - let offset = monaco_line_info[dot_text_index].get_tab_space_offset().0; - //add syscall after first index of .text if it exists - monaco_line_info[dot_text_index] - .updated_monaco_string - .push_str(format!("\n{offset}syscall").as_str()); + "print_int" => { + // Set info for no operands + let mut info = PseudoDescription { + name: "print_int".to_string(), + syntax: "print_int (optional: integer)".to_string(), + translation_lines: vec!["ori $a0, $zero, 1".to_string(), "syscall".to_string()], + }; - instructions.push(Instruction { - operator: Token { - token_name: "syscall".to_string(), - start_end_columns: (0, 0), - token_type: Operator, - }, - operands: vec![], - binary: 0, - instruction_number: 0, - line_number: dot_text_index, - errors: vec![], - labels: Vec::new(), - }); - } else { - let offset = monaco_line_info[0].get_tab_space_offset().0; - //otherwise, add it at the beginning of monaco - monaco_line_info[0] - .updated_monaco_string - .insert_str(0, format!("{offset}.text\n{offset}syscall\n").as_str()); + // Set up syscall instruction + let mut syscall_instruction = Instruction { + operator: Token { + token_name: "syscall".to_string(), + start_end_columns: (0, 0), + token_type: Operator, + }, + operands: vec![], + binary: 0, + instruction_number: instruction.instruction_number + 1, + line_number: instruction.line_number, + errors: vec![], + labels: Vec::new(), + }; - instructions.push(Instruction { - operator: Token { - token_name: "syscall".to_string(), - start_end_columns: (0, 0), - token_type: Operator, - }, - operands: vec![], - binary: 0, - instruction_number: 0, - line_number: 0, - errors: vec![], - labels: Vec::new(), - }); - } - } else { - let last_instruction = instructions.last().unwrap(); - //if the last instruction in monaco is not a syscall, add it in to updated_monaco_strings and to instructions - if last_instruction.operator.token_name != "syscall" { - let offset = monaco_line_info[last_instruction.line_number] - .get_tab_space_offset() - .0; - monaco_line_info[last_instruction.line_number] - .updated_monaco_string - .push_str(format!("\n{offset}syscall").as_str()); + match instruction.operands.len() { + 0 => { + monaco_line_info[instruction.line_number].mouse_hover_string = + info.to_string(); - instructions.push(Instruction { - operator: Token { - token_name: "syscall".to_string(), - start_end_columns: (0, 0), - token_type: Operator, - }, - operands: vec![], - binary: 0, - instruction_number: last_instruction.instruction_number + 1, - line_number: last_instruction.line_number, - errors: vec![], - labels: Vec::new(), - }) - } - } -} + // Adjust print_int for io syscall + instruction.operator.token_name = "ori".to_string(); + instruction.operator.start_end_columns = (0, 0); + instruction.operands.insert( + 0, + Token { + token_name: "$a0".to_string(), + token_type: Operator, + start_end_columns: (0, 0), + }, + ); + instruction.operands.insert( + 1, + Token { + token_name: "$zero".to_string(), + token_type: Default::default(), + start_end_columns: (0, 0), + }, + ); + instruction.operands.insert( + 2, + Token { + token_name: "1".to_string(), + token_type: Default::default(), + start_end_columns: (0, 0), + }, + ); + instruction.labels = Vec::new(); -///the second part of completing pseudo-instructions. LW and SW with labels requires the address of the label to be known, -/// the second part of this must occur after the label hashmap is completed. -pub fn complete_lw_sw_pseudo_instructions( - instructions: &mut Vec, - labels: &HashMap, - monaco_line_info: &mut [MonacoLineInfo], -) { - if instructions.len() < 2 { - return; - } + vec_of_added_instructions.push(syscall_instruction.clone()); - for mut index in 0..(instructions.len() - 1) { - if instructions[index].operator.token_name == "lui" - && instructions[index].operands.len() > 1 - && labels.contains_key(&*instructions[index].operands[1].token_name) - && (instructions[index + 1].operator.token_name == "sw" - || instructions[index + 1].operator.token_name == "lw") - { - //upper 16 bits are stored in $at using lui - let address = *labels - .get(&*instructions[index].operands[1].token_name) - .unwrap(); - instructions[index].operands[1].token_name = (address >> 16).to_string(); - instructions[index].operands[1].start_end_columns = (0, 0); + monaco_line_info[instruction.line_number] + .update_pseudo_string(vec![instruction, &mut syscall_instruction]); + } + 1 => { + info.translation_lines + .insert(1, "ori $a1, $zero, immediate".to_string()); + + // Set up print_int argument + let mut extra_instruction = Instruction { + operator: Token { + token_name: "ori".to_string(), + start_end_columns: (0, 0), + token_type: Operator, + }, + operands: vec![ + Token { + token_name: "$a0".to_string(), + start_end_columns: (0, 0), + token_type: Default::default(), + }, + Token { + token_name: "$zero".to_string(), + start_end_columns: (0, 0), + token_type: Default::default(), + }, + Token { + token_name: "1".to_string(), + start_end_columns: (0, 0), + token_type: Default::default(), + }, + ], + binary: 0, + instruction_number: instruction.instruction_number + 1, + line_number: instruction.line_number, + errors: vec![], + labels: Vec::new(), + }; - index += 1; + // Adjust current print_int instruction to set argument register + instruction.operator.token_name = "ori".to_string(); + instruction.operator.start_end_columns = (0, 0); + instruction.operands.insert( + 0, + Token { + token_name: "$a1".to_string(), + token_type: Operator, + start_end_columns: (0, 0), + }, + ); + instruction.operands.insert( + 1, + Token { + token_name: "$zero".to_string(), + token_type: Default::default(), + start_end_columns: (0, 0), + }, + ); + instruction.labels = Vec::new(); - //lower 16 bits are stored as the offset for the load/store operation - let lower_16_bits = address as u16; - let mut memory_operand = lower_16_bits.to_string(); - memory_operand.push_str("($at)"); - instructions[index].operands[1].token_name = memory_operand; - instructions[index].operands[1].start_end_columns = (0, 0); + syscall_instruction.instruction_number += 1; - monaco_line_info[instructions[index].line_number].update_pseudo_string(vec![ - &mut instructions.clone()[index - 1], - &mut instructions.clone()[index], - ]); - } + vec_of_added_instructions.push(extra_instruction.clone()); + vec_of_added_instructions.push(syscall_instruction.clone()); + monaco_line_info[instruction.line_number].update_pseudo_string(vec![ + instruction, + &mut extra_instruction, + &mut syscall_instruction, + ]); + monaco_line_info[instruction.line_number].mouse_hover_string = + info.to_string(); + continue; + } + _ => { + instruction.errors.push(Error { + error_name: IncorrectNumberOfOperands, + token_causing_error: "".to_string(), + start_end_columns: instruction.operator.start_end_columns, + message: "".to_string(), + }); + continue; + } + } + } + "print_float" => { + let info = PseudoDescription { + name: "print_float".to_string(), + syntax: "print_float".to_string(), + translation_lines: vec!["ori $a0, $zero, 2".to_string(), "syscall".to_string()], + }; + + // Set up syscall instruction + let mut syscall_instruction = Instruction { + operator: Token { + token_name: "syscall".to_string(), + start_end_columns: (0, 0), + token_type: Operator, + }, + operands: vec![], + binary: 0, + instruction_number: instruction.instruction_number + 1, + line_number: instruction.line_number, + errors: vec![], + labels: Vec::new(), + }; + + if !check_operands(instruction, 0) { + continue; + } + + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + + // Adjust print_float for io syscall + instruction.operator.token_name = "ori".to_string(); + instruction.operator.start_end_columns = (0, 0); + instruction.operands.insert( + 0, + Token { + token_name: "$a0".to_string(), + token_type: Operator, + start_end_columns: (0, 0), + }, + ); + instruction.operands.insert( + 1, + Token { + token_name: "$zero".to_string(), + token_type: Default::default(), + start_end_columns: (0, 0), + }, + ); + instruction.operands.insert( + 2, + Token { + token_name: "2".to_string(), + token_type: Default::default(), + start_end_columns: (0, 0), + }, + ); + instruction.labels = Vec::new(); + + vec_of_added_instructions.push(syscall_instruction.clone()); + + monaco_line_info[instruction.line_number] + .update_pseudo_string(vec![instruction, &mut syscall_instruction]); + } + "print_double" => { + let info = PseudoDescription { + name: "print_double".to_string(), + syntax: "print_double".to_string(), + translation_lines: vec!["ori $a0, $zero, 3".to_string(), "syscall".to_string()], + }; + + // Set up syscall instruction + let mut syscall_instruction = Instruction { + operator: Token { + token_name: "syscall".to_string(), + start_end_columns: (0, 0), + token_type: Operator, + }, + operands: vec![], + binary: 0, + instruction_number: instruction.instruction_number + 1, + line_number: instruction.line_number, + errors: vec![], + labels: Vec::new(), + }; + + if !check_operands(instruction, 0) { + continue; + } + + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + + // Adjust print_double for io syscall + instruction.operator.token_name = "ori".to_string(); + instruction.operator.start_end_columns = (0, 0); + instruction.operands.insert( + 0, + Token { + token_name: "$a0".to_string(), + token_type: Operator, + start_end_columns: (0, 0), + }, + ); + instruction.operands.insert( + 1, + Token { + token_name: "$zero".to_string(), + token_type: Default::default(), + start_end_columns: (0, 0), + }, + ); + instruction.operands.insert( + 2, + Token { + token_name: "3".to_string(), + token_type: Default::default(), + start_end_columns: (0, 0), + }, + ); + instruction.labels = Vec::new(); + + vec_of_added_instructions.push(syscall_instruction.clone()); + + monaco_line_info[instruction.line_number] + .update_pseudo_string(vec![instruction, &mut syscall_instruction]); + } + "print_string" => { + let info = PseudoDescription { + name: "print_string".to_string(), + syntax: "print_string address".to_string(), + translation_lines: vec![ + "ori $a0, $zero, 4".to_string(), + "ori $a1, $zero, address".to_string(), + "syscall".to_string(), + ], + }; + + if !check_operands(instruction, 1) { + continue; + } + + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + + // Instruction for setting memory address + let mut extra_instruction = Instruction { + operator: Token { + token_name: "ori".to_string(), + start_end_columns: (0, 0), + token_type: Operator, + }, + operands: vec![ + Token { + token_name: "$a1".to_string(), + start_end_columns: (0, 0), + token_type: Default::default(), + }, + Token { + token_name: "$zero".to_string(), + start_end_columns: (0, 0), + token_type: Default::default(), + }, + Token { + token_name: instruction.operands[0].token_name.clone(), + start_end_columns: (0, 0), + token_type: Default::default(), + }, + ], + binary: 0, + instruction_number: instruction.instruction_number + 1, + line_number: instruction.line_number, + errors: vec![], + labels: Vec::new(), + }; + + // Set up syscall instruction + let mut syscall_instruction = Instruction { + operator: Token { + token_name: "syscall".to_string(), + start_end_columns: (0, 0), + token_type: Operator, + }, + operands: vec![], + binary: 0, + instruction_number: instruction.instruction_number + 2, + line_number: instruction.line_number, + errors: vec![], + labels: Vec::new(), + }; + + // Adjust print_string for io syscall + instruction.operator.token_name = "ori".to_string(); + instruction.operator.start_end_columns = (0, 0); + instruction.operands[0].token_name = "$a0".to_string(); + instruction.operands.insert( + 1, + Token { + token_name: "$zero".to_string(), + token_type: Default::default(), + start_end_columns: (0, 0), + }, + ); + instruction.operands.insert( + 2, + Token { + token_name: "4".to_string(), + token_type: Default::default(), + start_end_columns: (0, 0), + }, + ); + instruction.labels = Vec::new(); + + vec_of_added_instructions.push(extra_instruction.clone()); + vec_of_added_instructions.push(syscall_instruction.clone()); + + monaco_line_info[instruction.line_number].update_pseudo_string(vec![ + instruction, + &mut extra_instruction, + &mut syscall_instruction, + ]); + } + "read_int" => { + let info = PseudoDescription { + name: "read_int".to_string(), + syntax: "read_int".to_string(), + translation_lines: vec!["ori $a0, $zero, 5".to_string(), "syscall".to_string()], + }; + + // Set up syscall instruction + let mut syscall_instruction = Instruction { + operator: Token { + token_name: "syscall".to_string(), + start_end_columns: (0, 0), + token_type: Operator, + }, + operands: vec![], + binary: 0, + instruction_number: instruction.instruction_number + 1, + line_number: instruction.line_number, + errors: vec![], + labels: Vec::new(), + }; + + if !check_operands(instruction, 0) { + continue; + } + + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + + // Adjust read_int for io syscall + instruction.operator.token_name = "ori".to_string(); + instruction.operator.start_end_columns = (0, 0); + instruction.operands.insert( + 0, + Token { + token_name: "$a0".to_string(), + token_type: Operator, + start_end_columns: (0, 0), + }, + ); + instruction.operands.insert( + 1, + Token { + token_name: "$zero".to_string(), + token_type: Default::default(), + start_end_columns: (0, 0), + }, + ); + instruction.operands.insert( + 2, + Token { + token_name: "5".to_string(), + token_type: Default::default(), + start_end_columns: (0, 0), + }, + ); + instruction.labels = Vec::new(); + + vec_of_added_instructions.push(syscall_instruction.clone()); + + monaco_line_info[instruction.line_number] + .update_pseudo_string(vec![instruction, &mut syscall_instruction]); + } + "read_float" => { + let info = PseudoDescription { + name: "read_float".to_string(), + syntax: "read_float".to_string(), + translation_lines: vec!["ori $a0, $zero, 6".to_string(), "syscall".to_string()], + }; + + // Set up syscall instruction + let mut syscall_instruction = Instruction { + operator: Token { + token_name: "syscall".to_string(), + start_end_columns: (0, 0), + token_type: Operator, + }, + operands: vec![], + binary: 0, + instruction_number: instruction.instruction_number + 1, + line_number: instruction.line_number, + errors: vec![], + labels: Vec::new(), + }; + + if !check_operands(instruction, 0) { + continue; + } + + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + + // Adjust read_float for io syscall + instruction.operator.token_name = "ori".to_string(); + instruction.operator.start_end_columns = (0, 0); + instruction.operands.insert( + 0, + Token { + token_name: "$a0".to_string(), + token_type: Operator, + start_end_columns: (0, 0), + }, + ); + instruction.operands.insert( + 1, + Token { + token_name: "$zero".to_string(), + token_type: Default::default(), + start_end_columns: (0, 0), + }, + ); + instruction.operands.insert( + 2, + Token { + token_name: "6".to_string(), + token_type: Default::default(), + start_end_columns: (0, 0), + }, + ); + instruction.labels = Vec::new(); + + vec_of_added_instructions.push(syscall_instruction.clone()); + + monaco_line_info[instruction.line_number] + .update_pseudo_string(vec![instruction, &mut syscall_instruction]); + } + "read_double" => { + let info = PseudoDescription { + name: "read_double".to_string(), + syntax: "read_double".to_string(), + translation_lines: vec!["ori $a0, $zero, 7".to_string(), "syscall".to_string()], + }; + + // Set up syscall instruction + let mut syscall_instruction = Instruction { + operator: Token { + token_name: "syscall".to_string(), + start_end_columns: (0, 0), + token_type: Operator, + }, + operands: vec![], + binary: 0, + instruction_number: instruction.instruction_number + 1, + line_number: instruction.line_number, + errors: vec![], + labels: Vec::new(), + }; + + if !check_operands(instruction, 0) { + continue; + } + + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + + // Adjust read_double for io syscall + instruction.operator.token_name = "ori".to_string(); + instruction.operator.start_end_columns = (0, 0); + instruction.operands.insert( + 0, + Token { + token_name: "$a0".to_string(), + token_type: Operator, + start_end_columns: (0, 0), + }, + ); + instruction.operands.insert( + 1, + Token { + token_name: "$zero".to_string(), + token_type: Default::default(), + start_end_columns: (0, 0), + }, + ); + instruction.operands.insert( + 2, + Token { + token_name: "7".to_string(), + token_type: Default::default(), + start_end_columns: (0, 0), + }, + ); + instruction.labels = Vec::new(); + + vec_of_added_instructions.push(syscall_instruction.clone()); + + monaco_line_info[instruction.line_number] + .update_pseudo_string(vec![instruction, &mut syscall_instruction]); + } + "read_string" => { + let info = PseudoDescription { + name: "read_string".to_string(), + syntax: "read_string address".to_string(), + translation_lines: vec![ + "ori $a0, $zero, 8".to_string(), + "ori $a1, $zero, address".to_string(), + "syscall".to_string(), + ], + }; + + if !check_operands(instruction, 1) { + continue; + } + + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + + // Instruction for setting memory address + let mut extra_instruction = Instruction { + operator: Token { + token_name: "ori".to_string(), + start_end_columns: (0, 0), + token_type: Operator, + }, + operands: vec![ + Token { + token_name: "$a1".to_string(), + start_end_columns: (0, 0), + token_type: Default::default(), + }, + Token { + token_name: "$zero".to_string(), + start_end_columns: (0, 0), + token_type: Default::default(), + }, + Token { + token_name: instruction.operands[0].token_name.clone(), + start_end_columns: (0, 0), + token_type: Default::default(), + }, + ], + binary: 0, + instruction_number: instruction.instruction_number + 1, + line_number: instruction.line_number, + errors: vec![], + labels: Vec::new(), + }; + + // Set up syscall instruction + let mut syscall_instruction = Instruction { + operator: Token { + token_name: "syscall".to_string(), + start_end_columns: (0, 0), + token_type: Operator, + }, + operands: vec![], + binary: 0, + instruction_number: instruction.instruction_number + 2, + line_number: instruction.line_number, + errors: vec![], + labels: Vec::new(), + }; + + // Adjust read_string for io syscall + instruction.operator.token_name = "ori".to_string(); + instruction.operator.start_end_columns = (0, 0); + instruction.operands[0].token_name = "$a0".to_string(); + instruction.operands.insert( + 1, + Token { + token_name: "$zero".to_string(), + token_type: Default::default(), + start_end_columns: (0, 0), + }, + ); + instruction.operands.insert( + 2, + Token { + token_name: "8".to_string(), + token_type: Default::default(), + start_end_columns: (0, 0), + }, + ); + instruction.labels = Vec::new(); + + vec_of_added_instructions.push(extra_instruction.clone()); + vec_of_added_instructions.push(syscall_instruction.clone()); + + monaco_line_info[instruction.line_number].update_pseudo_string(vec![ + instruction, + &mut extra_instruction, + &mut syscall_instruction, + ]); + } + _ => {} + } + } + + //insert all new new instructions + for instruction in vec_of_added_instructions { + instructions.insert(instruction.instruction_number, instruction); + } + + //if there aren't any instructions, add a syscall to monaco's updated string so the emulation core does not try to run data as an instruction + if instructions.is_empty() { + //try to find an instance of .text + let mut dot_text_index: Option = None; + for (i, monaco_line) in monaco_line_info.iter_mut().enumerate() { + if !monaco_line.tokens.is_empty() && monaco_line.tokens[0].token_name == ".text" { + dot_text_index = Some(i); + break; + } + } + if let Some(dot_text_index) = dot_text_index { + let offset = monaco_line_info[dot_text_index].get_tab_space_offset().0; + //add syscall after first index of .text if it exists + monaco_line_info[dot_text_index] + .updated_monaco_string + .push_str(format!("\n{offset}syscall").as_str()); + + instructions.push(Instruction { + operator: Token { + token_name: "syscall".to_string(), + start_end_columns: (0, 0), + token_type: Operator, + }, + operands: vec![], + binary: 0, + instruction_number: 0, + line_number: dot_text_index, + errors: vec![], + labels: Vec::new(), + }); + } else { + let offset = monaco_line_info[0].get_tab_space_offset().0; + //otherwise, add it at the beginning of monaco + monaco_line_info[0] + .updated_monaco_string + .insert_str(0, format!("{offset}.text\n{offset}syscall\n").as_str()); + + instructions.push(Instruction { + operator: Token { + token_name: "syscall".to_string(), + start_end_columns: (0, 0), + token_type: Operator, + }, + operands: vec![], + binary: 0, + instruction_number: 0, + line_number: 0, + errors: vec![], + labels: Vec::new(), + }); + } + } else { + let last_instruction = instructions.last().unwrap(); + //if the last instruction in monaco is not a syscall, add it in to updated_monaco_strings and to instructions + if last_instruction.operator.token_name != "syscall" { + let offset = monaco_line_info[last_instruction.line_number] + .get_tab_space_offset() + .0; + monaco_line_info[last_instruction.line_number] + .updated_monaco_string + .push_str(format!("\n{offset}syscall").as_str()); + + instructions.push(Instruction { + operator: Token { + token_name: "syscall".to_string(), + start_end_columns: (0, 0), + token_type: Operator, + }, + operands: vec![], + binary: 0, + instruction_number: last_instruction.instruction_number + 1, + line_number: last_instruction.line_number, + errors: vec![], + labels: Vec::new(), + }) + } + } +} + +///Iterates through the instruction list and translates pseudo-instructions into real instructions. +/// I/O operations have been given their own pseudo-instructions so the user does not have to reference +/// the syscall/ecall values or references registers used for integer/memory address values. +/// Updated pseudo-instructions are added to updated_monaco_string to appear in the editor after assembly. +pub fn expand_pseudo_instructions_and_assign_instruction_numbers_riscv( + instructions: &mut Vec, + data: &Vec, + monaco_line_info: &mut [MonacoLineInfo], +) { + //figure out list of labels + let mut list_of_labels: Vec = Vec::new(); + for instruction in instructions.clone() { + for label in instruction.labels { + list_of_labels.push(label.token.token_name); + } + } + for data in data { + list_of_labels.push(data.label.token_name.clone()); + } + + //vec_of_added_instructions is needed because of rust ownership rules. It will not let us + //insert into instruction_list while instruction_list is being iterated over. + let mut vec_of_added_instructions: Vec = Vec::new(); + + //iterate through every instruction and check if the operator is a pseudo-instruction + for (i, instruction) in &mut instructions.iter_mut().enumerate() { + instruction.instruction_number = i + vec_of_added_instructions.len(); + match &*instruction.operator.token_name.to_lowercase() { + "nop" => { + // Set Pseudo Description + let info = PseudoDescription { + name: "nop".to_string(), + syntax: "nop".to_string(), + translation_lines: vec!["addi x0, x0, 0".to_string()], + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + + // Check operands + if !check_operands(instruction, 0) { + continue; + } + + // Replace Instruction + instruction.operator.token_name = "addi".to_string(); + + // Replace Operands + instruction.operands.insert( + 0, + Token { + token_name: "x0".to_string(), + start_end_columns: (0, 0), + token_type: Default::default(), + }, + ); + instruction.operands.insert( + 1, + Token { + token_name: "x0".to_string(), + start_end_columns: (0, 0), + token_type: Default::default(), + }, + ); + instruction.operands.insert( + 2, + Token { + token_name: "0".to_string(), + start_end_columns: (0, 0), + token_type: Default::default(), + }, + ); + + // Update Line Info + monaco_line_info[instruction.line_number].update_pseudo_string(vec![instruction]); + } + "li" => { + let info = PseudoDescription { + name: "li".to_string(), + syntax: "li rd, imm".to_string(), + translation_lines: vec![ + "lui rd, imm[31:12]".to_string(), + "addi rd, rd, imm[11:0]".to_string(), + ], + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + + // Change to function implementation + if !check_operands(instruction, 2) { + continue; + } + + // Parse the immediate value from the second operand + let imm_value: i32 = match instruction.operands[1].token_name.parse() { + Ok(val) => val, + Err(_) => { + instruction.errors.push(Error { + error_name: IncorrectImmediateValue, + token_causing_error: instruction.operands[1].token_name.clone(), + start_end_columns: instruction.operands[1].start_end_columns, + message: "Invalid immediate value".to_string(), + }); + continue; + } + }; + + // Extract the upper and lower parts of the immediate value + let upper_imm = imm_value >> 12; + let lower_imm = imm_value & 0xFFF; + + // lui instruction + instruction.operator.token_name = "lui".to_string(); + instruction.operator.start_end_columns = (0, 0); + instruction.operator.token_type = Default::default(); + instruction.operands[1].token_name = upper_imm.to_string(); + instruction.operands[1].start_end_columns = (0, 0); + instruction.operands[1].token_type = Default::default(); + instruction.binary = 0; + instruction.errors = vec![]; + instruction.labels = Vec::new(); + + // addi instruction + let mut addi_instruction = Instruction { + operator: Token { + token_name: "addi".to_string(), + start_end_columns: (0, 0), + token_type: Default::default(), + }, + operands: vec![ + instruction.operands[0].clone(), + instruction.operands[0].clone(), + Token { + token_name: lower_imm.to_string(), + start_end_columns: (0, 0), + token_type: Default::default(), + }, + ], + binary: 0, + instruction_number: instruction.instruction_number + 1, + line_number: instruction.line_number, + errors: vec![], + labels: Vec::new(), + }; + vec_of_added_instructions.push(addi_instruction.clone()); + + monaco_line_info[instruction.line_number] + .update_pseudo_string(vec![instruction, &mut addi_instruction]); + } + "call" => { + let info = PseudoDescription { + name: "call".to_string(), + syntax: "call offset".to_string(), + translation_lines: vec![ + "auipc x6, offset[31:12]".to_string(), + "jalr x1, x6, offset[11:0]".to_string(), + ], + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + + // Change to function implementation + if !check_operands(instruction, 1) { + continue; + } + + // Parse the immediate value from the second operand + let imm_value: i32 = match instruction.operands[0].token_name.parse() { + Ok(val) => val, + Err(_) => { + instruction.errors.push(Error { + error_name: IncorrectImmediateValue, + token_causing_error: instruction.operands[1].token_name.clone(), + start_end_columns: instruction.operands[1].start_end_columns, + message: "Invalid immediate value".to_string(), + }); + continue; + } + }; + + // Extract the upper and lower parts of the immediate value + let upper_imm = imm_value >> 12; + let lower_imm = imm_value & 0xFFF; + + // auipc instruction + instruction.operator.token_name = "auipc".to_string(); + instruction.operator.start_end_columns = (0, 0); + instruction.operator.token_type = Default::default(); + instruction.operands[0].token_name = "x6".to_string(); + instruction.operands.insert( + 1, + Token { + token_name: upper_imm.to_string(), + start_end_columns: (0, 0), + token_type: Default::default(), + }, + ); + instruction.binary = 0; + instruction.errors = vec![]; + instruction.labels = Vec::new(); + + // jalr instruction + let mut jalr_instruction = Instruction { + operator: Token { + token_name: "jalr".to_string(), + start_end_columns: (0, 0), + token_type: Default::default(), + }, + operands: vec![ + Token { + token_name: "x1".to_string(), + start_end_columns: (0, 0), + token_type: Default::default(), + }, + Token { + token_name: "x6".to_string(), + start_end_columns: (0, 0), + token_type: Default::default(), + }, + Token { + token_name: lower_imm.to_string(), + start_end_columns: (0, 0), + token_type: Default::default(), + }, + ], + binary: 0, + instruction_number: instruction.instruction_number + 1, + line_number: instruction.line_number, + errors: vec![], + labels: Vec::new(), + }; + vec_of_added_instructions.push(jalr_instruction.clone()); + + monaco_line_info[instruction.line_number] + .update_pseudo_string(vec![instruction, &mut jalr_instruction]); + } + "tail" => { + let info = PseudoDescription { + name: "tail".to_string(), + syntax: "tail offset".to_string(), + translation_lines: vec![ + "auipc x6, offset[31:12]".to_string(), + "jalr x0, x6, offset[11:0]".to_string(), + ], + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + + // Change to function implementation + if !check_operands(instruction, 1) { + continue; + } + + // Parse the immediate value from the second operand + let imm_value: i32 = match instruction.operands[0].token_name.parse() { + Ok(val) => val, + Err(_) => { + instruction.errors.push(Error { + error_name: IncorrectImmediateValue, + token_causing_error: instruction.operands[1].token_name.clone(), + start_end_columns: instruction.operands[1].start_end_columns, + message: "Invalid immediate value".to_string(), + }); + continue; + } + }; + + // Extract the upper and lower parts of the immediate value + let upper_imm = imm_value >> 12; + let lower_imm = imm_value & 0xFFF; + + // auipc instruction + instruction.operator.token_name = "auipc".to_string(); + instruction.operator.start_end_columns = (0, 0); + instruction.operator.token_type = Default::default(); + instruction.operands[0].token_name = "x6".to_string(); + instruction.operands.insert( + 1, + Token { + token_name: upper_imm.to_string(), + start_end_columns: (0, 0), + token_type: Default::default(), + }, + ); + instruction.binary = 0; + instruction.errors = vec![]; + instruction.labels = Vec::new(); + + // jalr instruction + let mut jalr_instruction = Instruction { + operator: Token { + token_name: "jalr".to_string(), + start_end_columns: (0, 0), + token_type: Default::default(), + }, + operands: vec![ + Token { + token_name: "x0".to_string(), + start_end_columns: (0, 0), + token_type: Default::default(), + }, + Token { + token_name: "x6".to_string(), + start_end_columns: (0, 0), + token_type: Default::default(), + }, + Token { + token_name: lower_imm.to_string(), + start_end_columns: (0, 0), + token_type: Default::default(), + }, + ], + binary: 0, + instruction_number: instruction.instruction_number + 1, + line_number: instruction.line_number, + errors: vec![], + labels: Vec::new(), + }; + vec_of_added_instructions.push(jalr_instruction.clone()); + + monaco_line_info[instruction.line_number] + .update_pseudo_string(vec![instruction, &mut jalr_instruction]); + } + "mv" => { + // Set Pseudo Description + let info = PseudoDescription { + name: "mv".to_string(), + syntax: "mv rd, rs1".to_string(), + translation_lines: vec!["addi rd, rs, 0".to_string()], + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + + // Check operands + if !check_operands(instruction, 2) { + continue; + } + + // Replace Instruction + instruction.operator.token_name = "addi".to_string(); + + // Replace Operands + instruction.operands.insert( + 2, + Token { + token_name: "0".to_string(), + start_end_columns: (0, 0), + token_type: Default::default(), + }, + ); + + // Update Line Info + monaco_line_info[instruction.line_number].update_pseudo_string(vec![instruction]); + } + "not" => { + // Set Pseudo Description + let info = PseudoDescription { + name: "not".to_string(), + syntax: "not rd, rs1".to_string(), + translation_lines: vec!["xori rd, rs, -1".to_string()], + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + + // Check operands + if !check_operands(instruction, 2) { + continue; + } + + // Replace Instruction + instruction.operator.token_name = "xori".to_string(); + + // Replace Operands + instruction.operands.insert( + 2, + Token { + token_name: "-1".to_string(), + start_end_columns: (0, 0), + token_type: Default::default(), + }, + ); + + // Update Line Info + monaco_line_info[instruction.line_number].update_pseudo_string(vec![instruction]); + } + "neg" => { + // Set Pseudo Description + let info = PseudoDescription { + name: "neg".to_string(), + syntax: "neg rd, rs1".to_string(), + translation_lines: vec!["sub rd, x0, rs".to_string()], + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + + // Check operands + if !check_operands(instruction, 2) { + continue; + } + + // Replace Instruction + instruction.operator.token_name = "sub".to_string(); + + // Replace Operands + instruction.operands.insert( + 1, + Token { + token_name: "x0".to_string(), + start_end_columns: (0, 0), + token_type: Default::default(), + }, + ); + + // Update Line Info + monaco_line_info[instruction.line_number].update_pseudo_string(vec![instruction]); + } + "negw" => { + // Set Pseudo Description + let info = PseudoDescription { + name: "negw".to_string(), + syntax: "negw rd, rs1".to_string(), + translation_lines: vec!["subw rd, x0, rs".to_string()], + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + + // Check operands + if !check_operands(instruction, 2) { + continue; + } + + // Replace Instruction + instruction.operator.token_name = "subw".to_string(); + + // Replace Operands + instruction.operands.insert( + 1, + Token { + token_name: "x0".to_string(), + start_end_columns: (0, 0), + token_type: Default::default(), + }, + ); + + // Update Line Info + monaco_line_info[instruction.line_number].update_pseudo_string(vec![instruction]); + } + "sext.w" => { + // Set Pseudo Description + let info = PseudoDescription { + name: "sext.w".to_string(), + syntax: "sext.w rd, rs1".to_string(), + translation_lines: vec!["addiw rd, rs, 0".to_string()], + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + + // Check operands + if !check_operands(instruction, 2) { + continue; + } + + // Replace Instruction + instruction.operator.token_name = "addiw".to_string(); + + // Replace Operands + instruction.operands.insert( + 2, + Token { + token_name: "0".to_string(), + start_end_columns: (0, 0), + token_type: Default::default(), + }, + ); + + // Update Line Info + monaco_line_info[instruction.line_number].update_pseudo_string(vec![instruction]); + } + "seqz" => { + // Set Pseudo Description + let info = PseudoDescription { + name: "seqz".to_string(), + syntax: "seqz rd, rs1".to_string(), + translation_lines: vec!["sltiu rd, rs, 1".to_string()], + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + + // Check operands + if !check_operands(instruction, 2) { + continue; + } + + // Replace Instruction + instruction.operator.token_name = "sltiu".to_string(); + + // Replace Operands + instruction.operands.insert( + 2, + Token { + token_name: "1".to_string(), + start_end_columns: (0, 0), + token_type: Default::default(), + }, + ); + + // Update Line Info + monaco_line_info[instruction.line_number].update_pseudo_string(vec![instruction]); + } + "snez" => { + // Set Pseudo Description + let info = PseudoDescription { + name: "snez".to_string(), + syntax: "snez rd, rs1".to_string(), + translation_lines: vec!["sltu rd, x0, rs".to_string()], + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + + // Check operands + if !check_operands(instruction, 2) { + continue; + } + + // Replace Instruction + instruction.operator.token_name = "sltu".to_string(); + + // Replace Operands + instruction.operands.insert( + 1, + Token { + token_name: "x0".to_string(), + start_end_columns: (0, 0), + token_type: Default::default(), + }, + ); + + // Update Line Info + monaco_line_info[instruction.line_number].update_pseudo_string(vec![instruction]); + } + "sltz" => { + // Set Pseudo Description + let info = PseudoDescription { + name: "sltz".to_string(), + syntax: "sltz rd, rs1".to_string(), + translation_lines: vec!["slt rd, rs, x0".to_string()], + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + + // Check operands + if !check_operands(instruction, 2) { + continue; + } + + // Replace Instruction + instruction.operator.token_name = "slt".to_string(); + + // Replace Operands + instruction.operands.insert( + 2, + Token { + token_name: "x0".to_string(), + start_end_columns: (0, 0), + token_type: Default::default(), + }, + ); + + // Update Line Info + monaco_line_info[instruction.line_number].update_pseudo_string(vec![instruction]); + } + "sgtz" => { + // Set Pseudo Description + let info = PseudoDescription { + name: "sgtz".to_string(), + syntax: "sgtz rd, rs1".to_string(), + translation_lines: vec!["slt rd, x0, rs".to_string()], + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + + // Check operands + if !check_operands(instruction, 2) { + continue; + } + + // Replace Instruction + instruction.operator.token_name = "slt".to_string(); + + // Replace Operands + instruction.operands.insert( + 1, + Token { + token_name: "x0".to_string(), + start_end_columns: (0, 0), + token_type: Default::default(), + }, + ); + + // Update Line Info + monaco_line_info[instruction.line_number].update_pseudo_string(vec![instruction]); + } + "beqz" => { + // Set Pseudo Description + let info = PseudoDescription { + name: "beqz".to_string(), + syntax: "beqz rs1, offset".to_string(), + translation_lines: vec!["beq rs, x0, offset".to_string()], + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + + // Check operands + if !check_operands(instruction, 2) { + continue; + } + + // Replace Instruction + instruction.operator.token_name = "beq".to_string(); + + // Replace Operands + instruction.operands.insert( + 1, + Token { + token_name: "x0".to_string(), + start_end_columns: (0, 0), + token_type: Default::default(), + }, + ); + + // Update Line Info + monaco_line_info[instruction.line_number].update_pseudo_string(vec![instruction]); + } + "bnez" => { + // Set Pseudo Description + let info = PseudoDescription { + name: "bnez".to_string(), + syntax: "bnez rs1, offset".to_string(), + translation_lines: vec!["bne rs, x0, offset".to_string()], + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + + // Check operands + if !check_operands(instruction, 2) { + continue; + } + + // Replace Instruction + instruction.operator.token_name = "bne".to_string(); + + // Replace Operands + instruction.operands.insert( + 1, + Token { + token_name: "x0".to_string(), + start_end_columns: (0, 0), + token_type: Default::default(), + }, + ); + + // Update Line Info + monaco_line_info[instruction.line_number].update_pseudo_string(vec![instruction]); + } + "blez" => { + // Set Pseudo Description + let info = PseudoDescription { + name: "blez".to_string(), + syntax: "blez rs1, offset".to_string(), + translation_lines: vec!["bge x0, rs, offset".to_string()], + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + + // Check operands + if !check_operands(instruction, 2) { + continue; + } + + // Replace Instruction + instruction.operator.token_name = "bge".to_string(); + + // Replace Operands + instruction.operands.insert( + 0, + Token { + token_name: "x0".to_string(), + start_end_columns: (0, 0), + token_type: Default::default(), + }, + ); + + // Update Line Info + monaco_line_info[instruction.line_number].update_pseudo_string(vec![instruction]); + } + "bgez" => { + // Set Pseudo Description + let info = PseudoDescription { + name: "bgez".to_string(), + syntax: "bgez rs1, offset".to_string(), + translation_lines: vec!["bge rs, x0, offset".to_string()], + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + + // Check operands + if !check_operands(instruction, 2) { + continue; + } + + // Replace Instruction + instruction.operator.token_name = "bge".to_string(); + + // Replace Operands + instruction.operands.insert( + 1, + Token { + token_name: "x0".to_string(), + start_end_columns: (0, 0), + token_type: Default::default(), + }, + ); + + // Update Line Info + monaco_line_info[instruction.line_number].update_pseudo_string(vec![instruction]); + } + "bltz" => { + // Set Pseudo Description + let info = PseudoDescription { + name: "bltz".to_string(), + syntax: "bltz rs1, offset".to_string(), + translation_lines: vec!["blt rs, x0, offset".to_string()], + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + + // Check operands + if !check_operands(instruction, 2) { + continue; + } + + // Replace Instruction + instruction.operator.token_name = "blt".to_string(); + + // Replace Operands + instruction.operands.insert( + 1, + Token { + token_name: "x0".to_string(), + start_end_columns: (0, 0), + token_type: Default::default(), + }, + ); + + // Update Line Info + monaco_line_info[instruction.line_number].update_pseudo_string(vec![instruction]); + } + "bgtz" => { + // Set Pseudo Description + let info = PseudoDescription { + name: "bgtz".to_string(), + syntax: "bgtz rs1, offset".to_string(), + translation_lines: vec!["blt x0, rs, offset".to_string()], + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + + // Check operands + if !check_operands(instruction, 2) { + continue; + } + + // Replace Instruction + instruction.operator.token_name = "blt".to_string(); + + // Replace Operands + instruction.operands.insert( + 0, + Token { + token_name: "x0".to_string(), + start_end_columns: (0, 0), + token_type: Default::default(), + }, + ); + + // Update Line Info + monaco_line_info[instruction.line_number].update_pseudo_string(vec![instruction]); + } + "bgt" => { + // Set Pseudo Description + let info = PseudoDescription { + name: "bgt".to_string(), + syntax: "bgt rs, rt, offset".to_string(), + translation_lines: vec!["blt rt, rs, offset".to_string()], + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + + // Check operands + if !check_operands(instruction, 3) { + continue; + } + + // Replace Instruction + instruction.operator.token_name = "blt".to_string(); + + // Reorder Operands + let tmp = instruction.operands[0].token_name.clone(); + instruction.operands[0].token_name = instruction.operands[1].token_name.clone(); + instruction.operands[1].token_name = tmp; + + // Update Line Info + monaco_line_info[instruction.line_number].update_pseudo_string(vec![instruction]); + } + "ble" => { + // Set Pseudo Description + let info = PseudoDescription { + name: "ble".to_string(), + syntax: "ble rs, rt, offset".to_string(), + translation_lines: vec!["bge rt, rs, offset".to_string()], + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + + // Check operands + if !check_operands(instruction, 3) { + continue; + } + + // Replace Instruction + instruction.operator.token_name = "bge".to_string(); + + // Reorder Operands + let tmp = instruction.operands[0].token_name.clone(); + instruction.operands[0].token_name = instruction.operands[1].token_name.clone(); + instruction.operands[1].token_name = tmp; + + // Update Line Info + monaco_line_info[instruction.line_number].update_pseudo_string(vec![instruction]); + } + "bgtu" => { + // Set Pseudo Description + let info = PseudoDescription { + name: "bgtu".to_string(), + syntax: "bgtu rs, rt, offset".to_string(), + translation_lines: vec!["bltu rt, rs, offset".to_string()], + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + + // Check operands + if !check_operands(instruction, 3) { + continue; + } + + // Replace Instruction + instruction.operator.token_name = "bltu".to_string(); + + // Reorder Operands + let tmp = instruction.operands[0].token_name.clone(); + instruction.operands[0].token_name = instruction.operands[1].token_name.clone(); + instruction.operands[1].token_name = tmp; + + // Update Line Info + monaco_line_info[instruction.line_number].update_pseudo_string(vec![instruction]); + } + "bleu" => { + // Set Pseudo Description + let info = PseudoDescription { + name: "bleu".to_string(), + syntax: "bleu rs, rt, offset".to_string(), + translation_lines: vec!["bgeu rt, rs, offset".to_string()], + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + + // Check operands + if !check_operands(instruction, 3) { + continue; + } + + // Replace Instruction + instruction.operator.token_name = "bgeu".to_string(); + + // Reorder Operands + let tmp = instruction.operands[0].token_name.clone(); + instruction.operands[0].token_name = instruction.operands[1].token_name.clone(); + instruction.operands[1].token_name = tmp; + + // Update Line Info + monaco_line_info[instruction.line_number].update_pseudo_string(vec![instruction]); + } + // Start of Jump Pseudo-Instructions + "j" => { + // Set Pseudo Description + let info = PseudoDescription { + name: "j".to_string(), + syntax: "j offset".to_string(), + translation_lines: vec!["jal x0, offset".to_string()], + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + + // Check operands + if !check_operands(instruction, 1) { + continue; + } + + // Replace Instruction + instruction.operator.token_name = "jal".to_string(); + + // Replace Operands + instruction.operands.insert( + 0, + Token { + token_name: "x0".to_string(), + start_end_columns: (0, 0), + token_type: Default::default(), + }, + ); + + // Update Line Info + monaco_line_info[instruction.line_number].update_pseudo_string(vec![instruction]); + } + "jr" => { + // Set Pseudo Description + let info = PseudoDescription { + name: "jr".to_string(), + syntax: "jr rs".to_string(), + translation_lines: vec!["jalr x0, rs, 0".to_string()], + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + + // Check operands + if !check_operands(instruction, 1) { + continue; + } + + // Replace Instruction + instruction.operator.token_name = "jalr".to_string(); + + // Replace Operands + instruction.operands.insert( + 0, + Token { + token_name: "x0".to_string(), + start_end_columns: (0, 0), + token_type: Default::default(), + }, + ); + instruction.operands.insert( + 2, + Token { + token_name: "0".to_string(), + start_end_columns: (0, 0), + token_type: Default::default(), + }, + ); + + // Update Line Info + monaco_line_info[instruction.line_number].update_pseudo_string(vec![instruction]); + } + "jalr" => { + // Account for default jal instruction + if instruction.operands.len() != 1 { + continue; + } + + // Set Pseudo Description + let info = PseudoDescription { + name: "jalr".to_string(), + syntax: "jalr rs".to_string(), + translation_lines: vec!["jalr x1, rs, 0".to_string()], + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + + // Check operands + if !check_operands(instruction, 1) { + continue; + } + + // Replace Instruction + instruction.operator.token_name = "jalr".to_string(); + + // Replace Operands + instruction.operands.insert( + 0, + Token { + token_name: "x1".to_string(), + start_end_columns: (0, 0), + token_type: Default::default(), + }, + ); + instruction.operands.insert( + 2, + Token { + token_name: "0".to_string(), + start_end_columns: (0, 0), + token_type: Default::default(), + }, + ); + + // Update Line Info + monaco_line_info[instruction.line_number].update_pseudo_string(vec![instruction]); + } + "ret" => { + // Set Pseudo Description + let info = PseudoDescription { + name: "ret".to_string(), + syntax: "ret".to_string(), + translation_lines: vec!["jalr x0, x1, 0".to_string()], + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + + // Check operands + if !check_operands(instruction, 0) { + continue; + } + + // Replace Instruction + instruction.operator.token_name = "jalr".to_string(); + + // Replace Operands + instruction.operands.insert( + 0, + Token { + token_name: "x0".to_string(), + start_end_columns: (0, 0), + token_type: Default::default(), + }, + ); + instruction.operands.insert( + 1, + Token { + token_name: "x1".to_string(), + start_end_columns: (0, 0), + token_type: Default::default(), + }, + ); + instruction.operands.insert( + 2, + Token { + token_name: "0".to_string(), + start_end_columns: (0, 0), + token_type: Default::default(), + }, + ); + + // Update Line Info + monaco_line_info[instruction.line_number].update_pseudo_string(vec![instruction]); + } + // Start of F extension pseudo-instructions + "fmv.s" => { + // Set Pseudo Description + let info = PseudoDescription { + name: "fmv.s".to_string(), + syntax: "fmv.s frd, frs1".to_string(), + translation_lines: vec!["fsgnj.s frd, frs, frs".to_string()], + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + + // Check operands + if !check_operands(instruction, 2) { + continue; + } + + // Replace Instruction + instruction.operator.token_name = "fsgnj.s".to_string(); + + // Replace Operands + instruction + .operands + .insert(2, instruction.operands[1].clone()); + + // Update Line Info + monaco_line_info[instruction.line_number].update_pseudo_string(vec![instruction]); + } + "fabs.s" => { + // Set Pseudo Description + let info = PseudoDescription { + name: "fabs.s".to_string(), + syntax: "fabs.s frd, frs1".to_string(), + translation_lines: vec!["fsgnjx.s frd, frs, frs".to_string()], + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + + // Check operands + if !check_operands(instruction, 2) { + continue; + } + + // Replace Instruction + instruction.operator.token_name = "fsgnjx.s".to_string(); + + // Replace Operands + instruction + .operands + .insert(2, instruction.operands[1].clone()); + + // Update Line Info + monaco_line_info[instruction.line_number].update_pseudo_string(vec![instruction]); + } + "fneg.s" => { + // Set Pseudo Description + let info = PseudoDescription { + name: "fneg.s".to_string(), + syntax: "fneg.s frd, frs1".to_string(), + translation_lines: vec!["fsgnjn.s frd, frs, frs".to_string()], + }; + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + + // Check operands + if !check_operands(instruction, 2) { + continue; + } + + // Replace Instruction + instruction.operator.token_name = "fsgnjn.s".to_string(); + + // Replace Operands + instruction + .operands + .insert(2, instruction.operands[1].clone()); + + // Update Line Info + monaco_line_info[instruction.line_number].update_pseudo_string(vec![instruction]); + } + "exit" => { + let info = PseudoDescription { + name: "exit".to_string(), + syntax: "exit".to_string(), + translation_lines: vec!["ori a0, zero, 0".to_string(), "ecall".to_string()], + }; + + // Set up syscall instruction + let mut syscall_instruction = Instruction { + operator: Token { + token_name: "ecall".to_string(), + start_end_columns: (0, 0), + token_type: Operator, + }, + operands: vec![], + binary: 0, + instruction_number: instruction.instruction_number + 1, + line_number: instruction.line_number, + errors: vec![], + labels: Vec::new(), + }; + + if !check_operands(instruction, 0) { + continue; + } + + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + + // Adjust print_int for io syscall + instruction.operator.token_name = "ori".to_string(); + instruction.operator.start_end_columns = (0, 0); + instruction.operands.insert( + 0, + Token { + token_name: "a0".to_string(), + token_type: Operator, + start_end_columns: (0, 0), + }, + ); + instruction.operands.insert( + 1, + Token { + token_name: "zero".to_string(), + token_type: Default::default(), + start_end_columns: (0, 0), + }, + ); + instruction.operands.insert( + 2, + Token { + token_name: "0".to_string(), + token_type: Default::default(), + start_end_columns: (0, 0), + }, + ); + instruction.labels = Vec::new(); + + vec_of_added_instructions.push(syscall_instruction.clone()); + + monaco_line_info[instruction.line_number] + .update_pseudo_string(vec![instruction, &mut syscall_instruction]); + } + "print_int" => { + // Set info for no operands + let mut info = PseudoDescription { + name: "print_int".to_string(), + syntax: "print_int (optional: integer)".to_string(), + translation_lines: vec!["ori a0, zero, 1".to_string(), "ecall".to_string()], + }; + + // Set up syscall instruction + let mut syscall_instruction = Instruction { + operator: Token { + token_name: "ecall".to_string(), + start_end_columns: (0, 0), + token_type: Operator, + }, + operands: vec![], + binary: 0, + instruction_number: instruction.instruction_number + 1, + line_number: instruction.line_number, + errors: vec![], + labels: Vec::new(), + }; + + match instruction.operands.len() { + 0 => { + monaco_line_info[instruction.line_number].mouse_hover_string = + info.to_string(); + + // Adjust print_int for io syscall + instruction.operator.token_name = "ori".to_string(); + instruction.operator.start_end_columns = (0, 0); + instruction.operands.insert( + 0, + Token { + token_name: "a0".to_string(), + token_type: Operator, + start_end_columns: (0, 0), + }, + ); + instruction.operands.insert( + 1, + Token { + token_name: "zero".to_string(), + token_type: Default::default(), + start_end_columns: (0, 0), + }, + ); + instruction.operands.insert( + 2, + Token { + token_name: "1".to_string(), + token_type: Default::default(), + start_end_columns: (0, 0), + }, + ); + instruction.labels = Vec::new(); + + vec_of_added_instructions.push(syscall_instruction.clone()); + + monaco_line_info[instruction.line_number] + .update_pseudo_string(vec![instruction, &mut syscall_instruction]); + } + 1 => { + info.translation_lines + .insert(1, "ori a1, zero, immediate".to_string()); + + // Set up print_int argument + let mut extra_instruction = Instruction { + operator: Token { + token_name: "ori".to_string(), + start_end_columns: (0, 0), + token_type: Operator, + }, + operands: vec![ + Token { + token_name: "a0".to_string(), + start_end_columns: (0, 0), + token_type: Default::default(), + }, + Token { + token_name: "zero".to_string(), + start_end_columns: (0, 0), + token_type: Default::default(), + }, + Token { + token_name: "1".to_string(), + start_end_columns: (0, 0), + token_type: Default::default(), + }, + ], + binary: 0, + instruction_number: instruction.instruction_number + 1, + line_number: instruction.line_number, + errors: vec![], + labels: Vec::new(), + }; + + // Adjust current print_int instruction set argument register + instruction.operator.token_name = "ori".to_string(); + instruction.operator.start_end_columns = (0, 0); + instruction.operands.insert( + 0, + Token { + token_name: "a1".to_string(), + token_type: Operator, + start_end_columns: (0, 0), + }, + ); + instruction.operands.insert( + 1, + Token { + token_name: "zero".to_string(), + token_type: Default::default(), + start_end_columns: (0, 0), + }, + ); + instruction.labels = Vec::new(); + + syscall_instruction.instruction_number += 1; + + vec_of_added_instructions.push(extra_instruction.clone()); + vec_of_added_instructions.push(syscall_instruction.clone()); + monaco_line_info[instruction.line_number].update_pseudo_string(vec![ + instruction, + &mut extra_instruction, + &mut syscall_instruction, + ]); + monaco_line_info[instruction.line_number].mouse_hover_string = + info.to_string(); + continue; + } + _ => { + instruction.errors.push(Error { + error_name: IncorrectNumberOfOperands, + token_causing_error: "".to_string(), + start_end_columns: instruction.operator.start_end_columns, + message: "".to_string(), + }); + continue; + } + } + } + "print_float" => { + let info = PseudoDescription { + name: "print_int".to_string(), + syntax: "print_int".to_string(), + translation_lines: vec!["ori a0, zero, 2".to_string(), "ecall".to_string()], + }; + + // Set up syscall instruction + let mut syscall_instruction = Instruction { + operator: Token { + token_name: "ecall".to_string(), + start_end_columns: (0, 0), + token_type: Operator, + }, + operands: vec![], + binary: 0, + instruction_number: instruction.instruction_number + 1, + line_number: instruction.line_number, + errors: vec![], + labels: Vec::new(), + }; + + if !check_operands(instruction, 0) { + continue; + } + + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + + // Adjust print_float for io syscall + instruction.operator.token_name = "ori".to_string(); + instruction.operator.start_end_columns = (0, 0); + instruction.operands.insert( + 0, + Token { + token_name: "a0".to_string(), + token_type: Operator, + start_end_columns: (0, 0), + }, + ); + instruction.operands.insert( + 1, + Token { + token_name: "zero".to_string(), + token_type: Default::default(), + start_end_columns: (0, 0), + }, + ); + instruction.operands.insert( + 2, + Token { + token_name: "2".to_string(), + token_type: Default::default(), + start_end_columns: (0, 0), + }, + ); + instruction.labels = Vec::new(); + + vec_of_added_instructions.push(syscall_instruction.clone()); + + monaco_line_info[instruction.line_number] + .update_pseudo_string(vec![instruction, &mut syscall_instruction]); + } + "print_string" => { + let info = PseudoDescription { + name: "print_string".to_string(), + syntax: "print_string address".to_string(), + translation_lines: vec![ + "ori a0, zero, 4".to_string(), + "ori a1, zero, address".to_string(), + "ecall".to_string(), + ], + }; + + if !check_operands(instruction, 1) { + continue; + } + + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + + // Instruction for setting memory address + let mut extra_instruction = Instruction { + operator: Token { + token_name: "ori".to_string(), + start_end_columns: (0, 0), + token_type: Operator, + }, + operands: vec![ + Token { + token_name: "a1".to_string(), + start_end_columns: (0, 0), + token_type: Default::default(), + }, + Token { + token_name: "zero".to_string(), + start_end_columns: (0, 0), + token_type: Default::default(), + }, + Token { + token_name: instruction.operands[0].token_name.clone(), + start_end_columns: (0, 0), + token_type: Default::default(), + }, + ], + binary: 0, + instruction_number: instruction.instruction_number + 1, + line_number: instruction.line_number, + errors: vec![], + labels: Vec::new(), + }; + + // Set up syscall instruction + let mut syscall_instruction = Instruction { + operator: Token { + token_name: "ecall".to_string(), + start_end_columns: (0, 0), + token_type: Operator, + }, + operands: vec![], + binary: 0, + instruction_number: instruction.instruction_number + 2, + line_number: instruction.line_number, + errors: vec![], + labels: Vec::new(), + }; + + // Adjust print_string for io syscall + instruction.operator.token_name = "ori".to_string(); + instruction.operator.start_end_columns = (0, 0); + instruction.operands[0].token_name = "a0".to_string(); + instruction.operands.insert( + 1, + Token { + token_name: "zero".to_string(), + token_type: Default::default(), + start_end_columns: (0, 0), + }, + ); + instruction.operands.insert( + 2, + Token { + token_name: "4".to_string(), + token_type: Default::default(), + start_end_columns: (0, 0), + }, + ); + instruction.labels = Vec::new(); + + vec_of_added_instructions.push(extra_instruction.clone()); + vec_of_added_instructions.push(syscall_instruction.clone()); + + monaco_line_info[instruction.line_number].update_pseudo_string(vec![ + instruction, + &mut extra_instruction, + &mut syscall_instruction, + ]); + } + "read_int" => { + let info = PseudoDescription { + name: "read_int".to_string(), + syntax: "read_int".to_string(), + translation_lines: vec!["ori a0, zero, 5".to_string(), "ecall".to_string()], + }; + + // Set up syscall instruction + let mut syscall_instruction = Instruction { + operator: Token { + token_name: "ecall".to_string(), + start_end_columns: (0, 0), + token_type: Operator, + }, + operands: vec![], + binary: 0, + instruction_number: instruction.instruction_number + 1, + line_number: instruction.line_number, + errors: vec![], + labels: Vec::new(), + }; + + if !check_operands(instruction, 0) { + continue; + } + + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + + // Adjust read_int for io syscall + instruction.operator.token_name = "ori".to_string(); + instruction.operator.start_end_columns = (0, 0); + instruction.operands.insert( + 0, + Token { + token_name: "a0".to_string(), + token_type: Operator, + start_end_columns: (0, 0), + }, + ); + instruction.operands.insert( + 1, + Token { + token_name: "zero".to_string(), + token_type: Default::default(), + start_end_columns: (0, 0), + }, + ); + instruction.operands.insert( + 2, + Token { + token_name: "5".to_string(), + token_type: Default::default(), + start_end_columns: (0, 0), + }, + ); + instruction.labels = Vec::new(); + + vec_of_added_instructions.push(syscall_instruction.clone()); + + monaco_line_info[instruction.line_number] + .update_pseudo_string(vec![instruction, &mut syscall_instruction]); + } + "read_float" => { + let info = PseudoDescription { + name: "read_float".to_string(), + syntax: "read_float".to_string(), + translation_lines: vec!["ori a0, zero, 6".to_string(), "ecall".to_string()], + }; + + // Set up syscall instruction + let mut syscall_instruction = Instruction { + operator: Token { + token_name: "ecall".to_string(), + start_end_columns: (0, 0), + token_type: Operator, + }, + operands: vec![], + binary: 0, + instruction_number: instruction.instruction_number + 1, + line_number: instruction.line_number, + errors: vec![], + labels: Vec::new(), + }; + + if !check_operands(instruction, 0) { + continue; + } + + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + + // Adjust read_float for io syscall + instruction.operator.token_name = "ori".to_string(); + instruction.operator.start_end_columns = (0, 0); + instruction.operands.insert( + 0, + Token { + token_name: "a0".to_string(), + token_type: Operator, + start_end_columns: (0, 0), + }, + ); + instruction.operands.insert( + 1, + Token { + token_name: "zero".to_string(), + token_type: Default::default(), + start_end_columns: (0, 0), + }, + ); + instruction.operands.insert( + 2, + Token { + token_name: "6".to_string(), + token_type: Default::default(), + start_end_columns: (0, 0), + }, + ); + instruction.labels = Vec::new(); + + vec_of_added_instructions.push(syscall_instruction.clone()); + + monaco_line_info[instruction.line_number] + .update_pseudo_string(vec![instruction, &mut syscall_instruction]); + } + "read_string" => { + let info = PseudoDescription { + name: "read_string".to_string(), + syntax: "read_string address".to_string(), + translation_lines: vec![ + "ori a0, zero, 4".to_string(), + "ori a1, zero, address".to_string(), + "ecall".to_string(), + ], + }; + + if !check_operands(instruction, 1) { + continue; + } + + monaco_line_info[instruction.line_number].mouse_hover_string = info.to_string(); + + // Instruction for setting memory address + let mut extra_instruction = Instruction { + operator: Token { + token_name: "ori".to_string(), + start_end_columns: (0, 0), + token_type: Operator, + }, + operands: vec![ + Token { + token_name: "a1".to_string(), + start_end_columns: (0, 0), + token_type: Default::default(), + }, + Token { + token_name: "zero".to_string(), + start_end_columns: (0, 0), + token_type: Default::default(), + }, + Token { + token_name: instruction.operands[0].token_name.clone(), + start_end_columns: (0, 0), + token_type: Default::default(), + }, + ], + binary: 0, + instruction_number: instruction.instruction_number + 1, + line_number: instruction.line_number, + errors: vec![], + labels: Vec::new(), + }; + + // Set up syscall instruction + let mut syscall_instruction = Instruction { + operator: Token { + token_name: "ecall".to_string(), + start_end_columns: (0, 0), + token_type: Operator, + }, + operands: vec![], + binary: 0, + instruction_number: instruction.instruction_number + 2, + line_number: instruction.line_number, + errors: vec![], + labels: Vec::new(), + }; + + // Adjust read_string for io syscall + instruction.operator.token_name = "ori".to_string(); + instruction.operator.start_end_columns = (0, 0); + instruction.operands[0].token_name = "a0".to_string(); + instruction.operands.insert( + 1, + Token { + token_name: "zero".to_string(), + token_type: Default::default(), + start_end_columns: (0, 0), + }, + ); + instruction.operands.insert( + 2, + Token { + token_name: "8".to_string(), + token_type: Default::default(), + start_end_columns: (0, 0), + }, + ); + instruction.labels = Vec::new(); + + vec_of_added_instructions.push(extra_instruction.clone()); + vec_of_added_instructions.push(syscall_instruction.clone()); + + monaco_line_info[instruction.line_number].update_pseudo_string(vec![ + instruction, + &mut extra_instruction, + &mut syscall_instruction, + ]); + } + _ => {} + } + } + + //insert all new new instructions + for instruction in vec_of_added_instructions { + instructions.insert(instruction.instruction_number, instruction); + } +} + +///the second part of completing pseudo-instructions. LW and SW with labels requires the address of the label to be known, +/// the second part of this must occur after the label hashmap is completed. +pub fn complete_lw_sw_pseudo_instructions( + instructions: &mut [Instruction], + labels: &HashMap, + monaco_line_info: &mut [MonacoLineInfo], +) { + if instructions.len() < 2 { + return; + } + + for mut index in 0..(instructions.len() - 1) { + if instructions[index].operator.token_name == "lui" + && instructions[index].operands.len() > 1 + && labels.contains_key(&*instructions[index].operands[1].token_name) + && (instructions[index + 1].operator.token_name == "sw" + || instructions[index + 1].operator.token_name == "lw") + { + //upper 16 bits are stored in $at using lui + let address = *labels + .get(&*instructions[index].operands[1].token_name) + .unwrap(); + instructions[index].operands[1].token_name = (address >> 16).to_string(); + instructions[index].operands[1].start_end_columns = (0, 0); + + index += 1; + + //lower 16 bits are stored as the offset for the load/store operation + let lower_16_bits = address as u16; + let mut memory_operand = lower_16_bits.to_string(); + memory_operand.push_str("($at)"); + instructions[index].operands[1].token_name = memory_operand; + instructions[index].operands[1].start_end_columns = (0, 0); + + monaco_line_info[instructions[index].line_number].update_pseudo_string(vec![ + &mut instructions.to_owned()[index - 1], + &mut instructions.to_owned()[index], + ]); + } + } +} + +fn check_operands(instruction: &mut Instruction, num_operands: usize) -> bool { + if instruction.operands.len() != num_operands { + instruction.errors.push(Error { + error_name: IncorrectNumberOfOperands, + token_causing_error: "".to_string(), + start_end_columns: instruction.operator.start_end_columns, + message: "".to_string(), + }); + return false; } + true } diff --git a/src/tests.rs b/src/tests.rs index 9a39a3775..6accf3827 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -1,3 +1,4 @@ +pub mod agent; pub mod emulation_core; pub mod integration; pub mod parser; diff --git a/src/tests/agent.rs b/src/tests/agent.rs new file mode 100644 index 000000000..e21e7cdae --- /dev/null +++ b/src/tests/agent.rs @@ -0,0 +1 @@ +pub mod system_scanner; diff --git a/src/tests/agent/system_scanner.rs b/src/tests/agent/system_scanner.rs new file mode 100644 index 000000000..b84ca4c31 --- /dev/null +++ b/src/tests/agent/system_scanner.rs @@ -0,0 +1,139 @@ +use crate::agent::system_scanner::Scanner; + +#[test] +fn next_int_basic() { + let mut scanner = Scanner::new(); + + scanner.feed("2478".to_string()); + + assert_eq!(scanner.next_int().unwrap(), 2478) +} + +#[test] +fn next_int_ignore_characters() { + let mut scanner = Scanner::new(); + scanner.feed("Hello world, number here: 245".to_string()); + assert_eq!(scanner.next_int().unwrap(), 245) +} + +#[test] +fn next_int_newline_delimiter() { + let mut scanner = Scanner::new(); + scanner.feed("123".to_string()); + scanner.feed("456".to_string()); + assert_eq!(scanner.next_int().unwrap(), 123); + assert_eq!(scanner.next_int().unwrap(), 456); +} + +#[test] +fn next_int_return_max_on_overflow() { + let mut scanner = Scanner::new(); + scanner.feed("173298127398127398127398127398217398217398217398127398".to_string()); + assert_eq!(scanner.next_int().unwrap(), u64::MAX); +} + +#[test] +fn next_int_return_none_on_empty() { + let mut scanner = Scanner::new(); + assert_eq!(scanner.next_int(), None); +} + +#[test] +fn next_int_return_none_on_no_matches() { + let mut scanner = Scanner::new(); + scanner.feed("This is a non-numeric string.".to_string()); + assert_eq!(scanner.next_int(), None); +} + +#[test] +fn next_double_basic() { + let mut scanner = Scanner::new(); + + scanner.feed("2478".to_string()); + + assert_eq!(scanner.next_double().unwrap(), 2478f64) +} + +#[test] +fn next_double_decimal_point() { + let mut scanner = Scanner::new(); + + scanner.feed("123122.123111".to_string()); + + assert_eq!(scanner.next_double().unwrap(), 123122.123111) +} + +#[test] +fn next_double_allow_trailing_decimal() { + let mut scanner = Scanner::new(); + + scanner.feed("123123.".to_string()); + + assert_eq!(scanner.next_double().unwrap(), 123123f64); +} + +#[test] +fn next_double_ignore_characters() { + let mut scanner = Scanner::new(); + scanner.feed("Hello world, number here: 245".to_string()); + assert_eq!(scanner.next_double().unwrap(), 245f64) +} + +#[test] +fn next_double_newline_delimiter() { + let mut scanner = Scanner::new(); + scanner.feed("123".to_string()); + scanner.feed("456".to_string()); + assert_eq!(scanner.next_double().unwrap(), 123f64); + assert_eq!(scanner.next_double().unwrap(), 456f64); +} + +#[test] +fn next_double_return_max_on_overflow() { + let mut scanner = Scanner::new(); + // The max double value is very large + scanner.feed("1797693134862315708145274237317043567980705675258449965989174768031572607800285387605895586327668781715404589535143824642343213268894641827684675467035375169860499105765512820762454900903893289440758685084551339423045832369032229481658085593321233482747978262041447231687381771809192998812504040261841248583680".to_string()); + assert_eq!(scanner.next_double().unwrap(), f64::INFINITY); +} + +#[test] +fn next_double_return_none_on_empty() { + let mut scanner = Scanner::new(); + assert_eq!(scanner.next_double(), None); +} + +#[test] +fn next_double_return_none_on_no_matches() { + let mut scanner = Scanner::new(); + scanner.feed("This is a non-numeric string.".to_string()); + assert_eq!(scanner.next_double(), None); +} + +#[test] +fn next_line_basic() { + let mut scanner = Scanner::new(); + scanner.feed("This is a line123".to_string()); + scanner.feed("This is another line".to_string()); + assert_eq!(scanner.next_line().unwrap(), "This is a line123"); + scanner.feed("This is a final line456".to_string()); + assert_eq!(scanner.next_line().unwrap(), "This is another line"); + assert_eq!(scanner.next_line().unwrap(), "This is a final line456"); +} + +#[test] +fn mixed_reads() { + let mut scanner = Scanner::new(); + scanner.feed("This should be ignored | 123.45".to_string()); + scanner.feed("Hello world!".to_string()); + scanner.feed("44 <-- int float --> 10.2".to_string()); + scanner.feed("Read as an int, then a float: 56.2".to_string()); + assert_eq!(scanner.next_float().unwrap(), 123.45f32); + // Since the only thing left in the buffer is the newline from the last string. + // Java does this, I promise people are used to it. + assert_eq!(scanner.next_line().unwrap(), ""); + assert_eq!(scanner.next_line().unwrap(), "Hello world!"); + assert_eq!(scanner.next_int().unwrap(), 44); + assert_eq!(scanner.next_double().unwrap(), 10.2f64); + assert_eq!(scanner.next_int().unwrap(), 56); + assert_eq!(scanner.next_double().unwrap(), 2f64); +} diff --git a/src/tests/emulation_core.rs b/src/tests/emulation_core.rs index e01c7c3d4..2053d6deb 100644 --- a/src/tests/emulation_core.rs +++ b/src/tests/emulation_core.rs @@ -1,3 +1,7 @@ pub mod memory; pub mod mips; +pub mod mips_instruction; pub mod registers; +pub mod riscv; +pub mod riscv_instruction; +pub mod riscv_registers; diff --git a/src/tests/emulation_core/mips.rs b/src/tests/emulation_core/mips.rs index f04ad427d..f762a7159 100644 --- a/src/tests/emulation_core/mips.rs +++ b/src/tests/emulation_core/mips.rs @@ -2,11 +2,13 @@ use crate::emulation_core::datapath::Datapath; use crate::emulation_core::mips::datapath::MipsDatapath; -use crate::emulation_core::mips::registers::GpRegisterType; +use crate::emulation_core::mips::gp_registers::GpRegisterType; pub mod api { use super::*; - use crate::parser::parser_assembler_main::parser; + use crate::{ + emulation_core::architectures::AvailableDatapaths, parser::parser_assembler_main::parser, + }; #[test] fn reset_datapath() -> Result<(), String> { @@ -14,8 +16,8 @@ pub mod api { // Add instruction into emulation core memory. let instruction = String::from("ori $s0, $zero, 5"); - let (_, instruction_bits) = parser(instruction); - datapath.initialize(instruction_bits)?; + let (_, instruction_bits, _labels) = parser(instruction, AvailableDatapaths::MIPS); + datapath.initialize_legacy(instruction_bits)?; datapath.execute_instruction(); @@ -44,7 +46,7 @@ pub mod add { // $t1 = $t1 + $t1 // R-type t1 t1 t1 (shamt) ADD let instructions: Vec = vec![0b000000_01001_01001_01001_00000_100000]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; // Assume the register $t1 has the value 5. datapath.registers[GpRegisterType::T1] = 5; @@ -63,7 +65,7 @@ pub mod add { // $s2 = $s0 + $s1 // R-type s0 s1 s2 (shamt) ADD let instructions: Vec = vec![0b000000_10000_10001_10010_00000_100000]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; datapath.registers.gpr[16] = 15; // $s0 datapath.registers.gpr[17] = 40; // $s1 @@ -85,7 +87,7 @@ pub mod add { // $zero = $t3 + $t3 // R-type t3 t3 zero (shamt) ADD let instructions: Vec = vec![0b000000_01011_01011_00000_00000_100000]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; datapath.registers.gpr[11] = 1234; // $t3 @@ -107,7 +109,7 @@ pub mod add { // $t1 = $t4 + $t4 // R-type t4 t4 t1 (shamt) ADD let instructions: Vec = vec![0b000000_01100_01100_01001_00000_100000]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; // Assume register $t4 contains 2,454,267,026, a 32-bit integer. datapath.registers.gpr[12] = 0b10010010_01001001_00100100_10010010; @@ -133,7 +135,7 @@ pub mod add { // $t1 = $t4 + $t4 // R-type t4 t4 t1 (shamt) ADD let instructions: Vec = vec![0b000000_01100_01100_01001_00000_100000]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; // Assume register $t4 contains 3,528,008,850, a 32-bit integer. datapath.registers.gpr[12] = 0b11010010_01001001_00100100_10010010; @@ -158,7 +160,7 @@ pub mod addu { // $t1 = $t1 + $t1 // R-type t1 t1 t1 (shamt) ADDU let instructions: Vec = vec![0b000000_01001_01001_01001_00000_100001]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; // Assume the register $t1 has the value 5. datapath.registers[GpRegisterType::T1] = 5; @@ -177,7 +179,7 @@ pub mod addu { // $s2 = $s0 + $s1 // R-type s0 s1 s2 (shamt) ADDU let instructions: Vec = vec![0b000000_10000_10001_10010_00000_100001]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; datapath.registers.gpr[16] = 15; // $s0 datapath.registers.gpr[17] = 40; // $s1 @@ -199,7 +201,7 @@ pub mod addu { // $zero = $t3 + $t3 // R-type t3 t3 zero (shamt) ADDU let instructions: Vec = vec![0b000000_01011_01011_00000_00000_100001]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; datapath.registers.gpr[11] = 1234; // $t3 @@ -217,7 +219,7 @@ pub mod addu { // $t1 = $t4 + $t4 // R-type t4 t4 t1 (shamt) ADDU let instructions: Vec = vec![0b000000_01100_01100_01001_00000_100001]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; // Assume register $t4 contains 2,454,267,026, a 32-bit integer. datapath.registers.gpr[12] = 0b10010010_01001001_00100100_10010010; @@ -239,7 +241,7 @@ pub mod addu { // $t1 = $t4 + $t4 // R-type t4 t4 t1 (shamt) ADDU let instructions: Vec = vec![0b000000_01100_01100_01001_00000_100001]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; // Assume register $t4 contains 3,528,008,850, a 32-bit integer. datapath.registers.gpr[12] = 0b11010010_01001001_00100100_10010010; @@ -265,7 +267,7 @@ pub mod sub { // $s2 = $s3 - $s2 // R-type s3 s2 s2 (shamt) SUB let instructions: Vec = vec![0b000000_10011_10010_10010_00000_100010]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; datapath.registers.gpr[19] = 7; // $s3 datapath.registers.gpr[18] = 3; // $s2 @@ -284,7 +286,7 @@ pub mod sub { // $s0 = $s0 - $t0 // R-type s0 t0 s0 (shamt) SUB let instructions: Vec = vec![0b000000_10000_01000_10000_00000_100010]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; datapath.registers.gpr[16] = 5; // $s0 datapath.registers.gpr[8] = 20; // $t0 @@ -307,7 +309,7 @@ pub mod sub { // $s0 = $s0 - $t0 // R-type s0 t0 s0 (shamt) SUB let instructions: Vec = vec![0b000000_10000_01000_10000_00000_100010]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; datapath.registers.gpr[16] = 0; // $s0 datapath.registers.gpr[8] = 1; // $t0 @@ -333,7 +335,7 @@ pub mod mul { // $s5 = $t7 * $t6 // R-type t7 t6 s5 MUL SOP30 let instructions: Vec = vec![0b000000_01111_01110_10101_00010_011000]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; datapath.registers.gpr[15] = 8; // $t7 datapath.registers.gpr[14] = 95; // $t6 @@ -351,7 +353,7 @@ pub mod mul { // $s5 = $t7 * $t6 // R-type t7 t6 s5 MUL SOP30 let instructions: Vec = vec![0b000000_01111_01110_10101_00010_011000]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; datapath.registers.gpr[15] = 5; // $t7 datapath.registers.gpr[14] = -5_i64 as u64; // $t6 @@ -369,7 +371,7 @@ pub mod mul { // $s4 = $t6 * $t5 // R-type t6 t5 s4 MUL SOP30 let instructions: Vec = vec![0b000000_01110_01101_10100_00010_011000]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; datapath.registers.gpr[14] = 731_564_544; // $t6 datapath.registers.gpr[13] = 8; // $t5 @@ -397,7 +399,7 @@ pub mod div { // $s4 = $t6 / $t5 // R-type t6 t5 s4 DIV SOP32 let instructions: Vec = vec![0b000000_01110_01101_10100_00010_011010]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; datapath.registers.gpr[14] = 20; // $t6 datapath.registers.gpr[13] = 2; // $t5 @@ -415,7 +417,7 @@ pub mod div { // $s4 = $t6 / $t5 // R-type t6 t5 s4 DIV SOP32 let instructions: Vec = vec![0b000000_01110_01101_10100_00010_011010]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; datapath.registers.gpr[14] = 20; // $t6 datapath.registers.gpr[13] = -5_i64 as u64; // $t5 @@ -437,7 +439,7 @@ pub mod or { // $t1 = $t1 & $t1 // R-type t1 t1 t1 (shamt) OR let instructions: Vec = vec![0b000000_01001_01001_01001_00000_100101]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; // Assume the register $t1 has the value 5. datapath.registers[GpRegisterType::T1] = 0x5; @@ -454,7 +456,7 @@ pub mod or { // $s2 = $s0 & $s1 // R-type s0 s1 s2 (shamt) OR let instructions: Vec = vec![0b000000_10000_10001_10010_00000_100101]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; datapath.registers.gpr[16] = 0x1234; // $s0 datapath.registers.gpr[17] = 0x4321; // $s1 @@ -474,7 +476,7 @@ pub mod or { // $s2 = $s0 & $s1 // R-type s0 s1 s2 (shamt) OR let instructions: Vec = vec![0b000000_10000_10001_10010_00000_100101]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; datapath.registers.gpr[16] = 0x12341234; // $s0 datapath.registers.gpr[17] = 0x43214321; // $s1 @@ -494,7 +496,7 @@ pub mod or { // $s2 = $s0 & $s1 // R-type s0 s1 s2 (shamt) OR let instructions: Vec = vec![0b000000_10000_10001_10010_00000_100101]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; datapath.registers.gpr[16] = 0x1234123412341234; // $s0 datapath.registers.gpr[17] = 0x4321432143214321; // $s1 @@ -516,7 +518,7 @@ pub mod or { // $zero = $t3 & $t3 // R-type t3 t3 zero (shamt) OR let instructions: Vec = vec![0b000000_01011_01011_00000_00000_100101]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; datapath.registers.gpr[11] = 1234; // $t3 @@ -537,7 +539,7 @@ pub mod and { // $t1 = $t1 & $t1 // R-type t1 t1 t1 (shamt) AND let instructions: Vec = vec![0b000000_01001_01001_01001_00000_100100]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; // Assume the register $t1 has the value 5. datapath.registers[GpRegisterType::T1] = 0x5; @@ -554,7 +556,7 @@ pub mod and { // $s2 = $s0 & $s1 // R-type s0 s1 s2 (shamt) AND let instructions: Vec = vec![0b000000_10000_10001_10010_00000_100100]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; datapath.registers.gpr[16] = 0x1234; // $s0 datapath.registers.gpr[17] = 0x4321; // $s1 @@ -574,7 +576,7 @@ pub mod and { // $s2 = $s0 & $s1 // R-type s0 s1 s2 (shamt) AND let instructions: Vec = vec![0b000000_10000_10001_10010_00000_100100]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; datapath.registers.gpr[16] = 0x12341234; // $s0 datapath.registers.gpr[17] = 0x43214321; // $s1 @@ -594,7 +596,7 @@ pub mod and { // $s2 = $s0 & $s1 // R-type s0 s1 s2 (shamt) AND let instructions: Vec = vec![0b000000_10000_10001_10010_00000_100100]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; datapath.registers.gpr[16] = 0x1234123412341234; // $s0 datapath.registers.gpr[17] = 0x4321432143214321; // $s1 @@ -616,7 +618,7 @@ pub mod and { // $zero = $t3 & $t3 // R-type t3 t3 zero (shamt) AND let instructions: Vec = vec![0b000000_01011_01011_00000_00000_100100]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; datapath.registers.gpr[11] = 1234; // $t3 @@ -638,7 +640,7 @@ pub mod sll { // something // R-type s1 s2 (shamt) SLL let instructions: Vec = vec![0b000000_00000_10001_10010_00000_000000]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; datapath.registers.gpr[0b10001] = 123; datapath.registers.gpr[0b10010] = 321; @@ -655,7 +657,7 @@ pub mod sll { // Shift left by two logical // R-type s1 s2 (shamt) SLL let instructions: Vec = vec![0b000000_00000_10001_10010_00010_000000]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; datapath.registers.gpr[0b10001] = 0b1010; datapath.registers.gpr[0b10010] = 0; @@ -672,7 +674,7 @@ pub mod sll { // Shift left by two logical // R-type s1 s2 (shamt) SLL let instructions: Vec = vec![0b000000_00000_10001_10010_00010_000000]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; datapath.registers.gpr[0b10001] = 0x7fffffff; datapath.registers.gpr[0b10010] = 0x10101011; @@ -694,7 +696,7 @@ pub mod slt { // $s2 = $s0 < $s1 // R-type s0 s1 s2 (shamt) SLT let instructions: Vec = vec![0b000000_10000_10001_10010_00000_101010]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; datapath.registers[GpRegisterType::S0] = 1; datapath.registers[GpRegisterType::S1] = 123; @@ -712,7 +714,7 @@ pub mod slt { // $s2 = $s0 < $s1 // R-type s0 s1 s2 (shamt) SLT let instructions: Vec = vec![0b000000_10000_10001_10010_00000_101010]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; datapath.registers[GpRegisterType::S0] = 124; datapath.registers[GpRegisterType::S1] = 123; @@ -730,7 +732,7 @@ pub mod slt { // $s2 = $s0 < $s1 // R-type s0 s1 s2 (shamt) SLT let instructions: Vec = vec![0b000000_10000_10001_10010_00000_101010]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; datapath.registers[GpRegisterType::S0] = -124_i64 as u64; datapath.registers[GpRegisterType::S1] = 123; @@ -752,7 +754,7 @@ pub mod sltu { // $s2 = $s0 < $s1 // R-type s0 s1 s2 (shamt) SLTU let instructions: Vec = vec![0b000000_10000_10001_10010_00000_101011]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; datapath.registers[GpRegisterType::S0] = 1; datapath.registers[GpRegisterType::S1] = 123; @@ -770,7 +772,7 @@ pub mod sltu { // $s2 = $s0 < $s1 // R-type s0 s1 s2 (shamt) SLTU let instructions: Vec = vec![0b000000_10000_10001_10010_00000_101011]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; datapath.registers[GpRegisterType::S0] = 124; datapath.registers[GpRegisterType::S1] = 123; @@ -788,7 +790,7 @@ pub mod sltu { // $s2 = $s0 < $s1 // R-type s0 s1 s2 (shamt) SLTU let instructions: Vec = vec![0b000000_10000_10001_10010_00000_101011]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; datapath.registers[GpRegisterType::S0] = -124_i64 as u64; datapath.registers[GpRegisterType::S1] = 123; @@ -809,7 +811,7 @@ pub mod andi { // $s0 = $zero & 12345 // andi $zero $s0 12345 let instructions: Vec = vec![0b001100_00000_10000_0011000000111001]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; datapath.execute_instruction(); @@ -824,7 +826,7 @@ pub mod andi { // $s0 = $t0 & 12345 // andi $t0 $s0 12345 let instructions: Vec = vec![0b001100_01000_10000_0011000000111001]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; // In binary: 00111010 11011110 01101000 10110001 datapath.registers.gpr[8] = 987654321; // $t0 @@ -851,7 +853,7 @@ pub mod addi_addiu { // $s0 = $t0 + 0x4 // addi $t0 $s0 4 let instructions: Vec = vec![0b001000_01000_10000_0000000000000100]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; datapath.registers[GpRegisterType::T0] = 1; datapath.registers[GpRegisterType::S0] = 123; datapath.execute_instruction(); @@ -871,7 +873,7 @@ pub mod addi_addiu { // $s0 = $t0 + 0x4 // addi $t0 $s0 4 let instructions: Vec = vec![0b001000_01000_10000_0000000000000100]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; datapath.registers[GpRegisterType::T0] = 0xffffffff; datapath.registers[GpRegisterType::S0] = 123; datapath.execute_instruction(); @@ -888,7 +890,7 @@ pub mod addi_addiu { // $s0 = $t0 + 0x1 // addi $t0 $s0 1 let instructions: Vec = vec![0b001000_01000_10000_0000000000000001]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; datapath.registers[GpRegisterType::T0] = 0xfffffff1; datapath.execute_instruction(); @@ -903,7 +905,7 @@ pub mod addi_addiu { // $s0 = $t0 + 0x1 // addi $t0 $s0 1 let instructions: Vec = vec![0b001000_01000_10000_0000000000000001]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; datapath.registers[GpRegisterType::T0] = 0xfffffffe; datapath.execute_instruction(); @@ -918,7 +920,7 @@ pub mod addi_addiu { // $s0 = $t0 + 0x4 // addiu $t0 $s0 4 let instructions: Vec = vec![0b001001_01000_10000_0000000000000100]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; datapath.registers[GpRegisterType::T0] = 1; datapath.registers[GpRegisterType::S0] = 123; datapath.execute_instruction(); @@ -934,7 +936,7 @@ pub mod addi_addiu { // $s0 = $t0 + 0x4 // addiu $t0 $s0 4 let instructions: Vec = vec![0b001001_01000_10000_0000000000000100]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; datapath.registers[GpRegisterType::T0] = 0xffffffff; datapath.registers[GpRegisterType::S0] = 123; datapath.execute_instruction(); @@ -951,7 +953,7 @@ pub mod addi_addiu { // $s0 = $t0 + 0x1 // addi $t0 $s0 1 let instructions: Vec = vec![0b001000_01000_10000_0000000000000001]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; datapath.registers[GpRegisterType::T0] = 0xfffffff1; datapath.execute_instruction(); @@ -969,7 +971,7 @@ pub mod daddi_and_daddiu { // $s0 = $t0 + 0x4 // daddi $t0 $s0 4 let instructions: Vec = vec![0b011000_01000_10000_0000000000000100]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; datapath.registers[GpRegisterType::T0] = 1; datapath.registers[GpRegisterType::S0] = 123; datapath.execute_instruction(); @@ -989,7 +991,7 @@ pub mod daddi_and_daddiu { // $s0 = $t0 + 0x1 // daddi $t0 $s0 1 let instructions: Vec = vec![0b011000_01000_10000_0000000000000001]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; datapath.registers[GpRegisterType::T0] = 0xffffffffffffffff; datapath.registers[GpRegisterType::S0] = 123; datapath.execute_instruction(); @@ -1005,7 +1007,7 @@ pub mod daddi_and_daddiu { // $s0 = $t0 + 0x1 // daddi $t0 $s0 1 let instructions: Vec = vec![0b011000_01000_10000_0000000000000001]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; datapath.registers[GpRegisterType::T0] = 0xfffffffffffffff1; datapath.execute_instruction(); @@ -1020,7 +1022,7 @@ pub mod daddi_and_daddiu { // $s0 = $t0 + 0x1 // daddi $t0 $s0 1 let instructions: Vec = vec![0b011000_01000_10000_0000000000000001]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; datapath.registers[GpRegisterType::T0] = 0xfffffffffffffffe; datapath.execute_instruction(); @@ -1035,7 +1037,7 @@ pub mod daddi_and_daddiu { // $s0 = $t0 + 0x4 // daddiu $t0 $s0 4 let instructions: Vec = vec![0b011001_01000_10000_0000000000000100]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; datapath.registers[GpRegisterType::T0] = 1; datapath.registers[GpRegisterType::S0] = 123; datapath.execute_instruction(); @@ -1051,7 +1053,7 @@ pub mod daddi_and_daddiu { // $s0 = $t0 + 0x4 // daddiu $t0 $s0 4 let instructions: Vec = vec![0b011001_01000_10000_0000000000000100]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; datapath.registers[GpRegisterType::T0] = 0xffffffffffffffff; datapath.registers[GpRegisterType::S0] = 123; datapath.execute_instruction(); @@ -1069,7 +1071,7 @@ pub mod daddi_and_daddiu { // $s0 = $t0 + 0x1 // daddiu $t0 $s0 1 let instructions: Vec = vec![0b011001_01000_10000_0000000000000001]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; datapath.registers[GpRegisterType::T0] = 0xfffffffffffffff1; datapath.execute_instruction(); @@ -1087,7 +1089,7 @@ pub mod ori { // $s0 = $zero | 12345 // ori $zero $s0 12345 let instructions: Vec = vec![0b001101_00000_10000_0011000000111001]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; datapath.execute_instruction(); @@ -1102,7 +1104,7 @@ pub mod ori { // $s0 = $t0 | 12345 // ori $t0 $s0 12345 let instructions: Vec = vec![0b001101_01000_10000_0011000000111001]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; // In binary: 00111010 11011110 01101000 10110001 datapath.registers.gpr[8] = 987654321; // $t0 @@ -1133,7 +1135,7 @@ pub mod dadd_daddu { // SPECIAL rs rt rd 0 DADD // 13 13 2 let instructions: Vec = vec![0b000000_01101_01101_00010_00000_101100]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; // Assume register $t5 contains 969,093,589,304, which is an integer // that takes up 39 bits. @@ -1159,7 +1161,7 @@ pub mod dadd_daddu { // SPECIAL rs rt rd 0 DADD // 13 13 2 let instructions: Vec = vec![0b000000_01101_01101_00010_00000_101100]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; // Assume register $t5 contains 18,134,889,837,812,767,690, which is an integer // that takes up 64 bits. @@ -1182,7 +1184,7 @@ pub mod dadd_daddu { // SPECIAL rs rt rd 0 DADDU // 16 17 18 let instructions: Vec = vec![0b000000_10000_10001_10010_00000_101101]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; datapath.registers.gpr[16] = 1_069_193_590_294; // $s0 datapath.registers.gpr[17] = 34_359_738_368; // $s1 @@ -1210,7 +1212,7 @@ pub mod dsub_dsubu { // $s4 $s3 $s5 DSUB // 18 17 19 let instructions: Vec = vec![0b000000_10010_10001_10011_00000_101110]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; // Assume registers $s3 and $s4 contain numbers larger than 32 bits, // but smaller than 64 bits. @@ -1240,7 +1242,7 @@ pub mod dsub_dsubu { // $s4 $s3 $s5 DSUB // 18 17 19 let instructions: Vec = vec![0b000000_10010_10001_10011_00000_101110]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; // Assume registers $s4 is the minimum possible integer and $s4 is 1. datapath.registers.gpr[18] = 0; // $s4 @@ -1263,7 +1265,7 @@ pub mod dsub_dsubu { // SPECIAL rs rt rd 0 DSUBU // 16 17 18 let instructions: Vec = vec![0b000000_10000_10001_10010_00000_101111]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; datapath.registers.gpr[16] = 92_975_612_771_919; // $s0 datapath.registers.gpr[17] = 13_810_775_572_047; // $s1 @@ -1290,7 +1292,7 @@ pub mod dmul_dmulu { // SPECIAL $t8 $t9 $a0 DMUL SOP34 // 24 25 4 let instructions: Vec = vec![0b000000_11000_11001_00100_00010_011100]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; // Assume register $t8 contains a number larger than 32 bits, // but smaller than 64 bits. @@ -1315,7 +1317,7 @@ pub mod dmul_dmulu { // SPECIAL $t7 $t6 $s7 DMUL SOP34 // 15 14 23 let instructions: Vec = vec![0b000000_01111_01110_10111_00010_011100]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; // Assume register $t7 contains a number larger than 32 bits, // but smaller than 64 bits. @@ -1340,7 +1342,7 @@ pub mod dmul_dmulu { // SPECIAL $s4 $s3 $s2 DMUL SOP34 // 20 19 18 let instructions: Vec = vec![0b000000_10100_10011_10010_00010_011100]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; // Assume registers $s4 and $s3 contain numbers larger than 32 bits, // but smaller than 64 bits. @@ -1369,7 +1371,7 @@ pub mod dmul_dmulu { // SPECIAL $s0 $s1 $s2 DMULU SOP35 // 16 17 18 let instructions: Vec = vec![0b000000_10000_10001_10010_00010_011101]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; datapath.registers.gpr[16] = 17_592_186_044_416; // $s0 datapath.registers.gpr[17] = 1_000; // $s1 @@ -1397,7 +1399,7 @@ pub mod ddiv_ddivu { // 17 18 16 let instructions: Vec = vec![0b000000_10001_10010_10000_00010_011110]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; // Assume register $s1 contains a number larger than 32 bits, // but smaller than 64 bits. @@ -1425,7 +1427,7 @@ pub mod ddiv_ddivu { // 6 5 7 let instructions: Vec = vec![0b000000_00110_00101_00111_00010_011110]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; // Assume register $a2 contains a number larger than 32 bits, // but smaller than 64 bits. @@ -1453,7 +1455,7 @@ pub mod ddiv_ddivu { // 16 17 18 let instructions: Vec = vec![0b000000_10000_10001_10010_00010_011111]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; datapath.registers.gpr[16] = 10_213_202_487_240; // $s0 datapath.registers.gpr[17] = 11; // $s1 @@ -1479,7 +1481,7 @@ pub mod dahi_dati { // op rs rt immediate // REGIMM $a0 DAHI 1 let instructions: Vec = vec![0b000001_00100_00110_0000000000000001]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; datapath.registers.gpr[4] = 0xABCD; // $a0 @@ -1501,7 +1503,7 @@ pub mod dahi_dati { // op rs rt immediate // REGIMM $a1 DATI 1 let instructions: Vec = vec![0b000001_00101_11110_0000000000000001]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; datapath.registers.gpr[5] = 0xABCD; // $a1 @@ -1523,7 +1525,7 @@ pub mod load_word { // lw $t0 $s0 offset = 0 let instructions: Vec = vec![0b100011_01000_10000_0000000000000000]; - datapath.initialize(instructions.clone())?; + datapath.initialize_legacy(instructions.clone())?; datapath.execute_instruction(); assert_eq!(datapath.registers.gpr[16], instructions[0] as u64); Ok(()) @@ -1537,7 +1539,7 @@ pub mod load_word { // lw $t0 $s0 offset = 4 let instructions: Vec = vec![0b100011_01000_10000_0000000000000100]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; // place data at address datapath.memory.store_word(0b100, 0x10000)?; @@ -1556,7 +1558,7 @@ pub mod load_word { // lw $t0 $s0 offset = 0 let instructions: Vec = vec![0b100011_01000_10000_0000000000000000]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; // place data at address datapath.memory.store_word(0b100, 0x10000)?; @@ -1575,7 +1577,7 @@ pub mod load_word { // lw $t0 $s0 offset = 0 let instructions: Vec = vec![0b100011_01000_10000_0000000000000100]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; // place data at address datapath.memory.store_word(0b1000, 0x10000)?; @@ -1594,7 +1596,7 @@ pub mod load_word { // lw $t0 $s0 offset = 0 let instructions: Vec = vec![0b100011_01000_10000_1111111111111100]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; // place data at address datapath.memory.store_word(0b1000, 0x10000)?; @@ -1615,7 +1617,7 @@ pub mod load_upper_imm { // lui $t0 $s0 offset = 42 let instructions: Vec = vec![0b001111_01000_10000_0010101010101010]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; datapath.execute_instruction(); let t = datapath.registers[GpRegisterType::S0]; @@ -1629,7 +1631,7 @@ pub mod load_upper_imm { // lui $t0 $s0 offset = 42 let instructions: Vec = vec![0b001111_01000_10000_1010101010101010]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; datapath.execute_instruction(); let t = datapath.registers[GpRegisterType::S0]; @@ -1645,7 +1647,7 @@ pub mod store_word { // sw $t0 $s0 offset = 0 let instructions: Vec = vec![0b101011_01000_10000_0000000000000000]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; datapath.execute_instruction(); let t = datapath.memory.load_word(0)?; @@ -1659,7 +1661,7 @@ pub mod store_word { // sw $t0 $s0 offset = 4 let instructions: Vec = vec![0b101011_01000_10000_0000000000000100]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; datapath.registers.gpr[8] = 0; datapath.registers.gpr[16] = 0xff; @@ -1676,7 +1678,7 @@ pub mod store_word { // sw $t0 $s0 offset = 4 let instructions: Vec = vec![0b101011_01000_10000_0000000000000100]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; datapath.registers.gpr[8] = 4; datapath.registers.gpr[16] = 0xff; @@ -1693,7 +1695,7 @@ pub mod store_word { // sw $t0 $s0 offset = -4 let instructions: Vec = vec![0b101011_01000_10000_1111111111111100]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; datapath.registers.gpr[8] = 12; datapath.registers.gpr[16] = 0xff; @@ -1719,16 +1721,19 @@ pub mod coprocessor { // COP1 fmt ft fs fd function // s $f0 $f1 $f2 ADD let instructions: Vec = vec![0b010001_10000_00000_00001_00010_000000]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; - datapath.coprocessor.fpr[0] = f32::to_bits(0.25f32) as u64; - datapath.coprocessor.fpr[1] = f32::to_bits(0.5f32) as u64; + datapath.coprocessor.registers.fpr[0] = f32::to_bits(0.25f32) as u64; + datapath.coprocessor.registers.fpr[1] = f32::to_bits(0.5f32) as u64; datapath.execute_instruction(); // The result should be 0.75, represented in a 32-bit value as per the // IEEE 754 single-precision floating-point specification. - assert_eq!(f32::from_bits(datapath.coprocessor.fpr[2] as u32), 0.75); + assert_eq!( + f32::from_bits(datapath.coprocessor.registers.fpr[2] as u32), + 0.75 + ); Ok(()) } @@ -1742,16 +1747,19 @@ pub mod coprocessor { // COP1 fmt ft fs fd function // d $f0 $f1 $f2 ADD let instructions: Vec = vec![0b010001_10001_00000_00001_00010_000000]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; - datapath.coprocessor.fpr[0] = f64::to_bits(123.125); - datapath.coprocessor.fpr[1] = f64::to_bits(0.5); + datapath.coprocessor.registers.fpr[0] = f64::to_bits(123.125); + datapath.coprocessor.registers.fpr[1] = f64::to_bits(0.5); datapath.execute_instruction(); // The result should be 123.625, represented in a 64-bit value as per the // IEEE 754 double-precision floating-point specification. - assert_eq!(f64::from_bits(datapath.coprocessor.fpr[2]), 123.625); + assert_eq!( + f64::from_bits(datapath.coprocessor.registers.fpr[2]), + 123.625 + ); Ok(()) } @@ -1766,14 +1774,17 @@ pub mod coprocessor { // COP1 fmt ft fs fd function // s $f0 $f1 $f2 SUB let instructions: Vec = vec![0b010001_10000_00000_00001_00010_000001]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; - datapath.coprocessor.fpr[0] = f32::to_bits(5.625f32) as u64; - datapath.coprocessor.fpr[1] = f32::to_bits(3.125f32) as u64; + datapath.coprocessor.registers.fpr[0] = f32::to_bits(5.625f32) as u64; + datapath.coprocessor.registers.fpr[1] = f32::to_bits(3.125f32) as u64; datapath.execute_instruction(); - assert_eq!(f32::from_bits(datapath.coprocessor.fpr[2] as u32), -2.5); + assert_eq!( + f32::from_bits(datapath.coprocessor.registers.fpr[2] as u32), + -2.5 + ); Ok(()) } @@ -1788,14 +1799,17 @@ pub mod coprocessor { // COP1 fmt ft fs fd function // d $f0 $f1 $f2 SUB let instructions: Vec = vec![0b010001_10001_00000_00001_00010_000001]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; - datapath.coprocessor.fpr[0] = f64::to_bits(438.125); - datapath.coprocessor.fpr[1] = f64::to_bits(98765.5); + datapath.coprocessor.registers.fpr[0] = f64::to_bits(438.125); + datapath.coprocessor.registers.fpr[1] = f64::to_bits(98765.5); datapath.execute_instruction(); - assert_eq!(f64::from_bits(datapath.coprocessor.fpr[2]), 98327.375); + assert_eq!( + f64::from_bits(datapath.coprocessor.registers.fpr[2]), + 98327.375 + ); Ok(()) } @@ -1810,14 +1824,17 @@ pub mod coprocessor { // COP1 fmt ft fs fd function // s $f4 $f5 $f9 MUL let instructions: Vec = vec![0b010001_10000_00100_00101_01001_000010]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; - datapath.coprocessor.fpr[5] = f32::to_bits(24.5f32) as u64; - datapath.coprocessor.fpr[4] = f32::to_bits(0.5f32) as u64; + datapath.coprocessor.registers.fpr[5] = f32::to_bits(24.5f32) as u64; + datapath.coprocessor.registers.fpr[4] = f32::to_bits(0.5f32) as u64; datapath.execute_instruction(); - assert_eq!(f32::from_bits(datapath.coprocessor.fpr[9] as u32), 12.25f32); + assert_eq!( + f32::from_bits(datapath.coprocessor.registers.fpr[9] as u32), + 12.25f32 + ); Ok(()) } @@ -1832,14 +1849,17 @@ pub mod coprocessor { // COP1 fmt ft fs fd function // d $f9 $f6 $f4 MUL let instructions: Vec = vec![0b010001_10001_01001_00110_00100_000010]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; - datapath.coprocessor.fpr[6] = f64::to_bits(-150.0625); - datapath.coprocessor.fpr[9] = f64::to_bits(9.5); + datapath.coprocessor.registers.fpr[6] = f64::to_bits(-150.0625); + datapath.coprocessor.registers.fpr[9] = f64::to_bits(9.5); datapath.execute_instruction(); - assert_eq!(f64::from_bits(datapath.coprocessor.fpr[4]), -1425.59375); + assert_eq!( + f64::from_bits(datapath.coprocessor.registers.fpr[4]), + -1425.59375 + ); Ok(()) } @@ -1854,15 +1874,15 @@ pub mod coprocessor { // COP1 fmt ft fs fd function // s $f17 $f16 $f15 DIV let instructions: Vec = vec![0b010001_10000_10001_10000_01111_000011]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; - datapath.coprocessor.fpr[16] = f32::to_bits(901f32) as u64; - datapath.coprocessor.fpr[17] = f32::to_bits(2f32) as u64; + datapath.coprocessor.registers.fpr[16] = f32::to_bits(901f32) as u64; + datapath.coprocessor.registers.fpr[17] = f32::to_bits(2f32) as u64; datapath.execute_instruction(); assert_eq!( - f32::from_bits(datapath.coprocessor.fpr[15] as u32), + f32::from_bits(datapath.coprocessor.registers.fpr[15] as u32), 450.5f32 ); Ok(()) @@ -1879,14 +1899,17 @@ pub mod coprocessor { // COP1 fmt ft fs fd function // d $f20 $f10 $f1 DIV let instructions: Vec = vec![0b010001_10001_10100_01010_00001_000011]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; - datapath.coprocessor.fpr[10] = f64::to_bits(95405.375); - datapath.coprocessor.fpr[20] = f64::to_bits(2.0); + datapath.coprocessor.registers.fpr[10] = f64::to_bits(95405.375); + datapath.coprocessor.registers.fpr[20] = f64::to_bits(2.0); datapath.execute_instruction(); - assert_eq!(f64::from_bits(datapath.coprocessor.fpr[1]), 47702.6875); + assert_eq!( + f64::from_bits(datapath.coprocessor.registers.fpr[1]), + 47702.6875 + ); Ok(()) } @@ -1901,10 +1924,10 @@ pub mod coprocessor { // SWC1 base ft offset // $s1 $f3 0 let instructions: Vec = vec![0b111001_10001_00011_0000000000000000]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; datapath.registers.gpr[17] = 1028; // $s1 - datapath.coprocessor.fpr[3] = f32::to_bits(1.0625f32) as u64; + datapath.coprocessor.registers.fpr[3] = f32::to_bits(1.0625f32) as u64; datapath.execute_instruction(); @@ -1927,10 +1950,10 @@ pub mod coprocessor { // SWC1 base ft offset // $s0 $f5 32 let instructions: Vec = vec![0b111001_10000_00101_0000000000100000]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; datapath.registers.gpr[16] = 2000; // $s0 - datapath.coprocessor.fpr[5] = f32::to_bits(3.5f32) as u64; + datapath.coprocessor.registers.fpr[5] = f32::to_bits(3.5f32) as u64; datapath.execute_instruction(); @@ -1957,10 +1980,10 @@ pub mod coprocessor { // SWC1 base ft offset // $s2 $f0 0 let instructions: Vec = vec![0b111001_10010_00000_0000000000000000]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; datapath.registers.gpr[18] = 1000; // $s2 - datapath.coprocessor.fpr[0] = f64::to_bits(9853114.625); + datapath.coprocessor.registers.fpr[0] = f64::to_bits(9853114.625); datapath.execute_instruction(); @@ -1982,7 +2005,7 @@ pub mod coprocessor { // LWC1 base ft offset // $t0 $f10 0 let instructions: Vec = vec![0b110001_01000_01010_0000000000000000]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; datapath.registers.gpr[8] = 500; // $t0 @@ -1996,7 +2019,7 @@ pub mod coprocessor { datapath.execute_instruction(); assert_eq!( - f32::from_bits(datapath.coprocessor.fpr[10] as u32), + f32::from_bits(datapath.coprocessor.registers.fpr[10] as u32), 413.125f32 ); Ok(()) @@ -2013,7 +2036,7 @@ pub mod coprocessor { // LWC1 base ft offset // $t1 $f11 200 let instructions: Vec = vec![0b110001_01001_01011_0000000011001000]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; datapath.registers.gpr[9] = 1000; // $t1 @@ -2027,7 +2050,7 @@ pub mod coprocessor { datapath.execute_instruction(); assert_eq!( - f32::from_bits(datapath.coprocessor.fpr[11] as u32), + f32::from_bits(datapath.coprocessor.registers.fpr[11] as u32), 6.1875f32 ); Ok(()) @@ -2044,10 +2067,10 @@ pub mod coprocessor { // COP1 fmt ft fs cc __cond // s $f2 $f1 0 EQ let instructions: Vec = vec![0b010001_10000_00010_00001_000_00_110010]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; - datapath.coprocessor.fpr[1] = f32::to_bits(15.5f32) as u64; - datapath.coprocessor.fpr[2] = f32::to_bits(15.5f32) as u64; + datapath.coprocessor.registers.fpr[1] = f32::to_bits(15.5f32) as u64; + datapath.coprocessor.registers.fpr[2] = f32::to_bits(15.5f32) as u64; datapath.execute_instruction(); @@ -2067,10 +2090,10 @@ pub mod coprocessor { // COP1 fmt ft fs cc __cond // s $f3 $f14 0 EQ let instructions: Vec = vec![0b010001_10000_00011_01110_000_00_110010]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; - datapath.coprocessor.fpr[14] = f32::to_bits(20.125f32) as u64; - datapath.coprocessor.fpr[3] = f32::to_bits(100f32) as u64; + datapath.coprocessor.registers.fpr[14] = f32::to_bits(20.125f32) as u64; + datapath.coprocessor.registers.fpr[3] = f32::to_bits(100f32) as u64; datapath.execute_instruction(); @@ -2090,10 +2113,10 @@ pub mod coprocessor { // COP1 fmt ft fs cc __cond // d $f9 $f5 0 EQ let instructions: Vec = vec![0b010001_10001_01001_00101_000_00_110010]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; - datapath.coprocessor.fpr[5] = f64::to_bits(12951.625); - datapath.coprocessor.fpr[9] = f64::to_bits(12951.625); + datapath.coprocessor.registers.fpr[5] = f64::to_bits(12951.625); + datapath.coprocessor.registers.fpr[9] = f64::to_bits(12951.625); datapath.execute_instruction(); @@ -2113,10 +2136,10 @@ pub mod coprocessor { // COP1 fmt ft fs cc __cond // d $f19 $f15 0 EQ let instructions: Vec = vec![0b010001_10001_10011_01111_000_00_110010]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; - datapath.coprocessor.fpr[15] = f64::to_bits(6016.25); - datapath.coprocessor.fpr[19] = f64::to_bits(820.43); + datapath.coprocessor.registers.fpr[15] = f64::to_bits(6016.25); + datapath.coprocessor.registers.fpr[19] = f64::to_bits(820.43); datapath.execute_instruction(); @@ -2136,10 +2159,10 @@ pub mod coprocessor { // COP1 fmt ft fs cc __cond // s $f0 $f19 0 LT let instructions: Vec = vec![0b010001_10000_00000_10011_000_00_111100]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; - datapath.coprocessor.fpr[19] = f32::to_bits(2.875f32) as u64; - datapath.coprocessor.fpr[0] = f32::to_bits(70.6f32) as u64; + datapath.coprocessor.registers.fpr[19] = f32::to_bits(2.875f32) as u64; + datapath.coprocessor.registers.fpr[0] = f32::to_bits(70.6f32) as u64; datapath.execute_instruction(); @@ -2159,10 +2182,10 @@ pub mod coprocessor { // COP1 fmt ft fs cc __cond // s $f31 $f30 0 LT let instructions: Vec = vec![0b010001_10000_11111_11110_000_00_111100]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; - datapath.coprocessor.fpr[30] = f32::to_bits(90.7f32) as u64; - datapath.coprocessor.fpr[31] = f32::to_bits(-87.44f32) as u64; + datapath.coprocessor.registers.fpr[30] = f32::to_bits(90.7f32) as u64; + datapath.coprocessor.registers.fpr[31] = f32::to_bits(-87.44f32) as u64; datapath.execute_instruction(); @@ -2182,10 +2205,10 @@ pub mod coprocessor { // COP1 fmt ft fs cc __cond // d $f29 $f12 0 LT let instructions: Vec = vec![0b010001_10001_11101_01100_000_00_111100]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; - datapath.coprocessor.fpr[12] = f64::to_bits(4.0); - datapath.coprocessor.fpr[29] = f64::to_bits(30000.6); + datapath.coprocessor.registers.fpr[12] = f64::to_bits(4.0); + datapath.coprocessor.registers.fpr[29] = f64::to_bits(30000.6); datapath.execute_instruction(); @@ -2205,10 +2228,10 @@ pub mod coprocessor { // COP1 fmt ft fs cc __cond // d $f5 $f4 0 LT let instructions: Vec = vec![0b010001_10001_00101_00100_000_00_111100]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; - datapath.coprocessor.fpr[4] = f64::to_bits(413.420); - datapath.coprocessor.fpr[5] = f64::to_bits(-6600.9); + datapath.coprocessor.registers.fpr[4] = f64::to_bits(413.420); + datapath.coprocessor.registers.fpr[5] = f64::to_bits(-6600.9); datapath.execute_instruction(); @@ -2228,10 +2251,10 @@ pub mod coprocessor { // COP1 fmt ft fs cc __cond // s $f1 $f0 0 LE let instructions: Vec = vec![0b010001_10000_00001_00000_000_00_111110]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; - datapath.coprocessor.fpr[0] = f32::to_bits(171.937f32) as u64; - datapath.coprocessor.fpr[1] = f32::to_bits(9930.829f32) as u64; + datapath.coprocessor.registers.fpr[0] = f32::to_bits(171.937f32) as u64; + datapath.coprocessor.registers.fpr[1] = f32::to_bits(9930.829f32) as u64; datapath.execute_instruction(); @@ -2251,10 +2274,10 @@ pub mod coprocessor { // COP1 fmt ft fs cc __cond // s $f3 $f2 0 LE let instructions: Vec = vec![0b010001_10000_00011_00010_000_00_111110]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; - datapath.coprocessor.fpr[2] = f32::to_bits(6.5f32) as u64; - datapath.coprocessor.fpr[3] = f32::to_bits(6.5f32) as u64; + datapath.coprocessor.registers.fpr[2] = f32::to_bits(6.5f32) as u64; + datapath.coprocessor.registers.fpr[3] = f32::to_bits(6.5f32) as u64; datapath.execute_instruction(); @@ -2274,10 +2297,10 @@ pub mod coprocessor { // COP1 fmt ft fs cc __cond // s $f5 $f4 0 LE let instructions: Vec = vec![0b010001_10000_00101_00100_000_00_111110]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; - datapath.coprocessor.fpr[4] = f32::to_bits(5742.006f32) as u64; - datapath.coprocessor.fpr[5] = f32::to_bits(1336.568f32) as u64; + datapath.coprocessor.registers.fpr[4] = f32::to_bits(5742.006f32) as u64; + datapath.coprocessor.registers.fpr[5] = f32::to_bits(1336.568f32) as u64; datapath.execute_instruction(); @@ -2297,10 +2320,10 @@ pub mod coprocessor { // COP1 fmt ft fs cc __cond // d $f7 $f6 0 LE let instructions: Vec = vec![0b010001_10001_00111_00110_000_00_111110]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; - datapath.coprocessor.fpr[6] = f64::to_bits(3483.70216); - datapath.coprocessor.fpr[7] = f64::to_bits(7201.56625); + datapath.coprocessor.registers.fpr[6] = f64::to_bits(3483.70216); + datapath.coprocessor.registers.fpr[7] = f64::to_bits(7201.56625); datapath.execute_instruction(); @@ -2320,10 +2343,10 @@ pub mod coprocessor { // COP1 fmt ft fs cc __cond // d $f9 $f8 0 LE let instructions: Vec = vec![0b010001_10001_01001_01000_000_00_111110]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; - datapath.coprocessor.fpr[8] = f64::to_bits(77.4009); - datapath.coprocessor.fpr[9] = f64::to_bits(77.4009); + datapath.coprocessor.registers.fpr[8] = f64::to_bits(77.4009); + datapath.coprocessor.registers.fpr[9] = f64::to_bits(77.4009); datapath.execute_instruction(); @@ -2343,10 +2366,10 @@ pub mod coprocessor { // COP1 fmt ft fs cc __cond // d $f11 $f10 0 LE let instructions: Vec = vec![0b010001_10001_01011_01010_000_00_111110]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; - datapath.coprocessor.fpr[10] = f64::to_bits(9190.43309); - datapath.coprocessor.fpr[11] = f64::to_bits(2869.57622); + datapath.coprocessor.registers.fpr[10] = f64::to_bits(9190.43309); + datapath.coprocessor.registers.fpr[11] = f64::to_bits(2869.57622); datapath.execute_instruction(); @@ -2366,10 +2389,10 @@ pub mod coprocessor { // COP1 fmt ft fs cc __cond // s $f13 $f12 0 NGT let instructions: Vec = vec![0b010001_10000_01101_01100_000_00_111111]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; - datapath.coprocessor.fpr[12] = f32::to_bits(2469.465f32) as u64; - datapath.coprocessor.fpr[13] = f32::to_bits(3505.57f32) as u64; + datapath.coprocessor.registers.fpr[12] = f32::to_bits(2469.465f32) as u64; + datapath.coprocessor.registers.fpr[13] = f32::to_bits(3505.57f32) as u64; datapath.execute_instruction(); @@ -2389,10 +2412,10 @@ pub mod coprocessor { // COP1 fmt ft fs cc __cond // s $f15 $f14 0 NGT let instructions: Vec = vec![0b010001_10000_01111_01110_000_00_111111]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; - datapath.coprocessor.fpr[14] = f32::to_bits(7099.472f32) as u64; - datapath.coprocessor.fpr[15] = f32::to_bits(87.198f32) as u64; + datapath.coprocessor.registers.fpr[14] = f32::to_bits(7099.472f32) as u64; + datapath.coprocessor.registers.fpr[15] = f32::to_bits(87.198f32) as u64; datapath.execute_instruction(); @@ -2412,10 +2435,10 @@ pub mod coprocessor { // COP1 fmt ft fs cc __cond // d $f17 $f16 0 NGT let instructions: Vec = vec![0b010001_10001_10001_10000_000_00_111111]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; - datapath.coprocessor.fpr[16] = f64::to_bits(7726.4794015); - datapath.coprocessor.fpr[17] = f64::to_bits(9345.7753943); + datapath.coprocessor.registers.fpr[16] = f64::to_bits(7726.4794015); + datapath.coprocessor.registers.fpr[17] = f64::to_bits(9345.7753943); datapath.execute_instruction(); @@ -2435,10 +2458,10 @@ pub mod coprocessor { // COP1 fmt ft fs cc __cond // d $f19 $f18 0 NGT let instructions: Vec = vec![0b010001_10001_10011_10010_000_00_111111]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; - datapath.coprocessor.fpr[18] = f64::to_bits(4688.2854359); - datapath.coprocessor.fpr[19] = f64::to_bits(819.7956308); + datapath.coprocessor.registers.fpr[18] = f64::to_bits(4688.2854359); + datapath.coprocessor.registers.fpr[19] = f64::to_bits(819.7956308); datapath.execute_instruction(); @@ -2458,10 +2481,10 @@ pub mod coprocessor { // COP1 fmt ft fs cc __cond // s $f21 $f20 0 NGE let instructions: Vec = vec![0b010001_10000_10101_10100_000_00_111101]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; - datapath.coprocessor.fpr[20] = f32::to_bits(3090.244f32) as u64; - datapath.coprocessor.fpr[21] = f32::to_bits(7396.444f32) as u64; + datapath.coprocessor.registers.fpr[20] = f32::to_bits(3090.244f32) as u64; + datapath.coprocessor.registers.fpr[21] = f32::to_bits(7396.444f32) as u64; datapath.execute_instruction(); @@ -2481,10 +2504,10 @@ pub mod coprocessor { // COP1 fmt ft fs cc __cond // s $f23 $f22 0 NGE let instructions: Vec = vec![0b010001_10000_10111_10110_000_00_111101]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; - datapath.coprocessor.fpr[22] = f32::to_bits(6269.823f32) as u64; - datapath.coprocessor.fpr[23] = f32::to_bits(3089.393f32) as u64; + datapath.coprocessor.registers.fpr[22] = f32::to_bits(6269.823f32) as u64; + datapath.coprocessor.registers.fpr[23] = f32::to_bits(3089.393f32) as u64; datapath.execute_instruction(); @@ -2504,10 +2527,10 @@ pub mod coprocessor { // COP1 fmt ft fs cc __cond // d $f25 $f24 0 NGE let instructions: Vec = vec![0b010001_10001_11001_11000_000_00_111101]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; - datapath.coprocessor.fpr[24] = f64::to_bits(819.7956308); - datapath.coprocessor.fpr[25] = f64::to_bits(4688.2854359); + datapath.coprocessor.registers.fpr[24] = f64::to_bits(819.7956308); + datapath.coprocessor.registers.fpr[25] = f64::to_bits(4688.2854359); datapath.execute_instruction(); @@ -2527,10 +2550,10 @@ pub mod coprocessor { // COP1 fmt ft fs cc __cond // d $f27 $f26 0 NGE let instructions: Vec = vec![0b010001_10001_11011_11010_000_00_111101]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; - datapath.coprocessor.fpr[26] = f64::to_bits(9776.3465875); - datapath.coprocessor.fpr[27] = f64::to_bits(1549.8268716); + datapath.coprocessor.registers.fpr[26] = f64::to_bits(9776.3465875); + datapath.coprocessor.registers.fpr[27] = f64::to_bits(1549.8268716); datapath.execute_instruction(); @@ -2557,10 +2580,10 @@ pub mod coprocessor { // 0 -1 (which becomes -4) 0b010001_01000_000_0_1_1111111111111110, ]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; - datapath.coprocessor.fpr[5] = f64::to_bits(12951.625); - datapath.coprocessor.fpr[9] = f64::to_bits(12951.625); + datapath.coprocessor.registers.fpr[5] = f64::to_bits(12951.625); + datapath.coprocessor.registers.fpr[9] = f64::to_bits(12951.625); datapath.execute_instruction(); datapath.execute_instruction(); @@ -2588,10 +2611,10 @@ pub mod coprocessor { // 0 -1 (which becomes -4) 0b010001_01000_000_0_1_1111111111111110, ]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; - datapath.coprocessor.fpr[5] = f64::to_bits(12952.625); - datapath.coprocessor.fpr[9] = f64::to_bits(12951.625); + datapath.coprocessor.registers.fpr[5] = f64::to_bits(12952.625); + datapath.coprocessor.registers.fpr[9] = f64::to_bits(12951.625); datapath.execute_instruction(); datapath.execute_instruction(); @@ -2619,10 +2642,10 @@ pub mod coprocessor { // 0 -1 (which becomes -4) 0b010001_01000_000_0_0_1111111111111110, ]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; - datapath.coprocessor.fpr[4] = f32::to_bits(5742.006f32) as u64; - datapath.coprocessor.fpr[5] = f32::to_bits(1336.568f32) as u64; + datapath.coprocessor.registers.fpr[4] = f32::to_bits(5742.006f32) as u64; + datapath.coprocessor.registers.fpr[5] = f32::to_bits(1336.568f32) as u64; datapath.execute_instruction(); datapath.execute_instruction(); @@ -2650,10 +2673,10 @@ pub mod coprocessor { // 0 -1 (which becomes -4) 0b010001_01000_000_0_0_1111111111111110, ]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; - datapath.coprocessor.fpr[4] = f32::to_bits(742.006f32) as u64; - datapath.coprocessor.fpr[5] = f32::to_bits(1336.568f32) as u64; + datapath.coprocessor.registers.fpr[4] = f32::to_bits(742.006f32) as u64; + datapath.coprocessor.registers.fpr[5] = f32::to_bits(1336.568f32) as u64; datapath.execute_instruction(); datapath.execute_instruction(); @@ -2674,13 +2697,13 @@ pub mod coprocessor { // COP1 sub rt fs 0 // MT $s0 $f0 let instructions: Vec = vec![0b010001_00100_10000_00000_00000000000]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; datapath.registers.gpr[16] = 25; datapath.execute_instruction(); - assert_eq!(datapath.coprocessor.fpr[0], 25); + assert_eq!(datapath.coprocessor.registers.fpr[0], 25); Ok(()) } @@ -2696,13 +2719,13 @@ pub mod coprocessor { // COP1 sub rt fs 0 // MT $s1 $f1 let instructions: Vec = vec![0b010001_00100_10001_00001_00000000000]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; datapath.registers.gpr[17] = 0x1234_5678_ABCD_BEEF; datapath.execute_instruction(); - assert_eq!(datapath.coprocessor.fpr[1], 0xABCD_BEEF); + assert_eq!(datapath.coprocessor.registers.fpr[1], 0xABCD_BEEF); Ok(()) } @@ -2718,13 +2741,16 @@ pub mod coprocessor { // COP1 sub rt fs 0 // DMT $t0 $f30 let instructions: Vec = vec![0b010001_00101_01000_11110_00000000000]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; datapath.registers.gpr[8] = 0xDEAD_BEEF_FEED_DEED; datapath.execute_instruction(); - assert_eq!(datapath.coprocessor.fpr[30], 0xDEAD_BEEF_FEED_DEED); + assert_eq!( + datapath.coprocessor.registers.fpr[30], + 0xDEAD_BEEF_FEED_DEED + ); Ok(()) } @@ -2740,9 +2766,9 @@ pub mod coprocessor { // COP1 sub rt fs 0 // MF $s5 $f18 let instructions: Vec = vec![0b010001_00000_10101_10010_00000000000]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; - datapath.coprocessor.fpr[18] = 123; + datapath.coprocessor.registers.fpr[18] = 123; datapath.execute_instruction(); @@ -2762,9 +2788,9 @@ pub mod coprocessor { // COP1 sub rt fs 0 // MF $s6 $f19 let instructions: Vec = vec![0b010001_00000_10110_10011_00000000000]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; - datapath.coprocessor.fpr[19] = 0xABBA_BABB_3ABA_4444; + datapath.coprocessor.registers.fpr[19] = 0xABBA_BABB_3ABA_4444; datapath.execute_instruction(); @@ -2784,9 +2810,9 @@ pub mod coprocessor { // COP1 sub rt fs 0 // MF $s7 $f20 let instructions: Vec = vec![0b010001_00000_10111_10100_00000000000]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; - datapath.coprocessor.fpr[20] = 0xBADA_BEEF_BADA_B00E; + datapath.coprocessor.registers.fpr[20] = 0xBADA_BEEF_BADA_B00E; datapath.execute_instruction(); @@ -2806,9 +2832,9 @@ pub mod coprocessor { // COP1 sub rt fs 0 // DMF $t8 $f21 let instructions: Vec = vec![0b010001_00001_11000_10101_00000000000]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; - datapath.coprocessor.fpr[21] = 0xADDA_DADD_1BAA_CAFE; + datapath.coprocessor.registers.fpr[21] = 0xADDA_DADD_1BAA_CAFE; datapath.execute_instruction(); @@ -2826,7 +2852,7 @@ pub mod jump_tests { // J let instructions: Vec = vec![0b000010_00_00000000_00000000_00000010]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; datapath.execute_instruction(); assert_eq!(datapath.registers.pc, 8); @@ -2839,7 +2865,7 @@ pub mod jump_tests { // J let instructions: Vec = vec![0x0800_0fff]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; datapath.execute_instruction(); assert_eq!(datapath.registers.pc, 0x3ffc); @@ -2853,7 +2879,7 @@ pub mod jump_tests { // J low_26 let instructions: Vec = vec![0x0800_0000 | 0x03ff_ffff]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; datapath.execute_instruction(); assert_eq!(datapath.registers.pc, 0x0fff_fffc); @@ -2870,7 +2896,7 @@ pub mod jump_and_link_tests { // J let instructions: Vec = vec![0b000011_00_00000000_00000000_00000010]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; datapath.execute_instruction(); assert_eq!(datapath.registers.pc, 8); @@ -2885,7 +2911,7 @@ pub mod jump_and_link_tests { // J let instructions: Vec = vec![0x0c00_0fff]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; datapath.execute_instruction(); assert_eq!(datapath.registers.pc, 0x3ffc); @@ -2901,7 +2927,7 @@ pub mod jump_and_link_tests { // J low_26 let instructions: Vec = vec![0x0c00_0000 | 0x03ff_ffff]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; datapath.execute_instruction(); assert_eq!(datapath.registers.pc, 0x0fff_fffc); @@ -2919,7 +2945,7 @@ pub mod jr_and_jalr_tests { // JR $r8 // Special $r8 $zero $zero JALR let instructions: Vec = vec![0b000000_01000_00000_00000_00000_001001]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; datapath.registers.gpr[0b01000] = 24; datapath.execute_instruction(); @@ -2935,7 +2961,7 @@ pub mod jr_and_jalr_tests { // JALR $r8 // Special $r8 $zero $ra JALR let instructions: Vec = vec![0, 0, 0b000000_01000_00000_11111_00000_001001]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; datapath.registers.pc = 8; let initial_pc = datapath.registers.pc; datapath.registers.gpr[0b01000] = 24; @@ -2955,7 +2981,7 @@ pub mod beq_tests { // beq let instructions: Vec = vec![0b000100_01000_10000_0000000000000001]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; let initial_pc = datapath.registers.pc; datapath.execute_instruction(); @@ -2973,7 +2999,7 @@ pub mod beq_tests { 0b000100_01000_10000_0000000000000001, 0b000100_01000_10000_0000000000000001, ]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; datapath.registers.gpr[0b01000] = 1234; datapath.registers.gpr[0b10000] = 4321; @@ -2997,7 +3023,7 @@ pub mod beq_tests { 0, // 0x0c 0b000100_01000_10000_1111111111111011, // 0x10, Branch to 0x00 ]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; datapath.registers.gpr[0b01000] = 1234; datapath.registers.gpr[0b10000] = 1234; @@ -3046,7 +3072,7 @@ pub mod bne_tests { let instructions: Vec = vec![0b000101_01000_10000_0000000000000001]; datapath.registers.gpr[0b01000] = 1234; datapath.registers.gpr[0b10000] = 1234; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; datapath.execute_instruction(); let expt_result = 4; // PC + 4, PC starts at 0 with the bne instruction at address 0, no branch acures assert_eq!(datapath.registers.pc, expt_result); @@ -3074,7 +3100,7 @@ pub mod bne_tests { 0, // 0x1c 0b000101_01000_10000_1111111111111001, // 0x20, bne r8, r16, -24, (branch -28 relative to next addres), branch to 0x08 ]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; datapath.registers.gpr[0b01000] = 1234; datapath.registers.gpr[0b10000] = 4321; @@ -3118,7 +3144,7 @@ pub mod syscall { use super::*; #[test] - fn halts_on_syscall() -> Result<(), String> { + fn recognizes_syscall() -> Result<(), String> { let mut datapath = MipsDatapath::default(); assert!(datapath.is_halted()); @@ -3133,18 +3159,17 @@ pub mod syscall { // SPECIAL (code) SYSCALL 0b000000_00000000000000000000_001100, ]; - datapath.initialize(instructions)?; + datapath.initialize_legacy(instructions)?; assert!(!datapath.is_halted()); datapath.registers.gpr[9] = 5; // $t1 // Execute 2 instructions. - for _ in 0..2 { - datapath.execute_instruction(); - } + datapath.execute_instruction(); + let execution_result = datapath.execute_instruction(); assert_eq!(datapath.registers.gpr[9], 10); // $t1 - assert!(datapath.is_halted()); + assert!(execution_result.hit_syscall); Ok(()) } } diff --git a/src/tests/emulation_core/mips_instruction.rs b/src/tests/emulation_core/mips_instruction.rs new file mode 100644 index 000000000..c74febd64 --- /dev/null +++ b/src/tests/emulation_core/mips_instruction.rs @@ -0,0 +1,693 @@ +use std::collections::HashMap; + +use crate::emulation_core::mips::instruction::MipsInstruction; + +// ** R-TYPE INSTRUCTIONS ** // +#[test] +fn get_string_version_from_r_type() { + // R-type instructions: + // add, sub, mul, div + // addu + // dadd, dsub, dmul, ddiv + // daddu, dsubu, dmulu, ddivu + // or, and, sll + // slt, sltu + // jalr, jr + let instruction: u32 = 0b00000001111110000111000000100000; + let labels: HashMap = HashMap::::new(); + + assert!( + match MipsInstruction::get_string_version(instruction, labels.clone(), 0) { + Ok(string) => { + string.contains("add $t6, $t7, $t8") + } + _ => false, + } + ); + + let instruction: u32 = 0b00000010000100010100100000100001; + assert!( + match MipsInstruction::get_string_version(instruction, labels.clone(), 0) { + Ok(string) => { + string.contains("addu $t1, $s0, $s1") + } + _ => false, + } + ); + + let instruction: u32 = 0b00000011110010011110100000100010; + assert!( + match MipsInstruction::get_string_version(instruction, labels.clone(), 0) { + Ok(string) => { + string.contains("sub $sp, $fp, $t1") + } + _ => false, + } + ); + + let instruction: u32 = 0b00000001100010111101000010011000; + assert!( + match MipsInstruction::get_string_version(instruction, labels.clone(), 0) { + Ok(string) => { + string.contains("mul $k0, $t4, $t3") + } + _ => false, + } + ); + let instruction: u32 = 0b00000010000100010100100000101100; + assert!( + match MipsInstruction::get_string_version(instruction, labels.clone(), 0) { + Ok(string) => { + string.contains("dadd $t1, $s0, $s1") + } + _ => false, + } + ); + let instruction: u32 = 0b00000010000100010100100000101101; + assert!( + match MipsInstruction::get_string_version(instruction, labels.clone(), 0) { + Ok(string) => { + string.contains("daddu $t1, $s0, $s1") + } + _ => false, + } + ); + let instruction: u32 = 0b00000010000100010100100000101110; + assert!( + match MipsInstruction::get_string_version(instruction, labels.clone(), 0) { + Ok(string) => { + string.contains("dsub $t1, $s0, $s1") + } + _ => false, + } + ); + let instruction: u32 = 0b00000010000100010100100000101111; + assert!( + match MipsInstruction::get_string_version(instruction, labels.clone(), 0) { + Ok(string) => { + string.contains("dsubu $t1, $s0, $s1") + } + _ => false, + } + ); + let instruction: u32 = 0b00000001001010100100000010011100; + assert!( + match MipsInstruction::get_string_version(instruction, labels.clone(), 0) { + Ok(string) => { + string.contains("dmul $t0, $t1, $t2") + } + _ => false, + } + ); + let instruction: u32 = 0b00000001001010100100000010011101; + assert!( + match MipsInstruction::get_string_version(instruction, labels.clone(), 0) { + Ok(string) => { + string.contains("dmulu $t0, $t1, $t2") + } + _ => false, + } + ); + let instruction: u32 = 0b00000001001010100100000010011110; + assert!( + match MipsInstruction::get_string_version(instruction, labels.clone(), 0) { + Ok(string) => { + string.contains("ddiv $t0, $t1, $t2") + } + _ => false, + } + ); + let instruction: u32 = 0b00000001001010100100000010011111; + assert!( + match MipsInstruction::get_string_version(instruction, labels.clone(), 0) { + Ok(string) => { + string.contains("ddivu $t0, $t1, $t2") + } + _ => false, + } + ); + let instruction: u32 = 0b00000000101001100010000010011010; + assert!( + match MipsInstruction::get_string_version(instruction, labels.clone(), 0) { + Ok(string) => { + string.contains("div $a0, $a1, $a2") + } + _ => false, + } + ); + let instruction: u32 = 0b00000011111000000000000000001001; + assert!( + match MipsInstruction::get_string_version(instruction, labels.clone(), 0) { + Ok(string) => { + string.contains("jr $ra") + } + _ => false, + } + ); + + let instruction: u32 = 0b00000000010100010001000000101010; + assert!( + match MipsInstruction::get_string_version(instruction, labels.clone(), 0) { + Ok(string) => { + string.contains("slt $v0, $v0, $s1") + } + _ => false, + } + ); + let instruction: u32 = 0b00000010000100010100100000101011; + assert!( + match MipsInstruction::get_string_version(instruction, labels.clone(), 0) { + Ok(string) => { + string.contains("sltu $t1, $s0, $s1") + } + _ => false, + } + ); + + let instruction: u32 = 0b00000000000010000100101010000000; + assert!( + match MipsInstruction::get_string_version(instruction, labels.clone(), 0) { + Ok(string) => { + string.contains("sll $t1, $t0, 10") + } + _ => false, + } + ); + + let instruction: u32 = 0b00000000000111011111000000100101; + assert!( + match MipsInstruction::get_string_version(instruction, labels.clone(), 0) { + Ok(string) => { + string.contains("or $fp, $zero, $sp") + } + _ => false, + } + ); + + let instruction: u32 = 0b00000000000000001000000000100100; + assert!( + match MipsInstruction::get_string_version(instruction, labels.clone(), 0) { + Ok(string) => { + string.contains("and $s0, $zero, $zero") + } + _ => false, + } + ); +} + +// ** I-TYPE INSTRUCTIONS ** // +#[test] +fn get_string_version_from_i_type() { + // I-Type instructions: + // addi, addiu, daddi, daddiu + // lw, sw + // lui + // ori, andi + // dahi, dati + // beq, bne (FIX) + let labels: HashMap = HashMap::::new(); + + let instruction: u32 = 0b00100000000100010000000000000010; + assert!( + match MipsInstruction::get_string_version(instruction, labels.clone(), 0) { + Ok(string) => { + string.contains("addi $s1, $zero, 2") + } + _ => false, + } + ); + + let instruction: u32 = 0b00100111101111011111111111011000; + assert!( + match MipsInstruction::get_string_version(instruction, labels.clone(), 0) { + Ok(string) => { + string.contains("addiu $sp, $sp, -40") + } + _ => false, + } + ); + + let instruction: u32 = 0b01100011011011011111111111111101; + assert!( + match MipsInstruction::get_string_version(instruction, labels.clone(), 0) { + Ok(string) => { + string.contains("daddi $t5, $k1, -3") + } + _ => false, + } + ); + + let instruction: u32 = 0b01100110000100000000000000000001; + assert!( + match MipsInstruction::get_string_version(instruction, labels.clone(), 0) { + Ok(string) => { + string.contains("daddiu $s0, $s0, 1") + } + _ => false, + } + ); + + let instruction: u32 = 0b00010000010000000000000000000100; + let mut labels: HashMap = HashMap::::new(); + labels.insert("loop".to_string(), 0x00000004); + + // TODO: Fix this test to include labels + + assert!( + match MipsInstruction::get_string_version(instruction, labels.clone(), 0) { + Ok(string) => { + string.contains("beq $v0, $zero, ") + } + _ => false, + } + ); + + let instruction: u32 = 0b00010100010000000000000000000100; + let mut labels: HashMap = HashMap::::new(); + labels.insert("loop".to_string(), 0x00000004); + + // TODO: Fix this test to include labels + + assert!( + match MipsInstruction::get_string_version(instruction, labels.clone(), 0) { + Ok(string) => { + string.contains("bne $v0, $zero, ") + } + _ => false, + } + ); + + let instruction: u32 = 0b10101101110110010000000000000000; + assert!( + match MipsInstruction::get_string_version(instruction, labels.clone(), 0) { + Ok(string) => { + string.contains("sw $t9, 0($t6)") + } + _ => false, + } + ); + + let instruction: u32 = 0b10001111110000100000000000101000; + assert!( + match MipsInstruction::get_string_version(instruction, labels.clone(), 0) { + Ok(string) => { + string.contains("lw $v0, 40($fp)") + } + _ => false, + } + ); + + let instruction: u32 = 0b00111100000100000011111110000000; + assert!( + match MipsInstruction::get_string_version(instruction, labels.clone(), 0) { + Ok(string) => { + string.contains("lui $s0, 0x3f80") + } + _ => false, + } + ); + + let instruction: u32 = 0b00110100000011100000000111110100; + assert!( + match MipsInstruction::get_string_version(instruction, labels.clone(), 0) { + Ok(string) => { + string.contains("ori $t6, $zero, 500") + } + _ => false, + } + ); + + let instruction: u32 = 0b00110001010010010000000011111111; + assert!( + match MipsInstruction::get_string_version(instruction, labels.clone(), 0) { + Ok(string) => { + string.contains("andi $t1, $t2, 255") + } + _ => false, + } + ); + + let instruction: u32 = 0b00000101001001100000000000001111; + assert!( + match MipsInstruction::get_string_version(instruction, labels.clone(), 0) { + Ok(string) => { + string.contains("dahi $t1, 15") + } + _ => false, + } + ); + + let instruction: u32 = 0b00000101001111100000000000001111; + assert!( + match MipsInstruction::get_string_version(instruction, labels.clone(), 0) { + Ok(string) => { + string.contains("dati $t1, 15") + } + _ => false, + } + ); +} + +// ** J-TYPE INSTRUCTIONS ** // +#[test] +fn get_string_version_from_j_type() { + let mut labels: HashMap = HashMap::::new(); + labels.insert(String::from("fib(int)"), 0x0); + let instruction: u32 = 0b00001100000000000000000000000000; + + assert!( + match MipsInstruction::get_string_version(instruction, labels.clone(), 0) { + Ok(string) => { + string.contains("jal fib(int)") + } + _ => false, + } + ); +} + +// ** SYSCALL-TYPE INSTRUCTIONS ** // +#[test] +fn get_string_version_from_syscall_type() { + let labels: HashMap = HashMap::::new(); + let instruction: u32 = 0b00000000000000000000000000001100; + + assert!( + match MipsInstruction::get_string_version(instruction, labels.clone(), 0) { + Ok(string) => { + string.contains("syscall") + } + _ => false, + } + ); +} + +// ** FLOATING POINT INSTRUCTIONS ** // + +// ** FPU R-Type ** // +// add.fmt, sub.fmt, mul.fmt, div.fmt +#[test] +fn get_string_version_from_fpu_r_type() { + let labels: HashMap = HashMap::::new(); + let instruction: u32 = 0b01000110001001100010000010000000; + + assert!( + match MipsInstruction::get_string_version(instruction, labels.clone(), 0) { + Ok(string) => { + string.contains("add.d $f2, $f4, $f6") + } + _ => false, + } + ); + + let instruction: u32 = 0b01000110000001100010000010000000; + assert!( + match MipsInstruction::get_string_version(instruction, labels.clone(), 0) { + Ok(string) => { + string.contains("add.s $f2, $f4, $f6") + } + _ => false, + } + ); + + let instruction: u32 = 0b01000110001001100010000010000001; + assert!( + match MipsInstruction::get_string_version(instruction, labels.clone(), 0) { + Ok(string) => { + string.contains("sub.d $f2, $f4, $f6") + } + _ => false, + } + ); + + let instruction: u32 = 0b01000110000001100010000010000001; + assert!( + match MipsInstruction::get_string_version(instruction, labels.clone(), 0) { + Ok(string) => { + string.contains("sub.s $f2, $f4, $f6") + } + _ => false, + } + ); + + let instruction: u32 = 0b01000110001001100010000010000010; + assert!( + match MipsInstruction::get_string_version(instruction, labels.clone(), 0) { + Ok(string) => { + string.contains("mul.d $f2, $f4, $f6") + } + _ => false, + } + ); + + let instruction: u32 = 0b01000110000001100010000010000010; + assert!( + match MipsInstruction::get_string_version(instruction, labels.clone(), 0) { + Ok(string) => { + string.contains("mul.s $f2, $f4, $f6") + } + _ => false, + } + ); + + let instruction: u32 = 0b01000110001001100010000010000011; + assert!( + match MipsInstruction::get_string_version(instruction, labels.clone(), 0) { + Ok(string) => { + string.contains("div.d $f2, $f4, $f6") + } + _ => false, + } + ); + + let instruction: u32 = 0b01000110000001100010000010000011; + assert!( + match MipsInstruction::get_string_version(instruction, labels.clone(), 0) { + Ok(string) => { + string.contains("div.s $f2, $f4, $f6") + } + _ => false, + } + ); +} + +// ** FPU I-Type ** // +// lwc1, swc1 +#[test] +fn get_string_version_from_fpu_i_type() { + let labels: HashMap = HashMap::::new(); + let instruction: u32 = 0b11000101001000100000000000000000; + + assert!( + match MipsInstruction::get_string_version(instruction, labels.clone(), 0) { + Ok(string) => { + string.contains("lwc1 $f2, 0($t1)") + } + _ => false, + } + ); + + let instruction: u32 = 0b11100101001000100000000000000000; + assert!( + match MipsInstruction::get_string_version(instruction, labels.clone(), 0) { + Ok(string) => { + string.contains("swc1 $f2, 0($t1)") + } + _ => false, + } + ); +} + +// ** FPU Register-Immediate Type ** // +// mtc1, dmtc1, mfc1, dmfc1 +#[test] +fn get_string_version_from_fpu_register_immediate_type() { + let labels: HashMap = HashMap::::new(); + let instruction: u32 = 0b01000100100010010001000000000000; + + assert!( + match MipsInstruction::get_string_version(instruction, labels.clone(), 0) { + Ok(string) => { + string.contains("mtc1 $t1, $f2") + } + _ => false, + } + ); + + let instruction: u32 = 0b01000100000010010001000000000000; + assert!( + match MipsInstruction::get_string_version(instruction, labels.clone(), 0) { + Ok(string) => { + string.contains("mfc1 $t1, $f2") + } + _ => false, + } + ); + + let instruction: u32 = 0b01000100101010010001000000000000; + assert!( + match MipsInstruction::get_string_version(instruction, labels.clone(), 0) { + Ok(string) => { + string.contains("dmtc1 $t1, $f2") + } + _ => false, + } + ); + + let instruction: u32 = 0b01000100001010010001000000000000; + assert!( + match MipsInstruction::get_string_version(instruction, labels.clone(), 0) { + Ok(string) => { + string.contains("dmfc1 $t1, $f2") + } + _ => false, + } + ); +} + +// ** FPU Branch Type ** // +// bc1t, bc1f +#[test] +fn get_string_version_from_fpu_branch_type() { + let labels: HashMap = HashMap::::new(); + let instruction: u32 = 0b01000101000000010000000000000001; + + assert!( + match MipsInstruction::get_string_version(instruction, labels.clone(), 0) { + Ok(string) => { + string.contains("bc1t 1") + } + _ => false, + } + ); + + let instruction: u32 = 0b01000101000000000000000000000000; + assert!( + match MipsInstruction::get_string_version(instruction, labels.clone(), 0) { + Ok(string) => { + string.contains("bc1f 0") + } + _ => false, + } + ); +} + +// ** FPU Compare Type ** // +// c.eq.fmt, c.lt.fmt, c.le.fmt, c.ngt.fmt, c.nge.fmt +#[test] +fn get_string_version_from_fpu_compare_type() { + let labels: HashMap = HashMap::::new(); + let instruction: u32 = 0b01000110001001000001000000110010; + assert!( + match MipsInstruction::get_string_version(instruction, labels.clone(), 0) { + Ok(string) => { + string.contains("c.eq.d $f2, $f4") + } + _ => false, + } + ); + + let instruction: u32 = 0b01000110000001000001000000110010; + assert!( + match MipsInstruction::get_string_version(instruction, labels.clone(), 0) { + Ok(string) => { + string.contains("c.eq.s $f2, $f4") + } + _ => false, + } + ); + + let instruction: u32 = 0b01000110001001000001000000111110; + assert!( + match MipsInstruction::get_string_version(instruction, labels.clone(), 0) { + Ok(string) => { + string.contains("c.le.d $f2, $f4") + } + _ => false, + } + ); + + let instruction: u32 = 0b01000110000001000001000000111110; + assert!( + match MipsInstruction::get_string_version(instruction, labels.clone(), 0) { + Ok(string) => { + string.contains("c.le.s $f2, $f4") + } + _ => false, + } + ); + + let instruction: u32 = 0b01000110001001000001000000111100; + assert!( + match MipsInstruction::get_string_version(instruction, labels.clone(), 0) { + Ok(string) => { + string.contains("c.lt.d $f2, $f4") + } + _ => false, + } + ); + + let instruction: u32 = 0b01000110000001000001000000111100; + assert!( + match MipsInstruction::get_string_version(instruction, labels.clone(), 0) { + Ok(string) => { + string.contains("c.lt.s $f2, $f4") + } + _ => false, + } + ); + + let instruction: u32 = 0b01000110001001000001000000111101; + assert!( + match MipsInstruction::get_string_version(instruction, labels.clone(), 0) { + Ok(string) => { + string.contains("c.nge.d $f2, $f4") + } + _ => false, + } + ); + + let instruction: u32 = 0b01000110000001000001000000111101; + assert!( + match MipsInstruction::get_string_version(instruction, labels.clone(), 0) { + Ok(string) => { + string.contains("c.nge.s $f2, $f4") + } + _ => false, + } + ); + + let instruction: u32 = 0b01000110001001000001000000111111; + assert!( + match MipsInstruction::get_string_version(instruction, labels.clone(), 0) { + Ok(string) => { + string.contains("c.ngt.d $f2, $f4") + } + _ => false, + } + ); + + let instruction: u32 = 0b01000110000001000001000000111111; + assert!( + match MipsInstruction::get_string_version(instruction, labels.clone(), 0) { + Ok(string) => { + string.contains("c.ngt.s $f2, $f4") + } + _ => false, + } + ); +} + +// ** Test empty instruction ** // +#[test] +fn get_string_version_from_empty_instruction() { + let labels: HashMap = HashMap::::new(); + let instruction: u32 = 0b00000000000000000000000000000000; + assert!( + match MipsInstruction::get_string_version(instruction, labels.clone(), 0) { + Ok(string) => { + string.contains("nop") + } + _ => false, + } + ); +} diff --git a/src/tests/emulation_core/registers.rs b/src/tests/emulation_core/registers.rs index c912ffd66..695b87f90 100644 --- a/src/tests/emulation_core/registers.rs +++ b/src/tests/emulation_core/registers.rs @@ -1,4 +1,4 @@ -use crate::emulation_core::mips::registers::{GpRegisterType, GpRegisters}; +use crate::emulation_core::mips::gp_registers::{GpRegisterType, GpRegisters}; #[test] #[allow(clippy::field_reassign_with_default)] @@ -13,6 +13,7 @@ fn direct_access_register() { #[test] #[should_panic] #[allow(unconditional_panic)] +#[allow(clippy::out_of_bounds_indexing)] fn direct_access_register_bad_gpr() { let mut registers = GpRegisters::default(); diff --git a/src/tests/emulation_core/riscv.rs b/src/tests/emulation_core/riscv.rs new file mode 100644 index 000000000..7e29b1fb0 --- /dev/null +++ b/src/tests/emulation_core/riscv.rs @@ -0,0 +1,970 @@ +#![allow(clippy::unusual_byte_groupings)] + +use crate::emulation_core::datapath::Datapath; +use crate::emulation_core::riscv::datapath::RiscDatapath; +use crate::emulation_core::riscv::registers::RiscGpRegisterType; + +pub mod api { + use super::*; + use crate::{ + emulation_core::architectures::AvailableDatapaths, parser::parser_assembler_main::parser, + }; + + #[test] + fn reset_datapath() -> Result<(), String> { + let mut datapath = RiscDatapath::default(); + + // Add instruction into emulation core memory. + let instruction = String::from("ori s0, zero, 5"); + let (_, instruction_bits, _labels) = parser(instruction, AvailableDatapaths::RISCV); + datapath.initialize(0, instruction_bits)?; + + datapath.execute_instruction(); + + // Datapath should now have some data in it. + assert_ne!(datapath.registers.gpr[8], 0); // $s0 + assert_ne!(datapath.registers.pc, 0); + + datapath.reset(); + + // After resetting, these values should all be back to bitwise zero. + assert_eq!(datapath.memory.memory[0], 0); + assert_eq!(datapath.registers.gpr[16], 0); // $s0 + assert_eq!(datapath.registers.pc, 0); + + Ok(()) + } +} + +pub mod add { + use super::*; + #[test] + fn add_register_to_itself() -> Result<(), String> { + let mut datapath = RiscDatapath::default(); + + // $t1 = $t1 + $t1 + let instructions: Vec = vec![0b0000000_00110_00110_000_00110_0110011]; + datapath.initialize(0, instructions)?; + + // Assume the register $t1 has the value 5. + datapath.registers[RiscGpRegisterType::X6] = 5; + + datapath.execute_instruction(); + + // After the operation is finished, the register should be 10. + assert_eq!(datapath.registers[RiscGpRegisterType::X6], 10); + Ok(()) + } + + #[test] + fn add_register_to_another() -> Result<(), String> { + let mut datapath = RiscDatapath::default(); + + // $s2 = $s0 + $s1 + let instructions: Vec = vec![0b0000000_01001_01000_000_10010_0110011]; + datapath.initialize(0, instructions)?; + + datapath.registers.gpr[8] = 15; // $s0 + datapath.registers.gpr[9] = 40; // $s1 + + datapath.execute_instruction(); + + // Register $s2 should contain 55. + let result = datapath.registers.gpr[18] as u32; + assert_eq!(result, 55); + Ok(()) + } + + #[test] + // This test attempts to write to register $zero. The datapath should + // not overwrite this register, and remain with a value of 0. + fn add_to_register_zero() -> Result<(), String> { + let mut datapath = RiscDatapath::default(); + + // $zero = $t3 + $t3 + let instructions: Vec = vec![0b0000000_11100_11100_000_00000_0110011]; + datapath.initialize(0, instructions)?; + + datapath.registers.gpr[28] = 1234; // $t3 + + datapath.execute_instruction(); + + // $zero should still contain 0. + assert_eq!(datapath.registers.gpr[0], 0); + Ok(()) + } +} + +pub mod sub { + use super::*; + + #[test] + fn sub_positive_result() -> Result<(), String> { + let mut datapath = RiscDatapath::default(); + + // $s2 = $s3 - $s2 + let instructions: Vec = vec![0b0100000_10010_10011_000_10010_0110011]; + datapath.initialize(0, instructions)?; + + datapath.registers.gpr[19] = 7; // $s3 + datapath.registers.gpr[18] = 3; // $s2 + + datapath.execute_instruction(); + + // Register $s2 should contain 4, as 7 - 3 = 4. + assert_eq!(datapath.registers.gpr[18], 4); + Ok(()) + } + + #[test] + fn sub_negative_result() -> Result<(), String> { + let mut datapath = RiscDatapath::default(); + + // $s2 = $s3 - $s2 + let instructions: Vec = vec![0b0100000_10010_10011_000_10010_0110011]; + datapath.initialize(0, instructions)?; + + datapath.registers.gpr[19] = 3; // $s3 + datapath.registers.gpr[18] = 7; // $s2 + + datapath.execute_instruction(); + + // Register $s2 should contain 4, as 3 - 7 = -4. + assert_eq!(datapath.registers.gpr[18] as i64, -4); + Ok(()) + } +} + +pub mod or { + use super::*; + + #[test] + fn or_register_to_itself() -> Result<(), String> { + let mut datapath = RiscDatapath::default(); + + // $t1 = $t1 | $t1 + let instructions: Vec = vec![0b0000000_00110_00110_110_00110_0110011]; + datapath.initialize(0, instructions)?; + + // Assume the register $t1 has the value 5. + datapath.registers[RiscGpRegisterType::X6] = 0x5; + + datapath.execute_instruction(); + assert_eq!(datapath.registers[RiscGpRegisterType::X6], 0x5); + Ok(()) + } + + #[test] + fn or_register_to_another() -> Result<(), String> { + let mut datapath = RiscDatapath::default(); + + // $s2 = $s0 | $s1 + let instructions: Vec = vec![0b0000000_01001_01000_110_10010_0110011]; + datapath.initialize(0, instructions)?; + + datapath.registers.gpr[8] = 0x1234; // $s0 + datapath.registers.gpr[9] = 0x4321; // $s1 + + datapath.execute_instruction(); + + // Register $s2 should contain 55. + let result = datapath.registers.gpr[18]; + assert_eq!(result, 0x5335); + Ok(()) + } + + #[test] + // This test attempts to write to register $zero. The datapath should + // not overwrite this register, and remain with a value of 0. + fn or_to_register_zero() -> Result<(), String> { + let mut datapath = RiscDatapath::default(); + + // $zero = $t3 | $t3 + let instructions: Vec = vec![0b0000000_11100_11100_110_00000_0110011]; + datapath.initialize(0, instructions)?; + + datapath.registers.gpr[28] = 1234; // $t3 + + datapath.execute_instruction(); + + // $zero should still contain 0. + assert_eq!(datapath.registers.gpr[0], 0); + Ok(()) + } +} + +pub mod and { + use super::*; + + #[test] + fn and_register_to_itself() -> Result<(), String> { + let mut datapath = RiscDatapath::default(); + + // $t1 = $t1 & $t1 + let instructions: Vec = vec![0b0000000_00110_00110_111_00110_0110011]; + datapath.initialize(0, instructions)?; + + // Assume the register $t1 has the value 5. + datapath.registers[RiscGpRegisterType::X6] = 0x5; + + datapath.execute_instruction(); + assert_eq!(datapath.registers[RiscGpRegisterType::X6], 0x5); + Ok(()) + } + + #[test] + fn and_register_to_another() -> Result<(), String> { + let mut datapath = RiscDatapath::default(); + + // $s2 = $s0 & $s1 + let instructions: Vec = vec![0b0000000_01001_01000_111_10010_0110011]; + datapath.initialize(0, instructions)?; + + datapath.registers.gpr[8] = 0x1234; // $s0 + datapath.registers.gpr[9] = 0x4321; // $s1 + + datapath.execute_instruction(); + + // Register $s2 should contain 55. + let result = datapath.registers.gpr[18]; + assert_eq!(result, 0x0220); + Ok(()) + } + + #[test] + // This test attempts to write to register $zero. The datapath should + // not overwrite this register, and remain with a value of 0. + fn and_to_register_zero() -> Result<(), String> { + let mut datapath = RiscDatapath::default(); + + // $zero = $t3 & $t3 + let instructions: Vec = vec![0b0000000_11100_11100_111_00000_0110011]; + datapath.initialize(0, instructions)?; + + datapath.registers.gpr[28] = 1234; // $t3 + + datapath.execute_instruction(); + + // $zero should still contain 0. + assert_eq!(datapath.registers.gpr[0], 0); + Ok(()) + } +} + +pub mod andi { + use super::*; + #[test] + fn and_immediate_with_zero() -> Result<(), String> { + let mut datapath = RiscDatapath::default(); + + // $s0 = $zero & 1234 + let instructions: Vec = vec![0b010011010010_00000_111_01000_0010011]; + datapath.initialize(0, instructions)?; + + datapath.execute_instruction(); + + assert_eq!(datapath.registers.gpr[8], 0); // $s0 + Ok(()) + } + + #[test] + fn andi_immediate_with_value() -> Result<(), String> { + let mut datapath = RiscDatapath::default(); + + // $s0 = $t0 & 1234 + let instructions: Vec = vec![0b010011010010_00101_111_01000_0010011]; + datapath.initialize(0, instructions)?; + + // In binary: 00111010 11011110 01101000 10110001 + datapath.registers.gpr[5] = 987654321; // $t0 + + datapath.execute_instruction(); + + // The result should be as follows: + // $t0: 00111010 11011110 01101000 10110001 + // AND 1,234: 00000100 11010010 + // ================================================= + // 144: 00000000 00000000 00000000 10010000 + + assert_eq!(datapath.registers.gpr[8], 0x90); // $s0 + Ok(()) + } +} + +pub mod ori { + use super::*; + #[test] + fn or_immediate_with_zero() -> Result<(), String> { + let mut datapath = RiscDatapath::default(); + + // $s0 = $zero | 1234 + let instructions: Vec = vec![0b010011010010_00000_110_01000_0010011]; + datapath.initialize(0, instructions)?; + + datapath.execute_instruction(); + + assert_eq!(datapath.registers.gpr[8], 1234); // $s0 + Ok(()) + } + + #[test] + fn or_immediate_with_value() -> Result<(), String> { + let mut datapath = RiscDatapath::default(); + + // $s0 = $t0 | 1234 + let instructions: Vec = vec![0b010011010010_00101_110_01000_0010011]; + datapath.initialize(0, instructions)?; + + // In binary: 00111010 11011110 01101000 10110001 + datapath.registers.gpr[5] = 987654321; // $t0 + + datapath.execute_instruction(); + + // The result should be as follows: + // $t0: 00111010 11011110 01101000 10110001 + // OR 1,234: 00000100 11010010 + // ================================================= + // 00111010 11011110 01101100 11110011 + + assert_eq!(datapath.registers.gpr[8], 0x3ade6cf3); // $s0 + Ok(()) + } +} + +// Shift Left +pub mod sll { + use super::*; + #[test] + fn easy_test() -> Result<(), String> { + let mut datapath = RiscDatapath::default(); + + let instructions: Vec = vec![0b0000000_10010_10001_001_10011_0110011]; + datapath.initialize(0, instructions)?; + + datapath.registers.gpr[0b10001] = 0b101; + datapath.registers.gpr[0b10010] = 0b1; + + datapath.execute_instruction(); + assert_eq!(datapath.registers.gpr[0b10011], 0b1010); + Ok(()) + } + + #[test] + fn harder_test() -> Result<(), String> { + let mut datapath = RiscDatapath::default(); + + // Shift left by two logical + let instructions: Vec = vec![0b0000000_10010_10001_001_10011_0110011]; + datapath.initialize(0, instructions)?; + + datapath.registers.gpr[0b10001] = 60; + datapath.registers.gpr[0b10010] = 3; + + datapath.execute_instruction(); + println!("hmm {:#02x}", datapath.registers.gpr[0b10010]); + assert_eq!(datapath.registers.gpr[0b10011], 480); + Ok(()) + } +} + +// Shift Right +pub mod sr { + use super::*; + #[test] + fn srl_test() -> Result<(), String> { + let mut datapath = RiscDatapath::default(); + + let instructions: Vec = vec![0b0000000_10010_10001_101_10011_0110011]; + datapath.initialize(0, instructions)?; + + datapath.registers.gpr[0b10001] = 360; + datapath.registers.gpr[0b10010] = 1; + + datapath.execute_instruction(); + assert_eq!(datapath.registers.gpr[0b10011], 180); + Ok(()) + } + + #[test] + fn sra_test() -> Result<(), String> { + let mut datapath = RiscDatapath::default(); + + // Shift left by two logical + let instructions: Vec = vec![0b0100000_10010_10001_101_10011_0110011]; + datapath.initialize(0, instructions)?; + + datapath.registers.gpr[0b10001] = 0xf00f_0ff0_f0f0_0f0f; + datapath.registers.gpr[0b10010] = 4; + + datapath.execute_instruction(); + println!("hmm {:#02x}", datapath.registers.gpr[0b10011]); + assert_eq!(datapath.registers.gpr[0b10011], 0xff00_f0ff_0f0f_00f0); + Ok(()) + } +} + +pub mod slt { + use super::*; + + #[test] + fn easy_rs_less_than_rt_test() -> Result<(), String> { + let mut datapath = RiscDatapath::default(); + + // $s2 = $s0 < $s1 + let instructions: Vec = vec![0b0000000_01001_01000_010_10010_0110011]; + datapath.initialize(0, instructions)?; + + datapath.registers[RiscGpRegisterType::X8] = 1; + datapath.registers[RiscGpRegisterType::X9] = 123; + + datapath.execute_instruction(); + + assert_eq!(datapath.registers[RiscGpRegisterType::X18], 1); + Ok(()) + } + + #[test] + fn easy_rs_greater_than_rt_test() -> Result<(), String> { + let mut datapath = RiscDatapath::default(); + + // $s2 = $s0 < $s1 + let instructions: Vec = vec![0b0000000_01001_01000_010_10010_0110011]; + datapath.initialize(0, instructions)?; + + datapath.registers[RiscGpRegisterType::X8] = 124; + datapath.registers[RiscGpRegisterType::X9] = 123; + + datapath.execute_instruction(); + + assert_eq!(datapath.registers[RiscGpRegisterType::X18], 0); + Ok(()) + } + + #[test] + fn easy_signed_test() -> Result<(), String> { + let mut datapath = RiscDatapath::default(); + + // $s2 = $s0 < $s1 + let instructions: Vec = vec![0b0000000_01001_01000_010_10010_0110011]; + datapath.initialize(0, instructions)?; + + datapath.registers[RiscGpRegisterType::X8] = -124_i64 as u64; + datapath.registers[RiscGpRegisterType::X9] = 123; + + datapath.execute_instruction(); + + assert_eq!(datapath.registers[RiscGpRegisterType::X18], 1); + Ok(()) + } +} + +pub mod sltu { + use super::*; + + #[test] + fn easy_rs_less_than_rt_test() -> Result<(), String> { + let mut datapath = RiscDatapath::default(); + + // $s2 = $s0 < $s1 + let instructions: Vec = vec![0b0000000_01001_01000_011_10010_0110011]; + datapath.initialize(0, instructions)?; + + datapath.registers[RiscGpRegisterType::X8] = 1; + datapath.registers[RiscGpRegisterType::X9] = 123; + + datapath.execute_instruction(); + + assert_eq!(datapath.registers[RiscGpRegisterType::X18], 1); + Ok(()) + } + + #[test] + fn easy_rs_greater_than_rt_test() -> Result<(), String> { + let mut datapath = RiscDatapath::default(); + + // $s2 = $s0 < $s1 + let instructions: Vec = vec![0b0000000_01001_01000_011_10010_0110011]; + datapath.initialize(0, instructions)?; + + datapath.registers[RiscGpRegisterType::X8] = 124; + datapath.registers[RiscGpRegisterType::X9] = 123; + + datapath.execute_instruction(); + + assert_eq!(datapath.registers[RiscGpRegisterType::X18], 0); + Ok(()) + } + + #[test] + fn easy_signed_test() -> Result<(), String> { + let mut datapath = RiscDatapath::default(); + + // $s2 = $s0 < $s1 + let instructions: Vec = vec![0b0000000_01001_01000_011_10010_0110011]; + datapath.initialize(0, instructions)?; + + datapath.registers[RiscGpRegisterType::X8] = -124_i64 as u64; + datapath.registers[RiscGpRegisterType::X9] = 123; + + datapath.execute_instruction(); + + assert_eq!(datapath.registers[RiscGpRegisterType::X18], 0); + Ok(()) + } +} + +pub mod addi_addiu { + use super::*; + #[test] + fn addi_simple_test() -> Result<(), String> { + let mut datapath = RiscDatapath::default(); + + // $s0 = $t0 + 0x4 + let instructions: Vec = vec![0b000000000100_00101_000_01000_0010011]; + datapath.initialize(0, instructions)?; + datapath.registers[RiscGpRegisterType::X5] = 1; + datapath.registers[RiscGpRegisterType::X8] = 123; + datapath.execute_instruction(); + + assert_eq!(datapath.registers[RiscGpRegisterType::X8], 5); + Ok(()) + } + + #[test] + fn addi_overflow_test() -> Result<(), String> { + let mut datapath = RiscDatapath::default(); + + // $s0 = $t0 + 0x4 + let instructions: Vec = vec![0b000000000100_00101_000_01000_0010011]; + datapath.initialize(0, instructions)?; + datapath.registers[RiscGpRegisterType::X5] = 0xffffffffffffffff; + datapath.registers[RiscGpRegisterType::X8] = 123; + datapath.execute_instruction(); + + // If there is an overflow on addi, $s0 should not change. + assert_eq!(datapath.registers[RiscGpRegisterType::X8], 3); + Ok(()) + } + + #[test] + fn addi_sign_extend_test() -> Result<(), String> { + let mut datapath = RiscDatapath::default(); + + // $s0 = $t0 + 0x1 + let instructions: Vec = vec![0b000000000001_00101_000_01000_0010011]; + datapath.initialize(0, instructions)?; + datapath.registers[RiscGpRegisterType::X5] = 0xfffffffffffffff1; + datapath.execute_instruction(); + + assert_eq!( + datapath.registers[RiscGpRegisterType::X8], + 0xfffffffffffffff2 + ); + Ok(()) + } +} + +pub mod load_word { + use super::*; + #[test] + fn lw_zero_offset_test() -> Result<(), String> { + // for this test the lw instruction will load itself from + // memory + let mut datapath = RiscDatapath::default(); + + let instructions: Vec = vec![0b000000000000_01000_010_10000_0000011]; + datapath.initialize(0, instructions.clone())?; + datapath.execute_instruction(); + assert_eq!(datapath.registers.gpr[16], instructions[0] as u64); + Ok(()) + } + + #[test] + fn lw_offset_at_4_test() -> Result<(), String> { + // For this test the lw instruction will load 0x4 from memory + // by using the offset address plus zero + let mut datapath = RiscDatapath::default(); + + let instructions: Vec = vec![0b000000000100_01000_010_10000_0000011]; + datapath.initialize(0, instructions)?; + + // place data at address + datapath.memory.store_word(0b100, 0x10000)?; + + datapath.registers.gpr[8] = 0; + datapath.execute_instruction(); + assert_eq!(datapath.registers.gpr[16], 0x10000); + Ok(()) + } + + #[test] + fn lw_gpr_8_at_4_offset_at_0_test() -> Result<(), String> { + // for this test the lw instruction will load 0x4 from memory + // by using (offset = 0) + (gpr[8] = 4) + let mut datapath = RiscDatapath::default(); + + let instructions: Vec = vec![0b000000000000_01000_010_10000_0000011]; + datapath.initialize(0, instructions)?; + + // place data at address + datapath.memory.store_word(0b100, 0x10000)?; + + datapath.registers.gpr[8] = 4; + datapath.execute_instruction(); + assert_eq!(datapath.registers.gpr[16], 0x10000); + Ok(()) + } + + #[test] + fn lw_gpr_8_at_4_offset_at_4_test() -> Result<(), String> { + // for this test the lw instruction will load 0x8 from memory + // by adding the offset to gpr[8] + let mut datapath = RiscDatapath::default(); + + let instructions: Vec = vec![0b000000000100_01000_010_10000_0000011]; + datapath.initialize(0, instructions)?; + + // place data at address + datapath.memory.store_word(0b1000, 0x10000)?; + + datapath.registers.gpr[8] = 4; + datapath.execute_instruction(); + assert_eq!(datapath.registers.gpr[16], 0x10000); + Ok(()) + } + + #[test] + fn lw_gpr_8_at_12_offset_at_neg_4_test() -> Result<(), String> { + // for this test the lw instruction will load 0x8 from memory + // by adding the offset to gpr[8] + let mut datapath = RiscDatapath::default(); + + let instructions: Vec = vec![0b111111111100_01000_010_10000_0000011]; + datapath.initialize(0, instructions)?; + + // place data at address + datapath.memory.store_word(0b1000, 0x10000)?; + + datapath.registers.gpr[8] = 12; + datapath.execute_instruction(); + assert_eq!(datapath.registers.gpr[16], 0x10000); + Ok(()) + } +} + +pub mod store_word { + use super::*; + #[test] + fn sw_zero_offset_test() -> Result<(), String> { + let mut datapath = RiscDatapath::default(); + + let instructions: Vec = vec![0b0000000_10000_01000_010_00000_0100011]; + datapath.initialize(0, instructions)?; + datapath.execute_instruction(); + + let t = datapath.memory.load_word(0)?; + assert_eq!(t, 0); + Ok(()) + } + + #[test] + fn sw_offset_at_4_test() -> Result<(), String> { + let mut datapath = RiscDatapath::default(); + + let instructions: Vec = vec![0b0000000_10000_01000_010_00100_0100011]; + datapath.initialize(0, instructions)?; + + datapath.registers.gpr[8] = 0; + datapath.registers.gpr[16] = 0xff; + datapath.execute_instruction(); + + let t = datapath.memory.load_word(4)?; + assert_eq!(t, 0xff); + Ok(()) + } + + #[test] + fn sw_gpr_8_at_4_offset_at_4_test() -> Result<(), String> { + let mut datapath = RiscDatapath::default(); + + let instructions: Vec = vec![0b0000000_10000_01000_010_00100_0100011]; + datapath.initialize(0, instructions)?; + + datapath.registers.gpr[8] = 4; + datapath.registers.gpr[16] = 0xff; + datapath.execute_instruction(); + + let t = datapath.memory.load_word(8)?; + assert_eq!(t, 0xff); + Ok(()) + } + + #[test] + fn sw_gpr_8_at_4_offset_at_neg_4_test() -> Result<(), String> { + let mut datapath = RiscDatapath::default(); + + let instructions: Vec = vec![0b1111111_10000_01000_010_11100_0100011]; + datapath.initialize(0, instructions)?; + + datapath.registers.gpr[8] = 12; + datapath.registers.gpr[16] = 0xff; + datapath.execute_instruction(); + + let t = datapath.memory.load_word(8)?; + assert_eq!(t, 0xff); + Ok(()) + } +} + +pub mod mul { + use super::*; + + #[test] + fn mul_positive_result() -> Result<(), String> { + let mut datapath = RiscDatapath::default(); + + // $s5 = $t5 * $t6 + let instructions: Vec = vec![0b0000001_11111_11110_000_10101_0110011]; + datapath.initialize(0, instructions)?; + + datapath.registers.gpr[30] = 8; // $t5 + datapath.registers.gpr[31] = 95; // $t6 + + datapath.execute_instruction(); + + assert_eq!(datapath.registers.gpr[21], 760); // $s5 + Ok(()) + } + + #[test] + fn mul_negative_result() -> Result<(), String> { + let mut datapath = RiscDatapath::default(); + + // $s5 = $t5 * $t6 + let instructions: Vec = vec![0b0000001_11111_11110_000_10101_0110011]; + datapath.initialize(0, instructions)?; + + datapath.registers.gpr[30] = 5; // $t5 + datapath.registers.gpr[31] = -5_i64 as u64; // $t6 + + datapath.execute_instruction(); + + assert_eq!(datapath.registers.gpr[21] as i64, -25); // $s5 + Ok(()) + } +} + +pub mod div { + use super::*; + + #[test] + fn div_positive_result() -> Result<(), String> { + let mut datapath = RiscDatapath::default(); + + // $s4 = $t6 / $t5 + let instructions: Vec = vec![0b0000001_11110_11111_100_10100_0110011]; + datapath.initialize(0, instructions)?; + + datapath.registers.gpr[31] = 20; // $t6 + datapath.registers.gpr[30] = 2; // $t5 + + datapath.execute_instruction(); + + assert_eq!(datapath.registers.gpr[20], 10); // $s5 + Ok(()) + } + + #[test] + fn div_negative_result() -> Result<(), String> { + let mut datapath = RiscDatapath::default(); + + // $s4 = $t6 / $t5 + let instructions: Vec = vec![0b0000001_11110_11111_100_10100_0110011]; + datapath.initialize(0, instructions)?; + + datapath.registers.gpr[31] = 20; // $t6 + datapath.registers.gpr[30] = -5_i64 as u64; // $t5 + + datapath.execute_instruction(); + + assert_eq!(datapath.registers.gpr[20] as i64, -4); // $s5 + Ok(()) + } +} + +pub mod load_upper_imm { + use super::*; + + #[test] + fn basic_load_upper_imm_test() -> Result<(), String> { + let mut datapath = RiscDatapath::default(); + + let instructions: Vec = vec![0b00101010101010100000_01000_0110111]; + datapath.initialize(0, instructions)?; + datapath.execute_instruction(); + + let t = datapath.registers[RiscGpRegisterType::X8]; + assert_eq!(t, 0x2aaa_0000); + Ok(()) + } + + #[test] + fn sign_extend_load_upper_imm_test() -> Result<(), String> { + let mut datapath = RiscDatapath::default(); + + let instructions: Vec = vec![0b10101010101010100000_01000_0110111]; + datapath.initialize(0, instructions)?; + datapath.execute_instruction(); + + let t = datapath.registers[RiscGpRegisterType::X8]; + assert_eq!(t, 0xffff_ffff_aaaa_0000); + Ok(()) + } +} + +pub mod beq_tests { + use super::*; + #[test] + fn beq_test_basic_registers_are_equal() -> Result<(), String> { + let mut datapath = RiscDatapath::default(); + + let instructions: Vec = vec![0b000000000100_10000_000_01000_1100011]; + datapath.initialize(0, instructions)?; + + let initial_pc = datapath.registers.pc; + datapath.execute_instruction(); + let expt_result = (0b0000_0000_0000_0100 << 2) + initial_pc; + assert_eq!(datapath.registers.pc, expt_result); + Ok(()) + } + + #[test] + fn beq_test_basic_register_are_not_equal() -> Result<(), String> { + let mut datapath = RiscDatapath::default(); + + let instructions: Vec = vec![ + 0b000000000100_10000_000_01000_1100011, + 0b000000000100_10000_000_01000_1100011, + ]; + datapath.initialize(0, instructions)?; + + datapath.registers.gpr[0b01000] = 1234; + datapath.registers.gpr[0b10000] = 4321; + + datapath.execute_instruction(); + assert_eq!(datapath.registers.pc, 4); + + datapath.execute_instruction(); + assert_eq!(datapath.registers.pc, 8); + Ok(()) + } + + #[test] + fn beq_test_basic_branch_backwards() -> Result<(), String> { + let mut datapath = RiscDatapath::default(); + + let instructions: Vec = vec![ + 0b000000000100_10000_000_01000_1100011, // 0x00, Branch to 0x10 + 0, // 0x04 + 0, // 0x08 + 0, // 0x0c + 0b000000000000_10000_000_01000_1100011, // 0x10, Branch to 0x00 + ]; + datapath.initialize(0, instructions)?; + datapath.registers.gpr[0b01000] = 1234; + datapath.registers.gpr[0b10000] = 1234; + + // 0x10, aka 16 + let expt_result = 0x10; + + datapath.execute_instruction(); // branch to address 16 from address 0 + assert_eq!(datapath.registers.pc, expt_result); + assert_eq!(datapath.registers.gpr[0b01000], 1234); + assert_eq!(datapath.registers.gpr[0b10000], 1234); + + // 0x00 + let expt_result = 0x00; + + datapath.execute_instruction(); + assert_eq!(datapath.registers.pc, expt_result); + + // Some loop stuff: + datapath.execute_instruction(); // Branch to 0x10 + datapath.execute_instruction(); // Branch to 0x00 + datapath.execute_instruction(); // Branch to 0x10 + datapath.execute_instruction(); // Branch to 0x00 + datapath.execute_instruction(); // Branch to 0x10 + datapath.execute_instruction(); // Branch to 0x00 + assert_eq!(datapath.registers.pc, expt_result); + Ok(()) + } +} + +pub mod bne_tests { + use super::*; + #[test] + fn bne_test_basic_registers_are_equal() -> Result<(), String> { + // There should be no branching, the rs and rt are equal + + let mut datapath = RiscDatapath::default(); + + let instructions: Vec = vec![0b000000000100_10000_001_01000_1100011]; + datapath.registers.gpr[0b01000] = 1234; + datapath.registers.gpr[0b10000] = 1234; + datapath.initialize(0, instructions)?; + datapath.execute_instruction(); + let expt_result = 4; // PC + 4, PC starts at 0 with the bne instruction at address 0, no branch acures + assert_eq!(datapath.registers.pc, expt_result); + + Ok(()) + } + + #[test] + fn bne_test_loop() -> Result<(), String> { + // This test starts with Branching from 0x0 to 0x8. + // then from 0x8, at branch to 0x20. + // then from 0x20 back to 0x8. + // then from 0x8 to 0x20 + // backcally we have a loop of branching forever + + let mut datapath = RiscDatapath::default(); + let instructions: Vec = vec![ + 0b000000000010_10000_001_01000_1100011, // 0x00, Branch to 0x8 + 0, // 0x04 + 0b000000001000_10000_001_01000_1100011, // 0x08, Branch to 0x20 + 0, // 0x0c + 0, // 0x10 + 0, // 0x14 + 0, // 0x18 + 0, // 0x1c + 0b000000000010_10000_001_01000_1100011, // 0x20, branch to 0x08 + ]; + datapath.initialize(0, instructions)?; + datapath.registers.gpr[0b01000] = 1234; + datapath.registers.gpr[0b10000] = 4321; + + // test 0x0 to 0x8 + datapath.execute_instruction(); + assert_eq!(datapath.registers.pc, 8); + + // Branch from 0x8 to 0x20, aka from 8 to 32, branch by 24 + datapath.execute_instruction(); + let expt_result = 0b0000000000001000 << 2; // 32 + assert_eq!(datapath.registers.pc, expt_result); + + // Branch back to 0x8 from 0x20, aka 32 to 8 + let expt_result = 0x8; + + datapath.execute_instruction(); // branch to 0x08 + assert_eq!(datapath.registers.pc as i64, expt_result as i64); + + // loop around a few times + datapath.execute_instruction(); // branch to 0x20 + datapath.execute_instruction(); // branch to 0x08 + datapath.execute_instruction(); // branch to 0x20 + datapath.execute_instruction(); // branch to 0x08 + assert_eq!(datapath.registers.pc as i64, expt_result as i64); + + Ok(()) + } +} diff --git a/src/tests/emulation_core/riscv_instruction.rs b/src/tests/emulation_core/riscv_instruction.rs new file mode 100644 index 000000000..be3742341 --- /dev/null +++ b/src/tests/emulation_core/riscv_instruction.rs @@ -0,0 +1,328 @@ +use std::collections::HashMap; + +use crate::emulation_core::riscv::instruction::RiscInstruction; + +// *** Test negative number instructions *** + +#[test] +fn test_instruction_negative_16() { + let instruction: u32 = 0xff010113; + + let labels: HashMap = HashMap::::new(); + + assert!( + match RiscInstruction::get_string_version(instruction, labels.clone()) { + Ok(string) => string.contains("addi x2, x2, -16"), + _ => false, + } + ); +} + +#[test] +fn test_instruction_negative_2() { + let instruction: u32 = 0xffe50513; + + let labels: HashMap = HashMap::::new(); + + assert!( + match RiscInstruction::get_string_version(instruction, labels.clone()) { + Ok(string) => string.contains("addi x10, x10, -2"), + _ => false, + } + ); +} + +#[test] +fn test_jal_instruction() { + let instruction: u32 = 0x00008067; + + let labels: HashMap = HashMap::::new(); + + assert!( + match RiscInstruction::get_string_version(instruction, labels.clone()) { + Ok(string) => string.contains("jalr x0, x1, 0"), + _ => false, + } + ); +} + +// ** Test all other instructions ** + +#[test] +fn err_on_empty_instruction() { + let instruction: u32 = 0b00000000000000000000000000000000; + + let labels: HashMap = HashMap::::new(); + + assert!( + match RiscInstruction::get_string_version(instruction, labels.clone()) { + Err(e) => e.contains("not supported"), + _ => false, + } + ); +} + +#[test] +fn test_instructions() { + let instructions: Vec = vec![ + 0b10010001101000101000010110111, + 0b1000000000000000100010111, + 0b101000001000000110010011, + 0b10100010010001000010011, + 0b1111111100011100001010010011, + 0b1111000000100110001100010011, + 0b111100101111001110010011, + 0b1000110001010000010011, + 0b00000000001100111101010010010011, + 0b1000000000101000101010100010011, + 0b10000011000010110110011, + 0b1000000011000101000011000110011, + 0b100000111001011010110011, + 0b101001001010011100110011, + 0b110001011011011110110011, + 0b111001101110100000110011, + 0b1000001111111100010110011, + 0b1110011, + 0b100000000000001110011, + 0b1000000000000001110011, + 0b10000001000000000000001110011, + 0b110000001000000000000001110011, + 0b10000010100000000000001110011, + 0b1000110000000011, + 0b10000010001110010000011, + 0b100000011010110100000011, + 0b110000100100110110000011, + 0b1000000101101111000000011, + 0b1110100110000101000100011, + 0b1111000111001110000100011, + 0b1111101000010111000100011, + // 0b10011101111, + 0b1001000010101100111, + // 0b1100000010111100011, + // 0b1110001011011100011, + // 0b10000100011111100011, + // 0b10010101100011100011, + // 0b10100110100111100011, + // 0b10110111101011100011, + 0b101011000000101110011011, + 0b1011001001110010011011, + 0b1111010101110100011011, + 0b1110011011000110110111011, + 0b1000001110111100000111000111011, + 0b1111011101001111010111011, + 0b1111111110101111100111011, + 0b1000000000111111101111110111011, + 0b10110000010000011, + 0b100000011011000100000011, + 0b1100100011100000100011, + 0b10011000101000001000110011, + 0b10011100110001001010110011, + 0b10100000111010001100110011, + 0b10100101000011001110110011, + 0b10101001001100010000110011, + 0b10101101010101010010110011, + 0b10110001011110010100110011, + 0b10110101100111010110110011, + 0b10111001101000011000111011, + 0b10111101110100011010111011, + 0b11000001111101011100111011, + 0b11000110000110011110111011, + 0b11001010001111100000111011, + 0b100000001100010111000011000011, + 0b101000010000011111000101000111, + 0b110000010100100111000111001011, + 0b111000011000101111001001001111, + 0b11100110111001011010011, + 0b1000100000111111001101010011, + 0b10000100101000111001111010011, + 0b11000101001001111010001010011, + 0b1011000000001010111010011010011, + 0b100000110001011000010101010011, + 0b100000110101100001010111010011, + 0b100000111001101010011001010011, + 0b101000111101110000011011010011, + 0b101001000001111001011101010011, + 0b11000000000000001111000011010011, + 0b11000000000100010111000101010011, + 0b11100000000000011000000111010011, + 0b10100000010100100010001001010011, + 0b10100000011000101001001011010011, + 0b10100000011100110000001101010011, + 0b11100000000000111001001111010011, + 0b11010000000001000111000011010011, + 0b11010000000101001111000101010011, + 0b11110000000001010000000111010011, + 0b100010001100010111000011000011, + 0b101010010000011111000101000111, + 0b110010010100100111000111001111, + 0b111010011000101111001001001011, + 0b10011100110111001011010011, + 0b1010100000111111001101010011, + 0b10010100101000111001111010011, + 0b11010101001001111010001010011, + 0b1011010000001010111010011010011, + 0b100010110001011000010101010011, + 0b100010110101100001010111010011, + 0b100010111001101010011001010011, + 0b101010111101110000011011010011, + 0b101011000001111001011101010011, + 0b1000000000110000111011111010011, + 0b1000010000010001111100001010011, + 0b10100011001010001010100101010011, + 0b10100011001110010001100111010011, + 0b10100011010010011000101001010011, + 0b11100010000010100001101011010011, + 0b11000010000010101111101101010011, + 0b11000010000110110111101111010011, + 0b11010010000010111111100011010011, + 0b11010010000111000111100101010011, + 0b1010100110000111, + 0b1010000010010001000100111, + 0b100000011011101010000111, + 0b1011000100011011000100111, + 0b11000000001000001111110001010011, + 0b11000000001100010111110011010011, + 0b11010000001011001111000111010011, + 0b11010000001111010111001001010011, + ]; + + let labels: HashMap = HashMap::new(); + + let expected_instructions = vec![ + "lui x1, 74565", + "auipc x2, 4096", + "addi x3, x1, 10", + "slti x4, x2, 5", + "xori x5, x3, 255", + "ori x6, x4, 240", + "andi x7, x5, 15", + "slli x8, x6, 2", + "srli x9, x7, 3", + "srai x10, x8, 1", + "add x11, x3, x4", + "sub x12, x5, x6", + "sll x13, x7, x8", + "slt x14, x9, x10", + "sltu x15, x11, x12", + "or x16, x13, x14", + "and x17, x15, x16", + "ecall", + "ebreak", + "uret", + "sret", + "mret", + "wfi", + "lb x24, 0(x1)", + "lh x25, 4(x2)", + "lw x26, 8(x3)", + "lbu x27, 12(x4)", + "lhu x28, 16(x5)", + "sb x29, 20(x6)", + "sh x30, 24(x7)", + "sw x31, 28(x8)", + // "jal x9, 0x100", + "jalr x10, x9, 0", + // "beq x11, x12, 0x100", + // "bne x13, x14, 0x101", + // "blt x15, x16, 0x102", + // "bge x17, x18, 0x103", + // "bltu x19, x20, 0x104", + // "bgeu x21, x22, 0x105", + "addiw x23, x24, 10", + "slliw x25, x25, 2", + "srliw x26, x26, 3", + "addw x27, x27, x28", + "subw x28, x28, x29", + "sllw x29, x29, x30", + "srlw x30, x30, x31", + "sraw x31, x31, x1", + "lwu x1, 0(x2)", + "ld x2, 8(x3)", + "sd x3, 16(x4)", + "mul x4, x5, x6", + "mulh x5, x6, x7", + "mulhsu x6, x7, x8", + "mulhu x7, x8, x9", + "div x8, x9, x10", + "divu x9, x10, x11", + "rem x10, x11, x12", + "remu x11, x12, x13", + "mulw x12, x13, x14", + "divw x13, x14, x15", + "divuw x14, x15, x16", + "remw x15, x16, x17", + "remuw x16, x17, x18", + "fmadd.s f1, f2, f3, f4", + "fmsub.s f2, f3, f4, f5", + "fnmsub.s f3, f4, f5, f6", + "fnmadd.s f4, f5, f6, f7", + "fadd.s f5, f6, f7", + "fsub.s f6, f7, f8", + "fmul.s f7, f8, f9", + "fdiv.s f8, f9, f10", + "fsqrt.s f9, f10", + "fsgnj.s f10, f11, f12", + "fsgnjn.s f11, f12, f13", + "fsgnjx.s f12, f13, f14", + "fmin.s f13, f14, f15", + "fmax.s f14, f15, f16", + "fcvt.w.s x1, f1", + "fcvt.wu.s x2, f2", + "fmv.x.w x3, f3", + "feq.s x4, f4, f5", + "flt.s x5, f5, f6", + "fle.s x6, f6, f7", + "fclass.s x7, f7", + "fcvt.s.w f1, x8", + "fcvt.s.wu f2, x9", + "fmv.w.x f3, x10", + "fmadd.d f1, f2, f3, f4", + "fmsub.d f2, f3, f4, f5", + "fnmadd.d f3, f4, f5, f6", + "fnmsub.d f4, f5, f6, f7", + "fadd.d f5, f6, f7", + "fsub.d f6, f7, f8", + "fmul.d f7, f8, f9", + "fdiv.d f8, f9, f10", + "fsqrt.d f9, f10", + "fsgnj.d f10, f11, f12", + "fsgnjn.d f11, f12, f13", + "fsgnjx.d f12, f13, f14", + "fmin.d f13, f14, f15", + "fmax.d f14, f15, f16", + "fcvt.s.d f15, f16", + "fcvt.d.s f16, f17", + "feq.d x18, f17, f18", + "flt.d x19, f18, f19", + "fle.d x20, f19, f20", + "fclass.d x21, f20", + "fcvt.w.d x22, f21", + "fcvt.wu.d x23, f22", + "fcvt.d.w f17, x23", + "fcvt.d.wu f18, x24", + "flw f19, 0(x1)", + "fsw f20, 4(x2)", + "fld f21, 8(x3)", + "fsd f22, 12(x4)", + "fcvt.l.s x24, f1", + "fcvt.lu.s x25, f2", + "fcvt.s.l f3, x25", + "fcvt.s.lu f4, x26", + ]; + + for (instruction, expected) in instructions.iter().zip(expected_instructions.iter()) { + let instr_str = match RiscInstruction::get_string_version(*instruction, labels.clone()) { + Ok(string) => string, + Err(e) => panic!( + "Error for instruction {} that was expected {}: {}", + *instruction, *expected, e + ), + }; + assert!( + instr_str.contains(expected), + "Instruction {} does not match expected: {}", + instr_str, + expected + ); + } +} diff --git a/src/tests/emulation_core/riscv_registers.rs b/src/tests/emulation_core/riscv_registers.rs new file mode 100644 index 000000000..51c39119e --- /dev/null +++ b/src/tests/emulation_core/riscv_registers.rs @@ -0,0 +1,98 @@ +use crate::emulation_core::riscv::registers::{RiscGpRegisterType, RiscGpRegisters}; + +#[test] +#[allow(clippy::field_reassign_with_default)] +fn direct_access_register() { + let mut registers = RiscGpRegisters::default(); + + registers.pc = 5; + + assert_eq!(registers.pc, 5); +} + +#[test] +#[should_panic] +#[allow(unconditional_panic)] +#[allow(clippy::out_of_bounds_indexing)] +fn direct_access_register_bad_gpr() { + let mut registers = RiscGpRegisters::default(); + + registers.gpr[45] = 50; +} + +#[test] +fn access_valid_register_by_enum() { + let mut registers = RiscGpRegisters::default(); + + registers[RiscGpRegisterType::X10] = 4; + + assert_eq!(registers.gpr[10], 4); +} + +#[test] +fn access_valid_register_by_enum_2() { + let mut registers = RiscGpRegisters::default(); + + registers.gpr[5] = 20; + + assert_eq!(registers[RiscGpRegisterType::X5], 20); +} + +#[test] +fn access_valid_register_by_string() { + let mut registers = RiscGpRegisters::default(); + + registers["x31"] = 1; + + assert_eq!(registers.gpr[31], 1); +} + +#[test] +fn access_valid_register_by_string_2() { + let mut registers = RiscGpRegisters::default(); + + registers["x24"] = 24; + + assert_eq!(registers.gpr[24], 24); +} + +#[test] +#[should_panic] +fn access_bad_register_by_string() { + let mut registers = RiscGpRegisters::default(); + + registers["not_a_real_register"] = 7; +} + +#[test] +#[should_panic] +fn no_modify_zero_register_by_enum() { + let mut registers = RiscGpRegisters::default(); + + registers[RiscGpRegisterType::X0] = 5; +} + +#[test] +#[should_panic] +fn no_modify_zero_register_by_string() { + let mut registers = RiscGpRegisters::default(); + + registers["zero"] = 90; +} + +#[test] +fn registers_into_iter() { + let mut registers = RiscGpRegisters { + pc: 500, + ..Default::default() + }; + registers.gpr[1] = 19; + registers.gpr[2] = 45; + + let mut iter = registers.into_iter(); + + assert_eq!(Some((RiscGpRegisterType::Pc, 500)), iter.next()); + assert_eq!(Some((RiscGpRegisterType::X0, 0)), iter.next()); + assert_eq!(Some((RiscGpRegisterType::X1, 19)), iter.next()); + assert_eq!(Some((RiscGpRegisterType::X2, 45)), iter.next()); +} diff --git a/src/tests/integration/core_parser/arithmetic.rs b/src/tests/integration/core_parser/arithmetic.rs index be7583455..0b4fa396c 100644 --- a/src/tests/integration/core_parser/arithmetic.rs +++ b/src/tests/integration/core_parser/arithmetic.rs @@ -1,5 +1,7 @@ //! Tests for additional arithmetic instructions: addu, sll, move, nop. +use crate::emulation_core::architectures::AvailableDatapaths; + use super::*; #[test] @@ -8,8 +10,8 @@ fn basic_addu() -> Result<(), String> { let instructions = String::from("addu r20, r19, r18"); - let (_, instruction_bits) = parser(instructions); - datapath.initialize(instruction_bits)?; + let (_, instruction_bits, _labels) = parser(instructions, AvailableDatapaths::MIPS); + datapath.initialize_legacy(instruction_bits)?; datapath.registers.gpr[18] = 6849841; datapath.registers.gpr[19] = 99816512; @@ -32,8 +34,8 @@ fn basic_sll() -> Result<(), String> { sll $s1, $s1, 3"#, ); - let (_, instruction_bits) = parser(instructions); - datapath.initialize(instruction_bits)?; + let (_, instruction_bits, _labels) = parser(instructions, AvailableDatapaths::MIPS); + datapath.initialize_legacy(instruction_bits)?; while !datapath.is_halted() { datapath.execute_instruction(); @@ -53,8 +55,8 @@ fn basic_move() -> Result<(), String> { move $s5, $s4"#, ); - let (_, instruction_bits) = parser(instructions); - datapath.initialize(instruction_bits)?; + let (_, instruction_bits, _labels) = parser(instructions, AvailableDatapaths::MIPS); + datapath.initialize_legacy(instruction_bits)?; while !datapath.is_halted() { datapath.execute_instruction(); @@ -71,15 +73,19 @@ fn basic_nop() -> Result<(), String> { let instructions = String::from(r#"nop"#); - let (_, instruction_bits) = parser(instructions); - datapath.initialize(instruction_bits)?; + let (_, instruction_bits, _labels) = parser(instructions, AvailableDatapaths::MIPS); + datapath.initialize_legacy(instruction_bits)?; let mut expected_registers = datapath.registers; - expected_registers.pc = 4; + expected_registers.pc = 8; let expected_memory = datapath.memory.clone(); - while !datapath.is_halted() { - datapath.execute_instruction(); + // Execute until hitting a syscall + loop { + let result = datapath.execute_instruction(); + if result.hit_syscall { + break; + } } // Register and memory contents should be unchanged, except for the PC. diff --git a/src/tests/integration/core_parser/basic_immediate.rs b/src/tests/integration/core_parser/basic_immediate.rs index e7653d7c1..068feb582 100644 --- a/src/tests/integration/core_parser/basic_immediate.rs +++ b/src/tests/integration/core_parser/basic_immediate.rs @@ -2,6 +2,8 @@ //! //! Note that some of these instructions are pseudo-instructions. +use crate::emulation_core::architectures::AvailableDatapaths; + use super::*; #[test] @@ -10,8 +12,8 @@ fn basic_addi() -> Result<(), String> { let instructions = String::from("addi r11, r15, 2"); - let (_, instruction_bits) = parser(instructions); - datapath.initialize(instruction_bits)?; + let (_, instruction_bits, _labels) = parser(instructions, AvailableDatapaths::MIPS); + datapath.initialize_legacy(instruction_bits)?; datapath.registers.gpr[15] = 100; @@ -30,8 +32,8 @@ fn basic_addiu() -> Result<(), String> { let instructions = String::from("addiu r14, r17, 5"); - let (_, instruction_bits) = parser(instructions); - datapath.initialize(instruction_bits)?; + let (_, instruction_bits, _labels) = parser(instructions, AvailableDatapaths::MIPS); + datapath.initialize_legacy(instruction_bits)?; datapath.registers.gpr[17] = 500; @@ -50,8 +52,8 @@ fn basic_subi() -> Result<(), String> { let instructions = String::from("subi r11, r15, 2"); - let (_, instruction_bits) = parser(instructions); - datapath.initialize(instruction_bits)?; + let (_, instruction_bits, _labels) = parser(instructions, AvailableDatapaths::MIPS); + datapath.initialize_legacy(instruction_bits)?; datapath.registers.gpr[15] = 100; @@ -70,8 +72,8 @@ fn basic_muli() -> Result<(), String> { let instructions = String::from("muli r11, r15, 2"); - let (_, instruction_bits) = parser(instructions); - datapath.initialize(instruction_bits)?; + let (_, instruction_bits, _labels) = parser(instructions, AvailableDatapaths::MIPS); + datapath.initialize_legacy(instruction_bits)?; datapath.registers.gpr[15] = 100; @@ -90,8 +92,8 @@ fn basic_divi() -> Result<(), String> { let instructions = String::from("divi r11, r15, 2"); - let (_, instruction_bits) = parser(instructions); - datapath.initialize(instruction_bits)?; + let (_, instruction_bits, _labels) = parser(instructions, AvailableDatapaths::MIPS); + datapath.initialize_legacy(instruction_bits)?; datapath.registers.gpr[15] = 100; @@ -110,8 +112,8 @@ fn basic_ori() -> Result<(), String> { let instructions = String::from("ori r11, r15, 2"); - let (_, instruction_bits) = parser(instructions); - datapath.initialize(instruction_bits)?; + let (_, instruction_bits, _labels) = parser(instructions, AvailableDatapaths::MIPS); + datapath.initialize_legacy(instruction_bits)?; datapath.registers.gpr[15] = 100; @@ -130,8 +132,8 @@ fn basic_andi() -> Result<(), String> { let instructions = String::from("andi r11, r15, 4"); - let (_, instruction_bits) = parser(instructions); - datapath.initialize(instruction_bits)?; + let (_, instruction_bits, _labels) = parser(instructions, AvailableDatapaths::MIPS); + datapath.initialize_legacy(instruction_bits)?; datapath.registers.gpr[15] = 100; @@ -150,8 +152,8 @@ fn basic_li() -> Result<(), String> { let instructions = String::from("li r15, 56"); - let (_, instruction_bits) = parser(instructions); - datapath.initialize(instruction_bits)?; + let (_, instruction_bits, _labels) = parser(instructions, AvailableDatapaths::MIPS); + datapath.initialize_legacy(instruction_bits)?; while !datapath.is_halted() { datapath.execute_instruction(); @@ -169,8 +171,8 @@ fn basic_lui() -> Result<(), String> { // 65530 == 0xFFFA let instructions = String::from("lui r20, 65530"); - let (_, instruction_bits) = parser(instructions); - datapath.initialize(instruction_bits)?; + let (_, instruction_bits, _labels) = parser(instructions, AvailableDatapaths::MIPS); + datapath.initialize_legacy(instruction_bits)?; while !datapath.is_halted() { datapath.execute_instruction(); @@ -189,8 +191,8 @@ fn basic_aui() -> Result<(), String> { // 4612 == 0x1204 let instructions = String::from("aui r15, r18, 4612"); - let (_, instruction_bits) = parser(instructions); - datapath.initialize(instruction_bits)?; + let (_, instruction_bits, _labels) = parser(instructions, AvailableDatapaths::MIPS); + datapath.initialize_legacy(instruction_bits)?; datapath.registers.gpr[18] = 0x0000_0000_0030_ABCD; diff --git a/src/tests/integration/core_parser/basic_operations.rs b/src/tests/integration/core_parser/basic_operations.rs index fd2150e3b..00b8041b8 100644 --- a/src/tests/integration/core_parser/basic_operations.rs +++ b/src/tests/integration/core_parser/basic_operations.rs @@ -1,5 +1,7 @@ //! Covering the basic arithmetic instructions: add, sub, mul, div, or, and. +use crate::emulation_core::architectures::AvailableDatapaths; + use super::*; #[test] @@ -8,8 +10,8 @@ fn basic_add() -> Result<(), String> { let instructions = String::from("add r11, r7, r8"); - let (_, instruction_bits) = parser(instructions); - datapath.initialize(instruction_bits)?; + let (_, instruction_bits, _labels) = parser(instructions, AvailableDatapaths::MIPS); + datapath.initialize_legacy(instruction_bits)?; datapath.registers.gpr[7] = 51; datapath.registers.gpr[8] = 5; @@ -29,8 +31,8 @@ fn basic_sub() -> Result<(), String> { let instructions = String::from("sub r12, r7, r8"); - let (_, instruction_bits) = parser(instructions); - datapath.initialize(instruction_bits)?; + let (_, instruction_bits, _labels) = parser(instructions, AvailableDatapaths::MIPS); + datapath.initialize_legacy(instruction_bits)?; datapath.registers.gpr[7] = 51; datapath.registers.gpr[8] = 5; @@ -50,8 +52,8 @@ fn basic_mul() -> Result<(), String> { let instructions = String::from("mul r13, r7, r8"); - let (_, instruction_bits) = parser(instructions); - datapath.initialize(instruction_bits)?; + let (_, instruction_bits, _labels) = parser(instructions, AvailableDatapaths::MIPS); + datapath.initialize_legacy(instruction_bits)?; datapath.registers.gpr[7] = 51; datapath.registers.gpr[8] = 5; @@ -71,8 +73,8 @@ fn basic_div() -> Result<(), String> { let instructions = String::from("div r14, r7, r8"); - let (_, instruction_bits) = parser(instructions); - datapath.initialize(instruction_bits)?; + let (_, instruction_bits, _labels) = parser(instructions, AvailableDatapaths::MIPS); + datapath.initialize_legacy(instruction_bits)?; datapath.registers.gpr[7] = 51; datapath.registers.gpr[8] = 5; @@ -92,8 +94,8 @@ fn basic_or() -> Result<(), String> { let instructions = String::from("or r15, r7, r8"); - let (_, instruction_bits) = parser(instructions); - datapath.initialize(instruction_bits)?; + let (_, instruction_bits, _labels) = parser(instructions, AvailableDatapaths::MIPS); + datapath.initialize_legacy(instruction_bits)?; datapath.registers.gpr[7] = 51; datapath.registers.gpr[8] = 5; @@ -113,8 +115,8 @@ fn basic_and() -> Result<(), String> { let instructions = String::from("and r16, r7, r8"); - let (_, instruction_bits) = parser(instructions); - datapath.initialize(instruction_bits)?; + let (_, instruction_bits, _labels) = parser(instructions, AvailableDatapaths::MIPS); + datapath.initialize_legacy(instruction_bits)?; datapath.registers.gpr[7] = 51; datapath.registers.gpr[8] = 5; diff --git a/src/tests/integration/core_parser/branch_jump.rs b/src/tests/integration/core_parser/branch_jump.rs index 72d55ab65..ab1202331 100644 --- a/src/tests/integration/core_parser/branch_jump.rs +++ b/src/tests/integration/core_parser/branch_jump.rs @@ -1,5 +1,7 @@ //! Tests for the branch and jump instructions: j, jr, jal, jalr, beq, bne +use crate::emulation_core::architectures::AvailableDatapaths; + use super::*; #[test] @@ -16,8 +18,8 @@ loop: daddu $s1, $s1, $s0 j loop"#, ); - let (_, instruction_bits) = parser(instructions); - datapath.initialize(instruction_bits)?; + let (_, instruction_bits, _labels) = parser(instructions, AvailableDatapaths::MIPS); + datapath.initialize_legacy(instruction_bits)?; // Execute the ori instruction. datapath.execute_instruction(); @@ -44,8 +46,8 @@ fn basic_jr() -> Result<(), String> { jr r15"#, ); - let (_, instruction_bits) = parser(instructions); - datapath.initialize(instruction_bits)?; + let (_, instruction_bits, _labels) = parser(instructions, AvailableDatapaths::MIPS); + datapath.initialize_legacy(instruction_bits)?; // Execute 2 instructions. for _ in 0..2 { @@ -69,8 +71,8 @@ syscall function: ori $t0, $zero, 5831"#, ); - let (_, instruction_bits) = parser(instructions); - datapath.initialize(instruction_bits)?; + let (_, instruction_bits, _labels) = parser(instructions, AvailableDatapaths::MIPS); + datapath.initialize_legacy(instruction_bits)?; while !datapath.is_halted() { datapath.execute_instruction(); @@ -95,8 +97,8 @@ or $zero, $zero, $zero function: ori $t1, $zero, 9548"#, ); - let (_, instruction_bits) = parser(instructions); - datapath.initialize(instruction_bits)?; + let (_, instruction_bits, _labels) = parser(instructions, AvailableDatapaths::MIPS); + datapath.initialize_legacy(instruction_bits)?; // Execute 3 instructions. for _ in 0..3 { @@ -144,8 +146,8 @@ daddiu $s2, $s2, 20 change10: daddiu $s2, $s2, 10"#, ); - let (_, instruction_bits) = parser(instructions); - datapath.initialize(instruction_bits)?; + let (_, instruction_bits, _labels) = parser(instructions, AvailableDatapaths::MIPS); + datapath.initialize_legacy(instruction_bits)?; while !datapath.is_halted() { datapath.execute_instruction(); @@ -185,8 +187,8 @@ syscall changez: daddiu $s2, $s2, 20"#, ); - let (_, instruction_bits) = parser(instructions); - datapath.initialize(instruction_bits)?; + let (_, instruction_bits, _labels) = parser(instructions, AvailableDatapaths::MIPS); + datapath.initialize_legacy(instruction_bits)?; while !datapath.is_halted() { datapath.execute_instruction(); @@ -217,13 +219,15 @@ daddiu $s0, $s0, 1 bne $s0, $s2, loop"#, ); - let (_, instruction_bits) = parser(instructions); - datapath.initialize(instruction_bits)?; + let (_, instruction_bits, _labels) = parser(instructions, AvailableDatapaths::MIPS); + datapath.initialize_legacy(instruction_bits)?; let mut iterations = 0; - while !datapath.is_halted() { - datapath.execute_instruction(); + loop { + if datapath.execute_instruction().hit_syscall { + break; + } iterations += 1; // Catch an infinite loop. This program should not cause over 300 instructions to run. diff --git a/src/tests/integration/core_parser/conditions.rs b/src/tests/integration/core_parser/conditions.rs index 91d957f12..4d05649d8 100644 --- a/src/tests/integration/core_parser/conditions.rs +++ b/src/tests/integration/core_parser/conditions.rs @@ -2,6 +2,8 @@ //! //! Includes: seq, sne, slt, sltu, sle, sleu, sgt, sgtu, sge, sgeu. +use crate::emulation_core::architectures::AvailableDatapaths; + use super::*; akin! { @@ -26,8 +28,8 @@ akin! { let mut datapath = MipsDatapath::default(); let instructions = String::from("*instruction_name r*destination_register, r5, r6"); - let (_, instruction_bits) = parser(instructions); - datapath.initialize(instruction_bits)?; + let (_, instruction_bits, _labels) = parser(instructions, AvailableDatapaths::MIPS); + datapath.initialize_legacy(instruction_bits)?; datapath.registers.gpr[5] = *true_value1; datapath.registers.gpr[6] = *true_value2; @@ -45,8 +47,8 @@ akin! { let mut datapath = MipsDatapath::default(); let instructions = String::from("*instruction_name r*destination_register, r5, r6"); - let (_, instruction_bits) = parser(instructions); - datapath.initialize(instruction_bits)?; + let (_, instruction_bits, _labels) = parser(instructions, AvailableDatapaths::MIPS); + datapath.initialize_legacy(instruction_bits)?; datapath.registers.gpr[5] = *false_value1; datapath.registers.gpr[6] = *false_value2; diff --git a/src/tests/integration/core_parser/coprocessor_move.rs b/src/tests/integration/core_parser/coprocessor_move.rs index 38de80480..7885eda3b 100644 --- a/src/tests/integration/core_parser/coprocessor_move.rs +++ b/src/tests/integration/core_parser/coprocessor_move.rs @@ -1,5 +1,7 @@ //! Tests for the "move from/to Coprocessor 1" instructions: mtc1, dmtc1, mfc1, dmfc1 +use crate::emulation_core::architectures::AvailableDatapaths; + use super::*; #[test] @@ -7,8 +9,8 @@ fn basic_mtc1() -> Result<(), String> { let mut datapath = MipsDatapath::default(); let instructions = String::from("mtc1 $t2, $f5"); - let (_, instruction_bits) = parser(instructions); - datapath.initialize(instruction_bits)?; + let (_, instruction_bits, _labels) = parser(instructions, AvailableDatapaths::MIPS); + datapath.initialize_legacy(instruction_bits)?; datapath.registers.gpr[10] = 658461658; // $t2 @@ -16,7 +18,7 @@ fn basic_mtc1() -> Result<(), String> { datapath.execute_instruction(); } - assert_eq!(datapath.coprocessor.fpr[5], 658461658); + assert_eq!(datapath.coprocessor.registers.fpr[5], 658461658); Ok(()) } @@ -25,8 +27,8 @@ fn truncate_32_bit_mtc1() -> Result<(), String> { let mut datapath = MipsDatapath::default(); let instructions = String::from("mtc1 $t3, $f6"); - let (_, instruction_bits) = parser(instructions); - datapath.initialize(instruction_bits)?; + let (_, instruction_bits, _labels) = parser(instructions, AvailableDatapaths::MIPS); + datapath.initialize_legacy(instruction_bits)?; datapath.registers.gpr[11] = 0x0000_02F2_AC71_AC41; // $t3 @@ -34,7 +36,7 @@ fn truncate_32_bit_mtc1() -> Result<(), String> { datapath.execute_instruction(); } - assert_eq!(datapath.coprocessor.fpr[6], 0xAC71_AC41); + assert_eq!(datapath.coprocessor.registers.fpr[6], 0xAC71_AC41); Ok(()) } @@ -43,10 +45,10 @@ fn basic_mfc1() -> Result<(), String> { let mut datapath = MipsDatapath::default(); let instructions = String::from("mfc1 $t3, $f5"); - let (_, instruction_bits) = parser(instructions); - datapath.initialize(instruction_bits)?; + let (_, instruction_bits, _labels) = parser(instructions, AvailableDatapaths::MIPS); + datapath.initialize_legacy(instruction_bits)?; - datapath.coprocessor.fpr[5] = 657861659; + datapath.coprocessor.registers.fpr[5] = 657861659; while !datapath.is_halted() { datapath.execute_instruction(); @@ -61,10 +63,10 @@ fn truncate_32_bit_mfc1() -> Result<(), String> { let mut datapath = MipsDatapath::default(); let instructions = String::from("mfc1 $t4, $f6"); - let (_, instruction_bits) = parser(instructions); - datapath.initialize(instruction_bits)?; + let (_, instruction_bits, _labels) = parser(instructions, AvailableDatapaths::MIPS); + datapath.initialize_legacy(instruction_bits)?; - datapath.coprocessor.fpr[6] = 0x0003_7F80_E5E7_D785; + datapath.coprocessor.registers.fpr[6] = 0x0003_7F80_E5E7_D785; while !datapath.is_halted() { datapath.execute_instruction(); @@ -79,8 +81,8 @@ fn basic_dmtc1() -> Result<(), String> { let mut datapath = MipsDatapath::default(); let instructions = String::from("dmtc1 $t3, $f6"); - let (_, instruction_bits) = parser(instructions); - datapath.initialize(instruction_bits)?; + let (_, instruction_bits, _labels) = parser(instructions, AvailableDatapaths::MIPS); + datapath.initialize_legacy(instruction_bits)?; datapath.registers.gpr[11] = 0x0120_02F2_AC71_AC41; // $t3 @@ -88,7 +90,7 @@ fn basic_dmtc1() -> Result<(), String> { datapath.execute_instruction(); } - assert_eq!(datapath.coprocessor.fpr[6], 0x0120_02F2_AC71_AC41); + assert_eq!(datapath.coprocessor.registers.fpr[6], 0x0120_02F2_AC71_AC41); Ok(()) } @@ -97,10 +99,10 @@ fn basic_dmfc1() -> Result<(), String> { let mut datapath = MipsDatapath::default(); let instructions = String::from("dmfc1 $t4, $f6"); - let (_, instruction_bits) = parser(instructions); - datapath.initialize(instruction_bits)?; + let (_, instruction_bits, _labels) = parser(instructions, AvailableDatapaths::MIPS); + datapath.initialize_legacy(instruction_bits)?; - datapath.coprocessor.fpr[6] = 0x0003_7F90_E5E7_D785; + datapath.coprocessor.registers.fpr[6] = 0x0003_7F90_E5E7_D785; while !datapath.is_halted() { datapath.execute_instruction(); diff --git a/src/tests/integration/core_parser/double_arithmetic.rs b/src/tests/integration/core_parser/double_arithmetic.rs index 83ee30fdd..391b5f8cb 100644 --- a/src/tests/integration/core_parser/double_arithmetic.rs +++ b/src/tests/integration/core_parser/double_arithmetic.rs @@ -1,5 +1,7 @@ //! Tests for the double arithmetic instructions: dadd, dsub, dmul, ddiv, daddu, dsubu, dmulu, ddivu. +use crate::emulation_core::architectures::AvailableDatapaths; + use super::*; akin! { @@ -16,8 +18,8 @@ akin! { let mut datapath = MipsDatapath::default(); let instructions = String::from(*instruction); - let (_, instruction_bits) = parser(instructions); - datapath.initialize(instruction_bits)?; + let (_, instruction_bits, _labels) = parser(instructions, AvailableDatapaths::MIPS); + datapath.initialize_legacy(instruction_bits)?; datapath.registers.gpr[16] = *value1; datapath.registers.gpr[17] = *value2; @@ -45,8 +47,8 @@ akin! { let mut datapath = MipsDatapath::default(); let instructions = String::from(*instruction); - let (_, instruction_bits) = parser(instructions); - datapath.initialize(instruction_bits)?; + let (_, instruction_bits, _labels) = parser(instructions, AvailableDatapaths::MIPS); + datapath.initialize_legacy(instruction_bits)?; datapath.registers.gpr[25] = *value1; datapath.registers.gpr[26] = *value2; diff --git a/src/tests/integration/core_parser/double_immediate.rs b/src/tests/integration/core_parser/double_immediate.rs index 4bd27b211..6709667a7 100644 --- a/src/tests/integration/core_parser/double_immediate.rs +++ b/src/tests/integration/core_parser/double_immediate.rs @@ -1,5 +1,7 @@ //! Tests for the double immediate instructions: dahi, dati, daddi, dsubi, dmuli, ddivi, daddiu, dsubiu, dmuliu, ddiviu. +use crate::emulation_core::architectures::AvailableDatapaths; + use super::*; #[test] @@ -7,8 +9,8 @@ fn basic_dahi() -> Result<(), String> { let mut datapath = MipsDatapath::default(); let instructions = String::from("dahi r3, 123"); - let (_, instruction_bits) = parser(instructions); - datapath.initialize(instruction_bits)?; + let (_, instruction_bits, _labels) = parser(instructions, AvailableDatapaths::MIPS); + datapath.initialize_legacy(instruction_bits)?; datapath.registers.gpr[3] = 0; @@ -28,8 +30,8 @@ fn dahi_sign_extend() -> Result<(), String> { let mut datapath = MipsDatapath::default(); let instructions = String::from("dahi r5, 43158"); - let (_, instruction_bits) = parser(instructions); - datapath.initialize(instruction_bits)?; + let (_, instruction_bits, _labels) = parser(instructions, AvailableDatapaths::MIPS); + datapath.initialize_legacy(instruction_bits)?; datapath.registers.gpr[5] = 0; @@ -49,8 +51,8 @@ fn basic_dati() -> Result<(), String> { let mut datapath = MipsDatapath::default(); let instructions = String::from("dati r10, 4321"); - let (_, instruction_bits) = parser(instructions); - datapath.initialize(instruction_bits)?; + let (_, instruction_bits, _labels) = parser(instructions, AvailableDatapaths::MIPS); + datapath.initialize_legacy(instruction_bits)?; datapath.registers.gpr[10] = 0; @@ -77,8 +79,8 @@ akin! { let mut datapath = MipsDatapath::default(); let instructions = String::from(*instruction); - let (_, instruction_bits) = parser(instructions); - datapath.initialize(instruction_bits)?; + let (_, instruction_bits, _labels) = parser(instructions, AvailableDatapaths::MIPS); + datapath.initialize_legacy(instruction_bits)?; datapath.registers.gpr[20] = *rs_value; @@ -103,8 +105,8 @@ akin! { let mut datapath = MipsDatapath::default(); let instructions = String::from(*instruction); - let (_, instruction_bits) = parser(instructions); - datapath.initialize(instruction_bits)?; + let (_, instruction_bits, _labels) = parser(instructions, AvailableDatapaths::MIPS); + datapath.initialize_legacy(instruction_bits)?; datapath.registers.gpr[20] = *rs_value; diff --git a/src/tests/integration/core_parser/fibonacci.rs b/src/tests/integration/core_parser/fibonacci.rs index de64e4399..e27c99648 100644 --- a/src/tests/integration/core_parser/fibonacci.rs +++ b/src/tests/integration/core_parser/fibonacci.rs @@ -1,4 +1,5 @@ -use crate::emulation_core::mips::registers::GpRegisterType; +use crate::emulation_core::architectures::AvailableDatapaths; +use crate::emulation_core::mips::gp_registers::GpRegisterType; use super::*; @@ -22,11 +23,11 @@ fn recursive_fibonacci() -> Result<(), String> { slt $v0,$v0,$s1 beq $v0,$zero,L2 nop - + lw $v0,40($fp) b L3 nop - + L2: lw $v0,40($fp) nop @@ -34,7 +35,7 @@ fn recursive_fibonacci() -> Result<(), String> { move $a0,$v0 jal fib(int) nop - + move $s0,$v0 lw $v0,40($fp) nop @@ -42,7 +43,7 @@ fn recursive_fibonacci() -> Result<(), String> { move $a0,$v0 jal fib(int) nop - + addu $v0,$s0,$v0 L3: move $sp,$fp @@ -52,7 +53,7 @@ fn recursive_fibonacci() -> Result<(), String> { addiu $sp,$sp,40 jr $ra nop - + main: addiu $sp,$sp,-40 sw $ra,36($sp) @@ -63,7 +64,7 @@ fn recursive_fibonacci() -> Result<(), String> { lw $a0,24($fp) jal fib(int) nop - + sw $v0,28($fp) lw $v0,28($fp) # This is where the final answer gets loaded off the stack move $sp,$fp @@ -74,8 +75,8 @@ fn recursive_fibonacci() -> Result<(), String> { nop", ); - let (_, instruction_bits) = parser(instructions); - datapath.initialize(instruction_bits)?; + let (_, instruction_bits, _labels) = parser(instructions, AvailableDatapaths::MIPS); + datapath.initialize_legacy(instruction_bits)?; while !datapath.is_halted() { datapath.execute_instruction(); diff --git a/src/tests/integration/core_parser/floating_point_arithmetic.rs b/src/tests/integration/core_parser/floating_point_arithmetic.rs index c88473c28..5f9ac2d96 100644 --- a/src/tests/integration/core_parser/floating_point_arithmetic.rs +++ b/src/tests/integration/core_parser/floating_point_arithmetic.rs @@ -1,5 +1,7 @@ //! Tests for the floating-point arithmetic instructions: add.s, add.d, sub.s, sub.d, mul.s, mul.d, div.s, div.d +use crate::emulation_core::architectures::AvailableDatapaths; + use super::*; akin! { @@ -18,17 +20,17 @@ akin! { let mut datapath = MipsDatapath::default(); let instructions = String::from(*instruction); - let (_, instruction_bits) = parser(instructions); - datapath.initialize(instruction_bits)?; + let (_, instruction_bits, _labels) = parser(instructions, AvailableDatapaths::MIPS); + datapath.initialize_legacy(instruction_bits)?; - datapath.coprocessor.fpr[15] = *value1; - datapath.coprocessor.fpr[16] = *value2; + datapath.coprocessor.registers.fpr[15] = *value1; + datapath.coprocessor.registers.fpr[16] = *value2; while !datapath.is_halted() { datapath.execute_instruction(); } - assert_eq!(datapath.coprocessor.fpr[*result_register], *expected_result); + assert_eq!(datapath.coprocessor.registers.fpr[*result_register], *expected_result); Ok(()) } } @@ -49,17 +51,17 @@ akin! { let mut datapath = MipsDatapath::default(); let instructions = String::from(*instruction); - let (_, instruction_bits) = parser(instructions); - datapath.initialize(instruction_bits)?; + let (_, instruction_bits, _labels) = parser(instructions, AvailableDatapaths::MIPS); + datapath.initialize_legacy(instruction_bits)?; - datapath.coprocessor.fpr[15] = *value1; - datapath.coprocessor.fpr[16] = *value2; + datapath.coprocessor.registers.fpr[15] = *value1; + datapath.coprocessor.registers.fpr[16] = *value2; while !datapath.is_halted() { datapath.execute_instruction(); } - assert_eq!(datapath.coprocessor.fpr[*result_register], *expected_result); + assert_eq!(datapath.coprocessor.registers.fpr[*result_register], *expected_result); Ok(()) } } diff --git a/src/tests/integration/core_parser/floating_point_branch.rs b/src/tests/integration/core_parser/floating_point_branch.rs index 59db68d78..5db8b9bc3 100644 --- a/src/tests/integration/core_parser/floating_point_branch.rs +++ b/src/tests/integration/core_parser/floating_point_branch.rs @@ -1,5 +1,7 @@ //! Tests for the floating-point branch instructions: bc1t, bc1f +use crate::emulation_core::architectures::AvailableDatapaths; + use super::*; #[test] @@ -30,8 +32,8 @@ c.lt.s $f0, $f2 bc1t loop"#, ); - let (_, instruction_bits) = parser(instructions); - datapath.initialize(instruction_bits)?; + let (_, instruction_bits, _labels) = parser(instructions, AvailableDatapaths::MIPS); + datapath.initialize_legacy(instruction_bits)?; while !datapath.is_halted() { datapath.execute_instruction(); @@ -40,7 +42,10 @@ bc1t loop"#, // This should end when the loop has iterated 5 times. // Thus, $s2 should be 35 and $f0 should be 5.0. assert_eq!(datapath.registers.gpr[18], 35); // $s2 - assert_eq!(f32::from_bits(datapath.coprocessor.fpr[0] as u32), 5.0); // $f0 + assert_eq!( + f32::from_bits(datapath.coprocessor.registers.fpr[0] as u32), + 5.0 + ); // $f0 Ok(()) } @@ -73,8 +78,8 @@ c.lt.s $f2, $f0 bc1f loop"#, ); - let (_, instruction_bits) = parser(instructions); - datapath.initialize(instruction_bits)?; + let (_, instruction_bits, _labels) = parser(instructions, AvailableDatapaths::MIPS); + datapath.initialize_legacy(instruction_bits)?; while !datapath.is_halted() { datapath.execute_instruction(); @@ -83,7 +88,10 @@ bc1f loop"#, // This should end when the loop has iterated 6 times. // Thus, $s2 should be 42 and $f0 should be 6.0. assert_eq!(datapath.registers.gpr[18], 42); // $s2 - assert_eq!(f32::from_bits(datapath.coprocessor.fpr[0] as u32), 6.0); // $f0 + assert_eq!( + f32::from_bits(datapath.coprocessor.registers.fpr[0] as u32), + 6.0 + ); // $f0 Ok(()) } diff --git a/src/tests/integration/core_parser/floating_point_comparison.rs b/src/tests/integration/core_parser/floating_point_comparison.rs index 6b663a17d..5a729d553 100644 --- a/src/tests/integration/core_parser/floating_point_comparison.rs +++ b/src/tests/integration/core_parser/floating_point_comparison.rs @@ -1,5 +1,7 @@ //! Tests for the floating-point comparison instructions: c.eq.s, c.eq.d, c.lt.s, c.lt.d, c.le.s, c.le.d, c.ngt.s, c.ngt.d, c.nge.s, c.nge.d +use crate::emulation_core::architectures::AvailableDatapaths; + use super::*; akin! { @@ -16,11 +18,11 @@ akin! { let mut datapath = MipsDatapath::default(); let instructions = String::from(*instruction); - let (_, instruction_bits) = parser(instructions); - datapath.initialize(instruction_bits)?; + let (_, instruction_bits, _labels) = parser(instructions, AvailableDatapaths::MIPS); + datapath.initialize_legacy(instruction_bits)?; - datapath.coprocessor.fpr[15] = *value1; - datapath.coprocessor.fpr[16] = *value2; + datapath.coprocessor.registers.fpr[15] = *value1; + datapath.coprocessor.registers.fpr[16] = *value2; while !datapath.is_halted() { datapath.execute_instruction(); @@ -45,11 +47,11 @@ akin! { let mut datapath = MipsDatapath::default(); let instructions = String::from(*instruction); - let (_, instruction_bits) = parser(instructions); - datapath.initialize(instruction_bits)?; + let (_, instruction_bits, _labels) = parser(instructions, AvailableDatapaths::MIPS); + datapath.initialize_legacy(instruction_bits)?; - datapath.coprocessor.fpr[15] = *value1; - datapath.coprocessor.fpr[16] = *value2; + datapath.coprocessor.registers.fpr[15] = *value1; + datapath.coprocessor.registers.fpr[16] = *value2; while !datapath.is_halted() { datapath.execute_instruction(); diff --git a/src/tests/integration/core_parser/mod.rs b/src/tests/integration/core_parser/mod.rs index 1951eeb01..597b85848 100644 --- a/src/tests/integration/core_parser/mod.rs +++ b/src/tests/integration/core_parser/mod.rs @@ -1,5 +1,6 @@ use akin::akin; +use crate::emulation_core::architectures::AvailableDatapaths; use crate::emulation_core::datapath::Datapath; use crate::emulation_core::mips::datapath::MipsDatapath; use crate::parser::parser_assembler_main::parser; @@ -30,8 +31,8 @@ add $s1, $s0, $s0"#, ); // Parse instructions and load into emulation core memory. - let (_, instruction_bits) = parser(instructions); - datapath.initialize(instruction_bits)?; + let (_, instruction_bits, _labels) = parser(instructions, AvailableDatapaths::MIPS); + datapath.initialize_legacy(instruction_bits)?; // Execute 2 instructions. for _ in 0..2 { @@ -69,8 +70,8 @@ dati r1, 43982"#, // dati r1, 43982 | ABCD 8765 CCCC EEEE | 43982 == 0xABCE. FFFF + ABCE = ABCD. // Parse instructions and load into emulation core memory. - let (_, instruction_bits) = parser(instructions); - datapath.initialize(instruction_bits)?; + let (_, instruction_bits, _labels) = parser(instructions, AvailableDatapaths::MIPS); + datapath.initialize_legacy(instruction_bits)?; // Execute 4 instructions. for _ in 0..4 { @@ -96,8 +97,8 @@ dadd r7, r5, r6 dmuli r8, r7, 2"#, ); - let (_, instruction_bits) = parser(instructions); - datapath.initialize(instruction_bits)?; + let (_, instruction_bits, _labels) = parser(instructions, AvailableDatapaths::MIPS); + datapath.initialize_legacy(instruction_bits)?; while !datapath.is_halted() { datapath.execute_instruction(); diff --git a/src/tests/integration/core_parser/store_load_word.rs b/src/tests/integration/core_parser/store_load_word.rs index 68e4473e3..ecea03d3f 100644 --- a/src/tests/integration/core_parser/store_load_word.rs +++ b/src/tests/integration/core_parser/store_load_word.rs @@ -12,8 +12,8 @@ li r25, 1234 sw r25, 0(r14)"#, ); - let (_, instruction_bits) = parser(instructions); - datapath.initialize(instruction_bits)?; + let (_, instruction_bits, _labels) = parser(instructions, AvailableDatapaths::MIPS); + datapath.initialize_legacy(instruction_bits)?; while !datapath.is_halted() { datapath.execute_instruction(); @@ -33,8 +33,8 @@ fn basic_lw() -> Result<(), String> { lw r25, 0(r14)"#, ); - let (_, instruction_bits) = parser(instructions); - datapath.initialize(instruction_bits)?; + let (_, instruction_bits, _labels) = parser(instructions, AvailableDatapaths::MIPS); + datapath.initialize_legacy(instruction_bits)?; datapath.memory.memory[403] = 36; @@ -61,8 +61,8 @@ daddiu $s2, $s1, 1 sw $s2, secret_number"#, ); - let (_, instruction_bits) = parser(instructions); - datapath.initialize(instruction_bits)?; + let (_, instruction_bits, _labels) = parser(instructions, AvailableDatapaths::MIPS); + datapath.initialize_legacy(instruction_bits)?; while !datapath.is_halted() { datapath.execute_instruction(); @@ -88,8 +88,8 @@ mtc1 $s1, $f25 swc1 $f25, 0($s0)"#, ); - let (_, instruction_bits) = parser(instructions); - datapath.initialize(instruction_bits)?; + let (_, instruction_bits, _labels) = parser(instructions, AvailableDatapaths::MIPS); + datapath.initialize_legacy(instruction_bits)?; while !datapath.is_halted() { datapath.execute_instruction(); @@ -109,8 +109,8 @@ fn basic_lwc1() -> Result<(), String> { lwc1 $f12, 0($t4)"#, ); - let (_, instruction_bits) = parser(instructions); - datapath.initialize(instruction_bits)?; + let (_, instruction_bits, _labels) = parser(instructions, AvailableDatapaths::MIPS); + datapath.initialize_legacy(instruction_bits)?; datapath.memory.memory[403] = 36; @@ -118,7 +118,7 @@ lwc1 $f12, 0($t4)"#, datapath.execute_instruction(); } - assert_eq!(datapath.coprocessor.fpr[12], 36); + assert_eq!(datapath.coprocessor.registers.fpr[12], 36); Ok(()) } diff --git a/src/tests/parser/assembling.rs b/src/tests/parser/assembling.rs index 16b25f837..0c0ec4868 100644 --- a/src/tests/parser/assembling.rs +++ b/src/tests/parser/assembling.rs @@ -1,3 +1,4 @@ +use crate::emulation_core::architectures::AvailableDatapaths; use crate::parser::assembling::assemble_data_binary; use crate::parser::parser_assembler_main::parser; use crate::parser::parser_structs_and_enums::ErrorType::{NonASCIIChar, NonASCIIString}; @@ -374,20 +375,32 @@ fn assemble_data_binary_works_for_asciiz() { #[test] fn assemble_data_binary_gives_errors_on_non_ascii_characters_for_ascii_asciiz_and_byte() { - let result = parser(".data\nlabel: .ascii \"❤️🦧❤️\"".to_string()).0; + let result = parser( + ".data\nlabel: .ascii \"❤️🦧❤️\"".to_string(), + AvailableDatapaths::MIPS, + ) + .0; assert_eq!( result.monaco_line_info[1].errors[0].error_name, NonASCIIString ); - let result = parser(".data\nlabel: .asciiz \"❤️🦧❤️\"".to_string()).0; + let result = parser( + ".data\nlabel: .asciiz \"❤️🦧❤️\"".to_string(), + AvailableDatapaths::MIPS, + ) + .0; assert_eq!( result.monaco_line_info[1].errors[0].error_name, NonASCIIString ); - let result = parser(".data\nlabel: .byte \'🦧\'".to_string()).0; + let result = parser( + ".data\nlabel: .byte \'🦧\'".to_string(), + AvailableDatapaths::MIPS, + ) + .0; assert_eq!( result.monaco_line_info[1].errors[0].error_name, NonASCIIChar diff --git a/src/tests/parser/parser_assembler_main.rs b/src/tests/parser/parser_assembler_main.rs index ea70768b2..a38a24224 100644 --- a/src/tests/parser/parser_assembler_main.rs +++ b/src/tests/parser/parser_assembler_main.rs @@ -1,36 +1,1554 @@ #[cfg(test)] mod parser_main_function_tests { + use crate::emulation_core::architectures::AvailableDatapaths; use crate::parser::parser_assembler_main::*; #[test] fn parser_takes_string_and_returns_vec_of_instructions() { - let results = - parser("lw $t1, 512($t1)\nadd $t1, $s6, $t2\naddi $t1, $t2, 43690".to_string()); + let results = parser( + "lw $t1, 512($t1)\nadd $t1, $s6, $t2\naddi $t1, $t2, 43690".to_string(), + AvailableDatapaths::MIPS, + ); + + assert_eq!( + results.0.instructions[0].binary, + 0b10001101001010010000001000000000 + ); + assert_eq!( + results.0.instructions[1].binary, + 0b00000010110010100100100000100000 + ); + assert_eq!( + results.0.instructions[2].binary, + 0b00100001010010011010101010101010 + ); + } +} + +mod read_riscv_instructions_tests { + + use crate::tests::parser::parser_assembler_main::helper_functions::instruction_parser_riscv; + + // RV32I Instructions + + #[test] + fn read_instructions_add() { + let file_string = "add ra, t5, s0".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b00000000100011110000000010110011 + ); + } + + #[test] + fn read_instructions_sub() { + let file_string = "sub x1, x2, x3".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b01000000001100010000000010110011 + ); + } + + #[test] + fn read_instructions_sll() { + let file_string = "sll x4, x5, x6".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b00000000011000101001001000110011 + ); + } + + #[test] + fn read_instructions_slt() { + let file_string = "slt x7, x8, x9".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b00000000100101000010001110110011 + ); + } + + #[test] + fn read_instructions_sltu() { + let file_string = "sltu x10, x11, x12".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b00000000110001011011010100110011 + ); + } + + #[test] + fn read_instructions_xor() { + let file_string = "xor x13, x14, x15".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b00000000111101110100011010110011 + ); + } + + #[test] + fn read_instructions_srl() { + let file_string = "srl x16, x17, x18".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b00000001001010001101100000110011 + ); + } + + #[test] + fn read_instructions_sra() { + let file_string = "sra x19, x20, x21".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b01000001010110100101100110110011 + ); + } + + #[test] + fn read_instructions_or() { + let file_string = "or x22, x23, x24".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b00000001100010111110101100110011 + ); + } + + #[test] + fn read_instructions_and() { + let file_string = "and x25, x26, x27".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b00000001101111010111110010110011 + ); + } + + #[test] + fn read_instructions_addi() { + let file_string = "addi x28, x29, x30".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b00000000000011101000111000010011 + ); + } + + #[test] + fn read_instructions_slti() { + let file_string = "slti x31, t0, 150".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b00001001011000101010111110010011 + ); + } + + #[test] + fn read_instructions_sltiu() { + let file_string = "sltiu t1, t2, 241".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b00001111000100111011001100010011 + ); + } + + #[test] + fn read_instructions_xori() { + let file_string = "xori t3, t4, 440".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b00011011100011101100111000010011 + ); + } + + #[test] + fn read_instructions_ori() { + let file_string = "ori t5, t6, 621".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b00100110110111111110111100010011 + ); + } + + #[test] + fn read_instructions_andi() { + let file_string = "andi ra, sp, 1024".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b01000000000000010111000010010011 + ); + } + + #[test] + fn read_instructions_slli() { + let file_string = "slli gp, tp, 5".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b00000000010100100001000110010011 + ); + } + + #[test] + fn read_instructions_srli() { + let file_string = "srli s0, s1, 10".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b00000000101001001101010000010011 + ); + } + + #[test] + fn read_instructions_srai() { + let file_string = "srai a0, a1, 6".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b01000000011001011101010100010011 + ); + } + + #[test] + fn read_instructions_lb() { + let file_string = "lb a2, 150(a3)".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b00001001011001101000011000000011 + ); + } + + #[test] + fn read_instructions_lh() { + let file_string = "lh a4, 220(a5)".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b00001101110001111001011100000011 + ); + } + + #[test] + fn read_instructions_lw() { + let file_string = "lw a6, 32(a7)".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b00000010000010001010100000000011 + ); + } + + #[test] + fn read_instructions_lbu() { + let file_string = "lbu s2, 128(s3)".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b00001000000010011100100100000011 + ); + } + + #[test] + fn read_instructions_lhu() { + let file_string = "lhu s4, 256(s5)".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b00010000000010101101101000000011 + ); + } + + #[test] + fn read_instructions_sb() { + let file_string = "sb s6, 512(s7)".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b00100001011010111000000000100011 + ); + } + + #[test] + fn read_instructions_sh() { + let file_string = "sh s8, 1024(s9)".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b01000001100011001001000000100011 + ); + } + + #[test] + fn read_instructions_sw() { + let file_string = "sw s10, 2044(s11)".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b01111111101011011010111000100011 + ); + } + + #[test] + fn read_instructions_jal() { + let file_string = "main:\njal x1, L1\nret\nL1:\nadd x1, x2, x3".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b00000000000000000010000011101111 + ); + } + + #[test] + fn read_instructions_jalr() { + let file_string = "jalr x2, x3, 128".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b00001000000000011000000101100111 + ); + } + + #[test] + fn read_instructions_beq() { + let file_string = "main:\nbeq x1, x2, L1\nret\nL1:\nadd x1, x2, x3".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b00000000001000010000000011100011 + ); + } + + #[test] + fn read_instructions_bne() { + let file_string = "main:\nbne x1, x2, L1\nret\nL1:\nadd x1, x2, x3".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b00000000001000010001000011100011 + ); + } + + #[test] + fn read_instructions_blt() { + let file_string = "main:\nblt x1, x2, L1\nret\nL1:\nadd x1, x2, x3".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b00000000001000010100000011100011 + ); + } + + #[test] + fn read_instructions_bge() { + let file_string = "main:\nbge x1, x2, L1\nret\nL1:\nadd x1, x2, x3".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b00000000001000010101000011100011 + ); + } + + #[test] + fn read_instructions_bltu() { + let file_string = "main:\nbltu x1, x2, L1\nret\nL1:\nadd x1, x2, x3".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b00000000001000010110000011100011 + ); + } + + #[test] + fn read_instructions_bgeu() { + let file_string = "main:\nbgeu x1, x2, L1\nret\nL1:\nadd x1, x2, x3".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b00000000001000010111000011100011 + ); + } + + #[test] + fn read_instructions_ecall() { + let file_string = "ecall".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b00000000000000000000000001110011 + ); + } + + #[test] + fn read_instructions_ebreak() { + let file_string = "ebreak".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b00000000000100000000000001110011 + ); + } + + #[test] + fn read_instructions_uret() { + let file_string = "uret".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b00000000001000000000000001110011 + ); + } + + #[test] + fn read_instructions_sret() { + let file_string = "sret".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b00010000001000000000000001110011 + ); + } + + #[test] + fn read_instructions_mret() { + let file_string = "mret".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b00110000001000000000000001110011 + ); + } + + #[test] + fn read_instructions_wfi() { + let file_string = "wfi".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b00010000010100000000000001110011 + ); + } + + #[test] + fn read_instructions_fencei() { + let file_string = "fence.i".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b00000000000000000001000000001111 + ); + } + + #[test] + fn read_instructions_lui() { + let file_string = "lui x16, 4096".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b00000001000000000000100000110111 + ); + } + + #[test] + fn read_instructions_auipc() { + let file_string = "auipc x17, 5024".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b00000001001110100000100010010111 + ); + } + + // RV64I Tests + + #[test] + fn read_instructions_addiw() { + let file_string = "addiw x18, x19, 50".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b00000011001010011000100100011011 + ); + } + + #[test] + fn read_instructions_slliw() { + let file_string = "slliw x20, x21, 16".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b00000001000010101001101000011011 + ); + } + + #[test] + fn read_instructions_srliw() { + let file_string = "srliw x22, x23, 20".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b00000001010010111101101100011011 + ); + } + + #[test] + fn read_instructions_sraiw() { + let file_string = "sraiw x24, x25, 10".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b01000000101011001101110000011011 + ); + } + + #[test] + fn read_instructions_addw() { + let file_string = "addw x26, x27, x28".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b00000001110011011000110100111011 + ); + } + + #[test] + fn read_instructions_subw() { + let file_string = "subw x29, x30, x31".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b01000001111111110000111010111011 + ); + } + + #[test] + fn read_instructions_sllw() { + let file_string = "sllw ra, sp, gp".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b00000000001100010001000010111011 + ); + } + + #[test] + fn read_instructions_srlw() { + let file_string = "srlw tp, t0, t1".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b00000000011000101101001000111011 + ); + } + + #[test] + fn read_instructions_sraw() { + let file_string = "sraw t2, fp, s1".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b01000000100101000101001110111011 + ); + } + + #[test] + fn read_instructions_lwu() { + let file_string = "lwu a0, 50(a1)".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b00000011001001011110010100000011 + ); + } + + #[test] + fn read_instructions_ld() { + let file_string = "ld a2, 50(a3)".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b00000011001001101011011000000011 + ); + } + + #[test] + fn read_instructions_sd() { + let file_string = "sd a4, 50(a5)".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b00000010111001111011100100100011 + ); + } + + // Start of RV32M + + #[test] + fn read_instructions_mul() { + let file_string = "mul a6, a7, s2".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b00000011001010001000100000110011 + ); + } + + #[test] + fn read_instructions_mulh() { + let file_string = "mulh s3, s4, s5".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b00000011010110100001100110110011 + ); + } + + #[test] + fn read_instructions_mulhsu() { + let file_string = "mulhsu s6, s7, s8".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b00000011100010111010101100110011 + ); + } + + #[test] + fn read_instructions_mulhu() { + let file_string = "mulhu s9, s10, s11".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b00000011101111010011110010110011 + ); + } + + #[test] + fn read_instructions_div() { + let file_string = "div t3, t4, t5".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b00000011111011101100111000110011 + ); + } + + #[test] + fn read_instructions_divu() { + let file_string = "divu t6, x1, x2".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b00000010001000001101111110110011 + ); + } + + #[test] + fn read_instructions_rem() { + let file_string = "rem x3, x4, x5".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b00000010010100100110000110110011 + ); + } + + #[test] + fn read_instructions_remu() { + let file_string = "remu x6, x7, x8".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b00000010100000111111001100110011 + ); + } + + // RV64M + + #[test] + fn read_instructions_mulw() { + let file_string = "mulw x9, x10, x11".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b00000010101101010000010010111011 + ); + } + + #[test] + fn read_instructions_divw() { + let file_string = "divw x12, x13, x14".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b00000010111001101100011000111011 + ); + } + + #[test] + fn read_instructions_divuw() { + let file_string = "divuw x15, x16, x17".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b00000011000110000101011110111011 + ); + } + + #[test] + fn read_instructions_remw() { + let file_string = "remw x18, x19, x20".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b00000011010010011110100100111011 + ); + } + + #[test] + fn read_instructions_remuw() { + let file_string = "remuw x21, x22, x23".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b00000011011110110111101010111011 + ); + } + + // Start of RV32F + + #[test] + fn read_instructions_fmadds() { + let file_string = "fmadd.s f1, f2, f3, f4".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b00100000001100010111000011000011 + ); + } + + #[test] + fn read_instructions_fmsubs() { + let file_string = "fmsub.s f5, f6, f7, f8".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b01000000011100110111001011000111 + ); + } + + #[test] + fn read_instructions_fnmsubs() { + let file_string = "fnmsub.s f9, f10, f11, f12".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b01100000101101010111010011001011 + ); + } + + #[test] + fn read_instructions_fnmadds() { + let file_string = "fnmadd.s f13, f14, f15, f16".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b10000000111101110111011011001111 + ); + } + + #[test] + fn read_instructions_fadds() { + let file_string = "fadd.s f17, f18, f19".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b00000001001110010111100011010011 + ); + } + + #[test] + fn read_instructions_fsubs() { + let file_string = "fsub.s f20, f21, f22".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b00001001011010101111101001010011 + ); + } + + #[test] + fn read_instructions_fmuls() { + let file_string = "fmul.s f23, f24, f25".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b00010001100111000111101111010011 + ); + } + + #[test] + fn read_instructions_fdivs() { + let file_string = "fdiv.s f26, f27, f28".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b00011001110011011111110101010011 + ); + } + + #[test] + fn read_instructions_fsqrts() { + let file_string = "fsqrt.s f29, f30".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b01011000000011110111111011010011 + ); + } + + #[test] + fn read_instructions_fsgnjs() { + let file_string = "fsgnj.s f31, ft1, ft2".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b00100000001000001000111111010011 + ); + } + + #[test] + fn read_instructions_fsgnjns() { + let file_string = "fsgnjn.s ft3, ft4, ft5".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b00100000010100100001000111010011 + ); + } + + #[test] + fn read_instructions_fsgnjxs() { + let file_string = "fsgnjx.s f6, f7, f8".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b00100000100000111010001101010011 + ); + } + + #[test] + fn read_instructions_fmins() { + let file_string = "fmin.s fs1, fa0, fa1".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b00101000101101010000010011010011 + ); + } + + #[test] + fn read_instructions_fmaxs() { + let file_string = "fmax.s fa2, fa3, fa4".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b00101000111001101001011001010011 + ); + } + + #[test] + fn read_instructions_fcvtws() { + let file_string = "fcvt.w.s x1, fa5".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b11000000000001111111000011010011 + ); + } + + #[test] + fn read_instructions_fcvtwus() { + let file_string = "fcvt.wu.s x2, fa6".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b11000000000110000111000101010011 + ); + } + + #[test] + fn read_instructions_fmvxw() { + let file_string = "fmv.x.w x3, fa7".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b11100000000010001000000111010011 + ); + } + + #[test] + fn read_instructions_feqs() { + let file_string = "feq.s x4, fs2, fs3".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b10100001001110010010001001010011 + ); + } + + #[test] + fn read_instructions_flts() { + let file_string = "flt.s x5, fs4, fs5".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b10100001010110100001001011010011 + ); + } + + #[test] + fn read_instructions_fles() { + let file_string = "fle.s x6, fs6, fs7".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b10100001011110110000001101010011 + ); + } + + #[test] + fn read_instructions_fclasss() { + let file_string = "fclass.s x7, fs8".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b11100000000011000001001111010011 + ); + } + + #[test] + fn read_instructions_fcvtsw() { + let file_string = "fcvt.s.w fs9, x8".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b11010000000001000111110011010011 + ); + } + + #[test] + fn read_instructions_fcvtswu() { + let file_string = "fcvt.s.wu fs10, x9".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b11010000000101001111110101010011 + ); + } + + #[test] + fn read_instructions_fmvwx() { + let file_string = "fmv.w.x fs11, x10".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b11110000000001010000110111010011 + ); + } + + #[test] + fn read_instructions_fmaddd() { + let file_string = "fmadd.d ft8, ft9, ft10, ft11".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b11111011111011101111111001000011 + ); + } + + #[test] + fn read_instructions_fmsubd() { + let file_string = "fmsub.d f1, f2, f3, f4".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b00100010001100010111000011000111 + ); + } + + #[test] + fn read_instructions_fnmaddd() { + let file_string = "fnmsub.d f5, f6, f7, f8".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b01000010011100110111001011001011 + ); + } + + #[test] + fn read_instructions_fnmsubd() { + let file_string = "fnmadd.d f9, f10, f11, f12".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b01100010101101010111010011001111 + ); + } + + #[test] + fn read_instructions_faddd() { + let file_string = "fadd.d f13, f14, f15".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b00000010111101110111011011010011 + ); + } + + #[test] + fn read_instructions_fsubd() { + let file_string = "fsub.d f16, f17, f18".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b00001011001010001111100001010011 + ); + } + + #[test] + fn read_instructions_fmuld() { + let file_string = "fmul.d f19, f20, f21".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); assert_eq!( - results.0.instructions[0].binary, - 0b10001101001010010000001000000000 + instruction_list[0].binary, + 0b00010011010110100111100111010011 ); + } + + #[test] + fn read_instructions_fdivd() { + let file_string = "fdiv.d f22, f23, f24".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + assert_eq!( - results.0.instructions[1].binary, - 0b00000010110010100100100000100000 + instruction_list[0].binary, + 0b00011011100010111111101101010011 + ); + } + + #[test] + fn read_instructions_fsqrtd() { + let file_string = "fsqrt.d f25, f26".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b01011010000011010111110011010011 ); + } + + #[test] + fn read_instructions_fsgnjd() { + let file_string = "fsgnj.d f27, f28, f29".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + assert_eq!( - results.0.instructions[2].binary, - 0b00100001010010011010101010101010 + instruction_list[0].binary, + 0b00100011110111100000110111010011 + ); + } + + #[test] + fn read_instructions_fsgnjnd() { + let file_string = "fsgnjn.d f30, f31, f1".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b00100010000111111001111101010011 + ); + } + + #[test] + fn read_instructions_fsgnjxd() { + let file_string = "fsgnjx.d f2, f3, f4".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b00100010010000011010000101010011 + ); + } + + #[test] + fn read_instructions_fmind() { + let file_string = "fmin.d f5, f6, f7".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b00101010011100110000001011010011 + ); + } + + #[test] + fn read_instructions_fmaxd() { + let file_string = "fmax.d f8, f9, f10".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b00101010101001001001010001010011 + ); + } + + #[test] + fn read_instructions_fcvtsd() { + let file_string = "fcvt.s.d f11, f12".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b01000000000101100111010111010011 + ); + } + + #[test] + fn read_instructions_fcvtds() { + let file_string = "fcvt.d.s f13, f14".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b01000010000001110111011011010011 + ); + } + + #[test] + fn read_instructions_feqd() { + let file_string = "feq.d x11, f15, f16".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b10100011000001111010010111010011 + ); + } + + #[test] + fn read_instructions_fltd() { + let file_string = "flt.d x12, f17, f18".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b10100011001010001001011001010011 + ); + } + + #[test] + fn read_instructions_fled() { + let file_string = "fle.d x13, f19, f20".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b10100011010010011000011011010011 + ); + } + + #[test] + fn read_instructions_fclassd() { + let file_string = "fclass.d x14, f21".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b11100010000010101001011101010011 + ); + } + + #[test] + fn read_instructions_fcvtwd() { + let file_string = "fcvt.w.d x15, f22".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b11000010000010110111011111010011 + ); + } + + #[test] + fn read_instructions_fcvtwud() { + let file_string = "fcvt.wu.d x16, f23".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b11000010000110111111100001010011 + ); + } + + #[test] + fn read_instructions_fcvtdw() { + let file_string = "fcvt.d.w f24, x17".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b11010010000010001111110001010011 + ); + } + + #[test] + fn read_instructions_fcvtdwu() { + let file_string = "fcvt.d.wu f25, x18".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b11010010000110010111110011010011 + ); + } + + #[test] + fn read_instructions_flw() { + let file_string = "flw f26, 128(x19)".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b00001000000010011010110100000111 + ); + } + + #[test] + fn read_instructions_fsw() { + let file_string = "fsw f27, 256(x20)".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b00010001101110100010000000100111 + ); + } + + #[test] + fn read_instructions_fld() { + let file_string = "fld f28, 512(x21)".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b00100000000010101011111000000111 + ); + } + + #[test] + fn read_instructions_fsd() { + let file_string = "fsd f29, 1024(x22)".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b01000001110110110011000000100111 + ); + } + + #[test] + fn read_instructions_fcvtls() { + let file_string = "fcvt.l.s x23, f30".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b11000000001011110111101111010011 + ); + } + + #[test] + fn read_instructions_fcvtlus() { + let file_string = "fcvt.lu.s x24, f31".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b11000000001111111111110001010011 + ); + } + + #[test] + fn read_instructions_fcvtsl() { + let file_string = "fcvt.s.l f1, x25".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b11010000001011001111000011010011 + ); + } + + #[test] + fn read_instructions_fcvtslu() { + let file_string = "fcvt.s.lu f2, x26".to_string(); + + let instruction_list = instruction_parser_riscv(file_string); + + assert_eq!( + instruction_list[0].binary, + 0b11010000001111010111000101010011 ); } } -mod read_instructions_tests { +mod read_mips_instructions_tests { use crate::parser::parser_structs_and_enums::ErrorType::JALRRDRegisterZero; - use crate::tests::parser::parser_assembler_main::helper_functions::instruction_parser; + use crate::tests::parser::parser_assembler_main::helper_functions::instruction_parser_mips; #[test] fn read_instructions_add() { let file_string = "add $t1, $s6, $t2".to_string(); - let instruction_list = instruction_parser(file_string); + let instruction_list = instruction_parser_mips(file_string); assert_eq!( instruction_list[0].binary, @@ -42,7 +1560,7 @@ mod read_instructions_tests { fn read_instructions_addu() { let file_string = "addu $t1, $t2, $t3".to_string(); - let instruction_list = instruction_parser(file_string); + let instruction_list = instruction_parser_mips(file_string); assert_eq!( instruction_list[0].binary, @@ -54,7 +1572,7 @@ mod read_instructions_tests { fn read_instructions_sub() { let file_string = "sub $t1, $s6, $t2".to_string(); - let instruction_list = instruction_parser(file_string); + let instruction_list = instruction_parser_mips(file_string); assert_eq!( instruction_list[0].binary, @@ -66,7 +1584,7 @@ mod read_instructions_tests { fn read_instructions_mul() { let file_string = "mul $t1, $s6, $t2".to_string(); - let instruction_list = instruction_parser(file_string); + let instruction_list = instruction_parser_mips(file_string); assert_eq!( instruction_list[0].binary, @@ -78,7 +1596,7 @@ mod read_instructions_tests { fn read_instructions_div() { let file_string = "div $t1, $t1, $s6".to_string(); - let instruction_list = instruction_parser(file_string); + let instruction_list = instruction_parser_mips(file_string); assert_eq!( instruction_list[0].binary, @@ -90,7 +1608,7 @@ mod read_instructions_tests { fn read_instructions_lw() { let file_string = "lw $t1, 512($t1)".to_string(); - let instruction_list = instruction_parser(file_string); + let instruction_list = instruction_parser_mips(file_string); assert_eq!( instruction_list[0].binary, @@ -102,7 +1620,7 @@ mod read_instructions_tests { fn read_instructions_sw() { let file_string = "sw $t1, 512($t1)".to_string(); - let instruction_list = instruction_parser(file_string); + let instruction_list = instruction_parser_mips(file_string); assert_eq!( instruction_list[0].binary, @@ -114,7 +1632,7 @@ mod read_instructions_tests { fn read_instructions_lui() { let file_string = "lui $t1, 43690".to_string(); - let instruction_list = instruction_parser(file_string); + let instruction_list = instruction_parser_mips(file_string); assert_eq!( instruction_list[0].binary, @@ -126,7 +1644,7 @@ mod read_instructions_tests { fn read_instructions_aui() { let file_string = "aui $t1, $t1, 43690".to_string(); - let instruction_list = instruction_parser(file_string); + let instruction_list = instruction_parser_mips(file_string); assert_eq!( instruction_list[0].binary, @@ -138,7 +1656,7 @@ mod read_instructions_tests { fn read_instructions_addi() { let file_string = "addi $t1, $t2, 43690".to_string(); - let instruction_list = instruction_parser(file_string); + let instruction_list = instruction_parser_mips(file_string); assert_eq!( instruction_list[0].binary, 0b00100001010010011010101010101010 @@ -147,7 +1665,7 @@ mod read_instructions_tests { #[test] fn read_instructions_recognizes_addiu() { - let instruction_list = instruction_parser("addiu $t1, $t2, 0x64".to_string()); + let instruction_list = instruction_parser_mips("addiu $t1, $t2, 0x64".to_string()); assert_eq!( instruction_list[0].binary, @@ -159,7 +1677,7 @@ mod read_instructions_tests { fn read_instructions_and() { let file_string = "and $t1, $s6, $t2".to_string(); - let instruction_list = instruction_parser(file_string); + let instruction_list = instruction_parser_mips(file_string); assert_eq!( instruction_list[0].binary, @@ -171,7 +1689,7 @@ mod read_instructions_tests { fn read_instructions_or() { let file_string = "or $t1, $s6, $t2".to_string(); - let instruction_list = instruction_parser(file_string); + let instruction_list = instruction_parser_mips(file_string); assert_eq!( instruction_list[0].binary, @@ -183,7 +1701,7 @@ mod read_instructions_tests { fn read_instructions_ori() { let file_string = "ori $t1, $t2, 43690".to_string(); - let instruction_list = instruction_parser(file_string); + let instruction_list = instruction_parser_mips(file_string); assert_eq!( instruction_list[0].binary, @@ -195,7 +1713,7 @@ mod read_instructions_tests { fn read_instructions_andi() { let file_string = "andi $t1, $t2, 43690".to_string(); - let instruction_list = instruction_parser(file_string); + let instruction_list = instruction_parser_mips(file_string); assert_eq!( instruction_list[0].binary, @@ -207,7 +1725,7 @@ mod read_instructions_tests { fn read_instructions_dadd() { let file_string = "dadd $t1, $t2, $s6".to_string(); - let instruction_list = instruction_parser(file_string); + let instruction_list = instruction_parser_mips(file_string); assert_eq!( instruction_list[0].binary, @@ -219,7 +1737,7 @@ mod read_instructions_tests { fn read_instructions_dsub() { let file_string = "dsub $t1, $t2, $s6".to_string(); - let instruction_list = instruction_parser(file_string); + let instruction_list = instruction_parser_mips(file_string); assert_eq!( instruction_list[0].binary, @@ -231,7 +1749,7 @@ mod read_instructions_tests { fn read_instructions_dmul() { let file_string = "dmul $t1, $t2, $s6".to_string(); - let instruction_list = instruction_parser(file_string); + let instruction_list = instruction_parser_mips(file_string); assert_eq!( instruction_list[0].binary, @@ -243,7 +1761,7 @@ mod read_instructions_tests { fn read_instructions_ddiv() { let file_string = "ddiv $t1, $t1, $t2".to_string(); - let instruction_list = instruction_parser(file_string); + let instruction_list = instruction_parser_mips(file_string); assert_eq!( instruction_list[0].binary, @@ -255,7 +1773,7 @@ mod read_instructions_tests { fn read_instructions_add_s() { let file_string = "add.s $f9, $f10, $f22".to_string(); - let instruction_list = instruction_parser(file_string); + let instruction_list = instruction_parser_mips(file_string); assert_eq!( instruction_list[0].binary, @@ -267,7 +1785,7 @@ mod read_instructions_tests { fn read_instructions_add_d() { let file_string = "add.d $f9, $f10, $f22".to_string(); - let instruction_list = instruction_parser(file_string); + let instruction_list = instruction_parser_mips(file_string); assert_eq!( instruction_list[0].binary, @@ -279,7 +1797,7 @@ mod read_instructions_tests { fn read_instructions_sub_s() { let file_string = "sub.s $f9, $f10, $f22".to_string(); - let instruction_list = instruction_parser(file_string); + let instruction_list = instruction_parser_mips(file_string); assert_eq!( instruction_list[0].binary, @@ -291,7 +1809,7 @@ mod read_instructions_tests { fn read_instructions_sub_d() { let file_string = "sub.d $f9, $f10, $f22".to_string(); - let instruction_list = instruction_parser(file_string); + let instruction_list = instruction_parser_mips(file_string); assert_eq!( instruction_list[0].binary, @@ -303,7 +1821,7 @@ mod read_instructions_tests { fn read_instructions_mul_s() { let file_string = "mul.s $f9, $f10, $f22".to_string(); - let instruction_list = instruction_parser(file_string); + let instruction_list = instruction_parser_mips(file_string); assert_eq!( instruction_list[0].binary, @@ -315,7 +1833,7 @@ mod read_instructions_tests { fn read_instructions_mul_d() { let file_string = "mul.d $f9, $f10, $f22".to_string(); - let instruction_list = instruction_parser(file_string); + let instruction_list = instruction_parser_mips(file_string); assert_eq!( instruction_list[0].binary, @@ -327,7 +1845,7 @@ mod read_instructions_tests { fn read_instructions_div_s() { let file_string = "div.s $f9, $f10, $f22".to_string(); - let instruction_list = instruction_parser(file_string); + let instruction_list = instruction_parser_mips(file_string); assert_eq!( instruction_list[0].binary, @@ -339,7 +1857,7 @@ mod read_instructions_tests { fn read_instructions_div_d() { let file_string = "div.d $f9, $f10, $f22".to_string(); - let instruction_list = instruction_parser(file_string); + let instruction_list = instruction_parser_mips(file_string); assert_eq!( instruction_list[0].binary, @@ -351,7 +1869,7 @@ mod read_instructions_tests { fn read_instructions_dahi() { let file_string = "dahi $t1, 43690".to_string(); - let instruction_list = instruction_parser(file_string); + let instruction_list = instruction_parser_mips(file_string); assert_eq!( instruction_list[0].binary, @@ -363,7 +1881,7 @@ mod read_instructions_tests { fn read_instructions_dati() { let file_string = "dati $t1, 43690".to_string(); - let instruction_list = instruction_parser(file_string); + let instruction_list = instruction_parser_mips(file_string); assert_eq!( instruction_list[0].binary, @@ -375,7 +1893,7 @@ mod read_instructions_tests { fn read_instructions_daddi() { let file_string = "daddi $t1, $t2, 43690".to_string(); - let instruction_list = instruction_parser(file_string); + let instruction_list = instruction_parser_mips(file_string); assert_eq!( instruction_list[0].binary, @@ -387,7 +1905,7 @@ mod read_instructions_tests { fn read_instructions_daddiu() { let file_string = "daddiu $t1, $t2, 43690".to_string(); - let instruction_list = instruction_parser(file_string); + let instruction_list = instruction_parser_mips(file_string); assert_eq!( instruction_list[0].binary, @@ -399,7 +1917,7 @@ mod read_instructions_tests { fn read_instructions_daddu() { let file_string = "daddu $t1, $t2, $t3".to_string(); - let instruction_list = instruction_parser(file_string); + let instruction_list = instruction_parser_mips(file_string); assert_eq!( instruction_list[0].binary, @@ -411,7 +1929,7 @@ mod read_instructions_tests { fn read_instructions_dsubu() { let file_string = "dsubu $t1, $t2, $t3".to_string(); - let instruction_list = instruction_parser(file_string); + let instruction_list = instruction_parser_mips(file_string); assert_eq!( instruction_list[0].binary, @@ -423,7 +1941,7 @@ mod read_instructions_tests { fn read_instructions_dmulu() { let file_string = "dmulu $t1, $t2, $t3".to_string(); - let instruction_list = instruction_parser(file_string); + let instruction_list = instruction_parser_mips(file_string); assert_eq!( instruction_list[0].binary, @@ -435,7 +1953,7 @@ mod read_instructions_tests { fn read_instructions_ddivu() { let file_string = "ddivu $t1, $t1, $t2".to_string(); - let instruction_list = instruction_parser(file_string); + let instruction_list = instruction_parser_mips(file_string); assert_eq!( instruction_list[0].binary, @@ -447,7 +1965,7 @@ mod read_instructions_tests { fn read_instructions_slt() { let file_string = "slt $t1, $t2, $s6".to_string(); - let instruction_list = instruction_parser(file_string); + let instruction_list = instruction_parser_mips(file_string); assert_eq!( instruction_list[0].binary, @@ -459,7 +1977,7 @@ mod read_instructions_tests { fn read_instructions_sltu() { let file_string = "sltu $t1, $t2, $s6".to_string(); - let instruction_list = instruction_parser(file_string); + let instruction_list = instruction_parser_mips(file_string); assert_eq!( instruction_list[0].binary, @@ -471,7 +1989,7 @@ mod read_instructions_tests { fn read_instructions_swc1() { let file_string = "swc1 $f9, 43690($t2)".to_string(); - let instruction_list = instruction_parser(file_string); + let instruction_list = instruction_parser_mips(file_string); assert_eq!( instruction_list[0].binary, @@ -483,7 +2001,7 @@ mod read_instructions_tests { fn read_instructions_lwc1() { let file_string = "lwc1 $f9, 43690($t2)".to_string(); - let instruction_list = instruction_parser(file_string); + let instruction_list = instruction_parser_mips(file_string); assert_eq!( instruction_list[0].binary, @@ -495,7 +2013,7 @@ mod read_instructions_tests { fn read_instructions_mtc1() { let file_string = "mtc1 $t1, $f22".to_string(); - let instruction_list = instruction_parser(file_string); + let instruction_list = instruction_parser_mips(file_string); assert_eq!( instruction_list[0].binary, @@ -507,7 +2025,7 @@ mod read_instructions_tests { fn read_instructions_dmtc1() { let file_string = "dmtc1 $t1, $f22".to_string(); - let instruction_list = instruction_parser(file_string); + let instruction_list = instruction_parser_mips(file_string); assert_eq!( instruction_list[0].binary, @@ -519,7 +2037,7 @@ mod read_instructions_tests { fn read_instructions_mfc1() { let file_string = "mfc1 $t1, $f22".to_string(); - let instruction_list = instruction_parser(file_string); + let instruction_list = instruction_parser_mips(file_string); assert_eq!( instruction_list[0].binary, @@ -531,7 +2049,7 @@ mod read_instructions_tests { fn read_instructions_dmfc1() { let file_string = "dmfc1 $t1, $f22".to_string(); - let instruction_list = instruction_parser(file_string); + let instruction_list = instruction_parser_mips(file_string); assert_eq!( instruction_list[0].binary, @@ -545,7 +2063,7 @@ mod read_instructions_tests { "Add $t1, $t2, $t3\nAddress: add $t1, #t2, $t3\nlw $t1, 400($t2)\nj Address" .to_string(); - let instruction_list = instruction_parser(file_string); + let instruction_list = instruction_parser_mips(file_string); assert_eq!( instruction_list[3].binary, @@ -559,7 +2077,7 @@ mod read_instructions_tests { "Add $t1, $t2, $t3\nAddress: add $t1, #t2, $t3\nlw $t1, 400($t2)\njal Address" .to_string(); - let instruction_list = instruction_parser(file_string); + let instruction_list = instruction_parser_mips(file_string); assert_eq!( instruction_list[3].binary, @@ -570,7 +2088,7 @@ mod read_instructions_tests { #[test] fn read_instructions_beq() { let file_string = "Add $t1, $t2, $t3\nAddress: add $t1, #t2, $t3\nlw $t1, 400($t2)\nbeq $t1, $t2, address".to_string(); - let instruction_list = instruction_parser(file_string); + let instruction_list = instruction_parser_mips(file_string); assert_eq!( instruction_list[3].binary, @@ -581,7 +2099,7 @@ mod read_instructions_tests { #[test] fn read_instructions_bne() { let file_string = "Add $t1, $t2, $t3\nAddress: add $t1, #t2, $t3\nlw $t1, 400($t2)\nbne $t1, $t2, address".to_string(); - let instruction_list = instruction_parser(file_string); + let instruction_list = instruction_parser_mips(file_string); assert_eq!( instruction_list[3].binary, @@ -591,7 +2109,7 @@ mod read_instructions_tests { #[test] fn read_instructions_c_eq_s() { - let instruction_list = instruction_parser("c.eq.s $f9, $f22".to_string()); + let instruction_list = instruction_parser_mips("c.eq.s $f9, $f22".to_string()); assert_eq!( instruction_list[0].binary, @@ -601,7 +2119,7 @@ mod read_instructions_tests { #[test] fn read_instructions_c_eq_d() { - let instruction_list = instruction_parser("c.eq.d $f9, $f22".to_string()); + let instruction_list = instruction_parser_mips("c.eq.d $f9, $f22".to_string()); assert_eq!( instruction_list[0].binary, @@ -611,7 +2129,7 @@ mod read_instructions_tests { #[test] fn read_instructions_c_lt_s() { - let instruction_list = instruction_parser("c.lt.s $f9, $f22".to_string()); + let instruction_list = instruction_parser_mips("c.lt.s $f9, $f22".to_string()); assert_eq!( instruction_list[0].binary, @@ -621,7 +2139,7 @@ mod read_instructions_tests { #[test] fn read_instructions_c_lt_d() { - let instruction_list = instruction_parser("c.lt.d $f9, $f22".to_string()); + let instruction_list = instruction_parser_mips("c.lt.d $f9, $f22".to_string()); assert_eq!( instruction_list[0].binary, @@ -631,7 +2149,7 @@ mod read_instructions_tests { #[test] fn read_instructions_c_le_s() { - let instruction_list = instruction_parser("c.le.s $f9, $f22".to_string()); + let instruction_list = instruction_parser_mips("c.le.s $f9, $f22".to_string()); assert_eq!( instruction_list[0].binary, @@ -641,7 +2159,7 @@ mod read_instructions_tests { #[test] fn read_instructions_c_le_d() { - let instruction_list = instruction_parser("c.le.d $f9, $f22".to_string()); + let instruction_list = instruction_parser_mips("c.le.d $f9, $f22".to_string()); assert_eq!( instruction_list[0].binary, @@ -651,7 +2169,7 @@ mod read_instructions_tests { #[test] fn read_instructions_c_ngt_s() { - let instruction_list = instruction_parser("c.ngt.s $f9, $f22".to_string()); + let instruction_list = instruction_parser_mips("c.ngt.s $f9, $f22".to_string()); assert_eq!( instruction_list[0].binary, @@ -661,7 +2179,7 @@ mod read_instructions_tests { #[test] fn read_instructions_c_ngt_d() { - let instruction_list = instruction_parser("c.ngt.d $f9, $f22".to_string()); + let instruction_list = instruction_parser_mips("c.ngt.d $f9, $f22".to_string()); assert_eq!( instruction_list[0].binary, @@ -671,7 +2189,7 @@ mod read_instructions_tests { #[test] fn read_instructions_c_nge_s() { - let instruction_list = instruction_parser("c.nge.s $f9, $f22".to_string()); + let instruction_list = instruction_parser_mips("c.nge.s $f9, $f22".to_string()); assert_eq!( instruction_list[0].binary, @@ -681,7 +2199,7 @@ mod read_instructions_tests { #[test] fn read_instructions_c_nge_d() { - let instruction_list = instruction_parser("c.nge.d $f9, $f22".to_string()); + let instruction_list = instruction_parser_mips("c.nge.d $f9, $f22".to_string()); assert_eq!( instruction_list[0].binary, @@ -692,7 +2210,7 @@ mod read_instructions_tests { #[test] fn read_instruction_bc1t() { let instruction_list = - instruction_parser("instruction: add $t1, $t2, $t3\nbc1t instruction".to_string()); + instruction_parser_mips("instruction: add $t1, $t2, $t3\nbc1t instruction".to_string()); assert_eq!( instruction_list[1].binary, @@ -703,7 +2221,7 @@ mod read_instructions_tests { #[test] fn read_instruction_bc1f() { let instruction_list = - instruction_parser("instruction: add $t1, $t2, $t3\nbc1f instruction".to_string()); + instruction_parser_mips("instruction: add $t1, $t2, $t3\nbc1f instruction".to_string()); assert_eq!( instruction_list[1].binary, @@ -713,7 +2231,7 @@ mod read_instructions_tests { #[test] fn read_instruction_jalr_with_rd() { - let instruction_list = instruction_parser("jalr $t1, $t2".to_string()); + let instruction_list = instruction_parser_mips("jalr $t1, $t2".to_string()); assert_eq!( instruction_list[0].binary, @@ -723,7 +2241,7 @@ mod read_instructions_tests { #[test] fn read_instruction_jalr_without_rd() { - let instruction_list = instruction_parser("jalr $t2".to_string()); + let instruction_list = instruction_parser_mips("jalr $t2".to_string()); assert_eq!( instruction_list[0].binary, @@ -733,7 +2251,7 @@ mod read_instructions_tests { #[test] fn read_instruction_jalr_creates_error_with_rd_equal_0() { - let instruction_list = instruction_parser("jalr $zero, $t2".to_string()); + let instruction_list = instruction_parser_mips("jalr $zero, $t2".to_string()); assert_eq!(instruction_list[0].errors[0].error_name, JALRRDRegisterZero); } @@ -741,7 +2259,7 @@ mod read_instructions_tests { #[test] fn read_instructions_recognizes_b() { let instruction_list = - instruction_parser(".text\njump: addi $t1, $t2, 100\nb jump".to_string()); + instruction_parser_mips(".text\njump: addi $t1, $t2, 100\nb jump".to_string()); assert_eq!( instruction_list[1].binary, @@ -751,7 +2269,7 @@ mod read_instructions_tests { #[test] fn read_instructions_recognizes_jr() { - let instruction_list = instruction_parser(".text\njump: jr $zero\nb jump".to_string()); + let instruction_list = instruction_parser_mips(".text\njump: jr $zero\nb jump".to_string()); assert_eq!( instruction_list[0].binary, @@ -761,7 +2279,7 @@ mod read_instructions_tests { #[test] fn read_instructions_recognizes_jr_ra() { - let instruction_list = instruction_parser(".text\njump: jr $ra\nb jump".to_string()); + let instruction_list = instruction_parser_mips(".text\njump: jr $ra\nb jump".to_string()); // Page 249 in the MIPS64 release 6 manual // https://s3-eu-west-1.amazonaws.com/downloads-mips/documents/MIPS_Architecture_MIPS64_InstructionSet_%20AFP_P_MD00087_06.05.pdf @@ -773,7 +2291,7 @@ mod read_instructions_tests { #[test] fn read_instructions_recognizes_sll() { - let instruction_list = instruction_parser(".text\nsll $t1, $t2, 5".to_string()); + let instruction_list = instruction_parser_mips(".text\nsll $t1, $t2, 5".to_string()); assert_eq!( instruction_list[0].binary, @@ -783,12 +2301,13 @@ mod read_instructions_tests { #[test] fn read_instructions_recognizes_nop() { - let instruction_list = instruction_parser(".text\nnop".to_string()); + let instruction_list = instruction_parser_mips(".text\nnop".to_string()); assert_eq!(instruction_list[0].binary, 0); } } +use crate::emulation_core::architectures::AvailableDatapaths; use crate::parser::assembling::assemble_data_binary; use crate::parser::parser_assembler_main::{ create_binary_vec, parser, place_binary_in_middle_of_another, read_instructions, @@ -797,7 +2316,7 @@ use crate::parser::parser_structs_and_enums::ErrorType::{ UnrecognizedInstruction, UnsupportedInstruction, }; use crate::parser::parser_structs_and_enums::{ - ProgramInfo, SUPPORTED_INSTRUCTIONS, UNSUPPORTED_INSTRUCTIONS, + ProgramInfo, SUPPORTED_INSTRUCTIONS_MIPS, UNSUPPORTED_INSTRUCTIONS_MIPS, }; use crate::parser::parsing::{create_label_map, separate_data_and_text, tokenize_program}; use crate::parser::pseudo_instruction_parsing::{ @@ -829,13 +2348,16 @@ fn place_binary_works_dahi() { mod helper_functions { use crate::parser::assembling::assemble_data_binary; - use crate::parser::parser_assembler_main::read_instructions; + use crate::parser::parser_assembler_main::{read_instructions, read_instructions_riscv}; use crate::parser::parser_structs_and_enums::Instruction; use crate::parser::parsing::{create_label_map, separate_data_and_text, tokenize_program}; - use crate::parser::pseudo_instruction_parsing::expand_pseudo_instructions_and_assign_instruction_numbers; + use crate::parser::pseudo_instruction_parsing::{ + expand_pseudo_instructions_and_assign_instruction_numbers, + expand_pseudo_instructions_and_assign_instruction_numbers_riscv, + }; use std::collections::HashMap; - pub fn instruction_parser(mut file_string: String) -> Vec { + pub fn instruction_parser_mips(mut file_string: String) -> Vec { file_string = file_string.to_lowercase(); let mut monaco_line_info_vec = tokenize_program(file_string); @@ -854,6 +2376,26 @@ mod helper_functions { instruction_list } + + pub fn instruction_parser_riscv(mut file_string: String) -> Vec { + file_string = file_string.to_lowercase(); + + let mut monaco_line_info_vec = tokenize_program(file_string); + let (mut instruction_list, mut data) = + separate_data_and_text(&mut monaco_line_info_vec.clone()); + expand_pseudo_instructions_and_assign_instruction_numbers_riscv( + &mut instruction_list, + &data, + &mut monaco_line_info_vec, + ); + assemble_data_binary(&mut data); + + let labels: HashMap = create_label_map(&mut instruction_list, &mut data); + + read_instructions_riscv(&mut instruction_list, &labels, &mut monaco_line_info_vec); + + instruction_list + } } #[test] @@ -886,7 +2428,7 @@ fn create_binary_vec_works_with_data() { &mut program_info.monaco_line_info, ); - let result = create_binary_vec(program_info.instructions.clone(), vec_of_data); + let (result, _) = create_binary_vec(program_info.instructions.clone(), vec_of_data); assert_eq!(result[3], 0b01110100011010000110100101110011); assert_eq!(result[4], 0b00100000011010010111001100100000); @@ -896,7 +2438,11 @@ fn create_binary_vec_works_with_data() { #[test] fn read_instructions_recognizes_valid_but_unsupported_instructions() { - let program_info = parser("nor $t1, $t2, $t3\ndsrav $t1, $t2, $t3\n".to_string()).0; + let program_info = parser( + "nor $t1, $t2, $t3\ndsrav $t1, $t2, $t3\n".to_string(), + AvailableDatapaths::MIPS, + ) + .0; assert_eq!( program_info.instructions[0].errors[0].error_name, @@ -912,6 +2458,7 @@ fn read_instructions_recognizes_valid_but_unsupported_instructions() { fn console_output_post_assembly_works_with_errors() { let result = parser( ".text\nadd $t1, $t2, 1235\n.data\nlabel: .ascii 100\n.text\nlw t1, address".to_string(), + AvailableDatapaths::MIPS, ) .0 .console_out_post_assembly; @@ -924,6 +2471,7 @@ fn console_output_post_assembly_works_with_no_errors_present() { let result = parser( ".text\nadd $t1, $t2, $t3\n.data\nlabel: .ascii \"string\"\n.text\nlw $t1, 40($t1)" .to_string(), + AvailableDatapaths::MIPS, ) .0 .console_out_post_assembly; @@ -933,7 +2481,11 @@ fn console_output_post_assembly_works_with_no_errors_present() { #[test] fn mouse_hover_holds_information_about_valid_instructions() { - let program_info = parser(".text\nori $t1, $t2, 100\nsyscall".to_string()).0; + let program_info = parser( + ".text\nori $t1, $t2, 100\nsyscall".to_string(), + AvailableDatapaths::MIPS, + ) + .0; assert_eq!(program_info.monaco_line_info[0].mouse_hover_string, ""); assert_eq!(program_info.monaco_line_info[1].mouse_hover_string, "**Syntax:** `ori rt, rs, immediate`\n\nBitwise ors the contents of `rs` with the left zero-extended `immediate` value, and stores the result in `rt`.\n\n\n\n**Binary:** `0b00110101010010010000000001100100`"); @@ -941,7 +2493,11 @@ fn mouse_hover_holds_information_about_valid_instructions() { #[test] fn mouse_hover_holds_information_about_pseudo_instructions() { - let program_info = parser(".text\nlabel: subi $t1, $t2, 100\nsyscall".to_string()).0; + let program_info = parser( + ".text\nlabel: subi $t1, $t2, 100\nsyscall".to_string(), + AvailableDatapaths::MIPS, + ) + .0; assert_eq!(program_info.monaco_line_info[0].mouse_hover_string, ""); assert_eq!(program_info.monaco_line_info[1].mouse_hover_string, "`subi` is a pseudo-instruction.\n\n```\nsubi rt, rs, immediate =>\nori $at, $zero, immediate\nsub rt, rs, $at\n\n```\n\n\n\n**Binary:** `0b00110100000000010000000001100100`\n\n**Binary:** `0b00000001010000010100100000100010`"); @@ -949,7 +2505,11 @@ fn mouse_hover_holds_information_about_pseudo_instructions() { #[test] fn errors_do_not_go_into_mouse_hover() { - let program_info = parser(".text\nori $t1, $t2, $t3\nsyscall".to_string()).0; + let program_info = parser( + ".text\nori $t1, $t2, $t3\nsyscall".to_string(), + AvailableDatapaths::MIPS, + ) + .0; assert_eq!(program_info.monaco_line_info[0].mouse_hover_string, ""); assert_eq!(program_info.monaco_line_info[1].mouse_hover_string, "**Syntax:** `ori rt, rs, immediate`\n\nBitwise ors the contents of `rs` with the left zero-extended `immediate` value, and stores the result in `rt`.\n\n"); @@ -959,6 +2519,7 @@ fn errors_do_not_go_into_mouse_hover() { fn syscall_message_and_binary_does_not_go_in_mouse_hover_if_the_syscall_was_added_by_parser() { let monaco_line_info = parser( ".text\nori $t1, $t2, 100\nlabel: subi $t1, $t2, 100\nadd $t1, $t2, $t3\n".to_string(), + AvailableDatapaths::MIPS, ) .0 .monaco_line_info; @@ -968,7 +2529,9 @@ fn syscall_message_and_binary_does_not_go_in_mouse_hover_if_the_syscall_was_adde assert_eq!(monaco_line_info[2].mouse_hover_string, "`subi` is a pseudo-instruction.\n\n```\nsubi rt, rs, immediate =>\nori $at, $zero, immediate\nsub rt, rs, $at\n\n```\n\n\n\n**Binary:** `0b00110100000000010000000001100100`\n\n**Binary:** `0b00000001010000010100100000100010`"); assert_eq!(monaco_line_info[3].mouse_hover_string, "**Syntax:** `add rd, rs, rt`\n\nAdds the 32-bit values in `rs` and `rt`, and places the result in `rd`.\n\nIn hardware implementations, the result is not placed in `rd` if adding `rs` and `rt` causes a 32-bit overflow. However, SWIM places the result in `rd` regardless since there is no exception handling.\n\n**Binary:** `0b00000001010010110100100000100000`\n\n"); - let monaco_line_info = parser(".text".to_string()).0.monaco_line_info; + let monaco_line_info = parser(".text".to_string(), AvailableDatapaths::MIPS) + .0 + .monaco_line_info; assert_eq!(monaco_line_info[0].mouse_hover_string, "\n\n"); } @@ -977,6 +2540,7 @@ fn mouse_hover_holds_information_info_for_various_instruction_types() { let program_info = parser( ".text\nori $t1, $t2, 100\nlabel: subi $t1, $t2, 100\nadd $t1, $t2, $t3\nsyscall\n" .to_string(), + AvailableDatapaths::MIPS, ) .0; @@ -989,11 +2553,15 @@ fn mouse_hover_holds_information_info_for_various_instruction_types() { #[test] fn instructions_directives_and_registers_work_regardless_of_capitalization() { - let result = - parser(".TexT\nOR $t1, $T2, $t3\nor $t1, $t2, $t3\n.DATA\nabel: .WOrD 100".to_string()); + let result = parser( + ".TexT\nOR $t1, $T2, $t3\nor $t1, $t2, $t3\n.DATA\nabel: .WOrD 100".to_string(), + AvailableDatapaths::MIPS, + ); - let correct = - parser(".TexT\nOR $t1, $T2, $t3\nor $t1, $t2, $t3\n.DATA\nabel: .WOrD 100".to_lowercase()); + let correct = parser( + ".TexT\nOR $t1, $T2, $t3\nor $t1, $t2, $t3\n.DATA\nabel: .WOrD 100".to_lowercase(), + AvailableDatapaths::MIPS, + ); assert_eq!(result.1, correct.1); assert_eq!( result.0.console_out_post_assembly, @@ -1013,21 +2581,23 @@ fn instructions_directives_and_registers_work_regardless_of_capitalization() { #[test] fn parser_assembler_works_with_empty_strings() { - let _ = parser("".to_string()); - let _ = parser("\n".to_string()); - let _ = parser("\n\n".to_string()); + let _ = parser("".to_string(), AvailableDatapaths::MIPS); + let _ = parser("\n".to_string(), AvailableDatapaths::MIPS); + let _ = parser("\n\n".to_string(), AvailableDatapaths::MIPS); } #[test] fn create_binary_vec_works_with_all_mod_4_options() { let result = parser( "ori $s0, $zero, 12345\nori $s0, $zero, 12345\n.data\nlab: .ascii \"h\"".to_string(), + AvailableDatapaths::MIPS, ) .1; assert_eq!(result, vec![873476153, 873476153, 12, 1744830464]); let result = parser( "ori $s0, $zero, 12345\nori $s0, $zero, 12345\n.data\nlab: .ascii \"ha\"".to_string(), + AvailableDatapaths::MIPS, ) .1; assert_eq!( @@ -1037,6 +2607,7 @@ fn create_binary_vec_works_with_all_mod_4_options() { let result = parser( "ori $s0, $zero, 12345\nori $s0, $zero, 12345\n.data\nlab: .ascii \"han\"".to_string(), + AvailableDatapaths::MIPS, ) .1; assert_eq!( @@ -1046,6 +2617,7 @@ fn create_binary_vec_works_with_all_mod_4_options() { let result = parser( "ori $s0, $zero, 12345\nori $s0, $zero, 12345\n.data\nlab: .ascii \"hank\"".to_string(), + AvailableDatapaths::MIPS, ) .1; assert_eq!( @@ -1055,17 +2627,21 @@ fn create_binary_vec_works_with_all_mod_4_options() { } #[test] -fn no_unsupported_instructions_are_recognized_by_parser() { - for instruction in UNSUPPORTED_INSTRUCTIONS { - let result = parser(instruction.to_string()).0.monaco_line_info; +fn no_unsupported_mips_instructions_are_recognized_by_parser() { + for instruction in UNSUPPORTED_INSTRUCTIONS_MIPS { + let result = parser(instruction.to_string(), AvailableDatapaths::MIPS) + .0 + .monaco_line_info; assert_eq!(result[0].errors[0].error_name, UnsupportedInstruction); } } #[test] -fn supported_instructions_are_recognized_by_parser() { - for instruction in SUPPORTED_INSTRUCTIONS { - let result = parser(instruction.to_string()).0.monaco_line_info; +fn supported_mips_instructions_are_recognized_by_parser() { + for instruction in SUPPORTED_INSTRUCTIONS_MIPS { + let result = parser(instruction.to_string(), AvailableDatapaths::MIPS) + .0 + .monaco_line_info; for error in &result[0].errors { assert_ne!(error.error_name, UnsupportedInstruction); assert_ne!(error.error_name, UnrecognizedInstruction); @@ -1075,25 +2651,33 @@ fn supported_instructions_are_recognized_by_parser() { #[test] fn main_and_start_labelled_instructions_change_program_info_pc_starting_point() { - let result = parser("addi $t1, $t2, 100\nsw $t1, 400($zero)".to_string()) - .0 - .pc_starting_point; + let result = parser( + "addi $t1, $t2, 100\nsw $t1, 400($zero)".to_string(), + AvailableDatapaths::MIPS, + ) + .0 + .pc_starting_point; assert_eq!(result, 0); - let result = - parser("addi $t1, $t2, 100\nsw $t1, 400($zero)\nmain: lw $t2, 320($zero)".to_string()) - .0 - .pc_starting_point; + let result = parser( + "addi $t1, $t2, 100\nsw $t1, 400($zero)\nmain: lw $t2, 320($zero)".to_string(), + AvailableDatapaths::MIPS, + ) + .0 + .pc_starting_point; assert_eq!(result, 8); - let result = - parser("addi $t1, $t2, 100\nstart: sw $t1, 400($zero)\nlw $t2, 320($zero)".to_string()) - .0 - .pc_starting_point; + let result = parser( + "addi $t1, $t2, 100\nstart: sw $t1, 400($zero)\nlw $t2, 320($zero)".to_string(), + AvailableDatapaths::MIPS, + ) + .0 + .pc_starting_point; assert_eq!(result, 4); let result = parser( "addi $t1, $t2, 100\nstart: sw $t1, 400($zero)\nmain: lw $t2, 320($zero)".to_string(), + AvailableDatapaths::MIPS, ) .0 .pc_starting_point; diff --git a/src/tests/parser/parsing.rs b/src/tests/parser/parsing.rs index 1a6829c49..c0ca9ae18 100644 --- a/src/tests/parser/parsing.rs +++ b/src/tests/parser/parsing.rs @@ -1,3 +1,4 @@ +use crate::emulation_core::architectures::AvailableDatapaths; use crate::parser::assembling::assemble_data_binary; use crate::parser::parser_assembler_main::parser; use crate::parser::parser_structs_and_enums::ErrorType::{ @@ -325,13 +326,18 @@ fn separate_data_and_text_works_basic_version() { fn separate_data_and_text_can_handle_empty_lines() { //this test realistically is only important to check that it does not panic but we might as well go a step further and //check that the result generated with empty lines is identical to the result without empty lines save for line number - let mut result_1 = - parser(".text\nori $s0, $zero, 0x1234\n\n.data\nlabel: .word 0xface".to_string()) - .0 - .monaco_line_info; - let result_2 = parser(".text\nori $s0, $zero, 0x1234\n.data\nlabel: .word 0xface".to_string()) - .0 - .monaco_line_info; + let mut result_1 = parser( + ".text\nori $s0, $zero, 0x1234\n\n.data\nlabel: .word 0xface".to_string(), + AvailableDatapaths::MIPS, + ) + .0 + .monaco_line_info; + let result_2 = parser( + ".text\nori $s0, $zero, 0x1234\n.data\nlabel: .word 0xface".to_string(), + AvailableDatapaths::MIPS, + ) + .0 + .monaco_line_info; result_1[2].line_number = 1; result_1[3].line_number = 2; result_1[4].line_number = 3; @@ -344,9 +350,12 @@ fn separate_data_and_text_can_handle_empty_lines() { #[test] fn separate_data_and_text_generates_error_on_missing_commas_text() { - let result = parser("add, $t1, $t2, $t3,\nlw $t1 400($t2)".to_string()) - .0 - .monaco_line_info; + let result = parser( + "add, $t1, $t2, $t3,\nlw $t1 400($t2)".to_string(), + AvailableDatapaths::MIPS, + ) + .0 + .monaco_line_info; let error_0_line_0 = Error { error_name: UnnecessaryComma, @@ -736,9 +745,12 @@ fn build_instruction_list_allows_double_label_on_instructions() { #[test] fn build_instruction_list_generates_error_on_label_on_last_line() { - let result = parser("lw $t1, 400($zero)\nadd $t1, $t2, $t3\nlabel:\n".to_string()) - .0 - .monaco_line_info; + let result = parser( + "lw $t1, 400($zero)\nadd $t1, $t2, $t3\nlabel:\n".to_string(), + AvailableDatapaths::MIPS, + ) + .0 + .monaco_line_info; assert_eq!(result[2].errors[0].error_name, LabelAssignmentError); } @@ -832,9 +844,12 @@ fn create_label_map_pushes_errors_instead_of_inserting_duplicate_label_name() { } #[test] fn suggest_error_corrections_works_with_various_gp_registers() { - let result = parser("add $t1, $t2, @t3\nori not, ro, 100".to_string()) - .0 - .instructions; + let result = parser( + "add $t1, $t2, @t3\nori not, ro, 100".to_string(), + AvailableDatapaths::MIPS, + ) + .0 + .instructions; assert_eq!( result[0].errors[0].message, @@ -852,9 +867,12 @@ fn suggest_error_corrections_works_with_various_gp_registers() { #[test] fn suggest_error_corrections_works_with_various_fp_registers() { - let result = parser("add.s $f1, $f2, f3\nadd.d fake, $052, 1qp".to_string()) - .0 - .instructions; + let result = parser( + "add.s $f1, $f2, f3\nadd.d fake, $052, 1qp".to_string(), + AvailableDatapaths::MIPS, + ) + .0 + .instructions; assert_eq!( result[0].errors[0].message, @@ -876,10 +894,12 @@ fn suggest_error_corrections_works_with_various_fp_registers() { #[test] fn suggest_error_corrections_works_with_labels() { - let result = - parser("j stable\nlabel: add $t1, $t2, $t3\ntable: sub $t1, $t2, $t3\nj lapel".to_string()) - .0 - .instructions; + let result = parser( + "j stable\nlabel: add $t1, $t2, $t3\ntable: sub $t1, $t2, $t3\nj lapel".to_string(), + AvailableDatapaths::MIPS, + ) + .0 + .instructions; assert_eq!( result[0].errors[0].message, @@ -893,9 +913,12 @@ fn suggest_error_corrections_works_with_labels() { #[test] fn suggest_error_corrections_works_with_labels_when_no_labels_specified() { - let result = parser("add $t1, $t2, $t3\nj stable\nlw $t1, 100($zero)\n".to_string()) - .0 - .instructions; + let result = parser( + "add $t1, $t2, $t3\nj stable\nlw $t1, 100($zero)\n".to_string(), + AvailableDatapaths::MIPS, + ) + .0 + .instructions; assert_eq!( result[1].errors[0].message, "There is no recognized labelled memory.\n" @@ -904,9 +927,12 @@ fn suggest_error_corrections_works_with_labels_when_no_labels_specified() { #[test] fn suggest_error_corrections_works_with_instructions() { - let result = parser("sun $t1, $t2, $t3\nqq $t1, 100($zero)\n.c.eqd $f1, $f1, $f3".to_string()) - .0 - .instructions; + let result = parser( + "sun $t1, $t2, $t3\nqq $t1, 100($zero)\n.c.eqd $f1, $f1, $f3".to_string(), + AvailableDatapaths::MIPS, + ) + .0 + .instructions; assert_eq!( result[0].errors[0].message, @@ -927,6 +953,7 @@ fn suggest_error_corrections_works_with_data_types() { let result = parser( ".data\nlabel: word 100\ntable: .bite 'c','1'\nlapel: gobbledygook \"this is a string\"" .to_string(), + AvailableDatapaths::MIPS, ) .0 .data; @@ -947,10 +974,12 @@ fn suggest_error_corrections_works_with_data_types() { #[test] fn suggest_error_suggestions_associates_error_with_monaco_line_info() { - let lines = - parser("ori $t1, 100, $t2\nlw $f1, 400($zero)\n.data\nword .wod \"a\"\n".to_string()) - .0 - .monaco_line_info; + let lines = parser( + "ori $t1, 100, $t2\nlw $f1, 400($zero)\n.data\nword .wod \"a\"\n".to_string(), + AvailableDatapaths::MIPS, + ) + .0 + .monaco_line_info; let actual = Error { error_name: ErrorType::UnrecognizedGPRegister, @@ -995,7 +1024,9 @@ fn suggest_error_suggestions_associates_error_with_monaco_line_info() { #[test] fn operators_with_commas_cause_error() { - let result = parser("ori, $t1, $t2, 100".to_string()).0.monaco_line_info; + let result = parser("ori, $t1, $t2, 100".to_string(), AvailableDatapaths::MIPS) + .0 + .monaco_line_info; for line in result { for error in line.errors { diff --git a/src/tests/parser/pseudo_instruction_parsing.rs b/src/tests/parser/pseudo_instruction_parsing.rs index 99d90264f..1fcfb0b18 100644 --- a/src/tests/parser/pseudo_instruction_parsing.rs +++ b/src/tests/parser/pseudo_instruction_parsing.rs @@ -1,3 +1,4 @@ +use crate::emulation_core::architectures::AvailableDatapaths; use crate::parser::assembling::assemble_data_binary; use crate::parser::parser_assembler_main::parser; use crate::parser::parser_structs_and_enums::TokenType::Operator; @@ -10,9 +11,12 @@ use std::collections::HashMap; #[test] fn expand_pseudo_instructions_and_assign_instruction_number_adds_syscall_if_it_is_missing() { - let result = parser("addi $t1, $t2, 100\nsw $t1, label".to_string()) - .0 - .updated_monaco_string; + let result = parser( + "addi $t1, $t2, 100\nsw $t1, label".to_string(), + AvailableDatapaths::MIPS, + ) + .0 + .updated_monaco_string; let correct_result = "addi $t1, $t2, 100\nsw $t1, label\nsyscall\n".to_string(); assert_eq!(result, correct_result); @@ -21,9 +25,12 @@ fn expand_pseudo_instructions_and_assign_instruction_number_adds_syscall_if_it_i #[test] fn expand_pseudo_instructions_and_assign_instruction_number_adds_syscall_at_beginning_if_no_instruction( ) { - let result = parser(".data\nword .word 100\nother .byte 'a','a'\n".to_string()) - .0 - .updated_monaco_string; + let result = parser( + ".data\nword .word 100\nother .byte 'a','a'\n".to_string(), + AvailableDatapaths::MIPS, + ) + .0 + .updated_monaco_string; let correct_result = ".text\nsyscall\n.data\nword .word 100\nother .byte 'a','a'\n".to_string(); @@ -33,7 +40,7 @@ fn expand_pseudo_instructions_and_assign_instruction_number_adds_syscall_at_begi #[test] fn expand_pseudo_instructions_and_assign_instruction_number_adds_syscall_after_first_instance_of_text( ) { - let result = parser(".data\nword .word 100\n.text\n.data\nother .byte 'a','a'\n.text\n.data\nfinal: .space 10\n".to_string()).0.updated_monaco_string; + let result = parser(".data\nword .word 100\n.text\n.data\nother .byte 'a','a'\n.text\n.data\nfinal: .space 10\n".to_string(), AvailableDatapaths::MIPS).0.updated_monaco_string; let correct_result = ".data\nword .word 100\n.text\nsyscall\n.data\nother .byte 'a','a'\n.text\n.data\nfinal: .space 10\n".to_string(); @@ -43,9 +50,12 @@ fn expand_pseudo_instructions_and_assign_instruction_number_adds_syscall_after_f #[test] fn expand_pseudo_instructions_and_assign_instruction_number_does_not_add_syscall_if_it_is_present() { - let result = parser("addi $t1, $t2, 100\nsw $t1, label\nsyscall\n".to_string()) - .0 - .updated_monaco_string; + let result = parser( + "addi $t1, $t2, 100\nsw $t1, label\nsyscall\n".to_string(), + AvailableDatapaths::MIPS, + ) + .0 + .updated_monaco_string; let correct_result: String = "addi $t1, $t2, 100\nsw $t1, label\nsyscall\n".to_string(); @@ -55,9 +65,12 @@ fn expand_pseudo_instructions_and_assign_instruction_number_does_not_add_syscall #[test] fn expand_pseudo_instructions_and_assign_instruction_number_adds_syscall_at_proper_spot_with_data_after( ) { - let result = parser("addi $t1, $t2, 100\nsw $t1, label\n.data\n word: .word 100\n".to_string()) - .0 - .updated_monaco_string; + let result = parser( + "addi $t1, $t2, 100\nsw $t1, label\n.data\n word: .word 100\n".to_string(), + AvailableDatapaths::MIPS, + ) + .0 + .updated_monaco_string; let correct_result = "addi $t1, $t2, 100\nsw $t1, label\nsyscall\n.data\n word: .word 100\n".to_string(); @@ -67,9 +80,12 @@ fn expand_pseudo_instructions_and_assign_instruction_number_adds_syscall_at_prop #[test] fn add_syscall_to_program_info() { - let result = parser(".text\naddi $t1, $t2, $t3\nsyscall\n.data\n".to_string()) - .0 - .instructions; + let result = parser( + ".text\naddi $t1, $t2, $t3\nsyscall\n.data\n".to_string(), + AvailableDatapaths::MIPS, + ) + .0 + .instructions; for instr in result { println!("{}", instr.operator.token_name); @@ -1961,7 +1977,7 @@ fn complete_lw_sw_pseudo_instructions_doesnt_break_with_empty_instruction_list() fn expanded_pseudo_instructions_are_added_into_updated_monaco_string() { let result = parser( ".text\nli $t1, 100\nseq $t1, $t2, $t3\nsne $t1, $t2, $t3\nsle $t1, $t2, $t3\nsleu $t1, $t2, $t3\nsgt $t1, $t2, $t3\nsgtu $t1, $t2, $t3\nsge $t1, $t2, $t3\nsgeu $t1, $t2, $t3\nsubi $t1, $t2, 100\ndsubi $t1, $t2, 100\ndsubiu $t1, $t2, 100\nmuli $t1, $t2, 100\ndmuli $t1, $t2, 100\ndmuliu $t1, $t2, 100\ndivi $t1, 100\nddivi $t1, 100\nddiviu $t1, 100\nlw $t1, memory\n.data\nmemory: .word 200" - .to_string(), + .to_string(), AvailableDatapaths::MIPS ) .0.updated_monaco_string; @@ -1970,9 +1986,12 @@ fn expanded_pseudo_instructions_are_added_into_updated_monaco_string() { #[test] fn pseudo_instructions_with_labels_put_label_on_the_first_expanded_instruction() { - let result = parser("label: ddiviu $t2, $t2, 100\n".to_string()) - .0 - .instructions; + let result = parser( + "label: ddiviu $t2, $t2, 100\n".to_string(), + AvailableDatapaths::MIPS, + ) + .0 + .instructions; assert!(!result[0].labels.is_empty()); assert!(result[1].labels.is_empty()); } diff --git a/src/ui.rs b/src/ui.rs index 095d4b1c5..15b4fdff2 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -1,5 +1,9 @@ //! User interface using Yew, organized into components. +pub mod assembled_view; pub mod console; +pub mod footer; +pub mod hex_editor; pub mod regview; +pub mod swim_editor; pub mod visual_datapath; diff --git a/src/ui/assembled_view/component.rs b/src/ui/assembled_view/component.rs new file mode 100644 index 000000000..fc3c00557 --- /dev/null +++ b/src/ui/assembled_view/component.rs @@ -0,0 +1,479 @@ +use std::cell::RefCell; +use std::collections::HashMap; +use std::collections::HashSet; +use std::ops::Deref; +use std::rc::Rc; + +use crate::agent::datapath_communicator::DatapathCommunicator; +use crate::emulation_core::mips::memory::{Memory, MemoryIter}; +use crate::emulation_core::stack::Stack; +// use monaco::api::TextModel; +use crate::parser::parser_structs_and_enums::ProgramInfo; +use crate::ui::swim_editor::tab::TabState; +use wasm_bindgen::JsCast; +use web_sys::{HtmlElement, HtmlInputElement}; +use yew::prelude::*; +use yew::{Html, Properties}; + +// ** Segment Viewer Components ** // +// Displays the text, data, stack segments, and stack frame view +// IDEA: Create Segment Viewer component for extendability to any segment + +#[derive(PartialEq, Properties)] +pub struct TextSegmentProps { + pub program_info: ProgramInfo, + pub lines_content: Rc>>, + pub memory_curr_instr: UseStateHandle, + pub editor_curr_line: UseStateHandle, + pub pc: u64, + pub editor_active_tab: UseStateHandle, + pub console_active_tab: UseStateHandle, + pub communicator: &'static DatapathCommunicator, + pub breakpoints: UseStateHandle>, +} + +#[derive(PartialEq, Properties)] +pub struct DataSegmentProps { + pub program_info: ProgramInfo, + pub binary: Vec, + pub lines_content: Rc>>, + pub memory_curr_instr: UseStateHandle, + pub editor_curr_line: UseStateHandle, + pub editor_active_tab: UseStateHandle, + pub console_active_tab: UseStateHandle, + pub pc_limit: usize, +} + +#[function_component] +pub fn TextSegment(props: &TextSegmentProps) -> Html { + let program_info = &props.program_info; + let lines_content = props.lines_content.borrow_mut().clone(); + let memory_curr_instr = &props.memory_curr_instr; + let editor_curr_line = &props.editor_curr_line; + let editor_active_tab = &props.editor_active_tab; + let console_active_tab = &props.console_active_tab; + let executed_ref = use_node_ref(); + let communicator = props.communicator; + let current_pc = use_state(|| props.pc); + + // Scroll to the executed row on execution (when props.pc changes) + if *current_pc != props.pc { + let executed_row = executed_ref.cast::(); + if let Some(executed_row) = executed_row { + let mut options = web_sys::ScrollIntoViewOptions::new(); + options.block(web_sys::ScrollLogicalPosition::Center); + executed_row.scroll_into_view_with_scroll_into_view_options(&options); + } + current_pc.set(props.pc); + } + + // Set or remove breakpoint on checkbox click + let on_check = { + let breakpoints = props.breakpoints.clone(); + + Callback::from(move |args: (MouseEvent, i64)| { + let (e, address) = args; + let target = e.target(); + let input = target.unwrap().unchecked_into::(); + + if input.checked() { + communicator.set_breakpoint(address as u64); + breakpoints.set({ + let mut new_breakpoints = breakpoints.deref().clone(); + new_breakpoints.insert(address as u64); + new_breakpoints + }); + } else { + communicator.remove_breakpoint(address as u64); + breakpoints.set({ + let mut new_breakpoints = breakpoints.deref().clone(); + new_breakpoints.remove(&(address as u64)); + new_breakpoints + }); + } + }) + }; + + // Go to the memory address in hex editor + let on_address_click = { + let memory_curr_instr = memory_curr_instr.clone(); + let console_active_tab = console_active_tab.clone(); + use_callback( + move |args: (MouseEvent, usize), memory_curr_instr| { + let (_e, address) = args; + memory_curr_instr.set(address as u64); + console_active_tab.set(TabState::HexEditor); + }, + memory_curr_instr, + ) + }; + + // Go to the line in code editor + let on_assembled_click = { + let editor_curr_line = editor_curr_line.clone(); + let editor_active_tab = editor_active_tab.clone(); + use_callback( + move |args: (MouseEvent, usize), _| { + let (_e, line_number) = args; + editor_curr_line.set(line_number as f64 + 1.0); + editor_active_tab.set(TabState::Editor); + }, + (), + ) + }; + + let mut address = -4; + html! { + + // | breakpoint checkbox | address | instruction in binary | instruction in hex | updated string | source string + + + + + + + + + { + program_info.instructions.iter().enumerate().map(|(index, instruction)| { + let recreated_string = instruction.recreate_string(); + let on_check = Callback::clone(&on_check); + let on_address_click = Callback::clone(&on_address_click); + let on_assembled_click = Callback::clone(&on_assembled_click); + let executed_ref = executed_ref.clone(); + address += 4; + + let line_number = instruction.line_number; + + let mut conditional_class = ""; + if **editor_curr_line != 0.0 && props.pc as i64 == address + 4 { + // we add 4 to the address because we're highlighting the last executed instruction, and the pc is the address of the next instruction + conditional_class = "bg-primary-700 shadow-executing"; + html!{ + + + + + + + + + } + } + else { + html!{ + + + + + + + + + } + } + }).collect::() + } +
{"Bkpt"}{"Address"}{"Binary"}{"Hex"}{"Assembled"}{"Source"}
+ +
+
+ {format!("0x{:08x}", address as u64)} + + {format!("0b{:032b}", instruction.binary)} + + {format!("0x{:08x}", instruction.binary)} + + {recreated_string} + + {format!("{}: {:?}", line_number + 1, lines_content.get(line_number).unwrap_or(&String::from("")))} + // Adding 1 to line number because it is 0-indexed +
+ +
+
+ {format!("0x{:08x}", address as u64)} + + {format!("0b{:032b}", instruction.binary)} + + {format!("0x{:08x}", instruction.binary)} + + {recreated_string} + + {format!("{}: {:?}", line_number + 1, lines_content.get(line_number).unwrap_or(&String::from("")))} + // Adding 1 to line number because it is 0-indexed +
+ } +} + +#[function_component] +pub fn DataSegment(props: &DataSegmentProps) -> Html { + let program_info = &props.program_info; + let binary = &props.binary; + let lines_content = props.lines_content.borrow_mut().clone(); + let memory_curr_instr = &props.memory_curr_instr; + let editor_curr_line = &props.editor_curr_line; + let editor_active_tab = &props.editor_active_tab; + let console_active_tab = &props.console_active_tab; + + // Go to the memory address in hex editor + let on_address_click = { + let memory_curr_instr = memory_curr_instr.clone(); + let console_active_tab = console_active_tab.clone(); + use_callback( + move |args: (MouseEvent, usize), memory_curr_instr| { + let (_e, address) = args; + memory_curr_instr.set(address as u64); + console_active_tab.set(TabState::HexEditor); + }, + memory_curr_instr, + ) + }; + + // Go to the line in code editor + let on_assembled_click = { + let editor_curr_line = editor_curr_line.clone(); + let editor_active_tab = editor_active_tab.clone(); + use_callback( + move |args: (MouseEvent, usize), _| { + let (_e, line_number) = args; + editor_curr_line.set(line_number as f64); + editor_active_tab.set(TabState::Editor); + }, + (), + ) + }; + + html! { + + // | address | data in hex | source string + + + + + + + { + if !program_info.instructions.is_empty() && !binary.is_empty() { + let mut address = program_info.instructions.len() * 4 - 4; + let mut data_binary_index = program_info.data_starting_point - 1; + program_info.data.iter().enumerate().map(|(index, data)| { + let recreated_string = data.recreate_string(); + let on_address_click = Callback::clone(&on_address_click); + let on_assembled_click = Callback::clone(&on_assembled_click); + address += 4; + data_binary_index += 1; + html!{ + + + + + + + + } + }).collect::() + } + else { + html! {<>} + } + } +
{"Address"}{"Hex"}{"Assembled"}{"Source"}
+ {format!("0x{:08x}", address as u64)} + + {format!("0x{:08x}", binary[data_binary_index])} + + {recreated_string} + + {format!("{}: {:?}", data.line_number + 1, lines_content.get(data.line_number).unwrap_or(&String::from("")))} +
+ } +} + +#[derive(PartialEq, Properties)] +pub struct StackSegmentProps { + pub memory: Memory, + pub sp: u64, + pub memory_curr_instr: UseStateHandle, + pub console_active_tab: UseStateHandle, +} + +#[function_component] +pub fn StackSegment(props: &StackSegmentProps) -> Html { + let memory = &props.memory; + let sp = props.sp; + let console_active_tab = &props.console_active_tab; + let memory_curr_instr = &props.memory_curr_instr; + + // Go to the memory address in hex editor + let on_address_click = { + let memory_curr_instr = memory_curr_instr.clone(); + let console_active_tab = console_active_tab.clone(); + use_callback( + move |args: (MouseEvent, usize), memory_curr_instr| { + let (_e, address) = args; + memory_curr_instr.set(address as u64); + console_active_tab.set(TabState::HexEditor); + }, + memory_curr_instr, + ) + }; + + html! { + + // | address | data in hex + + + + + { + if !memory.memory.is_empty() && sp != 0 { + let memory_iter = MemoryIter::new(memory, sp as usize, memory.memory.len()); + memory_iter.map(|(address, words)| { + let on_address_click = Callback::clone(&on_address_click); + html! { + + + + + } + }).collect::() + } + else { + html! {<>} + } + } +
{"Address"}{"Hex"}
+ {format!("0x{:08x}", address as u64)} + + { + words.iter().fold(String::new(), |acc, word| { + format!("{}0x{:08x} ", acc, word) + }) + } +
+ } +} + +#[derive(PartialEq, Properties)] +pub struct StackFrameProps { + pub stack: Stack, + pub memory_curr_instr: UseStateHandle, + pub console_active_tab: UseStateHandle, + pub program_info: ProgramInfo, + pub labels: HashMap, + pub editor_curr_line: UseStateHandle, + pub editor_active_tab: UseStateHandle, +} + +#[function_component] +pub fn StackFrameView(props: &StackFrameProps) -> Html { + let console_active_tab = &props.console_active_tab; + let memory_curr_instr = &props.memory_curr_instr; + let stack = &props.stack; + let program_info = &props.program_info; + let labels = &props.labels; + let editor_curr_line = &props.editor_curr_line; + let editor_active_tab = &props.editor_active_tab; + + // Open the memory address in hex editor + let on_address_click = { + let memory_curr_instr = memory_curr_instr.clone(); + let console_active_tab = console_active_tab.clone(); + use_callback( + move |args: (MouseEvent, usize), memory_curr_instr| { + let (_e, address) = args; + memory_curr_instr.set(address as u64); + console_active_tab.set(TabState::HexEditor); + }, + memory_curr_instr, + ) + }; + + // Open the line in code editor + let on_assembled_click = { + let editor_curr_line = editor_curr_line.clone(); + let editor_active_tab = editor_active_tab.clone(); + use_callback( + move |args: (MouseEvent, usize), _| { + let (_e, line_number) = args; + editor_curr_line.set(line_number as f64); + editor_active_tab.set(TabState::Editor); + }, + (), + ) + }; + + html! { + + // | label | frame pointer | call mem address | call assembled line | return address | return to line + + + + + + + + + { + if !stack.is_empty() && !program_info.instructions.is_empty() { + let stack = stack.stack.clone(); + stack.into_iter().rev().enumerate().map(|(_address, frame)| { + // Get the call and return lines + let call_line_index = frame.call_address / 4; + let call_recreated_string = program_info.instructions[call_line_index as usize].recreate_string(); + let call_line_number = program_info.instructions[call_line_index as usize].line_number; + + let return_line_index = frame.return_address / 4; + let return_recreated_string = program_info.instructions[return_line_index as usize].recreate_string(); + let return_line_number = program_info.instructions[return_line_index as usize].line_number + 1; + + // Create the callbacks for cross reference links + let on_call_address_click = Callback::clone(&on_address_click); + let on_return_address_click = Callback::clone(&on_address_click); + let on_frame_pointer_click = Callback::clone(&on_address_click); + let on_return_line_click = Callback::clone(&on_assembled_click); + let on_call_line_click = Callback::clone(&on_assembled_click); + + // Get the label for the frame + let default_label = String::from(""); + let label = labels.iter().find_map(|(label, address)| { + if *address == frame.jump_address as usize { + Some(label) + } + else { + None + } + }).unwrap_or(&default_label); + + html! { + + + + + + + + + } + }).collect::() + } + else { + html! {<>} + } + } +
{"Label"}{"Frame Pointer"}{"Call Address"}{"Call Line"}{"Return Address"}{"Return Line"}
+ {label} + + {format!("0x{:08x}", frame.frame_pointer)} + + {format!("0x{:08x}", frame.call_address as u64)} + + {call_recreated_string} + + {format!("0x{:08x}", frame.return_address)} + + {return_recreated_string} +
+ } +} diff --git a/src/ui/assembled_view/mod.rs b/src/ui/assembled_view/mod.rs new file mode 100644 index 000000000..9cea807e4 --- /dev/null +++ b/src/ui/assembled_view/mod.rs @@ -0,0 +1 @@ +pub mod component; diff --git a/src/ui/console/component.rs b/src/ui/console/component.rs index c19c52a8b..8f6f09a59 100644 --- a/src/ui/console/component.rs +++ b/src/ui/console/component.rs @@ -1,129 +1,84 @@ -//use crate::parser::parser_structs_and_enums::instruction_tokenization::ProgramInfo; -//use monaco::api::TextModel; +use crate::agent::datapath_communicator::DatapathCommunicator; use wasm_bindgen::JsCast; -use web_sys::HtmlElement; +use web_sys::{HtmlInputElement, InputEvent, KeyboardEvent}; use yew::prelude::*; -use yew_hooks::prelude::*; -use crate::emulation_core::mips::datapath::MipsDatapath; -use crate::ui::visual_datapath::{DatapathSize, VisualDatapath}; +// ** Console Component ** // +// The console component is a container for the console messages and input #[derive(PartialEq, Properties)] pub struct Consoleprops { - pub datapath: MipsDatapath, + pub communicator: &'static DatapathCommunicator, + pub messages: Vec, pub parsermsg: String, - pub memorymsg: String, -} - -#[derive(Default, PartialEq)] -enum TabState { - #[default] - Console, - Datapath, - Memory, + pub show_input: UseStateHandle, } #[function_component(Console)] pub fn console(props: &Consoleprops) -> Html { - let active_tab = use_state_eq(TabState::default); - let zoom_datapath = use_bool_toggle(false); - let switch_datapath = use_bool_toggle(false); - let change_tab = { - let active_tab = active_tab.clone(); - Callback::from(move |event: MouseEvent| { - let target = event.target().unwrap().dyn_into::().unwrap(); - let tab_name = target - .get_attribute("label") - .unwrap_or(String::from("console")); - - let new_tab = match tab_name.as_str() { - "console" => TabState::Console, - "datapath" => TabState::Datapath, - "memory" => TabState::Memory, - _ => TabState::default(), - }; - - active_tab.set(new_tab); - }) - }; - - let toggle_zoom = { - let zoom_datapath = zoom_datapath.clone(); - - Callback::from(move |_| { - zoom_datapath.toggle(); - }) - }; - - let datapath_size = match *zoom_datapath { - true => DatapathSize::Big, - false => DatapathSize::Small, - }; - - let switch_datapath_type = { - let switch_datapath = switch_datapath.clone(); - - Callback::from(move |_| { - switch_datapath.toggle(); - }) - }; - - let svg_path = match *switch_datapath { - true => "static/datapath_full.svg", - false => "static/datapath_simple.svg", + let show_input = props.show_input.clone(); + let input_value = use_state_eq(String::new); + + let on_keyup = { + let input_value = input_value.clone(); + let communicator = props.communicator; + + use_callback( + move |event: KeyboardEvent, input_value| { + let key_code = event.key_code(); + let input_value = input_value.clone(); + // If Enter was pressed parse and send input to emulator core + if key_code == 13 { + communicator.send_input((*input_value).clone()); + input_value.set("".to_string()); + } + }, + input_value, + ) }; - let switch_datapath_button_label = match *switch_datapath { - true => "Switch to Simple Datapath", - false => "Switch to Full Datapath", + let on_input = { + let input_value = input_value.clone(); + use_callback( + move |event: InputEvent, input_value| { + let target = event.target(); + let input = target.unwrap().unchecked_into::(); + + input_value.set(input.value()); + }, + input_value, + ) }; html! { - <> - // Console buttons - if *active_tab == TabState::Console { -
-                    { props.parsermsg.clone() }
-                
- } else if *active_tab == TabState::Datapath { -
- -
- } else { -
-
-                        {props.datapath.memory.generate_formatted_hex() }
-                    
-
+
+ { + (*props.parsermsg) + .split('\n') + .map(|line| { + html! {
{line}
} + }) + .collect::() } -
-
- if *active_tab == TabState::Console { - - } else { - - } - - if *active_tab == TabState::Memory { - - } else { - - } - - if *active_tab == TabState::Datapath { - - } else { - - } -
- - if *active_tab == TabState::Datapath { -
- - -
+
+ { + props + .messages + .iter() + .flat_map(|msg| { + msg.split('\n').map(|line| { + html! {
{line}
} + }) + }) + .collect::() }
- + if *show_input { +
+
{">\u{00a0}"}
// Prompt followed by a non-breaking space + +
+ } +
} } diff --git a/src/ui/console/helper.rs b/src/ui/console/helper.rs deleted file mode 100644 index 8b1378917..000000000 --- a/src/ui/console/helper.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/ui/console/mod.rs b/src/ui/console/mod.rs index bff6cf38a..9cea807e4 100644 --- a/src/ui/console/mod.rs +++ b/src/ui/console/mod.rs @@ -1,2 +1 @@ pub mod component; -pub mod helper; diff --git a/src/ui/footer/component.rs b/src/ui/footer/component.rs new file mode 100644 index 000000000..eaf24a6ac --- /dev/null +++ b/src/ui/footer/component.rs @@ -0,0 +1,160 @@ +use crate::agent::datapath_communicator::DatapathCommunicator; +use crate::agent::datapath_reducer::DatapathReducer; +use crate::emulation_core::architectures::AvailableDatapaths::{MIPS, RISCV}; +use crate::emulation_core::mips::memory::Memory; +use crate::ui::console::component::Console; +use crate::ui::hex_editor::component::HexEditor; +use crate::ui::swim_editor::tab::TabState; +use crate::ui::visual_datapath::VisualDatapath; +use monaco::api::TextModel; +use std::str::FromStr; +use wasm_bindgen::JsCast; +use web_sys::HtmlElement; +use yew::prelude::*; +use yew_hooks::prelude::*; + +// ** Footer Component ** // +// The footer component is a container for the console, datapath, and hex editor components +// It holds the tabs for each of these components and allows the user to switch between them + +#[derive(PartialEq, Properties)] +pub struct Footerprops { + pub communicator: &'static DatapathCommunicator, + pub datapath_state: UseReducerHandle, + pub parsermsg: String, + pub show_input: UseStateHandle, + pub memory_text_model: UseStateHandle, + pub memory_curr_instr: UseStateHandle, + pub active_tab: UseStateHandle, + pub on_memory_clicked: Callback, + pub memory: Memory, + pub pc: u64, +} + +#[function_component(Footer)] +pub fn footer(props: &Footerprops) -> Html { + let active_tab = &props.active_tab; + let switch_datapath = use_bool_toggle(false); + let change_tab = { + let active_tab = active_tab.clone(); + Callback::from(move |event: MouseEvent| { + let target = event.target().unwrap().dyn_into::().unwrap(); + let tab_name = target + .get_attribute("label") + .unwrap_or(String::from("console")); + + let new_tab = TabState::from_str(&tab_name).unwrap(); + active_tab.set(new_tab); + }) + }; + + let switch_datapath_type = { + let switch_datapath = switch_datapath.clone(); + + Callback::from(move |_| { + switch_datapath.toggle(); + }) + }; + let svg_path = match props.datapath_state.current_architecture { + MIPS => match *switch_datapath { + true => "static/datapath_full.svg", + false => "static/datapath_simple.svg", + }, + RISCV => "static/datapath_riscv.svg", + }; + + let switch_datapath_button_label = match *switch_datapath { + true => "Switch to Simple Datapath", + false => "Switch to Full Datapath", + }; + + html! { + <> + // Console buttons + if **active_tab == TabState::Console { +
+ +
+ } else if **active_tab == TabState::Datapath { + + } else if **active_tab == TabState::HexEditor { +
+ +
+ } +
+
+ + + +
+ + if **active_tab == TabState::Datapath && props.datapath_state.current_architecture == MIPS { +
+ +
+ } + else if **active_tab == TabState::HexEditor { +
+ +
+ } +
+ + } +} + +#[derive(PartialEq, Properties)] +pub struct FooterTabProps { + pub label: String, + pub text: String, + pub on_click: Callback, + pub disabled: bool, + pub active_tab: UseStateHandle, + pub tab_name: TabState, +} + +#[function_component(FooterTab)] +pub fn footer_tab(props: &FooterTabProps) -> Html { + let active_tab = &props.active_tab; + html!( + if **active_tab == props.tab_name { + + } else { + + } + ) +} diff --git a/src/ui/footer/mod.rs b/src/ui/footer/mod.rs new file mode 100644 index 000000000..9cea807e4 --- /dev/null +++ b/src/ui/footer/mod.rs @@ -0,0 +1 @@ +pub mod component; diff --git a/src/ui/hex_editor/component.rs b/src/ui/hex_editor/component.rs new file mode 100644 index 000000000..a6d7c45a2 --- /dev/null +++ b/src/ui/hex_editor/component.rs @@ -0,0 +1,402 @@ +use std::ops::Deref; +use std::rc::Rc; +use wasm_bindgen::JsCast; +use wasm_bindgen::{closure::Closure, JsValue}; +use yew::prelude::*; +use yew::{function_component, html, use_callback, Html, Properties, UseStateHandle}; + +use monaco::{ + api::TextModel, + sys::{ + editor::{ + IEditorMinimapOptions, IEditorScrollbarOptions, IModelDecorationOptions, + IModelDeltaDecoration, IStandaloneEditorConstructionOptions, ISuggestOptions, + ScrollType, + }, + Range, + }, + yew::{CodeEditor, CodeEditorLink}, +}; + +use crate::emulation_core::mips::memory::{Memory, CAPACITY_BYTES}; + +// ** Hex Editor Component ** // +// Container for the hex editor. Enter if you dare. + +// Struct to store the coordinates of a specific instructon in the hex editor +#[derive(PartialEq, Properties)] +pub struct HexCoord { + pub line_number: f64, + pub start_column: f64, + pub end_column: f64, +} + +#[derive(PartialEq, Properties)] +pub struct HexEditorProps { + pub memory_text_model: UseStateHandle, + pub memory: Memory, + pub pc: u64, + // The instruction to highlight + pub memory_curr_instr: UseStateHandle, + pub initialized: bool, + pub executing: bool, +} + +// Struct for storing the updated string version of an instruction and its line number in the code editor +#[derive(Clone, Debug, PartialEq)] +pub struct UpdatedLine { + pub text: String, + pub line_number: usize, +} +impl UpdatedLine { + pub fn new(text: String, line_number: usize) -> Self { + UpdatedLine { text, line_number } + } +} + +#[function_component(HexEditor)] +pub fn hex_editor(props: &HexEditorProps) -> Html { + let editor_link = CodeEditorLink::new(); + let memory_text_model = &props.memory_text_model; + + // Store highlight decoration IDs + let decorations = use_mut_ref(js_sys::Array::new); + + // create a JavaScript closure for hex highlighting + let text_model_ref = memory_text_model.clone(); + let cb = Closure::new(Box::new( + move |event: monaco::sys::editor::ICursorSelectionChangedEvent| { + // Get a mutable reference to decorations + let decorations = Rc::clone(&decorations); + let mut decorations = decorations.borrow_mut(); + + // Get the monaco text model + let memory_text_model = text_model_ref.clone(); + let memory_text_model_ref = memory_text_model.as_ref(); + + // Clear previous highlights + // Create JS Array with an empty decoration + let not_highlighted = js_sys::Array::new(); + memory_text_model_ref.delta_decorations(&decorations, ¬_highlighted, None); + + // Create the ASCII highlight range + let selection = event.selection(); + let start_line_number = selection.selection_start_line_number(); + let end_line_number = selection.end_line_number(); + let start_column = selection.start_column(); + let end_column = selection.end_column(); + + // Get current line's contents + let line_number = event.selection().selection_start_line_number(); + let line_content = memory_text_model_ref.get_line_content(line_number); + let mut line_strings = line_content.split_whitespace().collect::>(); + // remove first element if it is an address + let address = line_strings.remove(0); + // save the address length to calculate the actual selection later + let address_length = address.len(); + + // doesn't support multi-line highlighting yet + if start_column <= address_length as f64 + 2.0 + || end_column <= address_length as f64 + 2.0 + || end_column <= start_column + || start_line_number != end_line_number + { + return; + } + + let mut final_start_column = 0; + let mut final_end_column = 0; + + // ** COLUMN NUMBERS FOR ASCII AND HEX SECTIONS WITHOUT SPACES ** // + let start_ascii_column_norm = 40; + let start_hex_column_norm = 10; + + // count whitespaces in line up to selection + let mut whitespace_count = 0; + for (i, c) in line_content.chars().enumerate() { + if i >= start_column as usize { + break; + } + if c == ' ' || c == '\t' { + whitespace_count += 1; + } + } + + // count whitespaces in selection + let selection_slice = &line_content[start_column as usize - 1..end_column as usize - 1]; + let mut whitespace_count_selection = 0; + for c in selection_slice.chars() { + if c == ' ' || c == '\t' { + whitespace_count_selection += 1; + } + } + + // get the actual column numbers for ASCII section with spaces + let start_ascii_column = start_ascii_column_norm + whitespace_count; + let end_ascii_column = + start_ascii_column_norm + whitespace_count + whitespace_count_selection; + + if start_column as usize >= start_ascii_column + && end_column as usize > end_ascii_column + && end_column > start_column + { + // we're selecting ASCII + // so we need to highlight the hex equivalent + // create the hex selection range + let ascii_selection_length = (end_column - start_column) as usize; + let start_word_index = start_column as usize - start_ascii_column; + let start_word_spaces = start_word_index / 4; + let end_word_index = end_column as usize - start_ascii_column; + let end_word_spaces = end_word_index / 4; + final_start_column = + start_hex_column_norm + start_word_spaces + start_word_index * 2; + final_end_column = + final_start_column + ascii_selection_length * 2 + end_word_spaces + - start_word_spaces; + + // edge case where the selection ends on a space + if end_word_index % 4 == 0 { + final_end_column -= 1; + } + } else { + let mut actual_start_col; + let mut actual_end_col; + // separate selection into valid bytes + // for example, if the selection is "7bd" in the word "27bdffd8", only "bd" is a valid byte to convert + actual_start_col = start_column as usize - whitespace_count - address_length; + actual_end_col = end_column as usize + - whitespace_count + - whitespace_count_selection + - address_length; + // if the first bit is part of an incomplete byte, remove it + if actual_start_col % 2 == 0 { + actual_start_col += 1; + } + // if the last bit is part of an incomplete byte, remove it + if actual_end_col % 2 == 0 && actual_end_col > 0 { + actual_end_col -= 1; + } + // make sure the resulting selection is valid + if actual_end_col > actual_start_col { + // uncomment to see the selection converted to ASCII + // // convert the selection to ASCII two bits at a time + // let no_whitespace_line = line_strings.join(""); + // let new_selection = &no_whitespace_line[actual_start_col - 1..actual_end_col - 1]; + // let mut converted_hex = String::new(); + // for (i, _c) in new_selection.chars().enumerate().step_by(2) { + // if (i + 1) >= new_selection.len() { + // break; + // } + // let ascii_digits = &new_selection[i..i + 2]; + // let ascii_digits = u8::from_str_radix(ascii_digits, 16).unwrap(); + // let ascii_str = ascii_digits as char; + // converted_hex.push(ascii_str); + // } + + // Create the ASCII highlight range + final_start_column = 46 + (actual_start_col / 2); + final_end_column = 46 + (actual_end_col / 2); + } + } + + let range = Range::new( + start_line_number, + final_start_column as f64, + start_line_number, + final_end_column as f64, + ); + + // Style the highlighting + let highlight_decoration: IModelDeltaDecoration = + js_sys::Object::new().unchecked_into(); + let highlight_options: IModelDecorationOptions = js_sys::Object::new().unchecked_into(); + highlight_options.set_inline_class_name("highlightHex".into()); + highlight_options.set_is_whole_line(false.into()); + highlight_decoration.set_options(&highlight_options); + let range_js = range.dyn_into::().expect("Range is not found."); + highlight_decoration.set_range(&monaco::sys::IRange::from(range_js)); + let decoration_js = highlight_decoration + .dyn_into::() + .expect("Highlight is not found."); + + // Create JS Array with the new decoration + let executed_line = js_sys::Array::new(); + executed_line.push(&decoration_js); + + // Get the monaco text model + let memory_text_model = text_model_ref.clone(); + let memory_text_model_ref = memory_text_model.as_ref(); + + let existing_decorations = memory_text_model_ref.get_all_decorations(None, None); + // Set new decorations and save their IDs + *decorations = memory_text_model_ref.delta_decorations( + &existing_decorations, + &executed_line, + None, + ); + }, + ) as Box); + + // Returns a struct containing monaco-like coordinates (start and end line numbers and columns) + // given the program counter (index of a WORD) + fn get_hex_coords(memory_curr_instr: u64) -> HexCoord { + let line_number = memory_curr_instr / 16 + 1; + let offset = 10; + let start_column = offset + ((memory_curr_instr % 16) * 2 + ((memory_curr_instr % 16) / 4)); + let end_column = start_column + 8; + + HexCoord { + line_number: line_number as f64, + start_column: start_column as f64, + end_column: end_column as f64, + } + } + + let on_editor_created = { + let memory_curr_instr = props.memory_curr_instr.clone(); + + // If the program is executing, set the current instruction to highlight to the program counter + if props.executing { + memory_curr_instr.set(props.pc); + } + + use_callback( + move |editor_link: CodeEditorLink, + (memory, memory_text_model, memory_curr_instr, initialized)| { + editor_link.with_editor(|editor| { + // Generate the hexdump from memory + let hexdump = memory.generate_formatted_hex(CAPACITY_BYTES); + // Replace the monaco text model contents with the hexdump + memory_text_model.set_value(&hexdump); + + // Get access to the raw IStandaloneCodeEditor which has the API calls we need + // https://microsoft.github.io/monaco-editor/typedoc/interfaces/editor.IStandaloneCodeEditor.html + let raw_editor = editor.as_ref(); + let cb_func = &cb.as_ref().unchecked_ref(); + + if *initialized { + let coords = get_hex_coords(**memory_curr_instr); + raw_editor.on_did_change_cursor_selection(cb_func); + raw_editor + .reveal_line_in_center(coords.line_number, Some(ScrollType::Smooth)); + + // Highlight line using delta decorations + let not_highlighted = js_sys::Array::new(); + let executed_line = js_sys::Array::new(); + let decoration: IModelDeltaDecoration = + js_sys::Object::new().unchecked_into(); + let options: IModelDecorationOptions = + js_sys::Object::new().unchecked_into(); + if coords.line_number != 0.0 { + // Show highlight if current line is not 0 + options.set_inline_class_name("executedLine".into()); + options.set_is_whole_line(false.into()); + } + decoration.set_options(&options); + let curr_range = Range::new( + coords.line_number, + coords.start_column, + coords.line_number, + coords.end_column, + ); + let range_js = curr_range + .dyn_into::() + .expect("Range is not found."); + decoration.set_range(&monaco::sys::IRange::from(range_js)); + let decoration_js = decoration + .dyn_into::() + .expect("Highlight is not found."); + executed_line.push(&decoration_js); + + raw_editor.delta_decorations(¬_highlighted, &executed_line); + } + }); + }, + ( + props.memory.clone(), + props.memory_text_model.clone(), + memory_curr_instr, + props.initialized, + ), + ) + }; + html! { + + } +} + +fn get_options() -> IStandaloneEditorConstructionOptions { + let options = IStandaloneEditorConstructionOptions::default(); + options.set_theme("vs-dark".into()); + options.set_language("ini".into()); + options.set_scroll_beyond_last_line(false.into()); + options.set_automatic_layout(true.into()); + + let minimap = IEditorMinimapOptions::default(); + minimap.set_enabled(false.into()); + options.set_minimap(Some(&minimap)); + + let scrollbar = IEditorScrollbarOptions::default(); + scrollbar.set_always_consume_mouse_wheel(false.into()); + options.set_scrollbar(Some(&scrollbar)); + + let suggest = ISuggestOptions::default(); + suggest.set_show_keywords(false.into()); + suggest.set_show_variables(false.into()); + suggest.set_show_icons(false.into()); + suggest.set_show_words(false.into()); + suggest.set_filter_graceful(false.into()); + options.set_suggest(Some(&suggest)); + + options +} + +// ** Helper functions ** +// Parse hexdump into a vector of u32 (ready to be stored in memory) +pub fn parse_hexdump(input: &str) -> Result<(Vec, Vec), String> { + let mut hex_words = Vec::new(); + let mut ascii_words = Vec::new(); + for line in input.lines() { + // remove all whitespace from the line + let parts: Vec<&str> = line.split_whitespace().collect::>(); + // don't include address or ASCII in hex parsing + for &part in &parts[1..5] { + let data = u32::from_str_radix(part, 16).map_err(|e| e.to_string())?; + hex_words.push(data); + } + // parse ASCII + // return ASCII translated to instructions in second vec + // we'll compare the ASCII section to memory as well in main.rs + let ascii = parts[5..].join(""); + let mut j = 0; + let mut hex = 0; + let hex_conglomerate = parts[1..5].join(""); + for (i, ascii_char) in ascii.chars().enumerate() { + if i % 4 == 0 && i != 0 { + ascii_words.push(hex); + hex = 0; + j = 0; + } + // shift the ASCII character into the hex word, starting from the left + if ascii_char == '.' { + // steal the bit from the hex portion + let hex_val = + u32::from_str_radix(&hex_conglomerate[(i * 2)..(i * 2 + 2)], 16).unwrap(); + hex |= hex_val << (24 - j * 8); + } else { + // valid ASCII number, shift it in + hex |= (ascii_char as u32) << (24 - j * 8); + } + j += 1; + } + // push the last word since we leave the loop at i = 16 + ascii_words.push(hex); + } + Ok((hex_words, ascii_words)) +} diff --git a/src/ui/hex_editor/mod.rs b/src/ui/hex_editor/mod.rs new file mode 100644 index 000000000..9cea807e4 --- /dev/null +++ b/src/ui/hex_editor/mod.rs @@ -0,0 +1 @@ +pub mod component; diff --git a/src/ui/regview/component.rs b/src/ui/regview/component.rs index df7210e9c..416e390f9 100644 --- a/src/ui/regview/component.rs +++ b/src/ui/regview/component.rs @@ -1,23 +1,32 @@ -use crate::emulation_core::mips::registers::{GpRegisterType, GpRegisters}; -//use gloo::console::log; +use crate::agent::datapath_communicator::DatapathCommunicator; +use crate::emulation_core::register::RegisterType; +use crate::ui::swim_editor::tab::Tab; +use std::rc::Rc; use wasm_bindgen::JsCast; -use web_sys::HtmlElement; +use web_sys::{HtmlInputElement, InputEvent}; use yew::prelude::*; +use yew::{html, Html}; + +// ** Register View Component ** // +// Container for the general purpose and floating point registers +// Is architecture-independent, as long as the registers implement the RegisterType trait -// datapath.coprocessor.fpr #[derive(PartialEq, Properties)] pub struct Regviewprops { - pub gp: GpRegisters, - pub fp: [u64; 32], + pub gp: Vec<(Rc, u64)>, + pub fp: Vec<(Rc, u64)>, + pub pc_limit: usize, + pub communicator: &'static DatapathCommunicator, } - -#[derive(PartialEq, Properties)] -pub struct Viewswitch { - pub switch_view: bool, +#[derive(PartialEq, Default)] +pub enum RegviewTabState { + #[default] + Gp, + Fp, } -#[derive(Default, PartialEq)] -enum UnitState { +#[derive(Default, PartialEq, Clone, Copy, Debug)] +pub enum UnitState { #[default] Dec, Hex, @@ -25,132 +34,183 @@ enum UnitState { Float, Double, } +// Stores the value and event data of the input +pub struct InputData { + pub value: String, + pub event: InputEvent, +} //Convert register to html through iterator -pub fn generate_gpr_rows(gp: GpRegisters) -> Html { - gp.into_iter() - .map(|(register, data)| { - html! { - - {get_gpr_name(register)} - {(data as i64).to_string()} - - } - }) - .collect::() -} -pub fn generate_gpr_rows_hex(gp: GpRegisters) -> Html { - gp.into_iter() - .map(|(register, data)| { - html! { - - {get_gpr_name(register)} - {format!("{data:#04x?}").to_string()} - - } - }) - .collect::() -} -pub fn generate_gpr_rows_bin(gp: GpRegisters) -> Html { - gp.into_iter() - .map(|(register, data)| { - html! { - - {get_gpr_name(register)} - {format!("{data:#b}").to_string()} - - } - }) - .collect::() -} -pub fn generate_fpr_rows(fp: [u64; 32]) -> Html { - fp.iter() - .enumerate() - .map(|(register, data)| { - html! { - - {format!("f{register}")} - {(*data as i64).to_string()} - - } - }) - .collect::() -} -pub fn generate_fpr_rows_hex(fp: [u64; 32]) -> Html { - fp.iter() - .enumerate() - .map(|(register, data)| { - html! { - - {format!("f{register}")} - {format!("{data:#04x?}").to_string()} - - } - }) - .collect::() -} -pub fn generate_fpr_rows_bin(fp: [u64; 32]) -> Html { - fp.iter() - .enumerate() - .map(|(register, data)| { - html! { - - {format!("f{register}")} - {format!("{data:#b}").to_string()} - - } - }) - .collect::() -} +// ============= General Purpose Registers ============= +pub fn generate_gpr_rows(props: &Regviewprops, radix: u32) -> Html { + let communicator = props.communicator; + let pc_limit = props.pc_limit; + let registers = props.gp.clone(); -pub fn generate_fpr_rows_float(fp: [u64; 32]) -> Html { - fp.iter() - .enumerate() + registers + .into_iter() .map(|(register, data)| { + let format_string = match radix { + 16 => format!("{:#04x?}", data), + 2 => format!("{:#b}", data), + _ => data.to_string(), + }; html! { - {format!("f{register}")} - {format!("{:e}",f32::from_bits((*data).try_into().unwrap())).to_string()} + {register.get_register_name()} + + (); + let input_string = input.value(); + let mut number = input_string.as_str(); + // remove prefix from hex and binary inputs + if radix == 2 || radix == 16 { + number = &input_string[2..]; + } + // use rust built-in parsing to convert string to u64 + let val = match u64::from_str_radix(number, radix) { + Ok(value) => { + input.set_class_name(""); + value + }, + Err(_err) => { + input.set_class_name("text-accent-red-200"); + return + } + }; + // if rust is like "yeah that's a perfectly valid u64 number right there", double check with the architecture + if register.is_valid_register_value(val, pc_limit) { + communicator.set_register(register.to_string(), val); + input.set_class_name(""); + } else { + input.set_class_name("text-accent-red-200"); + } + }} + value={format_string}/> + } }) .collect::() } -pub fn generate_fpr_rows_double(fp: [u64; 32]) -> Html { - fp.iter() - .enumerate() +// ============= Coprocessor Registers ============= +pub fn generate_fpr_rows(props: &Regviewprops, unit_type: UnitState) -> Html { + let communicator = props.communicator; + let pc_limit = props.pc_limit; + let registers = props.fp.clone(); + + registers + .into_iter() .map(|(register, data)| { html! { - {format!("f{register}")} - {format!("{:e}", f64::from_bits(*data)).to_string()} + {format!("{register}")} + + (); + let input_string = input.value(); + // parse the input depending on the unit type + let value = match unit_type { + UnitState::Float => { + match input_string.parse::() { + Ok(value) => { + input.set_class_name(""); + value.to_bits() as u64 // need to convert to bits to store in u64 + }, + Err(_err) => { + input.set_class_name("text-accent-red-200"); + return + } + } + }, + UnitState::Double => { + match input_string.parse::() { + Ok(value) => { + input.set_class_name(""); + value.to_bits() // need to convert to bits to store in u64 + }, + Err(_err) => { + input.set_class_name("text-accent-red-200"); + return + } + } + }, + UnitState::Hex => { + match u64::from_str_radix(&input_string[2..], 16) { + Ok(value) => { + input.set_class_name(""); + value + }, + Err(_err) => { + input.set_class_name("text-accent-red-200"); + return + } + } + }, + UnitState::Bin => { + match u64::from_str_radix(&input_string[2..], 2) { + Ok(value) => { + input.set_class_name(""); + value + }, + Err(_err) => { + input.set_class_name("text-accent-red-200"); + return + } + } + }, + _ => { + match input_string.parse::() { + Ok(value) => { + input.set_class_name(""); + value + }, + Err(_err) => { + input.set_class_name("text-accent-red-200"); + return + } + } + } + }; + if register.is_valid_register_value(value, pc_limit) { + communicator.set_fp_register(register.to_string(), value); + input.set_class_name(""); + } else { + input.set_class_name("text-accent-red-200"); + } + }} + value={ + match unit_type { + UnitState::Float => format!("{:e}", f32::from_bits(data as u32)).to_string(), + UnitState::Double => format!("{:e}", f64::from_bits(data)).to_string(), + UnitState::Hex => format!("{:#04x?}", data).to_string(), + UnitState::Bin => format!("{:#b}", data).to_string(), + _ => format!("{:?}", data).to_string(), + } + }/> + } }) .collect::() } -/// Returns the text to be shown for a general-purpose register. -pub fn get_gpr_name(register: GpRegisterType) -> String { - if register == GpRegisterType::Pc { - register.to_string() - } else { - format!("{} (r{})", register, register as u32) - } -} - #[function_component(Regview)] pub fn regview(props: &Regviewprops) -> Html { let active_view = use_state_eq(UnitState::default); - let switch_flag = use_state_eq(|| true); + let active_tab = use_state_eq(RegviewTabState::default); + + // Change the unit type let change_view = { let active_view = active_view.clone(); - Callback::from(move |event: MouseEvent| { - let target = event.target().unwrap().dyn_into::().unwrap(); - let mode = target - .get_attribute("label") - .unwrap_or(String::from("regview")); + Callback::from(move |event: Event| { + let target = event.target().unwrap().unchecked_into::(); + let mode = target.value(); let new_mode = match mode.as_str() { "bin" => UnitState::Bin, @@ -164,104 +224,72 @@ pub fn regview(props: &Regviewprops) -> Html { active_view.set(new_mode); }) }; - let on_switch_clicked_fp = { - let switch_flag = switch_flag.clone(); - use_callback( - move |_, switch_flag| { - if **switch_flag { - switch_flag.set(false); - } - }, - switch_flag, - ) - }; - let on_switch_clicked_gp = { - let switch_flag = switch_flag.clone(); - use_callback( - move |_, switch_flag| { - if !(**switch_flag) { - switch_flag.set(true); - } - }, - switch_flag, - ) - }; - //log!("This is ", *switch_flag); - html! { -
-
- if *active_view == UnitState::Dec { - - } else { - - } + // Change the active tab to GP or FP + let change_tab = { + let active_tab = active_tab.clone(); + Callback::from(move |event: MouseEvent| { + let target = event + .target() + .unwrap() + .dyn_into::() + .unwrap(); + let tab_name = target.get_attribute("label").unwrap_or(String::from("gp")); - if *active_view == UnitState::Bin { - - } else { - - } + let new_tab = match tab_name.as_str() { + "gp" => RegviewTabState::Gp, + "fp" => RegviewTabState::Fp, + _ => RegviewTabState::default(), + }; - if *active_view == UnitState::Hex { - - } else { - - } - if !*switch_flag{ - if *active_view == UnitState::Float { - - } else { - + active_tab.set(new_tab); + }) + }; + + html! { +
+
+
+ label="gp" text="GP" on_click={change_tab.clone()} disabled={false} active_tab={active_tab.clone()} tab_name={RegviewTabState::Gp}/> + label="fp" text="FP" on_click={change_tab.clone()} disabled={false} active_tab={active_tab.clone()} tab_name={RegviewTabState::Fp}/> +
+
-
- if *switch_flag { - - } else { - - } - if !(*switch_flag){ - - } else { - - } -
-
- +
+
- - + + - if *switch_flag{ + if *active_tab == RegviewTabState::Gp { if *active_view == UnitState::Bin { - {generate_gpr_rows_bin(props.gp)} + {generate_gpr_rows(props, 2)} } else if *active_view == UnitState::Hex { - {generate_gpr_rows_hex(props.gp)} + {generate_gpr_rows(props, 16)} } else { - {generate_gpr_rows(props.gp)} + {generate_gpr_rows(props, 10)} } } else { - if *active_view == UnitState::Bin { - {generate_fpr_rows_bin(props.fp)} - } else if *active_view == UnitState::Hex{ - {generate_fpr_rows_hex(props.fp)} - } else if *active_view == UnitState::Float{ - {generate_fpr_rows_float(props.fp)} - } else if *active_view == UnitState::Double{ - {generate_fpr_rows_double(props.fp)} - } else if *active_view == UnitState::Dec { - {generate_fpr_rows(props.fp)} - } + {generate_fpr_rows(props, *active_view.clone())} }
{"Register Name"}{"Data"}{"Register Name"}{"Data"}
diff --git a/src/ui/regview/labelcont.rs b/src/ui/regview/labelcont.rs deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/ui/regview/tabs.rs b/src/ui/regview/tabs.rs deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/ui/regview/value.rs b/src/ui/regview/value.rs deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/ui/swim_editor/component.rs b/src/ui/swim_editor/component.rs new file mode 100644 index 000000000..7039b3004 --- /dev/null +++ b/src/ui/swim_editor/component.rs @@ -0,0 +1,358 @@ +use std::collections::HashMap; +use std::collections::HashSet; +use std::str::FromStr; +use std::{cell::RefCell, rc::Rc}; + +use monaco::{ + api::TextModel, + sys::{ + editor::{ + IEditorMinimapOptions, IEditorScrollbarOptions, IModelDecorationOptions, + IModelDeltaDecoration, IStandaloneEditorConstructionOptions, ISuggestOptions, + ScrollType, + }, + IMarkdownString, Range, + }, + yew::{CodeEditor, CodeEditorLink}, +}; +use wasm_bindgen::{JsCast, JsValue}; +use web_sys::HtmlInputElement; +use yew::prelude::*; +use yew::{html, Callback, Properties}; +use yew_hooks::prelude::*; + +use crate::emulation_core::mips::memory::Memory; +use crate::emulation_core::stack::Stack; +use crate::parser::parser_assembler_main::parser; +use crate::ui::assembled_view::component::{StackFrameView, StackSegment}; +use crate::{ + agent::datapath_communicator::DatapathCommunicator, + emulation_core::architectures::AvailableDatapaths, + parser::parser_structs_and_enums::ProgramInfo, + ui::{ + assembled_view::component::{DataSegment, TextSegment}, + swim_editor::tab::{Tab, TabState}, + }, +}; +use strum::IntoEnumIterator; + +// ** SwimEditor Component ** // +// Houses the main editor and the tabs that allow the user to switch +// between the editor and the assembled view. +// It also contains the buttons that allow the user to copy the code to the clipboard and +// change the execution speed and architecture. +// Finally, it has the mouse hover event that allows the user to see the parsed information +// from the parser when hovering over the lines of code. + +#[derive(PartialEq, Properties)] +pub struct SwimEditorProps { + pub text_model: UseStateHandle, + pub lines_content: Rc>>, + pub program_info: ProgramInfo, + pub binary: Vec, + pub labels: HashMap, + pub pc: u64, + pub pc_limit: usize, + pub memory_curr_instr: UseStateHandle, + pub editor_curr_line: UseStateHandle, + pub editor_active_tab: UseStateHandle, + pub console_active_tab: UseStateHandle, + pub current_architecture: AvailableDatapaths, + pub speed: u32, + pub communicator: &'static DatapathCommunicator, + pub sp: u64, + pub memory: Memory, + pub stack: Stack, + pub breakpoints: UseStateHandle>, + pub initialized: bool, + pub executing: bool, +} + +#[derive(Default, PartialEq)] +pub enum EditorTabState { + #[default] + Editor, + TextSegment, + DataSegment, +} + +fn get_options() -> IStandaloneEditorConstructionOptions { + let options = IStandaloneEditorConstructionOptions::default(); + options.set_theme("vs-dark".into()); + options.set_language("mips".into()); + options.set_scroll_beyond_last_line(false.into()); + options.set_automatic_layout(true.into()); + + let minimap = IEditorMinimapOptions::default(); + minimap.set_enabled(false.into()); + options.set_minimap(Some(&minimap)); + + let scrollbar = IEditorScrollbarOptions::default(); + scrollbar.set_always_consume_mouse_wheel(false.into()); + options.set_scrollbar(Some(&scrollbar)); + + let suggest = ISuggestOptions::default(); + suggest.set_show_keywords(false.into()); + suggest.set_show_variables(false.into()); + suggest.set_show_icons(false.into()); + suggest.set_show_words(false.into()); + suggest.set_filter_graceful(false.into()); + options.set_suggest(Some(&suggest)); + + options +} + +#[function_component] +pub fn SwimEditor(props: &SwimEditorProps) -> Html { + let link = CodeEditorLink::new(); + let text_model = &*props.text_model; + let editor_active_tab = &props.editor_active_tab; + let console_active_tab = &props.console_active_tab; + + // Set up the array that would store hover decorations applied to the + // text model and initialize the options for it. + let hover_jsarray = js_sys::Array::new(); + let hover_decor_array = use_mut_ref(js_sys::Array::new); + + let on_editor_created = { + let curr_line = props.editor_curr_line.clone(); + let lines_content = Rc::clone(&props.lines_content); + + if props.executing { + let program_info = props.program_info.clone(); + // address_to_line_number converts program counters to line numbers + let list_of_line_numbers = program_info.address_to_line_number; + let index = props.pc as usize / 4; + curr_line.set(match list_of_line_numbers.get(index) { + Some(val) => (val + 1) as f64, // add one to account for the editor's line numbers + None => 0f64, + }); + } + + use_callback( + move |editor_link: CodeEditorLink, (curr_line, initialized)| { + editor_link.with_editor(|editor| { + let raw_editor = editor.as_ref(); + let model = raw_editor.get_model().unwrap(); + // store each line from the original code editor's contents for assembled view + let line_count = model.get_line_count() as usize; + let mut lines_content = lines_content.borrow_mut(); + let mut lines = Vec::new(); + for i in 1..line_count { + lines.push(model.get_line_content(i as f64)); + } + *lines_content = lines; + + // Only scroll to current line / highlight if the program has been initialized / begun execution + if *initialized { + // Scroll to current line + raw_editor.reveal_line_in_center(**curr_line, Some(ScrollType::Smooth)); + // Highlight current line using delta decorations + let not_highlighted = js_sys::Array::new(); + let executed_line = js_sys::Array::new(); + let decoration: IModelDeltaDecoration = + js_sys::Object::new().unchecked_into(); + let options: IModelDecorationOptions = + js_sys::Object::new().unchecked_into(); + if **curr_line != 0.0 { + // Show highlight if current line is not 0 + options.set_inline_class_name("executedLine".into()); + options.set_is_whole_line(true.into()); + } + decoration.set_options(&options); + let curr_range = Range::new(**curr_line, 0.0, **curr_line, 0.0); + let range_js = curr_range + .dyn_into::() + .expect("Range is not found."); + decoration.set_range(&monaco::sys::IRange::from(range_js)); + let decoration_js = decoration + .dyn_into::() + .expect("Highlight is not found."); + executed_line.push(&decoration_js); + raw_editor.delta_decorations(¬_highlighted, &executed_line); + } + }); + }, + (curr_line, props.initialized), + ) + }; + + // ** Callbacks for changing the active tab, architecture, and execution speed ** // + + // Changes the active tab to the one that was clicked + let change_tab = { + let editor_active_tab = editor_active_tab.clone(); + Callback::from(move |event: MouseEvent| { + let target = event + .target() + .unwrap() + .dyn_into::() + .unwrap(); + let tab_name = target + .get_attribute("label") + .unwrap_or(String::from("editor")); + + let new_tab: TabState = TabState::from_str(&tab_name).unwrap(); + editor_active_tab.set(new_tab); + }) + }; + + // Changes the architecture to the one that was selected + let change_architecture = { + let communicator = props.communicator; + Callback::from(move |event: Event| { + let target = event.target(); + let input = target.unwrap().unchecked_into::(); + let architecture = input.value(); + let new_architecture: AvailableDatapaths = + AvailableDatapaths::from(architecture.as_str()); + communicator.set_core(new_architecture); + }) + }; + + // Changes the execution speed to the one that was inputted + let change_execution_speed = { + let communicator = props.communicator; + Callback::from(move |event: Event| { + let target = event.target(); + let input = target.unwrap().unchecked_into::(); + // Parse the input value to a u32, or set it to 0 if it's not a valid number + let speed = input.value().parse::().unwrap_or(0); + communicator.set_execute_speed(speed); + }) + }; + + // Copies text to the user's clipboard + let on_clipboard_clicked = { + let text_model = text_model.clone(); + let clipboard = use_clipboard(); + Callback::from(move |_: _| { + let text_model = text_model.clone(); + clipboard.write_text(text_model.get_value()); + gloo::dialogs::alert("Your code is saved to the clipboard.\nPaste it onto a text file to save it.\n(Ctrl/Cmd + V)"); + }) + }; + + // We'll have the Mouse Hover event running at all times. + { + let text_model = text_model.clone(); + let current_arch = props.current_architecture; + use_event_with_window("mouseover", move |_: MouseEvent| { + let hover_jsarray = hover_jsarray.clone(); + let hover_decor_array = hover_decor_array.clone(); + let text_model = text_model.clone(); + let curr_model = text_model.as_ref(); + let (program_info, _, _) = parser(text_model.get_value(), current_arch); + + // Parse output from parser and create an instance of IModelDeltaDecoration for each line. + for (line_number, line_information) in program_info.monaco_line_info.iter().enumerate() + { + let decoration: IModelDeltaDecoration = js_sys::Object::new().unchecked_into(); + + let hover_range = + Range::new((line_number + 1) as f64, 0.0, (line_number + 1) as f64, 0.0); + let hover_range_js = hover_range + .dyn_into::() + .expect("Range is not found."); + decoration.set_range(&monaco::sys::IRange::from(hover_range_js)); + + let hover_opts: IModelDecorationOptions = js_sys::Object::new().unchecked_into(); + hover_opts.set_is_whole_line(true.into()); + let hover_message: IMarkdownString = js_sys::Object::new().unchecked_into(); + js_sys::Reflect::set( + &hover_message, + &JsValue::from_str("value"), + &JsValue::from_str(&line_information.mouse_hover_string), + ) + .unwrap(); + hover_opts.set_hover_message(&hover_message); + decoration.set_options(&hover_opts); + let hover_js = decoration + .dyn_into::() + .expect("Hover is not found."); + hover_jsarray.push(&hover_js); + } + + // log!("This is the array after the push"); + // log!(hover_jsarray.clone()); + + // properly pass the handlers onto the array + let new_hover_decor_array = + curr_model.delta_decorations(&hover_decor_array.borrow_mut(), &hover_jsarray, None); + *hover_decor_array.borrow_mut() = new_hover_decor_array; + + // log!("These are the arrays after calling Delta Decorations"); + // log!(hover_jsarray.clone()); + // log!(hover_decor_array.borrow_mut().clone()); + + // empty out the array that hold the decorations + hover_jsarray.set_length(0); + + // log!("These are the arrays after calling popping the hover_jsarray"); + // log!(hover_jsarray.clone()); + // log!(hover_decor_array.borrow_mut().clone()); + }); + }; + + let conditional_class = if **editor_active_tab == TabState::Editor { + "" + } else { + "hidden" + }; + + let arch_options = AvailableDatapaths::iter() + .map(|arch| { + let current_arch = props.current_architecture.to_string(); + html! { + + } + }) + .collect::(); + + html! { + <> + // Editor buttons +
+
+ label={TabState::Editor.to_string()} text={"Editor".to_string()} on_click={change_tab.clone()} disabled={false} active_tab={editor_active_tab.clone()} tab_name={TabState::Editor}/> + label={TabState::TextSegment.to_string()} text={"Text Segment".to_string()} on_click={change_tab.clone()} disabled={false} active_tab={editor_active_tab.clone()} tab_name={TabState::TextSegment}/> + label={TabState::DataSegment.to_string()} text={"Data Segment".to_string()} on_click={change_tab.clone()} disabled={false} active_tab={editor_active_tab.clone()} tab_name={TabState::DataSegment}/> + label={TabState::StackSegment.to_string()} text={"Stack Segment".to_string()} on_click={change_tab.clone()} disabled={false} active_tab={editor_active_tab.clone()} tab_name={TabState::StackSegment}/> + label={TabState::StackFrameView.to_string()} text={"Stack Frame".to_string()} on_click={change_tab.clone()} disabled={false} active_tab={editor_active_tab.clone()} tab_name={TabState::StackFrameView}/> +
+
+ + + {"Hz"} + +
+
+ if **editor_active_tab == TabState::Editor { + + } else if **editor_active_tab == TabState::TextSegment { + + } else if **editor_active_tab == TabState::DataSegment { + + } else if **editor_active_tab == TabState::StackSegment { + + } else if **editor_active_tab == TabState::StackFrameView { + + } + + } +} diff --git a/src/ui/swim_editor/mod.rs b/src/ui/swim_editor/mod.rs new file mode 100644 index 000000000..9a223b313 --- /dev/null +++ b/src/ui/swim_editor/mod.rs @@ -0,0 +1,2 @@ +pub mod component; +pub mod tab; diff --git a/src/ui/swim_editor/tab.rs b/src/ui/swim_editor/tab.rs new file mode 100644 index 000000000..070bb48c4 --- /dev/null +++ b/src/ui/swim_editor/tab.rs @@ -0,0 +1,69 @@ +use std::str::FromStr; +use strum_macros::Display; +use yew::prelude::*; + +// Custom TabState component that can be easily extended to include new tabs +// (and would allow in the future for the tabs to be movable) + +#[derive(PartialEq, Clone, Copy, Default, Display)] +pub enum TabState { + #[default] + Console, + Datapath, + HexEditor, + Editor, + TextSegment, + DataSegment, + StackSegment, + StackFrameView, +} + +impl FromStr for TabState { + type Err = (); + + fn from_str(tab_name: &str) -> Result { + match tab_name { + "Console" => Ok(TabState::Console), + "Datapath" => Ok(TabState::Datapath), + "HexEditor" => Ok(TabState::HexEditor), + "Editor" => Ok(TabState::Editor), + "TextSegment" => Ok(TabState::TextSegment), + "DataSegment" => Ok(TabState::DataSegment), + "StackSegment" => Ok(TabState::StackSegment), + "StackFrameView" => Ok(TabState::StackFrameView), + _ => Err(()), + } + } +} + +#[derive(Clone, PartialEq, Properties)] +pub struct TabProps { + pub label: String, + pub text: String, + pub on_click: Callback, + pub disabled: bool, + pub active_tab: UseStateHandle, + pub tab_name: T, +} + +#[function_component(Tab)] +pub fn tab(props: &TabProps) -> Html { + let active_tab = &props.active_tab; + let active_class = if **active_tab == props.tab_name { + "bg-primary-500" + } else { + "bg-primary-700" + }; + + html!( + + ) +} diff --git a/src/ui/visual_datapath/mod.rs b/src/ui/visual_datapath/mod.rs index 45790beb0..ad15f3004 100644 --- a/src/ui/visual_datapath/mod.rs +++ b/src/ui/visual_datapath/mod.rs @@ -29,24 +29,23 @@ use std::{cell::RefCell, rc::Rc}; use gloo_events::EventListener; use wasm_bindgen::{JsCast, JsValue}; -use web_sys::{Element, Event, HtmlCollection, HtmlElement}; +use web_sys::{Element, Event, HtmlCollection, HtmlElement, HtmlInputElement}; use yew::prelude::*; -use crate::emulation_core::mips::datapath::{MipsDatapath, Stage}; use consts::*; use utils::*; +use crate::agent::datapath_reducer::DatapathReducer; + #[derive(PartialEq, Properties)] pub struct VisualDatapathProps { - pub datapath: MipsDatapath, + pub datapath_state: UseReducerHandle, /// A path to the location of the datapath SVG file. This path should be /// relative to the project root. /// /// For example, "`static/datapath_full.svg`". pub svg_path: String, - - pub size: Option, } pub struct VisualDatapath { @@ -55,6 +54,7 @@ pub struct VisualDatapath { /// /// This can occur if the `svg_path` property of the component changes. should_reinitialize: bool, + size: Rc>, } #[derive(Clone, Copy, Default, PartialEq)] @@ -72,20 +72,20 @@ impl Component for VisualDatapath { VisualDatapath { active_listeners: Rc::new(RefCell::new(vec![])), should_reinitialize: false, + size: Rc::new(RefCell::new(0)), } } fn view(&self, ctx: &Context) -> Html { - // Set the size of the datapath based on props. - let size = ctx.props().size.unwrap_or_default(); - let size_class = match size { - DatapathSize::Big => "big-datapath", - DatapathSize::Small => "small-datapath", - }; - + let zoom_in_size = Rc::clone(&self.size); + let zoom_range_size = Rc::clone(&self.size); + let zoom_value_size = Rc::clone(&self.size); + let zoom_out_size = Rc::clone(&self.size); html! { - <> - +
+
+ +
- +
+ + (); + let value = target.value().parse::().unwrap(); + let mut size = zoom_range_size.borrow_mut(); + *size = value; + })}/> + +
+
} } @@ -104,22 +128,16 @@ impl Component for VisualDatapath { // there is actual data to view. A better way to see this is "when stage X is // set on the datapath, highlight the lines for stage Y." The datapath stage // tells where it will start the next time "execute" is pressed. - let current_stage = String::from(match ctx.props().datapath.current_stage { - Stage::InstructionFetch => "writeback", - Stage::InstructionDecode => "instruction_fetch", - Stage::Execute => "instruction_decode", - Stage::Memory => "execute", - Stage::WriteBack => "memory", - }); + let current_stage = ctx.props().datapath_state.get_current_stage(); if first_render || self.should_reinitialize { - self.initialize(current_stage, ctx.props().datapath.clone()); + self.initialize(current_stage, ctx.props().datapath_state.clone()); self.should_reinitialize = false; } else { let result = Self::highlight_stage( &get_g_elements(), current_stage, - ctx.props().datapath.clone(), + &ctx.props().datapath_state.clone(), ); // Capture new event listeners. @@ -179,7 +197,11 @@ impl VisualDatapath { /// circumvented by creating an event listener on the `` element for the /// "load" event, which will guarantee when that virtual DOM is actually ready /// to be manipulated. - pub fn initialize(&mut self, current_stage: String, datapath: MipsDatapath) { + pub fn initialize( + &mut self, + current_stage: String, + datapath_state: UseReducerHandle, + ) { let on_load = Callback::from(move |_| { let nodes = get_g_elements(); @@ -208,8 +230,7 @@ impl VisualDatapath { }); } }); - - Self::highlight_stage(&nodes, current_stage.clone(), datapath.clone()) + Self::highlight_stage(&nodes, current_stage.clone(), &datapath_state.clone()) }); let active_listeners = Rc::clone(&self.active_listeners); @@ -252,7 +273,7 @@ impl VisualDatapath { pub fn highlight_stage( nodes: &HtmlCollection, stage: String, - datapath: MipsDatapath, + datapath_state: &UseReducerHandle, ) -> Result, JsValue> { let mut active_listeners: Vec = vec![]; @@ -260,7 +281,7 @@ impl VisualDatapath { if element.has_attribute("data-stage") { let is_in_current_stage = element.get_attribute("data-stage").unwrap() == stage; if let Ok(listeners) = - Self::enable_interactivity(element, datapath.clone(), is_in_current_stage) + Self::enable_interactivity(element, datapath_state, is_in_current_stage) { for l in listeners { active_listeners.push(l); @@ -280,7 +301,7 @@ impl VisualDatapath { /// If successful, returns a [`Vec`] containing the newly created event listeners. pub fn enable_interactivity( element: &Element, - datapath: MipsDatapath, + datapath_state: &UseReducerHandle, is_active: bool, ) -> Result, JsValue> { // Color a line when hovering. @@ -299,39 +320,6 @@ impl VisualDatapath { } }); - // Show the popup when hovering. - let popup_on_mouseover = Callback::from(move |event: web_sys::Event| { - let event = event.unchecked_into::(); - - // Get relevant elements. - let target = event.target().unwrap().unchecked_into::(); - let popup = get_popup_element(); - - // Show popup. - let variable = target - .parent_element() - .unwrap() - .get_attribute("data-variable") - .unwrap_or_default(); - populate_popup_information(&datapath, &variable); - - // Calculate popup position. - let mouse_position = get_datapath_iframe_mouse_position(event); - let popup_size = (popup.offset_width(), popup.offset_height()); - let popup_position = - calculate_popup_position(mouse_position, popup_size, get_window_size()); - - popup - .style() - .set_property("left", &format!("{}px", popup_position.0)) - .ok(); - popup - .style() - .set_property("top", &format!("{}px", popup_position.1)) - .ok(); - popup.style().set_property("display", "block").ok(); - }); - // Move the popup if the mouse moves while still hovering. let popup_on_mousemove = Callback::from(move |event: Event| { let event = event.unchecked_into::(); @@ -342,7 +330,7 @@ impl VisualDatapath { let mouse_position = get_datapath_iframe_mouse_position(event); let popup_size = (popup.offset_width(), popup.offset_height()); let popup_position = - calculate_popup_position(mouse_position, popup_size, get_window_size()); + calculate_popup_position(mouse_position, popup_size, get_datapath_iframe_size()); // Move popup. popup @@ -394,10 +382,37 @@ impl VisualDatapath { color_on_mouseout.emit(event.clone()) }); - let popup_on_mouseover = popup_on_mouseover.clone(); + let datapath_state_ref = datapath_state.clone(); let popup_on_mouseover_listener = EventListener::new(&path, "mouseover", move |event| { - popup_on_mouseover.emit(event.clone()) + let event = event.clone().unchecked_into::(); + // Get relevant elements. + let target = event.target().unwrap().unchecked_into::(); + let popup = get_popup_element(); + + // Show popup. + let variable = target + .parent_element() + .unwrap() + .get_attribute("data-variable") + .unwrap_or_default(); + populate_popup_information(&datapath_state_ref, &variable); + + // Calculate popup position. + let mouse_position = get_datapath_iframe_mouse_position(event); + let popup_size = (popup.offset_width(), popup.offset_height()); + let popup_position = + calculate_popup_position(mouse_position, popup_size, get_window_size()); + + popup + .style() + .set_property("left", &format!("{}px", popup_position.0)) + .ok(); + popup + .style() + .set_property("top", &format!("{}px", popup_position.1)) + .ok(); + popup.style().set_property("display", "block").ok(); }); let popup_on_mousemove = popup_on_mousemove.clone(); diff --git a/src/ui/visual_datapath/utils.rs b/src/ui/visual_datapath/utils.rs index 38afb0046..2f6406672 100644 --- a/src/ui/visual_datapath/utils.rs +++ b/src/ui/visual_datapath/utils.rs @@ -3,8 +3,12 @@ use gloo::utils::{document, window}; use wasm_bindgen::JsCast; use web_sys::{Element, HtmlCollection, HtmlElement, HtmlObjectElement, MouseEvent}; +use yew::UseReducerHandle; -use crate::emulation_core::{datapath::VisualDatapath, mips::datapath::MipsDatapath}; +use crate::{ + agent::datapath_reducer::DatapathReducer, + emulation_core::{architectures::AvailableDatapaths, line_info::LineInformation}, +}; use super::consts::*; @@ -46,6 +50,18 @@ pub fn get_window_size() -> (i32, i32) { ) } +pub fn get_datapath_iframe_size() -> (i32, i32) { + let datapath_wrapper = document() + .get_element_by_id("datapath-scrollbox") + .unwrap() + .unchecked_into::(); + + ( + datapath_wrapper.client_width() as i32, + datapath_wrapper.client_height() as i32, + ) +} + /// Returns the relative coordinates of the `` element to the page. pub fn get_datapath_position() -> (i32, i32) { let datapath_root = get_datapath_root(); @@ -63,8 +79,7 @@ pub fn get_datapath_iframe_mouse_position(event: MouseEvent) -> (i32, i32) { let datapath_position = get_datapath_position(); let datapath_wrapper = gloo::utils::document() - .query_selector(".datapath-wrapper") - .unwrap() + .get_element_by_id("datapath-scrollbox") .unwrap() .unchecked_into::(); let scroll_position = ( @@ -138,7 +153,10 @@ where /// Parameters: /// - `datapath`: A reference to the datapath that information will be pulled from. /// - `variable`: The "variable" attribute of the line in the diagram that will have information. -pub fn populate_popup_information(datapath: &MipsDatapath, variable: &str) { +pub fn populate_popup_information( + datapath_state: &UseReducerHandle, + variable: &str, +) { let popup = get_popup_element(); let title = popup.query_selector(".title").unwrap().unwrap(); @@ -146,7 +164,7 @@ pub fn populate_popup_information(datapath: &MipsDatapath, variable: &str) { let bits = popup.query_selector(".data .code").unwrap().unwrap(); let meaning = popup.query_selector(".meaning").unwrap().unwrap(); - let information = datapath.visual_line_to_data(variable); + let information = visual_line_to_data(variable, datapath_state); title.set_text_content(Some(&information.title)); description.set_text_content(Some(&information.description)); @@ -170,3 +188,535 @@ pub fn u64_to_bits(mut value: u64, bits: u64) -> String { output } + +pub fn visual_line_to_data( + variable: &str, + datapath_state: &UseReducerHandle, +) -> LineInformation { + match datapath_state.current_architecture { + AvailableDatapaths::MIPS => { + match variable { + "alu_input2" => LineInformation { + title: String::from("ALU Input 2"), + description: String::from("The second input to the ALU. This is determined by the ALUSrc control signal to select between register data, a sign-extended and left-shifted immediate value, or a zero-extended immediate value."), + value: datapath_state.mips.state.alu_input2, + bits: 64, + }, + "alu_result" => LineInformation { + title: String::from("ALU Result"), + description: String::from("The result of the calculation performed by the ALU. This is used either as an address to access memory or as a value that is saved into a register."), + value: datapath_state.mips.state.alu_result, + bits: 64, + }, + "data_result" => LineInformation { + title: String::from("Writeback Data"), + description: String::from("After finishing processing the instruction, this will either be the ALU result, data from memory, or PC + 4, based on the MemToReg control signal. This data is saved into registers."), + value: datapath_state.mips.state.data_result, + bits: 64, + }, + "fpu_alu_result" => LineInformation { + title: String::from("Floating-Point ALU Result"), + description: String::from("The result of the calculation performed by the floating-point ALU. This is used as an option to be written to a floating-point register, based on the DataWrite and FpuMemToReg control signals."), + value: datapath_state.mips.coprocessor_state.alu_result, + bits: 64, + }, + "fpu_branch_decision" => LineInformation { + title: String::from("FPU Branch Decision"), + description: String::from("Based on the true/false branch flag, determines whether to branch. (The FpuBranch control signal must also be set.)"), + value: datapath_state.mips.coprocessor_state.condition_code_mux as u64, + bits: 1, + }, + "fpu_branch_flag" => LineInformation { + title: String::from("Instruction [16] (True/False Branch Flag)"), + description: String::from("The true/false branch flag of branching datapath_state.mips.coprocessor instructions. This flag specifies whether a floating-point branch instruction is BC1T or BC1F."), + value: datapath_state.mips.coprocessor_state.branch_flag as u64, + bits: 1, + }, + "fpu_comparator_result" => LineInformation { + title: String::from("Floating-Point Comparator Result"), + description: String::from("The result of the comparison of two floating-point values. This is routed to the \"Condition Code\" (cc) register, and will be written there if the CcWrite control signal is set."), + value: datapath_state.mips.coprocessor_state.comparator_result, + bits: 64, + }, + "fpu_condition_code" => LineInformation { + title: String::from("Condition Code Value"), + description: String::from("Data retrieved from the \"Condition Code\" (cc) register. This specifies whether a previous conditional instruction was true or false."), + value: datapath_state.mips.coprocessor_state.condition_code_bit as u64, + bits: 1, + }, + "fpu_condition_code_inverted" => LineInformation { + title: String::from("Condition Code Value (Inverted)"), + description: String::from("Inverted form of the condition code register value."), + value: datapath_state.mips.coprocessor_state.condition_code_bit_inverted as u64, + bits: 1, + }, + "fpu_data" => LineInformation { + title: String::from("Floating-Point Data Register Value"), + description: String::from("Data retrieved from the \"Data\" register. This register acts as a means to communicate data between the main processor and floating-point datapath_state.mips.coprocessor in MTC1 and MFC1 instructions."), + value: datapath_state.mips.coprocessor_state.fmt as u64, + bits: 64, + }, + "fpu_data_writeback" => LineInformation { + title: String::from("Floating-Point Data Writeback"), + description: String::from("The value from the floating-point unit's \"Data\" register. Depending on the FpuRegWidth control signal, this will be 64-bit data or sign-extended 32-bit data."), + value: datapath_state.mips.coprocessor_state.data_writeback, + bits: 64, + }, + "fpu_destination" => LineInformation { + title: String::from("Floating-Point Write Register"), + description: String::from("The register that will be written to, assuming FpuRegWrite is set. Depending on the FpuRegDst control signal, this will consist of the fs, ft, or fd register."), + value: datapath_state.mips.coprocessor_state.destination as u64, + bits: 5, + }, + "fpu_fd" => LineInformation { + title: String::from("Instruction [10-6] (fd)"), + description: String::from("The fd field. Depending on the FpuRegDst control signal, this will be the register written to in a floating-point operation. This register is used as the destination for most floating-point arithmetic instructions."), + value: datapath_state.mips.coprocessor_state.fd as u64, + bits: 5, + }, + "fpu_fmt" => LineInformation { + title: String::from("Instruction [25-21] (fmt)"), + description: String::from("The fmt field. This is used to distinguish between single-precision and double-precision floating-point instructions."), + value: datapath_state.mips.coprocessor_state.fmt as u64, + bits: 5, + }, + "fpu_fp_register_data_from_main_processor" => LineInformation { + title: String::from("Writeback Data (To Floating-Point Coprocessor)"), + description: String::from("This data is written to a floating-point register, given FpuMemToReg is set. This line allows data to load from memory to a floating-point register, specifically in the case of the LWC1 instruction."), + value: datapath_state.mips.coprocessor_state.fp_register_data_from_main_processor, + bits: 64, + }, + "fpu_fp_register_to_memory" => LineInformation { + title: String::from("Memory Write Data (from FPU)"), + description: String::from("If the MemWriteSrc control signal is set, this data will be written to memory. This is used for the SWC1 instruction."), + value: datapath_state.mips.coprocessor_state.fp_register_to_memory, + bits: 64, + }, + "fpu_fs" => LineInformation { + title: String::from("Instruction [15-11] (fs)"), + description: String::from("The fs field. Contains the first register to be read for a floating-point instruction."), + value: datapath_state.mips.coprocessor_state.fs as u64, + bits: 5, + }, + "fpu_ft" => LineInformation { + title: String::from("Instruction [20-16] (ft)"), + description: String::from("The ft field. Contains the second register to be read for a floating-point instruction."), + value: datapath_state.mips.coprocessor_state.ft as u64, + bits: 5, + }, + "fpu_new_data" => LineInformation { + title: String::from("New Floating-Point Data Register Value"), + description: String::from("Data sent to the \"Data\" register. Depending on the DataSrc control signal, this will either be data from the main processor or the floating-point datapath_state.mips.coprocessor. This register acts as a means to communicate data between the main processor and floating-point datapath_state.mips.coprocessor in MTC1 and MFC1 instructions."), + value: datapath_state.mips.coprocessor_state.fmt as u64, + bits: 64, + }, + "fpu_read_data_1" => LineInformation { + title: String::from("FPU Read Data 1"), + description: String::from("Data retrieved from the register specified by the fs instruction field. This is used as the first inputs to the floating-point ALU and comparator. This can additionally be written to the \"Data\" register, based on the DataSrc and DataWrite control signals."), + value: datapath_state.mips.coprocessor_state.read_data_1, + bits: 64, + }, + "fpu_read_data_2" => LineInformation { + title: String::from("FPU Read Data 2"), + description: String::from("Data retrieved from the register specified by the ft instruction field. This is used as the second inputs to the floating-point ALU and comparator. This can additionally be used as data to be written to memory, based on the MemWriteSrc control signal."), + value: datapath_state.mips.coprocessor_state.read_data_2, + bits: 64, + }, + "fpu_register_write_data" => LineInformation { + title: String::from("FPU Register Write Data"), + description: String::from("Data that will be written to a floating-point register, given that FpuRegWrite is set."), + value: datapath_state.mips.coprocessor_state.register_write_data, + bits: 64, + }, + "fpu_register_write_mux_to_mux" => LineInformation { + title: String::from("FPU Register Write Data (When FpuMemToReg is Unset)"), + description: String::from("Based on the DataWrite control signal, this will either be the result of the floating-point ALU or the contents of the \"Data\" register. (The \"Data\" register is used for transferring data between the processor and floating-point datapath_state.mips.coprocessor.)"), + value: datapath_state.mips.coprocessor_state.register_write_mux_to_mux, + bits: 64, + }, + "fpu_sign_extend_data" => LineInformation { + title: String::from("Floating-Point Data Register Value (Sign-Extended)"), + description: String::from("In the case where FpuRegWidth indicates a 32-bit width, this is the bottom 32 bits of the value from the \"Data\" register, then sign-extended to 64 bits."), + value: datapath_state.mips.coprocessor_state.sign_extend_data, + bits: 64, + }, + "funct" => LineInformation { + title: String::from("Instruction [5-0] (funct)"), + description: String::from("The funct field. Contains the type of operation to execute for R-type instructions."), + value: datapath_state.mips.state.funct as u64, + bits: 6, + }, + "imm" => LineInformation { + title: String::from("Instruction [15-0] (immediate)"), + description: String::from("The immediate field. Contains the 16-bit constant value used for I-type instructions."), + value: datapath_state.mips.state.imm as u64, + bits: 16, + }, + "instruction" => LineInformation { + title: String::from("Instruction"), + description: String::from("The currently-loaded instruction. This is broken down into different fields, where each field serves a different purpose in identifying what the instruction does."), + value: datapath_state.mips.state.instruction as u64, + bits: 32, + }, + "jump_address" => LineInformation { + title: String::from("Jump Address"), + description: String::from("The calculated next program counter for jump instructions. For I type instructions, this is the immediate value added to the data in rs1. For U type instructions, this is the immediate value shifted left by 2."), + value: datapath_state.mips.state.jump_address, + bits: 64, + }, + "lower_26" => LineInformation { + title: String::from("Instruction [25-0]"), + description: String::from("The lower 26 bits of instruction. This is used as part of the new PC value for J-type instructions."), + value: datapath_state.mips.state.lower_26 as u64, + bits: 26, + }, + "lower_26_shifted_left_by_2" => LineInformation { + title: String::from("Instruction [25-0] << 2"), + description: String::from("The lower 26 bits of instruction, shifted left by 2. This is used as part of the new PC value for J-type instructions."), + value: datapath_state.mips.state.lower_26_shifted_left_by_2 as u64, + bits: 28, + }, + "mem_mux1_to_mem_mux2" => LineInformation { + title: String::from("Relative PC Address"), + description: String::from("Based on the control signals for branching and jumping, this address may be the next PC value. This is used for general non-branching instructions or branch-type instructions."), + value: datapath_state.mips.state.mem_mux1_to_mem_mux2, + bits: 64, + }, + "memory_data" => LineInformation { + title: String::from("Memory Data"), + description: String::from("The data retrieved from memory, given that the MemRead control signal is set. This may be 32 bits or 64 bits, depending on the RegWidth control signal."), + value: datapath_state.mips.state.memory_data, + bits: 64, + }, + "new_pc" => LineInformation { + title: String::from("New Program Counter"), + description: String::from("The address of the next instruction to execute. In other words, the next value of the program counter (PC) register."), + value: datapath_state.mips.state.new_pc, + bits: 64, + }, + "pc" => LineInformation { + title: String::from("Program Counter"), + description: String::from("The address of the currently-executing instruction."), + value: datapath_state.mips.registers.pc, + bits: 64, + }, + "pc_plus_4" => LineInformation { + title: String::from("PC + 4"), + description: String::from("The address of the currently-executing instruction, plus 4. By default, this will become the next value of the PC register. However, a different address may be used in the case of a branch or jump instruction."), + value: datapath_state.mips.state.pc_plus_4, + bits: 64, + }, + "pc_plus_4_upper" => LineInformation { + title: String::from("PC + 4 [63-28]"), + description: String::from("The upper 36 bits of PC + 4. This is to be concatenated with the lower 26 bits of the instruction to calculate a jump address."), + value: datapath_state.mips.state.pc_plus_4 & 0xffff_ffff_f000_0000 >> 28, + bits: 36, + }, + "ra_id" => LineInformation { + title: String::from("Return Address Register Index"), + description: String::from("The value 31. This represents the thirty-second register, the return address register ($ra)."), + value: 31, + bits: 5, + }, + "rd" => LineInformation { + title: String::from("Instruction [15-11] (rd)"), + description: String::from("The rd field. Depending on the RegDst control signal, this will be the register written to for an instruction. This register is used as the destination for most R-type instructions."), + value: datapath_state.mips.state.rd as u64, + bits: 5, + }, + "read_data_1" => LineInformation { + title: String::from("Read Data 1"), + description: String::from("Data retrieved from the register specified by the rs instruction field. Based on the instruction, this may be used as the first input to the ALU, or the next value of the PC register."), + value: datapath_state.mips.state.read_data_1, + bits: 64, + }, + "read_data_2" => LineInformation { + title: String::from("Read Data 2"), + description: String::from("Data retrieved from the register specified by the rt instruction field. Based on the instruction, this may be used as the second input to the ALU, data written to memory, or data transferred to the floating-point coprocessor."), + value: datapath_state.mips.state.read_data_2, + bits: 64, + }, + "register_write_data" => LineInformation { + title: String::from("Register Write Data"), + description: String::from("Data that will be written to a general-purpose register, given that RegWrite is set."), + value: datapath_state.mips.state.register_write_data, + bits: 64, + }, + "relative_pc_branch" => LineInformation { + title: String::from("Relative PC Branch Address"), + description: String::from("The relative address used in branch instructions. This is the sum of PC + 4 and the sign-extended immediate value, shifted left by 2."), + value: datapath_state.mips.state.relative_pc_branch, + bits: 64, + }, + "rs" => LineInformation { + title: String::from("Instruction [25-21] (rs)"), + description: String::from("The rs field. Contains the first register to be read for an instruction."), + value: datapath_state.mips.state.rs as u64, + bits: 5, + }, + "rt" => LineInformation { + title: String::from("Instruction [20-16] (rt)"), + description: String::from("The rt field. Contains the second register to be read for an instruction."), + value: datapath_state.mips.state.rt as u64, + bits: 5, + }, + "shamt" => LineInformation { + title: String::from("Instruction [10-6] (shamt)"), + description: String::from("The shamt (\"shift amount\") field. Specifies the number of bits to shift for those instructions that perform bit-shifting."), + value: datapath_state.mips.state.shamt as u64, + bits: 5, + }, + "sign_extend" => LineInformation { + title: String::from("Sign-Extended Immediate"), + description: String::from("The immediate field, sign-extended to a 64-bit value."), + value: datapath_state.mips.state.sign_extend, + bits: 64, + }, + "sign_extend_shift_left_by_2" => LineInformation { + title: String::from("Sign-Extended Immediate << 2"), + description: String::from("The immediate field, sign-extended to a 64-bit value, then shifted left by 2."), + value: datapath_state.mips.state.sign_extend_shift_left_by_2, + bits: 64, + }, + "write_data" => LineInformation { + title: String::from("Memory Write Data"), + description: String::from("Given that the MemWrite control signal is set, this data will be written to memory."), + value: datapath_state.mips.state.write_data, + bits: 64, + }, + "write_register" => LineInformation { + title: String::from("Write Register"), + description: String::from("The register that will be written to, assuming RegWrite is set. Depending on the RegDst control signal, this will consist of the rs, rt, or rd register, or 31 (indicating the $ra register)."), + value: datapath_state.mips.state.write_register_destination as u64, + bits: 5, + }, + "zero_extended_immediate" => LineInformation { + title: String::from("Zero-Extended Immediate"), + description: String::from("The immediate field, zero-extended to a 64-bit value."), + value: datapath_state.mips.state.imm as u64, + bits: 64, + }, + _ => LineInformation { + title: String::from("[Title]"), + description: String::from("[Description]"), + value: 0, + bits: 0, + }, + } + }, + AvailableDatapaths::RISCV => { + match variable { + "alu_input1" => LineInformation { + title: String::from("ALU Input 1"), + description: String::from("The first input to the ALU. This is determined by the OP1Select control signal to select between the current program counter, a sign-extended and left-shifted immediate value, or the data from rs1."), + value: datapath_state.riscv.state.alu_input1, + bits: 64, + }, + "alu_input2" => LineInformation { + title: String::from("ALU Input 2"), + description: String::from("The second input to the ALU. This is determined by the ALUSrc control signal to select between register data and a sign-extended and left-shifted immediate value."), + value: datapath_state.riscv.state.alu_input2, + bits: 64, + }, + "alu_result" => LineInformation { + title: String::from("ALU Result"), + description: String::from("The result of the calculation performed by the ALU. This is used either as an address to access memory or as a value that is saved into a register."), + value: datapath_state.riscv.state.alu_result, + bits: 64, + }, + "data_result" => LineInformation { + title: String::from("Writeback Data"), + description: String::from("After finishing processing the instruction, this will either be the ALU result, data from memory, or PC + 4, based on the MemToReg control signal. This data is saved into registers."), + value: datapath_state.riscv.state.data_result, + bits: 64, + }, + "funct3" => LineInformation { + title: String::from("Instruction [14-12] (funct)"), + description: String::from("The funct3 field. Controls the type of operation to execute for all RISC-V instructions except for U and J type instructions."), + value: datapath_state.riscv.state.funct3 as u64, + bits: 3, + }, + "funct7" => LineInformation { + title: String::from("Instruction [31-25] (funct7)"), + description: String::from("The funct7 field. Controls the type of operation to execute for R type instructions."), + value: datapath_state.riscv.state.funct7 as u64, + bits: 7, + }, + "imm" => LineInformation { + title: String::from("Immediate"), + description: String::from("The immediate field. This field is a different size depending on the instruction type, but it can be at most 20 bits."), + value: datapath_state.riscv.state.imm as u64, + bits: 20, + }, + "imm_input" => LineInformation { + title: String::from("Instruction [31-12] (immediate)"), + description: String::from("The part of the instruction used to extract the imm field from an instruction. In RISC-V, depending on the instruction type, the imm field can be split up within an instruction (S, B, and J type) or even out of order (B and J type)."), + value: datapath_state.riscv.state.imm_input, + bits: 20, + }, + "imm_shifted_left_by_2" => LineInformation { + title: String::from("Immediate (shifted left by 2)"), + description: String::from("The immediate field shifed left by two bits. This value is later used as a jump address."), + value: (datapath_state.riscv.state.imm << 2) as u64, + bits: 22, + }, + "i_type_address" => LineInformation { + title: String::from("I Type Jump Address"), + description: String::from("The jump address used for I type jump instructions. This is calculated by adding the immediate value and the contents of rs1."), + value: datapath_state.riscv.state.i_type_jump, + bits: 22, + }, + "instruction" => LineInformation { + title: String::from("Instruction"), + description: String::from("The currently-loaded instruction. This is broken down into different fields, where each field serves a different purpose in identifying what the instruction does."), + value: datapath_state.riscv.state.instruction as u64, + bits: 32, + }, + "jump_address" => LineInformation { + title: String::from("Jump Address"), + description: String::from("The calculated next program counter for jump instructions. For I type instructions, this is the immediate value added to the data in rs1. For U type instructions, this is the immediate value shifted left by 2."), + value: datapath_state.riscv.state.jump_address, + bits: 64, + }, + "lower_26" => LineInformation { + title: String::from("Instruction [25-0]"), + description: String::from("The lower 26 bits of instruction. This is used as part of the new PC value for J-type instructions."), + value: datapath_state.riscv.state.lower_26 as u64, + bits: 26, + }, + "lower_26_shifted_left_by_2" => LineInformation { + title: String::from("Instruction [25-0] << 2"), + description: String::from("The lower 26 bits of instruction, shifted left by 2. This is used as part of the new PC value for J-type instructions."), + value: datapath_state.riscv.state.lower_26_shifted_left_by_2 as u64, + bits: 28, + }, + "mem_mux1_to_mem_mux2" => LineInformation { + title: String::from("Relative PC Address"), + description: String::from("Based on the control signals for branching and jumping, this address may be the next PC value. This is used for general non-branching instructions or branch-type instructions."), + value: datapath_state.riscv.state.mem_mux1_to_mem_mux2, + bits: 64, + }, + "memory_data" => LineInformation { + title: String::from("Memory Data"), + description: String::from("The data retrieved from memory, given that the MemRead control signal is set. This may be 32 bits or 64 bits, depending on the RegWidth control signal."), + value: datapath_state.riscv.state.memory_data, + bits: 64, + }, + "new_pc" => LineInformation { + title: String::from("New Program Counter"), + description: String::from("The address of the next instruction to execute. In other words, the next value of the program counter (PC) register."), + value: datapath_state.riscv.state.new_pc, + bits: 64, + }, + "pc" => LineInformation { + title: String::from("Program Counter"), + description: String::from("The address of the currently-executing instruction."), + value: datapath_state.riscv.registers.pc, + bits: 64, + }, + "pc_plus_4" => LineInformation { + title: String::from("PC + 4"), + description: String::from("The address of the currently-executing instruction, plus 4. By default, this will become the next value of the PC register. However, a different address may be used in the case of a branch or jump instruction."), + value: datapath_state.riscv.state.pc_plus_4, + bits: 64, + }, + "pc_plus_4_upper" => LineInformation { + title: String::from("PC + 4 [63-28]"), + description: String::from("The upper 36 bits of PC + 4. This is to be concatenated with the lower 26 bits of the instruction to calculate a jump address."), + value: datapath_state.riscv.state.pc_plus_4 & 0xffff_ffff_f000_0000 >> 28, + bits: 36, + }, + "ra_id" => LineInformation { + title: String::from("Return Address Register Index"), + description: String::from("The value 31. This represents the thirty-second register, the return address register ($ra)."), + value: 31, + bits: 5, + }, + "rd" => LineInformation { + title: String::from("Instruction [11-7] (rd)"), + description: String::from("The rd field. This will be the register written to for an instruction."), + value: datapath_state.riscv.state.rd as u64, + bits: 5, + }, + "read_data_1" => LineInformation { + title: String::from("Read Data 1"), + description: String::from("Data retrieved from the register specified by the rs1 instruction field. Based on the instruction, this may be used as the first input to the ALU, or in the calculation of the next value of the PC register."), + value: datapath_state.riscv.state.read_data_1, + bits: 64, + }, + "read_data_2" => LineInformation { + title: String::from("Read Data 2"), + description: String::from("Data retrieved from the register specified by the rs2 instruction field. Based on the instruction, this may be used as the second input to the ALU or data written to memory."), + value: datapath_state.riscv.state.read_data_2, + bits: 64, + }, + "register_write_data" => LineInformation { + title: String::from("Register Write Data"), + description: String::from("Data that will be written to a general-purpose register, given that RegWrite is set."), + value: datapath_state.riscv.state.register_write_data, + bits: 64, + }, + "relative_pc_branch" => LineInformation { + title: String::from("Branch Address"), + description: String::from("The absolute address used in branch instructions. This is the sign-extended immediate value, shifted left by 2."), + value: datapath_state.riscv.state.relative_pc_branch, + bits: 64, + }, + "rs1" => LineInformation { + title: String::from("Instruction [19-15] (rs1)"), + description: String::from("The rs1 field. Contains the first register to be read for an instruction."), + value: datapath_state.riscv.state.rs1 as u64, + bits: 5, + }, + "rs2" => LineInformation { + title: String::from("Instruction [24-20] (rs2)"), + description: String::from("The rs2 field. Contains the second register to be read for an instruction."), + value: datapath_state.riscv.state.rs2 as u64, + bits: 5, + }, + "shamt" => LineInformation { + title: String::from("Instruction [10-6] (shamt)"), + description: String::from("The shamt (\"shift amount\") field. Specifies the number of bits to shift for those instructions that perform bit-shifting."), + value: datapath_state.riscv.state.shamt as u64, + bits: 5, + }, + "sign_extend" => LineInformation { + title: String::from("Sign-Extended Immediate"), + description: String::from("The immediate field, sign-extended to a 64-bit value."), + value: datapath_state.riscv.state.sign_extend, + bits: 64, + }, + "sign_extend_shift_left_by_2" => LineInformation { + title: String::from("Sign-Extended Immediate << 2"), + description: String::from("The immediate field, sign-extended to a 64-bit value, then shifted left by 2."), + value: datapath_state.riscv.state.sign_extend_shift_left_by_2, + bits: 64, + }, + "write_data" => LineInformation { + title: String::from("Memory Write Data"), + description: String::from("Given that the MemWrite control signal is set, this data will be written to memory."), + value: datapath_state.riscv.state.write_data, + bits: 64, + }, + "write_register" => LineInformation { + title: String::from("Write Register"), + description: String::from("The register that will be written to, assuming RegWrite is set. Depending on the RegDst control signal, this will consist of the rs, rt, or rd register, or 31 (indicating the $ra register)."), + value: datapath_state.riscv.state.write_register_destination as u64, + bits: 5, + }, + "zero_extended_immediate" => LineInformation { + title: String::from("Zero-Extended Immediate"), + description: String::from("The immediate field, zero-extended to a 64-bit value."), + value: datapath_state.riscv.state.imm as u64, + bits: 64, + }, + _ => LineInformation { + title: String::from("[Title]"), + description: String::from("[Description]"), + value: 0, + bits: 0, + }, + } + } + } +} diff --git a/static/assembly_examples/data.asm b/static/assembly_examples/data.asm new file mode 100644 index 000000000..4f2ad3957 --- /dev/null +++ b/static/assembly_examples/data.asm @@ -0,0 +1,14 @@ +.text +addi $t0, $zero, 300 +addi $t1, $zero, 340 +addi $t2, $zero, 380 +syscall +.data +a_secret: .ascii "hi" +importantQuestion: .asciiz "What is system software?" +numbers: .byte 0x64, 100, 'a', 'b' +half: .half 1000 +buffer: .space 8 +word: .word 12345678 +float: .float 1.20 +double: .double 1.22222 diff --git a/static/assembly_examples/fibonacci.asm b/static/assembly_examples/fibonacci.asm index 19e62420a..fd01dc889 100644 --- a/static/assembly_examples/fibonacci.asm +++ b/static/assembly_examples/fibonacci.asm @@ -66,8 +66,8 @@ main: # PC starts here lw $ra,36($sp) lw $fp,32($sp) addiu $sp,$sp,40 + li $a0, 0 # Load 0 into $a0 to run the exit syscall syscall # replaced jr $ra with syscall to prevent infinite execution - nop # Here's the demo code in C: # diff --git a/static/assembly_examples/riscv_fib.asm b/static/assembly_examples/riscv_fib.asm new file mode 100644 index 000000000..e5d745b27 --- /dev/null +++ b/static/assembly_examples/riscv_fib.asm @@ -0,0 +1,28 @@ +fib(int): + # t0 and t1 are the last two values of the fib sequence + ori t0, zero, 0 # a_n-2 + ori t1, zero, 1 # a_n-1 + ori t2, zero, 1 # a_n + ori t3, zero, 1 # index + +COND: + bne a0, t3, LOOP # If we've reached our index, return + or a1, zero, t2 + #Pseudo-Instruction: ret + jalr x0, x1, 0 #Pseudo-Instruction Translation + +LOOP: + # Legendary fibonacci calculation + add t2, t0, t1 + or t0, zero, t1 + or t1, zero, t2 + addi t3, t3, 1 + jal x0, COND + +main: + ori a0, zero, 15 + jal ra, fib(int) + ori a0, zero, 1 + ecall + ori a0, zero, 0 + ecall \ No newline at end of file diff --git a/static/assembly_examples/riscv_fib_recursive.asm b/static/assembly_examples/riscv_fib_recursive.asm new file mode 100644 index 000000000..5d67e4bfd --- /dev/null +++ b/static/assembly_examples/riscv_fib_recursive.asm @@ -0,0 +1,55 @@ +fib(int): + # Stack variables: | return addr | argument | n - 1 | n - 2 | + + # Save return address and argument + sw ra, 0(sp) + sw a0, 4(sp) + + ori t0, zero, 0 + ori t1, zero, 1 + beq a0, t0, RET_0 + beq a0, t1, RET_1 + + # Recursive call for fib(n-1) + addi a0, a0, -1 # Setting arg + addi sp, sp, -16 # Making space on the stack + jal ra, fib(int) + sw a1, 8(sp) # Storing the return value + lw a0, 4(sp) # Restoring our original argument + + # Recursive call for fib(n-2) + addi a0, a0, -2 + addi sp, sp, -16 + jal ra, fib(int) + sw a1, 12(sp) + + # Adding together n-1 and n-2 and returning + lw t0, 8(sp) + lw t1, 12(sp) + + add a1, t0, t1 + lw ra, 0(sp) + addi sp, sp, 16 + #Pseudo-Instruction: ret + jalr x0, x1, 0 #Pseudo-Instruction Translation + + +RET_0: + ori a1, zero, 0 + addi sp, sp, 16 + #Pseudo-Instruction: ret + jalr x0, x1, 0 #Pseudo-Instruction Translation +RET_1: + ori a1, zero, 1 + addi sp, sp, 16 + #Pseudo-Instruction: ret + jalr x0, x1, 0 #Pseudo-Instruction Translation + +main: + ori a0, zero, 5 + addi sp, sp, -16 + jal ra, fib(int) + ori a0, zero, 1 + ecall + ori a0, zero, 0 + ecall diff --git a/static/assembly_examples/riscv_fib_xregisters.asm b/static/assembly_examples/riscv_fib_xregisters.asm new file mode 100644 index 000000000..106d5a94d --- /dev/null +++ b/static/assembly_examples/riscv_fib_xregisters.asm @@ -0,0 +1,55 @@ +fib: + # Stack variables: | return addr | argument | n - 1 | n - 2 | + + # Save return address and argument + sw x1, 0(x2) + sw x10, 4(x2) + + ori x5, x0, 0 + ori x6, x0, 1 + beq x10, x5, RET_0 + beq x10, x6, RET_1 + + # Recursive call for fib(n-1) + addi x10, x10, -1 # Setting arg + addi x2, x2, -16 # Making space on the stack + jalr x1, x5, 0 + sw x11, 8(x2) # Storing the return value + lw x10, 4(x2) # Restoring our original argument + + # Recursive call for fib(n-2) + addi x10, x10, -2 + addi x2, x2, -16 + jalr x1, x5, 0 + sw x11, 12(x2) + + # Adding together n-1 and n-2 and returning + lw x5, 8(x2) + lw x6, 12(x2) + + add x11, x5, x6 + lw x1, 0(x2) + addi x2, x2, 16 + # Pseudo-Instruction: ret + jalr x0, x1, 0 # Pseudo-Instruction Translation + + +RET_0: + ori x11, x0, 0 + addi x2, x2, 16 + # Pseudo-Instruction: ret + jalr x0, x1, 0 # Pseudo-Instruction Translation +RET_1: + ori x11, x0, 1 + addi x2, x2, 16 + # Pseudo-Instruction: ret + jalr x0, x1, 0 # Pseudo-Instruction Translation + +main: + ori x10, x0, 5 + addi x2, x2, -16 + jalr x1, x5, 0 + ori x10, x0, 1 + ecall + ori x10, x0, 0 + ecall diff --git a/static/assembly_examples/riscv_fpu_arithmetic.asm b/static/assembly_examples/riscv_fpu_arithmetic.asm new file mode 100644 index 000000000..d9a113ed1 --- /dev/null +++ b/static/assembly_examples/riscv_fpu_arithmetic.asm @@ -0,0 +1,56 @@ +main: + # Testing FPU Arithmetic Works Using Pythagorean Theorem + # All expected output noted in comments. + # Set Print Flag + ori a0, zero, 1 + + # Initialize Initial Values + addi t0, zero, 3 + addi t1, zero, 4 + + # Convert to Floating Point + fcvt.s.w ft0, t0 + fcvt.s.w ft1, t1 + + # Test Minimum (3) + fmin.s ft2, ft0, ft1 + fcvt.w.s a1, ft2 + ecall + + # Test Maximum (4) + fmax.s ft3, ft0, ft1 + fcvt.w.s a1, ft3 + ecall + + # Test Multiplication (9, 16) + fmul.s ft4, ft0, ft0 + fcvt.w.s a1, ft4 + ecall + + fmul.s ft5, ft1, ft1 + fcvt.w.s a1, ft5 + ecall + + # Test Addition (25) + fadd.s ft6, ft4, ft5 + fcvt.w.s a1, ft6 + ecall + + # Test Subtraction (21) + fsub.s ft7, ft6, ft1 + fcvt.w.s a1, ft7 + ecall + + # Test Division (7) + fdiv.s ft0, ft7, ft0 + fcvt.w.s a1, ft0 + ecall + + # Test Sqrt and Return Hypotenuse (5) + fsqrt.s ft7, ft6 + fcvt.w.s a1, ft7 + ecall + + # Reset Print Flag + ori a0, zero, 0 + ecall diff --git a/static/assembly_examples/riscv_fpu_bit_operations.asm b/static/assembly_examples/riscv_fpu_bit_operations.asm new file mode 100644 index 000000000..6bbe4b19b --- /dev/null +++ b/static/assembly_examples/riscv_fpu_bit_operations.asm @@ -0,0 +1,56 @@ +main: + # Testing FPU FMV by Moving Bit Patterns and Manipulating Them Across Both Paths + # All expected output noted in comments. + # Set Print Flag + ori a0, zero, 1 + + # Initialize Initial Value + addi t0, zero, 3 + + # Convert to FP + fcvt.s.w ft0, t0 + + # Sqrt (Check FP Register for 1.7... value) + fsqrt.s ft1, ft0 + + # Move Bit Pattern to Integer + fmv.x.w t1, ft1 + + # Negate + addi t2, zero, 0x1 + slli t3, t2, 31 + or t4, t1, t3 + + # Move Back To FP (Check FP Register Again) + fmv.w.x ft2, t4 + + # Square + fmul.s ft3, ft2, ft2 + + # EQ to Original Value (Should Return 1) + feq.s a1, ft0, ft3 + ecall + + # LT to Original Value (Should Return 0) + flt.s a1, ft3, ft0 + ecall + + # LE to Original Value (Should Return 1) + fle.s a1, ft3, ft0 + ecall + + # LE to Lesser Value (Should Return 0) + fle.s a1, ft3, ft1 + ecall + + # Classify ft2 (Negative, should return 2 (0010)) + fclass.s a1, ft2 + ecall + + # Classify ft3 (Positive, should return 64 (0100 0000)) + fclass.s a1, ft3 + ecall + + # Reset Print Flag + ori a0, zero, 0 + ecall diff --git a/static/assembly_examples/riscv_fpu_pythagorean.asm b/static/assembly_examples/riscv_fpu_pythagorean.asm new file mode 100644 index 000000000..0e39f4b1c --- /dev/null +++ b/static/assembly_examples/riscv_fpu_pythagorean.asm @@ -0,0 +1,62 @@ +main: + # Testing FPU Arithmetic and I/O Works Using Pythagorean Theorem + # Set Print Flag + ori a0, zero, 5 + + # Enter first value and then store to t0 + ecall + or t0, a1, zero + + # Enter second value and then store to t1 + ecall + or t1, a1, zero + + # Set SYSCALL Flag to Print + ori a0, zero, 1 + + # Convert to Floating Point + fcvt.s.w ft0, t0 + fcvt.s.w ft1, t1 + + # Print Minimum + fmin.s ft2, ft0, ft1 + fcvt.w.s a1, ft2 + ecall + + # Print Maximum + fmax.s ft3, ft0, ft1 + fcvt.w.s a1, ft3 + ecall + + # Print Squares (Multiplication) + fmul.s ft4, ft0, ft0 + fcvt.w.s a1, ft4 + ecall + + fmul.s ft5, ft1, ft1 + fcvt.w.s a1, ft5 + ecall + + # Print Addition + fadd.s ft6, ft4, ft5 + fcvt.w.s a1, ft6 + ecall + + # Print Subtraction + fsub.s ft7, ft6, ft1 + fcvt.w.s a1, ft7 + ecall + + # Print Division + fdiv.s ft0, ft7, ft0 + fcvt.w.s a1, ft0 + ecall + + # Test Sqrt and Print Hypotenuse + fsqrt.s ft7, ft6 + fcvt.w.s a1, ft7 + ecall + + # Reset Print Flag + ori a0, zero, 0 + ecall diff --git a/static/assembly_examples/riscv_fpu_r4.asm b/static/assembly_examples/riscv_fpu_r4.asm new file mode 100644 index 000000000..554c7f3ad --- /dev/null +++ b/static/assembly_examples/riscv_fpu_r4.asm @@ -0,0 +1,42 @@ +main: + # Testing FPU R4 Type Operations! + # All results are made positive since displays/prints always display unsigned forms and it gets weird. + # All expected output noted in comments. + # Set Print Flag + ori a0, zero, 1 + + # Initialize Initial Value + addi t0, zero, 5 + addi t1, zero, 7 + addi t2, zero, 10 + + + # Convert to FP + fcvt.s.w ft0, t0 + fcvt.s.w ft1, t1 + fcvt.s.w ft2, t2 + + # Test fmadd (Should return 45) + fmadd.s ft3, ft0, ft1, ft2 + fcvt.w.s a1, ft3 + ecall + + # Test fmsub (Should return 25) + fmsub.s ft4, ft0, ft1, ft2 + fcvt.w.s a1, ft4 + ecall + + # Test fnmsub (Should return 10) + fnmsub.s ft5, ft0, ft1, ft3 + fcvt.w.s a1, ft5 + ecall + + # Test fnmadd (Should return 5) + fnmadd.s ft6, ft0, ft1, ft0 + fadd.s ft7, ft6, ft3 + fcvt.w.s a1, ft7 + ecall + + # Reset Flags + ori a0, zero, 0 + ecall diff --git a/static/assembly_examples/riscv_fpu_sign_bits.asm b/static/assembly_examples/riscv_fpu_sign_bits.asm new file mode 100644 index 000000000..e76ed478e --- /dev/null +++ b/static/assembly_examples/riscv_fpu_sign_bits.asm @@ -0,0 +1,26 @@ +main: + # Since Printing Negatives To The Console is a bit Temperamental at the moment, use the Registers with Float mode to check values. + # Sign Bit Operations Check + + addi t0, zero, 13 + addi t1, zero, 7 + addi t2, zero, -5 + addi t3, zero, -3 + + fcvt.s.w ft0, t0 + fcvt.s.w ft1, t1 + fcvt.s.w ft2, t2 + fcvt.s.w ft3, t3 + + # Check Sign Bit Normal (Should be Negative 13 in Register) + fsgnj.s ft4, ft0, ft2 + # Check Sign Bit Negation (Should be Negative 7 in Register) + fsgnjn.s ft5, ft1, ft0 + # Check Sign Bit Xor Both (Should be Positive 5 in Register) + fsgnjx.s ft6, ft2, ft3 + # Check Sign Bit Xor Neither (Should be Positive 13 in Register) + fsgnjx.s ft7, ft0, ft1 + # Check Sign Bit Xor One (Should be Negative 3 in Register) + fsgnjx.s ft8, ft3, ft0 + + ecall diff --git a/static/assembly_examples/riscv_fpu_sqrt_load_store_test.asm b/static/assembly_examples/riscv_fpu_sqrt_load_store_test.asm new file mode 100644 index 000000000..ff3f3d793 --- /dev/null +++ b/static/assembly_examples/riscv_fpu_sqrt_load_store_test.asm @@ -0,0 +1,22 @@ +main: + # Initialize Original Value + addi a1, zero, 49 + # Print Original Value + ori a0, zero, 1 + ecall + # Convert to Floating Point + fcvt.s.w ft0, a1 + # Take Square Root (7) + fsqrt.s ft1, ft0 + # Store/Load Test (Both Processors). Ensure the value is kept the same across all transitions. + fsw ft1, 0x100(zero) + lw t0, 0x100(zero) + sw t0, 0x110(zero) + flw ft2, 0x110(zero) + # Reconvert to Integer + fcvt.w.s a1, ft2 + # Print out Value (Should be ==7) + ecall + # Reset Print Register and Return + ori a0, zero, 0 + ecall diff --git a/static/assembly_examples/riscv_test.asm b/static/assembly_examples/riscv_test.asm new file mode 100644 index 000000000..7cbfc3705 --- /dev/null +++ b/static/assembly_examples/riscv_test.asm @@ -0,0 +1,7 @@ +main: + addi x1, x0, 1 + addi x2, x0, 1 + beq x3, x1, L1 + +L1: + addi x1, x0, 100 \ No newline at end of file diff --git a/static/backward.svg b/static/backward.svg new file mode 100644 index 000000000..7b9498caf --- /dev/null +++ b/static/backward.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/static/cog.svg b/static/cog.svg new file mode 100644 index 000000000..05081d3c4 --- /dev/null +++ b/static/cog.svg @@ -0,0 +1,7 @@ + + +circle-left + + + + diff --git a/static/datapath_full.svg b/static/datapath_full.svg index 280bad613..b6efe2728 100644 --- a/static/datapath_full.svg +++ b/static/datapath_full.svg @@ -1,4 +1,3 @@ - -
ImmShift
ImmShift
ALUOp
ALUOp
ALUSrc
ALUSrc
MemWriteSrc
MemWriteSrc
FpuRegDst
FpuRegDst
RegDst
RegDst
PC
PC
Instruction Memory
Instruction Memory
Sign-Extend
Sign-Extend
Shift Left 2
Shift Left 2
ALU Control
ALU Control
Add
Add
Add
Add
01S0
Mux
Mux
Relative PC Branch
Relative PC Branch
PC + 4
PC + 4
BranchType
BranchType
Control Unit
Control Unit
Instruction
Instruction
Branch
Branch
MemRead
MemRead
MemToReg
MemToReg
MemWrite
MemWrite
RegWrite
RegWrite
Shift Left 2
Shift Left 2
Jump Address
Jump Address
Jump
Jump
PC + 4 [63-28]
PC + 4 [63-28]
Floating Point Unit (Coprocessor 1)
Floating Point Unit (Coprocessor 1)
Registers
Registers
Read Register 1
Read Register 1
Read Register 2
Read Register 2
Write Register
Write Register
Write Data
Write Data
Read Data 1
Read Data 1
Read Data 2
Read Data 2
Data Memory
Data Memory
Write Data
Write Data
Address
Address
Read Data
Read Data
Floating Point Registers
Floating Point Registers
Read Register 1
Read Register 1
Read Register 2
Read Register 2
Write Register
Write Register
Write Data
Write Data
Read Data 1
Read Data 1
Read Data 2
Read Data 2
Format
Format
Instruction [15-11] (fs)
Instruction [15-11] (fs)
Instruction [25-21] (fmt)
Instruction [25-21] (fmt)
FpuRegWrite
FpuRegWrite
CCWrite
CCWrite
DataSrc
DataSrc
CC
CC
FpuBranch
FpuBranch
FpuMemToReg
FpuMemToReg
FPU Control Unit
FPU Control Unit
FP
Comparator
FP...
Zero
Zero
ALU Result
ALU Result
Condition Code Register
Condition Cod...
Instruction [31-0]
Instruction [31-0]
FpuALUOp
FpuALUOp
FP ALU
FP ALU
Zero
Zero
ALU Result
ALU Result
DataWrite
DataWrite
Data
Data
01S0
Mux
Mux
Sign-Extend
Sign-Ext...
01S0
Mux
Mux
Instruction [10-6] (fd)
Instruction [10-6] (fd)
01S0
Mux
Mux
Instruction [16] (branch if true/false flag)
Instruction [16] (branch if true/false flag)
01S0
Mux
Mux
01S0
Mux
Mux
01S0
Mux
Mux
RegWidth
RegWidth
FpuRegWidth
FpuRegWidth
Instruction [25-21] (rs or base)
Instruction [25-21] (rs or base)
Instruction [15-11] (rd)
Instruction [15-11] (rd)
Instruction [20-16] (rt)
Instruction [20-16] (rt)
<<
<<
Mux012
Zero-extended immediate
Zero-extended immediate
Instruction Fetch
Instruction Fetch
Instruction Decode / Register Read
Instruction Decode / Register Read
Execute
Execute
Memory
Memory
Writeback
Writeback
Instruction [20-16] (ft)
Instruction [20-16] (ft)
01S0
Mux
Mux
[31-0]
[31-0]
01S0
Mux
Mux
Mux210
4
4
ALU
ALU
Zero
Zero
Mux012
Instruction [10-6] (fd)
Instruction [10-6] (fd)
Mux0123
31
31
Instruction [10-6] (shamt)
Instruction [10-6] (shamt)
Mux210
Instruction [25-0]
Instruction [25-0]
Instruction [5-0] (funct)
Instruction [5-0] (funct)
Text is not SVG - cannot display
\ No newline at end of file +
ImmShift
ImmShift
ALUOp
ALUOp
ALUSrc
ALUSrc
MemWriteSrc
MemWriteSrc
FpuRegDst
FpuRegDst
RegDst
RegDst
PC
PC
Instruction Memory
Instruction Memory
Sign-Extend
Sign-Extend
Shift Left 2
Shift Left 2
ALU Control
ALU Control
Add
Add
Add
Add
01S0
Mux
Mux
Relative PC Branch
Relative PC Branch
PC + 4
PC + 4
BranchType
BranchType
Control Unit
Control Unit
Instruction
Instruction
Branch
Branch
MemRead
MemRead
MemToReg
MemToReg
MemWrite
MemWrite
RegWrite
RegWrite
Shift Left 2
Shift Left 2
Jump Address
Jump Address
Jump
Jump
PC + 4 [63-28]
PC + 4 [63-28]
Floating Point Unit (Coprocessor 1)
Floating Point Unit (Coprocessor 1)
Registers
Registers
Read Register 1
Read Register 1
Read Register 2
Read Register 2
Write Register
Write Register
Write Data
Write Data
Read Data 1
Read Data 1
Read Data 2
Read Data 2
Data Memory
Data Memory
Write Data
Write Data
Address
Address
Read Data
Read Data
Floating Point Registers
Floating Point Registers
Read Register 1
Read Register 1
Read Register 2
Read Register 2
Write Register
Write Register
Write Data
Write Data
Read Data 1
Read Data 1
Read Data 2
Read Data 2
Format
Format
Instruction [15-11] (fs)
Instruction [15-11] (fs)
Instruction [25-21] (fmt)
Instruction [25-21] (fmt)
FpuRegWrite
FpuRegWrite
CCWrite
CCWrite
DataSrc
DataSrc
CC
CC
FpuBranch
FpuBranch
FpuMemToReg
FpuMemToReg
FPU Control Unit
FPU Control Unit
FP
Comparator
FP...
Zero
Zero
ALU Result
ALU Result
Condition Code Register
Condition Cod...
Instruction [31-0]
Instruction [31-0]
FpuALUOp
FpuALUOp
FP ALU
FP ALU
Zero
Zero
ALU Result
ALU Result
DataWrite
DataWrite
Data
Data
01S0
Mux
Mux
Sign-Extend
Sign-Ext...
01S0
Mux
Mux
Instruction [10-6] (fd)
Instruction [10-6] (fd)
01S0
Mux
Mux
Instruction [16] (branch if true/false flag)
Instruction [16] (branch if true/false flag)
01S0
Mux
Mux
01S0
Mux
Mux
01S0
Mux
Mux
RegWidth
RegWidth
FpuRegWidth
FpuRegWidth
Instruction [25-21] (rs or base)
Instruction [25-21] (rs or base)
Instruction [15-11] (rd)
Instruction [15-11] (rd)
Instruction [20-16] (rt)
Instruction [20-16] (rt)
<<
<<
Mux012
Zero-extended immediate
Zero-extended immediate
Instruction Fetch
Instruction Fetch
Instruction Decode / Register Read
Instruction Decode / Register Read
Execute
Execute
Memory
Memory
Writeback
Writeback
Instruction [20-16] (ft)
Instruction [20-16] (ft)
01S0
Mux
Mux
[31-0]
[31-0]
01S0
Mux
Mux
Mux210
4
4
ALU
ALU
Zero
Zero
Mux012
Instruction [10-6] (fd)
Instruction [10-6] (fd)
Mux0123
31
31
Instruction [10-6] (shamt)
Instruction [10-6] (shamt)
Mux210
Instruction [25-0]
Instruction [25-0]
Instruction [5-0] (funct)
Instruction [5-0] (funct)
Text is not SVG - cannot display
\ No newline at end of file diff --git a/static/datapath_riscv.svg b/static/datapath_riscv.svg new file mode 100644 index 000000000..3ce00dbad --- /dev/null +++ b/static/datapath_riscv.svg @@ -0,0 +1,3 @@ + + +
Instruction [31-25] (funct7)
Instruction [31-25] (funct7)
Instruction [14-12] (funct3)
Instruction [14-12] (funct3)
ALUOp
ALUOp
OP2Select
OP2Select
PC
PC
Instruction Memory
Instruction Memory
IMM Decode
IMM Decode
ALU Control
ALU Control
Instruction [31-12] (imm)
Instruction [31-12] (imm)
Add
Add
01S0
Mux
Mux
Branch
Branch
PC + 4
PC + 4
Control Unit
Control Unit
Instruction
Instruction
WbSel
WbSel
ReadWrite
ReadWrite
Jump Address
Jump Address
Jump
Jump
Registers
Registers
Read Register 1
Read Register 1
Read Register 2
Read Register 2
Write Register
Write Register
Write Data
Write Data
Read Data 1
Read Data 1
Read Data 2
Read Data 2
Data Memory
Data Memory
Write Data
Write Data
Address
Address
Read Data
Read Data
Instruction [11-7] (rd)
Instruction [11-7] (rd)
Instruction [24-20] (rs2)
Instruction [24-20] (rs2)
Instruction Fetch
Instruction Fetch
Instruction Decode / Register Read
Instruction Decode / Register Read
Execute
Execute
Memory
Memory
Writeback
Writeback
4
4
ALU
ALU
Zero
Zero
01S0
Mux
Mux
01S0
Mux
Mux
Mux012
Instruction [19-15] (rs1)
Instruction [19-15] (rs1)
IMMSelect
IMMSelect
RegWriteEn
RegWriteEn
Mux0123
Shift Left 2
Shift Left 2
Add
Add
01S0
Mux
Mux
Shift Left 2
Shift Left 2
Branch
Branch
JumpSrc
JumpSrc
OP1Select
OP1Select
Text is not SVG - cannot display
\ No newline at end of file diff --git a/static/datapath_simple.svg b/static/datapath_simple.svg index 8ef5a51a1..65dfdbae4 100644 --- a/static/datapath_simple.svg +++ b/static/datapath_simple.svg @@ -1,4 +1,3 @@ - -
ALUOp
ALUOp
ALUSrc
ALUSrc
RegDst
RegDst
PC
PC
Instruction Memory
Instruction Memory
Sign-Extend
Sign-Extend
Shift Left 2
Shift Left 2
ALU Control
ALU Control
Instruction [5-0] (funct)
Instruction [5-0] (funct)
Instruction [15-0] (imm)
Instruction [15-0] (imm)
Add
Add
Add
Add
01S0
Mux
Mux
Relative PC Branch
Relative PC Branch
PC + 4
PC + 4
Control Unit
Control Unit
Instruction
Instruction
Branch
Branch
MemRead
MemRead
MemToReg
MemToReg
MemWrite
MemWrite
RegWrite
RegWrite
Shift Left 2
Shift Left 2
Jump Address
Jump Address
Jump
Jump
PC + 4 [63-28]
PC + 4 [63-28]
Registers
Registers
Read Register 1
Read Register 1
Read Register 2
Read Register 2
Write Register
Write Register
Write Data
Write Data
Read Data 1
Read Data 1
Read Data 2
Read Data 2
Data Memory
Data Memory
Write Data
Write Data
Address
Address
Read Data
Read Data
Instruction [25-21] (rs or base)
Instruction [25-21] (rs or base)
Instruction [15-11] (rd)
Instruction [15-11] (rd)
Instruction [20-16] (rt)
Instruction [20-16] (rt)
Instruction Fetch
Instruction Fetch
Instruction Decode / Register Read
Instruction Decode / Register Read
Execute
Execute
Memory
Memory
Writeback
Writeback
4
4
ALU
ALU
Zero
Zero
01S0
Mux
Mux
01S0
Mux
Mux
01S0
Mux
Mux
01S0
Mux
Mux
Instruction [25-0]
Instruction [25-0]
Text is not SVG - cannot display
\ No newline at end of file +
ALUOp
ALUOp
ALUSrc
ALUSrc
RegDst
RegDst
PC
PC
Instruction Memory
Instruction Memory
Sign-Extend
Sign-Extend
Shift Left 2
Shift Left 2
ALU Control
ALU Control
Instruction [5-0] (funct)
Instruction [5-0] (funct)
Instruction [15-0] (imm)
Instruction [15-0] (imm)
Add
Add
Add
Add
01S0
Mux
Mux
Relative PC Branch
Relative PC Branch
PC + 4
PC + 4
Control Unit
Control Unit
Instruction
Instruction
Branch
Branch
MemRead
MemRead
MemToReg
MemToReg
MemWrite
MemWrite
RegWrite
RegWrite
Shift Left 2
Shift Left 2
Jump Address
Jump Address
Jump
Jump
PC + 4 [63-28]
PC + 4 [63-28]
Registers
Registers
Read Register 1
Read Register 1
Read Register 2
Read Register 2
Write Register
Write Register
Write Data
Write Data
Read Data 1
Read Data 1
Read Data 2
Read Data 2
Data Memory
Data Memory
Write Data
Write Data
Address
Address
Read Data
Read Data
Instruction [25-21] (rs or base)
Instruction [25-21] (rs or base)
Instruction [15-11] (rd)
Instruction [15-11] (rd)
Instruction [20-16] (rt)
Instruction [20-16] (rt)
Instruction Fetch
Instruction Fetch
Instruction Decode / Register Read
Instruction Decode / Register Read
Execute
Execute
Memory
Memory
Writeback
Writeback
4
4
ALU
ALU
Zero
Zero
01S0
Mux
Mux
01S0
Mux
Mux
01S0
Mux
Mux
01S0
Mux
Mux
Instruction [25-0]
Instruction [25-0]
Text is not SVG - cannot display
\ No newline at end of file diff --git a/static/folder-upload.svg b/static/folder-upload.svg new file mode 100644 index 000000000..5302c6539 --- /dev/null +++ b/static/folder-upload.svg @@ -0,0 +1,7 @@ + + +circle-left + + + + diff --git a/static/forward.svg b/static/forward.svg new file mode 100644 index 000000000..c074308cc --- /dev/null +++ b/static/forward.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/static/github-mark-white.svg b/static/github-mark-white.svg new file mode 100644 index 000000000..d5e649185 --- /dev/null +++ b/static/github-mark-white.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/hammer.svg b/static/hammer.svg new file mode 100644 index 000000000..98f283f0d --- /dev/null +++ b/static/hammer.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/static/next.svg b/static/next.svg new file mode 100644 index 000000000..848d571b5 --- /dev/null +++ b/static/next.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/static/pause.svg b/static/pause.svg new file mode 100644 index 000000000..692adbb17 --- /dev/null +++ b/static/pause.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/static/play.svg b/static/play.svg new file mode 100644 index 000000000..f3832db46 --- /dev/null +++ b/static/play.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/static/previous.svg b/static/previous.svg new file mode 100644 index 000000000..869c4e4af --- /dev/null +++ b/static/previous.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/static/rust-logo-blk.svg b/static/rust-logo-blk.svg new file mode 100644 index 000000000..1a6c762d4 --- /dev/null +++ b/static/rust-logo-blk.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/stop.svg b/static/stop.svg new file mode 100644 index 000000000..27be07d39 --- /dev/null +++ b/static/stop.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/static/styles/main.css b/static/styles/main.css index 26024710a..535f00c6d 100644 --- a/static/styles/main.css +++ b/static/styles/main.css @@ -1,108 +1,70 @@ body { background-color: #474343; + color: #aeaeae; font-family: 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif; } -/****** Buttons ******/ -.buttons { - display: inline-block; - margin: 0 auto; -} -.buttons .button:first-child { - border-radius: 300px 0 0 300px; -} -.buttons .button:last-child { - border-radius: 0 300px 300px 0; -} -.buttons .button:only-child { - border-radius: 300px; -} -.button { - overflow: hidden; - background-color: rgb(255, 255, 255); - padding: 2px 16px 2px 16px; - color: black; - text-align: center; - display: inline-block; - cursor: pointer; - transition-duration: 0.3s; -} -.button:hover:not(:disabled), .button:hover:not([disabled]) { - opacity: 1; - background-color: gray; -} -.button:disabled, .button[disabled] { - border-color: black; - border-style: solid; - background-color: #222; - color: #777; -} -.button:active { - background-color: #2e2b2b; -} - /****** Editor ******/ .editor { - max-height: 1px; max-width: 1px; - min-height: 100%; + min-height: 0; min-width: 100%; + flex-grow: 1; } -.myInlineDecoration { - color: rgb(49, 212, 144) !important; +.executedLine { + color: rgb(0 255 149) !important; font-weight: bold; + /* animation-name: flash_executed; + animation-duration: 2s; + animation-fill-mode: forwards; */ } - -/****** Console ******/ -.console { - border: 3px groove #ccc; - background: #012456; - color: #ccc; - overflow-y: auto; - overflow-wrap: break-word; - word-break: break-all; - padding-top: 25px; - padding-right: 25px; - padding-bottom: 25px; - padding-left: 25px; - min-height: max(20%, 8em); - max-height: 50%; -} - -.memory-view { - font-size: 1rem; -} - -.datapath-wrapper { - flex-grow: 1.4; - border: 2px solid black; - overflow: auto; - width: 100%; - flex-basis: 50%; - background-color: #FFF; +.updatedLine { + /* color: rgb(49, 212, 144) !important; */ + font-weight: bold; + animation-name: flash_updated; + animation-duration: 1s; + animation-timing-function: ease-in; } -.button-bar { - width: 100%; +.highlightHex { + background-color: rgb(71 188 255 / 41%); } -.button-bar > .tabs + .buttons { - margin-left: 8px; +@keyframes flash_executed { + from { + color: transparent; + } + to { + color: transparent; + } + to { + color: rgb(0 255 149); + } } -/****** Register View ******/ -.table-wrapper { - border: 1px solid black; - overflow-y: auto; +@keyframes flash_updated { + from { + color: red; + } + to { + color: red; + } + to { + color: white; + } } -thead { - background-color: #006591; - color: #fff; +/****** Tables ******/ +th { + background-color: #303030; } th, td { - border: 1px solid black; padding: 2px 16px 2px 16px; + text-wrap: nowrap; +} + +th:not(:last-child), td:not(:last-child) { + border-right: 1px solid black; } table { @@ -110,12 +72,15 @@ table { border-spacing: 0; display: block; overflow-y: auto; + overflow-x: hidden; } -tr:nth-child(even) -{ - background-color: #ccc; -} +tr { + background-color: #1e1e1e; + input { + background-color: #1e1e1e; + } +} td:nth-child(2) { font-family: 'Iosevka', 'Droid Sans Mono', 'Consolas', monospace; } @@ -128,4 +93,4 @@ th:not(:last-child), td:not(:last-child) { /* Make the last column expand to the entire table. */ th:last-child, td:last-child { width: 100%; -} +} \ No newline at end of file diff --git a/static/styles/tabs.css b/static/styles/tabs.css deleted file mode 100644 index 8c1be472f..000000000 --- a/static/styles/tabs.css +++ /dev/null @@ -1,42 +0,0 @@ -.tabs { - display: inline-block; - margin: 0 auto; -} -.tabs .tab:first-child { - border-radius: 300px 0 0 300px; -} -.tabs .tab:last-child { - border-radius: 0 300px 300px 0; -} -.tabs .tab:only-child { - border-radius: 300px; -} -.tab { - overflow: hidden; - background-color: rgb(255, 255, 255); - padding: 2px 16px 2px 16px; - color: black; - text-align: center; - display: inline-block; - cursor: pointer; - transition-duration: 0.3s; -} -.tab:hover { - opacity: 1; - background-color: gray; -} -.tab:focus, -.tab:active { - background-color: #2e2b2b !important; - color: white !important; -} -.tab::after { - background-color: #2e2b2b; -} -.pressed { - background-color: #2e2b2b ; - padding: 2px 16px 2px 16px; - color: white; - text-align: center; - display: inline-block; -} diff --git a/static/styles/tailwind.css b/static/styles/tailwind.css new file mode 100644 index 000000000..bd6213e1d --- /dev/null +++ b/static/styles/tailwind.css @@ -0,0 +1,3 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; \ No newline at end of file diff --git a/static/styles/visual_datapath.css b/static/styles/visual_datapath.css index d36c624e5..fd3b80c10 100644 --- a/static/styles/visual_datapath.css +++ b/static/styles/visual_datapath.css @@ -6,6 +6,71 @@ width: auto; } +/** Zoom Levels **/ +.datapath.size-0 { + width: 100%; +} +.datapath.size-10 { + width: 110%; +} +.datapath.size-20 { + width: 120%; +} +.datapath.size-30 { + width: 130%; +} +.datapath.size-40 { + width: 140%; +} +.datapath.size-50 { + width: 150%; +} +.datapath.size-60 { + width: 160%; +} +.datapath.size-70 { + width: 170%; +} +.datapath.size-80 { + width: 180%; +} +.datapath.size-90 { + width: 190%; +} +.datapath.size-100 { + width: 200%; +} +.datapath.size-110 { + width: 210%; +} +.datapath.size-120 { + width: 220%; +} +.datapath.size-130 { + width: 230%; +} +.datapath.size-140 { + width: 240%; +} +.datapath.size-150 { + width: 250%; +} +.datapath.size-160 { + width: 260%; +} +.datapath.size-170 { + width: 270%; +} +.datapath.size-180 { + width: 280%; +} +.datapath.size-190 { + width: 290%; +} +.datapath.size-200 { + width: 300%; +} + #popup { display: none; position: absolute; diff --git a/tailwind.config.js b/tailwind.config.js new file mode 100644 index 000000000..1242d25fb --- /dev/null +++ b/tailwind.config.js @@ -0,0 +1,49 @@ +module.exports = { + content: { + files: ["*.html", "./src/**/*.rs"], + }, + darkMode: 'class', // Enables dark mode based on class + theme: { + extend: { + colors: { + light: { + 100: '#474343' + }, + primary: { + 900: '#1e1e1e', + 800: '#303030', + 700: '#383838', + 600: '#474343', + 500: '#4e4e4e', + 400: '#616161', + 300: '#aeaeae', + 200: '#bbbbbb', + 100: '#ffffff' + }, + accent: { + red: { + 200: '#C08686', + 100: '#FFB0B0', + }, + green: { + 300: '#5F894D', + 200: '#74a770', + 100: '#9ee19a', + }, + blue: { + 400: '#0075FF', + 300: '#012456', + 200: '#006591', + 100: '#0178ab', + } + }, + console: { + bg: '#012456' + } + }, + boxShadow: { + 'executing': 'inset 0px 0px 20px 0px #12ff77', + } + }, + } +} \ No newline at end of file