Compare commits

..

49 Commits

Author SHA1 Message Date
Mwa
128a53bd3b improved debug 2026-03-27 22:40:28 +01:00
Mwa
689f4b77fd slightly improved asm debug message 2026-03-26 21:24:14 +01:00
Mwa
96c3a514f2 fixed up debug PC/SP addr 2026-03-26 20:26:51 +01:00
Mwa
e97b4c4ee4 fixed up debug command 2026-03-26 20:11:32 +01:00
Mwa
a51b6589f0 removed subdir 2026-03-26 17:07:00 +01:00
Mwa
cebc8fcc5b bitmap to asm moved to the other repo 2026-03-26 13:18:51 +01:00
Mwa
b664538923 added color to img 2026-03-25 09:42:23 +01:00
Mwa
3525db5299 added a warn when frame lag 2026-03-24 23:16:33 +01:00
Mwa
8d7cb5e6f3 fixed import on non-debug build 2026-03-24 15:36:52 +01:00
Mwa
ced0e13f6b improved debug interface 2026-03-23 19:19:38 +01:00
Mwa
c6b58dbc21 again, some minor perf improvement, probably be the last 2026-03-21 00:08:45 +01:00
Mwa
af111c5992 minor interupt on halt responsivity improvement and final Cargo.toml args 2026-03-20 19:30:00 +01:00
Mwa
b5962c6b50 minor performance improvement for jump/call 2026-03-20 11:18:48 +01:00
Mwa
ff6427b020 performance improvement 2026-03-20 11:08:18 +01:00
Mwa
c72e133cde Implemented correct behavior for lsl, asr, lsr (mask and correct for input = 32) 2026-03-19 21:35:49 +01:00
Mwa
c844f8d806 added nosize arg 2026-03-18 15:07:05 +01:00
Mwa
5c5d8471fa minor graphic modification 2026-03-17 23:10:58 +01:00
Mwa
94120273bb better rgba 2026-03-17 22:04:21 +01:00
Mwa
978bb30fdb Merge branch 'main' of ssh://gitea.jthillard.fr:222/mwa/bisare_sim_rs 2026-03-17 21:21:53 +01:00
Mwa
5dd9328008 fic debug build 2026-03-17 21:21:46 +01:00
mwa
f6cc53075a clarified entry point meaning 2026-03-17 20:16:33 +00:00
Mwa
8fa56c5b6f compilation fix 2026-03-17 21:05:51 +01:00
Mwa
902ed046ca futex feature explained 2026-03-17 20:32:33 +01:00
Mwa
f17b849da2 busy wait for macos 2026-03-17 17:23:00 +01:00
Mwa
d04b6f35c3 busy wait for macos 2026-03-17 17:21:52 +01:00
Mwa
47efeef83d changed futex crate for mac compat (maybe) and image to bitmap assembly converter 2026-03-17 16:31:45 +01:00
Mwa
20f7c289ed -1 on unrecognized input and start bitmap converter 2026-03-17 13:22:33 +01:00
Mwa
30a7d1c5e4 better kb explanation 2026-03-16 20:09:36 +01:00
Mwa
7713564d79 better ctrl detection 2026-03-16 20:04:25 +01:00
Mwa
56fcfb5230 removed debug print 2026-03-16 19:08:02 +01:00
Mwa
a43277c159 improved debuger 2026-03-16 19:02:00 +01:00
Mwa
410c54c2f8 improved debuger 2026-03-16 19:00:59 +01:00
Mwa
899bb90230 improved debuger 2026-03-16 18:40:23 +01:00
Mwa
53966ad22f improved debuger 2026-03-16 18:36:14 +01:00
Mwa
4e23ac7bff Merge branch 'main' of ssh://gitea.jthillard.fr:222/mwa/bisare_sim_rs 2026-03-16 18:12:03 +01:00
Mwa
cd2e5319bc minor bugfixes 2026-03-16 18:10:43 +01:00
mwa
fc3b588425 Update README.md
tweaks for better rendering on web
2026-03-16 16:35:40 +00:00
Mwa
636ff0a430 cleanup 2026-03-16 16:33:49 +01:00
Mwa
fb6722a47e add not pseudo-instruction 2026-03-16 16:21:07 +01:00
Mwa
e385288e70 Debugging mode 2026-03-16 04:36:59 +01:00
Mwa
2d50702acc rich keyboard 2026-03-16 01:48:39 +01:00
Mwa
8cb41c61cd Explained and finalized interrupts 2026-03-16 00:40:53 +01:00
Mwa
c38bb22e88 Interuptions 2026-03-15 23:32:13 +01:00
Mwa
aead858727 performance improvement 2026-03-14 00:48:03 +01:00
Mwa
3f97a352cc performance improvement and no crash in debug mode 2026-03-12 15:49:43 +01:00
Mwa
d762e1449b specifyng wrapping add 2026-03-12 15:10:14 +01:00
Mwa
2741332630 added asm 2026-03-11 03:55:12 +01:00
Mwa
21e534378f added asm project 2026-03-11 03:42:24 +01:00
Mwa
e277c11eef reworking workspace 2026-03-11 03:40:23 +01:00
17 changed files with 2873 additions and 572 deletions

