Compare commits
56 Commits
c03ac42eec
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 128a53bd3b | |||
| 689f4b77fd | |||
| 96c3a514f2 | |||
| e97b4c4ee4 | |||
| a51b6589f0 | |||
| cebc8fcc5b | |||
| b664538923 | |||
| 3525db5299 | |||
| 8d7cb5e6f3 | |||
| ced0e13f6b | |||
| c6b58dbc21 | |||
| af111c5992 | |||
| b5962c6b50 | |||
| ff6427b020 | |||
| c72e133cde | |||
| c844f8d806 | |||
| 5c5d8471fa | |||
| 94120273bb | |||
| 978bb30fdb | |||
| 5dd9328008 | |||
| f6cc53075a | |||
| 8fa56c5b6f | |||
| 902ed046ca | |||
| f17b849da2 | |||
| d04b6f35c3 | |||
| 47efeef83d | |||
| 20f7c289ed | |||
| 30a7d1c5e4 | |||
| 7713564d79 | |||
| 56fcfb5230 | |||
| a43277c159 | |||
| 410c54c2f8 | |||
| 899bb90230 | |||
| 53966ad22f | |||
| 4e23ac7bff | |||
| cd2e5319bc | |||
| fc3b588425 | |||
| 636ff0a430 | |||
| fb6722a47e | |||
| e385288e70 | |||
| 2d50702acc | |||
| 8cb41c61cd | |||
| c38bb22e88 | |||
| aead858727 | |||
| 3f97a352cc | |||
| d762e1449b | |||
| 2741332630 | |||
| 21e534378f | |||
| e277c11eef | |||
| e747425381 | |||
| c7ce6f6e69 | |||
| 04b428612f | |||
| 149a0d6144 | |||
| f718f94850 | |||
| 034620591b | |||
| a68d72621c |
2
.cargo/config.toml
Normal file
2
.cargo/config.toml
Normal file
@@ -0,0 +1,2 @@
|
||||
[build]
|
||||
rustflags = ["-Ctarget-cpu=native"]
|
||||
7
.gitignore
vendored
7
.gitignore
vendored
@@ -10,9 +10,4 @@ target/
|
||||
# MSVC Windows builds of rustc generate these, which store debugging information
|
||||
*.pdb
|
||||
|
||||
# RustRover
|
||||
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
||||
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||
#.idea/
|
||||
files
|
||||
|
||||
663
Cargo.lock
generated
663
Cargo.lock
generated
@@ -31,6 +31,15 @@ dependencies = [
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "allocator-api2"
|
||||
version = "0.2.21"
|
||||
@@ -73,6 +82,56 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"anstyle-parse",
|
||||
"anstyle-query",
|
||||
"anstyle-wincon",
|
||||
"colorchoice",
|
||||
"is_terminal_polyfill",
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle"
|
||||
version = "1.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000"
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-parse"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e"
|
||||
dependencies = [
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-query"
|
||||
version = "1.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc"
|
||||
dependencies = [
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-wincon"
|
||||
version = "3.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"once_cell_polyfill",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "arrayref"
|
||||
version = "0.3.9"
|
||||
@@ -100,6 +159,14 @@ dependencies = [
|
||||
"libloading 0.7.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "asm"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"parse_int",
|
||||
"regex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atomic-waker"
|
||||
version = "1.1.2"
|
||||
@@ -138,6 +205,9 @@ name = "bitflags"
|
||||
version = "2.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block"
|
||||
@@ -200,9 +270,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.56"
|
||||
version = "1.2.57"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2"
|
||||
checksum = "7a0dd1ca384932ff3641c8718a02769f1698e7563dc6974ffd03346116310423"
|
||||
dependencies = [
|
||||
"find-msvc-tools",
|
||||
"jobserver",
|
||||
@@ -234,6 +304,92 @@ version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0"
|
||||
dependencies = [
|
||||
"iana-time-zone",
|
||||
"num-traits",
|
||||
"serde",
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b193af5b67834b676abd72466a96c1024e6a6ad978a1f484bd90b85c94041351"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap-repl"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4ffd4e57297ee7a10fb637964a79fd4bf5c8d22fd4ceaac5d3964d55df9e9e38"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"clap_complete",
|
||||
"console",
|
||||
"nu-ansi-term",
|
||||
"reedline",
|
||||
"shlex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
"clap_lex 1.1.0",
|
||||
"strsim",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_complete"
|
||||
version = "4.5.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c677cd0126f3026d8b093fa29eae5d812fde5c05bc66dbb29d0374eea95113a"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"clap_lex 0.7.7",
|
||||
"is_executable",
|
||||
"pathdiff",
|
||||
"shlex",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1110bd8a634a1ab8cb04345d8d878267d57c3cf1b38d91b71af6686408bbca6a"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.117",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.7.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3e64b0cc0439b12df2fa678eae89a1c56a529fd067a9115f7827f1fffd22b32"
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9"
|
||||
|
||||
[[package]]
|
||||
name = "codespan-reporting"
|
||||
version = "0.11.1"
|
||||
@@ -241,9 +397,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e"
|
||||
dependencies = [
|
||||
"termcolor",
|
||||
"unicode-width",
|
||||
"unicode-width 0.1.14",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570"
|
||||
|
||||
[[package]]
|
||||
name = "com"
|
||||
version = "0.6.0"
|
||||
@@ -294,6 +456,19 @@ dependencies = [
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "console"
|
||||
version = "0.15.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "054ccb5b10f9f2cbf51eb355ca1d05c2d279ce1804688d0db74b4733a5aeafd8"
|
||||
dependencies = [
|
||||
"encode_unicode",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"unicode-width 0.2.2",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation"
|
||||
version = "0.9.4"
|
||||
@@ -340,6 +515,32 @@ version = "0.8.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
|
||||
|
||||
[[package]]
|
||||
name = "crossterm"
|
||||
version = "0.28.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6"
|
||||
dependencies = [
|
||||
"bitflags 2.11.0",
|
||||
"crossterm_winapi",
|
||||
"mio",
|
||||
"parking_lot",
|
||||
"rustix 0.38.44",
|
||||
"serde",
|
||||
"signal-hook",
|
||||
"signal-hook-mio",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossterm_winapi"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cursor-icon"
|
||||
version = "1.2.0"
|
||||
@@ -384,6 +585,18 @@ version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76"
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
|
||||
|
||||
[[package]]
|
||||
name = "encode_unicode"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0"
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.2"
|
||||
@@ -400,6 +613,17 @@ dependencies = [
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fd-lock"
|
||||
version = "4.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ce92ff622d6dadf7349484f42c93271a0d49b7cc4d466a936405bacbe10aa78"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"rustix 1.1.4",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "find-msvc-tools"
|
||||
version = "0.1.9"
|
||||
@@ -594,6 +818,12 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.5.2"
|
||||
@@ -606,6 +836,30 @@ version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df"
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone"
|
||||
version = "0.1.65"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470"
|
||||
dependencies = [
|
||||
"android_system_properties",
|
||||
"core-foundation-sys",
|
||||
"iana-time-zone-haiku",
|
||||
"js-sys",
|
||||
"log",
|
||||
"wasm-bindgen",
|
||||
"windows-core 0.62.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone-haiku"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.13.0"
|
||||
@@ -616,6 +870,30 @@ dependencies = [
|
||||
"hashbrown 0.16.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "is_executable"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baabb8b4867b26294d818bf3f651a454b6901431711abb96e296245888d6e8c4"
|
||||
dependencies = [
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "is_terminal_polyfill"
|
||||
version = "1.70.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695"
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jni"
|
||||
version = "0.21.1"
|
||||
@@ -677,9 +955,9 @@ checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.182"
|
||||
version = "0.2.183"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112"
|
||||
checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d"
|
||||
|
||||
[[package]]
|
||||
name = "libloading"
|
||||
@@ -779,6 +1057,18 @@ dependencies = [
|
||||
"paste",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "1.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"wasi",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "naga"
|
||||
version = "0.19.2"
|
||||
@@ -838,6 +1128,15 @@ dependencies = [
|
||||
"jni-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nu-ansi-term"
|
||||
version = "0.50.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
|
||||
dependencies = [
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.19"
|
||||
@@ -849,9 +1148,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "num_enum"
|
||||
version = "0.7.5"
|
||||
version = "0.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1207a7e20ad57b847bbddc6776b968420d38292bbfe2089accff5e19e82454c"
|
||||
checksum = "5d0bca838442ec211fa11de3a8b0e0e8f3a4522575b5c4c06ed722e005036f26"
|
||||
dependencies = [
|
||||
"num_enum_derive",
|
||||
"rustversion",
|
||||
@@ -859,9 +1158,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "num_enum_derive"
|
||||
version = "0.7.5"
|
||||
version = "0.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ff32365de1b6743cb203b710788263c44a03de03802daf96092f2da4fe6ba4d7"
|
||||
checksum = "680998035259dcfcafe653688bf2aa6d3e2dc05e98be6ab46afb089dc84f1df8"
|
||||
dependencies = [
|
||||
"proc-macro-crate",
|
||||
"proc-macro2",
|
||||
@@ -1093,15 +1392,21 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.21.3"
|
||||
version = "1.21.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
||||
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell_polyfill"
|
||||
version = "1.70.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
|
||||
|
||||
[[package]]
|
||||
name = "orbclient"
|
||||
version = "0.3.50"
|
||||
version = "0.3.51"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "52ad2c6bae700b7aa5d1cc30c59bdd3a1c180b09dbaea51e2ae2b8e1cf211fdd"
|
||||
checksum = "59aed3b33578edcfa1bc96a321d590d31832b6ad55a26f0313362ce687e9abd6"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"libredox",
|
||||
@@ -1139,12 +1444,27 @@ dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parse_int"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1c464266693329dd5a8715098c7f86e6c5fd5d985018b8318f53d9c6c2b21a31"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "paste"
|
||||
version = "1.0.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
|
||||
|
||||
[[package]]
|
||||
name = "pathdiff"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3"
|
||||
|
||||
[[package]]
|
||||
name = "percent-encoding"
|
||||
version = "2.3.2"
|
||||
@@ -1316,6 +1636,55 @@ dependencies = [
|
||||
"bitflags 2.11.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "reedline"
|
||||
version = "0.38.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9bfa8cb0ad84c396c936d8abb814703d7042a433d2da75a0c7060cbdc89109f3"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"crossterm",
|
||||
"fd-lock",
|
||||
"itertools",
|
||||
"nu-ansi-term",
|
||||
"serde",
|
||||
"strip-ansi-escapes",
|
||||
"strum",
|
||||
"strum_macros",
|
||||
"thiserror",
|
||||
"unicode-segmentation",
|
||||
"unicode-width 0.1.14",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-automata",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a"
|
||||
|
||||
[[package]]
|
||||
name = "renderdoc-sys"
|
||||
version = "1.1.0"
|
||||
@@ -1410,6 +1779,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1439,10 +1809,45 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||
|
||||
[[package]]
|
||||
name = "sim"
|
||||
name = "signal-hook"
|
||||
version = "0.3.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"signal-hook-registry",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-mio"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b75a19a7a740b25bc7944bdee6172368f988763b744e3d4dfe753f6b4ece40cc"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"mio",
|
||||
"signal-hook",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
version = "1.4.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b"
|
||||
dependencies = [
|
||||
"errno",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "simu"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"clap-repl",
|
||||
"parse_int",
|
||||
"pixels",
|
||||
"wait_on_address",
|
||||
"winit",
|
||||
"winit_input_helper",
|
||||
]
|
||||
@@ -1523,6 +1928,40 @@ version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731"
|
||||
|
||||
[[package]]
|
||||
name = "strip-ansi-escapes"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2a8f8038e7e7969abb3f1b7c2a811225e9296da208539e0f79c5251d6cac0025"
|
||||
dependencies = [
|
||||
"vte",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||
|
||||
[[package]]
|
||||
name = "strum"
|
||||
version = "0.26.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06"
|
||||
|
||||
[[package]]
|
||||
name = "strum_macros"
|
||||
version = "0.26.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustversion",
|
||||
"syn 2.0.117",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.109"
|
||||
@@ -1601,18 +2040,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toml_datetime"
|
||||
version = "1.0.0+spec-1.1.0"
|
||||
version = "1.0.1+spec-1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32c2555c699578a4f59f0cc68e5116c8d7cabbd45e1409b989d4be085b53f13e"
|
||||
checksum = "9b320e741db58cac564e26c607d3cc1fdc4a88fd36c879568c07856ed83ff3e9"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
version = "0.25.4+spec-1.1.0"
|
||||
version = "0.25.5+spec-1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7193cbd0ce53dc966037f54351dbbcf0d5a642c7f0038c382ef9e677ce8c13f2"
|
||||
checksum = "8ca1a40644a28bce036923f6a431df0b34236949d111cc07cb6dca830c9ef2e1"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"toml_datetime",
|
||||
@@ -1622,9 +2061,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toml_parser"
|
||||
version = "1.0.9+spec-1.1.0"
|
||||
version = "1.0.10+spec-1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "702d4415e08923e7e1ef96cd5727c0dfed80b4d2fa25db9647fe5eb6f7c5a4c4"
|
||||
checksum = "7df25b4befd31c4816df190124375d5a20c6b6921e2cad937316de3fccd63420"
|
||||
dependencies = [
|
||||
"winnow",
|
||||
]
|
||||
@@ -1678,18 +2117,53 @@ version = "0.1.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||
|
||||
[[package]]
|
||||
name = "vte"
|
||||
version = "0.14.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "231fdcd7ef3037e8330d8e17e61011a2c244126acc0a982f4040ac3f9f0bc077"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wait_on_address"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0f46f0d222fdc7fac38a2ad9f0fabdc5af18d0c9d12c5fc52438509cf88674cf"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"libc",
|
||||
"rustversion",
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "walkdir"
|
||||
version = "2.5.0"
|
||||
@@ -1700,6 +2174,12 @@ dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.1+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
|
||||
|
||||
[[package]]
|
||||
name = "wasip2"
|
||||
version = "1.0.2+wasi-0.2.9"
|
||||
@@ -2057,7 +2537,7 @@ version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be"
|
||||
dependencies = [
|
||||
"windows-core",
|
||||
"windows-core 0.52.0",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
@@ -2070,12 +2550,65 @@ dependencies = [
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-core"
|
||||
version = "0.62.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb"
|
||||
dependencies = [
|
||||
"windows-implement",
|
||||
"windows-interface",
|
||||
"windows-link",
|
||||
"windows-result",
|
||||
"windows-strings",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-implement"
|
||||
version = "0.60.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.117",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-interface"
|
||||
version = "0.59.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.117",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-link"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
||||
|
||||
[[package]]
|
||||
name = "windows-result"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-strings"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.45.0"
|
||||
@@ -2103,6 +2636,15 @@ dependencies = [
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.60.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
|
||||
dependencies = [
|
||||
"windows-targets 0.53.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.61.2"
|
||||
@@ -2136,13 +2678,30 @@ dependencies = [
|
||||
"windows_aarch64_gnullvm 0.52.6",
|
||||
"windows_aarch64_msvc 0.52.6",
|
||||
"windows_i686_gnu 0.52.6",
|
||||
"windows_i686_gnullvm",
|
||||
"windows_i686_gnullvm 0.52.6",
|
||||
"windows_i686_msvc 0.52.6",
|
||||
"windows_x86_64_gnu 0.52.6",
|
||||
"windows_x86_64_gnullvm 0.52.6",
|
||||
"windows_x86_64_msvc 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.53.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
"windows_aarch64_gnullvm 0.53.1",
|
||||
"windows_aarch64_msvc 0.53.1",
|
||||
"windows_i686_gnu 0.53.1",
|
||||
"windows_i686_gnullvm 0.53.1",
|
||||
"windows_i686_msvc 0.53.1",
|
||||
"windows_x86_64_gnu 0.53.1",
|
||||
"windows_x86_64_gnullvm 0.53.1",
|
||||
"windows_x86_64_msvc 0.53.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.42.2"
|
||||
@@ -2155,6 +2714,12 @@ version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.42.2"
|
||||
@@ -2167,6 +2732,12 @@ version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.42.2"
|
||||
@@ -2179,12 +2750,24 @@ version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.42.2"
|
||||
@@ -2197,6 +2780,12 @@ version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.42.2"
|
||||
@@ -2209,6 +2798,12 @@ version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.42.2"
|
||||
@@ -2221,6 +2816,12 @@ version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.42.2"
|
||||
@@ -2233,6 +2834,12 @@ version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"
|
||||
|
||||
[[package]]
|
||||
name = "winit"
|
||||
version = "0.30.13"
|
||||
@@ -2297,9 +2904,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.7.15"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945"
|
||||
checksum = "a90e88e4667264a994d34e6d1ab2d26d398dcdca8b7f52bec8668957517fc7d8"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
@@ -2375,18 +2982,18 @@ checksum = "3ae8337f8a065cfc972643663ea4279e04e7256de865aa66fe25cec5fb912d3f"
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.8.40"
|
||||
version = "0.8.47"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a789c6e490b576db9f7e6b6d661bcc9799f7c0ac8352f56ea20193b2681532e5"
|
||||
checksum = "efbb2a062be311f2ba113ce66f697a4dc589f85e78a4aea276200804cea0ed87"
|
||||
dependencies = [
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.8.40"
|
||||
version = "0.8.47"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f65c489a7071a749c849713807783f70672b28094011623e200cb86dcb835953"
|
||||
checksum = "0e8bc7269b54418e7aeeef514aa68f8690b8c0489a06b0136e5f57c4c5ccab89"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
||||
14
Cargo.toml
14
Cargo.toml
@@ -1,9 +1,7 @@
|
||||
[package]
|
||||
name = "sim"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
[workspace]
|
||||
resolver = "3"
|
||||
members = ["simu","asm"]
|
||||
|
||||
[dependencies]
|
||||
pixels = "0.15.0"
|
||||
winit = "0.30.13"
|
||||
winit_input_helper = "0.17.0"
|
||||
[profile.release]
|
||||
opt-level = 2
|
||||
# panic = "abort"
|
||||
|
||||
115
README.md
115
README.md
@@ -1,3 +1,114 @@
|
||||
# bisare_sim_rs
|
||||
# Assembleur
|
||||
pour assembler un fichier:
|
||||
- depuis ce dossier
|
||||
`cargo run --release -p asm fichierentre [fichiersortie]`
|
||||
- depuis le sous dossier asm
|
||||
`cargo run --release fichierentre [fichiersortie]`
|
||||
|
||||
Un petit simulateur de processeur pour les cours de L3 de l'ENS de Lyon
|
||||
syntaxe supplémentaire supportée:
|
||||
- `let rx label`
|
||||
assigne l'adresse de label a rx
|
||||
- `D valeur`
|
||||
rentre la donné brute égale à la valeur (immédiate ou addresse de label)
|
||||
(pour mettre des données dans le fichier et pas du code executable)
|
||||
- `eint` Active les interruptions
|
||||
- `dint` Desactive les interruptions
|
||||
- `swi` Active l'interruption swi
|
||||
- `reti` Retourne depuis le handler d'interruption
|
||||
- `umull` multiplication non signée usuelle
|
||||
- `umulh` multiplication non signée, moitié haute du résultat 64 bits
|
||||
- `smull` et `sumlh` versions signée de la multiplication
|
||||
- `div` division non signée
|
||||
- `mod` modulo non signé
|
||||
|
||||
# Simulateur
|
||||
## pour lancer la simulation:
|
||||
- depuis ce dossier
|
||||
`cargo run --release -p simu fichierentre`
|
||||
- depuis le sous dossier simu
|
||||
`cargo run --release fichierentre`
|
||||
|
||||
## Features
|
||||
rajouter --features=[liste séparé par des virgules]
|
||||
- div_mul: Support des instruction de multiplication / division
|
||||
- rgba: Écran au format RGBA plutot que 0BGR (alpha ignoré mais stocké)
|
||||
- rich_keyboard: rajoute trois champs de mmio pour le clavier. Voir MMIO
|
||||
- debug: repl similaire a la version python du simulateur
|
||||
- futex: accélère le programme. ne marche pas sur les macs. actif par default, utiliser --no-default-features pour le desactiver
|
||||
|
||||
### instruction spéciale:
|
||||
halt (jump 0) met le programme en pause, mais on peut se reveiller par des interuptions
|
||||
|
||||
call 0 termine l'exécution du simulateur proprement
|
||||
|
||||
## mmio:
|
||||
- 0x01000000 à 0x0112c000 : Écran en lecture/écriture. un pixel par 32bits, de gauche a droite puis de bas en haut.
|
||||
Format de pixel en 0BGR, 4 bits par couleur.
|
||||
Passage au format RGBA (alpha ignoré, mais stocké) avec la feature rgba
|
||||
- 0x01200000 : Clavier (scancode) (lecture seule)
|
||||
- 0x01200004 : Horloge (millisecondes écoulé depuis le début de la simulation, wrappe tout les 49 jours) (lecture seule)
|
||||
- 0x01200008 : Boutons de la souris (OR des boutons préssés) Gauche = 1, Droit=2, Clic Molette = 3, Autres non testé (lecture seule)
|
||||
- 0x0120000c : Position horizontale de la souris (en pixels, -1 si hors de l'écran) (lecture seule)
|
||||
- 0x01200010 : Position verticale de la souris (en pixels, -1 si hors de l'écran) (lecture seule)
|
||||
- 0x01201000 : Activation des Interupts de MMIO. Remis a zero par `dint`. Attention, Écrire ici sans activer les interruptions va bloquer les intéruptions et l'affichage (écriture seule)
|
||||
|
||||
|
||||
Si la feature rich_keyboard est active, tout en lecture seule:
|
||||
- 0x01200000 - 12 : Clavier : Representation ascii multichar de la touche pressé, modifié par Alt, Ctrl ... (eg ascii(a), ascii(A), ascii(^a)) l'unicode peut faire des val > 255
|
||||
- 0x01200000 - 8 : Clavier Representation ascii multichar non modifié (eg ascii(a), ascii(\r)) l'unicode peut faire des val > 255
|
||||
- 0x01200000 - 4: Clavier (press | repeat << 1) press: 1 si appuis, 0 si relache. repeat: 1 si l'appuis vient d'une répétition due a l'OS
|
||||
- 0x01200000 : Clavier (scancode, comme précédement) (lecture seule)
|
||||
|
||||
## Interuptions:
|
||||
Si les interruptions sont activées (avec eint), qu'il y a une raison de faire une interruption,
|
||||
et qu'on n'est pas déjà dans une routine d'interruption de priorité >=,
|
||||
alors le cpu effectue les actions suivantes:
|
||||
|
||||
Finis l'instruction en cours si c'est une interruption externe
|
||||
si interruption a des arguments, les mets dans les registres par ordre croissant,
|
||||
Mets PC a (priorité de l'intéruption)*4 (instruction n°priorité)
|
||||
|
||||
Cela veut dire que votre début de programme, si vous voulez utiliser les interruption, doit être une table de jump vers les handler correspondant
|
||||
|
||||
Pour sortir d'une interuption, utilisez `reti`
|
||||
*Il FAUT sortir avec un reti*
|
||||
|
||||
*Les interuptions utilisent la stack, donc lorsque les interuptions sont activés, il est interdit de stocker des information au dessus du pointeur de stack.
|
||||
de même, `reti` ne peut pas être appellé par une sous fonction appellée depuis l'interrupt handler*
|
||||
|
||||
Les intéruptions et leurs arguments, par priorité croissante:
|
||||
### 0: Point d'entrée
|
||||
C'est ici que le programme commence. Seul cas d'utilisation
|
||||
(ce n'est pas vraiment une interruption, juste la première entrée dans la jump table)
|
||||
### 1: MMIO
|
||||
Doivent être activé par le MMIO a l'adresse 0x01201000, en y écrivant le OR des (1<<identifiant)
|
||||
On toujours pour argument (r0) leur identifiant
|
||||
Les mmio corespondant sont modifié avant d'entrer dans l'intéruption, et TOUT LES MMIO d'une interuption active sont bloqués tant qu'on n'est pas sorti
|
||||
- 1 : Clavier
|
||||
- 2 : Boutons de la souris
|
||||
- 3 : Mouvements de la souris
|
||||
- 4 : Écran (rien n'est modifié, attend la sortie de l'intéruption pour afficher une frame)
|
||||
### 2: SWI
|
||||
Pas d'argument
|
||||
|
||||
### 3: Division par zero
|
||||
2 arguments: numéro de registre de sortie et valeur du dividende.
|
||||
lors de l'appel a reti, attend que r0 n'ai pas été modifié et un retour dans r1.
|
||||
la registre de destination prend la valeur retourné dans r1
|
||||
### 4: Load/Store illégal
|
||||
Un load store a été fait a une adresse non multiple de 4
|
||||
3 aruments:
|
||||
si c'est un load: 0, addresse, numéro de registre de retour
|
||||
si c'est un store: 1, addresse, valeure a stocker
|
||||
ne modifiez pas r0, et dans le cas des load, r2. (ou restaurez avant le reti)
|
||||
Si c'est un load, le registre destination prendra la valeur de retour, attendue dans r1
|
||||
### 5: Unsupported Opcode
|
||||
Une division multiplication alors qu'elle ne sont pas activées
|
||||
4 arguments:
|
||||
indice de registre de retour, argument 1, argument 2, fonction
|
||||
Même mode de retour que la division par zero
|
||||
### 6: Instruction Illégale
|
||||
Une instruction non légale, ou un load / store a une adresse non mappé.
|
||||
1 argument: l'opcode de l'instruction associé
|
||||
Il est fortement recommendé d'utiliser cette intéruption pour afficher du debug, puis de quitter le programme, mais il est possible
|
||||
de continuer a l'instruction suivante à l'aide d'un `reti`
|
||||
|
||||
8
asm/Cargo.toml
Normal file
8
asm/Cargo.toml
Normal file
@@ -0,0 +1,8 @@
|
||||
[package]
|
||||
name = "asm"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
parse_int = "0.9.0"
|
||||
regex = "1.12.3"
|
||||
2
asm/rust-toolchain.toml
Normal file
2
asm/rust-toolchain.toml
Normal file
@@ -0,0 +1,2 @@
|
||||
[toolchain]
|
||||
channel = "nightly"
|
||||
699
asm/src/main.rs
Normal file
699
asm/src/main.rs
Normal file
@@ -0,0 +1,699 @@
|
||||
#![feature(file_buffered)]
|
||||
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
collections::HashMap,
|
||||
env::args,
|
||||
io::{BufRead, BufReader, Write, stdin},
|
||||
ops::Index,
|
||||
process::exit,
|
||||
};
|
||||
|
||||
use regex::Regex;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
enum Op2 {
|
||||
Direct(i32),
|
||||
Register(u8),
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct Reg(u8);
|
||||
|
||||
impl Into<u32> for Reg {
|
||||
fn into(self) -> u32 {
|
||||
self.0 as u32
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<u32> for &Reg {
|
||||
fn into(self) -> u32 {
|
||||
self.0 as u32
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
enum Cond {
|
||||
Ifeq,
|
||||
Ifne,
|
||||
Iflt,
|
||||
Ifge,
|
||||
Ifgt,
|
||||
Ifle,
|
||||
Ifult,
|
||||
Ifuge,
|
||||
Ifugt,
|
||||
Ifule,
|
||||
}
|
||||
|
||||
impl From<Cond> for u32 {
|
||||
fn from(value: Cond) -> Self {
|
||||
match value {
|
||||
Cond::Ifeq => 0b0000,
|
||||
Cond::Ifne => 0b0001,
|
||||
Cond::Iflt => 0b1000,
|
||||
Cond::Ifge => 0b1001,
|
||||
Cond::Ifgt => 0b1010,
|
||||
Cond::Ifle => 0b1011,
|
||||
Cond::Ifult => 0b1100,
|
||||
Cond::Ifuge => 0b1101,
|
||||
Cond::Ifugt => 0b1110,
|
||||
Cond::Ifule => 0b1111,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Cond> for u32 {
|
||||
fn from(value: &Cond) -> Self {
|
||||
(*value).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&str> for Cond {
|
||||
type Error = ();
|
||||
fn try_from(value: &str) -> Result<Self, ()> {
|
||||
Ok(match value {
|
||||
"ifeq" => Cond::Ifeq,
|
||||
"ifne" => Cond::Ifne,
|
||||
"iflt" => Cond::Iflt,
|
||||
"ifge" => Cond::Ifge,
|
||||
"ifgt" => Cond::Ifgt,
|
||||
"ifle" => Cond::Ifle,
|
||||
"ifult" => Cond::Ifult,
|
||||
"ifuge" => Cond::Ifuge,
|
||||
"ifugt" => Cond::Ifugt,
|
||||
"ifule" => Cond::Ifule,
|
||||
s => {
|
||||
println!("unrecognised condition: {s}");
|
||||
return Err(());
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
enum Labeli32 {
|
||||
Value(i32),
|
||||
Label(String),
|
||||
}
|
||||
|
||||
enum Labeli17 {
|
||||
Register(Reg),
|
||||
Value(i32),
|
||||
LabelLow(String),
|
||||
LabelHigh(String),
|
||||
}
|
||||
|
||||
enum Instruction {
|
||||
Copy(Reg, Labeli17),
|
||||
Add(Reg, Reg, Labeli17),
|
||||
Sub(Reg, Reg, Op2),
|
||||
Or(Reg, Reg, Op2),
|
||||
And(Reg, Reg, Op2),
|
||||
Xor(Reg, Reg, Op2),
|
||||
Lsl(Reg, Reg, Op2),
|
||||
Lsr(Reg, Reg, Op2),
|
||||
Asr(Reg, Reg, Op2),
|
||||
Smull(Reg, Reg, Op2),
|
||||
Smulh(Reg, Reg, Op2),
|
||||
Umull(Reg, Reg, Op2),
|
||||
Umulh(Reg, Reg, Op2),
|
||||
Div(Reg, Reg, Op2),
|
||||
Mod(Reg, Reg, Op2),
|
||||
Store(Reg, Op2, Reg),
|
||||
Load(Reg, Reg, Op2),
|
||||
Push(Op2),
|
||||
Pop(Reg),
|
||||
Skip(Labeli32, Cond, Reg, Op2),
|
||||
Jump(Labeli32), //address / 4
|
||||
Call(Labeli32), //address / 4
|
||||
Ret(),
|
||||
Reti(),
|
||||
Swi(),
|
||||
Dint(),
|
||||
Eint(),
|
||||
GetStack(Reg),
|
||||
SetStack(Op2),
|
||||
Data(Labeli32),
|
||||
}
|
||||
|
||||
fn encode_reg(fmt: u32, function: u32, d: u32, x: u32, y: u32) -> u32 {
|
||||
(fmt << 30) | (function << 24) | (d << 20) | (x << 16) | (y << 12)
|
||||
}
|
||||
fn encode_imm(fmt: u32, function: u32, d: u32, x: u32, c: i32) -> u32 {
|
||||
let c_sign = if c < 0 { 1 } else { 0 };
|
||||
let c_mask = c as u32 & 0xFFFF;
|
||||
(fmt << 30)
|
||||
| (c_sign << 29)
|
||||
| (1 << 28)
|
||||
| (function << 24)
|
||||
| (d << 20)
|
||||
| (x << 16)
|
||||
| (c_mask << 0)
|
||||
}
|
||||
|
||||
fn encode_op2(fmt: u32, function: u32, d: u32, x: u32, y_or_c: Op2) -> u32 {
|
||||
match y_or_c {
|
||||
Op2::Direct(c) => encode_imm(fmt, function, d, x, c),
|
||||
Op2::Register(y) => encode_reg(fmt, function, d, x, y as u32),
|
||||
}
|
||||
}
|
||||
|
||||
impl Instruction {
|
||||
fn encode(&self, prgm: &Program, self_addr: u32) -> Result<u32, ()> {
|
||||
Ok(match self {
|
||||
Instruction::Copy(dest, value) => match value {
|
||||
Labeli17::Value(v) => encode_imm(1, 0, dest.into(), 0, *v),
|
||||
Labeli17::LabelLow(label) => {
|
||||
encode_imm(1, 0, dest.into(), 0, (prgm[label] & 0xFFFF) as i32)
|
||||
}
|
||||
Labeli17::LabelHigh(label) => {
|
||||
encode_imm(1, 0, dest.into(), 0, (prgm[label] as u32 >> 16) as i32)
|
||||
}
|
||||
Labeli17::Register(reg) => encode_reg(1, 0, dest.into(), 0, reg.into()),
|
||||
},
|
||||
Instruction::Add(reg, reg1, value) => match value {
|
||||
Labeli17::Register(regy) => encode_reg(1, 1, reg.into(), reg1.into(), regy.into()),
|
||||
|
||||
Labeli17::Value(v) => encode_imm(1, 1, reg.into(), reg1.into(), *v),
|
||||
Labeli17::LabelLow(l) => {
|
||||
encode_imm(1, 1, reg.into(), reg1.into(), (prgm[l] & 0xFFFF) as i32)
|
||||
}
|
||||
Labeli17::LabelHigh(l) => {
|
||||
encode_imm(1, 1, reg.into(), reg1.into(), (prgm[l] as u32 >> 16) as i32)
|
||||
}
|
||||
},
|
||||
Instruction::Sub(reg, reg1, op2) => encode_op2(01, 2, reg.into(), reg1.into(), *op2),
|
||||
Instruction::Or(reg, reg1, op2) => encode_op2(01, 3, reg.into(), reg1.into(), *op2),
|
||||
Instruction::And(reg, reg1, op2) => encode_op2(01, 4, reg.into(), reg1.into(), *op2),
|
||||
Instruction::Xor(reg, reg1, op2) => encode_op2(01, 5, reg.into(), reg1.into(), *op2),
|
||||
Instruction::Lsl(reg, reg1, op2) => encode_op2(01, 6, reg.into(), reg1.into(), *op2),
|
||||
Instruction::Lsr(reg, reg1, op2) => encode_op2(01, 7, reg.into(), reg1.into(), *op2),
|
||||
Instruction::Asr(reg, reg1, op2) => encode_op2(01, 8, reg.into(), reg1.into(), *op2),
|
||||
Instruction::Smull(reg, reg1, op2) => encode_op2(01, 9, reg.into(), reg1.into(), *op2),
|
||||
Instruction::Smulh(reg, reg1, op2) => encode_op2(01, 10, reg.into(), reg1.into(), *op2),
|
||||
Instruction::Umull(reg, reg1, op2) => encode_op2(01, 11, reg.into(), reg1.into(), *op2),
|
||||
Instruction::Umulh(reg, reg1, op2) => encode_op2(01, 12, reg.into(), reg1.into(), *op2),
|
||||
Instruction::Div(reg, reg1, op2) => encode_op2(01, 13, reg.into(), reg1.into(), *op2),
|
||||
Instruction::Mod(reg, reg1, op2) => encode_op2(01, 14, reg.into(), reg1.into(), *op2),
|
||||
Instruction::Store(addr, op2, val) => encode_op2(2, 0, val.into(), addr.into(), *op2),
|
||||
Instruction::Load(dest, addr, op2) => encode_op2(2, 1, dest.into(), addr.into(), *op2),
|
||||
Instruction::Push(op2) => encode_op2(2, 2, 0, 0, *op2),
|
||||
Instruction::Pop(reg) => encode_reg(2, 3, reg.into(), 0, 0),
|
||||
Instruction::Skip(labeli32, cond, reg, op2) => {
|
||||
let jump_distance = match labeli32 {
|
||||
Labeli32::Value(v) => *v as u32,
|
||||
Labeli32::Label(label) => {
|
||||
let dest = prgm[label];
|
||||
dest.wrapping_sub(self_addr + 4) / 4
|
||||
}
|
||||
};
|
||||
if jump_distance > 15 {
|
||||
println!(
|
||||
"Error, cannot skip {jump_distance} which is more than 15 instructions"
|
||||
);
|
||||
return Err(());
|
||||
}
|
||||
encode_op2(11, cond.into(), jump_distance, reg.into(), *op2)
|
||||
}
|
||||
Instruction::Jump(labeli32) => {
|
||||
let dest = match labeli32 {
|
||||
Labeli32::Value(v) => *v as u32,
|
||||
Labeli32::Label(label) => {
|
||||
let dest = prgm[label];
|
||||
dest.wrapping_sub(self_addr) >> 2
|
||||
}
|
||||
};
|
||||
dest & 0x1FFF_FFFF
|
||||
}
|
||||
Instruction::Call(labeli32) => {
|
||||
let dest = match labeli32 {
|
||||
Labeli32::Value(v) => *v as u32,
|
||||
Labeli32::Label(label) => {
|
||||
let dest = prgm[label];
|
||||
dest.wrapping_sub(self_addr) >> 2
|
||||
}
|
||||
};
|
||||
(dest & 0x1FFF_FFFF) | (1 << 29)
|
||||
}
|
||||
Instruction::Ret() => encode_reg(2, 0b1000, 0, 0, 0),
|
||||
Instruction::Reti() => encode_reg(2, 0b1000, 0, 0, 0) | 1 << 29,
|
||||
Instruction::Swi() => encode_reg(2, 0b1001, 0, 0, 0),
|
||||
Instruction::Dint() => encode_reg(2, 0b1011, 0, 0, 0),
|
||||
Instruction::Eint() => encode_reg(2, 0b1100, 0, 0, 0),
|
||||
Instruction::GetStack(reg) => encode_reg(2, 0b1101, reg.into(), 0, 0),
|
||||
Instruction::SetStack(op2) => encode_op2(2, 0b1110, 0, 0, *op2),
|
||||
Instruction::Data(labeli32) => match labeli32 {
|
||||
Labeli32::Value(v) => *v as u32,
|
||||
Labeli32::Label(label) => prgm[label],
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct Program {
|
||||
locations: HashMap<String, u32>,
|
||||
instructions: Vec<(Instruction, usize)>,
|
||||
}
|
||||
|
||||
impl Index<&String> for &Program {
|
||||
type Output = u32;
|
||||
|
||||
fn index(&self, index: &String) -> &Self::Output {
|
||||
match self.locations.get(index) {
|
||||
Some(v) => v,
|
||||
None => {
|
||||
println!("Could not resolve label {index}");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn reg(s: &str, n: usize, l: &str) -> Reg {
|
||||
match s {
|
||||
"r0" => Reg(0),
|
||||
"r1" => Reg(1),
|
||||
"r2" => Reg(2),
|
||||
"r3" => Reg(3),
|
||||
"r4" => Reg(4),
|
||||
"r5" => Reg(5),
|
||||
"r6" => Reg(6),
|
||||
"r7" => Reg(7),
|
||||
"r8" => Reg(8),
|
||||
"r9" => Reg(9),
|
||||
"r10" => Reg(10),
|
||||
"r11" => Reg(11),
|
||||
"r12" => Reg(12),
|
||||
"r13" => Reg(13),
|
||||
"r14" => Reg(14),
|
||||
"r15" => Reg(15),
|
||||
_ => {
|
||||
println!("Error: illegal register name: {s}. Error in {l} at line {n}");
|
||||
exit(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
fn _reg(s: &str, n: usize, l: &str) -> Reg {
|
||||
reg(s, n, l)
|
||||
}
|
||||
fn op2(s: &str, n: usize, l: &str) -> Op2 {
|
||||
Op2::Register(match s {
|
||||
"r0" => 0,
|
||||
"r1" => 1,
|
||||
"r2" => 2,
|
||||
"r3" => 3,
|
||||
"r4" => 4,
|
||||
"r5" => 5,
|
||||
"r6" => 6,
|
||||
"r7" => 7,
|
||||
"r8" => 8,
|
||||
"r9" => 9,
|
||||
"r10" => 10,
|
||||
"r11" => 11,
|
||||
"r12" => 12,
|
||||
"r13" => 13,
|
||||
"r14" => 14,
|
||||
"r15" => 15,
|
||||
_ => match parse_int::parse::<i64>(s) {
|
||||
Ok(v) => {
|
||||
if -0x10000 <= v && v <= 0xFFFF {
|
||||
return Op2::Direct(v as i32);
|
||||
} else {
|
||||
println!("Error: constant {s} is too large to fit in {l} at line {n}");
|
||||
exit(1)
|
||||
}
|
||||
}
|
||||
Err(_) => {
|
||||
println!(
|
||||
"Error: neither a register name nor a constant: {s}. Error in {l} at line {n}"
|
||||
);
|
||||
exit(1)
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
fn memaddr<'a>(n: usize, l: &'a str, rgx: &RegexCollection) -> (Reg, Op2, Cow<'a, str>) {
|
||||
let f = || {
|
||||
println!("invalid memory operand in {l} at line {n}");
|
||||
exit(1);
|
||||
};
|
||||
let (rx, op) = match rgx.memop.captures(l) {
|
||||
Some(c) => {
|
||||
let rx = reg(c.get(1).unwrap().as_str(), n, l);
|
||||
let sign = c.get(2);
|
||||
match sign {
|
||||
Some(s) => {
|
||||
let sign = s.as_str();
|
||||
|
||||
let op = op2(c.get(3).unwrap().as_str(), n, l);
|
||||
match (sign, op) {
|
||||
("+", o) => (rx, o),
|
||||
("-", Op2::Direct(v)) => (rx, Op2::Direct(-v)),
|
||||
_ => f(),
|
||||
}
|
||||
}
|
||||
None => (rx, Op2::Direct(0)),
|
||||
}
|
||||
}
|
||||
None => f(),
|
||||
};
|
||||
let replace = rgx.memop.replace(l, "0");
|
||||
(rx, op, replace)
|
||||
}
|
||||
|
||||
struct RegexCollection {
|
||||
label: Regex,
|
||||
memop: Regex,
|
||||
}
|
||||
|
||||
fn process_line(prgm: &mut Program, (linenum, line): (usize, String), rgx: &RegexCollection) {
|
||||
let linenum = linenum + 1;
|
||||
let mut no_comment = line.split(';').next().unwrap_or("");
|
||||
let binding = no_comment.to_ascii_lowercase();
|
||||
no_comment = binding.as_str();
|
||||
let comma = no_comment.find(':');
|
||||
match comma {
|
||||
Some(index) => {
|
||||
let (label, next) = no_comment.split_at(index);
|
||||
let label = label.trim();
|
||||
if !rgx.label.is_match(label) {
|
||||
println!("The label {label} on line {linenum} is illegal")
|
||||
}
|
||||
prgm.locations
|
||||
.insert(label.to_string(), (prgm.instructions.len() * 4) as u32);
|
||||
no_comment = &next[1..]
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
let mut words = no_comment.split_ascii_whitespace();
|
||||
let mnemonic = words.next();
|
||||
let args: Vec<&str> = words.collect();
|
||||
if mnemonic.is_none() {
|
||||
return;
|
||||
}
|
||||
|
||||
let reg = |i| reg(args[i], linenum, &line);
|
||||
let op2 = |i| op2(args[i], linenum, &line);
|
||||
let labi32 = |i| {
|
||||
if rgx.label.is_match(args[i]) {
|
||||
Labeli32::Label((args[i] as &str).to_string())
|
||||
} else {
|
||||
Labeli32::Value(parse_int::parse::<i64>(args[i]).unwrap_or_else(|e| {
|
||||
println!(
|
||||
"Malformated lable/constant: '{}' in {line} at line {linenum}: {e}",
|
||||
args[i]
|
||||
);
|
||||
exit(1);
|
||||
}) as i32)
|
||||
}
|
||||
};
|
||||
let chk = |n| {
|
||||
if args.len() != n {
|
||||
println!(
|
||||
"Wrong number of argument for {} in {line} at line {linenum}",
|
||||
mnemonic.unwrap()
|
||||
);
|
||||
exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
let next = match mnemonic.unwrap() {
|
||||
"copy" => {
|
||||
chk(2);
|
||||
if args[0] == "sp" {
|
||||
Instruction::SetStack(op2(1))
|
||||
} else if args[1] == "sp" {
|
||||
Instruction::GetStack(reg(0))
|
||||
} else {
|
||||
Instruction::Copy(
|
||||
reg(0),
|
||||
match op2(1) {
|
||||
Op2::Direct(v) => Labeli17::Value(v),
|
||||
Op2::Register(r) => Labeli17::Register(Reg(r)),
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
"add" => {
|
||||
chk(3);
|
||||
Instruction::Add(
|
||||
reg(0),
|
||||
reg(1),
|
||||
match op2(2) {
|
||||
Op2::Direct(v) => Labeli17::Value(v),
|
||||
Op2::Register(r) => Labeli17::Register(Reg(r)),
|
||||
},
|
||||
)
|
||||
}
|
||||
"sub" => {
|
||||
chk(3);
|
||||
Instruction::Sub(reg(0), reg(1), op2(2))
|
||||
}
|
||||
"or" => {
|
||||
chk(3);
|
||||
Instruction::Or(reg(0), reg(1), op2(2))
|
||||
}
|
||||
"and" => {
|
||||
chk(3);
|
||||
Instruction::And(reg(0), reg(1), op2(2))
|
||||
}
|
||||
"xor" => {
|
||||
chk(3);
|
||||
Instruction::Xor(reg(0), reg(1), op2(2))
|
||||
}
|
||||
"lsl" => {
|
||||
chk(3);
|
||||
Instruction::Lsl(reg(0), reg(1), op2(2))
|
||||
}
|
||||
"lsr" => {
|
||||
chk(3);
|
||||
Instruction::Lsr(reg(0), reg(1), op2(2))
|
||||
}
|
||||
"smull" => {
|
||||
chk(3);
|
||||
Instruction::Smull(reg(0), reg(1), op2(2))
|
||||
}
|
||||
"smulh" => {
|
||||
chk(3);
|
||||
Instruction::Smulh(reg(0), reg(1), op2(2))
|
||||
}
|
||||
"umull" => {
|
||||
chk(3);
|
||||
Instruction::Umull(reg(0), reg(1), op2(2))
|
||||
}
|
||||
"umulh" => {
|
||||
chk(3);
|
||||
Instruction::Umulh(reg(0), reg(1), op2(2))
|
||||
}
|
||||
"mod" => {
|
||||
chk(3);
|
||||
Instruction::Mod(reg(0), reg(1), op2(2))
|
||||
}
|
||||
"div" => {
|
||||
chk(3);
|
||||
Instruction::Div(reg(0), reg(1), op2(2))
|
||||
}
|
||||
"asr" => {
|
||||
chk(3);
|
||||
Instruction::Asr(reg(0), reg(1), op2(2))
|
||||
}
|
||||
"load" => {
|
||||
let (rx, op2, new_line) = memaddr(linenum, no_comment, rgx);
|
||||
let args: Vec<_> = new_line.split_ascii_whitespace().collect();
|
||||
if args.len() != 3 {
|
||||
println!("wrong number of args in {line} at line {linenum}");
|
||||
exit(1);
|
||||
}
|
||||
Instruction::Load(_reg(args[1], linenum, &line), rx, op2)
|
||||
}
|
||||
"store" => {
|
||||
let (rx, op2, new_line) = memaddr(linenum, no_comment, rgx);
|
||||
let args: Vec<_> = new_line.split_ascii_whitespace().collect();
|
||||
if args.len() != 3 {
|
||||
println!("wrong number of args in {line} at line {linenum}");
|
||||
exit(1);
|
||||
}
|
||||
Instruction::Store(rx, op2, _reg(args[2], linenum, &line))
|
||||
}
|
||||
"push" => {
|
||||
chk(1);
|
||||
Instruction::Push(op2(0))
|
||||
}
|
||||
"pop" => {
|
||||
chk(1);
|
||||
Instruction::Pop(reg(0))
|
||||
}
|
||||
"skip" => {
|
||||
chk(4);
|
||||
let d = labi32(0);
|
||||
match args[1].try_into() {
|
||||
Ok(c) => Instruction::Skip(d, c, reg(2), op2(3)),
|
||||
Err(_) => {
|
||||
println!("in {line} at line {linenum}");
|
||||
exit(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
"skipto" => {
|
||||
chk(4);
|
||||
let d = labi32(0);
|
||||
match args[1].try_into() {
|
||||
Ok(c) => Instruction::Skip(d, c, reg(2), op2(3)),
|
||||
Err(_) => {
|
||||
println!("in {line} at line {linenum}");
|
||||
exit(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
"jump" => {
|
||||
chk(1);
|
||||
Instruction::Jump(labi32(0))
|
||||
}
|
||||
"call" => {
|
||||
chk(1);
|
||||
Instruction::Call(labi32(0))
|
||||
}
|
||||
"ret" => {
|
||||
chk(0);
|
||||
Instruction::Ret()
|
||||
}
|
||||
"reti" => {
|
||||
chk(0);
|
||||
Instruction::Reti()
|
||||
}
|
||||
"eint" => {
|
||||
chk(0);
|
||||
Instruction::Eint()
|
||||
}
|
||||
"dint" => {
|
||||
chk(0);
|
||||
Instruction::Dint()
|
||||
}
|
||||
"halt" => {
|
||||
chk(0);
|
||||
Instruction::Jump(Labeli32::Value(0))
|
||||
}
|
||||
"nop" => {
|
||||
chk(0);
|
||||
Instruction::Add(Reg(0), Reg(0), Labeli17::Value(0))
|
||||
}
|
||||
"not" => {
|
||||
chk(2);
|
||||
Instruction::Xor(reg(0), reg(1), Op2::Direct(-1))
|
||||
}
|
||||
"swi" => {
|
||||
chk(0);
|
||||
Instruction::Swi()
|
||||
}
|
||||
"let" => {
|
||||
chk(2);
|
||||
let r = reg(0);
|
||||
match labi32(1) {
|
||||
Labeli32::Label(l) => {
|
||||
prgm.instructions.push((
|
||||
Instruction::Copy(r, Labeli17::LabelHigh(l.clone())),
|
||||
linenum,
|
||||
));
|
||||
prgm.instructions
|
||||
.push((Instruction::Lsl(r, r, Op2::Direct(16)), linenum));
|
||||
Instruction::Add(r, r, Labeli17::LabelLow(l))
|
||||
}
|
||||
Labeli32::Value(v) => {
|
||||
if -0xFFFF <= v && v <= 0xFFFF {
|
||||
Instruction::Copy(r, Labeli17::Value(v))
|
||||
} else {
|
||||
prgm.instructions.push((
|
||||
Instruction::Copy(r, Labeli17::Value((v as u32 >> 16) as i32)),
|
||||
linenum,
|
||||
));
|
||||
prgm.instructions
|
||||
.push((Instruction::Lsl(r, r, Op2::Direct(16)), linenum));
|
||||
Instruction::Add(r, r, Labeli17::Value(v & 0xFFFF))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"d" => {
|
||||
chk(1);
|
||||
Instruction::Data(labi32(0))
|
||||
}
|
||||
m => {
|
||||
println!("Unknown mnemnonic {m} in {line} at line {linenum}");
|
||||
exit(1);
|
||||
}
|
||||
};
|
||||
prgm.instructions.push((next, linenum));
|
||||
}
|
||||
|
||||
const HELP: &str = "usage: cargo run --release INPUT [OUTPUT]
|
||||
where INPUT is a file name or - for stdin
|
||||
and the optional OUTPUT is a output file name
|
||||
|
||||
if stdin is used and no output is used, out.bin is used";
|
||||
|
||||
fn main() {
|
||||
let progname = args().nth(1).expect(HELP);
|
||||
let outname = args().nth(2);
|
||||
|
||||
let mut prgm = Program {
|
||||
locations: HashMap::new(),
|
||||
instructions: Vec::new(),
|
||||
};
|
||||
let rgx = RegexCollection {
|
||||
label: Regex::new("^[a-zA-Z_][a-zA-Z0-9_]*$").unwrap(),
|
||||
memop: Regex::new(r"\[ *(r[0-9]{1,2}) *(?:(\+|-) *(r[0-9]{1,2}|0x[0-9a-f]*|[0-9]*) *)?\]")
|
||||
.unwrap(),
|
||||
};
|
||||
if progname == "-" {
|
||||
for line in stdin()
|
||||
.lines()
|
||||
.map(|x| x.expect("error while reading stdin"))
|
||||
.enumerate()
|
||||
{
|
||||
process_line(&mut prgm, line, &rgx);
|
||||
}
|
||||
} else {
|
||||
let infile = std::fs::File::open(progname.as_str()).expect("Cannot open input file");
|
||||
for line in BufReader::new(infile)
|
||||
.lines()
|
||||
.map(|x| x.expect("Erro while reading input file"))
|
||||
.enumerate()
|
||||
{
|
||||
process_line(&mut prgm, line, &rgx);
|
||||
}
|
||||
}
|
||||
|
||||
let outname = match outname {
|
||||
Some(s) => s,
|
||||
None => {
|
||||
if progname == "-" {
|
||||
"out.bin".to_string()
|
||||
} else {
|
||||
progname
|
||||
.strip_suffix(".asm")
|
||||
.unwrap_or(progname.as_str())
|
||||
.to_string()
|
||||
+ ".bin"
|
||||
}
|
||||
}
|
||||
};
|
||||
let mut outfile = std::fs::File::create_buffered(outname).expect("could not open out file");
|
||||
for (num, (instruction, linenum)) in prgm.instructions.iter().enumerate() {
|
||||
let v = instruction.encode(&prgm, num as u32 * 4);
|
||||
match v {
|
||||
Ok(v) => writeln!(outfile, "{v:08x}").expect("error writing"),
|
||||
Err(_) => {
|
||||
println!("in source at line {linenum}");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
if prgm.locations.len() != 0 {
|
||||
writeln!(outfile, "SYMBOL TABLE:").expect("error writing");
|
||||
}
|
||||
let mut assocs: Vec<_> = prgm.locations.iter().collect();
|
||||
assocs.sort_by_key(|(_, v)| **v);
|
||||
for (s, v) in assocs {
|
||||
writeln!(outfile, "{v:08x} {s}").expect("error writing");
|
||||
}
|
||||
}
|
||||
2
rust-toolchain.toml
Normal file
2
rust-toolchain.toml
Normal file
@@ -0,0 +1,2 @@
|
||||
[toolchain]
|
||||
channel = "nightly"
|
||||
BIN
sim_rs.tar.xz
BIN
sim_rs.tar.xz
Binary file not shown.
24
simu/Cargo.toml
Normal file
24
simu/Cargo.toml
Normal file
@@ -0,0 +1,24 @@
|
||||
[package]
|
||||
name = "simu"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
pixels = "0.15.0"
|
||||
winit = { version = "0.30.13", features = ["x11", "x11-dl", "x11rb", "ahash", "bytemuck", "memmap2", "rwh_06", "sctk", "sctk-adwaita"] }
|
||||
winit_input_helper = "0.17.0"
|
||||
parse_int = { version = "0.9.0", optional = true }
|
||||
wait_on_address = { version = "0.1.4", optional = true }
|
||||
clap-repl = { version = "0.3.2", optional = true }
|
||||
clap = { version = "4.6.0", optional = true }
|
||||
|
||||
|
||||
[features]
|
||||
default = ["futex"]
|
||||
div_mul = []
|
||||
rgba = []
|
||||
rich_keyboard = []
|
||||
debug = ["dep:parse_int","dep:clap-repl","dep:clap"]
|
||||
futex = ["dep:wait_on_address"]
|
||||
clap-repl = ["dep:clap-repl"]
|
||||
clap = ["dep:clap"]
|
||||
14
simu/README.md
Normal file
14
simu/README.md
Normal file
@@ -0,0 +1,14 @@
|
||||
# bisare_sim_rs
|
||||
|
||||
Un petit simulateur de processeur pour les cours de L3 de l'ENS de Lyon
|
||||
|
||||
usage: cargo run --release path_to_bin
|
||||
|
||||
MMIO:
|
||||
écran: à partir de 0x01000000
|
||||
- pixels en 0BGR, de en haut a gauche a en bas a droite.
|
||||
clavier: 0x01200000
|
||||
- scancode (comme sim.py, != du simulateur c)
|
||||
clock: 0x01200004
|
||||
- millisecondes écoulées depuis la création du simulateur
|
||||
(nb: un u32 peut stocker 49 jours)
|
||||
2
simu/rust-toolchain.toml
Normal file
2
simu/rust-toolchain.toml
Normal file
@@ -0,0 +1,2 @@
|
||||
[toolchain]
|
||||
channel = "nightly"
|
||||
803
simu/src/cpu.rs
Normal file
803
simu/src/cpu.rs
Normal file
@@ -0,0 +1,803 @@
|
||||
use crate::wait::WaitOnAtomic;
|
||||
#[cfg(feature = "debug")]
|
||||
use std::collections::HashMap;
|
||||
use std::{
|
||||
hint::{cold_path, likely, unlikely, unreachable_unchecked},
|
||||
io::Read,
|
||||
mem::transmute,
|
||||
ops::{Index, IndexMut},
|
||||
sync::atomic::AtomicU32,
|
||||
time::{self, Instant},
|
||||
};
|
||||
|
||||
#[cfg(not(feature = "debug"))]
|
||||
use std::process::exit;
|
||||
pub(crate) struct SharedState {
|
||||
pub(crate) keyboard: [AtomicU32; 4],
|
||||
pub(crate) screen_buf: [AtomicU32; 480 * 640],
|
||||
pub(crate) external_interupts: AtomicU32,
|
||||
pub(crate) external_enabled_interupts: AtomicU32,
|
||||
pub(crate) mouse: [AtomicU32; 3],
|
||||
}
|
||||
|
||||
pub(crate) static SHARED: SharedState = SharedState {
|
||||
keyboard: [const { AtomicU32::new(0) }; 4],
|
||||
screen_buf: [const { AtomicU32::new(0) }; 480 * 640],
|
||||
external_interupts: AtomicU32::new(0),
|
||||
external_enabled_interupts: AtomicU32::new(0),
|
||||
mouse: [const { AtomicU32::new(0) }; 3],
|
||||
};
|
||||
|
||||
enum Op2 {
|
||||
Direct(u32),
|
||||
Register(u8),
|
||||
}
|
||||
|
||||
struct Reg(u8);
|
||||
|
||||
#[allow(unused)] //constructed by transmute
|
||||
#[repr(u8)]
|
||||
enum Cond {
|
||||
Ifeq = 0b000,
|
||||
Ifne = 0b0001,
|
||||
UK1 = 2,
|
||||
UK2 = 3,
|
||||
UK3 = 4,
|
||||
UK4 = 5,
|
||||
UK5 = 6,
|
||||
UK6 = 7,
|
||||
Iflt = 0b1000,
|
||||
Ifge = 0b1001,
|
||||
Ifgt = 0b1010,
|
||||
Ifle = 0b1011,
|
||||
Ifult = 0b1100,
|
||||
Ifuge = 0b1101,
|
||||
Ifugt = 0b1110,
|
||||
Ifule = 0b1111,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum InteruptState {
|
||||
Disabled,
|
||||
Enabled,
|
||||
Serving(InteruptKind, u8),
|
||||
}
|
||||
|
||||
#[derive(PartialEq, PartialOrd, Clone, Copy, Debug)]
|
||||
#[repr(u32)]
|
||||
#[allow(unused)] //some are there only based on features
|
||||
pub(crate) enum InteruptKind {
|
||||
MMIO = 1,
|
||||
Swi = 2,
|
||||
DivByZero = 3,
|
||||
IllegalLoadStore = 4,
|
||||
UnsupportedOpcode = 5,
|
||||
IllegalOpcode = 6,
|
||||
}
|
||||
|
||||
#[repr(u32)]
|
||||
pub enum MMIOInterupt {
|
||||
Keyboard = 1,
|
||||
MouseClick = 2,
|
||||
MouseMove = 4,
|
||||
VSync = 8,
|
||||
}
|
||||
|
||||
impl From<u8> for Cond {
|
||||
#[inline(always)]
|
||||
fn from(value: u8) -> Self {
|
||||
//unsafe if called with value >= 15 buuut only ever called on actually an u4 so it's ok
|
||||
unsafe { transmute(value) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<u32> for MMIOInterupt {
|
||||
fn into(self) -> u32 {
|
||||
unsafe { transmute(self) }
|
||||
}
|
||||
}
|
||||
impl Into<u32> for InteruptKind {
|
||||
fn into(self) -> u32 {
|
||||
unsafe { transmute(self) }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u32> for InteruptKind {
|
||||
fn from(v: u32) -> Self {
|
||||
unsafe { transmute(v as u32) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Cond {
|
||||
#[inline(always)]
|
||||
fn eval(self, a: u32, b: u32) -> Option<bool> {
|
||||
Some(match self {
|
||||
Cond::Ifeq => a == b,
|
||||
Cond::Ifne => a != b,
|
||||
Cond::Iflt => (a as i32) < (b as i32),
|
||||
Cond::Ifge => (a as i32) >= (b as i32),
|
||||
Cond::Ifgt => (a as i32) > (b as i32),
|
||||
Cond::Ifle => (a as i32) <= (b as i32),
|
||||
Cond::Ifult => a < b,
|
||||
Cond::Ifuge => a >= b,
|
||||
Cond::Ifugt => a > b,
|
||||
Cond::Ifule => a <= b,
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)] //depen on features
|
||||
enum Instruction {
|
||||
Copy(Reg, Op2),
|
||||
Add(Reg, Reg, Op2),
|
||||
Sub(Reg, Reg, Op2),
|
||||
Or(Reg, Reg, Op2),
|
||||
And(Reg, Reg, Op2),
|
||||
Xor(Reg, Reg, Op2),
|
||||
Lsl(Reg, Reg, Op2),
|
||||
Lsr(Reg, Reg, Op2),
|
||||
Asr(Reg, Reg, Op2),
|
||||
Smull(Reg, Reg, Op2),
|
||||
Smulh(Reg, Reg, Op2),
|
||||
Umull(Reg, Reg, Op2),
|
||||
Umulh(Reg, Reg, Op2),
|
||||
Div(Reg, Reg, Op2),
|
||||
Mod(Reg, Reg, Op2),
|
||||
Store(Reg, Op2, Reg),
|
||||
Load(Reg, Reg, Op2),
|
||||
Push(Op2),
|
||||
Pop(Reg),
|
||||
Skip(u8, Cond, Reg, Op2),
|
||||
Jump(u32), //address / 4
|
||||
Call(u32), //address / 4
|
||||
Ret(),
|
||||
Reti(),
|
||||
Swi(),
|
||||
Eint(),
|
||||
Dint(),
|
||||
GetStack(Reg),
|
||||
SetStack(Op2),
|
||||
}
|
||||
|
||||
impl TryFrom<u32> for Instruction {
|
||||
#[inline(always)]
|
||||
fn try_from(value: u32) -> Result<Self, Self::Error> {
|
||||
Ok(match value >> 30 {
|
||||
0b00 => {
|
||||
let t = value & (1 << 29); // 3rd bit set
|
||||
let t = t != 0;
|
||||
if t {
|
||||
Self::Call(value)
|
||||
} else {
|
||||
Self::Jump(value)
|
||||
}
|
||||
}
|
||||
fmt => {
|
||||
let imediate = (value & (1 << 28)) != 0;
|
||||
let s = (value & 1 << 29) != 0;
|
||||
const MASK: u32 = 0b1111;
|
||||
let opcode = (value >> 24) & MASK;
|
||||
let rd = Reg(((value >> 20) & MASK) as u8);
|
||||
let rx = Reg(((value >> 16) & MASK) as u8);
|
||||
let op2 = {
|
||||
if imediate {
|
||||
let value = if s {
|
||||
value | 0xFFFF0000
|
||||
} else {
|
||||
value & 0x0000FFFF
|
||||
};
|
||||
Op2::Direct(value)
|
||||
} else {
|
||||
Op2::Register((value >> 12 & MASK) as u8)
|
||||
}
|
||||
};
|
||||
match (fmt, opcode) {
|
||||
(1, 0b0000) => Self::Copy(rd, op2),
|
||||
(1, 0b0001) => Self::Add(rd, rx, op2),
|
||||
(1, 0b0010) => Self::Sub(rd, rx, op2),
|
||||
(1, 0b0011) => Self::Or(rd, rx, op2),
|
||||
(1, 0b0100) => Self::And(rd, rx, op2),
|
||||
(1, 0b0101) => Self::Xor(rd, rx, op2),
|
||||
(1, 0b0110) => Self::Lsl(rd, rx, op2),
|
||||
(1, 0b0111) => Self::Lsr(rd, rx, op2),
|
||||
(1, 0b1000) => Self::Asr(rd, rx, op2),
|
||||
#[cfg(feature = "div_mul")]
|
||||
(1, 0b1001) => Self::Smull(rd, rx, op2),
|
||||
#[cfg(feature = "div_mul")]
|
||||
(1, 0b1010) => Self::Smulh(rd, rx, op2),
|
||||
#[cfg(feature = "div_mul")]
|
||||
(1, 0b1011) => Self::Umull(rd, rx, op2),
|
||||
#[cfg(feature = "div_mul")]
|
||||
(1, 0b1100) => Self::Umulh(rd, rx, op2),
|
||||
#[cfg(feature = "div_mul")]
|
||||
(1, 0b1101) => Self::Div(rd, rx, op2),
|
||||
#[cfg(feature = "div_mul")]
|
||||
(1, 0b1110) => Self::Mod(rd, rx, op2),
|
||||
#[cfg(not(feature = "div_mul"))]
|
||||
(1, 0b1001)
|
||||
| (1, 0b1010)
|
||||
| (1, 0b1011)
|
||||
| (1, 0b1100)
|
||||
| (1, 0b1101)
|
||||
| (1, 0b1110) => {
|
||||
return Err((InteruptKind::UnsupportedOpcode, rd, rx, op2, opcode));
|
||||
}
|
||||
(2, 0b0000) => Self::Store(rx, op2, rd),
|
||||
(2, 0b0001) => Self::Load(rd, rx, op2),
|
||||
(2, 0b0010) => Self::Push(op2),
|
||||
(2, 0b0011) => Self::Pop(rd),
|
||||
(2, 0b1000) => {
|
||||
if s {
|
||||
Self::Reti()
|
||||
} else {
|
||||
Self::Ret()
|
||||
}
|
||||
}
|
||||
(2, 0b1001) => Self::Swi(),
|
||||
(2, 0b1011) => Self::Dint(),
|
||||
(2, 0b1100) => Self::Eint(),
|
||||
(2, 0b1101) => Self::GetStack(rd),
|
||||
(2, 0b1110) => Self::SetStack(op2),
|
||||
(3, skip) => Self::Skip(rd.0, (skip as u8).into(), rx, op2),
|
||||
_ => return Err((InteruptKind::IllegalOpcode, rd, rx, op2, opcode)),
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
type Error = (InteruptKind, Reg, Reg, Op2, u32);
|
||||
}
|
||||
|
||||
use std::fmt::Display;
|
||||
|
||||
impl Display for Op2 {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Op2::Direct(v) => write!(f, "{v}"),
|
||||
Op2::Register(r) => write!(f, "r{r}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Display for Reg {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "r{}", self.0)
|
||||
}
|
||||
}
|
||||
impl Display for Cond {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Cond::Ifeq => write!(f, "ifeq"),
|
||||
Cond::Ifne => write!(f, "ifne"),
|
||||
Cond::Iflt => write!(f, "iflt"),
|
||||
Cond::Ifge => write!(f, "ifge"),
|
||||
Cond::Ifgt => write!(f, "ifgt"),
|
||||
Cond::Ifle => write!(f, "ifle"),
|
||||
Cond::Ifult => write!(f, "ifult"),
|
||||
Cond::Ifuge => write!(f, "ifuge"),
|
||||
Cond::Ifugt => write!(f, "ifugt"),
|
||||
Cond::Ifule => write!(f, "ifule"),
|
||||
_ => write!(f, "unknown"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "debug")]
|
||||
pub(crate) fn instr_to_text(i: u32, a: u32, book: &HashMap<u32, String>) -> String {
|
||||
let addr = |addr: u32| {
|
||||
let real = a.wrapping_add(addr * 4) % 0x8000_0000;
|
||||
match book.get(&real) {
|
||||
Some(s) => s.clone(),
|
||||
None => format!("{addr}"),
|
||||
}
|
||||
};
|
||||
match Instruction::try_from(i) {
|
||||
Ok(i) => match i {
|
||||
Instruction::Copy(reg, op2) => format!("copy {reg} {op2}"),
|
||||
Instruction::Add(reg, reg1, op2) => format!("add {reg} {reg1} {op2}"),
|
||||
Instruction::Sub(reg, reg1, op2) => format!("sub {reg} {reg1} {op2}"),
|
||||
Instruction::Or(reg, reg1, op2) => format!("or {reg} {reg1} {op2}"),
|
||||
Instruction::And(reg, reg1, op2) => format!("and {reg} {reg1} {op2}"),
|
||||
Instruction::Xor(reg, reg1, op2) => format!("xor {reg} {reg1} {op2}"),
|
||||
Instruction::Lsl(reg, reg1, op2) => format!("lsl {reg} {reg1} {op2}"),
|
||||
Instruction::Lsr(reg, reg1, op2) => format!("lsr {reg} {reg1} {op2}"),
|
||||
Instruction::Asr(reg, reg1, op2) => format!("asr {reg} {reg1} {op2}"),
|
||||
Instruction::Smull(reg, reg1, op2) => format!("smull {reg} {reg1} {op2}"),
|
||||
Instruction::Smulh(reg, reg1, op2) => format!("smulh {reg} {reg1} {op2}"),
|
||||
Instruction::Umull(reg, reg1, op2) => format!("umull {reg} {reg1} {op2}"),
|
||||
Instruction::Umulh(reg, reg1, op2) => format!("umulh {reg} {reg1} {op2}"),
|
||||
Instruction::Div(reg, reg1, op2) => format!("div {reg} {reg1} {op2}"),
|
||||
Instruction::Mod(reg, reg1, op2) => format!("mod {reg} {reg1} {op2}"),
|
||||
Instruction::Store(reg, op2, reg1) => format!("store [{reg} + {op2}] {reg1}]"),
|
||||
Instruction::Load(reg, reg1, op2) => format! {"load {reg} [{reg1} + {op2}]"},
|
||||
Instruction::Push(op2) => format!("push {op2}"),
|
||||
Instruction::Pop(reg) => format!("pop {reg}"),
|
||||
Instruction::Skip(d, cond, reg, op2) => {
|
||||
format!("skip {} {cond} {reg} {op2}", addr(d as u32))
|
||||
}
|
||||
Instruction::Jump(a) => format!("jump {}", addr(a)),
|
||||
Instruction::Call(a) => format!("call {}", addr(a % 0x1000_0000)),
|
||||
Instruction::Ret() => format!("ret"),
|
||||
Instruction::Reti() => format!("reti"),
|
||||
Instruction::Swi() => format!("swi"),
|
||||
Instruction::Eint() => format!("eint"),
|
||||
Instruction::Dint() => format!("dint"),
|
||||
Instruction::GetStack(reg) => format!("copy {reg} sp"),
|
||||
Instruction::SetStack(op2) => format!("copy sp {op2}"),
|
||||
},
|
||||
Err(_) => {
|
||||
format!("D 0x{:8x}", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Computer {
|
||||
creation: Instant,
|
||||
pub(crate) ram: Box<[u32; 0x01000000 / 4]>,
|
||||
pub(crate) regs: [u32; 16],
|
||||
pub(crate) pc: usize,
|
||||
pub(crate) sp: usize,
|
||||
pub(crate) interupts: InteruptState,
|
||||
#[cfg(feature = "debug")]
|
||||
pub(crate) error: bool,
|
||||
#[cfg(feature = "debug")]
|
||||
pub(crate) book: (HashMap<u32, String>, HashMap<String, u32>),
|
||||
}
|
||||
|
||||
impl Index<Reg> for Computer {
|
||||
type Output = u32;
|
||||
|
||||
fn index(&self, index: Reg) -> &Self::Output {
|
||||
&self.regs[index.0 as usize]
|
||||
}
|
||||
}
|
||||
|
||||
impl IndexMut<Reg> for Computer {
|
||||
fn index_mut(&mut self, index: Reg) -> &mut Self::Output {
|
||||
&mut self.regs[index.0 as usize]
|
||||
}
|
||||
}
|
||||
|
||||
impl Computer {
|
||||
pub fn new(filename: String) -> Self {
|
||||
let mut new = Self {
|
||||
creation: Instant::now(),
|
||||
ram: unsafe { Box::new_zeroed().assume_init() },
|
||||
regs: [0; 16],
|
||||
pc: 0,
|
||||
sp: 0,
|
||||
interupts: InteruptState::Disabled,
|
||||
#[cfg(feature = "debug")]
|
||||
error: false,
|
||||
#[cfg(feature = "debug")]
|
||||
book: (HashMap::new(), HashMap::new()),
|
||||
};
|
||||
let mut buf = String::new();
|
||||
std::fs::File::open(filename)
|
||||
.unwrap()
|
||||
.read_to_string(&mut buf)
|
||||
.unwrap();
|
||||
let mut lines = buf.lines().enumerate();
|
||||
while let Some((i, line)) = lines.next() {
|
||||
match u32::from_str_radix(line, 16) {
|
||||
Ok(val) => new.ram[i] = val,
|
||||
Err(_) => break,
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "debug")]
|
||||
while let Some((_, line)) = lines.next() {
|
||||
if let Some([addr, s]) = line.split_ascii_whitespace().collect::<Vec<_>>().as_array() {
|
||||
if let Ok(i) = u32::from_str_radix(addr, 16) {
|
||||
new.book.0.insert(i, s.to_string());
|
||||
new.book.1.insert(s.to_string(), i);
|
||||
}
|
||||
}
|
||||
}
|
||||
new
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn step(&mut self, s: usize) {
|
||||
match self.interupts {
|
||||
InteruptState::Disabled => {}
|
||||
InteruptState::Enabled => {
|
||||
let mmio_interupts =
|
||||
(&SHARED.external_interupts).load(std::sync::atomic::Ordering::Acquire);
|
||||
if unlikely(mmio_interupts != 0) {
|
||||
let enabled = (&SHARED.external_enabled_interupts)
|
||||
.load(std::sync::atomic::Ordering::Relaxed);
|
||||
if unlikely(mmio_interupts & !(enabled) != 0) {
|
||||
(&SHARED.external_interupts).store(0, std::sync::atomic::Ordering::Relaxed);
|
||||
} else {
|
||||
let kind = mmio_interupts.highest_one().unwrap();
|
||||
self.serve_interupt(InteruptKind::MMIO, [kind]);
|
||||
}
|
||||
}
|
||||
}
|
||||
InteruptState::Serving(..) => {}
|
||||
}
|
||||
for _ in 0..s {
|
||||
//potentially just changed by interupt.
|
||||
let next_opcode = self.ram[self.pc];
|
||||
|
||||
let instruction = Instruction::try_from(next_opcode);
|
||||
|
||||
match instruction {
|
||||
Ok(instruction) => {
|
||||
match instruction {
|
||||
Instruction::Copy(reg, op2) => {
|
||||
self[reg] = self.resolve(op2);
|
||||
self.pc += 1;
|
||||
}
|
||||
Instruction::Add(reg, reg1, op2) => {
|
||||
self[reg] = self[reg1] + self.resolve(op2);
|
||||
self.pc += 1;
|
||||
}
|
||||
Instruction::Sub(reg, reg1, op2) => {
|
||||
self[reg] = self[reg1] - self.resolve(op2);
|
||||
self.pc += 1;
|
||||
}
|
||||
Instruction::Or(reg, reg1, op2) => {
|
||||
self[reg] = self[reg1] | self.resolve(op2);
|
||||
self.pc += 1;
|
||||
}
|
||||
Instruction::And(reg, reg1, op2) => {
|
||||
self[reg] = self[reg1] & self.resolve(op2);
|
||||
self.pc += 1;
|
||||
}
|
||||
Instruction::Xor(reg, reg1, op2) => {
|
||||
self[reg] = self[reg1] ^ self.resolve(op2);
|
||||
self.pc += 1;
|
||||
}
|
||||
Instruction::Lsl(reg, reg1, op2) => {
|
||||
self[reg] = (self[reg1] as u64).wrapping_shl(self.resolve(op2)) as u32;
|
||||
self.pc += 1;
|
||||
}
|
||||
Instruction::Lsr(reg, reg1, op2) => {
|
||||
self[reg] = (self[reg1] as u64).wrapping_shr(self.resolve(op2)) as u32;
|
||||
self.pc += 1;
|
||||
}
|
||||
Instruction::Asr(reg, reg1, op2) => {
|
||||
self[reg] = (self[reg1] as i64).wrapping_shr(self.resolve(op2)) as u32;
|
||||
self.pc += 1;
|
||||
}
|
||||
Instruction::Umull(reg, reg1, op2) => {
|
||||
self[reg] = self[reg1].wrapping_mul(self.resolve(op2));
|
||||
self.pc += 1;
|
||||
}
|
||||
Instruction::Smull(reg, reg1, op2) => {
|
||||
self[reg] =
|
||||
(self[reg1] as i32).wrapping_mul(self.resolve(op2) as i32) as u32;
|
||||
self.pc += 1;
|
||||
}
|
||||
Instruction::Umulh(reg, reg1, op2) => {
|
||||
self[reg] = self[reg1].widening_mul(self.resolve(op2)).1;
|
||||
self.pc += 1;
|
||||
}
|
||||
Instruction::Smulh(reg, reg1, op2) => {
|
||||
self[reg] =
|
||||
(self[reg1] as i32).widening_mul(self.resolve(op2) as i32).1 as u32;
|
||||
self.pc += 1;
|
||||
}
|
||||
Instruction::Div(reg, reg1, op2) => {
|
||||
self.pc += 1;
|
||||
let d = self.resolve(op2);
|
||||
if unlikely(d == 0) {
|
||||
self.serve_interupt(
|
||||
InteruptKind::DivByZero,
|
||||
[reg.0.into(), self[reg1]],
|
||||
);
|
||||
return;
|
||||
}
|
||||
self[reg] = self[reg1] / d;
|
||||
}
|
||||
Instruction::Mod(reg, reg1, op2) => {
|
||||
self[reg] = self[reg1] % self.resolve(op2);
|
||||
self.pc += 1
|
||||
}
|
||||
Instruction::Store(reg, op2, reg1) => {
|
||||
self.pc += 1;
|
||||
let addr = (self[reg].wrapping_add(self.resolve(op2))) as usize;
|
||||
if !addr.is_multiple_of(4) {
|
||||
self.serve_interupt(
|
||||
InteruptKind::IllegalLoadStore,
|
||||
[1, addr as u32, self[reg1]],
|
||||
);
|
||||
return;
|
||||
}
|
||||
if addr <= 0x00ffffff {
|
||||
self.ram[addr / 4] = self[reg1];
|
||||
} else if addr <= 0x00ff_ffff + 480 * 640 * 4 {
|
||||
let buf_addr = (addr - 0x0100_0000) / 4;
|
||||
let dat = if cfg!(feature = "rgba") {
|
||||
self[reg1]
|
||||
} else {
|
||||
self[reg1] & 0x00FF_FFFF
|
||||
};
|
||||
(&SHARED.screen_buf[buf_addr])
|
||||
.store(dat, std::sync::atomic::Ordering::Relaxed);
|
||||
} else if addr == 0x0120_1000 {
|
||||
(&SHARED.external_enabled_interupts)
|
||||
.store(self[reg1], std::sync::atomic::Ordering::Relaxed);
|
||||
} else {
|
||||
cold_path();
|
||||
self.serve_interupt(InteruptKind::IllegalOpcode, [next_opcode]);
|
||||
}
|
||||
}
|
||||
Instruction::Load(reg, reg1, op2) => {
|
||||
self.pc += 1;
|
||||
let addr = (self[reg1].wrapping_add(self.resolve(op2))) as usize;
|
||||
if !addr.is_multiple_of(4) {
|
||||
self.serve_interupt(
|
||||
InteruptKind::IllegalLoadStore,
|
||||
[0, addr as u32, reg.0 as u32],
|
||||
);
|
||||
return;
|
||||
}
|
||||
self[reg] = if addr <= 0x00ffffff {
|
||||
self.ram[addr / 4]
|
||||
} else if addr <= 0x00ffffff + 480 * 640 * 4 {
|
||||
let buf_addr = (addr - 0x0100_0000) / 4;
|
||||
(&SHARED.screen_buf[buf_addr])
|
||||
.load(std::sync::atomic::Ordering::Relaxed)
|
||||
} else {
|
||||
match addr as isize - 0x0120_0000 {
|
||||
#[cfg(feature = "rich_keyboard")]
|
||||
-12 => SHARED.keyboard[0]
|
||||
.load(std::sync::atomic::Ordering::Relaxed),
|
||||
#[cfg(feature = "rich_keyboard")]
|
||||
-8 => SHARED.keyboard[1]
|
||||
.load(std::sync::atomic::Ordering::Relaxed),
|
||||
#[cfg(feature = "rich_keyboard")]
|
||||
-4 => SHARED.keyboard[2]
|
||||
.load(std::sync::atomic::Ordering::Relaxed),
|
||||
0 => SHARED.keyboard[3]
|
||||
.load(std::sync::atomic::Ordering::Relaxed),
|
||||
4 => time::Instant::now()
|
||||
.duration_since(self.creation)
|
||||
.as_millis()
|
||||
as u32,
|
||||
8 => SHARED.mouse[0].load(std::sync::atomic::Ordering::Relaxed),
|
||||
12 => {
|
||||
SHARED.mouse[1].load(std::sync::atomic::Ordering::Relaxed)
|
||||
}
|
||||
16 => {
|
||||
SHARED.mouse[2].load(std::sync::atomic::Ordering::Relaxed)
|
||||
}
|
||||
//guaranted by the inequality and is multiple of 4
|
||||
_ => {
|
||||
cold_path();
|
||||
self.serve_interupt(
|
||||
InteruptKind::IllegalOpcode,
|
||||
[next_opcode],
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
Instruction::Push(op2) => {
|
||||
self.sp -= 1;
|
||||
self.ram[self.sp] = self.resolve(op2);
|
||||
self.pc += 1;
|
||||
}
|
||||
Instruction::Pop(reg) => {
|
||||
self[reg] = self.ram[self.sp];
|
||||
self.sp += 1;
|
||||
self.pc += 1;
|
||||
}
|
||||
Instruction::Skip(d, cond, reg, op2) => {
|
||||
self.pc += 1;
|
||||
match cond.eval(self[reg], self.resolve(op2)) {
|
||||
Some(false) => { /*Nothing*/ }
|
||||
Some(true) => self.pc += d as usize,
|
||||
None => {
|
||||
cold_path();
|
||||
self.serve_interupt(InteruptKind::IllegalOpcode, [next_opcode])
|
||||
}
|
||||
}
|
||||
}
|
||||
Instruction::Jump(addr) => {
|
||||
if unlikely(addr == 0) {
|
||||
#[cfg(feature = "debug")]
|
||||
{
|
||||
match self.interupts {
|
||||
InteruptState::Disabled => {
|
||||
println!("program terminated");
|
||||
self.error = true;
|
||||
return;
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
if SHARED
|
||||
.external_enabled_interupts
|
||||
.load(std::sync::atomic::Ordering::Relaxed)
|
||||
== 0
|
||||
{
|
||||
println!("Program terminated");
|
||||
self.error = true;
|
||||
return;
|
||||
}
|
||||
println!("awaiting interupt...");
|
||||
}
|
||||
SHARED.external_interupts.wait(0);
|
||||
return;
|
||||
}
|
||||
self.pc = ((addr + self.pc as u32) & 0x1FFF_FFFF) as usize;
|
||||
}
|
||||
Instruction::Call(addr) => {
|
||||
//WARNING! addr still has the type bit set at this point!
|
||||
self.sp -= 1;
|
||||
self.ram[self.sp] = ((self.pc << 2) + 4) as u32;
|
||||
|
||||
if unlikely(addr == 0 + (1 << 29) /*t bit*/) {
|
||||
#[cfg(feature = "debug")]
|
||||
{
|
||||
println!("program terminated");
|
||||
self.error = true;
|
||||
return;
|
||||
}
|
||||
#[cfg(not(feature = "debug"))]
|
||||
exit(0);
|
||||
}
|
||||
//The mask take care of both wrapping and shedding the t bit
|
||||
self.pc = ((addr + self.pc as u32) & 0x1FFF_FFFF) as usize;
|
||||
}
|
||||
Instruction::Ret() => {
|
||||
self.pc = (self.ram[self.sp] >> 2) as usize;
|
||||
self.sp += 1;
|
||||
}
|
||||
Instruction::Reti() => {
|
||||
let mut ret_index = None;
|
||||
let mut ret_value = 0;
|
||||
|
||||
match self.interupts {
|
||||
InteruptState::Serving(kind, prev) => {
|
||||
match prev.highest_one() {
|
||||
None => self.interupts = InteruptState::Enabled,
|
||||
Some(i) => {
|
||||
self.interupts =
|
||||
InteruptState::Serving(i.into(), prev ^ (1 << i))
|
||||
}
|
||||
}
|
||||
match kind {
|
||||
InteruptKind::MMIO => {
|
||||
(&SHARED.external_interupts)
|
||||
.store(0, std::sync::atomic::Ordering::Release);
|
||||
SHARED.external_interupts.signal();
|
||||
//no need to check prev because MMIO is the lowest priority
|
||||
self.interupts = InteruptState::Enabled
|
||||
}
|
||||
InteruptKind::Swi => {}
|
||||
InteruptKind::DivByZero
|
||||
| InteruptKind::UnsupportedOpcode => {
|
||||
ret_index = Some(self.regs[0]);
|
||||
ret_value = self.regs[1];
|
||||
}
|
||||
InteruptKind::IllegalLoadStore => {
|
||||
if self.regs[0] == 0 {
|
||||
ret_value = self.regs[1];
|
||||
ret_index = Some(self.regs[2]);
|
||||
}
|
||||
}
|
||||
InteruptKind::IllegalOpcode => {}
|
||||
}
|
||||
}
|
||||
_ => { /* This is a troubling case but ... well it's ok */ }
|
||||
}
|
||||
let ret = self.ram[self.sp];
|
||||
self.pc = (ret & 0x0FFF_FFFF) as usize;
|
||||
self.sp += 1 as usize;
|
||||
for i in (0..(ret >> 28) as usize).rev() {
|
||||
self.regs[i] = self.ram[self.sp];
|
||||
self.sp += 1
|
||||
}
|
||||
if let Some(idx) = ret_index {
|
||||
self.regs[idx as usize] = ret_value;
|
||||
}
|
||||
}
|
||||
Instruction::Eint() => {
|
||||
match self.interupts {
|
||||
InteruptState::Disabled => self.interupts = InteruptState::Enabled,
|
||||
_ => {}
|
||||
}
|
||||
self.pc += 1;
|
||||
}
|
||||
Instruction::Dint() => {
|
||||
self.interupts = InteruptState::Disabled;
|
||||
self.pc += 1;
|
||||
|
||||
(&SHARED.external_enabled_interupts)
|
||||
.store(0, std::sync::atomic::Ordering::Relaxed);
|
||||
(&SHARED.external_interupts)
|
||||
.store(0, std::sync::atomic::Ordering::Relaxed);
|
||||
SHARED.external_interupts.signal();
|
||||
}
|
||||
Instruction::Swi() => {
|
||||
self.pc += 1;
|
||||
self.serve_interupt(InteruptKind::Swi, []);
|
||||
}
|
||||
Instruction::GetStack(reg) => {
|
||||
self[reg] = (self.sp << 2) as u32;
|
||||
self.pc += 1;
|
||||
}
|
||||
Instruction::SetStack(op2) => {
|
||||
let v = self.resolve(op2);
|
||||
if likely(v.is_multiple_of(4)) {
|
||||
self.sp = (v >> 2) as usize;
|
||||
} else {
|
||||
self.sp = usize::MAX //Yes, that means that clever program using sp to store information wont work on my emulator. Deal with it
|
||||
}
|
||||
self.pc += 1;
|
||||
}
|
||||
};
|
||||
}
|
||||
Err((kind, rx, ry, op2, opcode)) => {
|
||||
self.pc += 1;
|
||||
match kind {
|
||||
InteruptKind::UnsupportedOpcode => self.serve_interupt(
|
||||
kind,
|
||||
[rx.0.into(), self[ry], self.resolve(op2), opcode],
|
||||
),
|
||||
InteruptKind::IllegalOpcode => {
|
||||
cold_path();
|
||||
|
||||
self.serve_interupt(kind, [next_opcode])
|
||||
}
|
||||
_ => unsafe { unreachable_unchecked() },
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "debug")]
|
||||
pub fn debug_step(&mut self, s: usize) {
|
||||
self.step(s);
|
||||
}
|
||||
#[inline(always)]
|
||||
fn resolve(&self, op2: Op2) -> u32 {
|
||||
match op2 {
|
||||
Op2::Direct(v) => v,
|
||||
Op2::Register(r) => self.regs[r as usize],
|
||||
}
|
||||
}
|
||||
fn serve_interupt<const N: usize>(&mut self, kind: InteruptKind, arg: [u32; N]) {
|
||||
if N >= 16 {
|
||||
panic!();
|
||||
}
|
||||
#[cfg(feature = "debug")]
|
||||
println!("serving interupt {kind:?} {arg:?}");
|
||||
match self.interupts {
|
||||
InteruptState::Disabled => {
|
||||
println!("Illegal Instruction whith interupt disabled {kind:?} {arg:?}");
|
||||
#[cfg(feature = "debug")]
|
||||
{
|
||||
self.error = true;
|
||||
return;
|
||||
}
|
||||
#[cfg(not(feature = "debug"))]
|
||||
exit(1);
|
||||
}
|
||||
InteruptState::Enabled => self.interupts = InteruptState::Serving(kind, 0),
|
||||
InteruptState::Serving(interupt_kind, previous) => {
|
||||
if kind > interupt_kind {
|
||||
self.interupts = InteruptState::Serving(
|
||||
kind,
|
||||
previous | (1_u8 << Into::<u32>::into(interupt_kind)),
|
||||
);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (i, a) in arg.iter().enumerate() {
|
||||
self.sp -= 1;
|
||||
self.ram[self.sp] = self.regs[i];
|
||||
self.regs[i] = *a
|
||||
}
|
||||
self.sp -= 1;
|
||||
self.ram[self.sp] = (self.pc | N << 28) as u32;
|
||||
self.pc = Into::<u32>::into(kind) as usize
|
||||
}
|
||||
}
|
||||
531
simu/src/main.rs
Normal file
531
simu/src/main.rs
Normal file
@@ -0,0 +1,531 @@
|
||||
#![feature(likely_unlikely, widening_mul, int_lowest_highest_one)]
|
||||
#![deny(clippy::all)]
|
||||
|
||||
use std::env::args;
|
||||
use std::hint::unlikely;
|
||||
use std::process::exit;
|
||||
use std::sync::{
|
||||
Arc,
|
||||
atomic::Ordering::{Acquire, Relaxed, Release},
|
||||
};
|
||||
use std::thread::scope;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use pixels::wgpu::{BlendState, Color};
|
||||
use pixels::{Error, Pixels, PixelsBuilder, SurfaceTexture};
|
||||
use winit::application::ApplicationHandler;
|
||||
use winit::dpi::LogicalSize;
|
||||
use winit::event::WindowEvent;
|
||||
use winit::event_loop::EventLoop;
|
||||
use winit::platform::scancode::PhysicalKeyExtScancode;
|
||||
use winit::window::Window;
|
||||
|
||||
use crate::cpu::{Computer, MMIOInterupt};
|
||||
mod wait;
|
||||
use wait::WaitOnAtomic;
|
||||
mod cpu;
|
||||
use cpu::SHARED;
|
||||
|
||||
fn wait_int() {
|
||||
let mut v = (&SHARED.external_interupts).load(Acquire);
|
||||
while unlikely(v != 0) {
|
||||
#[cfg(feature = "debug")]
|
||||
println!("wating for interupt clear {v}");
|
||||
SHARED.external_interupts.wait(v);
|
||||
v = (&SHARED.external_interupts).load(Acquire);
|
||||
}
|
||||
}
|
||||
|
||||
const WIDTH: u32 = 640;
|
||||
const HEIGHT: u32 = 480;
|
||||
|
||||
struct App<'a> {
|
||||
w: Option<Arc<Window>>,
|
||||
pixels: Option<Pixels<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> ApplicationHandler for App<'a> {
|
||||
fn resumed(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) {
|
||||
let window = {
|
||||
let size = LogicalSize::new(WIDTH as f64, HEIGHT as f64);
|
||||
Arc::new(
|
||||
event_loop
|
||||
.create_window(
|
||||
Window::default_attributes()
|
||||
.with_title("bisare screen")
|
||||
.with_min_inner_size(size)
|
||||
.with_maximized(true),
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
};
|
||||
self.w = Some(window.clone());
|
||||
let size = window.inner_size();
|
||||
let surface_texture = SurfaceTexture::new(size.width, size.height, window);
|
||||
let pix = PixelsBuilder::new(WIDTH, HEIGHT, surface_texture)
|
||||
.clear_color(Color::BLACK)
|
||||
.blend_state(BlendState::REPLACE)
|
||||
.build();
|
||||
|
||||
self.pixels = Some(pix.unwrap());
|
||||
}
|
||||
|
||||
fn window_event(
|
||||
&mut self,
|
||||
elwt: &winit::event_loop::ActiveEventLoop,
|
||||
_: winit::window::WindowId,
|
||||
event: WindowEvent,
|
||||
) {
|
||||
// Draw the current frame
|
||||
match event {
|
||||
WindowEvent::KeyboardInput {
|
||||
event: key_event, ..
|
||||
} => {
|
||||
let enabled = (&SHARED.external_enabled_interupts).load(Relaxed)
|
||||
& Into::<u32>::into(MMIOInterupt::Keyboard)
|
||||
!= 0;
|
||||
|
||||
if enabled {
|
||||
wait_int();
|
||||
}
|
||||
#[cfg(feature = "debug")]
|
||||
print!("Keyboard event: ");
|
||||
#[cfg(feature = "rich_keyboard")]
|
||||
{
|
||||
use winit::platform::modifier_supplement::KeyEventExtModifierSupplement;
|
||||
let kb0 = key_event.text_with_all_modifiers().map_or(u32::MAX, |s| {
|
||||
s.as_bytes()
|
||||
.into_iter()
|
||||
.fold(0, |a, e| a << 8 | (*e as u32))
|
||||
});
|
||||
SHARED.keyboard[0].store(kb0, Relaxed);
|
||||
let kb1 = key_event
|
||||
.key_without_modifiers()
|
||||
.to_text()
|
||||
.map_or(u32::MAX, |s| {
|
||||
s.as_bytes()
|
||||
.into_iter()
|
||||
.fold(0, |a, e| a << 8 | (*e as u32))
|
||||
});
|
||||
|
||||
SHARED.keyboard[1].store(kb1, Relaxed);
|
||||
let kb2 =
|
||||
key_event.state.is_pressed() as u32 | ((key_event.repeat as u32) << 1);
|
||||
SHARED.keyboard[2].store(kb2, Relaxed);
|
||||
#[cfg(feature = "debug")]
|
||||
print!("{kb0} {kb1} {kb2}")
|
||||
}
|
||||
let kb3 = key_event.physical_key.to_scancode().unwrap_or(0);
|
||||
#[cfg(feature = "debug")]
|
||||
println!(" {kb3}");
|
||||
SHARED.keyboard[3].store(kb3, Relaxed);
|
||||
if enabled {
|
||||
(&SHARED.external_interupts).store(MMIOInterupt::Keyboard.into(), Release);
|
||||
#[cfg(feature = "debug")]
|
||||
println!("wake due to keyboard event");
|
||||
SHARED.external_interupts.signal();
|
||||
}
|
||||
}
|
||||
WindowEvent::CursorMoved { position, .. } => {
|
||||
let enabled = (&SHARED.external_enabled_interupts).load(Relaxed)
|
||||
& Into::<u32>::into(MMIOInterupt::MouseMove)
|
||||
!= 0;
|
||||
if enabled {
|
||||
wait_int();
|
||||
}
|
||||
match self
|
||||
.pixels
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.window_pos_to_pixel((position.x as f32, position.y as f32))
|
||||
{
|
||||
Ok((x, y)) => {
|
||||
(&cpu::SHARED.mouse[1]).store(x as u32, Relaxed);
|
||||
(&cpu::SHARED.mouse[2]).store(y as u32, Relaxed);
|
||||
}
|
||||
Err(_) => {
|
||||
(&SHARED.mouse[1]).store(u32::MAX, Relaxed);
|
||||
(&SHARED.mouse[2]).store(u32::MAX, Relaxed);
|
||||
}
|
||||
}
|
||||
if enabled {
|
||||
(&SHARED.external_interupts).store(MMIOInterupt::MouseMove.into(), Release);
|
||||
#[cfg(feature = "debug")]
|
||||
println!("wake due mouse move");
|
||||
SHARED.external_interupts.signal();
|
||||
}
|
||||
}
|
||||
// WindowEvent::MouseWheel {
|
||||
// delta,
|
||||
// ..
|
||||
// } => {}
|
||||
WindowEvent::MouseInput { state, button, .. } => {
|
||||
let enabled = (&SHARED.external_enabled_interupts).load(Relaxed)
|
||||
& Into::<u32>::into(MMIOInterupt::MouseClick)
|
||||
!= 0;
|
||||
if enabled {
|
||||
wait_int();
|
||||
}
|
||||
let but = 1
|
||||
<< match button {
|
||||
winit::event::MouseButton::Left => 0,
|
||||
winit::event::MouseButton::Right => 1,
|
||||
winit::event::MouseButton::Middle => 2,
|
||||
winit::event::MouseButton::Back => 3,
|
||||
winit::event::MouseButton::Forward => 4,
|
||||
winit::event::MouseButton::Other(i) => i & 31,
|
||||
};
|
||||
match state {
|
||||
winit::event::ElementState::Pressed => {
|
||||
(&cpu::SHARED.mouse[0]).fetch_or(but, std::sync::atomic::Ordering::Relaxed)
|
||||
}
|
||||
winit::event::ElementState::Released => (&cpu::SHARED.mouse[0])
|
||||
.fetch_and(!but, std::sync::atomic::Ordering::Relaxed),
|
||||
};
|
||||
if enabled {
|
||||
(&SHARED.external_interupts).store(MMIOInterupt::MouseClick.into(), Release);
|
||||
#[cfg(feature = "debug")]
|
||||
println!("wake mouse click");
|
||||
SHARED.external_interupts.signal();
|
||||
}
|
||||
}
|
||||
WindowEvent::ScaleFactorChanged { .. } => {
|
||||
self.w.as_ref().unwrap().request_redraw();
|
||||
}
|
||||
//handling redraws and other graphical events
|
||||
WindowEvent::RedrawRequested => {
|
||||
let enabled = (&SHARED.external_enabled_interupts).load(Relaxed)
|
||||
& Into::<u32>::into(MMIOInterupt::VSync)
|
||||
!= 0;
|
||||
if enabled {
|
||||
(&SHARED.external_interupts).store(MMIOInterupt::VSync.into(), Relaxed);
|
||||
SHARED.external_interupts.signal();
|
||||
wait_int();
|
||||
}
|
||||
let pix = self.pixels.as_mut().unwrap();
|
||||
|
||||
let screen = pix.frame_mut();
|
||||
for (addr, ubgr) in cpu::SHARED.screen_buf.iter().enumerate() {
|
||||
let raw = ubgr.load(std::sync::atomic::Ordering::Relaxed);
|
||||
#[cfg(not(feature = "rgba"))]
|
||||
let rgba: [u8; 4] = raw.to_le_bytes();
|
||||
#[cfg(feature = "rgba")]
|
||||
let rgba = raw.to_be_bytes();
|
||||
for i in 0..4 {
|
||||
screen[addr * 4 + i] = rgba[i];
|
||||
}
|
||||
}
|
||||
|
||||
if let Err(_) = pix.render() {
|
||||
elwt.exit();
|
||||
return;
|
||||
}
|
||||
}
|
||||
WindowEvent::Resized(size) => {
|
||||
if self
|
||||
.pixels
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.resize_surface(size.width, size.height)
|
||||
.is_err()
|
||||
{
|
||||
println!("Error while resising pixels, exiting!");
|
||||
elwt.exit();
|
||||
return;
|
||||
}
|
||||
self.w.as_ref().unwrap().request_redraw();
|
||||
}
|
||||
WindowEvent::CloseRequested => {
|
||||
elwt.exit();
|
||||
return;
|
||||
}
|
||||
WindowEvent::Destroyed => {
|
||||
println!("Windows destroyed, exiting!");
|
||||
elwt.exit();
|
||||
return;
|
||||
}
|
||||
//do nothing for the other events
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
fn new_events(
|
||||
&mut self,
|
||||
event_loop: &winit::event_loop::ActiveEventLoop,
|
||||
cause: winit::event::StartCause,
|
||||
) {
|
||||
match cause {
|
||||
winit::event::StartCause::ResumeTimeReached {
|
||||
requested_resume, ..
|
||||
} => {
|
||||
let mut next = requested_resume + Duration::from_secs_f64(1. / 60.);
|
||||
let now = Instant::now();
|
||||
if next < now {
|
||||
next = now + Duration::from_secs_f64(1. / 30.);
|
||||
println!("Warning: rendering is lagging!")
|
||||
}
|
||||
event_loop.set_control_flow(winit::event_loop::ControlFlow::WaitUntil(next));
|
||||
if let Some(w) = self.w.as_ref() {
|
||||
w.request_redraw();
|
||||
}
|
||||
}
|
||||
winit::event::StartCause::WaitCancelled { .. } => {}
|
||||
winit::event::StartCause::Poll => {
|
||||
let next = Instant::now() + Duration::from_secs_f64(1. / 60.);
|
||||
event_loop.set_control_flow(winit::event_loop::ControlFlow::WaitUntil(next));
|
||||
}
|
||||
winit::event::StartCause::Init => {
|
||||
let next = Instant::now() + Duration::from_secs_f64(1. / 60.);
|
||||
event_loop.set_control_flow(winit::event_loop::ControlFlow::WaitUntil(next));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() -> Result<(), Error> {
|
||||
let event_loop = EventLoop::new().unwrap();
|
||||
// let mut input = WinitInputHelper::new();
|
||||
|
||||
let mut app = App {
|
||||
w: None,
|
||||
pixels: None,
|
||||
};
|
||||
|
||||
let program = args()
|
||||
.nth(1)
|
||||
.expect("you must supply the exec name as the first argument");
|
||||
|
||||
scope(|sc| {
|
||||
sc.spawn(|| {
|
||||
let mut simulation = Computer::new(program);
|
||||
#[cfg(not(feature = "debug"))]
|
||||
loop {
|
||||
simulation.step(64);
|
||||
}
|
||||
|
||||
//ugly debug code, I should improve that using a real TUI crate
|
||||
#[cfg(feature = "debug")]
|
||||
{
|
||||
debug_loop(&mut simulation);
|
||||
}
|
||||
});
|
||||
|
||||
#[allow(deprecated)]
|
||||
let res = event_loop.run_app(&mut app);
|
||||
match res {
|
||||
Ok(_) => exit(0),
|
||||
Err(e) => {
|
||||
println!("{e}");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(feature = "debug")]
|
||||
fn debug_loop(com: &mut Computer) {
|
||||
struct Wrap(DefaultCompleter, DefaultCompleter);
|
||||
|
||||
use clap::Parser;
|
||||
use clap_repl::ClapEditor;
|
||||
use clap_repl::reedline::{
|
||||
Completer, DefaultCompleter, DefaultPrompt, DefaultPromptSegment, FileBackedHistory,
|
||||
};
|
||||
|
||||
use crate::cpu::instr_to_text;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
#[command(name = "")] // This name will show up in clap's error messages, so it is important to set it to "".
|
||||
enum Commands {
|
||||
/// Step by single instrcution
|
||||
#[command(alias = "s")]
|
||||
Step {
|
||||
///number of instruction to step (default one)
|
||||
num: Option<usize>,
|
||||
},
|
||||
/// Run until program halt, or specified instruction is reached
|
||||
#[command(alias = "r")]
|
||||
Run {
|
||||
/// Can be either a label or a address (support hex format)
|
||||
desigantor: Option<String>,
|
||||
},
|
||||
/// run until the current function return.
|
||||
#[command(alias = "u")]
|
||||
Up,
|
||||
/// Print memory at address. Support hexa format
|
||||
#[command(alias = "p")]
|
||||
Print { address: String },
|
||||
/// Print the address associated with a label
|
||||
#[command(alias = "l")]
|
||||
Label { label: String },
|
||||
/// Print context
|
||||
#[command(alias = "c")]
|
||||
Context,
|
||||
}
|
||||
let prompt = DefaultPrompt {
|
||||
left_prompt: DefaultPromptSegment::Basic(">>".to_owned()),
|
||||
right_prompt: DefaultPromptSegment::Empty,
|
||||
};
|
||||
|
||||
let commands_comp = DefaultCompleter::new_with_wordlen(
|
||||
["step", "run", "up", "print", "label", "help", "context"]
|
||||
.map(|s| s.to_string())
|
||||
.to_vec(),
|
||||
0,
|
||||
);
|
||||
let mut labels_comp =
|
||||
DefaultCompleter::with_inclusions("_0123456789".chars().collect::<Vec<_>>().as_slice())
|
||||
.set_min_word_len(0);
|
||||
labels_comp.insert(com.book.0.values().cloned().collect());
|
||||
|
||||
let editor = ClapEditor::<Commands>::builder()
|
||||
.with_prompt(Box::new(prompt))
|
||||
.with_editor_hook(|reed| {
|
||||
reed.with_history(Box::new(
|
||||
FileBackedHistory::with_file(1000, "debug_cmd.hist".into()).unwrap(),
|
||||
))
|
||||
.with_completer(Box::new(Wrap(commands_comp, labels_comp)))
|
||||
.with_quick_completions(true)
|
||||
.with_partial_completions(true)
|
||||
})
|
||||
.build();
|
||||
|
||||
debug_context(com);
|
||||
editor.repl(|command| match command {
|
||||
Commands::Step { num } => {
|
||||
let steps = num.unwrap_or(1);
|
||||
com.debug_step(steps);
|
||||
debug_context(com);
|
||||
}
|
||||
Commands::Run { desigantor } => match desigantor {
|
||||
Some(s) => match parse_int::parse::<usize>(s.as_str()) {
|
||||
Ok(addr) => {
|
||||
while com.pc != (addr / 4) && !com.error {
|
||||
com.debug_step(1);
|
||||
}
|
||||
debug_context(com);
|
||||
}
|
||||
Err(_) => match com.book.1.get(s.as_str()).cloned() {
|
||||
Some(addr) => {
|
||||
while com.pc != (addr as usize / 4) && !com.error {
|
||||
com.debug_step(1);
|
||||
}
|
||||
debug_context(com);
|
||||
}
|
||||
None => {
|
||||
println!("Error, {s} cannot be interpreted as addr nor label")
|
||||
}
|
||||
},
|
||||
},
|
||||
None => {
|
||||
while !com.error {
|
||||
com.debug_step(64);
|
||||
}
|
||||
debug_context(com);
|
||||
}
|
||||
},
|
||||
Commands::Up => {
|
||||
let curr_sp = com.sp;
|
||||
while (com.sp < curr_sp
|
||||
|| (com.ram[com.pc] != (0b10001000 << 24) && com.ram[com.pc] != (0b10101000 << 24)))
|
||||
&& !com.error
|
||||
{
|
||||
com.debug_step(1);
|
||||
}
|
||||
debug_context(com);
|
||||
}
|
||||
Commands::Print { address } => match parse_int::parse::<usize>(address.as_str()) {
|
||||
Ok(addr) => match com.ram.get(addr / 4) {
|
||||
Some(i) => {
|
||||
println!(
|
||||
"RAM at {addr:8x}: {:8x} {}",
|
||||
i,
|
||||
instr_to_text(*i, u32::MAX, &com.book.0)
|
||||
)
|
||||
}
|
||||
None => println!("Cannot index RAM at address {addr:8x}"),
|
||||
},
|
||||
Err(_) => match com.book.1.get(address.as_str()).cloned() {
|
||||
Some(addr) => println!(
|
||||
"RAM at {addr:8x}: {:8x} {}",
|
||||
com.ram[addr as usize / 4],
|
||||
instr_to_text(com.ram[addr as usize / 4], addr, &com.book.0)
|
||||
),
|
||||
None => {
|
||||
println!("Error, {address} cannot be interpreted as addr nor label")
|
||||
}
|
||||
},
|
||||
},
|
||||
Commands::Label { label } => match com.book.1.get(label.as_str()) {
|
||||
Some(addr) => println!("label is at addr {addr}"),
|
||||
None => println!("error: label not found"),
|
||||
},
|
||||
Commands::Context => debug_context(com),
|
||||
});
|
||||
exit(0);
|
||||
|
||||
impl Completer for Wrap {
|
||||
fn complete(&mut self, line: &str, pos: usize) -> Vec<clap_repl::reedline::Suggestion> {
|
||||
let trimmed = line.trim_start();
|
||||
let line_parts = trimmed.splitn(2, ' ').collect::<Vec<_>>();
|
||||
|
||||
if line_parts.len() <= 1 {
|
||||
self.0.complete(line, pos)
|
||||
} else {
|
||||
match line_parts[0] {
|
||||
"r" | "run" | "p" | "print" | "l" | "label" => {
|
||||
let trimmed_2 = line_parts[1].trim_start();
|
||||
let offset = line.len() - trimmed_2.len();
|
||||
let mut sub = self.1.complete(trimmed_2, pos - offset);
|
||||
for sug in sub.iter_mut() {
|
||||
use clap_repl::reedline::Span;
|
||||
|
||||
sug.span = Span {
|
||||
start: sug.span.start + offset,
|
||||
end: sug.span.end + offset,
|
||||
}
|
||||
}
|
||||
sub
|
||||
}
|
||||
_ => Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "debug")]
|
||||
fn debug_context(com: &Computer) {
|
||||
use crate::cpu::instr_to_text;
|
||||
println!("Interupt state: {:?}", com.interupts);
|
||||
for i in 0..8 {
|
||||
println!(
|
||||
"r{i} = {:8x} r{:<2} = {:8x}",
|
||||
com.regs[i],
|
||||
i + 8,
|
||||
com.regs[i + 8]
|
||||
);
|
||||
}
|
||||
println!("SP={:08x} PC={:08x}", com.sp * 4, com.pc * 4);
|
||||
println!("RAM at SP | Ram at PC:");
|
||||
|
||||
let mut pc_lines = Vec::new();
|
||||
|
||||
for i in 0..16 {
|
||||
match com.book.0.get(&((com.pc + i) as u32 * 4)) {
|
||||
Some(label) => pc_lines.push(format!(" {label}:")),
|
||||
None => {}
|
||||
};
|
||||
pc_lines.push(format!(
|
||||
"{:08x} {}",
|
||||
com.ram[com.pc + i],
|
||||
instr_to_text(com.ram[com.pc + i], (com.pc + i) as u32 * 4, &com.book.0)
|
||||
));
|
||||
}
|
||||
for (i, pc_l) in pc_lines.iter().enumerate() {
|
||||
if com.sp + i < com.ram.len() {
|
||||
println!("{:08x} | {pc_l}", com.ram[com.sp + i])
|
||||
} else {
|
||||
println!(" -- | {pc_l}")
|
||||
}
|
||||
}
|
||||
}
|
||||
35
simu/src/wait.rs
Normal file
35
simu/src/wait.rs
Normal file
@@ -0,0 +1,35 @@
|
||||
use std::sync::atomic::AtomicU32;
|
||||
|
||||
#[cfg(feature = "futex")]
|
||||
use wait_on_address::AtomicWait;
|
||||
|
||||
pub trait WaitOnAtomic {
|
||||
fn wait(&self, v: u32);
|
||||
fn signal(&self);
|
||||
}
|
||||
|
||||
#[cfg(feature = "futex")]
|
||||
impl WaitOnAtomic for AtomicU32 {
|
||||
fn wait(&self, v: u32) {
|
||||
<AtomicU32 as AtomicWait>::wait(self, v);
|
||||
}
|
||||
|
||||
fn signal(&self) {
|
||||
self.notify_one();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "futex"))]
|
||||
impl WaitOnAtomic for AtomicU32 {
|
||||
fn wait(&self, v: u32) {
|
||||
while self.load(std::sync::atomic::Ordering::Acquire) == v {
|
||||
use std::{thread::sleep, time::Duration};
|
||||
|
||||
sleep(Duration::from_micros(500));
|
||||
}
|
||||
}
|
||||
|
||||
fn signal(&self) {
|
||||
//
|
||||
}
|
||||
}
|
||||
330
src/cpu.rs
330
src/cpu.rs
@@ -1,330 +0,0 @@
|
||||
use std::{
|
||||
hint::{likely, unlikely},
|
||||
io::Read,
|
||||
process::exit,
|
||||
sync::{Mutex, atomic::AtomicU32},
|
||||
thread::sleep,
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use pixels::Pixels;
|
||||
|
||||
enum Op2 {
|
||||
Direct(u32),
|
||||
Register(u8),
|
||||
}
|
||||
|
||||
struct Reg(u8);
|
||||
enum Cond {
|
||||
Ifeq,
|
||||
Ifne,
|
||||
Iflt,
|
||||
Ifge,
|
||||
Ifgt,
|
||||
Ifle,
|
||||
Ifult,
|
||||
Ifuge,
|
||||
Ifugt,
|
||||
Ifule,
|
||||
}
|
||||
|
||||
impl From<u8> for Cond {
|
||||
fn from(value: u8) -> Self {
|
||||
match value {
|
||||
0b0000 => Cond::Ifeq,
|
||||
0b0001 => Cond::Ifne,
|
||||
0b1000 => Cond::Iflt,
|
||||
0b1001 => Cond::Ifge,
|
||||
0b1010 => Cond::Ifgt,
|
||||
0b1011 => Cond::Ifle,
|
||||
0b1100 => Cond::Ifult,
|
||||
0b1101 => Cond::Ifuge,
|
||||
0b1110 => Cond::Ifugt,
|
||||
0b1111 => Cond::Ifule,
|
||||
_ => {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Cond {
|
||||
fn eval(self, a: u32, b: u32) -> bool {
|
||||
match self {
|
||||
Cond::Ifeq => a == b,
|
||||
Cond::Ifne => a != b,
|
||||
Cond::Iflt => (a as i32) < (b as i32),
|
||||
Cond::Ifge => (a as i32) >= (b as i32),
|
||||
Cond::Ifgt => (a as i32) > (b as i32),
|
||||
Cond::Ifle => (a as i32) <= (b as i32),
|
||||
Cond::Ifult => a < b,
|
||||
Cond::Ifuge => a >= b,
|
||||
Cond::Ifugt => a > b,
|
||||
Cond::Ifule => a <= b,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum Instruction {
|
||||
Copy(Reg, Op2),
|
||||
Add(Reg, Reg, Op2),
|
||||
Sub(Reg, Reg, Op2),
|
||||
Or(Reg, Reg, Op2),
|
||||
And(Reg, Reg, Op2),
|
||||
Xor(Reg, Reg, Op2),
|
||||
Lsl(Reg, Reg, Op2),
|
||||
Lsr(Reg, Reg, Op2),
|
||||
Asr(Reg, Reg, Op2),
|
||||
Store(Reg, Op2, Reg),
|
||||
Load(Reg, Reg, Op2),
|
||||
Push(Op2),
|
||||
Pop(Reg),
|
||||
Skip(u8, Cond, Reg, Op2),
|
||||
Jump(u32), //address / 4
|
||||
Call(u32), //address / 4
|
||||
Ret(),
|
||||
GetStack(Reg),
|
||||
SetStack(Op2),
|
||||
}
|
||||
|
||||
impl From<u32> for Instruction {
|
||||
fn from(value: u32) -> Self {
|
||||
match value >> 30 {
|
||||
0b00 => {
|
||||
let t = value & (1 << 29); // 3rd bit set
|
||||
let value = value - t;
|
||||
let t = t != 0;
|
||||
if t {
|
||||
Self::Call(value)
|
||||
} else {
|
||||
Self::Jump(value)
|
||||
}
|
||||
}
|
||||
fmt => {
|
||||
let imediate = (value & (1 << 28)) != 0;
|
||||
let s = (value & 1 << 27) != 0;
|
||||
const MASK: u32 = 0b1111;
|
||||
let opcode = (value >> 24) & MASK;
|
||||
let rd = Reg(((value >> 20) & MASK) as u8);
|
||||
let rx = Reg(((value >> 16) & MASK) as u8);
|
||||
let op2 = {
|
||||
if imediate {
|
||||
let value = if s {
|
||||
value | 0xFFFF0000
|
||||
} else {
|
||||
value & 0x0000FFFF
|
||||
};
|
||||
Op2::Direct(value)
|
||||
} else {
|
||||
Op2::Register((value >> 12 & MASK) as u8)
|
||||
}
|
||||
};
|
||||
match (fmt, opcode) {
|
||||
(1, 0b0000) => Self::Copy(rd, op2),
|
||||
(1, 0b0001) => Self::Add(rd, rx, op2),
|
||||
(1, 0b0010) => Self::Sub(rd, rx, op2),
|
||||
(1, 0b0011) => Self::Or(rd, rx, op2),
|
||||
(1, 0b0100) => Self::And(rd, rx, op2),
|
||||
(1, 0b0101) => Self::Xor(rd, rx, op2),
|
||||
(1, 0b0110) => Self::Lsl(rd, rx, op2),
|
||||
(1, 0b0111) => Self::Lsr(rd, rx, op2),
|
||||
(1, 0b1000) => Self::Asr(rd, rx, op2),
|
||||
(2, 0b0000) => Self::Store(rx, op2, rd),
|
||||
(2, 0b0001) => Self::Load(rd, rx, op2),
|
||||
(2, 0b0010) => Self::Push(op2),
|
||||
(2, 0b0011) => Self::Pop(rd),
|
||||
(2, 0b1000) => Self::Ret(),
|
||||
(2, 0b1101) => Self::GetStack(rd),
|
||||
(2, 0b1110) => Self::SetStack(op2),
|
||||
(3, skip) => Self::Skip(rd.0, (skip as u8).into(), rx, op2),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Computer<'a, 'b> {
|
||||
ram: Box<[u32; 0x01000000 / 4]>,
|
||||
regs: [u32; 16],
|
||||
pc: usize,
|
||||
sp: usize,
|
||||
screen: &'b Mutex<Pixels<'a>>,
|
||||
key: &'b AtomicU32,
|
||||
}
|
||||
|
||||
fn iot() -> ! {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
impl<'a, 'b> Computer<'a, 'b> {
|
||||
pub fn new(filename: String, screen: &'b Mutex<Pixels<'a>>, key: &'b AtomicU32) -> Self {
|
||||
let mut new = Self {
|
||||
ram: Box::new([0; 0x01000000 / 4]),
|
||||
regs: [0; 16],
|
||||
pc: 0,
|
||||
sp: 0,
|
||||
screen,
|
||||
key,
|
||||
};
|
||||
let mut buf = String::new();
|
||||
std::fs::File::open(filename)
|
||||
.unwrap()
|
||||
.read_to_string(&mut buf)
|
||||
.unwrap();
|
||||
for (i, line) in buf.lines().enumerate() {
|
||||
match u32::from_str_radix(line, 16) {
|
||||
Ok(val) => new.ram[i] = val,
|
||||
Err(_) => break,
|
||||
}
|
||||
}
|
||||
new
|
||||
}
|
||||
pub fn step(&mut self) {
|
||||
let next_opcode = self.ram[self.pc];
|
||||
match Instruction::from(next_opcode) {
|
||||
Instruction::Copy(reg, op2) => {
|
||||
self.rg_wr(reg, self.resolve(op2));
|
||||
self.pc += 1;
|
||||
}
|
||||
Instruction::Add(reg, reg1, op2) => {
|
||||
self.rg_wr(reg, self.rg_r(reg1) + self.resolve(op2));
|
||||
self.pc += 1;
|
||||
}
|
||||
Instruction::Sub(reg, reg1, op2) => {
|
||||
self.rg_wr(reg, self.rg_r(reg1) - self.resolve(op2));
|
||||
self.pc += 1;
|
||||
}
|
||||
Instruction::Or(reg, reg1, op2) => {
|
||||
self.rg_wr(reg, self.rg_r(reg1) | self.resolve(op2));
|
||||
self.pc += 1;
|
||||
}
|
||||
Instruction::And(reg, reg1, op2) => {
|
||||
self.rg_wr(reg, self.rg_r(reg1) & self.resolve(op2));
|
||||
self.pc += 1;
|
||||
}
|
||||
Instruction::Xor(reg, reg1, op2) => {
|
||||
self.rg_wr(reg, self.rg_r(reg1) ^ self.resolve(op2));
|
||||
self.pc += 1;
|
||||
}
|
||||
Instruction::Lsl(reg, reg1, op2) => {
|
||||
self.rg_wr(reg, self.rg_r(reg1) << self.resolve(op2));
|
||||
self.pc += 1;
|
||||
}
|
||||
Instruction::Lsr(reg, reg1, op2) => {
|
||||
self.rg_wr(reg, self.rg_r(reg1) >> self.resolve(op2));
|
||||
self.pc += 1;
|
||||
}
|
||||
Instruction::Asr(reg, reg1, op2) => {
|
||||
self.rg_wr(reg, (self.rg_r(reg1) as i32 >> self.resolve(op2)) as u32);
|
||||
self.pc += 1;
|
||||
}
|
||||
Instruction::Store(reg, op2, reg1) => {
|
||||
let addr = (self.rg_r(reg) + self.resolve(op2)) as usize;
|
||||
if !addr.is_multiple_of(4) {
|
||||
iot();
|
||||
}
|
||||
if addr <= 0x00ffffff {
|
||||
self.ram[addr / 4] = self.rg_r(reg1);
|
||||
} else if addr <= 0x00ffffff + 480 * 640 * 4 {
|
||||
let addr_screen = addr - 0x01000000;
|
||||
let ubgr = self.rg_r(reg1);
|
||||
let rgba = [ubgr as u8, (ubgr >> 8) as u8, (ubgr >> 16) as u8, 0xff];
|
||||
let mut pixels = self.screen.lock().unwrap();
|
||||
let screen = pixels.frame_mut();
|
||||
for i in 0..4 {
|
||||
screen[addr_screen + i] = rgba[i];
|
||||
}
|
||||
} else {
|
||||
iot();
|
||||
}
|
||||
self.pc += 1;
|
||||
}
|
||||
Instruction::Load(reg, reg1, op2) => {
|
||||
let addr = (self.rg_r(reg1) + self.resolve(op2)) as usize;
|
||||
if !addr.is_multiple_of(4) {
|
||||
iot();
|
||||
}
|
||||
self.rg_wr(
|
||||
reg,
|
||||
if addr <= 0x00ffffff {
|
||||
self.ram[addr / 4]
|
||||
} else if addr <= 0x00ffffff + 480 * 640 * 4 {
|
||||
let pixels = self.screen.lock().unwrap();
|
||||
let buffer = pixels.frame();
|
||||
let mut res = 0;
|
||||
let addr_screen = addr - 0x01000000;
|
||||
for i in 0..3 {
|
||||
res += (buffer[addr_screen + i] as u32) << (i as u32 * 8)
|
||||
}
|
||||
res
|
||||
} else if addr == 0x01200000 {
|
||||
self.key.load(std::sync::atomic::Ordering::Relaxed)
|
||||
} else {
|
||||
iot();
|
||||
},
|
||||
);
|
||||
self.pc += 1;
|
||||
}
|
||||
Instruction::Push(op2) => {
|
||||
self.sp -= 1;
|
||||
self.ram[self.sp] = self.resolve(op2);
|
||||
self.pc += 1;
|
||||
}
|
||||
Instruction::Pop(reg) => {
|
||||
self.rg_wr(reg, self.ram[self.sp]);
|
||||
self.sp += 1;
|
||||
self.pc += 1;
|
||||
}
|
||||
Instruction::Skip(d, cond, reg, op2) => {
|
||||
self.pc += 1;
|
||||
if cond.eval(self.rg_r(reg), self.resolve(op2)) {
|
||||
self.pc += d as usize
|
||||
}
|
||||
}
|
||||
Instruction::Jump(mut addr) => {
|
||||
if addr & (1 << 28) != 0 {
|
||||
addr += 7 << 29;
|
||||
} else if unlikely(addr == 0) {
|
||||
sleep(Duration::from_hours(1));
|
||||
}
|
||||
self.pc = (addr + self.pc as u32) as usize;
|
||||
}
|
||||
Instruction::Call(addr) => {
|
||||
self.sp -= 1;
|
||||
self.ram[self.sp] = ((self.pc << 2) + 4) as u32;
|
||||
self.pc += addr as usize;
|
||||
self.pc &= 0x3FFFFFFF; //wrapping on 30 bit pc
|
||||
}
|
||||
Instruction::Ret() => {
|
||||
self.pc = (self.ram[self.sp] >> 2) as usize;
|
||||
self.sp += 1;
|
||||
}
|
||||
Instruction::GetStack(reg) => {
|
||||
self.rg_wr(reg, (self.sp << 2) as u32);
|
||||
self.pc += 1;
|
||||
}
|
||||
Instruction::SetStack(op2) => {
|
||||
let v = self.resolve(op2);
|
||||
if likely(v.is_multiple_of(4)) {
|
||||
self.sp = (v >> 2) as usize;
|
||||
} else {
|
||||
self.sp = usize::MAX //Yes, that means that clever program using sp to store information wont work on my emulator. Deal with it
|
||||
}
|
||||
self.pc += 1;
|
||||
}
|
||||
};
|
||||
}
|
||||
fn resolve(&self, op2: Op2) -> u32 {
|
||||
match op2 {
|
||||
Op2::Direct(v) => v,
|
||||
Op2::Register(r) => self.regs[r as usize],
|
||||
}
|
||||
}
|
||||
fn rg_wr(&mut self, reg: Reg, v: u32) {
|
||||
self.regs[reg.0 as usize] = v
|
||||
}
|
||||
fn rg_r(&self, reg: Reg) -> u32 {
|
||||
self.regs[reg.0 as usize]
|
||||
}
|
||||
}
|
||||
136
src/main.rs
136
src/main.rs
@@ -1,136 +0,0 @@
|
||||
#![feature(likely_unlikely)]
|
||||
#![deny(clippy::all)]
|
||||
|
||||
use std::env::args;
|
||||
use std::process::exit;
|
||||
use std::sync::atomic::AtomicU32;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::thread::{scope, sleep};
|
||||
use std::time::Duration;
|
||||
|
||||
use pixels::{Error, Pixels, SurfaceTexture};
|
||||
use winit::dpi::LogicalSize;
|
||||
use winit::event::{Event, WindowEvent};
|
||||
use winit::event_loop::EventLoop;
|
||||
use winit::platform::scancode::PhysicalKeyExtScancode;
|
||||
use winit::window::Window;
|
||||
use winit_input_helper::WinitInputHelper;
|
||||
|
||||
use crate::cpu::Computer;
|
||||
|
||||
const WIDTH: u32 = 640;
|
||||
const HEIGHT: u32 = 480;
|
||||
|
||||
mod cpu;
|
||||
|
||||
fn main() -> Result<(), Error> {
|
||||
let event_loop = EventLoop::new().unwrap();
|
||||
let mut input = WinitInputHelper::new();
|
||||
let window = {
|
||||
let size = LogicalSize::new((WIDTH * 3) as f64, (HEIGHT * 3) as f64);
|
||||
#[allow(deprecated)]
|
||||
Arc::new(
|
||||
event_loop
|
||||
.create_window(
|
||||
Window::default_attributes()
|
||||
.with_title("bisare screen")
|
||||
.with_inner_size(size)
|
||||
.with_min_inner_size(size),
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
};
|
||||
|
||||
let pixels = Mutex::new({
|
||||
let window_size = window.inner_size();
|
||||
let surface_texture = SurfaceTexture::new(window_size.width, window_size.height, &window);
|
||||
Pixels::new(WIDTH, HEIGHT, surface_texture)?
|
||||
});
|
||||
|
||||
let keyboard = AtomicU32::new(0);
|
||||
let program = args()
|
||||
.nth(1)
|
||||
.expect("you must supply the exec name as the first argument");
|
||||
let kbref = &keyboard;
|
||||
let pixelref = &pixels;
|
||||
scope(|sc| {
|
||||
sc.spawn(|| {
|
||||
let mut simulation = Computer::new(program, pixelref, kbref);
|
||||
loop {
|
||||
simulation.step();
|
||||
}
|
||||
});
|
||||
|
||||
#[allow(deprecated)]
|
||||
let res = event_loop.run(|event, elwt| {
|
||||
match event {
|
||||
Event::Resumed => {}
|
||||
Event::NewEvents(_) => input.step(),
|
||||
Event::AboutToWait => input.end_step(),
|
||||
Event::DeviceEvent { event, .. } => {
|
||||
input.process_device_event(&event);
|
||||
}
|
||||
Event::WindowEvent { event, .. } => {
|
||||
// Draw the current frame
|
||||
if event == WindowEvent::RedrawRequested {
|
||||
if let Err(_) = pixels.lock().unwrap().render() {
|
||||
elwt.exit();
|
||||
return;
|
||||
}
|
||||
sleep(Duration::from_millis(30));
|
||||
}
|
||||
|
||||
if let WindowEvent::KeyboardInput {
|
||||
device_id: _,
|
||||
ref event,
|
||||
is_synthetic: _,
|
||||
} = event
|
||||
{
|
||||
match event.state {
|
||||
winit::event::ElementState::Pressed => {
|
||||
if let Some(val) = event.physical_key.to_scancode() {
|
||||
kbref.store(val, std::sync::atomic::Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
winit::event::ElementState::Released => {
|
||||
kbref.store(0, std::sync::atomic::Ordering::Relaxed)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle input events
|
||||
if input.process_window_event(&event) {
|
||||
// Close events
|
||||
if input.close_requested() {
|
||||
elwt.exit();
|
||||
return;
|
||||
}
|
||||
|
||||
// Resize the window
|
||||
if let Some(size) = input.window_resized() {
|
||||
if let Err(_) = pixels
|
||||
.lock()
|
||||
.unwrap()
|
||||
.resize_surface(size.width, size.height)
|
||||
{
|
||||
elwt.exit();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Update internal state and request a redraw
|
||||
window.request_redraw();
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
});
|
||||
match res {
|
||||
Ok(_) => exit(0),
|
||||
Err(e) => {
|
||||
println!("{e}");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user