diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 01bab79..76c2ea7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -64,7 +64,7 @@ jobs: strategy: fail-fast: false matrix: - toolchain: [ nightly, beta, stable, 1.66.0 ] + toolchain: [ nightly, beta, stable, 1.67.0 ] steps: - uses: actions/checkout@v2 - name: Install rust ${{ matrix.toolchain }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 28eecb1..0b6ac92 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -22,3 +22,18 @@ jobs: uses: davidB/rust-cargo-make@v1 - name: Test run: cargo make test + wasm-testing: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Install rust nightly + uses: actions-rs/toolchain@v1 + with: + toolchain: nightly + override: true + - uses: Swatinem/rust-cache@v2 + - uses: jetli/wasm-pack-action@v0.3.0 + - name: Add wasm32 target + run: rustup target add wasm32-unknown-unknown + - name: Test in headless Chrome + run: wasm-pack test --headless --chrome diff --git a/Cargo.lock b/Cargo.lock index 48c8874..cdadfff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,19 +4,23 @@ version = 3 [[package]] name = "aluvm" -version = "0.10.3" +version = "0.10.4" dependencies = [ "amplify", "baid58", "curve25519-dalek", + "getrandom 0.2.10", "half", "paste", + "rand", "ripemd", "secp256k1", "serde", "sha2", "strict_encoding", "strict_types", + "wasm-bindgen", + "wasm-bindgen-test", ] [[package]] @@ -183,6 +187,16 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "console_error_panic_hook" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" +dependencies = [ + "cfg-if", + "wasm-bindgen", +] + [[package]] name = "constant_time_eq" version = "0.2.6" @@ -222,7 +236,7 @@ checksum = "90f9d052967f590a76e62eb387bd0bbb1b000182c3cefe5364db6b7211651bc0" dependencies = [ "byteorder", "digest 0.9.0", - "rand_core", + "rand_core 0.5.1", "subtle", "zeroize", ] @@ -265,7 +279,20 @@ checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" dependencies = [ "cfg-if", "libc", - "wasi", + "wasi 0.9.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", + "wasm-bindgen", ] [[package]] @@ -305,6 +332,15 @@ version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" +[[package]] +name = "js-sys" +version = "0.3.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" +dependencies = [ + "wasm-bindgen", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -345,6 +381,12 @@ version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79" +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + [[package]] name = "proc-macro2" version = "1.0.60" @@ -363,13 +405,43 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + [[package]] name = "rand_core" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" dependencies = [ - "getrandom", + "getrandom 0.1.16", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.10", ] [[package]] @@ -387,6 +459,12 @@ version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" +[[package]] +name = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + [[package]] name = "secp256k1" version = "0.27.0" @@ -472,9 +550,9 @@ dependencies = [ [[package]] name = "strict_encoding" -version = "2.3.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0be9dd3df80dc083d4e20ac87246a67f42b3a5016375ae5a48f292e85ccfb1bd" +checksum = "23b1c91b79e62afc09025d828fa0b8dbf4105513de38f126a017919c09bca472" dependencies = [ "amplify", "half", @@ -496,9 +574,9 @@ dependencies = [ [[package]] name = "strict_types" -version = "1.3.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e611df6ed5752860c760c94509b838cd225e9e9e5de301517db195cf6f1f2cd" +checksum = "75574939fc4c5a13ab4f49d06e965a9d4bd0aa1deff4e227a2afb6dbebff1249" dependencies = [ "amplify", "baid58", @@ -586,6 +664,12 @@ version = "0.9.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + [[package]] name = "wasm-bindgen" version = "0.2.87" @@ -611,6 +695,18 @@ dependencies = [ "wasm-bindgen-shared", ] +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "wasm-bindgen-macro" version = "0.2.87" @@ -640,6 +736,40 @@ version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" +[[package]] +name = "wasm-bindgen-test" +version = "0.3.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e6e302a7ea94f83a6d09e78e7dc7d9ca7b186bc2829c24a22d0753efd680671" +dependencies = [ + "console_error_panic_hook", + "js-sys", + "scoped-tls", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-bindgen-test-macro", +] + +[[package]] +name = "wasm-bindgen-test-macro" +version = "0.3.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecb993dd8c836930ed130e020e77d9b2e65dd0fbab1b67c790b0f5d80b11a575" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "web-sys" +version = "0.3.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "zeroize" version = "1.3.0" diff --git a/Cargo.toml b/Cargo.toml index 9ee719f..bb10300 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,13 +1,13 @@ [package] name = "aluvm" description = "Functional registry-based RISC virtual machine" -version = "0.10.3" +version = "0.10.4" authors = ["Dr Maxim Orlovsky "] repository = "https://github.com/aluvm/rust-aluvm" homepage = "https://aluvm.org" keywords = ["virtual-machine", "emulator", "functional", "risc", "edge-computing"] categories = ["no-std", "embedded", "compilers", "cryptography", "emulators"] -rust-version = "1.66" # Due to strict encoding library (caused by GAD) +rust-version = "1.67" # Due to strict encoding library edition = "2018" license = "Apache-2.0" readme = "README.md" @@ -20,21 +20,17 @@ name = "aluvm" name = "aluvm-stl" required-features = ["stl"] -[[example]] -name = "asm" -required-features = ["all"] - [dependencies] amplify = { version = "4.0.0", default-features = false, features = ["apfloat", "derive", "hex"] } paste = "1" -strict_encoding = { version = "2.3.0", default-features = false, features = ["float", "derive"] } -strict_types = { version = "1.3.0", optional = true } +strict_encoding = { version = "2.5.0", default-features = false, features = ["float", "derive"] } +strict_types = { version = "1.5.0", optional = true } sha2 = "0.10.6" ripemd = "0.1.3" baid58 = "0.3.2" secp256k1 = { version = "0.27.0", optional = true, features = ["global-context"] } curve25519-dalek = { version = "3.2", optional = true } -half = "2.1.0" +half = "~2.2.0" # Required to maintain MSRV serde_crate = { package = "serde", version = "1", optional = true } [features] @@ -46,5 +42,13 @@ alloc = ["amplify/alloc"] curve25519 = ["curve25519-dalek"] serde = ["serde_crate", "amplify/serde", "std"] +[target.'cfg(target_arch = "wasm32")'.dependencies] +wasm-bindgen = "0.2" +rand = { version = "0.8.4", optional = true } +getrandom = { version = "0.2", features = ["js"] } + +[target.'cfg(target_arch = "wasm32")'.dev-dependencies] +wasm-bindgen-test = "0.3" + [package.metadata.docs.rs] features = [ "all" ] diff --git a/MANIFEST.yml b/MANIFEST.yml index 6ec60bd..b0f5e6b 100644 --- a/MANIFEST.yml +++ b/MANIFEST.yml @@ -3,7 +3,7 @@ Type: Library Kind: Free software License: Apache-2.0 Language: Rust -Compiler: 1.66 +Compiler: 1.67 Author: Maxim Orlovsky Maintained: UBIDECO Institute, Switzerland Maintainers: diff --git a/examples/asm.rs b/examples/asm.rs deleted file mode 100644 index 8229b19..0000000 --- a/examples/asm.rs +++ /dev/null @@ -1,105 +0,0 @@ -// Reference rust implementation of AluVM (arithmetic logic unit virtual machine). -// To find more on AluVM please check -// -// Designed & written in 2021-2022 by -// Dr. Maxim Orlovsky -// This work is donated to LNP/BP Standards Association by Pandora Core AG -// -// This software is licensed under the terms of MIT License. -// You should have received a copy of the MIT License along with this software. -// If not, see . - -use aluvm::isa::Instr; -use aluvm::library::Lib; -use aluvm::{aluasm, Prog, Vm}; - -fn main() { - let code = aluasm! { - clr r1024[5] ; - put a16[8],5 ; - putif r256[1],0xaf67937b5498dc ; - putif a8[1],13 ; - swp a8[1],a8[2] ; - swp f256[8],f256[7] ; - dup a256[1],a256[7] ; - mov a16[1],a16[2] ; - mov r256[8],r256[7] ; - cpy a256[1],a256[7] ; - cnv f128[4],a128[3] ; - spy a1024[15],r1024[24] ; - gt.u a8[5],a64[9] ; - lt.s a8[5],a64[9] ; - gt.e f64[5],f64[9] ; - lt.r f64[5],f64[9] ; - gt r160[5],r256[9] ; - lt r160[5],r256[9] ; - eq.e a8[5],a8[9] ; - eq.n r160[5],r160[9] ; - eq.e f64[19],f64[29] ; - ifn a32[31] ; - ifz r2048[17] ; - inv st0 ; - st.s a8[1] ; - put a32[12],13 ; - put a32[13],66 ; - add.uc a32[12],a32[13] ; - add.sw a32[12],a32[13] ; - sub.sc a32[13],a32[12] ; - mul.uw a32[12],a32[13] ; - div.cu a32[12],a32[13] ; - put f32[12],2.13 ; - put f32[13],5.18 ; - add.z f32[12],f32[13] ; - sub.n f32[13],f32[12] ; - mul.c f32[12],f32[13] ; - div.f f32[12],f32[13] ; - rem a8[2],a64[8] ; - inc a16[3] ; - add a16[4],5 ; - dec a16[8] ; - sub a16[4],82 ; - neg a64[15] ; - abs f128[11] ; - and a32[5],a32[6],a32[5] ; - xor r128[5],r128[6],r128[5] ; - shr.u a256[12],a16[2] ; - shr.s a256[12],a16[2] ; - shl r256[24],a16[22] ; - shr r256[24],a16[22] ; - scr r256[24],a16[22] ; - scl r256[24],a16[22] ; - rev a512[28] ; - ripemd r160[7],s16[9] ; - sha2 r256[2],s16[19] ; - secpgen r512[1],r256[1] ; - dup r512[1],r512[22] ; - spy a512[1],r512[22] ; - secpmul r256[1],r512[1],r512[2] ; - secpadd r512[1],r512[22] ; - secpneg r512[3],r512[1] ; - ifz a16[8] ; - jif 190 ; - call 56 @ alu07EnUZgFtu28sWqqH3womkTopXCkgAGsCLvLnYvNcPLRt ; - ret ; - }; - - println!("Instructions:\n{:#?}\n", code); - let lib = Lib::assemble(&code).unwrap(); - let code = lib.disassemble::().unwrap(); - println!("Assembly:"); - for instr in code { - println!("\t\t{}", instr); - } - let lib_repr = lib.to_string(); - - eprint!("\nExecuting the program {} ... ", lib.id()); - let program = Prog::::new(lib); - let mut runtime = Vm::::new(); - match runtime.run(&program, &()) { - true => eprintln!("success"), - false => eprintln!("failure"), - } - - println!("\nVM microprocessor core state:\n{:#?}", runtime.registers); - println!("\n{}\n", lib_repr); -} diff --git a/src/data/number.rs b/src/data/number.rs index 90c34c7..5a41b68 100644 --- a/src/data/number.rs +++ b/src/data/number.rs @@ -218,15 +218,8 @@ impl IntLayout { } /// Returns whether a `usize`-value fits the layout dimensions. - /// - /// # Panics - /// - /// Panics on platforms where usize is less than `u64` pub fn fits_usize(self, value: usize) -> bool { - (self.bytes() <= 8 && value > u64::MAX as usize) - || (self.bytes() <= 4 && value > u32::MAX as usize) - || (self.bytes() <= 2 && value > u16::MAX as usize) - || (self.bytes() <= 1 && value > u8::MAX as usize) + self.bits() >= (usize::BITS - value.leading_zeros()) as u16 } } diff --git a/src/isa/asm.rs b/src/isa/asm.rs deleted file mode 100644 index 57e596d..0000000 --- a/src/isa/asm.rs +++ /dev/null @@ -1,1328 +0,0 @@ -// Reference rust implementation of AluVM (arithmetic logic unit virtual machine). -// To find more on AluVM please check -// -// SPDX-License-Identifier: Apache-2.0 -// -// Written in 2021-2023 by -// Dr Maxim Orlovsky -// -// Copyright (C) 2021-2022 LNP/BP Standards Association. All rights reserved. -// Copyright (C) 2023 UBIDECO Institute. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -/// Macro compiler for AluVM assembler. -/// -/// # Example -/// -/// ``` -/// # use aluvm::{aluasm, Prog, Vm}; -/// # use aluvm::library::Lib; -/// # use aluvm::isa::Instr; -/// -/// let code = aluasm! { -/// clr r1024[5] ; -/// put a16[8],378 ; -/// putif r128[5],0xaf67937b5498dc ; -/// swp a8[1],a8[2] ; -/// swp f256[8],f256[7] ; -/// dup a256[1],a256[7] ; -/// mov a16[1],a16[2] ; -/// mov r256[8],r256[7] ; -/// cpy a256[1],a256[7] ; -/// ret ; -/// jmp 0 ; -/// }; -/// -/// let lib = Lib::assemble(&code).unwrap(); -/// let program = Prog::::new(lib); -/// let mut vm = Vm::::new(); -/// match vm.run(&program, &()) { -/// true => println!("success"), -/// false => println!("failure"), -/// } -/// ``` -#[macro_export] -macro_rules! aluasm { - ($( $tt:tt )+) => { { #[allow(unused_imports)] { - #[cfg(feature = "alloc")] - extern crate alloc; - #[cfg(feature = "alloc")] - use alloc::boxed::Box; - #[cfg(not(feature = "alloc"))] - use std::boxed::Box; - - use $crate::isa::{ - ArithmeticOp, BitwiseOp, BytesOp, CmpOp, ControlFlowOp, DigestOp, ExtendFlag, FloatEqFlag, Instr, IntFlags, - MergeFlag, MoveOp, ReservedOp, PutOp, RoundingFlag, Secp256k1Op, SignFlag, NoneEqFlag, - }; - use $crate::reg::{ - Reg16, Reg32, Reg8, RegA, RegA2, RegBlockAFR, RegBlockAR, RegF, RegR, RegS, - NumericRegister, - }; - use $crate::library::LibSite; - use $crate::data::{ByteStr, Number, MaybeNumber, Step}; - - let mut code: Vec> = vec![]; - #[allow(unreachable_code)] { - $crate::aluasm_inner! { code => $( $tt )+ }; - } - code - } } } -} - -#[doc(hidden)] -#[macro_export] -macro_rules! aluasm_inner { - { $code:ident => } => { }; - { $code:ident => $op:ident ; $($tt:tt)* } => { - $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 ),+ ; $($tt:tt)* } => { - $code.push($crate::instr!{ $op $( $arg ),+ }); - $crate::aluasm_inner! { $code => $( $tt )* } - }; - { $code:ident => $op:ident $( $arg:ident ),+ ; $($tt:tt)* } => { - $code.push($crate::instr!{ $op $( $arg ),+ }); - $crate::aluasm_inner! { $code => $( $tt )* } - }; - { $code:ident => $op:ident . $flag:ident $( $arg:ident ),+ ; $($tt:tt)* } => { - $code.push($crate::instr!{ $op . $flag $( $arg ),+ }); - $crate::aluasm_inner! { $code => $( $tt )* } - }; - { $code:ident => $op:ident $( $arg:ident [ $idx:literal ] ),+ ; $($tt:tt)* } => { - $code.push($crate::instr!{ $op $( $arg [ $idx ] ),+ }); - $crate::aluasm_inner! { $code => $( $tt )* } - }; - { $code:ident => $op:ident . $flag:ident $( $arg:ident [ $idx:literal ] ),+ ; $($tt:tt)* } => { - $code.push($crate::instr!{ $op . $flag $( $arg [ $idx ] ),+ }); - $crate::aluasm_inner! { $code => $( $tt )* } - }; - { $code:ident => $op:ident $arg:ident [ $idx:literal ] , $arglit:literal ; $($tt:tt)* } => { - $code.push($crate::instr!{ $op $arg [ $idx ] , $arglit }); - $crate::aluasm_inner! { $code => $( $tt )* } - }; - { $code:ident => $op:ident . $flag:ident $arg:ident [ $idx:literal ], $arglit:expr ; $($tt:tt)* } => { - $code.push($crate::instr!{ $op . $flag $arg [ $idx ], $arglit }); - $crate::aluasm_inner! { $code => $( $tt )* } - }; -} - -#[doc(hidden)] -#[macro_export] -macro_rules! instr { - (fail) => { - Instr::ControlFlow(ControlFlowOp::Fail) - }; - (succ) => { - Instr::ControlFlow(ControlFlowOp::Succ) - }; - (jmp $offset:literal) => { - Instr::ControlFlow(ControlFlowOp::Jmp($offset)) - }; - (jif $offset:literal) => { - Instr::ControlFlow(ControlFlowOp::Jif($offset)) - }; - (routine $offset:literal) => { - Instr::ControlFlow(ControlFlowOp::Reutine($offset)) - }; - (call $offset:literal @ $lib:ident) => { - Instr::ControlFlow(ControlFlowOp::Call(LibSite::with( - $offset, - stringify!($lib).parse().expect("wrong library reference"), - ))) - }; - (exec $offset:literal @ $lib:ident) => { - Instr::ControlFlow(ControlFlowOp::Exec(LibSite::with( - $offset, - stringify!($lib).parse().expect("wrong library reference"), - ))) - }; - (ret) => { - Instr::ControlFlow(ControlFlowOp::Ret) - }; - - (clr $reg:ident[$idx:literal]) => { - Instr::Put($crate::_reg_sfx!(PutOp, Clr, $reg)( - $crate::_reg_ty!(Reg, $reg), - $crate::_reg_idx!($idx), - )) - }; - - (extr $regr:ident[$regr_idx:literal],s16[$idx:literal],a16[$offset_idx:literal]) => { - Instr::Bytes(BytesOp::Extr( - RegS::from($idx), - $crate::_reg_ty!(Reg, $regr), - $crate::_reg_idx16!($regr_idx), - $crate::_reg_idx16!($offset_idx), - )) - }; - (put s16[$idx:literal], $val:literal) => {{ - Instr::Bytes(BytesOp::Put(RegS::from($idx), Box::new(ByteStr::with(&$val)), false)) - }}; - (fill.e s16[$idx0:literal],a16[$idx1:literal],a16[$idx2:literal],a8[$idx3:literal]) => {{ - Instr::Bytes(BytesOp::Fill( - RegS::from($idx0), - $crate::_reg_idx!($idx1), - $crate::_reg_idx!($idx2), - $crate::_reg_idx!($idx3), - ExtendFlag::Extend, - )) - }}; - (fill.f s16[$idx0:literal],a16[$idx1:literal],a16[$idx2:literal],a8[$idx3:literal]) => {{ - Instr::Bytes(BytesOp::Fill( - RegS::from($idx0), - $crate::_reg_idx!($idx1), - $crate::_reg_idx!($idx2), - $crate::_reg_idx!($idx3), - ExtendFlag::Fail, - )) - }}; - (put $reg:ident[$idx:literal], $val:literal) => {{ - let s = stringify!($val); - let mut num = s.parse::().expect(&format!("invalid number literal `{}`", s)); - let reg = $crate::_reg_ty!(Reg, $reg); - num.reshape(reg.layout()); - Instr::Put($crate::_reg_sfx!(PutOp, Put, $reg)(reg, $crate::_reg_idx!($idx), Box::new(num))) - }}; - (putif $reg:ident[$idx:literal], $val:literal) => {{ - let s = stringify!($val); - let mut num = s.parse::().expect(&format!("invalid number literal `{}`", s)); - let reg = $crate::_reg_ty!(Reg, $reg); - num.reshape(reg.layout()); - Instr::Put($crate::_reg_sfx!(PutOp, PutIf, $reg)( - reg, - $crate::_reg_idx!($idx), - Box::new(num), - )) - }}; - - (swp $reg1:ident[$idx1:literal], $reg2:ident[$idx2:literal]) => {{ - if $crate::_reg_ty!(Reg, $reg1) != $crate::_reg_ty!(Reg, $reg2) { - panic!("Swap operation must be performed between registers of the same type"); - } - Instr::Move($crate::_reg_sfx!(MoveOp, Swp, $reg1)( - $crate::_reg_ty!(Reg, $reg1), - $crate::_reg_idx!($idx1), - $crate::_reg_idx!($idx2), - )) - }}; - (mov $dst_reg:ident[$dst_idx:literal], $src_reg:ident[$src_idx:literal]) => {{ - if $crate::_reg_ty!(Reg, $src_reg) != $crate::_reg_ty!(Reg, $dst_reg) { - panic!("Move operation must be performed between registers of the same type"); - } - Instr::Move($crate::_reg_sfx!(MoveOp, Mov, $src_reg)( - $crate::_reg_ty!(Reg, $src_reg), - $crate::_reg_idx!($src_idx), - $crate::_reg_idx!($dst_idx), - )) - }}; - (dup $dst_reg:ident[$dst_idx:literal], $src_reg:ident[$src_idx:literal]) => {{ - if $crate::_reg_ty!(Reg, $src_reg) != $crate::_reg_ty!(Reg, $dst_reg) { - panic!("Dup operation must be performed between registers of the same type"); - } - Instr::Move($crate::_reg_sfx!(MoveOp, Dup, $src_reg)( - $crate::_reg_ty!(Reg, $src_reg), - $crate::_reg_idx!($src_idx), - $crate::_reg_idx!($dst_idx), - )) - }}; - (cpy $dst_reg:ident[$dst_idx:literal], $src_reg:ident[$src_idx:literal]) => {{ - if $crate::_reg_ty!(Reg, $src_reg) != $crate::_reg_ty!(Reg, $dst_reg) { - panic!("Copy operation must be performed between registers of the same type"); - } - Instr::Move($crate::_reg_sfx!(MoveOp, Cpy, $src_reg)( - $crate::_reg_ty!(Reg, $src_reg), - $crate::_reg_idx!($src_idx), - $crate::_reg_ty!(Reg, $dst_reg), - $crate::_reg_idx!($dst_idx), - )) - }}; - (cnv $dst_reg:ident[$dst_idx:literal], $src_reg:ident[$src_idx:literal]) => {{ - match ($crate::_reg_block!($src_reg), $crate::_reg_block!($dst_reg)) { - (RegBlockAFR::A, RegBlockAFR::F) => Instr::Move(MoveOp::CnvAF( - $crate::_reg_tya!(Reg, $src_reg), - $crate::_reg_idx!($src_idx), - $crate::_reg_tyf!(Reg, $dst_reg), - $crate::_reg_idx!($dst_idx), - )), - (RegBlockAFR::F, RegBlockAFR::A) => Instr::Move(MoveOp::CnvFA( - $crate::_reg_tyf!(Reg, $src_reg), - $crate::_reg_idx!($src_idx), - $crate::_reg_tya!(Reg, $dst_reg), - $crate::_reg_idx!($dst_idx), - )), - (RegBlockAFR::A, RegBlockAFR::A) => Instr::Move(MoveOp::CnvA( - $crate::_reg_tya!(Reg, $src_reg), - $crate::_reg_idx!($src_idx), - $crate::_reg_tya!(Reg, $src_reg), - $crate::_reg_idx!($dst_idx), - )), - (RegBlockAFR::F, RegBlockAFR::F) => Instr::Move(MoveOp::CnvF( - $crate::_reg_tyf!(Reg, $src_reg), - $crate::_reg_idx!($src_idx), - $crate::_reg_tyf!(Reg, $src_reg), - $crate::_reg_idx!($dst_idx), - )), - (_, _) => panic!("Conversion operation between unsupported register types"), - } - }}; - (spy $dst_reg:ident[$dst_idx:literal], $src_reg:ident[$src_idx:literal]) => {{ - match ($crate::_reg_block!($src_reg), $crate::_reg_block!($dst_reg)) { - (RegBlockAFR::A, RegBlockAFR::R) => Instr::Move(MoveOp::SpyAR( - $crate::_reg_tya!(Reg, $src_reg), - $crate::_reg_idx!($src_idx), - $crate::_reg_tyr!(Reg, $dst_reg), - $crate::_reg_idx!($dst_idx), - )), - (RegBlockAFR::R, RegBlockAFR::A) => Instr::Move(MoveOp::SpyAR( - $crate::_reg_tya!(Reg, $dst_reg), - $crate::_reg_idx!($dst_idx), - $crate::_reg_tyr!(Reg, $src_reg), - $crate::_reg_idx!($src_idx), - )), - (_, _) => { - panic!("Swap-conversion operation is supported only between A and R registers") - } - } - }}; - - (gt $reg1:ident[$idx1:literal], $reg2:ident[$idx2:literal]) => {{ - if $crate::_reg_block!($reg1) != $crate::_reg_block!($reg2) { - panic!("`gt` operation may be applied only to the registers of the same family"); - } - if $crate::_reg_block!($reg1) != RegBlockAFR::R { - panic!("`gt` operation for arithmetic registers requires suffix"); - } - Instr::Cmp(CmpOp::GtR( - $crate::_reg_tyr!(Reg, $reg1), - $crate::_reg_idx!($idx1), - $crate::_reg_idx!($idx2), - )) - }}; - (gt.u $reg1:ident[$idx1:literal], $reg2:ident[$idx2:literal]) => {{ - if $crate::_reg_block!($reg1) != $crate::_reg_block!($reg2) { - panic!("`gt` operation may be applied only to the registers of the same family"); - } - Instr::Cmp(CmpOp::GtA( - SignFlag::Unsigned, - $crate::_reg_tya!(Reg, $reg1), - $crate::_reg_idx!($idx1), - $crate::_reg_idx!($idx2), - )) - }}; - (gt.s $reg1:ident[$idx1:literal], $reg2:ident[$idx2:literal]) => {{ - if $crate::_reg_block!($reg1) != $crate::_reg_block!($reg2) { - panic!("`gt` operation may be applied only to the registers of the same family"); - } - Instr::Cmp(CmpOp::GtA( - SignFlag::Signed, - $crate::_reg_tya!(Reg, $reg1), - $crate::_reg_idx!($idx1), - $crate::_reg_idx!($idx2), - )) - }}; - (gt.e $reg1:ident[$idx1:literal], $reg2:ident[$idx2:literal]) => {{ - if $crate::_reg_block!($reg1) != $crate::_reg_block!($reg2) { - panic!("`gt` operation may be applied only to the registers of the same family"); - } - Instr::Cmp(CmpOp::GtF( - FloatEqFlag::Exact, - $crate::_reg_tyf!(Reg, $reg1), - $crate::_reg_idx!($idx1), - $crate::_reg_idx!($idx2), - )) - }}; - (gt.r $reg1:ident[$idx1:literal], $reg2:ident[$idx2:literal]) => {{ - if $crate::_reg_block!($reg1) != $crate::_reg_block!($reg2) { - panic!("`gt` operation may be applied only to the registers of the same family"); - } - Instr::Cmp(CmpOp::GtF( - FloatEqFlag::Rounding, - $crate::_reg_tyf!(Reg, $reg1), - $crate::_reg_idx!($idx1), - $crate::_reg_idx!($idx2), - )) - }}; - (lt $reg1:ident[$idx1:literal], $reg2:ident[$idx2:literal]) => {{ - if $crate::_reg_block!($reg1) != $crate::_reg_block!($reg2) { - panic!("`lt` operation may be applied only to the registers of the same family"); - } - if $crate::_reg_block!($reg1) != RegBlockAFR::R { - panic!("`lt` operation for arithmetic registers requires suffix"); - } - Instr::Cmp(CmpOp::LtR( - $crate::_reg_tyr!(Reg, $reg1), - $crate::_reg_idx!($idx1), - $crate::_reg_idx!($idx2), - )) - }}; - (lt.u $reg1:ident[$idx1:literal], $reg2:ident[$idx2:literal]) => {{ - if $crate::_reg_block!($reg1) != $crate::_reg_block!($reg2) { - panic!("`lt` operation may be applied only to the registers of the same family"); - } - Instr::Cmp(CmpOp::LtA( - SignFlag::Unsigned, - $crate::_reg_tya!(Reg, $reg1), - $crate::_reg_idx!($idx1), - $crate::_reg_idx!($idx2), - )) - }}; - (lt.s $reg1:ident[$idx1:literal], $reg2:ident[$idx2:literal]) => {{ - if $crate::_reg_block!($reg1) != $crate::_reg_block!($reg2) { - panic!("`lt` operation may be applied only to the registers of the same family"); - } - Instr::Cmp(CmpOp::LtA( - SignFlag::Signed, - $crate::_reg_tya!(Reg, $reg1), - $crate::_reg_idx!($idx1), - $crate::_reg_idx!($idx2), - )) - }}; - (lt.e $reg1:ident[$idx1:literal], $reg2:ident[$idx2:literal]) => {{ - if $crate::_reg_block!($reg1) != $crate::_reg_block!($reg2) { - panic!("`lt` operation may be applied only to the registers of the same family"); - } - Instr::Cmp(CmpOp::LtF( - FloatEqFlag::Exact, - $crate::_reg_tyf!(Reg, $reg1), - $crate::_reg_idx!($idx1), - $crate::_reg_idx!($idx2), - )) - }}; - (lt.r $reg1:ident[$idx1:literal], $reg2:ident[$idx2:literal]) => {{ - if $crate::_reg_block!($reg1) != $crate::_reg_block!($reg2) { - panic!("`lt` operation may be applied only to the registers of the same family"); - } - Instr::Cmp(CmpOp::LtF( - FloatEqFlag::Rounding, - $crate::_reg_tyf!(Reg, $reg1), - $crate::_reg_idx!($idx1), - $crate::_reg_idx!($idx2), - )) - }}; - (eq.e $reg1:ident[$idx1:literal], $reg2:ident[$idx2:literal]) => {{ - if $crate::_reg_block!($reg1) != $crate::_reg_block!($reg2) { - panic!( - "Equivalence check must be performed between registers of the same type and size" - ); - } - match $crate::_reg_block!($reg1) { - RegBlockAFR::A => Instr::Cmp(CmpOp::EqA( - NoneEqFlag::Equal, - $crate::_reg_tya!(Reg, $reg1), - $crate::_reg_idx!($idx1), - $crate::_reg_idx!($idx2), - )), - RegBlockAFR::R => Instr::Cmp(CmpOp::EqR( - NoneEqFlag::Equal, - $crate::_reg_tyr!(Reg, $reg1), - $crate::_reg_idx!($idx1), - $crate::_reg_idx!($idx2), - )), - RegBlockAFR::F => Instr::Cmp(CmpOp::EqF( - FloatEqFlag::Exact, - $crate::_reg_tyf!(Reg, $reg1), - $crate::_reg_idx!($idx1), - $crate::_reg_idx!($idx2), - )), - } - }}; - (eq.n $reg1:ident[$idx1:literal], $reg2:ident[$idx2:literal]) => {{ - if $crate::_reg_block!($reg1) != $crate::_reg_block!($reg2) { - panic!( - "Equivalence check must be performed between registers of the same type and size" - ); - } - match $crate::_reg_block!($reg1) { - RegBlockAFR::A => Instr::Cmp(CmpOp::EqA( - NoneEqFlag::NonEqual, - $crate::_reg_tya!(Reg, $reg1), - $crate::_reg_idx!($idx1), - $crate::_reg_idx!($idx2), - )), - RegBlockAFR::R => Instr::Cmp(CmpOp::EqR( - NoneEqFlag::NonEqual, - $crate::_reg_tyr!(Reg, $reg1), - $crate::_reg_idx!($idx1), - $crate::_reg_idx!($idx2), - )), - _ => panic!("Wrong registers for `eq` operation"), - } - }}; - (eq.r $reg1:ident[$idx1:literal], $reg2:ident[$idx2:literal]) => {{ - if $crate::_reg_block!($reg1) != $crate::_reg_block!($reg2) { - panic!( - "Equivalence check must be performed between registers of the same type and size" - ); - } - Instr::Cmp(CmpOp::EqF( - FloatEqFlag::Rounding, - $crate::_reg_tyf!(Reg, $reg1), - $crate::_reg_idx!($idx1), - $crate::_reg_idx!($idx2), - )) - }}; - (eq s16[$idx1:literal],s16[$idx2:literal]) => {{ - Instr::Bytes(BytesOp::Eq(RegS::from($idx1), RegS::from($idx2))) - }}; - (ifn $reg:ident[$idx:literal]) => { - match $crate::_reg_block!($reg) { - RegBlockAFR::A => { - Instr::Cmp(CmpOp::IfNA($crate::_reg_tya!(Reg, $reg), $crate::_reg_idx!($idx))) - } - RegBlockAFR::R => { - Instr::Cmp(CmpOp::IfNR($crate::_reg_tyr!(Reg, $reg), $crate::_reg_idx!($idx))) - } - _ => panic!("Wrong registers for `ifn` operation"), - } - }; - (ifz $reg:ident[$idx:literal]) => { - match $crate::_reg_block!($reg) { - RegBlockAFR::A => { - Instr::Cmp(CmpOp::IfZA($crate::_reg_tya!(Reg, $reg), $crate::_reg_idx!($idx))) - } - RegBlockAFR::R => { - Instr::Cmp(CmpOp::IfZR($crate::_reg_tyr!(Reg, $reg), $crate::_reg_idx!($idx))) - } - _ => panic!("Wrong registers for `ifz` operation"), - } - }; - (st. $flag:ident $reg:ident[$idx:literal]) => { - Instr::Cmp(CmpOp::St( - $crate::_merge_flag!($flag), - $crate::_reg_tya!(Reg, $reg), - $crate::_reg_idx8!($idx), - )) - }; - (inv st0) => { - Instr::Cmp(CmpOp::StInv) - }; - - (add. $flag:ident $dst_reg:ident[$dst_idx:literal], $reg1:ident[$idx1:literal]) => { - match ($crate::_reg_block!($reg1), $crate::_reg_block!($dst_reg)) { - (RegBlockAFR::A, RegBlockAFR::A) => Instr::Arithmetic(ArithmeticOp::AddA( - $crate::_int_flags!($flag), - $crate::_reg_tya!(Reg, $reg1), - $crate::_reg_idx!($idx1), - $crate::_reg_idx!($dst_idx), - )), - (RegBlockAFR::F, RegBlockAFR::F) => Instr::Arithmetic(ArithmeticOp::AddF( - $crate::_rounding_flag!($flag), - $crate::_reg_tyf!(Reg, $reg1), - $crate::_reg_idx!($idx1), - $crate::_reg_idx!($dst_idx), - )), - (a, b) if a == b => panic!("addition requires integer or float registers"), - (_, _) if $crate::_reg_ty!(Reg, $reg1) != $crate::_reg_ty!(Reg, $dst_reg) => { - panic!("addition must be performed between registers of the same size") - } - (_, _) => panic!("addition must be performed between registers of the same type"), - } - }; - (sub. $flag:ident $dst_reg:ident[$dst_idx:literal], $reg1:ident[$idx1:literal]) => { - match ($crate::_reg_block!($reg1), $crate::_reg_block!($dst_reg)) { - (RegBlockAFR::A, RegBlockAFR::A) => Instr::Arithmetic(ArithmeticOp::SubA( - $crate::_int_flags!($flag), - $crate::_reg_tya!(Reg, $reg1), - $crate::_reg_idx!($idx1), - $crate::_reg_idx!($dst_idx), - )), - (RegBlockAFR::F, RegBlockAFR::F) => Instr::Arithmetic(ArithmeticOp::SubF( - $crate::_rounding_flag!($flag), - $crate::_reg_tyf!(Reg, $reg1), - $crate::_reg_idx!($idx1), - $crate::_reg_idx!($dst_idx), - )), - (a, b) if a == b => panic!("subtraction requires integer or float registers"), - (_, _) if $crate::_reg_ty!(Reg, $reg1) != $crate::_reg_ty!(Reg, $dst_reg) => { - panic!("subtraction must be performed between registers of the same size") - } - (_, _) => panic!("subtraction must be performed between registers of the same type"), - } - }; - (mul. $flag:ident $dst_reg:ident[$dst_idx:literal], $reg1:ident[$idx1:literal]) => { - match ($crate::_reg_block!($reg1), $crate::_reg_block!($dst_reg)) { - (RegBlockAFR::A, RegBlockAFR::A) => Instr::Arithmetic(ArithmeticOp::MulA( - $crate::_int_flags!($flag), - $crate::_reg_tya!(Reg, $reg1), - $crate::_reg_idx!($idx1), - $crate::_reg_idx!($dst_idx), - )), - (RegBlockAFR::F, RegBlockAFR::F) => Instr::Arithmetic(ArithmeticOp::MulF( - $crate::_rounding_flag!($flag), - $crate::_reg_tyf!(Reg, $reg1), - $crate::_reg_idx!($idx1), - $crate::_reg_idx!($dst_idx), - )), - (a, b) if a == b => panic!("multiplication requires integer or float registers"), - (_, _) if $crate::_reg_ty!(Reg, $reg1) != $crate::_reg_ty!(Reg, $dst_reg) => { - panic!("multiplication must be performed between registers of the same size") - } - (_, _) => panic!("multiplication must be performed between registers of the same type"), - } - }; - (div. $flag:ident $dst_reg:ident[$dst_idx:literal], $reg1:ident[$idx1:literal]) => { - match ($crate::_reg_block!($reg1), $crate::_reg_block!($dst_reg)) { - (RegBlockAFR::A, RegBlockAFR::A) => Instr::Arithmetic(ArithmeticOp::DivA( - $crate::_int_flags!($flag), - $crate::_reg_tya!(Reg, $reg1), - $crate::_reg_idx!($idx1), - $crate::_reg_idx!($dst_idx), - )), - (RegBlockAFR::F, RegBlockAFR::F) => Instr::Arithmetic(ArithmeticOp::DivF( - $crate::_rounding_flag!($flag), - $crate::_reg_tyf!(Reg, $reg1), - $crate::_reg_idx!($idx1), - $crate::_reg_idx!($dst_idx), - )), - (a, b) if a == b => panic!("division requires integer or float registers"), - (_, _) if $crate::_reg_ty!(Reg, $reg1) != $crate::_reg_ty!(Reg, $dst_reg) => { - panic!("division must be performed between registers of the same size") - } - (_, _) => panic!("division must be performed between registers of the same type"), - } - }; - (rem $dst_reg:ident[$dst_idx:literal], $reg1:ident[$idx1:literal]) => { - if $crate::_reg_block!($reg1) != RegBlockAFR::A - || $crate::_reg_block!($dst_reg) != RegBlockAFR::A - { - panic!("modulo division must be performed only using integer arithmetic registers"); - } else { - Instr::Arithmetic(ArithmeticOp::Rem( - $crate::_reg_tya!(Reg, $reg1), - $crate::_reg_idx!($idx1), - $crate::_reg_tya!(Reg, $dst_reg), - $crate::_reg_idx!($dst_idx), - )) - } - }; - (inc $reg:ident[$idx:literal]) => { - Instr::Arithmetic(ArithmeticOp::Stp( - $crate::_reg_tya!(Reg, $reg), - $crate::_reg_idx!($idx), - Step::with(1), - )) - }; - (add $reg:ident[$idx:literal], $step:literal) => { - Instr::Arithmetic(ArithmeticOp::Stp( - $crate::_reg_tya!(Reg, $reg), - $crate::_reg_idx!($idx), - Step::with($step), - )) - }; - (dec $reg:ident[$idx:literal]) => { - Instr::Arithmetic(ArithmeticOp::Stp( - $crate::_reg_tya!(Reg, $reg), - $crate::_reg_idx!($idx), - Step::with(-1), - )) - }; - (sub $reg:ident[$idx:literal], $step:literal) => { - Instr::Arithmetic(ArithmeticOp::Stp( - $crate::_reg_tya!(Reg, $reg), - $crate::_reg_idx!($idx), - Step::with($step * -1), - )) - }; - (neg $reg:ident[$idx:literal]) => { - Instr::Arithmetic(ArithmeticOp::Neg( - $crate::_reg_ty!(Reg, $reg).into(), - $crate::_reg_idx16!($idx), - )) - }; - (abs $reg:ident[$idx:literal]) => { - Instr::Arithmetic(ArithmeticOp::Abs( - $crate::_reg_ty!(Reg, $reg).into(), - $crate::_reg_idx16!($idx), - )) - }; - - ( - and - $dst_reg:ident[$dst_idx:literal], - $reg1:ident[$idx1:literal], - $reg2:ident[$idx2:literal] - ) => { - if $crate::_reg_ty!(Reg, $reg1) != $crate::_reg_ty!(Reg, $reg2) - || $crate::_reg_ty!(Reg, $reg2) != $crate::_reg_ty!(Reg, $dst_reg) - { - panic!("`and` operation must use the same type of registers for all of its operands"); - } else if $crate::_reg_block!($reg1) != RegBlockAFR::A - && $crate::_reg_block!($reg1) != RegBlockAFR::R - { - panic!("`and` operation requires integer arithmetic or generic registers"); - } else { - Instr::Bitwise(BitwiseOp::And( - $crate::_reg_ty!(Reg, $reg1).into(), - $crate::_reg_idx16!($idx1), - $crate::_reg_idx16!($idx2), - $crate::_reg_idx16!($dst_idx), - )) - } - }; - ( - or $dst_reg:ident[$dst_idx:literal], $reg2:ident[$idx2:literal], $reg1:ident[$idx1:literal] - ) => { - if $crate::_reg_ty!(Reg, $reg1) != $crate::_reg_ty!(Reg, $reg2) - || $crate::_reg_ty!(Reg, $reg2) != $crate::_reg_ty!(Reg, $dst_reg) - { - panic!("`or` operation must use the same type of registers for all of its operands"); - } else if $crate::_reg_block!($reg1) != RegBlockAFR::A - && $crate::_reg_block!($reg1) != RegBlockAFR::R - { - panic!("`or` operation requires integer arithmetic or generic registers"); - } else { - Instr::Bitwise(BitwiseOp::Or( - $crate::_reg_ty!(Reg, $reg1).into(), - $crate::_reg_idx16!($idx1), - $crate::_reg_idx16!($idx2), - $crate::_reg_idx16!($dst_idx), - )) - } - }; - ( - xor - $dst_reg:ident[$dst_idx:literal], - $reg2:ident[$idx2:literal], - $reg1:ident[$idx1:literal] - ) => { - if $crate::_reg_ty!(Reg, $reg1) != $crate::_reg_ty!(Reg, $reg2) - || $crate::_reg_ty!(Reg, $reg2) != $crate::_reg_ty!(Reg, $dst_reg) - { - panic!("`xor` operation must use the same type of registers for all of its operands"); - } else if $crate::_reg_block!($reg1) != RegBlockAFR::A - && $crate::_reg_block!($reg1) != RegBlockAFR::R - { - panic!("`xor` operation requires integer arithmetic or generic registers"); - } else { - Instr::Bitwise(BitwiseOp::Xor( - $crate::_reg_ty!(Reg, $reg1).into(), - $crate::_reg_idx16!($idx1), - $crate::_reg_idx16!($idx2), - $crate::_reg_idx16!($dst_idx), - )) - } - }; - (shl $reg1:ident[$idx1:literal], $reg2:ident[$idx2:literal]) => { - Instr::Bitwise(BitwiseOp::Shl( - $crate::_reg_tya2!(Reg, $reg2), - $crate::_reg_idx!($idx2), - $crate::_reg_ty!(Reg, $reg1).into(), - $crate::_reg_idx!($idx1), - )) - }; - (shr.u $reg1:ident[$idx1:literal], $reg2:ident[$idx2:literal]) => { - Instr::Bitwise(BitwiseOp::ShrA( - SignFlag::Unsigned, - $crate::_reg_tya2!(Reg, $reg2), - $crate::_reg_idx16!($idx2), - $crate::_reg_ty!(Reg, $reg1), - $crate::_reg_idx!($idx1), - )) - }; - (shr.s $reg1:ident[$idx1:literal], $reg2:ident[$idx2:literal]) => { - Instr::Bitwise(BitwiseOp::ShrA( - SignFlag::Signed, - $crate::_reg_tya2!(Reg, $reg2), - $crate::_reg_idx16!($idx2), - $crate::_reg_ty!(Reg, $reg1), - $crate::_reg_idx!($idx1), - )) - }; - (shr $reg1:ident[$idx1:literal], $reg2:ident[$idx2:literal]) => { - Instr::Bitwise(BitwiseOp::ShrR( - $crate::_reg_tya2!(Reg, $reg2), - $crate::_reg_idx!($idx2), - $crate::_reg_ty!(Reg, $reg1), - $crate::_reg_idx!($idx1), - )) - }; - (scl $reg1:ident[$idx1:literal], $reg2:ident[$idx2:literal]) => { - Instr::Bitwise(BitwiseOp::Scl( - $crate::_reg_tya2!(Reg, $reg2), - $crate::_reg_idx!($idx2), - $crate::_reg_ty!(Reg, $reg1).into(), - $crate::_reg_idx!($idx1), - )) - }; - (scr $reg1:ident[$idx1:literal], $reg2:ident[$idx2:literal]) => { - Instr::Bitwise(BitwiseOp::Scr( - $crate::_reg_tya2!(Reg, $reg2), - $crate::_reg_idx!($idx2), - $crate::_reg_ty!(Reg, $reg1).into(), - $crate::_reg_idx!($idx1), - )) - }; - (rev $reg:ident[$idx:literal]) => { - match $crate::_reg_block!($reg) { - RegBlockAFR::A => Instr::Bitwise(BitwiseOp::RevA( - $crate::_reg_tya!(Reg, $reg), - $crate::_reg_idx!($idx), - )), - RegBlockAFR::R => Instr::Bitwise(BitwiseOp::RevR( - $crate::_reg_tyr!(Reg, $reg), - $crate::_reg_idx!($idx), - )), - _ => panic!("Wrong registers for `rev` operation"), - } - }; - - (ripemd r160[$idx2:literal],s16[$idx1:literal]) => { - Instr::Digest(DigestOp::Ripemd(RegS::from($idx1), $crate::_reg_idx16!($idx2))) - }; - (sha2 r256[$idx2:literal],s16[$idx1:literal]) => { - Instr::Digest(DigestOp::Sha256(RegS::from($idx1), $crate::_reg_idx16!($idx2))) - }; - (sha2 r512[$idx2:literal],s16[$idx1:literal]) => { - Instr::Digest(DigestOp::Sha512(RegS::from($idx1), $crate::_reg_idx16!($idx2))) - }; - - (secpgen $reg2:ident[$idx2:literal], $reg1:ident[$idx1:literal]) => { - if $crate::_reg_block!($reg1) != RegBlockAFR::R - || $crate::_reg_block!($reg2) != RegBlockAFR::R - { - panic!("elliptic curve instruction accept only generic registers (R-registers)"); - } else { - Instr::Secp256k1(Secp256k1Op::Gen($crate::_reg_idx!($idx1), $crate::_reg_idx8!($idx2))) - } - }; - ( - secpmul $reg3:ident[$idx3:literal], $reg1:ident[$idx1:literal], $reg2:ident[$idx2:literal] - ) => { - if $crate::_reg_ty!(Reg, $reg2) != $crate::_reg_ty!(Reg, $reg3) { - panic!("ecmul instruction can be used only with registers of the same type"); - } else { - Instr::Secp256k1(Secp256k1Op::Mul( - $crate::_reg_block_ar!($reg1), - $crate::_reg_idx!($idx1), - $crate::_reg_idx!($idx2), - $crate::_reg_idx!($idx3), - )) - } - }; - (secpadd $reg2:ident[$idx2:literal], $reg1:ident[$idx1:literal]) => { - if $crate::_reg_block!($reg1) != RegBlockAFR::R - || $crate::_reg_block!($reg2) != RegBlockAFR::R - { - panic!("elliptic curve instruction accept only generic registers (R-registers)"); - } else { - Instr::Secp256k1(Secp256k1Op::Add($crate::_reg_idx!($idx1), $crate::_reg_idx8!($idx2))) - } - }; - (secpneg $reg2:ident[$idx2:literal], $reg1:ident[$idx1:literal]) => { - if $crate::_reg_block!($reg1) != RegBlockAFR::R - || $crate::_reg_block!($reg2) != RegBlockAFR::R - { - panic!("elliptic curve instruction accept only generic registers (R-registers)"); - } else { - Instr::Secp256k1(Secp256k1Op::Neg($crate::_reg_idx!($idx1), $crate::_reg_idx8!($idx2))) - } - }; -} - -#[doc(hidden)] -#[macro_export] -macro_rules! _reg_block_ar { - (a8) => { - RegBlockAR::A - }; - (a16) => { - RegBlockAR::A - }; - (a32) => { - RegBlockAR::A - }; - (a64) => { - RegBlockAR::A - }; - (a128) => { - RegBlockAR::A - }; - (a256) => { - RegBlockAR::A - }; - (a512) => { - RegBlockAR::A - }; - (a1024) => { - RegBlockAFR::A - }; - - (r128) => { - RegBlockAR::R - }; - (r160) => { - RegBlockAR::R - }; - (r256) => { - RegBlockAR::R - }; - (r512) => { - RegBlockAR::R - }; - (r1024) => { - RegBlockAR::R - }; - (r2048) => { - RegBlockAR::R - }; - (r4096) => { - RegBlockAR::R - }; - (r8192) => { - RegBlockAR::R - }; -} - -#[doc(hidden)] -#[macro_export] -macro_rules! _reg_block { - (a8) => { - RegBlockAFR::A - }; - (a16) => { - RegBlockAFR::A - }; - (a32) => { - RegBlockAFR::A - }; - (a64) => { - RegBlockAFR::A - }; - (a128) => { - RegBlockAFR::A - }; - (a256) => { - RegBlockAFR::A - }; - (a512) => { - RegBlockAFR::A - }; - (a1024) => { - RegBlockAFR::A - }; - - (f16b) => { - RegBlockAFR::F - }; - (f16) => { - RegBlockAFR::F - }; - (f32) => { - RegBlockAFR::F - }; - (f64) => { - RegBlockAFR::F - }; - (f80) => { - RegBlockAFR::F - }; - (f128) => { - RegBlockAFR::F - }; - (f256) => { - RegBlockAFR::F - }; - (f512) => { - RegBlockAFR::F - }; - - (r128) => { - RegBlockAFR::R - }; - (r160) => { - RegBlockAFR::R - }; - (r256) => { - RegBlockAFR::R - }; - (r512) => { - RegBlockAFR::R - }; - (r1024) => { - RegBlockAFR::R - }; - (r2048) => { - RegBlockAFR::R - }; - (r4096) => { - RegBlockAFR::R - }; - (r8192) => { - RegBlockAFR::R - }; -} - -#[doc(hidden)] -#[macro_export] -macro_rules! _reg_sfx { - ($a:ident, $b:ident,a8) => { - $crate::paste! { $a :: [<$b A>] } - }; - ($a:ident, $b:ident,a16) => { - $crate::paste! { $a :: [<$b A>] } - }; - ($a:ident, $b:ident,a32) => { - $crate::paste! { $a :: [<$b A>] } - }; - ($a:ident, $b:ident,a64) => { - $crate::paste! { $a :: [<$b A>] } - }; - ($a:ident, $b:ident,a128) => { - $crate::paste! { $a :: [<$b A>] } - }; - ($a:ident, $b:ident,a256) => { - $crate::paste! { $a :: [<$b A>] } - }; - ($a:ident, $b:ident,a512) => { - $crate::paste! { $a :: [<$b A>] } - }; - ($a:ident, $b:ident,a1024) => { - $crate::paste! { $a :: [<$b A>] } - }; - - ($a:ident, $b:ident,f16b) => { - $crate::paste! { $a :: [<$b F>] } - }; - ($a:ident, $b:ident,f16) => { - $crate::paste! { $a :: [<$b F>] } - }; - ($a:ident, $b:ident,f32) => { - $crate::paste! { $a :: [<$b F>] } - }; - ($a:ident, $b:ident,f64) => { - $crate::paste! { $a :: [<$b F>] } - }; - ($a:ident, $b:ident,f80) => { - $crate::paste! { $a :: [<$b F>] } - }; - ($a:ident, $b:ident,f128) => { - $crate::paste! { $a :: [<$b F>] } - }; - ($a:ident, $b:ident,f256) => { - $crate::paste! { $a :: [<$b F>] } - }; - ($a:ident, $b:ident,f512) => { - $crate::paste! { $a :: [<$b F>] } - }; - - ($a:ident, $b:ident,r128) => { - $crate::paste! { $a :: [<$b R>] } - }; - ($a:ident, $b:ident,r160) => { - $crate::paste! { $a :: [<$b R>] } - }; - ($a:ident, $b:ident,r256) => { - $crate::paste! { $a :: [<$b R>] } - }; - ($a:ident, $b:ident,r512) => { - $crate::paste! { $a :: [<$b R>] } - }; - ($a:ident, $b:ident,r1024) => { - $crate::paste! { $a :: [<$b R>] } - }; - ($a:ident, $b:ident,r2048) => { - $crate::paste! { $a :: [<$b R>] } - }; - ($a:ident, $b:ident,r4096) => { - $crate::paste! { $a :: [<$b R>] } - }; - ($a:ident, $b:ident,r8192) => { - $crate::paste! { $a :: [<$b R>] } - }; -} - -#[doc(hidden)] -#[macro_export] -macro_rules! _reg_ty { - ($ident:ident,a8) => { - $crate::paste! { [<$ident A>] :: A8 } - }; - ($ident:ident,a16) => { - $crate::paste! { [<$ident A>] :: A16 } - }; - ($ident:ident,a32) => { - $crate::paste! { [<$ident A>] :: A32 } - }; - ($ident:ident,a64) => { - $crate::paste! { [<$ident A>] :: A64 } - }; - ($ident:ident,a128) => { - $crate::paste! { [<$ident A>] :: A128 } - }; - ($ident:ident,a256) => { - $crate::paste! { [<$ident A>] :: A256 } - }; - ($ident:ident,a512) => { - $crate::paste! { [<$ident A>] :: A512 } - }; - ($ident:ident,a1024) => { - $crate::paste! { [<$ident A>] :: A1024 } - }; - - ($ident:ident,f16b) => { - $crate::paste! { [<$ident F>] :: F16B } - }; - ($ident:ident,f16) => { - $crate::paste! { [<$ident F>] :: F16 } - }; - ($ident:ident,f32) => { - $crate::paste! { [<$ident F>] :: F32 } - }; - ($ident:ident,f64) => { - $crate::paste! { [<$ident F>] :: F64 } - }; - ($ident:ident,f80) => { - $crate::paste! { [<$ident F>] :: F80 } - }; - ($ident:ident,f128) => { - $crate::paste! { [<$ident F>] :: F128 } - }; - ($ident:ident,f256) => { - $crate::paste! { [<$ident F>] :: F256 } - }; - ($ident:ident,f512) => { - $crate::paste! { [<$ident F>] :: F512 } - }; - - ($ident:ident,r128) => { - $crate::paste! { [<$ident R>] :: R128 } - }; - ($ident:ident,r160) => { - $crate::paste! { [<$ident R>] :: R160 } - }; - ($ident:ident,r256) => { - $crate::paste! { [<$ident R>] :: R256 } - }; - ($ident:ident,r512) => { - $crate::paste! { [<$ident R>] :: R512 } - }; - ($ident:ident,r1024) => { - $crate::paste! { [<$ident R>] :: R1024 } - }; - ($ident:ident,r2048) => { - $crate::paste! { [<$ident R>] :: R2048 } - }; - ($ident:ident,r4096) => { - $crate::paste! { [<$ident R>] :: R4096 } - }; - ($ident:ident,r8192) => { - $crate::paste! { [<$ident R>] :: R8192 } - }; -} - -#[doc(hidden)] -#[macro_export] -macro_rules! _reg_tya2 { - ($ident:ident,a8) => { - $crate::paste! { [<$ident A2>] :: A8 } - }; - ($ident:ident,a16) => { - $crate::paste! { [<$ident A2>] :: A16 } - }; -} - -#[doc(hidden)] -#[macro_export] -macro_rules! _reg_tya { - ($ident:ident,a8) => { - $crate::paste! { [<$ident A>] :: A8 } - }; - ($ident:ident,a16) => { - $crate::paste! { [<$ident A>] :: A16 } - }; - ($ident:ident,a32) => { - $crate::paste! { [<$ident A>] :: A32 } - }; - ($ident:ident,a64) => { - $crate::paste! { [<$ident A>] :: A64 } - }; - ($ident:ident,a128) => { - $crate::paste! { [<$ident A>] :: A128 } - }; - ($ident:ident,a256) => { - $crate::paste! { [<$ident A>] :: A256 } - }; - ($ident:ident,a512) => { - $crate::paste! { [<$ident A>] :: A512 } - }; - ($ident:ident,a1024) => { - $crate::paste! { [<$ident A>] :: A1024 } - }; - ($ident:ident, $other:ident) => { - panic!("operation requires `A` register") - }; -} - -#[doc(hidden)] -#[macro_export] -macro_rules! _reg_tyf { - ($ident:ident,f16b) => { - $crate::paste! { [<$ident F>] :: F16B } - }; - ($ident:ident,f16) => { - $crate::paste! { [<$ident F>] :: F16 } - }; - ($ident:ident,f32) => { - $crate::paste! { [<$ident F>] :: F32 } - }; - ($ident:ident,f64) => { - $crate::paste! { [<$ident F>] :: F64 } - }; - ($ident:ident,f80) => { - $crate::paste! { [<$ident F>] :: F80 } - }; - ($ident:ident,f128) => { - $crate::paste! { [<$ident F>] :: F128 } - }; - ($ident:ident,f256) => { - $crate::paste! { [<$ident F>] :: F256 } - }; - ($ident:ident,f512) => { - $crate::paste! { [<$ident F>] :: F512 } - }; - ($ident:ident, $other:ident) => { - panic!("operation requires `F` register") - }; -} - -#[doc(hidden)] -#[macro_export] -macro_rules! _reg_tyr { - ($ident:ident,r128) => { - $crate::paste! { [<$ident R>] :: R128 } - }; - ($ident:ident,r160) => { - $crate::paste! { [<$ident R>] :: R160 } - }; - ($ident:ident,r256) => { - $crate::paste! { [<$ident R>] :: R256 } - }; - ($ident:ident,r512) => { - $crate::paste! { [<$ident R>] :: R512 } - }; - ($ident:ident,r1024) => { - $crate::paste! { [<$ident R>] :: R1024 } - }; - ($ident:ident,r2048) => { - $crate::paste! { [<$ident R>] :: R2048 } - }; - ($ident:ident,r4096) => { - $crate::paste! { [<$ident R>] :: R4096 } - }; - ($ident:ident,r8192) => { - $crate::paste! { [<$ident R>] :: R8192 } - }; - ($ident:ident, $other:ident) => { - panic!("operation requires `R` register") - }; -} - -#[doc(hidden)] -#[macro_export] -macro_rules! _reg_idx { - ($idx:literal) => { - $crate::paste! { Reg32::[] } - }; -} - -#[doc(hidden)] -#[macro_export] -macro_rules! _reg_idx8 { - ($idx:literal) => { - $crate::paste! { Reg8::[] } - }; -} - -#[doc(hidden)] -#[macro_export] -macro_rules! _reg_idx16 { - ($idx:literal) => { - $crate::paste! { Reg16::[] } - }; -} - -#[doc(hidden)] -#[macro_export] -macro_rules! _merge_flag { - (s) => { - MergeFlag::Set - }; - (a) => { - MergeFlag::Add - }; - (n) => { - MergeFlag::And - }; - (o) => { - MergeFlag::Or - }; -} - -#[doc(hidden)] -#[macro_export] -macro_rules! _rounding_flag { - (z) => { - RoundingFlag::TowardsZero - }; - (n) => { - RoundingFlag::TowardsNearest - }; - (f) => { - RoundingFlag::Floor - }; - (c) => { - RoundingFlag::Ceil - }; - ($other:ident) => { - panic!("wrong float rounding flag") - }; -} - -#[doc(hidden)] -#[macro_export] -macro_rules! _int_flags { - (uc) => { - IntFlags::unsigned_checked() - }; - (cu) => { - IntFlags::unsigned_checked() - }; - (sc) => { - IntFlags::signed_checked() - }; - (cs) => { - IntFlags::signed_checked() - }; - (uw) => { - IntFlags::unsigned_wrapped() - }; - (wu) => { - IntFlags::unsigned_wrapped() - }; - (sw) => { - IntFlags::signed_wrapped() - }; - (ws) => { - IntFlags::signed_wrapped() - }; - ($other:ident) => { - panic!("wrong integer operation flags") - }; -} diff --git a/src/isa/bytecode.rs b/src/isa/bytecode.rs index 2a15f97..d6def7c 100644 --- a/src/isa/bytecode.rs +++ b/src/isa/bytecode.rs @@ -35,7 +35,7 @@ use super::{ }; use crate::data::{ByteStr, MaybeNumber}; use crate::library::{CodeEofError, LibSite, Read, Write, WriteError}; -use crate::reg::{RegAR, RegBlockAR}; +use crate::reg::RegBlockAR; /// Errors encoding instructions #[derive(Clone, Copy, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Display, From)] @@ -1030,17 +1030,26 @@ impl Bytecode for BitwiseOp { } else if instr == INSTR_SHF { let code = reader.read_u1()?; let a2 = reader.read_u1()?.into(); - let shift = reader.read_u4()?; - let sign = reader.read_u1()?; - let block = reader.read_u1()?; - let reg = reader.read_u3()?; - let idx = reader.read_u5()?.into(); - let shift2 = u5::with(shift.to_u8() << 1 | sign.into_u8()).into(); - let regar = RegAR::from(block, reg); - match (code.into_u8(), block.into_u8()) { - (0b0, _) => Self::Shl(a2, shift2, regar, idx), - (0b1, 0b0) => Self::ShrA(sign.into(), a2, shift.into(), reg.into(), idx), - (0b1, 0b1) => Self::ShrR(a2, shift2, reg.into(), idx), + match code.into_u8() { + 0b0 => { + let shift = reader.read_u5()?; + let reg = reader.read_u4()?; + let idx = reader.read_u5()?.into(); + Self::Shl(a2, shift.into(), reg.into(), idx) + } + 0b1 => { + let shift = reader.read_u4()?; + let sign = reader.read_u1()?; + let block = reader.read_u1()?; + let reg = reader.read_u3()?; + let idx = reader.read_u5()?.into(); + let shift2 = u5::with(sign.into_u8() << 4 | shift.to_u8()).into(); + match block.into_u8() { + 0b0 => Self::ShrA(sign.into(), a2, shift.into(), reg.into(), idx), + 0b1 => Self::ShrR(a2, shift2, reg.into(), idx), + _ => unreachable!(), + } + } _ => unreachable!(), } } else { diff --git a/src/isa/exec.rs b/src/isa/exec.rs index 66373f4..4d8bfb7 100644 --- a/src/isa/exec.rs +++ b/src/isa/exec.rs @@ -37,7 +37,7 @@ use super::{ use crate::data::{ByteStr, MaybeNumber, Number, NumberLayout}; use crate::isa::{ExtendFlag, FloatEqFlag, IntFlags, MergeFlag, NoneEqFlag, SignFlag}; use crate::library::{constants, LibSite}; -use crate::reg::{CoreRegs, NumericRegister, Reg32, RegA, RegR}; +use crate::reg::{CoreRegs, NumericRegister, Reg32, RegA, RegA2, RegAR, RegR}; /// Turing machine movement after instruction execution #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] @@ -504,12 +504,21 @@ impl InstructionSet for ArithmeticOp { reg, idx, regs.get(reg, idx).and_then(|val| { - let mut n = Number::from(*step); - debug_assert!( - n.reshape(val.layout()), - "reshape target byte length is always greater" - ); - val.int_add(n, IntFlags { signed: false, wrap: false }) + if step.as_i8() < 0 { + let mut n = Number::from(-step.as_i8()); + debug_assert!( + n.reshape(val.layout()), + "reshape target byte length is always greater" + ); + val.int_sub(n, IntFlags { signed: false, wrap: false }) + } else { + let mut n = Number::from(*step); + debug_assert!( + n.reshape(val.layout()), + "reshape target byte length is always greater" + ); + val.int_add(n, IntFlags { signed: false, wrap: false }) + } }), ), ArithmeticOp::Neg(reg, idx) => { @@ -528,6 +537,36 @@ impl InstructionSet for BitwiseOp { fn isa_ids() -> BTreeSet<&'static str> { BTreeSet::default() } fn exec(&self, regs: &mut CoreRegs, _site: LibSite, _: &()) -> ExecStep { + fn shl(original: &[u8], shift: usize, n_bytes: usize) -> [u8; 1024] { + let mut ret = [0u8; 1024]; + let word_shift = shift / 8; + let bit_shift = shift % 8; + for i in 0..n_bytes { + // Shift + if bit_shift < 8 && i + word_shift < n_bytes { + ret[i + word_shift] += original[i] << bit_shift; + } + // Carry + if bit_shift > 0 && i + word_shift + 1 < n_bytes { + ret[i + word_shift + 1] += original[i] >> (8 - bit_shift); + } + } + ret + } + fn shr(original: &[u8], shift: usize, n_bytes: usize) -> [u8; 1024] { + let mut ret = [0u8; 1024]; + let word_shift = shift / 8; + let bit_shift = shift % 8; + for i in word_shift..n_bytes { + // Shift + ret[i - word_shift] += original[i] >> bit_shift; + // Carry + if bit_shift > 0 && i < n_bytes - 1 { + ret[i - word_shift] += original[i + 1] << (8 - bit_shift); + } + } + ret + } match self { BitwiseOp::And(reg, src1, src2, dst) => { regs.op(reg, src1, reg, src2, reg, dst, BitAnd::bitand) @@ -541,11 +580,29 @@ impl InstructionSet for BitwiseOp { BitwiseOp::Not(reg, idx) => { regs.set(reg, idx, !regs.get(reg, idx)); } - BitwiseOp::Shl(reg1, shift, reg2, srcdst) => { - regs.op(reg2, srcdst, reg1, shift, reg2, srcdst, Shl::shl) - } + BitwiseOp::Shl(reg1, shift, reg2, srcdst) => match reg2 { + RegAR::A(a) => { + let msb = regs.get(a, srcdst).unwrap_or_default()[a.bytes() - 1] & 0x80; + regs.st0 = msb == 0x80; + regs.op(reg2, srcdst, reg1, shift, reg2, srcdst, Shl::shl) + } + RegAR::R(r) => { + let shift = match reg1 { + RegA2::A8 => regs.a8[shift.to_usize()].unwrap_or_default() as usize, + RegA2::A16 => regs.a16[shift.to_usize()].unwrap_or_default() as usize, + }; + if let Some(original) = regs.get_r_mut(*r, srcdst) { + let msb = original.last().copied().unwrap_or_default() & 0x80; + let n_bytes = reg2.bytes() as usize; + original.copy_from_slice(&shl(original, shift, n_bytes)[..n_bytes]); + regs.st0 = msb == 0x80; + } + } + }, BitwiseOp::ShrA(flag, reg1, shift, reg2, srcdst) => { let res = regs.get_both(reg1, shift, reg2, srcdst).map(|(shift, val)| { + let lsb = val[0] & 1; + regs.st0 = lsb == 1; if *flag == SignFlag::Signed { val.into_signed().shr(shift) } else { @@ -555,19 +612,75 @@ impl InstructionSet for BitwiseOp { regs.set(reg2, srcdst, res); } BitwiseOp::ShrR(reg1, shift, reg2, srcdst) => { - regs.op(reg2, srcdst, reg1, shift, reg2, srcdst, Shr::shr) - } - BitwiseOp::Scl(reg1, shift, reg2, srcdst) => { - regs.op(reg2, srcdst, reg1, shift, reg2, srcdst, Number::scl) - } - BitwiseOp::Scr(reg1, shift, reg2, srcdst) => { - regs.op(reg2, srcdst, reg1, shift, reg2, srcdst, Number::scr) + let shift = match reg1 { + RegA2::A8 => regs.a8[shift.to_usize()].unwrap_or_default() as usize, + RegA2::A16 => regs.a16[shift.to_usize()].unwrap_or_default() as usize, + }; + if let Some(original) = regs.get_r_mut(*reg2, srcdst) { + let lsb = original[0] & 1; + let n_bytes = reg2.bytes() as usize; + original.copy_from_slice(&shr(original, shift, n_bytes)[..n_bytes]); + regs.st0 = lsb == 1; + } } + BitwiseOp::Scl(reg1, shift, reg2, srcdst) => match reg2 { + RegAR::A(_) => { + let msb = regs.get(reg2, srcdst).unwrap_or_default()[reg2.bytes() - 1] & 0x80; + regs.st0 = msb == 0x80; + regs.op(reg2, srcdst, reg1, shift, reg2, srcdst, Number::scl) + } + RegAR::R(r) => { + let shift = match reg1 { + RegA2::A8 => regs.a8[shift.to_usize()].unwrap_or_default() as usize, + RegA2::A16 => regs.a16[shift.to_usize()].unwrap_or_default() as usize, + }; + let shift = shift % reg2.bits() as usize; + if let Some(original) = regs.get_r_mut(*r, srcdst) { + let msb = original.last().copied().unwrap_or_default() & 0x80; + let n_bytes = reg2.bytes() as usize; + let mut shl = shl(original, shift, n_bytes); + let shr = shr(original, reg2.bits() as usize - shift, n_bytes); + for i in 0..n_bytes { + shl[i] |= shr[i]; + } + original.copy_from_slice(&shl[..n_bytes]); + regs.st0 = msb == 0x80; + } + } + }, + BitwiseOp::Scr(reg1, shift, reg2, srcdst) => match reg2 { + RegAR::A(_) => { + let lsb = regs.get(reg2, srcdst).unwrap_or_default()[0] & 1; + regs.st0 = lsb == 1; + regs.op(reg2, srcdst, reg1, shift, reg2, srcdst, Number::scr) + } + RegAR::R(r) => { + let shift = match reg1 { + RegA2::A8 => regs.a8[shift.to_usize()].unwrap_or_default() as usize, + RegA2::A16 => regs.a16[shift.to_usize()].unwrap_or_default() as usize, + }; + let shift = shift % reg2.bits() as usize; + if let Some(original) = regs.get_r_mut(*r, srcdst) { + let lsb = original[0] & 1; + let n_bytes = reg2.bytes() as usize; + let mut shr = shr(original, shift, n_bytes); + let shl = shl(original, reg2.bits() as usize - shift, n_bytes); + for i in 0..n_bytes { + shr[i] |= shl[i]; + } + original.copy_from_slice(&shr[..n_bytes]); + regs.st0 = lsb == 1; + } + } + }, BitwiseOp::RevA(reg, idx) => { regs.set(reg, idx, regs.get(reg, idx).map(Number::reverse_bits)); } BitwiseOp::RevR(reg, idx) => { - regs.set(reg, idx, regs.get(reg, idx).map(Number::reverse_bits)); + if let Some(original) = regs.get_r_mut(*reg, idx) { + original.reverse(); + original.iter_mut().for_each(|byte| *byte = byte.reverse_bits()); + } } } ExecStep::Next @@ -669,17 +782,8 @@ impl InstructionSet for BytesOp { let (s1, s2) = regs.get_both_s(*reg1, *reg2)?; let r1 = s1.as_ref(); let r2 = s2.as_ref(); - let len = r2.len(); - let mut count = 0usize; - for i in 0..r1.len() { - if r1[i..len] == r2[..len] { - count += 1; - } - } - if count > u16::MAX as usize { - regs.st0 = false; - count -= 1; - } + let count = r1.windows(r2.len()).filter(|r1| *r1 == r2).count(); + assert!(count <= u16::MAX as usize); regs.set(RegA::A16, Reg32::Reg0, count as u16); Some(()) }; @@ -698,7 +802,7 @@ impl InstructionSet for BytesOp { }; f().unwrap_or_else(|| { regs.st0 = false; - regs.set(RegA::A16, Reg32::Reg0, MaybeNumber::none()); + regs.s16[reg2.as_usize()] = None; }) } BytesOp::Con(reg1, reg2, n, offset_dst, len_dst) => { @@ -822,8 +926,12 @@ impl InstructionSet for DigestOp { DigestOp::Ripemd(src, dst) => { let s = regs.get_s(*src); none = s.is_none(); - let hash: Option<[u8; 20]> = - s.map(|s| ripemd::Ripemd160::digest(s.as_ref()).into()); + let hash = s.map(|s| { + let mut hash: [u8; 20] = ripemd::Ripemd160::digest(s.as_ref()).into(); + // RIPEMD-160 is big-endian + hash.reverse(); + hash + }); regs.set(RegR::R160, dst, hash); } DigestOp::Sha256(src, dst) => { diff --git a/src/isa/instr.rs b/src/isa/instr.rs index af15117..ba023b7 100644 --- a/src/isa/instr.rs +++ b/src/isa/instr.rs @@ -170,7 +170,7 @@ pub enum PutOp { /// this instruction is assembled and the data are not present in the data segment (their /// offset + length exceeds data segment size) the operation will set destination register /// into undefined state and `st0` to `false`. Otherwise, `st0` value is not affected. - #[display("put {2},{0}{1}")] + #[display("put {0}{1},{2}")] PutA(RegA, Reg32, Box), /// Unconditionally assigns a value to `F` register @@ -179,7 +179,7 @@ pub enum PutOp { /// this instruction is assembled and the data are not present in the data segment (their /// offset + length exceeds data segment size) the operation will set destination register /// into undefined state and `st0` to `false`. Otherwise, `st0` value is not affected. - #[display("put {2},{0}{1}")] + #[display("put {0}{1},{2}")] PutF(RegF, Reg32, Box), /// Unconditionally assigns a value to `R` register @@ -188,7 +188,7 @@ pub enum PutOp { /// this instruction is assembled and the data are not present in the data segment (their /// offset + length exceeds data segment size) the operation will set destination register /// into undefined state and `st0` to `false`. Otherwise, `st0` value is not affected. - #[display("put {2},{0}{1}")] + #[display("put {0}{1},{2}")] PutR(RegR, Reg32, Box), /// Conditionally assigns a value to `A` register if the register is in uninitialized state. @@ -199,7 +199,7 @@ pub enum PutOp { /// offset + length exceeds data segment size) _and_ the destination register is /// initialized, the operation will set destination register into undefined state and `st0` /// to `false`. Otherwise, `st0` value is changed according to the general operation rules. - #[display("putif {2},{0}{1}")] + #[display("putif {0}{1},{2}")] PutIfA(RegA, Reg32, Box), /// Conditionally assigns a value to `R` register if the register is in uninitialized state. @@ -210,7 +210,7 @@ pub enum PutOp { /// offset + length exceeds data segment size) _and_ the destination register is /// initialized, the operation will set destination register into undefined state and `st0` /// to `false`. Otherwise, `st0` value is changed according to the general operation rules. - #[display("putif {2},{0}{1}")] + #[display("putif {0}{1},{2}")] PutIfR(RegR, Reg32, Box), } @@ -470,7 +470,7 @@ pub enum ArithmeticOp { /// Increment/decrement register value on a given signed step. /// /// Sets the destination to `None` and `st0` to `false` in case of overflow. - #[display("{2:#} {2}{0}{1}")] + #[display("{2:#} {0}{1},{2}")] Stp(RegA, Reg32, Step), /// Negates most significant bit @@ -650,11 +650,8 @@ pub enum BytesOp { /// Count number of byte occurrences from the `a8` register within the string and stores that /// value into destination `a16` register. /// - /// If the string register is empty, or destination register can't fit the length, sets `st0` - /// to `false` and destination register to `None`. - /// - /// If the source byte value register is uninitialized, sets destination register to `None` and - /// `st0` to `false`. + /// If the string register is empty, or the source byte value register is uninitialized, sets + /// `st0` to `false` and destination register to `None`. #[display("cnt {0},a8{1},a16{2}")] Cnt( /** `s` register index */ RegS, @@ -669,24 +666,21 @@ pub enum BytesOp { Eq(RegS, RegS), /// Compute offset and length of the `n`th fragment shared between two strings ("conjoint - /// fragment"), putting it to the destination `u16` registers. If strings have no conjoint + /// fragment"), putting it to the destination `a16` registers. If strings have no conjoint /// fragment sets destination to `None`. - #[display("con {0},{1},a16{2},u16{3},u16{4}")] + #[display("con {0},{1},a16{2},a16{3},a16{4}")] Con( /** First source string register */ RegS, /** Second source string register */ RegS, /** Index of the conjoint fragment to match */ Reg32, - /** `u16` register index to save the offset of the conjoint fragment */ Reg32, - /** `u16` register index to save the length of the conjoint fragment */ Reg32, + /** `a16` register index to save the offset of the conjoint fragment */ Reg32, + /** `a16` register index to save the length of the conjoint fragment */ Reg32, ), - /// Count number of occurrences of one string within another putting result to `a16[1]`, - /// - /// If the first or the second string is `None`, sets `st0` to `false` and `a16[1]` to `None`. + /// Count number of occurrences of one string within another putting result to `a16[0]`, /// - /// If the number of occurrences is `u16::MAX + 1`, sets `a16[1]` to `u16::MAX` and `st0` to - /// `false`. - #[display("find {0},{1},a16[1]")] + /// If the first or the second string is `None`, sets `st0` to `false` and `a16[0]` to `None`. + #[display("find a16[0],{0},{1}")] Find(/** `s` register with string */ RegS, /** `s` register with matching fragment */ RegS), /// Extract byte string slice into general `r` register. The length of the extracted string is @@ -737,7 +731,7 @@ pub enum BytesOp { /// (2) first <- None, second <- `src_len > 0` ? src : None; `st0` <- false /// (3) first <- None, second <- `src_len > 0` ? src : zero-len; `st0` <- false /// (4) first <- zero-len, second <- `src_len > 0` ? src : zero-len - /// `offset > 0 && offset < src_len`: `st0` always set to false + /// `offset > 0 && offset > src_len`: `st0` always set to false /// (1) first, second <- None /// (5) first <- short, second <- None /// (6) first <- short, second <- zero-len @@ -747,7 +741,7 @@ pub enum BytesOp { /// (1) first, second <- None; `st0` <- false /// (5,7) first <- ok, second <- None; `st0` <- false /// (6,8) first <- ok, second <- zero-len - /// `offset > src_len`: operation succeeds anyway, `st0` value is not changed + /// `offset < src_len`: operation succeeds anyway, `st0` value is not changed /// /// /// Rule on `st0` changes: if at least one of the destination registers is set to `None`, or diff --git a/src/isa/mod.rs b/src/isa/mod.rs index 6c728fb..fe9be49 100644 --- a/src/isa/mod.rs +++ b/src/isa/mod.rs @@ -23,8 +23,6 @@ //! AluVM instruction set architecture -#[macro_use] -mod asm; mod bytecode; mod exec; mod flags; diff --git a/src/library/cursor.rs b/src/library/cursor.rs index b891f38..8f63fa9 100644 --- a/src/library/cursor.rs +++ b/src/library/cursor.rs @@ -126,21 +126,32 @@ where #[inline] fn as_ref(&self) -> &[u8] { self.bytecode.as_ref() } - fn extract(&mut self, bit_count: u3) -> Result { - if self.is_eof() { - return Err(CodeEofError); - } - let byte = self.as_ref()[self.byte_pos as usize]; - let mut mask = 0x00u8; + fn read(&mut self, bit_count: u5) -> Result { + let mut ret = 0u32; let mut cnt = bit_count.to_u8(); while cnt > 0 { - mask <<= 1; - mask |= 0x01; - cnt -= 1; + if self.is_eof() { + return Err(CodeEofError); + } + let byte = self.as_ref()[self.byte_pos as usize]; + let remaining_bits = 8 - self.bit_pos.to_u8(); + let mask = match remaining_bits < cnt { + true => 0xFFu8 << self.bit_pos.to_u8(), + false => (((1u16 << (cnt)) - 1) << (self.bit_pos.to_u8() as u16)) as u8, + }; + let value = ((byte & mask) >> self.bit_pos.to_u8()) as u32; + ret |= (value << (bit_count.to_u8() - cnt)) as u32; + match remaining_bits.min(cnt) { + 8 => { + self.inc_bytes(1)?; + } + _ => { + self.inc_bits(u3::with(remaining_bits.min(cnt)))?; + } + } + cnt = cnt.saturating_sub(remaining_bits); } - mask <<= self.bit_pos.to_u8(); - let val = (byte & mask) >> self.bit_pos.to_u8(); - self.inc_bits(bit_count).map(|_| val) + Ok(ret) } fn inc_bits(&mut self, bit_count: u3) -> Result<(), CodeEofError> { @@ -172,6 +183,31 @@ where Self: 'a, { fn as_mut(&mut self) -> &mut [u8] { self.bytecode.as_mut() } + + fn write(&mut self, value: u32, bit_count: u5) -> Result<(), CodeEofError> { + let mut cnt = bit_count.to_u8(); + let value = ((value as u64) << (self.bit_pos.to_u8())).to_le_bytes(); + let n_bytes = (cnt + self.bit_pos.to_u8() + 7) / 8; + for i in 0..n_bytes { + if self.is_eof() { + return Err(CodeEofError); + } + let byte_pos = self.byte_pos as usize; + let bit_pos = self.bit_pos.to_u8(); + let byte = &mut self.as_mut()[byte_pos]; + *byte |= value[i as usize]; + match (bit_pos, cnt) { + (0, cnt) if cnt >= 8 => { + self.inc_bytes(1)?; + } + (_, cnt) => { + self.inc_bits(u3::with(cnt.min(8 - bit_pos)))?; + } + } + cnt = cnt.saturating_sub(cnt.min(8 - bit_pos)); + } + Ok(()) + } } impl<'a, T, D> Cursor<'a, T, D> @@ -184,7 +220,11 @@ where // We write the value only if the value is not yet present in the data segment let len = bytes.len(); let offset = self.data.as_ref().len(); - if let Some(offset) = self.data.as_ref().windows(len).position(|window| window == bytes) { + if len == 0 { + Ok(offset as u16) + } else if let Some(offset) = + self.data.as_ref().windows(len).position(|window| window == bytes) + { Ok(offset as u16) } else if offset + len > DATA_SEGMENT_MAX_LEN { Err(WriteError::DataNotFittingSegment) @@ -224,89 +264,66 @@ where Ok(self.as_ref()[self.byte_pos as usize]) } - fn read_bool(&mut self) -> Result { - if self.is_eof() { - return Err(CodeEofError); - } - let byte = self.extract(u3::with(1))?; - Ok(byte == 0x01) - } + fn read_bool(&mut self) -> Result { Ok(self.read(u5::with(1))? == 0x01) } fn read_u1(&mut self) -> Result { - Ok(self.extract(u3::with(1))?.try_into().expect("bit extractor failure")) + let res = self.read(u5::with(1))? as u8; + Ok(res.try_into().expect("bit extractor failure")) } fn read_u2(&mut self) -> Result { - Ok(self.extract(u3::with(2))?.try_into().expect("bit extractor failure")) + let res = self.read(u5::with(2))? as u8; + Ok(res.try_into().expect("bit extractor failure")) } fn read_u3(&mut self) -> Result { - Ok(self.extract(u3::with(3))?.try_into().expect("bit extractor failure")) + let res = self.read(u5::with(3))? as u8; + Ok(res.try_into().expect("bit extractor failure")) } fn read_u4(&mut self) -> Result { - Ok(self.extract(u3::with(4))?.try_into().expect("bit extractor failure")) + let res = self.read(u5::with(4))? as u8; + Ok(res.try_into().expect("bit extractor failure")) } fn read_u5(&mut self) -> Result { - Ok(self.extract(u3::with(5))?.try_into().expect("bit extractor failure")) + let res = self.read(u5::with(5))? as u8; + Ok(res.try_into().expect("bit extractor failure")) } fn read_u6(&mut self) -> Result { - Ok(self.extract(u3::with(6))?.try_into().expect("bit extractor failure")) + let res = self.read(u5::with(6))? as u8; + Ok(res.try_into().expect("bit extractor failure")) } fn read_u7(&mut self) -> Result { - Ok(self.extract(u3::with(7))?.try_into().expect("bit extractor failure")) + let res = self.read(u5::with(7))? as u8; + Ok(res.try_into().expect("bit extractor failure")) } fn read_u8(&mut self) -> Result { - if self.is_eof() { - return Err(CodeEofError); - } - let byte = self.as_ref()[self.byte_pos as usize]; - self.inc_bytes(1).map(|_| byte) + let res = self.read(u5::with(8))? as u8; + Ok(res.try_into().expect("bit extractor failure")) } fn read_i8(&mut self) -> Result { - if self.is_eof() { - return Err(CodeEofError); - } - let byte = self.as_ref()[self.byte_pos as usize] as i8; - self.inc_bytes(1).map(|_| byte) + let res = self.read(u5::with(8))? as i8; + Ok(res.try_into().expect("bit extractor failure")) } fn read_u16(&mut self) -> Result { - if self.is_eof() { - return Err(CodeEofError); - } - let pos = self.byte_pos as usize; - let mut buf = [0u8; 2]; - buf.copy_from_slice(&self.as_ref()[pos..pos + 2]); - let word = u16::from_le_bytes(buf); - self.inc_bytes(2).map(|_| word) + let res = self.read(u5::with(16))? as u16; + Ok(res.try_into().expect("bit extractor failure")) } fn read_i16(&mut self) -> Result { - if self.is_eof() { - return Err(CodeEofError); - } - let pos = self.byte_pos as usize; - let mut buf = [0u8; 2]; - buf.copy_from_slice(&self.as_ref()[pos..pos + 2]); - let word = i16::from_le_bytes(buf); - self.inc_bytes(2).map(|_| word) + let res = self.read(u5::with(16))? as i16; + Ok(res.try_into().expect("bit extractor failure")) } fn read_u24(&mut self) -> Result { - if self.is_eof() { - return Err(CodeEofError); - } - let pos = self.byte_pos as usize; - let mut buf = [0u8; 3]; - buf.copy_from_slice(&self.as_ref()[pos..pos + 3]); - let word = u24::from_le_bytes(buf); - self.inc_bytes(3).map(|_| word) + let res = self.read(u5::with(24))?; + Ok(res.try_into().expect("bit extractor failure")) } #[inline] @@ -341,97 +358,55 @@ where Self: 'a, { fn write_bool(&mut self, data: bool) -> Result<(), WriteError> { - let data = u8::from(data) << self.bit_pos.to_u8(); - let pos = self.byte_pos as usize; - self.as_mut()[pos] |= data; - self.inc_bits(u3::with(1)).map_err(WriteError::from) + self.write(data as u32, u5::with(1)).map_err(WriteError::from) } fn write_u1(&mut self, data: impl Into) -> Result<(), WriteError> { - let data = data.into().into_u8() << self.bit_pos.to_u8(); - let pos = self.byte_pos as usize; - self.as_mut()[pos] |= data; - self.inc_bits(u3::with(1)).map_err(WriteError::from) + self.write(data.into().into_u8() as u32, u5::with(1)).map_err(WriteError::from) } fn write_u2(&mut self, data: impl Into) -> Result<(), WriteError> { - let data = data.into().to_u8() << self.bit_pos.to_u8(); - let pos = self.byte_pos as usize; - self.as_mut()[pos] |= data; - self.inc_bits(u3::with(2)).map_err(WriteError::from) + self.write(data.into().to_u8() as u32, u5::with(2)).map_err(WriteError::from) } fn write_u3(&mut self, data: impl Into) -> Result<(), WriteError> { - let data = data.into().to_u8() << self.bit_pos.to_u8(); - let pos = self.byte_pos as usize; - self.as_mut()[pos] |= data; - self.inc_bits(u3::with(3)).map_err(WriteError::from) + self.write(data.into().to_u8() as u32, u5::with(3)).map_err(WriteError::from) } fn write_u4(&mut self, data: impl Into) -> Result<(), WriteError> { - let data = data.into().to_u8() << self.bit_pos.to_u8(); - let pos = self.byte_pos as usize; - self.as_mut()[pos] |= data; - self.inc_bits(u3::with(4)).map_err(WriteError::from) + self.write(data.into().to_u8() as u32, u5::with(4)).map_err(WriteError::from) } fn write_u5(&mut self, data: impl Into) -> Result<(), WriteError> { - let data = data.into().to_u8() << self.bit_pos.to_u8(); - let pos = self.byte_pos as usize; - self.as_mut()[pos] |= data; - self.inc_bits(u3::with(5)).map_err(WriteError::from) + self.write(data.into().to_u8() as u32, u5::with(5)).map_err(WriteError::from) } fn write_u6(&mut self, data: impl Into) -> Result<(), WriteError> { - let data = data.into().to_u8() << self.bit_pos.to_u8(); - let pos = self.byte_pos as usize; - self.as_mut()[pos] |= data; - self.inc_bits(u3::with(6)).map_err(WriteError::from) + self.write(data.into().to_u8() as u32, u5::with(6)).map_err(WriteError::from) } fn write_u7(&mut self, data: impl Into) -> Result<(), WriteError> { - let data = data.into().to_u8() << self.bit_pos.to_u8(); - let pos = self.byte_pos as usize; - self.as_mut()[pos] |= data; - self.inc_bits(u3::with(7)).map_err(WriteError::from) + self.write(data.into().to_u8() as u32, u5::with(7)).map_err(WriteError::from) } fn write_u8(&mut self, data: impl Into) -> Result<(), WriteError> { - let pos = self.byte_pos as usize; - self.as_mut()[pos] = data.into(); - self.inc_bytes(1).map_err(WriteError::from) + self.write(data.into() as u32, u5::with(8)).map_err(WriteError::from) } fn write_i8(&mut self, data: impl Into) -> Result<(), WriteError> { - let data = data.into().to_le_bytes(); - let pos = self.byte_pos as usize; - self.as_mut()[pos] = data[0]; - self.inc_bytes(1).map_err(WriteError::from) + self.write(data.into() as u32, u5::with(8)).map_err(WriteError::from) } fn write_u16(&mut self, data: impl Into) -> Result<(), WriteError> { - let data = data.into().to_le_bytes(); - let pos = self.byte_pos as usize; - self.as_mut()[pos] = data[0]; - self.as_mut()[pos + 1] = data[1]; - self.inc_bytes(2).map_err(WriteError::from) + self.write(data.into() as u32, u5::with(16)).map_err(WriteError::from) } fn write_i16(&mut self, data: impl Into) -> Result<(), WriteError> { - let data = data.into().to_le_bytes(); - let pos = self.byte_pos as usize; - self.as_mut()[pos] = data[0]; - self.as_mut()[pos + 1] = data[1]; - self.inc_bytes(2).map_err(WriteError::from) + self.write(data.into() as u32, u5::with(16)).map_err(WriteError::from) } fn write_u24(&mut self, data: impl Into) -> Result<(), WriteError> { - let data = data.into().to_le_bytes(); - let pos = self.byte_pos as usize; - self.as_mut()[pos] = data[0]; - self.as_mut()[pos + 1] = data[1]; - self.as_mut()[pos + 2] = data[2]; - self.inc_bytes(3).map_err(WriteError::from) + self.write(data.into().into_u32(), u5::with(24)).map_err(WriteError::from) } #[inline] @@ -483,3 +458,79 @@ where Ok(()) } } + +#[cfg(test)] +mod tests { + use amplify::num::{u2, u3, u5, u7}; + + use super::Cursor; + use crate::data::ByteStr; + use crate::library::{LibSeg, Read, Write}; + + #[test] + fn read() { + let libseg = LibSeg::default(); + let mut cursor = Cursor::<_, ByteStr>::new([0b01010111, 0b00001001], &libseg); + assert_eq!(cursor.read_u2().unwrap().to_u8(), 0b00000011); + assert_eq!(cursor.read_u2().unwrap().to_u8(), 0b00000001); + assert_eq!(cursor.read_u8().unwrap(), 0b10010101); + + let mut cursor = Cursor::<_, ByteStr>::new([0b01010111, 0b00001001], &libseg); + assert_eq!(cursor.read_u2().unwrap().to_u8(), 0b00000011); + assert_eq!(cursor.read_u3().unwrap().to_u8(), 0b00000101); + assert_eq!(cursor.read_u8().unwrap(), 0b01001010); + + let mut cursor = Cursor::<_, ByteStr>::new([0b01110111, 0b00001111], &libseg); + assert_eq!(cursor.read_u8().unwrap(), 0b01110111); + assert_eq!(cursor.read_u3().unwrap().to_u8(), 0b00000111); + assert_eq!(cursor.read_u5().unwrap().to_u8(), 0b00000001); + + let bytes = 0b11101011_11110000_01110111; + let mut cursor = Cursor::<_, ByteStr>::new(u32::to_le_bytes(bytes), &libseg); + assert_eq!(cursor.read(u5::with(24)).unwrap(), bytes); + } + + #[test] + fn read_eof() { + let libseg = LibSeg::default(); + let mut cursor = Cursor::<_, ByteStr>::new([0b01010111], &libseg); + assert_eq!(cursor.read_u2().unwrap().to_u8(), 0b00000011); + assert_eq!(cursor.read_u2().unwrap().to_u8(), 0b00000001); + assert!(cursor.read_u8().is_err()); + } + + #[test] + fn write() { + let libseg = LibSeg::default(); + let mut code = [0, 0, 0, 0, 0, 0]; + let mut cursor = Cursor::<_, ByteStr>::new(&mut code, &libseg); + cursor.write_u2(u2::with(0b00000011)).unwrap(); + cursor.write_u3(u3::with(0b00000101)).unwrap(); + cursor.write_u7(u7::with(0b01011111)).unwrap(); + cursor.write_u8(0b11100111).unwrap(); + cursor.write_bool(true).unwrap(); + cursor.write_u3(u3::with(0b00000110)).unwrap(); + let two_bytes = 0b11110000_10101010u16; + cursor.write_u16(two_bytes).unwrap(); + + let mut cursor = Cursor::<_, ByteStr>::new(code, &libseg); + assert_eq!(cursor.read_u2().unwrap().to_u8(), 0b00000011); + assert_eq!(cursor.read_u3().unwrap().to_u8(), 0b00000101); + assert_eq!(cursor.read_u7().unwrap().to_u8(), 0b01011111); + assert_eq!(cursor.read_u8().unwrap(), 0b11100111); + assert_eq!(cursor.read_bool().unwrap(), true); + assert_eq!(cursor.read_u3().unwrap().to_u8(), 0b00000110); + assert_eq!(cursor.read_u16().unwrap(), two_bytes); + } + + #[test] + fn write_eof() { + let libseg = LibSeg::default(); + let mut code = [0, 0]; + let mut cursor = Cursor::<_, ByteStr>::new(&mut code, &libseg); + cursor.write_u2(u2::with(0b00000011)).unwrap(); + cursor.write_u3(u3::with(0b00000101)).unwrap(); + cursor.write_u7(u7::with(0b01011111)).unwrap(); + assert!(cursor.write_u8(0b11100111).is_err()); + } +} diff --git a/src/reg/core_regs.rs b/src/reg/core_regs.rs index f3a46be..fbfadf0 100644 --- a/src/reg/core_regs.rs +++ b/src/reg/core_regs.rs @@ -26,7 +26,8 @@ use alloc::string::ToString; use alloc::vec::Vec; use core::fmt::{self, Debug, Formatter}; -use amplify::num::apfloat::ieee; +use amplify::hex::ToHex; +use amplify::num::apfloat::{ieee, Float}; use amplify::num::{u1024, u256, u512}; use half::bf16; @@ -253,6 +254,25 @@ impl CoreRegs { } } + /// Retrieves mutable reference to R-register value + pub fn get_r_mut( + &mut self, + reg: impl Into, + index: impl Into, + ) -> Option<&mut [u8]> { + let index = index.into().to_usize(); + match reg.into() { + RegR::R128 => self.r128[index].as_mut().map(|x| x.as_mut_slice()), + RegR::R160 => self.r160[index].as_mut().map(|x| x.as_mut_slice()), + RegR::R256 => self.r256[index].as_mut().map(|x| x.as_mut_slice()), + RegR::R512 => self.r512[index].as_mut().map(|x| x.as_mut_slice()), + RegR::R1024 => self.r1024[index].as_mut().map(|x| x.as_mut_slice()), + RegR::R2048 => self.r2048[index].as_mut().map(|x| x.as_mut_slice()), + RegR::R4096 => self.r4096[index].as_mut().map(|x| x.as_mut_slice()), + RegR::R8192 => self.r8192[index].as_mut().map(|x| x.as_mut_slice()), + } + } + /// Returns value from one of `S`-registers #[inline] pub fn get_s(&self, index: impl Into) -> Option<&ByteStr> { @@ -579,7 +599,18 @@ impl Debug for CoreRegs { } for i in 0..32 { if let Some(v) = self.f80[i] { - write!(f, "{}f80{}[{}{:02}{}]={}{}{}\n\t\t", reg, eq, reset, i, eq, val, v, reset)?; + write!( + f, + "{}f80{}[{}{:02}{}]={}{}{}\n\t\t", + reg, + eq, + reset, + i, + eq, + val, + v.to_bits(), + reset + )?; } } for i in 0..32 { @@ -587,7 +618,14 @@ impl Debug for CoreRegs { write!( f, "{}f128{}[{}{:02}{}]={}{}{}\n\t\t", - reg, eq, reset, i, eq, val, v, reset + reg, + eq, + reset, + i, + eq, + val, + v.to_bits(), + reset )?; } } @@ -596,7 +634,14 @@ impl Debug for CoreRegs { write!( f, "{}f256{}[{}{:02}{}]={}{}{}\n\t\t", - reg, eq, reset, i, eq, val, v, reset + reg, + eq, + reset, + i, + eq, + val, + v.to_bits(), + reset )?; } } @@ -605,7 +650,7 @@ impl Debug for CoreRegs { let v = Number::from(v); write!( f, - "{}f512{}[{}{:02}{}]={}{}{}\n\t\t", + "{}f512{}[{}{:02}{}]={}{:x}{}\n\t\t", reg, eq, reset, i, eq, val, v, reset )?; } @@ -653,42 +698,66 @@ impl Debug for CoreRegs { } } for i in 0..32 { - if let Some(v) = self.r1024[i] { - let v = Number::from(v); + if let Some(v) = &self.r1024[i] { write!( f, - "{}r1024{}[{}{:02}{}]={}{:X}{}h\n\t\t", - reg, eq, reset, i, eq, val, v, reset + "{}r1024{}[{}{:02}{}]={}{}{}h\n\t\t", + reg, + eq, + reset, + i, + eq, + val, + v.to_hex().to_uppercase(), + reset )?; } } for i in 0..32 { - if let Some(v) = self.r2048[i] { - let v = Number::from(v); + if let Some(v) = &self.r2048[i] { write!( f, - "{}r2048{}[{}{:02}{}]={}{:X}{}h\n\t\t", - reg, eq, reset, i, eq, val, v, reset + "{}r2048{}[{}{:02}{}]={}{}{}h\n\t\t", + reg, + eq, + reset, + i, + eq, + val, + v.to_hex().to_uppercase(), + reset )?; } } for i in 0..32 { - if let Some(v) = self.r4096[i] { - let v = Number::from(v); + if let Some(v) = &self.r4096[i] { write!( f, - "{}r4096{}[{}{:02}{}]={}{:X}{}h\n\t\t", - reg, eq, reset, i, eq, val, v, reset + "{}r4096{}[{}{:02}{}]={}{}{}h\n\t\t", + reg, + eq, + reset, + i, + eq, + val, + v.to_hex().to_uppercase(), + reset )?; } } for i in 0..32 { - if let Some(v) = self.r8192[i] { - let v = Number::from(v); + if let Some(v) = &self.r8192[i] { write!( f, - "{}r8192{}[{}{:02}{}]={}{:X}{}h\n\t\t", - reg, eq, reset, i, eq, val, v, reset + "{}r8192{}[{}{:02}{}]={}{}{}h\n\t\t", + reg, + eq, + reset, + i, + eq, + val, + v.to_hex().to_uppercase(), + reset )?; } } @@ -702,3 +771,40 @@ impl Debug for CoreRegs { Ok(()) } } + +#[cfg(test)] +mod test { + use amplify::num::u4; + + use super::*; + + // Checks that we do not overflow the stack if using all registers + #[test] + fn init_all() { + let mut regs = CoreRegs::new(); + + for reg in RegA::ALL { + for idx in Reg32::ALL { + regs.set(reg, idx, u8::from(idx)); + } + } + + for reg in RegF::ALL { + for idx in Reg32::ALL { + regs.set(reg, idx, u8::from(idx)); + } + } + + for reg in RegR::ALL { + for idx in Reg32::ALL { + regs.set(reg, idx, u8::from(idx)); + } + } + + for idx in 0u8..16 { + regs.set_s(u4::with(idx), Some(ByteStr::with(format!("string index {idx}")))); + } + + eprintln!("{regs:#?}"); + } +} diff --git a/src/reg/families.rs b/src/reg/families.rs index 778f476..71475f5 100644 --- a/src/reg/families.rs +++ b/src/reg/families.rs @@ -105,6 +105,18 @@ impl NumericRegister for RegA { } impl RegA { + /// Set of all A registers + pub const ALL: [RegA; 8] = [ + RegA::A8, + RegA::A16, + RegA::A32, + RegA::A64, + RegA::A128, + RegA::A256, + RegA::A512, + RegA::A1024, + ]; + /// Constructs [`RegA`] object for a provided requirement for register bit size pub fn with(bits: u16) -> Option { Some(match bits { @@ -328,6 +340,18 @@ impl NumericRegister for RegF { } impl RegF { + /// Set of all F registers + pub const ALL: [RegF; 8] = [ + RegF::F16B, + RegF::F16, + RegF::F32, + RegF::F64, + RegF::F80, + RegF::F128, + RegF::F256, + RegF::F512, + ]; + /// Constructs [`RegF`] object for a provided requirement for register bit size pub fn with(bits: u16, use_bfloat16: bool) -> Option { Some(match bits { @@ -445,6 +469,18 @@ impl NumericRegister for RegR { } impl RegR { + /// Set of all R registers + pub const ALL: [RegR; 8] = [ + RegR::R128, + RegR::R160, + RegR::R256, + RegR::R512, + RegR::R1024, + RegR::R2048, + RegR::R4096, + RegR::R8192, + ]; + /// Constructs [`RegR`] object for a provided requirement for register bit size #[inline] pub fn with(bits: u16) -> Option { diff --git a/src/reg/indexes.rs b/src/reg/indexes.rs index c352564..5ab8a04 100644 --- a/src/reg/indexes.rs +++ b/src/reg/indexes.rs @@ -164,6 +164,42 @@ pub enum Reg32 { } impl Reg32 { + /// Constant enumerating all register indexes. + pub const ALL: [Reg32; 32] = [ + Reg32::Reg0, + Reg32::Reg1, + Reg32::Reg2, + Reg32::Reg3, + Reg32::Reg4, + Reg32::Reg5, + Reg32::Reg6, + Reg32::Reg7, + Reg32::Reg8, + Reg32::Reg9, + Reg32::Reg10, + Reg32::Reg11, + Reg32::Reg12, + Reg32::Reg13, + Reg32::Reg14, + Reg32::Reg15, + Reg32::Reg16, + Reg32::Reg17, + Reg32::Reg18, + Reg32::Reg19, + Reg32::Reg20, + Reg32::Reg21, + Reg32::Reg22, + Reg32::Reg23, + Reg32::Reg24, + Reg32::Reg25, + Reg32::Reg26, + Reg32::Reg27, + Reg32::Reg28, + Reg32::Reg29, + Reg32::Reg30, + Reg32::Reg31, + ]; + /// Returns `usize` representation of the register index #[inline] pub fn to_usize(self) -> usize { self as u8 as usize } @@ -311,6 +347,28 @@ pub enum Reg16 { Reg15 = 15, } +impl Reg16 { + /// Constant enumerating all register indexes. + pub const ALL: [Reg16; 16] = [ + Reg16::Reg0, + Reg16::Reg1, + Reg16::Reg2, + Reg16::Reg3, + Reg16::Reg4, + Reg16::Reg5, + Reg16::Reg6, + Reg16::Reg7, + Reg16::Reg8, + Reg16::Reg9, + Reg16::Reg10, + Reg16::Reg11, + Reg16::Reg12, + Reg16::Reg13, + Reg16::Reg14, + Reg16::Reg15, + ]; +} + impl Register for Reg16 { #[inline] fn description() -> &'static str { "4-bit register index" } @@ -408,6 +466,20 @@ pub enum Reg8 { Reg7 = 7, } +impl Reg8 { + /// Constant enumerating all register indexes. + pub const ALL: [Reg8; 8] = [ + Reg8::Reg0, + Reg8::Reg1, + Reg8::Reg2, + Reg8::Reg3, + Reg8::Reg4, + Reg8::Reg5, + Reg8::Reg6, + Reg8::Reg7, + ]; +} + impl Register for Reg8 { #[inline] fn description() -> &'static str { "3-bit register index" }