diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index dabae5c..195c891 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -34,9 +34,11 @@ jobs: fail-fast: false matrix: feature: + - log - ascii-armor - secp256k1 - curve25519 + - serde - stl steps: - uses: actions/checkout@v4 diff --git a/.rustfmt.toml b/.rustfmt.toml index 98e9b85..5704008 100644 --- a/.rustfmt.toml +++ b/.rustfmt.toml @@ -1,5 +1,5 @@ edition = "2021" -version = "Two" +style_edition = "2021" max_width = 100 array_width = 100 diff --git a/Cargo.lock b/Cargo.lock index f30a1e4..eb435a3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,7 +4,7 @@ version = 3 [[package]] name = "aluvm" -version = "0.11.0-beta.8" +version = "0.11.0-beta.9" dependencies = [ "amplify", "ascii-armor", @@ -87,9 +87,9 @@ dependencies = [ [[package]] name = "arrayref" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d151e35f61089500b617991b791fc8bfd237ae50cd5950803758a179b41e67a" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" [[package]] name = "arrayvec" @@ -146,6 +146,22 @@ dependencies = [ "thiserror", ] +[[package]] +name = "bitcoin-io" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "340e09e8399c7bd8912f495af6aa58bea0c9214773417ffaa8f6460f93aaee56" + +[[package]] +name = "bitcoin_hashes" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb18c03d0db0247e147a21a6faafd5a7eb851c743db062de72018b6b7e8e4d16" +dependencies = [ + "bitcoin-io", + "hex-conservative", +] + [[package]] name = "bitflags" version = "2.6.0" @@ -188,9 +204,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "cc" -version = "1.1.16" +version = "1.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9d013ecb737093c0e86b151a7b837993cf9ec6c502946cfb44bedc392421e0b" +checksum = "b16803a61b81d9eabb7eae2588776c4c1e584b738ede45fdbb4c972cec1e9945" dependencies = [ "shlex", ] @@ -219,9 +235,9 @@ checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" [[package]] name = "cpufeatures" -version = "0.2.13" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51e852e6dc9a5bed1fae92dd2375037bf2b768725bf3be87811edee3249d09ad" +checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" dependencies = [ "libc", ] @@ -326,9 +342,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.14.5" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" [[package]] name = "heck" @@ -336,11 +352,20 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "hex-conservative" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5313b072ce3c597065a808dbf612c4c8e8590bdbf8b579508bf7a762c5eae6cd" +dependencies = [ + "arrayvec", +] + [[package]] name = "indexmap" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" +checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" dependencies = [ "equivalent", "hashbrown", @@ -348,18 +373,18 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.70" +version = "0.3.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" +checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" dependencies = [ "wasm-bindgen", ] [[package]] name = "libc" -version = "0.2.158" +version = "0.2.159" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" +checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" [[package]] name = "log" @@ -385,9 +410,9 @@ checksum = "f2b8f3a258db515d5e91a904ce4ae3f73e091149b90cadbdb93d210bee07f63b" [[package]] name = "once_cell" -version = "1.19.0" +version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "paste" @@ -406,9 +431,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.86" +version = "1.0.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "b3e4daa0dcf6feba26f985457cdf104d4b4256fc5a09547140f3631bb076b19a" dependencies = [ "unicode-ident", ] @@ -487,40 +512,42 @@ checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" [[package]] name = "secp256k1" -version = "0.29.0" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e0cc0f1cf93f4969faf3ea1c7d8a9faed25918d96affa959720823dfe86d4f3" +checksum = "b50c5943d326858130af85e049f2661ba3c78b26589b8ab98e65e80ae44a1252" dependencies = [ + "bitcoin_hashes", + "rand", "secp256k1-sys", ] [[package]] name = "secp256k1-sys" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1433bd67156263443f14d603720b082dd3121779323fce20cba2aa07b874bc1b" +checksum = "d4387882333d3aa8cb20530a17c69a3752e97837832f34f6dccc760e715001d9" dependencies = [ "cc", ] [[package]] name = "serde" -version = "1.0.209" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99fce0ffe7310761ca6bf9faf5115afbc19688edd00171d81b1bb1b116c63e09" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.209" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -622,9 +649,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.77" +version = "2.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" +checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" dependencies = [ "proc-macro2", "quote", @@ -633,22 +660,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.63" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" +checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.63" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" +checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -659,9 +686,9 @@ checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" [[package]] name = "version_check" @@ -703,9 +730,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.93" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" +checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" dependencies = [ "cfg-if", "once_cell", @@ -714,24 +741,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.93" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" +checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.43" +version = "0.4.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61e9300f63a621e96ed275155c108eb6f843b6a26d053f122ab69724559dc8ed" +checksum = "cc7ec4f8827a71586374db3e87abdb5a2bb3a15afed140221307c3ec06b1f63b" dependencies = [ "cfg-if", "js-sys", @@ -741,9 +768,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.93" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" +checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -751,28 +778,28 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.93" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" +checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.93" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" +checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" [[package]] name = "wasm-bindgen-test" -version = "0.3.43" +version = "0.3.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68497a05fb21143a08a7d24fc81763384a3072ee43c44e86aad1744d6adef9d9" +checksum = "d381749acb0943d357dcbd8f0b100640679883fcdeeef04def49daf8d33a5426" dependencies = [ "console_error_panic_hook", "js-sys", @@ -785,20 +812,20 @@ dependencies = [ [[package]] name = "wasm-bindgen-test-macro" -version = "0.3.43" +version = "0.3.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b8220be1fa9e4c889b30fd207d4906657e7e90b12e0e6b0c8b8d8709f5de021" +checksum = "c97b2ef2c8d627381e51c071c2ab328eac606d3f69dd82bcbca20a9e389d95f0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] name = "web-sys" -version = "0.3.70" +version = "0.3.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0" +checksum = "f6488b90108c040df0fe62fa815cbdee25124641df01814dd7282749234c6112" dependencies = [ "js-sys", "wasm-bindgen", @@ -904,7 +931,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 8874b41..f562287 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "aluvm" description = "Functional registry-based RISC virtual machine" -version = "0.11.0-beta.8" +version = "0.11.0-beta.9" authors = ["Dr Maxim Orlovsky "] repository = "https://github.com/aluvm/rust-aluvm" homepage = "https://aluvm.org" @@ -30,7 +30,7 @@ strict_types = { version = "2.7.0", optional = true } sha2 = "0.10.8" blake3 = "1.5.4" ripemd = "0.1.3" -secp256k1 = { version = "0.29.0", optional = true, features = ["global-context"] } +secp256k1 = { version = "0.30.0", optional = true, features = ["global-context"] } curve25519-dalek = { version = "3.2.1", optional = true } half = "2.4.1" # Required to maintain MSRV serde_crate = { package = "serde", version = "1", optional = true } diff --git a/src/data/arithm.rs b/src/data/arithm.rs index 5c9b3a3..df44e16 100644 --- a/src/data/arithm.rs +++ b/src/data/arithm.rs @@ -461,7 +461,11 @@ impl Neg for Number { impl Number { /// Returns the absolute value of the number pub fn abs(self) -> Option { - if self.is_positive() { Some(self) } else { self.applying_sign(false) } + if self.is_positive() { + Some(self) + } else { + self.applying_sign(false) + } } } diff --git a/src/data/encoding.rs b/src/data/encoding.rs index 6a0c8d4..f39f959 100644 --- a/src/data/encoding.rs +++ b/src/data/encoding.rs @@ -260,7 +260,11 @@ impl Encode for Option { #[inline] fn encode(&self, writer: impl Write) -> Result { - if let Some(s) = self { s.encode(writer) } else { Ok(0u8.encode(writer)?) } + if let Some(s) = self { + s.encode(writer) + } else { + Ok(0u8.encode(writer)?) + } } } @@ -422,7 +426,11 @@ impl Encode for Option { #[inline] fn encode(&self, writer: impl Write) -> Result { - if let Some(s) = self { s.encode(writer) } else { Ok(0u8.encode(writer)?) } + if let Some(s) = self { + s.encode(writer) + } else { + Ok(0u8.encode(writer)?) + } } } diff --git a/src/isa/exec.rs b/src/isa/exec.rs index e9fe937..2dfb077 100644 --- a/src/isa/exec.rs +++ b/src/isa/exec.rs @@ -48,6 +48,9 @@ pub enum ExecStep { /// Stop program execution Stop, + /// Stop and fail program execution + Fail, + /// Move to the next instruction Next, @@ -227,35 +230,32 @@ impl InstructionSet for ControlFlowOp { fn exec(&self, regs: &mut CoreRegs, site: LibSite, _: &()) -> ExecStep { match self { - ControlFlowOp::Fail => { - regs.st0 = false; - ExecStep::Stop - } + ControlFlowOp::Fail => ExecStep::Fail, ControlFlowOp::Test => { if regs.st0 { ExecStep::Next } else { - ExecStep::Stop + ExecStep::Fail } } ControlFlowOp::Jmp(offset) => { - regs.jmp().map(|_| ExecStep::Jump(*offset)).unwrap_or(ExecStep::Stop) + regs.jmp().map(|_| ExecStep::Jump(*offset)).unwrap_or(ExecStep::Fail) } ControlFlowOp::Jif(offset) => { if regs.st0 { - regs.jmp().map(|_| ExecStep::Jump(*offset)).unwrap_or(ExecStep::Stop) + regs.jmp().map(|_| ExecStep::Jump(*offset)).unwrap_or(ExecStep::Fail) } else { ExecStep::Next } } ControlFlowOp::Routine(offset) => { - regs.call(site).map(|_| ExecStep::Jump(*offset)).unwrap_or(ExecStep::Stop) + regs.call(site).map(|_| ExecStep::Jump(*offset)).unwrap_or(ExecStep::Fail) } ControlFlowOp::Call(site) => { - regs.call(*site).map(|_| ExecStep::Call(*site)).unwrap_or(ExecStep::Stop) + regs.call(*site).map(|_| ExecStep::Call(*site)).unwrap_or(ExecStep::Fail) } ControlFlowOp::Exec(site) => { - regs.jmp().map(|_| ExecStep::Call(*site)).unwrap_or(ExecStep::Stop) + regs.jmp().map(|_| ExecStep::Call(*site)).unwrap_or(ExecStep::Fail) } ControlFlowOp::Ret => regs.ret().map(ExecStep::Call).unwrap_or(ExecStep::Stop), } @@ -1418,7 +1418,7 @@ impl InstructionSet for DigestOp { let none; match self { DigestOp::Ripemd(src, dst) => { - let s = regs.get_s(*src); + let s = regs.s16(*src); none = s.is_none(); let hash = s.map(|s| { let mut hash: [u8; 20] = ripemd::Ripemd160::digest(s.as_ref()).into(); @@ -1429,19 +1429,19 @@ impl InstructionSet for DigestOp { regs.set_n(RegR::R160, dst, hash); } DigestOp::Sha256(src, dst) => { - let s = regs.get_s(*src); + let s = regs.s16(*src); none = s.is_none(); let hash: Option<[u8; 32]> = s.map(|s| sha2::Sha256::digest(s.as_ref()).into()); regs.set_n(RegR::R256, dst, hash); } DigestOp::Blake3(src, dst) => { - let s = regs.get_s(*src); + let s = regs.s16(*src); none = s.is_none(); let hash: Option<[u8; 32]> = s.map(|s| blake3::hash(s.as_ref()).into()); regs.set_n(RegR::R256, dst, hash); } DigestOp::Sha512(src, dst) => { - let s = regs.get_s(*src); + let s = regs.s16(*src); none = s.is_none(); let hash: Option<[u8; 64]> = s.map(|s| sha2::Sha512::digest(s.as_ref()).into()); regs.set_n(RegR::R512, dst, hash); diff --git a/src/isa/flags.rs b/src/isa/flags.rs index 28067e9..121c2a8 100644 --- a/src/isa/flags.rs +++ b/src/isa/flags.rs @@ -402,7 +402,11 @@ impl Display for IntFlags { } else { f.write_char('u')?; } - if self.wrap { f.write_char('w') } else { f.write_char('c') } + if self.wrap { + f.write_char('w') + } else { + f.write_char('c') + } } } diff --git a/src/isa/instr.rs b/src/isa/instr.rs index 9b37c96..a4f8083 100644 --- a/src/isa/instr.rs +++ b/src/isa/instr.rs @@ -250,7 +250,7 @@ pub enum MoveOp { SwpF(RegF, Reg32, Reg32), /// Move operation: moves value of one of the general non-arithmetic registers into another - /// general non- arithmetic register of the same bit size, clearing its previous value and + /// general non-arithmetic register of the same bit size, clearing its previous value and /// setting the source to `None`. #[display("mov {0}{1},{0}{2}")] MovR(RegR, Reg32, Reg32), @@ -280,7 +280,7 @@ pub enum MoveOp { /// Conversion operation: converts value from one of the float arithmetic registers to a /// destination register according to floating encoding rules. If the value does not fit /// destination bit dimension, truncates the most significant non-sign bits until they fit, - /// setting `st0` value to `false`. Otherwise sets `st0` to `true`. + /// setting `st0` value to `false`. Otherwise, sets `st0` to `true`. #[display("cnv {0}{1},{2}{3}")] CnvF(RegF, Reg32, RegF, Reg32), @@ -291,7 +291,7 @@ pub enum MoveOp { #[display("cpy {0}{1},{2}{3}")] CpyR(RegR, Reg32, RegR, Reg32), - /// Swap-copy operation: swaps value one of the integer arithmetic registers with a value of an + /// Swap-copy operation: swaps value one of the integer arithmetic registers with a value of a /// general non-arithmetic register. If any of the values do not fit destination bit /// dimensions, truncates the most significant bits until they fit, setting `st0` value to /// `false`. Otherwise, extends most significant bits with zeros and sets `st0` to `true`. @@ -301,7 +301,7 @@ pub enum MoveOp { /// Conversion operation: converts value of an integer arithmetic register to a float register /// according to floating encoding rules. If the value does not fit destination bit dimension, /// truncates the most significant non-sign bits until they fit, setting `st0` value to - /// `false`. Otherwise sets `st0` to `true`. + /// `false`. Otherwise, sets `st0` to `true`. /// /// NB: operation always treats integers as signed integers. #[display("cnv {0}{1},{2}{3}")] @@ -310,7 +310,7 @@ pub enum MoveOp { /// Conversion operation: converts value of a float arithmetic register to an integer register /// according to floating encoding rules. If the value does not fit destination bit dimension, /// truncates the most significant non-sign bits until they fit, setting `st0` value to - /// `false`. Otherwise sets `st0` to `true`. + /// `false`. Otherwise, sets `st0` to `true`. /// /// NB: operation always treats integers as signed integers. #[display("cnv {0}{1},{2}{3}")] @@ -411,7 +411,7 @@ pub enum CmpOp { St(MergeFlag, RegA, Reg8), /// Inverses value in `st0` register - #[display("stinv")] + #[display("inv st0")] StInv, } @@ -565,7 +565,7 @@ pub enum BitwiseOp { /** Source & destination register */ Reg32, ), - /// Right bit shift, cycling the shifted values (least significant bit becomes nost + /// Right bit shift, cycling the shifted values (the least significant bit becomes the most /// significant), putting the result into the first source register. Does not modify `st0` /// value. /// @@ -579,11 +579,11 @@ pub enum BitwiseOp { /** Source & destination register */ Reg32, ), - /// Reverses bit order in the integer arithmetic register. Does not modify `st0` value. + /// Reverses bits order in the integer arithmetic register. Does not modify `st0` value. #[display("rev {0}{1}")] RevA(RegA, Reg32), - /// Reverses bit order in the generic non-arithmetic register. Does not modify `st0` value. + /// Reverses bits order in the generic non-arithmetic register. Does not modify `st0` value. #[display("rev {0}{1}")] RevR(RegR, Reg32), } @@ -628,9 +628,9 @@ pub enum BytesOp { /// arguments, the end offset is the greatest one. If any of the offsets exceeds the length of /// the string in the destination register, operation behaviour is defined by the provided /// boolean flag: - /// - if the flag is `true`, the string length is extended to the largest of the offsets and all - /// bytes between previous string length and start offset are filled with zeros, setting `st0` - /// value to `false`; + /// - if the flag is `true`, the string length is extended to the largest of the offsets and + /// all bytes between previous string length and start offset are filled with zeros, setting + /// `st0` value to `false`; /// - if the flag is `false`, the destination register is set to `None` and `st0` is set to /// `false`. /// @@ -716,7 +716,7 @@ pub enum BytesOp { ), /// Join bytestrings from two registers into destination, overwriting its value. If the length - /// of the joined string exceeds the maximum string register length (2^16 bytes), than the + /// of the joined string exceeds the maximum string register length (2^16 bytes), then the /// destination register is set to `None` state and `st0` is set to `false`. Otherwise, /// `st0` value is not modified. #[display("join {0},{1},{2}")] @@ -724,7 +724,7 @@ pub enum BytesOp { /// Split bytestring at a given offset taken from `a16` register into two destination strings, /// overwriting their value. If offset exceeds the length of the string in the register, - /// than the behaviour is determined by the [`SplitFlag`] value. + /// then the behaviour is determined by the [`SplitFlag`] value. /// ///
     /// +--------------------
diff --git a/src/isa/macros.rs b/src/isa/macros.rs
index ba32341..e07ca3c 100644
--- a/src/isa/macros.rs
+++ b/src/isa/macros.rs
@@ -95,6 +95,10 @@ macro_rules! aluasm_inner {
         $code.push($crate::instr!{ $op });
         $crate::aluasm_inner! { $code => $( $tt )* }
     };
+    { $code:ident => $op:ident $arg:literal @ $lib:ident ; $($tt:tt)* } => {
+        $code.push($crate::instr!{ $op $arg @ $lib });
+        $crate::aluasm_inner! { $code => $( $tt )* }
+    };
     { $code:ident => $op:ident $arg:literal @ $lib:literal ; $($tt:tt)* } => {
         $code.push($crate::instr!{ $op $arg @ $lib });
         $crate::aluasm_inner! { $code => $( $tt )* }
@@ -179,10 +183,16 @@ macro_rules! instr {
         Instr::ControlFlow(ControlFlowOp::Jif($offset))
     };
     (routine $offset:literal) => {
-        Instr::ControlFlow(ControlFlowOp::Reutine($offset))
+        Instr::ControlFlow(ControlFlowOp::Routine($offset))
     };
     (routine $offset:ident) => {
-        Instr::ControlFlow(ControlFlowOp::Reutine($offset))
+        Instr::ControlFlow(ControlFlowOp::Routine($offset))
+    };
+    (call $offset:literal @ $lib:ident) => {
+        Instr::ControlFlow(ControlFlowOp::Call(LibSite::with(
+            $offset,
+            $lib
+        )))
     };
     (call $offset:literal @ $lib:literal) => {
         Instr::ControlFlow(ControlFlowOp::Call(LibSite::with(
@@ -190,6 +200,12 @@ macro_rules! instr {
             $lib.parse().expect("wrong library reference"),
         )))
     };
+    (exec $offset:literal @ $lib:ident) => {
+        Instr::ControlFlow(ControlFlowOp::Exec(LibSite::with(
+            $offset,
+            $lib
+        )))
+    };
     (call $offset:ident @ $lib:ident) => {
         Instr::ControlFlow(ControlFlowOp::Call(LibSite::with(
             $offset,
diff --git a/src/library/lib.rs b/src/library/lib.rs
index 640b99c..49b2ec8 100644
--- a/src/library/lib.rs
+++ b/src/library/lib.rs
@@ -324,7 +324,7 @@ impl Lib {
         let mut reader = Cursor::with(&self.code, &self.data, &self.libs);
         while !reader.is_eof() {
             let pos = reader.offset().0 as usize;
-            write!(writer, "offset_0x{pos:04X}: ")?;
+            write!(writer, "@x{pos:06X}: ")?;
             match Instr::::decode(&mut reader) {
                 Ok(instr) => writeln!(writer, "{instr}")?,
                 Err(_) => writeln!(writer, "\n{}", ByteStr::with(&self.code.as_ref()[pos..]))?,
@@ -384,8 +384,19 @@ impl Lib {
         );
 
         let mut cursor = Cursor::with(&self.code, &self.data, &self.libs);
-        let lib_hash = self.id();
-        cursor.seek(entrypoint).ok()?;
+        let lib_id = self.id();
+
+        #[cfg(feature = "log")]
+        let lib_mnemonic = lib_id.to_baid64_mnemonic();
+        #[cfg(feature = "log")]
+        let lib_ref = lib_mnemonic.split_at(5).0;
+
+        if cursor.seek(entrypoint).is_err() {
+            registers.st0 = false;
+            #[cfg(feature = "log")]
+            eprintln!("jump to non-existing offset; halting, {d}st0{z} is set to {r}false{z}");
+            return None;
+        }
 
         #[cfg(feature = "log")]
         let mut st0 = registers.st0;
@@ -397,14 +408,14 @@ impl Lib {
 
             #[cfg(feature = "log")]
             {
-                eprint!("{m}@{pos:06}:{z} {: <32}; ", instr.to_string());
+                eprint!("{m}{}@x{pos:06X}:{z} {: <32}; ", lib_ref, instr.to_string());
                 for reg in instr.src_regs() {
                     let val = registers.get(reg);
                     eprint!("{d}{reg}={z}{w}{val}{z} ");
                 }
             }
 
-            let next = instr.exec(registers, LibSite::with(pos, lib_hash), context);
+            let next = instr.exec(registers, LibSite::with(pos, lib_id), context);
 
             #[cfg(feature = "log")]
             {
@@ -435,6 +446,13 @@ impl Lib {
                     }
                     return None;
                 }
+                ExecStep::Fail => {
+                    registers.st0 = false;
+                    assert_eq!(registers.st0, false);
+                    #[cfg(feature = "log")]
+                    eprintln!("halting, {d}st0{z} is set to {r}false{z}");
+                    return None;
+                }
                 ExecStep::Next => {
                     #[cfg(feature = "log")]
                     eprintln!();
@@ -443,7 +461,14 @@ impl Lib {
                 ExecStep::Jump(pos) => {
                     #[cfg(feature = "log")]
                     eprintln!("{}", pos);
-                    cursor.seek(pos).ok()?;
+                    if cursor.seek(pos).is_err() {
+                        registers.st0 = false;
+                        #[cfg(feature = "log")]
+                        eprintln!(
+                            "jump to non-existing offset; halting, {d}st0{z} is set to {r}false{z}"
+                        );
+                        return None;
+                    }
                 }
                 ExecStep::Call(site) => {
                     #[cfg(feature = "log")]
diff --git a/src/reg/core_regs.rs b/src/reg/core_regs.rs
index 01f47d1..b9e5319 100644
--- a/src/reg/core_regs.rs
+++ b/src/reg/core_regs.rs
@@ -211,13 +211,81 @@ impl CoreRegs {
         }
     }
 
+    /// Get value from `a8` register.
+    pub fn a8(&self, idx: impl Into) -> Option { self.a8[idx.into().to_usize()] }
+    /// Get value from `a16` register.
+    pub fn a16(&self, idx: impl Into) -> Option { self.a16[idx.into().to_usize()] }
+    /// Get value from `a32` register.
+    pub fn a32(&self, idx: impl Into) -> Option { self.a32[idx.into().to_usize()] }
+    /// Get value from `a64` register.
+    pub fn a64(&self, idx: impl Into) -> Option { self.a64[idx.into().to_usize()] }
+    /// Get value from `a128` register.
+    pub fn a128(&self, idx: impl Into) -> Option { self.a128[idx.into().to_usize()] }
+
+    /// Sets `a8` register to a given value. Returns previous register value.
+    pub fn set_a8(&mut self, idx: impl Into, val: u8) -> Option {
+        self.a8[idx.into().to_usize()].replace(val)
+    }
+    /// Sets `a16` register to a given value. Returns previous register value.
+    pub fn set_a16(&mut self, idx: impl Into, val: u16) -> Option {
+        self.a16[idx.into().to_usize()].replace(val)
+    }
+    /// Sets `a32` register to a given value. Returns previous register value.
+    pub fn set_a32(&mut self, idx: impl Into, val: u32) -> Option {
+        self.a32[idx.into().to_usize()].replace(val)
+    }
+    /// Sets `a64` register to a given value. Returns previous register value.
+    pub fn set_a64(&mut self, idx: impl Into, val: u64) -> Option {
+        self.a64[idx.into().to_usize()].replace(val)
+    }
+    /// Sets `a128` register to a given value. Returns previous register value.
+    pub fn set_a128(&mut self, idx: impl Into, val: u128) -> Option {
+        self.a128[idx.into().to_usize()].replace(val)
+    }
+
+    /// Clears `a8` register (sets its value to `None`). Returns previous register value.
+    pub fn clr_a8(&mut self, idx: impl Into) -> Option {
+        self.a8[idx.into().to_usize()].take()
+    }
+    /// Clears `a16` register (sets its value to `None`). Returns previous register value.
+    pub fn clr_a16(&mut self, idx: impl Into) -> Option {
+        self.a16[idx.into().to_usize()].take()
+    }
+    /// Clears `a32` register (sets its value to `None`). Returns previous register value.
+    pub fn clr_a32(&mut self, idx: impl Into) -> Option {
+        self.a32[idx.into().to_usize()].take()
+    }
+    /// Clears `a64` register (sets its value to `None`). Returns previous register value.
+    pub fn clr_a64(&mut self, idx: impl Into) -> Option {
+        self.a64[idx.into().to_usize()].take()
+    }
+    /// Clears `a128` register (sets its value to `None`). Returns previous register value.
+    pub fn clr_a128(&mut self, idx: impl Into) -> Option {
+        self.a128[idx.into().to_usize()].take()
+    }
+
+    /// Gets `s16` register value.
+    pub fn s16(&self, idx: impl Into) -> Option<&ByteStr> {
+        self.s16[idx.into().as_usize()].as_ref()
+    }
+
+    /// Sets `s16` register to a given value. Returns previous register value.
+    pub fn set_s16(&mut self, idx: impl Into, val: impl Into) -> Option {
+        self.s16[idx.into().as_usize()].replace(val.into())
+    }
+
+    /// Clears `s16` register (sets to `None`). Returns previous register value.
+    pub fn clr_s16(&mut self, idx: impl Into) -> Option {
+        self.s16[idx.into().as_usize()].take()
+    }
+
     /// Extracts value for any type of registers
     pub fn get(&self, reg: impl Into) -> RegValue {
         match reg.into() {
             Reg::A(reg, index) => self.get_n(reg, index).into(),
             Reg::F(reg, index) => self.get_n(reg, index).into(),
             Reg::R(reg, index) => self.get_n(reg, index).into(),
-            Reg::S(reg) => self.get_s(reg).cloned().into(),
+            Reg::S(reg) => self.s16(reg).cloned().into(),
         }
     }
 
@@ -298,6 +366,7 @@ impl CoreRegs {
 
     /// Returns value from one of `S`-registers
     #[inline]
+    #[deprecated(since = "0.11.0-beta.9", note = "use `s16` method")]
     pub fn get_s(&self, index: impl Into) -> Option<&ByteStr> {
         self.s16[index.into().as_usize()].as_ref()
     }
@@ -323,7 +392,7 @@ impl CoreRegs {
         idx1: impl Into,
         idx2: impl Into,
     ) -> Option<(&ByteStr, &ByteStr)> {
-        self.get_s(idx1).and_then(|val1| self.get_s(idx2).map(|val2| (val1, val2)))
+        self.s16(idx1).and_then(|val1| self.s16(idx2).map(|val2| (val1, val2)))
     }
 
     /// Assigns the provided value to the register bit-wise. Silently discards most significant bits
@@ -396,6 +465,7 @@ impl CoreRegs {
     /// Assigns the provided value to the string register.
     ///
     /// Returns `true` if the value was not `None`.
+    #[deprecated(since = "0.11.0-beta.9", note = "use `set_s16` method")]
     pub fn set_s(&mut self, index: impl Into, value: Option>) -> bool {
         let reg = &mut self.s16[index.into().as_usize()];
         let was_set = reg.is_some();
@@ -406,13 +476,19 @@ impl CoreRegs {
     /// Assigns the provided value to the string register if the register is not initialized.
     ///
     /// Returns `false` if the register is initialized and the value is not `None`.
+    #[deprecated(since = "0.11.0-beta.9")]
     pub fn set_s_if(&mut self, index: impl Into, value: Option>) -> bool {
         let index = index.into();
-        if self.get_s(index).is_none() { self.set_s(index, value) } else { value.is_none() }
+        if self.s16(index).is_none() {
+            #[allow(deprecated)]
+            self.set_s(index, value)
+        } else {
+            value.is_none()
+        }
     }
 
     /// Executes provided operation (as callback function) if and only if all the provided registers
-    /// contain a value (initialized). Otherwise, sets destination to `None` and does not calls the
+    /// contain a value (initialized). Otherwise, sets destination to `None` and does not call the
     /// callback.
     #[inline]
     #[allow(clippy::too_many_arguments)]
@@ -828,7 +904,7 @@ mod test {
         }
 
         for idx in 0u8..16 {
-            regs.set_s(u4::with(idx), Some(ByteStr::with(format!("string index {idx}"))));
+            regs.set_s16(u4::with(idx), ByteStr::with(format!("string index {idx}")));
         }
 
         eprintln!("{regs:#?}");
diff --git a/src/vm.rs b/src/vm.rs
index 1e1b6a6..46a24cb 100644
--- a/src/vm.rs
+++ b/src/vm.rs
@@ -34,7 +34,7 @@ use crate::library::{Lib, LibId, LibSite};
 use crate::reg::CoreRegs;
 
 /// Alu virtual machine providing single-core execution environment
-#[derive(Debug, Default)]
+#[derive(Clone, Debug, Default)]
 pub struct Vm>
 where Isa: InstructionSet
 {