From 6d238b24c171bbde8143c60b64eec610b6abf5b1 Mon Sep 17 00:00:00 2001 From: Aubrey Taylor Date: Sun, 23 Mar 2025 20:33:38 -0600 Subject: [PATCH] a decently improved architecture --- .envrc | 3 +- Cargo.lock | 1115 +++++++++++++++++ Cargo.toml | 7 + build.gradle.kts | 142 ++- flake.nix | 1 + proto/all.proto | 28 + rust/Cargo.toml | 7 +- rust/build.gradle.kts | 8 +- rust/build.rs | 6 + rust/examples/condvar.rs | 37 + rust/src/component/mmio.rs | 251 ---- rust/src/component/mod.rs | 120 -- rust/src/components/core.rs | 72 ++ rust/src/components/debug_chat.rs | 50 + rust/src/components/interrupt_controller.rs | 29 + rust/src/components/mod.rs | 66 + rust/src/components/storage.rs | 2 + rust/src/computer.rs | 235 ++-- rust/src/core.rs | 302 ++--- rust/src/device_bus.rs | 174 +++ rust/src/lib.rs | 50 +- rust/src/memory_map.rs | 3 - rust/src/unsync_cell.rs | 82 +- settings.gradle.kts | 1 + .../sanae/golemcomputers/blocks/GolemBlock.kt | 5 +- .../golemcomputers/blocks/GolemBlockEntity.kt | 15 +- .../sanae/golemcomputers/computer/Computer.kt | 86 +- .../golemcomputers/computer/RustNative.kt | 29 +- .../computer/components/ChatComponent.kt | 28 +- .../computer/components/Component.kt | 31 - 30 files changed, 2061 insertions(+), 924 deletions(-) create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 proto/all.proto create mode 100644 rust/build.rs create mode 100644 rust/examples/condvar.rs delete mode 100644 rust/src/component/mmio.rs delete mode 100644 rust/src/component/mod.rs create mode 100644 rust/src/components/core.rs create mode 100644 rust/src/components/debug_chat.rs create mode 100644 rust/src/components/interrupt_controller.rs create mode 100644 rust/src/components/mod.rs create mode 100644 rust/src/components/storage.rs create mode 100644 rust/src/device_bus.rs delete mode 100644 rust/src/memory_map.rs delete mode 100644 src/main/kotlin/ca/sanae/golemcomputers/computer/components/Component.kt diff --git a/.envrc b/.envrc index 8392d15..991edf7 100644 --- a/.envrc +++ b/.envrc @@ -1 +1,2 @@ -use flake \ No newline at end of file +use flake +TMPDIR=/tmp diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..4aa4936 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,1115 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "anyhow" +version = "1.0.97" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f" + +[[package]] +name = "async-channel" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a" +dependencies = [ + "concurrent-queue", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-executor" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30ca9a001c1e8ba5149f91a74362376cc6bc5b919d92d988668657bd570bdcec" +dependencies = [ + "async-task", + "concurrent-queue", + "fastrand", + "futures-lite", + "slab", +] + +[[package]] +name = "async-fs" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebcd09b382f40fcd159c2d695175b2ae620ffa5f3bd6f664131efff4e8b9e04a" +dependencies = [ + "async-lock", + "blocking", + "futures-lite", +] + +[[package]] +name = "async-io" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a2b323ccce0a1d90b449fd71f2a06ca7faa7c54c2751f06c9bd851fc061059" +dependencies = [ + "async-lock", + "cfg-if", + "concurrent-queue", + "futures-io", + "futures-lite", + "parking", + "polling", + "rustix 0.38.44", + "slab", + "tracing", + "windows-sys 0.59.0", +] + +[[package]] +name = "async-lock" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" +dependencies = [ + "event-listener", + "event-listener-strategy", + "pin-project-lite", +] + +[[package]] +name = "async-net" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b948000fad4873c1c9339d60f2623323a0cfd3816e5181033c6a5cb68b2accf7" +dependencies = [ + "async-io", + "blocking", + "futures-lite", +] + +[[package]] +name = "async-process" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63255f1dc2381611000436537bbedfe83183faa303a5a0edaf191edef06526bb" +dependencies = [ + "async-channel", + "async-io", + "async-lock", + "async-signal", + "async-task", + "blocking", + "cfg-if", + "event-listener", + "futures-lite", + "rustix 0.38.44", + "tracing", +] + +[[package]] +name = "async-signal" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "637e00349800c0bdf8bfc21ebbc0b6524abea702b0da4168ac00d070d0c0b9f3" +dependencies = [ + "async-io", + "async-lock", + "atomic-waker", + "cfg-if", + "futures-core", + "futures-io", + "rustix 0.38.44", + "signal-hook-registry", + "slab", + "windows-sys 0.59.0", +] + +[[package]] +name = "async-task" +version = "4.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "bitfield-struct" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2be5a46ba01b60005ae2c51a36a29cfe134bcacae2dd5cedcd4615fbaad1494b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "bitflags" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" +dependencies = [ + "serde", +] + +[[package]] +name = "blocking" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea" +dependencies = [ + "async-channel", + "async-task", + "futures-io", + "futures-lite", + "piper", +] + +[[package]] +name = "btree_monstrousity" +version = "0.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ec92912346b936c974181a172d9abc81f50d41e40118fc101dac8aa8134bee3" +dependencies = [ + "cfg-if", + "rustversion", +] + +[[package]] +name = "bytes" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" + +[[package]] +name = "cc" +version = "1.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be714c154be609ec7f5dad223a33bf1482fff90472de28f7362806e6d4832b8c" +dependencies = [ + "shlex", +] + +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cmake" +version = "0.1.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7caa3f9de89ddbe2c607f4101924c5abec803763ae9534e4f4d7d8f84aa81f0" +dependencies = [ + "cc", +] + +[[package]] +name = "combine" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "disarm64" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ccd32ba4e20b8b4a0db9c11d3a34a38ef03d729aa1d9ea4c9cd5dc6b29b5946" +dependencies = [ + "bitfield-struct", + "disarm64_defn", +] + +[[package]] +name = "disarm64_defn" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a968ec75d668b2e37898ba8b84d522e7261abde8721d0da8d877c2f51d911b2" +dependencies = [ + "bitflags", + "serde", + "strum", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "event-listener" +version = "5.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3492acde4c3fc54c845eaab3eed8bd00c7a7d881f78bfc801e43a93dec1331ae" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c3e4e0dd3673c1139bf041f3008816d9cf2946bbfac2945c09e523b8d7b05b2" +dependencies = [ + "event-listener", + "pin-project-lite", +] + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "fixedbitset" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-lite" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5edaec856126859abb19ed65f39e90fea3a9574b9707f13539acf4abf7eb532" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + +[[package]] +name = "getrandom" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8" +dependencies = [ + "cfg-if", + "libc", + "wasi", + "windows-targets 0.52.6", +] + +[[package]] +name = "golem-computers" +version = "0.1.0" +dependencies = [ + "bitfield-struct", + "disarm64", + "jni", + "nodit", + "num-derive", + "num-traits", + "oneshot", + "pollster", + "prost", + "prost-build", + "smol", + "static_assertions", + "unicorn-engine", + "zerocopy", +] + +[[package]] +name = "hashbrown" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" + +[[package]] +name = "indexmap" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + +[[package]] +name = "jni" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +dependencies = [ + "cesu8", + "cfg-if", + "combine", + "jni-sys", + "log", + "thiserror", + "walkdir", + "windows-sys 0.45.0", +] + +[[package]] +name = "jni-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" + +[[package]] +name = "libc" +version = "0.2.170" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "875b3680cb2f8f71bdcf9a30f38d48282f5d3c95cbf9b3fa57269bb5d5c06828" + +[[package]] +name = "linux-raw-sys" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" + +[[package]] +name = "linux-raw-sys" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db9c683daf087dc577b7506e9695b3d556a9f3849903fa28186283afd6809e9" + +[[package]] +name = "log" +version = "0.4.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "multimap" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "defc4c55412d89136f966bbb339008b474350e5e6e78d2714439c386b3137a03" + +[[package]] +name = "nodit" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f74369f80df24efd2266602fdcd8fcd56a17c2e2c94ab48d2f7a15eaa137bf49" +dependencies = [ + "btree_monstrousity", + "itertools 0.13.0", + "smallvec", +] + +[[package]] +name = "num-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e" + +[[package]] +name = "oneshot" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ce411919553d3f9fa53a0880544cda985a112117a0444d5ff1e870a893d6ea" + +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + +[[package]] +name = "petgraph" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772" +dependencies = [ + "fixedbitset", + "indexmap", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "piper" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066" +dependencies = [ + "atomic-waker", + "fastrand", + "futures-io", +] + +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + +[[package]] +name = "polling" +version = "3.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a604568c3202727d1507653cb121dbd627a58684eb09a820fd746bee38b4442f" +dependencies = [ + "cfg-if", + "concurrent-queue", + "hermit-abi", + "pin-project-lite", + "rustix 0.38.44", + "tracing", + "windows-sys 0.59.0", +] + +[[package]] +name = "pollster" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f3a9f18d041e6d0e102a0a46750538147e5e8992d3b4873aaafee2520b00ce3" + +[[package]] +name = "prettyplease" +version = "0.2.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1ccf34da56fc294e7d4ccf69a85992b7dfb826b7cf57bac6a70bba3494cc08a" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.94" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "prost" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2796faa41db3ec313a31f7624d9286acf277b52de526150b7e69f3debf891ee5" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-build" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be769465445e8c1474e9c5dac2018218498557af32d9ed057325ec9a41ae81bf" +dependencies = [ + "heck", + "itertools 0.14.0", + "log", + "multimap", + "once_cell", + "petgraph", + "prettyplease", + "prost", + "prost-types", + "regex", + "syn", + "tempfile", +] + +[[package]] +name = "prost-derive" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" +dependencies = [ + "anyhow", + "itertools 0.14.0", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "prost-types" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52c2c1bf36ddb1a1c396b3601a3cec27c2462e45f07c386894ec3ccf5332bd16" +dependencies = [ + "prost", +] + +[[package]] +name = "quote" +version = "1.0.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1f1914ce909e1658d9907913b4b91947430c7d9be598b15a1912935b8c04801" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "rustix" +version = "0.38.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys 0.4.15", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustix" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dade4812df5c384711475be5fcd8c162555352945401aed22a35bffeab61f657" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys 0.9.2", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustversion" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "serde" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" + +[[package]] +name = "smol" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a33bd3e260892199c3ccfc487c88b2da2265080acb316cd920da72fdfd7c599f" +dependencies = [ + "async-channel", + "async-executor", + "async-fs", + "async-io", + "async-lock", + "async-net", + "async-process", + "blocking", + "futures-lite", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "strum" +version = "0.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" +dependencies = [ + "strum_macros", +] + +[[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", +] + +[[package]] +name = "syn" +version = "2.0.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tempfile" +version = "3.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c317e0a526ee6120d8dabad239c8dadca62b24b6f168914bbbc8e2fb1f0e567" +dependencies = [ + "cfg-if", + "fastrand", + "getrandom", + "once_cell", + "rustix 1.0.1", + "windows-sys 0.59.0", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing" +version = "0.1.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +dependencies = [ + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "unicorn-engine" +version = "2.1.3" +source = "git+https://github.com/Sanae6/unicorn/?branch=cpreg#11c3fde4dc0f38e00688dee3e7424bd6f231e748" +dependencies = [ + "bitflags", + "cc", + "cmake", + "libc", + "pkg-config", + "zerocopy", +] + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.13.3+wasi-0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2" +dependencies = [ + "wit-bindgen-rt", +] + +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "wit-bindgen-rt" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" +dependencies = [ + "bitflags", +] + +[[package]] +name = "zerocopy" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd97444d05a4328b90e75e503a34bad781f14e28a823ad3557f0750df1ebcbc6" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6352c01d0edd5db859a63e2605f4ea3183ddbd15e2c4a9e7d32184df75e4f154" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..ec042e9 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,7 @@ +[workspace] +resolver = "3" +members = ["rust"] + +[profile.release] +panic = "abort" +debug-assertions = true diff --git a/build.gradle.kts b/build.gradle.kts index 262875d..8bc8e27 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,114 +2,126 @@ import org.jetbrains.kotlin.gradle.dsl.JvmTarget import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { - kotlin("jvm") version "2.1.10" - id("fabric-loom") version "1.9-SNAPSHOT" - id("maven-publish") + kotlin("jvm") version "2.1.10" + id("fabric-loom") version "1.9-SNAPSHOT" + id("maven-publish") + id("com.google.protobuf") version "0.9.4" } version = project.property("mod_version") as String group = project.property("maven_group") as String base { - archivesName.set(project.property("archives_base_name") as String) + archivesName.set(project.property("archives_base_name") as String) } val targetJavaVersion = 21 java { - toolchain.languageVersion = JavaLanguageVersion.of(targetJavaVersion) - // Loom will automatically attach sourcesJar to a RemapSourcesJar task and to the "build" task - // if it is present. - // If you remove this line, sources will not be generated. - withSourcesJar() + toolchain.languageVersion = JavaLanguageVersion.of(targetJavaVersion) + // Loom will automatically attach sourcesJar to a RemapSourcesJar task and to the "build" task + // if it is present. + // If you remove this line, sources will not be generated. + withSourcesJar() } loom { - mods { - register("golemcomputers") { - sourceSet("main") - } + mods { + register("golemcomputers") { + sourceSet("main") } + } } repositories { - // Add repositories to retrieve artifacts from in here. - // You should only use this when depending on other mods because - // Loom adds the essential maven repositories to download Minecraft and libraries from automatically. - // See https://docs.gradle.org/current/userguide/declaring_repositories.html - // for more information about repositories. + // Add repositories to retrieve artifacts from in here. + // You should only use this when depending on other mods because + // Loom adds the essential maven repositories to download Minecraft and libraries from automatically. + // See https://docs.gradle.org/current/userguide/declaring_repositories.html + // for more information about repositories. +} + +protobuf { + protoc { + artifact = "com.google.protobuf:protoc:4.30.0" + } + generateProtoTasks { + file("./proto/all.proto") + } } configurations { - create("rustlib") { - isCanBeConsumed = false - isCanBeResolved = true - } + create("rustlib") { + isCanBeConsumed = false + isCanBeResolved = true + } } dependencies { - // To change the versions see the gradle.properties file - minecraft("com.mojang:minecraft:${project.property("minecraft_version")}") - mappings("net.fabricmc:yarn:${project.property("yarn_mappings")}:v2") - modImplementation("net.fabricmc:fabric-loader:${project.property("loader_version")}") - modImplementation("net.fabricmc:fabric-language-kotlin:${project.property("kotlin_loader_version")}") + // To change the versions see the gradle.properties file + minecraft("com.mojang:minecraft:${project.property("minecraft_version")}") + mappings("net.fabricmc:yarn:${project.property("yarn_mappings")}:v2") + modImplementation("net.fabricmc:fabric-loader:${project.property("loader_version")}") + modImplementation("net.fabricmc:fabric-language-kotlin:${project.property("kotlin_loader_version")}") - modImplementation("net.fabricmc.fabric-api:fabric-api:${project.property("fabric_version")}") - add("rustlib", project(":rust")) - implementation("com.github.jhg023:Pbbl:1.0.2") + modImplementation("net.fabricmc.fabric-api:fabric-api:${project.property("fabric_version")}") + add("rustlib", project(":rust")) + implementation("com.github.jhg023:Pbbl:1.0.2") + implementation("com.google.protobuf:protobuf-java:4.30.0") + protobuf(files("proto/")) } tasks.processResources { - inputs.property("version", project.version) - inputs.property("minecraft_version", project.property("minecraft_version")) - inputs.property("loader_version", project.property("loader_version")) - filteringCharset = "UTF-8" + inputs.property("version", project.version) + inputs.property("minecraft_version", project.property("minecraft_version")) + inputs.property("loader_version", project.property("loader_version")) + filteringCharset = "UTF-8" - from(configurations.getByName("rustlib")) + from(configurations.getByName("rustlib")) - filesMatching("fabric.mod.json") { - expand( - "version" to project.version, - "minecraft_version" to project.property("minecraft_version"), - "loader_version" to project.property("loader_version"), - "kotlin_loader_version" to project.property("kotlin_loader_version") - ) - } + filesMatching("fabric.mod.json") { + expand( + "version" to project.version, + "minecraft_version" to project.property("minecraft_version"), + "loader_version" to project.property("loader_version"), + "kotlin_loader_version" to project.property("kotlin_loader_version") + ) + } } tasks.withType().configureEach { - // ensure that the encoding is set to UTF-8, no matter what the system default is - // this fixes some edge cases with special characters not displaying correctly - // see http://yodaconditions.net/blog/fix-for-java-file-encoding-problems-with-gradle.html - // If Javadoc is generated, this must be specified in that task too. - options.encoding = "UTF-8" - options.release.set(targetJavaVersion) + // ensure that the encoding is set to UTF-8, no matter what the system default is + // this fixes some edge cases with special characters not displaying correctly + // see http://yodaconditions.net/blog/fix-for-java-file-encoding-problems-with-gradle.html + // If Javadoc is generated, this must be specified in that task too. + options.encoding = "UTF-8" + options.release.set(targetJavaVersion) } tasks.withType().configureEach { - compilerOptions.jvmTarget.set(JvmTarget.fromTarget(targetJavaVersion.toString())) + compilerOptions.jvmTarget.set(JvmTarget.fromTarget(targetJavaVersion.toString())) } tasks.jar { - from("LICENSE") { - rename { "${it}_${project.base.archivesName}" } - } + from("LICENSE") { + rename { "${it}_${project.base.archivesName}" } + } } // configure the maven publication publishing { - publications { - create("mavenJava") { - artifactId = project.property("archives_base_name") as String - from(components["java"]) - } + publications { + create("mavenJava") { + artifactId = project.property("archives_base_name") as String + from(components["java"]) } + } - // See https://docs.gradle.org/current/userguide/publishing_maven.html for information on how to set up publishing. - repositories { - // Add repositories to publish to here. - // Notice: This block does NOT have the same function as the block in the top level. - // The repositories here will be used for publishing your artifact, not for - // retrieving dependencies. - } + // See https://docs.gradle.org/current/userguide/publishing_maven.html for information on how to set up publishing. + repositories { + // Add repositories to publish to here. + // Notice: This block does NOT have the same function as the block in the top level. + // The repositories here will be used for publishing your artifact, not for + // retrieving dependencies. + } } diff --git a/flake.nix b/flake.nix index 64a4d5f..a7c4c87 100644 --- a/flake.nix +++ b/flake.nix @@ -32,6 +32,7 @@ cmake ninja pkg-config + protobuf #needed for minecraft to function properly libGL diff --git a/proto/all.proto b/proto/all.proto new file mode 100644 index 0000000..458dc93 --- /dev/null +++ b/proto/all.proto @@ -0,0 +1,28 @@ +syntax = "proto3"; + +package computer; + +option java_package = "ca.sanae.golemcomputers.computer"; +option java_outer_classname = "NativeProtobuf"; + +message ComputerInit { + repeated ComponentInit components = 1; + + uint32 primary_ram_pages = 2; + sint32 primary_rom_index = 3; +} + +message ComponentInit { + oneof data { + CoreComponentInit core = 1; + ChatComponentInit chat = 2; + } +} + +message CoreComponentInit { + uint32 instructions_per_tick = 1; +} + +message ChatComponentInit { + sint32 java_index = 1; +} diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 89627a2..4e5e262 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -10,15 +10,16 @@ crate-type = ["cdylib", "rlib"] bitfield-struct = "0.10.1" disarm64 = "0.1.24" jni = "0.21.1" +nodit = "0.9.2" num-derive = "0.4.2" num-traits = "0.2.19" oneshot = "0.1.10" pollster = "0.4.0" +prost = "0.13.5" smol = "2.0.2" static_assertions = "1.1.0" unicorn-engine = { version = "2.1.1", default-features = false, features = ["arch_aarch64"], git = "https://github.com/Sanae6/unicorn/", branch = "cpreg" } zerocopy = { version = "0.8.20", features = ["derive"] } -[profile.release] -panic = "abort" -debug-assertions = true +[build-dependencies] +prost-build = "0.13.5" diff --git a/rust/build.gradle.kts b/rust/build.gradle.kts index b501b7e..ca93c46 100644 --- a/rust/build.gradle.kts +++ b/rust/build.gradle.kts @@ -1,20 +1,18 @@ val task = tasks.register("default") { commandLine("cargo", "build", "--release") val library = System.mapLibraryName("golem_computers") - val libraryPath = "$workingDir/target/release/$library" + val libraryPath = "$workingDir/../target/release/$library" outputs.file(libraryPath) inputs.dir("$workingDir/src") doLast { if (!file(libraryPath).exists()) { - throw GradleException("the library wasn't properly build! expected $libraryPath") + throw GradleException("the library wasn't properly built! expected $libraryPath") } } } configurations { - create("default") { - - } + create("default") } artifacts.add("default", task) \ No newline at end of file diff --git a/rust/build.rs b/rust/build.rs new file mode 100644 index 0000000..760cf69 --- /dev/null +++ b/rust/build.rs @@ -0,0 +1,6 @@ +use std::io::Result; + +fn main() -> Result<()> { + prost_build::compile_protos(&["../proto/all.proto"], &["../proto"])?; + Ok(()) +} diff --git a/rust/examples/condvar.rs b/rust/examples/condvar.rs new file mode 100644 index 0000000..5cc7716 --- /dev/null +++ b/rust/examples/condvar.rs @@ -0,0 +1,37 @@ +use std::{ + sync::{Arc, Condvar, Mutex}, + thread, + time::Duration, +}; + +fn main() { + let pair = Arc::new((Mutex::new(false), Condvar::new())); + let pair_clone1 = Arc::clone(&pair); + let pair_clone2 = Arc::clone(&pair); + + thread::spawn(move || { + let (lock, cvar) = &*pair_clone1; + let mut started = lock.lock().unwrap(); + *started = true; + std::thread::sleep(Duration::from_secs(3)); + cvar.notify_all(); + }); + + thread::spawn(move || { + let (lock, cvar) = &*pair_clone2; + let mut started = lock.lock().unwrap(); + while !*started { + println!("waiting 2"); + started = cvar.wait(started).unwrap(); + } + println!("Thread 2 proceeding..."); + }); + + let (lock, cvar) = &*pair; + let mut started = lock.lock().unwrap(); + while !*started { + println!("waiting 1"); + started = cvar.wait(started).unwrap(); + } + println!("Thread 1 proceeding..."); +} diff --git a/rust/src/component/mmio.rs b/rust/src/component/mmio.rs deleted file mode 100644 index 1762c67..0000000 --- a/rust/src/component/mmio.rs +++ /dev/null @@ -1,251 +0,0 @@ -use std::{ - mem::offset_of, - ops::{Deref, DerefMut, Range}, - pin::Pin, - sync::{atomic::Ordering, Arc}, -}; - -use bitfield_struct::bitfield; -use jni::JavaVM; -use unicorn_engine::Unicorn; -use zerocopy::{ - big_endian::U16 as U16BE, - little_endian::{U16, U32, U64}, - FromBytes, Immutable, IntoBytes, KnownLayout, -}; - -use crate::{ - align_up, component::{Component, ComponentInterface}, core::CpuContext, memory_map::RAM_START, overlapping, range_of_field, unsync_cell::UnsyncCell, unsync_read -}; - -use super::ComponentDma; - -pub struct ComponentIoResult { - component_index: usize, - direction: ComponentIoDirection, -} - -pub enum ComponentIoDirection { - ToComponent, - ToGuest, -} - -impl Component { - pub fn perform_write( - &self, - range: Range, - writer: impl FnOnce(&UnsyncCell), - ram: Arc>, - java_vm: JavaVM, - ) -> Option>>> { - let Self::Populated { - read_transfer_active, - write_transfer_active, - info, - interface, - own_index, - java_component, - .. - } = self - else { - return None; - }; - - if overlapping(&range, &range_of_field!(ComponentInterface, info)) { - return None; - } - let read_range = range_of_field!(ComponentInterface, read_dma); - if overlapping(&range, &read_range) { - if read_transfer_active.load(Ordering::Acquire) || read_range != range { - return None; - } - - writer(interface); - - let dma: ComponentDma = unsync_read!(interface, ComponentInterface, read_dma); - if dma.dma_enabled() - && read_transfer_active.compare_exchange(false, true, Ordering::AcqRel, Ordering::Acquire).is_ok() - { - if !overlapping( - &(dma.address..dma.address + dma.size()), - &(RAM_START..RAM_START + ram.len() as u64), - ) { - read_transfer_active.store(false, Ordering::Release); - return None; - } - let bytes_per_chunk: u32 = info.bytes_per_tick / 5; - let java_component = - let index = return Some(Box::pin(async move { - let java_component = java_component; - ComponentIoResult { component_index: 0, direction: todo!() } - })); - } - - return None; - } - // let write_range = range_of_field!(ComponentInterface, write_dma); - // if write_transfer_active && overlapping(range.clone(), write_range) { - // return; - // } - - return None; - } -} - -#[derive(FromBytes, IntoBytes, Immutable, KnownLayout)] -#[repr(C)] -struct ComponentMmioHeader { - component_count: U32, - /// version bump when component access method (offset table) changes or [ComponentInfo] is non-additively changed - version: U16, - control: U16BE, -} - -#[bitfield(u16)] -#[derive(FromBytes, IntoBytes, Immutable, KnownLayout)] -pub struct ComponentMmioControl { - interrupt_added: bool, - interrupt_removed: bool, - #[bits(2)] - _reserved: U16, - /// read-write, indicates the core to fire interrupts on. indicating an invalid core means no core will receive the interrupt - #[bits(4)] - interrupt_firing_core: u8, - #[bits(8)] - _reserved: U16, -} - -pub fn calculate_components_start(component_count: usize) -> usize { - OFFSETS_START + align_up(component_count as u64 * 4 + 16, 0x1000) as usize -} - -fn read_bytes_to_u64(bytes: &[u8]) -> u64 { - match bytes.len() { - 1 => bytes[0] as u64, - 2 => U16::read_from_bytes(bytes).unwrap().get() as u64, - 4 => U32::read_from_bytes(bytes).unwrap().get() as u64, - 8 => U64::read_from_bytes(bytes).unwrap().get() as u64, - 16 => todo!("128 bit reads are currently neither implemented here nor supported by unicorn hooks"), - _ => panic!("expected byte slice of primitive size"), - } -} - -fn write_bytes_from_u64(bytes: &mut [u8], value: u64) { - match bytes.len() { - 1 => bytes[0] = value.try_into().unwrap(), - 2 => U16::new(value.try_into().unwrap()).write_to(bytes).unwrap(), - 4 => U32::new(value.try_into().unwrap()).write_to(bytes).unwrap(), - 8 => U64::new(value).write_to(bytes).unwrap(), - 16 => todo!("128 bit writes are currently neither implemented here nor supported by unicorn hooks"), - _ => panic!("expected byte slice of primitive size"), - } -} - -const OFFSETS_START: usize = 0x200; -const INTERFACE_INTERVAL: usize = 0x800; - -pub fn component_mmio_read(vcpu: &mut Unicorn, offset: u64, size: usize) -> u64 { - let offset = offset as usize; - let component_count = vcpu.get_data().components.len(); - if offset < OFFSETS_START { - // mmio header - println!("reading from mmio start!"); - let header = ComponentMmioHeader { - component_count: U32::new(component_count as u32), - version: U16::new(0), - control: U16BE::new(vcpu.get_data().component_control.read().into_bits()), - }; - if offset > size_of::() { - return 0; - } - let mut region = [0; size_of::() + size_of::()]; - header.write_to_prefix(&mut region).unwrap(); - let range = offset as usize..(offset + size) as usize; - return read_bytes_to_u64(®ion[range]); - } - - if offset < vcpu.get_data().component_data_start { - // offsets - let range = offset as usize..(offset + size) as usize; - let components_range = OFFSETS_START..(size_of::() * component_count); - - let range = range.start - components_range.start as usize..range.end - components_range.start as usize; - let first_offset_index = range.start >> 2; - let offset_buffer: [U32; 3] = std::array::from_fn(|index| { - (first_offset_index + index < component_count as usize) - .then_some(U32::new( - ((first_offset_index + index) * INTERFACE_INTERVAL as usize) as u32, - )) - .unwrap_or_default() - }); - - return read_bytes_to_u64(&offset_buffer.as_bytes()[range]); - } - - // info region - - let offset = offset - vcpu.get_data().component_data_start; - let index = offset / INTERFACE_INTERVAL as usize; - let offset = offset - (index * INTERFACE_INTERVAL as usize); - - let Some(Component::Populated { interface, .. }) = vcpu.get_data().components.get(index) else { - return 0; - }; - - match size { - 1 => interface.read_into::(offset).into(), - 2 => interface.read_into::(offset).into(), - 4 => interface.read_into::(offset).into(), - 8 => interface.read_into::(offset).into(), - _ => unreachable!(), - } -} - -pub fn component_mmio_write(vcpu: &mut Unicorn, offset: u64, size: usize, value: u64) { - let offset = offset as usize; - let range = offset..offset + size; - - if offset < OFFSETS_START { - if overlapping( - &range, - &(offset_of!(ComponentMmioHeader, control)..size_of::()), - ) { - // control included - let mut buffer = [0; size_of::() + size_of::()]; - write_bytes_from_u64(&mut buffer[range], value); - let header = ComponentMmioHeader::ref_from_prefix(&buffer).unwrap().0; - - vcpu.get_data().component_control.write(ComponentMmioControl::from_bits(header.control.get())); - } - - return; - } - - if offset < vcpu.get_data().component_data_start { - return; - } - - let offset = offset - vcpu.get_data().component_data_start; - let index = offset / INTERFACE_INTERVAL as usize; - let offset = offset - (index * INTERFACE_INTERVAL as usize); - - let Some(component) = vcpu.get_data().components.get(index) else { - return; - }; - - if offset > size_of::() { - return; - } - - component.perform_write( - range, - |interface| match size { - 1 => interface.write_from(offset, &(value as u8)), - 2 => interface.write_from(offset, &U16::new(value as u16)), - 4 => interface.write_from(offset, &U32::new(value as u32)), - 8 => interface.write_from(offset, &U64::new(value as u64)), - _ => unreachable!(), - }, - vcpu.get_data().ram, - ); -} diff --git a/rust/src/component/mod.rs b/rust/src/component/mod.rs deleted file mode 100644 index 9fd9c24..0000000 --- a/rust/src/component/mod.rs +++ /dev/null @@ -1,120 +0,0 @@ -pub mod mmio; - -use std::{ - cell::RefCell, - mem::offset_of, - ops::{Deref, DerefMut, Range, RangeBounds}, - pin::Pin, - rc::Rc, - sync::{ - atomic::{AtomicBool, Ordering}, - mpsc, Arc, LazyLock, Mutex, - }, -}; - -use bitfield_struct::bitfield; -use jni::objects::GlobalRef; -use unicorn_engine::Unicorn; -use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout}; - -use crate::{ - core::CpuContext, memory_map::RAM_START, overlapping, range_of_field, subtract_range, unsync_cell::UnsyncCell, - unsync_read, -}; - -#[derive(Default)] -pub enum Component { - #[default] - Empty, - Populated { - own_index: usize, - java_component: GlobalRef, - read_transfer_active: AtomicBool, - write_transfer_active: AtomicBool, - info: ComponentInfo, - interface: UnsyncCell, - }, -} - -#[derive(Clone, Copy, Default, FromBytes, IntoBytes, Immutable, KnownLayout)] -#[repr(C)] -pub struct ComponentInterface { - // read only - pub info: ComponentInfo, - - // read-write - control: ComponentControl, - _restart_device: u8, - _reserved: [u8; 6], - read_dma: ComponentDma, - write_dma: ComponentDma, -} - -#[bitfield(u8)] -#[derive(FromBytes, IntoBytes, Immutable, KnownLayout)] -pub struct ComponentControl { - interrupt_when_added: bool, - interrupt_when_removed: bool, - #[bits(2)] - _reserved: u8, - - #[bits(4)] - interrupt_firing_core: u8, -} - -#[derive(Clone, Copy, Default, FromBytes, IntoBytes, Immutable, KnownLayout)] -#[repr(C)] -pub struct ComponentDma { - pub upper_word: ComponentDmaUpper, - pub address: u64, -} - -impl Deref for ComponentDma { - type Target = ComponentDmaUpper; - - fn deref(&self) -> &Self::Target { - &self.upper_word - } -} - -impl DerefMut for ComponentDma { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.upper_word - } -} - -#[bitfield(u64)] -#[derive(FromBytes, IntoBytes, Immutable, KnownLayout)] -pub struct ComponentDmaUpper { - dma_enabled: bool, - #[bits(3)] - _reserved: u8, - /// read-write, indicates the core to fire interrupts on. indicating an invalid core means no core will receive the interrupt - #[bits(4)] - interrupt_firing_core: u8, - - #[bits(56)] - size: u64, -} - -#[derive(Clone, Copy, FromBytes, IntoBytes, Immutable, KnownLayout)] -#[repr(C)] -pub struct ComponentInfo { - /// a null terminated string, the type name of the component - pub type_name: [u8; 256], - /// a null terminated string, the protocol identifier for how to talk to the component - pub protocol: [u8; 256], - pub bytes_per_tick: u32, - pub version: u32, -} - -impl Default for ComponentInfo { - fn default() -> Self { - Self { - type_name: [0; 256], - protocol: [0; 256], - bytes_per_tick: 0, - version: 0, - } - } -} diff --git a/rust/src/components/core.rs b/rust/src/components/core.rs new file mode 100644 index 0000000..c7fcb8a --- /dev/null +++ b/rust/src/components/core.rs @@ -0,0 +1,72 @@ +use std::{mem::offset_of, sync::{mpsc, Arc}}; + +use bitfield_struct::bitfield; +use zerocopy::{ + little_endian::{U32, U64}, + FromBytes, IntoBytes, KnownLayout, +}; + +use crate::{component_name, core::CoreMessage, overlapping, unsync_cell::UnsyncCell}; + +use super::Component; + +#[derive(Clone, Copy, FromBytes, IntoBytes, KnownLayout)] +#[repr(C)] +pub struct Registers { + pub reset_address: U64, + pub flags: U32, +} + +#[bitfield(u32)] +pub struct Flags { + should_reset: bool, + #[bits(31)] + _reserved: u32, +} + +pub struct CoreComponent { + registers: Arc>, + message_sender: mpsc::Sender, +} + +impl CoreComponent { + pub fn new(registers: Arc>, message_sender: mpsc::Sender) -> Self { + Self { + registers, + message_sender, + } + } +} + +impl Component for CoreComponent { + fn name() -> [u8; 0x30] { + component_name!(b"core") + } + + fn memory_map_size(&self) -> u64 { + size_of::() as u64 + } + + fn read(&self, _: &mut jni::JNIEnv, offset: u64, data: &mut [u8]) { + assert!((offset + data.len() as u64) < self.memory_map_size() as u64); + self.registers.read_into_byte_slice(offset as usize, data); + } + + fn write(&self, _: &mut jni::JNIEnv, offset: u64, data: &[u8]) { + assert!((offset + data.len() as u64) < self.memory_map_size() as u64); + self.registers.write_from_byte_slice(offset as usize, data); + + if overlapping( + &(offset..offset + data.len() as u64), + &(offset_of!(Registers, flags) as u64..size_of::() as u64), + ) { + let mut registers = self.registers.read(); + let flags = Flags::from_bits(registers.flags.get()); + if flags.should_reset() { + let _ = self.message_sender.send(CoreMessage::Reset); + registers.flags.set(flags.with_should_reset(false).into_bits()); + self.registers.write(registers); + } + } + } +} diff --git a/rust/src/components/debug_chat.rs b/rust/src/components/debug_chat.rs new file mode 100644 index 0000000..7cc1e2b --- /dev/null +++ b/rust/src/components/debug_chat.rs @@ -0,0 +1,50 @@ +use std::sync::Mutex; + +use jni::{ + objects::{GlobalRef, JValueGen}, + JNIEnv, +}; + +use crate::component_name; + +use super::Component; + +pub struct DebugChat { + buffer: Mutex, + object: GlobalRef, +} +impl DebugChat { + pub(crate) fn new(component: GlobalRef) -> Self { + Self { + buffer: Default::default(), + object: component, + } + } +} + +impl Component for DebugChat { + fn name() -> [u8; 0x30] { + component_name!(b"chat") + } + + fn memory_map_size(&self) -> u64 { + 1 + } + + fn write(&self, env: &mut JNIEnv, _: u64, data: &[u8]) { + let mut string = self.buffer.lock().unwrap(); + if data[0] == 0 { + let jni_string = env.new_string(string.drain(..).collect::()).expect("failed to create string"); + env + .call_method( + self.object.clone(), + "sendChatMessage", + "(Ljava_lang_String;)V", + &[JValueGen::Object(&jni_string)], + ) + .expect("failed to call..."); + return; + } + string.push(data[0].into()); + } +} diff --git a/rust/src/components/interrupt_controller.rs b/rust/src/components/interrupt_controller.rs new file mode 100644 index 0000000..e87766b --- /dev/null +++ b/rust/src/components/interrupt_controller.rs @@ -0,0 +1,29 @@ +use bitfield_struct::bitfield; + +use crate::component_name; + +use super::Component; + +#[bitfield(u8)] +struct InterruptEntry { + #[bits(4)] + affinity: u8, + masked: bool, + fired: bool, + #[bits(2)] + _unused: u8, +} + +pub struct InterruptController { + _interrupt_entries: [InterruptEntry; 32], +} + +impl Component for InterruptController { + fn name() -> [u8; 0x30] { + component_name!(b"interrupt_controller") + } + + fn memory_map_size(&self) -> u64 { + 0 + } +} diff --git a/rust/src/components/mod.rs b/rust/src/components/mod.rs new file mode 100644 index 0000000..193e544 --- /dev/null +++ b/rust/src/components/mod.rs @@ -0,0 +1,66 @@ +use core::CoreComponent; + +use debug_chat::DebugChat; +use jni::JNIEnv; + +pub mod core; +pub mod debug_chat; +pub mod interrupt_controller; +pub mod storage; + +pub trait Component: Sized { + fn name() -> [u8; 0x30]; + fn memory_map_size(&self) -> u64; + + fn write(&self, env: &mut JNIEnv, offset: u64, data: &[u8]) { + let _ = (env, offset, data); + } + fn read(&self, env: &mut JNIEnv, offset: u64, data: &mut [u8]) { + let _ = (env, offset, data); + } + fn has_interrupt(&self) -> bool { + false + } +} + +pub enum AllComponent { + Core(CoreComponent), + DebugChat(DebugChat), +} + +impl AllComponent { + pub fn name(&self) -> [u8; 0x30] { + match self { + AllComponent::Core(_) => CoreComponent::name(), + AllComponent::DebugChat(_) => DebugChat::name(), + } + } + + pub fn memory_map_size(&self) -> u64 { + match self { + AllComponent::Core(core_component) => core_component.memory_map_size(), + AllComponent::DebugChat(debug_chat) => debug_chat.memory_map_size(), + } + } + + pub fn write(&self, env: &mut JNIEnv, offset: u64, data: &[u8]) { + match self { + AllComponent::Core(core_component) => core_component.write(env, offset, data), + AllComponent::DebugChat(debug_chat) => debug_chat.write(env, offset, data), + } + } + + pub fn read(&self, env: &mut JNIEnv, offset: u64, data: &mut [u8]) { + match self { + AllComponent::Core(core_component) => core_component.read(env, offset, data), + AllComponent::DebugChat(debug_chat) => debug_chat.read(env, offset, data), + } + } + + pub fn has_interrupt(&self) -> bool { + match self { + AllComponent::Core(core_component) => core_component.has_interrupt(), + AllComponent::DebugChat(debug_chat) => debug_chat.has_interrupt(), + } + } +} diff --git a/rust/src/components/storage.rs b/rust/src/components/storage.rs new file mode 100644 index 0000000..04d15ac --- /dev/null +++ b/rust/src/components/storage.rs @@ -0,0 +1,2 @@ +pub struct Storage { +} diff --git a/rust/src/computer.rs b/rust/src/computer.rs index b0dcfb4..352dc77 100644 --- a/rust/src/computer.rs +++ b/rust/src/computer.rs @@ -1,86 +1,114 @@ -use std::sync::{Arc, OnceLock, RwLock}; +pub(crate) mod proto { + include!(concat!(env!("OUT_DIR"), "/computer.rs")); +} + +use std::sync::{mpsc, Arc}; use jni::{ - objects::{JByteBuffer, JIntArray, JObject, JString, JValue}, + objects::{JByteArray, JByteBuffer, JObject, JObjectArray, JValue}, signature::{Primitive, ReturnType}, - strings::JNIString, - sys::{jint, jlong}, + sys::jlong, JNIEnv, }; +use prost::Message; +use proto::component_init::Data; +use zerocopy::{little_endian::U64, FromZeros}; use crate::{ - assert_positive, - component::{mmio::ComponentMmioControl, Component, ComponentInfo, ComponentInterface}, - core::CoreHandle, + align_up_to_page, + components::{ + core::{CoreComponent, Registers}, + debug_chat::DebugChat, + AllComponent, + }, + core::{spawn_core_thread, Core, CoreMessage}, + device_bus::{DeviceBus, ROM_START}, unsync_cell::UnsyncCell, }; pub struct Computer { - pub components: Arc<[RwLock]>, - pub component_control: Arc>, - pub shared_ram: Arc>, - pub shared_rom: Arc>, + cores: Vec>, +} - pub cores: OnceLock>, +fn try_get_object<'local>( + env: &mut JNIEnv<'local>, + init_objects: &JObjectArray<'local>, + index: i32, +) -> jni::errors::Result>> { + if index < 0 { + return Ok(None); + } + + env.get_object_array_element(&init_objects, index).map(Some) } impl Computer { #[unsafe(export_name = "Java_ca_sanae_golemcomputers_computer_Computer_new")] - extern "C" fn new<'local>( + unsafe extern "C" fn new<'local>( mut env: JNIEnv<'local>, java_computer: JObject<'local>, - component_capacity: i32, - memory_size: i32, - core_speeds: JIntArray<'local>, + init_data: JByteArray<'local>, + init_objects: JObjectArray<'local>, ) { - let (component_capacity, memory_size): (usize, usize) = ( - assert_positive!(env, component_capacity), - assert_positive!(env, memory_size), - ); + let init_data = env.convert_byte_array(&init_data).expect("unable to convert byte array to vec"); + let init = proto::ComputerInit::decode(init_data.as_slice()).expect("failed to decode init data from java!"); + let ram = UnsyncCell::new_zeroed_box(init.primary_ram_pages as usize * 0x1000); - let core_count = env.get_array_length(&core_speeds).unwrap() as usize; - if core_count > 16 { - env.throw("no more than 16 cores are allowed").unwrap(); - return; - } - println!("got to computer construction!"); - let computer = Box::new(Self { - components: { - let mut components = Vec::with_capacity(component_capacity); - components.extend(std::iter::from_fn(|| Default::default()).take(component_capacity)); - Arc::from(components.into_boxed_slice()) - }, - component_control: Arc::new(UnsyncCell::new(ComponentMmioControl::new())), - shared_ram: Arc::from(UnsyncCell::new_zeroed_box(memory_size as usize)), - shared_rom: Arc::from(UnsyncCell::new_zeroed_box(0x1000000 as usize)), - // late initalized, see below - cores: OnceLock::new(), - }); - - println!("core construction"); - let mut speeds = [0; 16]; - env.get_int_array_region(core_speeds, 0, &mut speeds[..core_count]).unwrap(); - println!("core speeds acquired {:?}", &speeds[..core_count]); - let Ok(cores) = speeds - .into_iter() - .take(core_count) - .enumerate() - .map(|(core_index, speed)| { - Ok(CoreHandle::new( - &computer, - core_index, - assert_positive!(env, speed, Err(())), - env.get_java_vm().unwrap(), - )) + let rom = try_get_object(&mut env, &init_objects, init.primary_rom_index) + .expect("failed to get rom from java") + .map(|rom| { + if !env.is_instance_of(&rom, "java/nio/ByteBuffer").expect("failed to perform instanceOf") { + panic!("rom was not a byte buffer") + } + let rom_byte_buf = JByteBuffer::from(rom); + let capacity = env.get_direct_buffer_capacity(&rom_byte_buf).expect("unable to extract capacity from rom"); + let rom = UnsyncCell::new_zeroed_box(align_up_to_page(capacity)); + rom.update_from_jni(&mut env, &rom_byte_buf).unwrap(); + rom }) - .collect::, _>>() - else { - return; - }; + .expect("no rom was provided"); - let _ = computer.cores.set(cores.into_boxed_slice()); + let mut core_init_datas = Vec::new(); + let components = init + .components + .into_iter() + .map(|component| match component.data.unwrap() { + Data::Core(init) => { + let registers = Arc::new(UnsyncCell::new(Registers { + reset_address: U64::new(ROM_START), + ..Registers::new_zeroed() + })); + let (sender, receiver) = mpsc::channel(); + core_init_datas.push((init, registers.clone(), sender.clone(), receiver)); + AllComponent::Core(CoreComponent::new(registers, sender)) + } + Data::Chat(init) => { + let component = try_get_object(&mut env, &init_objects, init.java_index) + .expect("failed to get chat component from java") + .expect("chat component object was not provided"); - println!("storing computer info in field!"); + let component = env.new_global_ref(component).unwrap(); + + AllComponent::DebugChat(DebugChat::new(component)) + } + }) + .collect(); + + let device_bus = Arc::new(DeviceBus::new(ram, rom, components)); + + let cores = core_init_datas + .into_iter() + .map(|(init, registers, sender, receiver)| { + spawn_core_thread( + Core::new(registers, device_bus.clone(), init.instructions_per_tick), + env.get_java_vm().unwrap(), + receiver, + ); + sender + }) + .collect(); + + let computer = Box::new(Self { cores }); env .set_field( @@ -104,67 +132,6 @@ impl Computer { .unwrap() as *mut _ } - #[unsafe(export_name = "Java_ca_sanae_golemcomputers_computer_Computer_insertComponentNative")] - unsafe extern "C" fn insert_component<'local>( - mut env: JNIEnv<'local>, - computer: JObject<'local>, - component_index: jint, - component_object: JObject<'local>, - type_name: JString<'local>, - protocol: JString<'local>, - bytes_per_tick: u32, - version: u32, - ) { - // Safety: the responsibility of asserting the pointer is valid is on the java caller - let computer = unsafe { &*Self::from_jni(&mut env, computer) }; - - let component_index: usize = assert_positive!(env, component_index); - let mut make_info_string = |string: JString<'local>| { - let mut bytes = [0u8; 256]; - let jstring = env.get_string(&string).unwrap(); - - let length = bytes.len().min(jstring.to_bytes().len()); - bytes[..length].copy_from_slice(&jstring.to_bytes()[..length]); - - bytes - }; - let type_name = make_info_string(type_name); - let protocol = make_info_string(protocol); - let info = ComponentInfo { - type_name, - protocol, - bytes_per_tick, - version, - }; - let java_component = env.new_global_ref(component_object).unwrap(); - *computer.components[component_index].write().unwrap() = Component::Populated { - own_index: component_index, - java_component, - read_transfer_active: false.into(), - write_transfer_active: false.into(), - info, - interface: ComponentInterface::default().into(), - }; - } - - #[unsafe(export_name = "Java_ca_sanae_golemcomputers_computer_Computer_updateRomTest")] - unsafe extern "C" fn update_rom_test<'local>( - mut env: JNIEnv<'local>, - computer: JObject<'local>, - bytes: JByteBuffer<'local>, - ) { - // Safety: the responsibility of asserting the pointer is valid is on the java caller - let computer = unsafe { &*Self::from_jni(&mut env, computer) }; - - computer.shared_rom.update_from_jni(&mut env, &bytes).expect("failed to update rom"); - } - - #[unsafe(export_name = "Java_ca_sanae_golemcomputers_computer_Computer_updateRomFile")] - unsafe extern "C" fn update_rom_file<'local>(env: JNIEnv<'local>, computer: JObject<'local>) { - drop((env, computer)); - todo!() - } - #[unsafe(export_name = "Java_ca_sanae_golemcomputers_computer_Computer_free")] unsafe extern "C" fn free<'local>(mut env: JNIEnv<'local>, computer: JObject<'local>) { let computer = Self::from_jni(&mut env, computer); @@ -172,31 +139,23 @@ impl Computer { drop(unsafe { Box::from_raw(computer) }) } - #[unsafe(export_name = "Java_ca_sanae_golemcomputers_computer_Computer_wakeCoreAt")] - unsafe extern "C" fn wake_core_at<'local>( - mut env: JNIEnv<'local>, - computer: JObject<'local>, - core: jint, - address: jlong, - ) { - let (core, address): (usize, u64) = (assert_positive!(env, core), assert_positive!(env, address)); + #[unsafe(export_name = "Java_ca_sanae_golemcomputers_computer_Computer_resetCore")] + unsafe extern "C" fn reset_core<'local>(mut env: JNIEnv<'local>, computer: JObject<'local>, core: u32, wake: bool) { // Safety: the responsibility of asserting the pointer is valid is on the java caller let computer = unsafe { &*Self::from_jni(&mut env, computer) }; - - let Some(core) = computer.cores.get().unwrap().get(core) else { - env.throw("core index was out of bounds").unwrap(); - return; - }; - core.wake_at(address); + let core = computer.cores.get(core as usize).unwrap(); + core.send(CoreMessage::Reset).unwrap(); + if wake { + core.send(CoreMessage::Wake).unwrap(); + } } #[unsafe(export_name = "Java_ca_sanae_golemcomputers_computer_Computer_tick")] unsafe extern "C" fn tick<'local>(mut env: JNIEnv<'local>, computer: JObject<'local>) { // Safety: the responsibility of asserting the pointer is valid is on the java caller let computer = unsafe { &*Self::from_jni(&mut env, computer) }; - let cores = &computer.cores.get().unwrap(); - for core in cores.iter() { - core.tick(); + for core in &computer.cores { + core.send(CoreMessage::Tick).unwrap(); } } } diff --git a/rust/src/core.rs b/rust/src/core.rs index d75ab58..609bd88 100644 --- a/rust/src/core.rs +++ b/rust/src/core.rs @@ -1,103 +1,122 @@ -use std::{ - cell::{OnceCell, RefCell}, - pin::Pin, - rc::Rc, - sync::{mpsc, Arc, RwLock}, -}; +use std::sync::{mpsc, Arc}; use disarm64::decoder; -use jni::{JNIEnv, JavaVM}; +use jni::{AttachGuard, JNIEnv, JavaVM}; use num_derive::FromPrimitive; use num_traits::FromPrimitive; -use unicorn_engine::{Arch, ArmCpRegister, HookType, Mode, Permission, Query, RegisterARM64, Unicorn}; +use unicorn_engine::{Arch, HookType, Mode, Unicorn}; use zerocopy::IntoBytes; -use crate::{component::{mmio::{calculate_components_start, component_mmio_read, component_mmio_write, ComponentMmioControl}, Component, ComponentIoResult}, computer::Computer, unsync_cell::UnsyncCell}; +use crate::{components::core::Registers, device_bus::DeviceBus, unsync_cell::UnsyncCell}; -pub struct CoreHandle { - message_sender: mpsc::Sender, +pub struct Core { + registers: Arc>, + bus: Arc, + instructions_per_tick: u32, } -/* - - - -todo REWRITEEEEEEE RAAAAH - - - - */ - -pub struct CpuContext<'a> { - component_data_start: usize, - components: Arc<[RwLock]>, - /// see comment on [Computer] - component_control: Arc>, - message_sender: mpsc::Sender, - component_access_futures: Vec>>>, - ram: Arc>, - stopped: bool, - _env: JNIEnv<'a>, +impl Core { + pub fn new(registers: Arc>, bus: Arc, instructions_per_tick: u32) -> Self { + Self { + registers, + bus, + instructions_per_tick, + } + } } -enum CpuMessage { +pub struct CoreContext<'a> { + core: Core, + env: AttachGuard<'a>, + is_sleeping: bool, +} + +impl<'a> CoreContext<'a> { + pub fn get_env(&mut self) -> &mut JNIEnv<'a> { + &mut self.env + } +} + +pub enum CoreMessage { Tick, - Wake(Option), + Reset, + Wake, Sleep, } -impl CoreHandle { - pub fn new(computer: &Computer, core_index: usize, instructions_per_tick: u32, java_vm: JavaVM) -> Self { - let (components, component_control) = (computer.components.clone(), computer.component_control.clone()); - let (started_up_sender, started_up_receiver) = oneshot::channel(); +pub fn spawn_core_thread(core: Core, jvm: JavaVM, message_receiver: mpsc::Receiver) { + std::thread::spawn(|| core_main(core, jvm, message_receiver)); +} - let (message_sender, message_receiver) = mpsc::channel(); - let memories = (computer.shared_ram.clone(), computer.shared_rom.clone()); +fn core_main(core: Core, jvm: JavaVM, message_receiver: mpsc::Receiver) { + let env = jvm.attach_current_thread().unwrap(); + let mut uc = Unicorn::new_with_data( + Arch::ARM64, + Mode::LITTLE_ENDIAN, + CoreContext { + core, + env, + is_sleeping: true, + }, + ) + .unwrap(); - std::thread::spawn({ - let message_sender = message_sender.clone(); + let bus = uc.get_data().core.bus.clone(); + bus.map(&mut uc); + uc.add_intr_hook(interrupt_handler).unwrap(); + uc + .add_mem_hook( + HookType::MEM_INVALID, + u64::MIN, + u64::MAX, + |vcpu, mem_type, address, _, value| { + eprintln!("memory exception: {mem_type:?} at {address:X} (value: {value:X})"); - move || { - let env = java_vm.attach_current_thread_permanently().unwrap(); + vcpu.emu_stop().unwrap(); + true + }, + ) + .unwrap(); - let context = CpuContext { - component_data_start: calculate_components_start(components.len()), - components, - component_control, - message_sender, - component_access_futures: Vec::new(), - ram: memories.0.clone(), - _env: env, - stopped: true, - }; - thread( - core_index, - instructions_per_tick, - memories, - context, - started_up_sender, - message_receiver, - ) + let try_fetch_message = |stopped| { + if stopped { + message_receiver.recv().map(Some).map_err(|_| ()) + } else { + Ok(message_receiver.try_recv().ok()) + } + }; + + let mut instructions_left = 0; + loop { + while let Some(message) = try_fetch_message(uc.get_data().is_sleeping || instructions_left == 0).transpose() { + let Ok(message) = message else { return }; + + match message { + CoreMessage::Tick => { + if uc.get_data().is_sleeping { + continue; + } + println!("ticking"); + instructions_left = uc.get_data().core.instructions_per_tick as usize; + } + CoreMessage::Reset => { + let reset_address = uc.get_data().core.registers.read().reset_address.get(); + println!("reset! address: {reset_address:08X}"); + uc.set_pc(reset_address).expect("error while executing cpu") + } + CoreMessage::Wake => uc.get_data_mut().is_sleeping = false, + CoreMessage::Sleep => uc.get_data_mut().is_sleeping = true, } - }); - - let _ = started_up_receiver.recv().expect("failed to start up core"); - - CoreHandle { message_sender } - } - - #[unsafe(export_name = "Java_ca_sanae_golemcomputers_computer_RustNative_coreFree")] - unsafe extern "C" fn free<'local>(_env: JNIEnv<'local>, core: *const Self) { - // Safety: the responsibility of verifying the pointer is valid is on the java caller - drop(unsafe { Arc::from_raw(core) }) - } - - pub fn tick(&self) { - self.message_sender.send(CpuMessage::Tick).expect("cpu thread panicked :("); - } - - pub fn wake_at(&self, start: u64) { - self.message_sender.send(CpuMessage::Wake(Some(start))).unwrap(); + } + println!("executing"); + uc.emu_start( + uc.pc_read().expect("error while executing cpu"), + 0, + 0, + instructions_left.min(1000), + ) + .expect("error while executing cpu"); + instructions_left = instructions_left.saturating_sub(1000); } } @@ -126,110 +145,29 @@ enum Exception { Unaligned = 22, } -fn thread( - core_index: usize, - instructions_per_tick: u32, - (shared_ram, shared_rom): (Arc>, Arc>), - context: CpuContext, - started_up_sender: oneshot::Sender<()>, - message_receiver: mpsc::Receiver, -) -> Result<(), ()> { - let mut vcpu = - Unicorn::new_with_data(Arch::ARM64, Mode::LITTLE_ENDIAN, context).expect("failed to spawn unicorn engine"); +fn interrupt_handler(uc: &mut Unicorn, kind: u32) { + let exception = Exception::from_u32(kind).unwrap(); - const MPIDR_EL1: ArmCpRegister = ArmCpRegister::from_manual(0b11, 0b000, 0b0000, 0b0000, 0b101); - vcpu.reg_write_cp(CP_REG, MPIDR_EL1, core_index as u64).unwrap(); - - vcpu - .mmio_map( - 0x2000_0000, - 0x1000, - Some(component_mmio_read), - Some(component_mmio_write), - ) - .expect("failed to map mmio for core"); - shared_rom - .map(&mut vcpu, 0x4000_0000, None, Permission::READ | Permission::EXEC) - .expect("failed to map memory for rom"); - shared_ram.map(&mut vcpu, 0x8000_0000, None, Permission::ALL).expect("failed to map memory for rom"); - use RegisterARM64::*; - - vcpu - .add_intr_hook(|vcpu, excp| { - println!("interrupt: {excp}"); - let exception = Exception::from_u32(excp).unwrap(); - - let pc = vcpu.pc_read().unwrap(); - println!("exception: {exception:?}"); - let mut opcode = 0u32; - if let Ok(_) = vcpu.mem_read(pc, opcode.as_mut_bytes()) { - if let Some(opcode) = decoder::decode(opcode) { - let mut inst = String::new(); - disarm64::format_insn::format_insn_pc(pc, &mut inst, &opcode).unwrap(); - println!("pc {pc:08X} {inst}"); - } else { - println!("pc {pc:08X} - op parse failed {opcode:08X}"); - } - } else { - println!("pc {pc:08X} - can't read"); - } - println!("x0 {:08X}", vcpu.reg_read(X0).unwrap()); - println!("x1 {:08X}", vcpu.reg_read(X1).unwrap()); - - let syndrome = vcpu.query(Query::SYNDROME).unwrap(); - println!("0x{syndrome:08X}"); - vcpu.get_data_mut().stopped = true; - vcpu.emu_stop().unwrap(); - }) - .unwrap(); - - vcpu - .add_mem_hook( - HookType::MEM_INVALID, - u64::MIN, - u64::MAX, - |vcpu, mem_type, address, _, value| { - eprintln!("memory exception: {mem_type:?} at {address:X} (value: {value:X})"); - - vcpu.emu_stop().unwrap(); - true - }, - ) - .unwrap(); - - started_up_sender.send(()).expect("panicked when trying to core"); - let try_fetch_message = |stopped| { - if stopped { - message_receiver.recv().map(Some).map_err(|_| ()) + let pc = uc.pc_read().unwrap(); + println!("exception: {exception:?}"); + let mut opcode = 0u32; + if let Ok(_) = uc.mem_read(pc, opcode.as_mut_bytes()) { + if let Some(opcode) = decoder::decode(opcode) { + let mut inst = String::new(); + disarm64::format_insn::format_insn_pc(pc, &mut inst, &opcode).unwrap(); + println!("pc {pc:08X} {inst}"); } else { - Ok(message_receiver.try_recv().ok()) + println!("pc {pc:08X} - op parse failed {opcode:08X}"); } - }; - - let mut instructions_left = 0; - loop { - while let Some(message) = try_fetch_message(vcpu.get_data().stopped || instructions_left == 0)? { - match message { - CpuMessage::Tick => { - if vcpu.get_data().stopped { - continue; - } - } - CpuMessage::Wake(address) => { - if let Some(address) = address { - vcpu.set_pc(address).unwrap(); - println!("waking at {address}"); - } - vcpu.get_data_mut().stopped = false; - - let pc = vcpu.pc_read().unwrap(); - println!("executing {pc:X}"); - vcpu.emu_start(pc, 0, 0, instructions_per_tick as usize).expect("error while executing cpu"); - } - CpuMessage::Sleep => vcpu.get_data_mut().stopped = true, - } - } - vcpu.emu_start(vcpu.pc_read().unwrap(), 0, 0, instructions_left.min(1000)).expect("error while executing cpu"); - instructions_left = instructions_left.saturating_sub(1000); + } else { + println!("pc {pc:08X} - can't read"); } + + use unicorn_engine::RegisterARM64::*; + println!("X0: {:X}", uc.reg_read(X0).unwrap()); + println!("X1: {:X}", uc.reg_read(X1).unwrap()); + println!("X2: {:X}", uc.reg_read(X2).unwrap()); + + // if an exception occurs, we don't want to execute anymore + uc.get_data_mut().is_sleeping = true; } diff --git a/rust/src/device_bus.rs b/rust/src/device_bus.rs new file mode 100644 index 0000000..fbcf6cb --- /dev/null +++ b/rust/src/device_bus.rs @@ -0,0 +1,174 @@ +use std::{num::NonZeroUsize, sync::Arc}; + +use bitfield_struct::bitfield; +use nodit::{interval::ie, Interval, NoditMap, OverlapError}; +use unicorn_engine::{Permission, Unicorn}; +use zerocopy::{little_endian::U32, FromBytes, FromZeros, Immutable, IntoBytes, KnownLayout}; + +use crate::{align_up_to_page, components::AllComponent, core::CoreContext, unsync_cell::UnsyncCell}; + +pub const COMPONENT_TABLE_START: u64 = 0x1000_0000; +pub const COMPONENTS_START: u64 = 0x2000_0000; +pub const ROM_START: u64 = 0x4000_0000; +pub const RAM_START: u64 = 0x8000_0000; + +enum Device { + Rom(Box>), + Ram(Box>), + ComponentTable, + Component(AllComponent), +} + +#[bitfield(u32)] +struct ComponentEntryHeader { + #[bits(31)] + _padding: u32, + filled: bool, +} + +#[derive(Clone, Copy, FromBytes, IntoBytes, Immutable, KnownLayout)] +#[repr(C)] +struct ComponentTableEntry { + header: U32, + address: U32, + name: [u8; 0x30], +} + +struct ComponentTable(Box<[u8]>); + +pub struct DeviceBus { + devices: NoditMap, Device>, + component_table: ComponentTable, +} + +fn assert_non_overlapping(result: Result<(), OverlapError>) { + if result.is_err() { + panic!("insert overlapped!") + } +} + +impl DeviceBus { + pub fn new(ram: Box>, rom: Box>, components: Vec) -> Self { + let mut devices = NoditMap::new(); + + assert_non_overlapping(devices.insert_strict(ie(ROM_START, ROM_START + rom.len() as u64), Device::Rom(rom))); + assert_non_overlapping(devices.insert_strict(ie(RAM_START, RAM_START + ram.len() as u64), Device::Ram(ram))); + + let mut component_table = { + let mut table = Box::new_uninit_slice(align_up_to_page(components.len() * size_of::())); + table.zero(); + unsafe { table.assume_init() } + }; + assert_non_overlapping(devices.insert_strict( + ie( + COMPONENT_TABLE_START, + COMPONENT_TABLE_START + component_table.len() as u64, + ), + Device::ComponentTable, + )); + + { + let component_table = <[ComponentTableEntry]>::mut_from_bytes_with_elems( + &mut component_table[..components.len() * size_of::()], + components.len(), + ) + .unwrap(); + + let mut component_offset = COMPONENTS_START; + for (index, component) in components.into_iter().enumerate() { + let start = component_offset; + let end = align_up_to_page(component_offset + component.memory_map_size()); + println!("component offset: {start:X}..{end:X} and {}", u32::MAX); + assert!(end < ROM_START); + + component_offset = end; + component_table[index] = ComponentTableEntry { + header: U32::new(ComponentEntryHeader::new().with_filled(true).into_bits()), + address: U32::new(start as u32), + name: component.name(), + }; + + assert_non_overlapping(devices.insert_strict(ie(start, end), Device::Component(component))); + } + } + + Self { + devices, + component_table: ComponentTable(component_table), + } + } + + pub fn map(self: Arc, uc: &mut Unicorn) { + for (interval, device) in self.devices.iter() { + let (address, size) = (interval.start(), (interval.end() - interval.start()) as usize + 1); + println!("address, size {address:X}, {size:X} {:X?}", interval); + match device { + Device::Rom(memory) => unsafe { + uc.mem_map_ptr( + address, + size, + Permission::READ | Permission::WRITE, + memory.as_mut_slice_ptr() as _, + ) + .unwrap(); + }, + Device::Ram(memory) => unsafe { + uc.mem_map_ptr( + address, + size, + Permission::READ | Permission::WRITE | Permission::EXEC, + memory.as_mut_slice_ptr() as _, + ) + .unwrap(); + }, + Device::ComponentTable => unsafe { + println!("component table range {address:X} + {size:X}"); + uc.mem_map_ptr(address, size, Permission::READ, self.component_table.0.as_ptr() as _).unwrap() + }, + Device::Component(_) => { + fn calculate_size(component: &AllComponent, offset: u64, size: usize) -> Option { + if offset >= component.memory_map_size() { + return None; + } + + let offset = offset as usize; + NonZeroUsize::new(usize::min(offset + size, component.memory_map_size() as usize) - offset) + } + let bus_read = self.clone(); + let bus_write = self.clone(); + uc.mmio_map( + address, + size, + Some(move |uc: &mut Unicorn, offset: u64, size: usize| { + let Device::Component(component) = bus_read.devices.get_at_point(address).unwrap() else { + unreachable!("device was not a component") + }; + + let Some(size) = calculate_size(component, offset, size).map(NonZeroUsize::get) else { + return 0; + }; + + let mut data = 0u64; + component.read(uc.get_data_mut().get_env(), offset, &mut data.as_mut_bytes()[..size]); + data + }), + Some( + move |uc: &mut Unicorn, offset: u64, size: usize, data: u64| { + let Device::Component(component) = bus_write.devices.get_at_point(address).unwrap() else { + unreachable!("device was not a component") + }; + + let Some(size) = calculate_size(component, offset, size).map(NonZeroUsize::get) else { + return; + }; + + component.write(uc.get_data_mut().get_env(), offset, &data.to_le_bytes()[..size]); + }, + ), + ) + .unwrap() + } + } + } + } +} diff --git a/rust/src/lib.rs b/rust/src/lib.rs index def964d..b0b2855 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -1,6 +1,5 @@ #![deny(unsafe_op_in_unsafe_fn)] #![feature(sync_unsafe_cell)] -#![feature(generic_const_exprs)] use std::ops::Range; @@ -8,12 +7,13 @@ use jni::{ sys::{jint, JNI_VERSION_1_8}, JNIEnv, }; +use num_traits::PrimInt; -pub mod component; +pub mod components; mod computer; -mod core; +pub mod core; +mod device_bus; pub mod unsync_cell; -mod memory_map; #[macro_export] macro_rules! assert_positive { @@ -45,6 +45,30 @@ macro_rules! assert_positive { }}; } +#[macro_export] +macro_rules! component_name { + ($value: expr) => {{ + const DATA: [u8; 0x30] = { + let name = { $value }; + if name.len() >= 0x30 { + panic!("name length must be less than 64 characters") + } + let mut new_name = [0; 0x30]; + let mut index = name.len(); + loop { + index -= 1; + new_name[index] = name[index]; + if index == 0 { + break; + } + } + new_name + }; + + DATA.clone() + }}; +} + #[macro_export] macro_rules! range_of_field { ($type: ty, $field: ident) => {{ @@ -68,19 +92,19 @@ fn overlapping(a: &Range, b: &Range) -> bool { a.contains(&b.start) && a.contains(&b.end) } -fn subtract_range(a: &Range, b: &Range) -> Range { - a.start - b.start..a.end - b.end +// fn subtract_range(a: &Range, b: &Range) -> Range { +// a.start - b.start..a.end - b.end +// } + +pub fn align_up_to_page(value: T) -> T { + align_up(value, T::one().unsigned_shl(12)) } -pub fn align_up(value: u64, alignment: u64) -> u64 { - (value + alignment - 1) & !(alignment - 1) -} - -struct UnicornWrap { - +pub fn align_up(value: T, alignment: T) -> T { + (value + alignment - T::one()) & !(alignment - T::one()) } #[unsafe(no_mangle)] -pub extern "system" fn JNI_OnLoad<'local>(_: JNIEnv<'local>, _: usize) -> jint { +extern "system" fn JNI_OnLoad<'local>(_: JNIEnv<'local>, _: usize) -> jint { JNI_VERSION_1_8 } diff --git a/rust/src/memory_map.rs b/rust/src/memory_map.rs deleted file mode 100644 index db4b51c..0000000 --- a/rust/src/memory_map.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub const COMPONENTS_START: u64 = 0x2000_0000; -pub const ROM_START: u64 = 0x4000_0000; -pub const RAM_START: u64 = 0x8000_0000; diff --git a/rust/src/unsync_cell.rs b/rust/src/unsync_cell.rs index 9050b55..9f910b3 100644 --- a/rust/src/unsync_cell.rs +++ b/rust/src/unsync_cell.rs @@ -2,7 +2,7 @@ use std::cell::SyncUnsafeCell; use jni::{objects::JByteBuffer, JNIEnv}; use unicorn_engine::{uc_error, Permission, Unicorn}; -use zerocopy::{FromBytes, FromZeros, Immutable, IntoBytes, KnownLayout}; +use zerocopy::{FromBytes, FromZeros, IntoBytes, KnownLayout}; /// A simple replacement for [std::cell::Cell] in Sync contexts which explicitly does not try to synchronize the contents. /// This is used for contexts where unsynchronized access is explicitly fine. @@ -19,6 +19,12 @@ impl UnsyncCell { } } +impl UnsyncCell<[T]> { + pub fn as_mut_slice_ptr(&self) -> *mut [T] { + self.0.get() + } +} + impl UnsyncCell { pub fn new(value: T) -> Self { Self(SyncUnsafeCell::new(value)) @@ -35,24 +41,36 @@ impl UnsyncCell { impl UnsyncCell { // N must be the same as the size of U - pub fn read_into(&self, offset: usize) -> U - where - [u8; size_of::()]:, - { - let mut buffer = [0; size_of::()]; - let end = (offset + size_of::()).min(size_of::()); - assert!(end - offset <= size_of::()); - // Safety: both pointers are valid for 0..(end - offset) - unsafe { (self.as_mut_ptr() as *const u8).copy_to(buffer.as_mut_ptr(), end - offset) }; - U::read_from_bytes(&buffer).expect("N was not the same as the size of U") + // pub fn read_into(&self, offset: usize) -> U + // where + // [u8; size_of::()]:, + // { + // let mut buffer = [0; size_of::()]; + // let end = (offset + size_of::()).min(size_of::()); + // assert!(end - offset <= size_of::()); + // // Safety: both pointers are valid for 0..(end - offset) + // unsafe { (self.as_mut_ptr() as *const u8).copy_to(buffer.as_mut_ptr(), end - offset) }; + // U::read_from_bytes(&buffer).expect("N was not the same as the size of U") + // } + + pub fn read_into_byte_slice(&self, offset: usize, data: &mut [u8]) { + let end = (offset + data.len()).min(size_of::()); + assert!(end - offset <= data.len()); + unsafe { (self.as_mut_ptr() as *const u8).copy_to(data.as_mut_ptr(), end - offset) }; } } impl UnsyncCell { - pub fn write_from(&self, offset: usize, value: &U) { - let end = (offset + size_of::()).min(size_of::()); - assert!(end - offset <= size_of::()); - unsafe { (self.as_mut_ptr() as *mut u8).copy_from(value.as_bytes().as_ptr(), end - offset) }; + // pub fn write_from(&self, offset: usize, value: &U) { + // let end = (offset + size_of::()).min(size_of::()); + // assert!(end - offset <= size_of::()); + // unsafe { (self.as_mut_ptr() as *mut u8).copy_from(value.as_bytes().as_ptr(), end - offset) }; + // } + + pub fn write_from_byte_slice(&self, offset: usize, data: &[u8]) { + let end = (offset + data.len()).min(size_of::()); + assert!(end - offset <= data.len()); + unsafe { (self.as_mut_ptr() as *mut u8).copy_from(data.as_ptr(), end - offset) }; } } @@ -69,7 +87,7 @@ impl UnsyncCell<[T]> { memory.zero(); // Safety: UnsafeCell transparently wraps the slice, meaning the two types are semantically identical - unsafe { std::mem::transmute::, Box>(memory.assume_init()) } + unsafe { std::mem::transmute::, Box>>(memory.assume_init()) } } fn as_mut_ptr(&self) -> *mut [T] { @@ -80,6 +98,23 @@ impl UnsyncCell<[T]> { self.as_mut_ptr().len() } + pub fn map( + &self, + vcpu: &mut Unicorn, + address: u64, + size: Option, + permissions: Permission, + ) -> Result<(), uc_error> { + let size = size.unwrap_or(self.len()); + if size > self.len() { + return Err(uc_error::ARG); + } + println!("mapping {permissions:?} at {address:08X} for {size:08X}"); + unsafe { vcpu.mem_map_ptr(address, size, permissions, self.as_mut_ptr() as *mut _) } + } +} + +impl UnsyncCell<[u8]> { pub fn update_from_slice(&self, bytes: &[u8]) -> Result<(), usize> { if self.len() < bytes.len() { return Err(bytes.len() - self.len()); @@ -107,21 +142,6 @@ impl UnsyncCell<[T]> { Ok(()) } - - pub fn map( - &self, - vcpu: &mut Unicorn, - address: u64, - size: Option, - permissions: Permission, - ) -> Result<(), uc_error> { - let size = size.unwrap_or(self.len()); - if size > self.len() { - return Err(uc_error::ARG); - } - println!("mapping {permissions:?} at {address:08X} for {size:08X}"); - unsafe { vcpu.mem_map_ptr(address, size, permissions, self.as_mut_ptr() as *mut _) } - } } impl Clone for UnsyncCell { diff --git a/settings.gradle.kts b/settings.gradle.kts index 61d9d66..878f797 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -3,6 +3,7 @@ pluginManagement { maven("https://maven.fabricmc.net/") { name = "Fabric" } + gradlePluginPortal() mavenCentral() } diff --git a/src/main/kotlin/ca/sanae/golemcomputers/blocks/GolemBlock.kt b/src/main/kotlin/ca/sanae/golemcomputers/blocks/GolemBlock.kt index d3cdabd..163c136 100644 --- a/src/main/kotlin/ca/sanae/golemcomputers/blocks/GolemBlock.kt +++ b/src/main/kotlin/ca/sanae/golemcomputers/blocks/GolemBlock.kt @@ -4,10 +4,12 @@ import ca.sanae.golemcomputers.GolemComputers import com.mojang.serialization.MapCodec import net.minecraft.block.BlockState import net.minecraft.block.BlockWithEntity +import net.minecraft.block.CommandBlock import net.minecraft.block.entity.BlockEntity import net.minecraft.block.entity.BlockEntityTicker import net.minecraft.block.entity.BlockEntityType import net.minecraft.entity.player.PlayerEntity +import net.minecraft.server.command.SayCommand import net.minecraft.sound.BlockSoundGroup import net.minecraft.text.Text import net.minecraft.util.ActionResult @@ -51,8 +53,9 @@ class GolemBlock : if (world.isClient()) { return ActionResult.SUCCESS } + world.server!!.playerManager.broadcast(Text.literal("Hello world!"), false) blockEntity.incrementClicks(); - player.sendMessage(Text.literal("You've clicked the block for the ${blockEntity.getClicks()}th time."), true); +// player.sendMessage(Text.literal("You've clicked the block for the ${blockEntity.getClicks()}th time."), true); return ActionResult.SUCCESS_SERVER } diff --git a/src/main/kotlin/ca/sanae/golemcomputers/blocks/GolemBlockEntity.kt b/src/main/kotlin/ca/sanae/golemcomputers/blocks/GolemBlockEntity.kt index be6f6d1..9265771 100644 --- a/src/main/kotlin/ca/sanae/golemcomputers/blocks/GolemBlockEntity.kt +++ b/src/main/kotlin/ca/sanae/golemcomputers/blocks/GolemBlockEntity.kt @@ -2,6 +2,7 @@ package ca.sanae.golemcomputers.blocks import ca.sanae.golemcomputers.GolemComputers import ca.sanae.golemcomputers.computer.Computer +import ca.sanae.golemcomputers.computer.components.ChatComponent import net.fabricmc.api.EnvType import net.fabricmc.api.Environment import net.minecraft.block.BlockState @@ -10,6 +11,8 @@ import net.minecraft.nbt.NbtCompound import net.minecraft.registry.RegistryWrapper import net.minecraft.util.math.BlockPos import net.minecraft.world.World +import java.nio.channels.FileChannel +import kotlin.io.path.Path class GolemBlockEntity(pos: BlockPos?, state: BlockState?) : BlockEntity(GolemComputers.GOLEM_BLOCK_ENTITY, pos, state) { @@ -34,7 +37,17 @@ class GolemBlockEntity(pos: BlockPos?, state: BlockState?) : } override fun setWorld(world: World?) { - computer = if (!world!!.isClient) Computer(world) else null + val path = Path("/home/aubrey/Projects/golem-software/assembly.bin") + val channel = FileChannel.open(path); + val buffer = channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size()) + if (!world!!.isClient) { + val computer = Computer.Builder(world, buffer, 1u) + .addChat(ChatComponent(world.server!!)) + .addCore(1000) + .build() + computer.resetCore(0, true); + this.computer = computer; + } super.setWorld(world) } diff --git a/src/main/kotlin/ca/sanae/golemcomputers/computer/Computer.kt b/src/main/kotlin/ca/sanae/golemcomputers/computer/Computer.kt index 6586220..003447c 100644 --- a/src/main/kotlin/ca/sanae/golemcomputers/computer/Computer.kt +++ b/src/main/kotlin/ca/sanae/golemcomputers/computer/Computer.kt @@ -1,62 +1,66 @@ package ca.sanae.golemcomputers.computer -import ca.sanae.golemcomputers.computer.RustNative.Companion.cleaner +import ca.sanae.golemcomputers.computer.NativeProtobuf.ChatComponentInit +import ca.sanae.golemcomputers.computer.NativeProtobuf.ComponentInit +import ca.sanae.golemcomputers.computer.NativeProtobuf.ComputerInit +import ca.sanae.golemcomputers.computer.NativeProtobuf.CoreComponentInit +import ca.sanae.golemcomputers.computer.RustNative.cleaner import ca.sanae.golemcomputers.computer.components.ChatComponent -import ca.sanae.golemcomputers.computer.components.Component import net.minecraft.world.World import java.nio.ByteBuffer import java.nio.channels.FileChannel -import java.nio.file.Files import kotlin.io.path.Path -class Computer(val world: World) { +class Computer private constructor(val world: World, init: ComputerInit, initObjects: Array) { @Suppress("unused") private val address: Long = 0 init { - new(1, 0x1000, intArrayOf(0x10000)) + new(init.toByteArray(), initObjects) cleaner.register(this) { free() } - val path = Path("/home/aubrey/Projects/golem-software/assembly.bin") - val channel = FileChannel.open(path); - val buffer = channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size()) - val wakeAddress = buffer.getUnsignedInt(); - - updateRomTest(buffer.rewind()) - println("waking at 0x${wakeAddress.toString(16)}") - insertComponent(0, ChatComponent(this)) - wakeCoreAt(0, wakeAddress) } - fun ByteBuffer.getUnsignedInt(): Long { - return Integer.reverseBytes(this.getInt()).toLong() and 0xFFFFFFFFL - } - - fun insertComponent(coreIndex: Int, component: Component) { - insertComponentNative( - coreIndex, - component, - component.info.typeName.toString(), - component.info.protocol.toString(), - component.info.bytesPerTick, - component.info.version - ) - } - - private external fun new(componentCapacity: Int, memorySize: Int, coreIps: IntArray): Long + private external fun new(toByteArray: ByteArray, initObjects: Array): Long private external fun free() - private external fun updateRomTest(data: ByteBuffer) - private external fun wakeCoreAt(coreIndex: Int, address: Long) - private external fun insertComponentNative( - index: Int, - component: Component, - typeName: String, - protocol: String, - bytesPerTick: UInt, - version: UInt - ) - + external fun resetCore(core: Int, wakeCore: Boolean) external fun tick(); + class Builder(val world: World, rom: ByteBuffer, ramPages: UInt) { + private val objects: ArrayList = ArrayList() + private val builder: ComputerInit.Builder = ComputerInit.newBuilder() + + init { + builder.setPrimaryRomIndex(addObject(rom)) + builder.setPrimaryRamPages(ramPages.toInt()) + } + + private fun addObject(value: Any): Int { + val index = objects.size + objects.add(value) + return index + } + + fun addCore(instructionsPerTick: Int): Builder { + builder.addComponents( + ComponentInit.newBuilder() + .setCore(CoreComponentInit.newBuilder().setInstructionsPerTick(instructionsPerTick).build()) + ) + + return this + } + + fun addChat(chat: ChatComponent): Builder { + builder.addComponents( + ComponentInit.newBuilder().setChat(ChatComponentInit.newBuilder().setJavaIndex(addObject(chat)).build()) + ) + + return this + } + + fun build(): Computer { + return Computer(world, builder.build(), objects.toArray()) + } + } } \ No newline at end of file diff --git a/src/main/kotlin/ca/sanae/golemcomputers/computer/RustNative.kt b/src/main/kotlin/ca/sanae/golemcomputers/computer/RustNative.kt index 37836f7..e03419e 100644 --- a/src/main/kotlin/ca/sanae/golemcomputers/computer/RustNative.kt +++ b/src/main/kotlin/ca/sanae/golemcomputers/computer/RustNative.kt @@ -6,27 +6,24 @@ import java.lang.ref.Cleaner import java.nio.channels.Channels -class RustNative private constructor() { - companion object { - val cleaner: Cleaner = Cleaner.create() - private val instance: RustNative = RustNative(); +object RustNative { + val cleaner: Cleaner = Cleaner.create() - init { - val nativeLibraryName = System.mapLibraryName("golem_computers") - val tempFile: File = File.createTempFile("extracted_", nativeLibraryName) + init { + val nativeLibraryName = System.mapLibraryName("golem_computers") + val tempFile: File = File.createTempFile("extracted_", nativeLibraryName) - val classLoader = RustNative::class.java.getClassLoader() + val classLoader = RustNative::class.java.getClassLoader() - Channels.newChannel(classLoader.getResourceAsStream(nativeLibraryName)!!).use { src -> - FileOutputStream(tempFile).channel.use { dst -> - dst.transferFrom(src, 0, Long.MAX_VALUE) - } + Channels.newChannel(classLoader.getResourceAsStream(nativeLibraryName)!!).use { src -> + FileOutputStream(tempFile).channel.use { dst -> + dst.transferFrom(src, 0, Long.MAX_VALUE) } - System.load(tempFile.absolutePath) } + System.load(tempFile.absolutePath) + } - fun isInitialized(): Boolean { - return true - } + fun isInitialized(): Boolean { + return true } } \ No newline at end of file diff --git a/src/main/kotlin/ca/sanae/golemcomputers/computer/components/ChatComponent.kt b/src/main/kotlin/ca/sanae/golemcomputers/computer/components/ChatComponent.kt index ff0445e..2b333c3 100644 --- a/src/main/kotlin/ca/sanae/golemcomputers/computer/components/ChatComponent.kt +++ b/src/main/kotlin/ca/sanae/golemcomputers/computer/components/ChatComponent.kt @@ -1,29 +1,13 @@ package ca.sanae.golemcomputers.computer.components -import ca.sanae.golemcomputers.GolemComputers -import ca.sanae.golemcomputers.computer.Computer -import kotlinx.io.bytestring.decodeToString -import kotlinx.io.bytestring.getByteString +import net.minecraft.server.MinecraftServer import net.minecraft.text.Text -import java.nio.ByteBuffer -class ChatComponent(private val computer: Computer) : - Component(GolemComputers.id("chat"), GolemComputers.id("string_stream"), 10000, 0u) { - var chatBuffer: String = "" - override fun gotByteChunk(buffer: ByteBuffer) { - chatBuffer += buffer.getByteString().decodeToString() - while (true) { - val index = chatBuffer.indexOf('\u0000') - if (index == -1) break - val (message, new) = chatBuffer.splitAtIndex(index) - chatBuffer = new - computer.world.server!!.sendMessage(Text.of(message)) +class ChatComponent(val server: MinecraftServer) { + @Suppress("unused") + fun sendChatMessage(string: String) { + server.execute { + server.playerManager.broadcast(Text.of(string), true) } } - - override fun restarted() { - chatBuffer = "" - } - - fun String.splitAtIndex(index: Int) = take(index) to substring(index) } \ No newline at end of file diff --git a/src/main/kotlin/ca/sanae/golemcomputers/computer/components/Component.kt b/src/main/kotlin/ca/sanae/golemcomputers/computer/components/Component.kt deleted file mode 100644 index 0a49acd..0000000 --- a/src/main/kotlin/ca/sanae/golemcomputers/computer/components/Component.kt +++ /dev/null @@ -1,31 +0,0 @@ -package ca.sanae.golemcomputers.computer.components - -import com.github.pbbl.heap.ByteBufferPool -import com.jogamp.common.util.LFRingbuffer -import com.jogamp.common.util.Ringbuffer -import net.minecraft.util.Identifier -import java.nio.ByteBuffer - -abstract class Component(val info: ComponentInfo) { - @Suppress("unused") - private val address: Long = 0 - private val pool = ByteBufferPool() - abstract fun gotByteChunk(buffer: ByteBuffer) - private external fun writeBytesNative(buffer: ByteBuffer): Boolean - fun writeBytes(lambda: ((size: Int) -> ByteBuffer) -> ByteBuffer) { - synchronized(pool) { - val buffer = lambda(pool::take) - val overflowed = writeBytesNative(buffer) - pool.give(buffer) - if (overflowed) { - overflowShutdown() - } - } - } - - // called when the component runs out of runway to store unread data - open fun overflowShutdown() {} - abstract fun restarted() - - data class ComponentInfo(val typeName: Identifier, val protocol: Identifier, val bytesPerTick: UInt, val version: UInt) -} \ No newline at end of file