2
.cargo/config.toml Normal file
View File

@@ -0,0 +1,2 @@
[build]
rustflags = ["-Ctarget-cpu=native"]

7
.gitignore vendored
View File

@@ -10,9 +10,4 @@ target/
# MSVC Windows builds of rustc generate these, which store debugging information # MSVC Windows builds of rustc generate these, which store debugging information
*.pdb *.pdb
# RustRover files
# 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/

663
Cargo.lock generated
View File

@@ -31,6 +31,15 @@ dependencies = [
"zerocopy", "zerocopy",
] ]
[[package]]
name = "aho-corasick"
version = "1.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301"
dependencies = [
"memchr",
]
[[package]] [[package]]
name = "allocator-api2" name = "allocator-api2"
version = "0.2.21" version = "0.2.21"
@@ -73,6 +82,56 @@ dependencies = [
"libc", "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]] [[package]]
name = "arrayref" name = "arrayref"
version = "0.3.9" version = "0.3.9"
@@ -100,6 +159,14 @@ dependencies = [
"libloading 0.7.4", "libloading 0.7.4",
] ]
[[package]]
name = "asm"
version = "0.1.0"
dependencies = [
"parse_int",
"regex",
]
[[package]] [[package]]
name = "atomic-waker" name = "atomic-waker"
version = "1.1.2" version = "1.1.2"
@@ -138,6 +205,9 @@ name = "bitflags"
version = "2.11.0" version = "2.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af"
dependencies = [
"serde_core",
]
[[package]] [[package]]
name = "block" name = "block"
@@ -200,9 +270,9 @@ dependencies = [
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.2.56" version = "1.2.57"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2" checksum = "7a0dd1ca384932ff3641c8718a02769f1698e7563dc6974ffd03346116310423"
dependencies = [ dependencies = [
"find-msvc-tools", "find-msvc-tools",
"jobserver", "jobserver",
@@ -234,6 +304,92 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" 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]] [[package]]
name = "codespan-reporting" name = "codespan-reporting"
version = "0.11.1" version = "0.11.1"
@@ -241,9 +397,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e"
dependencies = [ dependencies = [
"termcolor", "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]] [[package]]
name = "com" name = "com"
version = "0.6.0" version = "0.6.0"
@@ -294,6 +456,19 @@ dependencies = [
"crossbeam-utils", "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]] [[package]]
name = "core-foundation" name = "core-foundation"
version = "0.9.4" version = "0.9.4"
@@ -340,6 +515,32 @@ version = "0.8.21"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" 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]] [[package]]
name = "cursor-icon" name = "cursor-icon"
version = "1.2.0" version = "1.2.0"
@@ -384,6 +585,18 @@ version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76" 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]] [[package]]
name = "equivalent" name = "equivalent"
version = "1.0.2" version = "1.0.2"
@@ -400,6 +613,17 @@ dependencies = [
"windows-sys 0.61.2", "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]] [[package]]
name = "find-msvc-tools" name = "find-msvc-tools"
version = "0.1.9" version = "0.1.9"
@@ -594,6 +818,12 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "heck"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]] [[package]]
name = "hermit-abi" name = "hermit-abi"
version = "0.5.2" version = "0.5.2"
@@ -606,6 +836,30 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" 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]] [[package]]
name = "indexmap" name = "indexmap"
version = "2.13.0" version = "2.13.0"
@@ -616,6 +870,30 @@ dependencies = [
"hashbrown 0.16.1", "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]] [[package]]
name = "jni" name = "jni"
version = "0.21.1" version = "0.21.1"
@@ -677,9 +955,9 @@ checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.182" version = "0.2.183"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112" checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d"
[[package]] [[package]]
name = "libloading" name = "libloading"
@@ -779,6 +1057,18 @@ dependencies = [
"paste", "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]] [[package]]
name = "naga" name = "naga"
version = "0.19.2" version = "0.19.2"
@@ -838,6 +1128,15 @@ dependencies = [
"jni-sys", "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]] [[package]]
name = "num-traits" name = "num-traits"
version = "0.2.19" version = "0.2.19"
@@ -849,9 +1148,9 @@ dependencies = [
[[package]] [[package]]
name = "num_enum" name = "num_enum"
version = "0.7.5" version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1207a7e20ad57b847bbddc6776b968420d38292bbfe2089accff5e19e82454c" checksum = "5d0bca838442ec211fa11de3a8b0e0e8f3a4522575b5c4c06ed722e005036f26"
dependencies = [ dependencies = [
"num_enum_derive", "num_enum_derive",
"rustversion", "rustversion",
@@ -859,9 +1158,9 @@ dependencies = [
[[package]] [[package]]
name = "num_enum_derive" name = "num_enum_derive"
version = "0.7.5" version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff32365de1b6743cb203b710788263c44a03de03802daf96092f2da4fe6ba4d7" checksum = "680998035259dcfcafe653688bf2aa6d3e2dc05e98be6ab46afb089dc84f1df8"
dependencies = [ dependencies = [
"proc-macro-crate", "proc-macro-crate",
"proc-macro2", "proc-macro2",
@@ -1093,15 +1392,21 @@ dependencies = [
[[package]] [[package]]
name = "once_cell" name = "once_cell"
version = "1.21.3" version = "1.21.4"
source = "registry+https://github.com/rust-lang/crates.io-index" 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]] [[package]]
name = "orbclient" name = "orbclient"
version = "0.3.50" version = "0.3.51"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52ad2c6bae700b7aa5d1cc30c59bdd3a1c180b09dbaea51e2ae2b8e1cf211fdd" checksum = "59aed3b33578edcfa1bc96a321d590d31832b6ad55a26f0313362ce687e9abd6"
dependencies = [ dependencies = [
"libc", "libc",
"libredox", "libredox",
@@ -1139,12 +1444,27 @@ dependencies = [
"windows-link", "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]] [[package]]
name = "paste" name = "paste"
version = "1.0.15" version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
[[package]]
name = "pathdiff"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3"
[[package]] [[package]]
name = "percent-encoding" name = "percent-encoding"
version = "2.3.2" version = "2.3.2"
@@ -1316,6 +1636,55 @@ dependencies = [
"bitflags 2.11.0", "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]] [[package]]
name = "renderdoc-sys" name = "renderdoc-sys"
version = "1.1.0" version = "1.1.0"
@@ -1410,6 +1779,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
dependencies = [ dependencies = [
"serde_core", "serde_core",
"serde_derive",
] ]
[[package]] [[package]]
@@ -1439,10 +1809,45 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]] [[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" version = "0.1.0"
dependencies = [ dependencies = [
"clap",
"clap-repl",
"parse_int",
"pixels", "pixels",
"wait_on_address",
"winit", "winit",
"winit_input_helper", "winit_input_helper",
] ]
@@ -1523,6 +1928,40 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" 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]] [[package]]
name = "syn" name = "syn"
version = "1.0.109" version = "1.0.109"
@@ -1601,18 +2040,18 @@ dependencies = [
[[package]] [[package]]
name = "toml_datetime" 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" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32c2555c699578a4f59f0cc68e5116c8d7cabbd45e1409b989d4be085b53f13e" checksum = "9b320e741db58cac564e26c607d3cc1fdc4a88fd36c879568c07856ed83ff3e9"
dependencies = [ dependencies = [
"serde_core", "serde_core",
] ]
[[package]] [[package]]
name = "toml_edit" 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" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7193cbd0ce53dc966037f54351dbbcf0d5a642c7f0038c382ef9e677ce8c13f2" checksum = "8ca1a40644a28bce036923f6a431df0b34236949d111cc07cb6dca830c9ef2e1"
dependencies = [ dependencies = [
"indexmap", "indexmap",
"toml_datetime", "toml_datetime",
@@ -1622,9 +2061,9 @@ dependencies = [
[[package]] [[package]]
name = "toml_parser" 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" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "702d4415e08923e7e1ef96cd5727c0dfed80b4d2fa25db9647fe5eb6f7c5a4c4" checksum = "7df25b4befd31c4816df190124375d5a20c6b6921e2cad937316de3fccd63420"
dependencies = [ dependencies = [
"winnow", "winnow",
] ]
@@ -1678,18 +2117,53 @@ version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
[[package]]
name = "unicode-width"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254"
[[package]] [[package]]
name = "unicode-xid" name = "unicode-xid"
version = "0.2.6" version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
[[package]]
name = "utf8parse"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]] [[package]]
name = "version_check" name = "version_check"
version = "0.9.5" version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" 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]] [[package]]
name = "walkdir" name = "walkdir"
version = "2.5.0" version = "2.5.0"
@@ -1700,6 +2174,12 @@ dependencies = [
"winapi-util", "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]] [[package]]
name = "wasip2" name = "wasip2"
version = "1.0.2+wasi-0.2.9" 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" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be"
dependencies = [ dependencies = [
"windows-core", "windows-core 0.52.0",
"windows-targets 0.52.6", "windows-targets 0.52.6",
] ]
@@ -2070,12 +2550,65 @@ dependencies = [
"windows-targets 0.52.6", "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]] [[package]]
name = "windows-link" name = "windows-link"
version = "0.2.1" version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" 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]] [[package]]
name = "windows-sys" name = "windows-sys"
version = "0.45.0" version = "0.45.0"
@@ -2103,6 +2636,15 @@ dependencies = [
"windows-targets 0.52.6", "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]] [[package]]
name = "windows-sys" name = "windows-sys"
version = "0.61.2" version = "0.61.2"
@@ -2136,13 +2678,30 @@ dependencies = [
"windows_aarch64_gnullvm 0.52.6", "windows_aarch64_gnullvm 0.52.6",
"windows_aarch64_msvc 0.52.6", "windows_aarch64_msvc 0.52.6",
"windows_i686_gnu 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_i686_msvc 0.52.6",
"windows_x86_64_gnu 0.52.6", "windows_x86_64_gnu 0.52.6",
"windows_x86_64_gnullvm 0.52.6", "windows_x86_64_gnullvm 0.52.6",
"windows_x86_64_msvc 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]] [[package]]
name = "windows_aarch64_gnullvm" name = "windows_aarch64_gnullvm"
version = "0.42.2" version = "0.42.2"
@@ -2155,6 +2714,12 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53"
[[package]] [[package]]
name = "windows_aarch64_msvc" name = "windows_aarch64_msvc"
version = "0.42.2" version = "0.42.2"
@@ -2167,6 +2732,12 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_aarch64_msvc"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006"
[[package]] [[package]]
name = "windows_i686_gnu" name = "windows_i686_gnu"
version = "0.42.2" version = "0.42.2"
@@ -2179,12 +2750,24 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnu"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3"
[[package]] [[package]]
name = "windows_i686_gnullvm" name = "windows_i686_gnullvm"
version = "0.52.6" version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_gnullvm"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c"
[[package]] [[package]]
name = "windows_i686_msvc" name = "windows_i686_msvc"
version = "0.42.2" version = "0.42.2"
@@ -2197,6 +2780,12 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_i686_msvc"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2"
[[package]] [[package]]
name = "windows_x86_64_gnu" name = "windows_x86_64_gnu"
version = "0.42.2" version = "0.42.2"
@@ -2209,6 +2798,12 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 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]] [[package]]
name = "windows_x86_64_gnullvm" name = "windows_x86_64_gnullvm"
version = "0.42.2" version = "0.42.2"
@@ -2221,6 +2816,12 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 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]] [[package]]
name = "windows_x86_64_msvc" name = "windows_x86_64_msvc"
version = "0.42.2" version = "0.42.2"
@@ -2233,6 +2834,12 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 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]] [[package]]
name = "winit" name = "winit"
version = "0.30.13" version = "0.30.13"
@@ -2297,9 +2904,9 @@ dependencies = [
[[package]] [[package]]
name = "winnow" name = "winnow"
version = "0.7.15" version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" checksum = "a90e88e4667264a994d34e6d1ab2d26d398dcdca8b7f52bec8668957517fc7d8"
dependencies = [ dependencies = [
"memchr", "memchr",
] ]
@@ -2375,18 +2982,18 @@ checksum = "3ae8337f8a065cfc972643663ea4279e04e7256de865aa66fe25cec5fb912d3f"
[[package]] [[package]]
name = "zerocopy" name = "zerocopy"
version = "0.8.40" version = "0.8.47"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a789c6e490b576db9f7e6b6d661bcc9799f7c0ac8352f56ea20193b2681532e5" checksum = "efbb2a062be311f2ba113ce66f697a4dc589f85e78a4aea276200804cea0ed87"
dependencies = [ dependencies = [
"zerocopy-derive", "zerocopy-derive",
] ]
[[package]] [[package]]
name = "zerocopy-derive" name = "zerocopy-derive"
version = "0.8.40" version = "0.8.47"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f65c489a7071a749c849713807783f70672b28094011623e200cb86dcb835953" checksum = "0e8bc7269b54418e7aeeef514aa68f8690b8c0489a06b0136e5f57c4c5ccab89"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",

View File

@@ -1,9 +1,7 @@
[package] [workspace]
name = "sim" resolver = "3"
version = "0.1.0" members = ["simu","asm"]
edition = "2024"
[dependencies] [profile.release]
pixels = "0.15.0" opt-level = 2
winit = { version = "0.30.13", features = ["x11", "x11-dl", "x11rb", "ahash", "bytemuck", "memmap2", "rwh_06", "sctk", "sctk-adwaita"] } # panic = "abort"
winit_input_helper = "0.17.0"

122
README.md
View File

@@ -1,14 +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é
usage: cargo run --release path_to_bin # Simulateur
## pour lancer la simulation:
- depuis ce dossier
`cargo run --release -p simu fichierentre`
- depuis le sous dossier simu
`cargo run --release fichierentre`
MMIO: ## Features
écran: à partir de 0x01000000 rajouter --features=[liste séparé par des virgules]
- pixels en 0BGR, de en haut a gauche a en bas a droite. - div_mul: Support des instruction de multiplication / division
clavier: 0x01200000 - rgba: Écran au format RGBA plutot que 0BGR (alpha ignoré mais stocké)
- scancode (comme sim.py, != du simulateur c) - rich_keyboard: rajoute trois champs de mmio pour le clavier. Voir MMIO
clock: 0x01200004 - debug: repl similaire a la version python du simulateur
- millisecondes écoulées depuis la création du simulateur - futex: accélère le programme. ne marche pas sur les macs. actif par default, utiliser --no-default-features pour le desactiver
(nb: un u32 peut stocker 49 jours)
### 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
View 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
View File

@@ -0,0 +1,2 @@
[toolchain]
channel = "nightly"

699
asm/src/main.rs Normal file
View 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");
}
}

Binary file not shown.

24
simu/Cargo.toml Normal file
View 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
View 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
View File

@@ -0,0 +1,2 @@
[toolchain]
channel = "nightly"

803
simu/src/cpu.rs Normal file
View 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
View 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
View 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) {
//
}
}

View File

@@ -1,383 +0,0 @@
use std::{
hint::{likely, unlikely},
io::Read,
process::exit,
sync::{Mutex, atomic::AtomicU32},
thread::sleep,
time::{self, Duration, Instant},
};
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),
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(),
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 << 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),
(1, 0b1001) => Self::Smull(rd, rx, op2),
(1, 0b1010) => Self::Smulh(rd, rx, op2),
(1, 0b1011) => Self::Umull(rd, rx, op2),
(1, 0b1100) => Self::Umulh(rd, rx, op2),
(1, 0b1101) => Self::Div(rd, rx, op2),
(1, 0b1110) => Self::Mod(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> {
creation: Instant,
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 {
creation: Instant::now(),
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::Umull(reg, reg1, op2) => {
self.rg_wr(reg, self.rg_r(reg1).wrapping_mul(self.resolve(op2)));
self.pc += 1;
}
Instruction::Smull(reg, reg1, op2) => {
self.rg_wr(
reg,
(self.rg_r(reg1) as i32).wrapping_mul(self.resolve(op2) as i32) as u32,
);
self.pc += 1;
}
Instruction::Umulh(reg, reg1, op2) => {
self.rg_wr(reg, self.rg_r(reg1).widening_mul(self.resolve(op2)).1);
self.pc += 1;
}
Instruction::Smulh(reg, reg1, op2) => {
self.rg_wr(
reg,
(self.rg_r(reg1) as i32)
.widening_mul(self.resolve(op2) as i32)
.1 as u32,
);
self.pc += 1;
}
Instruction::Div(reg, reg1, op2) => {
self.rg_wr(reg, self.rg_r(reg1) / self.resolve(op2));
self.pc += 1
}
Instruction::Mod(reg, reg1, op2) => {
self.rg_wr(reg, self.rg_r(reg1) % self.resolve(op2));
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 if addr == 0x01200004{
time::Instant::now().duration_since(self.creation).as_millis() as u32
} 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(mut addr) => {
self.sp -= 1;
self.ram[self.sp] = ((self.pc << 2) + 4) as u32;
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::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]
}
}

View File

@@ -1,136 +0,0 @@
#![feature(likely_unlikely, widening_mul)]
#![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 + 8, 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);
}
}
})
}