diff --git a/harm/src/instructions/ldst.rs b/harm/src/instructions/ldst.rs index 698aff4..4dd6c63 100644 --- a/harm/src/instructions/ldst.rs +++ b/harm/src/instructions/ldst.rs @@ -1,4 +1,4 @@ -/* Copyright (C) 2025 Ivan Boldyrev +/* Copyright (C) 2026 Ivan Boldyrev * * This document is licensed under the BSD 3-clause license. */ @@ -32,6 +32,7 @@ mod strh; mod stur; mod sturb; mod sturh; +mod unprivileged; pub use self::increment::*; pub use self::ldnp::*; @@ -58,6 +59,7 @@ pub use self::strh::*; pub use self::stur::*; pub use self::sturb::*; pub use self::sturh::*; +pub use self::unprivileged::*; use crate::bits::{SBitValue, UBitValue}; use crate::sealed::Sealed; diff --git a/harm/src/instructions/ldst/ldur.rs b/harm/src/instructions/ldst/ldur.rs index 45fd7e0..1c3fc40 100644 --- a/harm/src/instructions/ldst/ldur.rs +++ b/harm/src/instructions/ldst/ldur.rs @@ -40,8 +40,8 @@ pub trait MakeLdur: Sealed { fn new(rt: Rt, addr: Addr) -> Self::Output; } -define_unscaled_imm_offset_rules!(Ldur, MakeLdur, LDUR, RegOrZero64, 64); -define_unscaled_imm_offset_rules!(Ldur, MakeLdur, LDUR, RegOrZero32, 32); +define_unscaled_imm_offset_rules!(Ldur, MakeLdur, LDUR, RegOrZero64, 64, "ldst_unscaled"); +define_unscaled_imm_offset_rules!(Ldur, MakeLdur, LDUR, RegOrZero32, 32, "ldst_unscaled"); pub fn ldur( dst: TargetInp, diff --git a/harm/src/instructions/ldst/ldurb.rs b/harm/src/instructions/ldst/ldurb.rs index c8ed475..66b7fae 100644 --- a/harm/src/instructions/ldst/ldurb.rs +++ b/harm/src/instructions/ldst/ldurb.rs @@ -38,7 +38,7 @@ pub trait MakeLdurb: Sealed { fn new(rt: Rt, addr: Addr) -> Self::Output; } -define_unscaled_imm_offset_rules!(Ldurb, MakeLdurb, LDURB, RegOrZero32, 32); +define_unscaled_imm_offset_rules!(Ldurb, MakeLdurb, LDURB, RegOrZero32, 32, "ldst_unscaled"); pub fn ldurb( dst: TargetInp, diff --git a/harm/src/instructions/ldst/ldurh.rs b/harm/src/instructions/ldst/ldurh.rs index 99d2a1b..043c499 100644 --- a/harm/src/instructions/ldst/ldurh.rs +++ b/harm/src/instructions/ldst/ldurh.rs @@ -38,7 +38,7 @@ pub trait MakeLdurh: Sealed { fn new(rt: Rt, addr: Addr) -> Self::Output; } -define_unscaled_imm_offset_rules!(Ldurh, MakeLdurh, LDURH, RegOrZero32, 32); +define_unscaled_imm_offset_rules!(Ldurh, MakeLdurh, LDURH, RegOrZero32, 32, "ldst_unscaled"); pub fn ldurh( dst: TargetInp, diff --git a/harm/src/instructions/ldst/ldursb.rs b/harm/src/instructions/ldst/ldursb.rs index 4d5b5d2..2f18904 100644 --- a/harm/src/instructions/ldst/ldursb.rs +++ b/harm/src/instructions/ldst/ldursb.rs @@ -41,8 +41,8 @@ pub trait MakeLdursb: Sealed { fn new(rt: Rt, addr: Addr) -> Self::Output; } -define_unscaled_imm_offset_rules!(Ldursb, MakeLdursb, LDURSB, RegOrZero64, 64); -define_unscaled_imm_offset_rules!(Ldursb, MakeLdursb, LDURSB, RegOrZero32, 32); +define_unscaled_imm_offset_rules!(Ldursb, MakeLdursb, LDURSB, RegOrZero64, 64, "ldst_unscaled"); +define_unscaled_imm_offset_rules!(Ldursb, MakeLdursb, LDURSB, RegOrZero32, 32, "ldst_unscaled"); pub fn ldursb( dst: TargetInp, diff --git a/harm/src/instructions/ldst/ldursh.rs b/harm/src/instructions/ldst/ldursh.rs index b93bafa..63933ec 100644 --- a/harm/src/instructions/ldst/ldursh.rs +++ b/harm/src/instructions/ldst/ldursh.rs @@ -41,8 +41,8 @@ pub trait MakeLdursh: Sealed { fn new(rt: Rt, addr: Addr) -> Self::Output; } -define_unscaled_imm_offset_rules!(Ldursh, MakeLdursh, LDURSH, RegOrZero64, 64); -define_unscaled_imm_offset_rules!(Ldursh, MakeLdursh, LDURSH, RegOrZero32, 32); +define_unscaled_imm_offset_rules!(Ldursh, MakeLdursh, LDURSH, RegOrZero64, 64, "ldst_unscaled"); +define_unscaled_imm_offset_rules!(Ldursh, MakeLdursh, LDURSH, RegOrZero32, 32, "ldst_unscaled"); pub fn ldursh( dst: TargetInp, diff --git a/harm/src/instructions/ldst/ldursw.rs b/harm/src/instructions/ldst/ldursw.rs index c8a8324..db6e678 100644 --- a/harm/src/instructions/ldst/ldursw.rs +++ b/harm/src/instructions/ldst/ldursw.rs @@ -38,7 +38,7 @@ pub trait MakeLdursw: Sealed { fn new(rt: Rt, addr: Addr) -> Self::Output; } -define_unscaled_imm_offset_rules!(Ldursw, MakeLdursw, LDURSW, RegOrZero64, 64); +define_unscaled_imm_offset_rules!(Ldursw, MakeLdursw, LDURSW, RegOrZero64, 64, "ldst_unscaled"); pub fn ldursw( dst: TargetInp, diff --git a/harm/src/instructions/ldst/macros.rs b/harm/src/instructions/ldst/macros.rs index df92cb3..313327d 100644 --- a/harm/src/instructions/ldst/macros.rs +++ b/harm/src/instructions/ldst/macros.rs @@ -278,7 +278,7 @@ specific range and alignment."] } macro_rules! define_unscaled_imm_offset_rules { - ($name:ident, $make_name:ident, $mnem:ident, $rt:ty, $bitness:expr) => { + ($name:ident, $make_name:ident, $mnem:ident, $rt:ty, $bitness:expr, $suffix:expr) => { impl $make_name for $name<$rt, (RegOrSp64, UnscaledOffset)> where @@ -348,7 +348,7 @@ macro_rules! define_unscaled_imm_offset_rules { #[inline] fn to_code(&self) -> $crate::InstructionCode { let (base, offset) = self.addr; - [<$mnem:upper _ $bitness _ldst_unscaled>](offset.into(), base.index(), self.rt.index()) + [<$mnem:upper _ $bitness _ $suffix>](offset.into(), base.index(), self.rt.index()) } } } diff --git a/harm/src/instructions/ldst/stur.rs b/harm/src/instructions/ldst/stur.rs index eeeb9ba..0b37638 100644 --- a/harm/src/instructions/ldst/stur.rs +++ b/harm/src/instructions/ldst/stur.rs @@ -40,8 +40,8 @@ pub trait MakeStur: Sealed { fn new(rt: Rt, addr: Addr) -> Self::Output; } -define_unscaled_imm_offset_rules!(Stur, MakeStur, STUR, RegOrZero64, 64); -define_unscaled_imm_offset_rules!(Stur, MakeStur, STUR, RegOrZero32, 32); +define_unscaled_imm_offset_rules!(Stur, MakeStur, STUR, RegOrZero64, 64, "ldst_unscaled"); +define_unscaled_imm_offset_rules!(Stur, MakeStur, STUR, RegOrZero32, 32, "ldst_unscaled"); pub fn stur( dst: TargetInp, diff --git a/harm/src/instructions/ldst/sturb.rs b/harm/src/instructions/ldst/sturb.rs index 4e3caba..3e066db 100644 --- a/harm/src/instructions/ldst/sturb.rs +++ b/harm/src/instructions/ldst/sturb.rs @@ -38,7 +38,7 @@ pub trait MakeSturb: Sealed { fn new(rt: Rt, addr: Addr) -> Self::Output; } -define_unscaled_imm_offset_rules!(Sturb, MakeSturb, STURB, RegOrZero32, 32); +define_unscaled_imm_offset_rules!(Sturb, MakeSturb, STURB, RegOrZero32, 32, "ldst_unscaled"); pub fn sturb( dst: TargetInp, diff --git a/harm/src/instructions/ldst/sturh.rs b/harm/src/instructions/ldst/sturh.rs index c0febcb..0a3527a 100644 --- a/harm/src/instructions/ldst/sturh.rs +++ b/harm/src/instructions/ldst/sturh.rs @@ -38,7 +38,7 @@ pub trait MakeSturh: Sealed { fn new(rt: Rt, addr: Addr) -> Self::Output; } -define_unscaled_imm_offset_rules!(Sturh, MakeSturh, STURH, RegOrZero32, 32); +define_unscaled_imm_offset_rules!(Sturh, MakeSturh, STURH, RegOrZero32, 32, "ldst_unscaled"); pub fn sturh( dst: TargetInp, diff --git a/harm/src/instructions/ldst/unprivileged.rs b/harm/src/instructions/ldst/unprivileged.rs new file mode 100644 index 0000000..97e3f60 --- /dev/null +++ b/harm/src/instructions/ldst/unprivileged.rs @@ -0,0 +1,29 @@ +/* Copyright (C) 2026 Ivan Boldyrev + * + * This document is licensed under the BSD 3-clause license. + */ + +//! Unprivileged (translated) load and store instructions. + +mod ldtr; +mod ldtrb; +mod ldtrh; +mod ldtrsb; +mod ldtrsh; +mod ldtrsw; +mod sttr; +mod sttrb; +mod sttrh; + +pub use self::ldtr::*; +pub use self::ldtrb::*; +pub use self::ldtrh::*; +pub use self::ldtrsb::*; +pub use self::ldtrsh::*; +pub use self::ldtrsw::*; +pub use self::sttr::*; +pub use self::sttrb::*; +pub use self::sttrh::*; + +// Re-exported so the child modules can keep referring to `super::UnscaledOffset`. +use super::UnscaledOffset; diff --git a/harm/src/instructions/ldst/unprivileged/ldtr.rs b/harm/src/instructions/ldst/unprivileged/ldtr.rs new file mode 100644 index 0000000..c3321dc --- /dev/null +++ b/harm/src/instructions/ldst/unprivileged/ldtr.rs @@ -0,0 +1,157 @@ +/* Copyright (C) 2026 Ivan Boldyrev + * + * This document is licensed under the BSD 3-clause license. + */ + +use aarchmrs_instructions::A64::ldst::ldst_unpriv::{ + LDTR_32_ldst_unpriv::LDTR_32_ldst_unpriv, LDTR_64_ldst_unpriv::LDTR_64_ldst_unpriv, +}; + +use crate::bits::BitError; +use crate::instructions::RawInstruction; +use crate::register::{IntoReg, RegOrSp64, RegOrZero32, RegOrZero64, Register}; +use crate::sealed::Sealed; + +use super::UnscaledOffset; + +/// A `LDTR` instruction with a destination and an address. +pub struct Ldtr { + rt: Rt, + addr: Addr, +} + +impl Ldtr { + pub fn rt(&self) -> &Rt { + &self.rt + } + + pub fn addr(&self) -> &Addr { + &self.addr + } +} + +impl Sealed for Ldtr {} + +/// Defines possible ways to construct a `ldtr` instruction. +pub trait MakeLdtr: Sealed { + /// Allows defining both fallible and infallible constructors. + type Output; + + fn new(rt: Rt, addr: Addr) -> Self::Output; +} + +pub fn ldtr( + dst: TargetInp, + addr: AddrInp, +) -> as MakeLdtr>::Output +where + Ldtr: MakeLdtr, +{ + Ldtr::new(dst, addr) +} + +define_unscaled_imm_offset_rules!(Ldtr, MakeLdtr, LDTR, RegOrZero64, 64, "ldst_unpriv"); +define_unscaled_imm_offset_rules!(Ldtr, MakeLdtr, LDTR, RegOrZero32, 32, "ldst_unpriv"); + +#[cfg(test)] +mod tests { + use harm_test_utils::test_cases; + + use super::*; + use crate::instructions::InstructionSeq; + use crate::register::Reg32::*; + use crate::register::Reg64::*; + use RegOrSp64::SP; + use RegOrZero32::WZR; + use RegOrZero64::XZR; + + // 'ldtr (x1|w1|xzr|wzr), [(x2|sp), (-1|1|255|-256|0)] + const LDTR_DB: &str = " +f85ff841 ldtr x1, [x2, -1] +f8401841 ldtr x1, [x2, 1] +f84ff841 ldtr x1, [x2, 255] +f8500841 ldtr x1, [x2, -256] +f8400841 ldtr x1, [x2, 0] +f8400841 ldtr x1, [x2] +f85ffbe1 ldtr x1, [sp, -1] +f8401be1 ldtr x1, [sp, 1] +f84ffbe1 ldtr x1, [sp, 255] +f8500be1 ldtr x1, [sp, -256] +f8400be1 ldtr x1, [sp, 0] +b85ff841 ldtr w1, [x2, -1] +b8401841 ldtr w1, [x2, 1] +b84ff841 ldtr w1, [x2, 255] +b8500841 ldtr w1, [x2, -256] +b8400841 ldtr w1, [x2, 0] +b85ffbe1 ldtr w1, [sp, -1] +b8401be1 ldtr w1, [sp, 1] +b84ffbe1 ldtr w1, [sp, 255] +b8500be1 ldtr w1, [sp, -256] +b8400be1 ldtr w1, [sp, 0] +f85ff85f ldtr xzr, [x2, -1] +f840185f ldtr xzr, [x2, 1] +f84ff85f ldtr xzr, [x2, 255] +f850085f ldtr xzr, [x2, -256] +f840085f ldtr xzr, [x2, 0] +f85ffbff ldtr xzr, [sp, -1] +f8401bff ldtr xzr, [sp, 1] +f84ffbff ldtr xzr, [sp, 255] +f8500bff ldtr xzr, [sp, -256] +f8400bff ldtr xzr, [sp, 0] +b85ff85f ldtr wzr, [x2, -1] +b840185f ldtr wzr, [x2, 1] +b84ff85f ldtr wzr, [x2, 255] +b850085f ldtr wzr, [x2, -256] +b840085f ldtr wzr, [x2, 0] +b85ffbff ldtr wzr, [sp, -1] +b8401bff ldtr wzr, [sp, 1] +b84ffbff ldtr wzr, [sp, 255] +b8500bff ldtr wzr, [sp, -256] +b8400bff ldtr wzr, [sp, 0] +"; + + test_cases! { + LDTR_DB, untested_ldtr_cases; + test_ldtr_x1_x2_m1, ldtr(X1, (X2, -1)).unwrap(), "ldtr x1, [x2, -1]"; + test_ldtr_x1_x2_1, ldtr(X1, (X2, 1)).unwrap(), "ldtr x1, [x2, 1]"; + test_ldtr_x1_x2_255, ldtr(X1, (X2, 255)).unwrap(), "ldtr x1, [x2, 255]"; + test_ldtr_x1_x2_m256, ldtr(X1, (X2, -256)).unwrap(), "ldtr x1, [x2, -256]"; + test_ldtr_x1_x2_0, ldtr(X1, (X2, 0)).unwrap(), "ldtr x1, [x2, 0]"; + test_ldtr_x1_x2_simple, ldtr(X1, (X2,)), "ldtr x1, [x2]"; + test_ldtr_x1_sp_m1, ldtr(X1, (SP, -1)).unwrap(), "ldtr x1, [sp, -1]"; + test_ldtr_x1_sp_1, ldtr(X1, (SP, 1)).unwrap(), "ldtr x1, [sp, 1]"; + test_ldtr_x1_sp_255, ldtr(X1, (SP, 255)).unwrap(), "ldtr x1, [sp, 255]"; + test_ldtr_x1_sp_m256, ldtr(X1, (SP, -256)).unwrap(), "ldtr x1, [sp, -256]"; + test_ldtr_x1_sp_0, ldtr(X1, (SP, 0)).unwrap(), "ldtr x1, [sp, 0]"; + test_ldtr_w1_x2_m1, ldtr(W1, (X2, -1)).unwrap(), "ldtr w1, [x2, -1]"; + test_ldtr_w1_x2_1, ldtr(W1, (X2, 1)).unwrap(), "ldtr w1, [x2, 1]"; + test_ldtr_w1_x2_255, ldtr(W1, (X2, 255)).unwrap(), "ldtr w1, [x2, 255]"; + test_ldtr_w1_x2_m256, ldtr(W1, (X2, -256)).unwrap(), "ldtr w1, [x2, -256]"; + test_ldtr_w1_x2_0, ldtr(W1, (X2, 0)).unwrap(), "ldtr w1, [x2, 0]"; + test_ldtr_w1_sp_m1, ldtr(W1, (SP, -1)).unwrap(), "ldtr w1, [sp, -1]"; + test_ldtr_w1_sp_1, ldtr(W1, (SP, 1)).unwrap(), "ldtr w1, [sp, 1]"; + test_ldtr_w1_sp_255, ldtr(W1, (SP, 255)).unwrap(), "ldtr w1, [sp, 255]"; + test_ldtr_w1_sp_m256, ldtr(W1, (SP, -256)).unwrap(), "ldtr w1, [sp, -256]"; + test_ldtr_w1_sp_0, ldtr(W1, (SP, 0)).unwrap(), "ldtr w1, [sp, 0]"; + test_ldtr_xzr_x2_m1, ldtr(XZR, (X2, -1)).unwrap(), "ldtr xzr, [x2, -1]"; + test_ldtr_xzr_x2_1, ldtr(XZR, (X2, 1)).unwrap(), "ldtr xzr, [x2, 1]"; + test_ldtr_xzr_x2_255, ldtr(XZR, (X2, 255)).unwrap(), "ldtr xzr, [x2, 255]"; + test_ldtr_xzr_x2_m256, ldtr(XZR, (X2, -256)).unwrap(), "ldtr xzr, [x2, -256]"; + test_ldtr_xzr_x2_0, ldtr(XZR, (X2, 0)).unwrap(), "ldtr xzr, [x2, 0]"; + test_ldtr_xzr_sp_m1, ldtr(XZR, (SP, -1)).unwrap(), "ldtr xzr, [sp, -1]"; + test_ldtr_xzr_sp_1, ldtr(XZR, (SP, 1)).unwrap(), "ldtr xzr, [sp, 1]"; + test_ldtr_xzr_sp_255, ldtr(XZR, (SP, 255)).unwrap(), "ldtr xzr, [sp, 255]"; + test_ldtr_xzr_sp_m256, ldtr(XZR, (SP, -256)).unwrap(), "ldtr xzr, [sp, -256]"; + test_ldtr_xzr_sp_0, ldtr(XZR, (SP, 0)).unwrap(), "ldtr xzr, [sp, 0]"; + test_ldtr_wzr_x2_m1, ldtr(WZR, (X2, -1)).unwrap(), "ldtr wzr, [x2, -1]"; + test_ldtr_wzr_x2_1, ldtr(WZR, (X2, 1)).unwrap(), "ldtr wzr, [x2, 1]"; + test_ldtr_wzr_x2_255, ldtr(WZR, (X2, 255)).unwrap(), "ldtr wzr, [x2, 255]"; + test_ldtr_wzr_x2_m256, ldtr(WZR, (X2, -256)).unwrap(), "ldtr wzr, [x2, -256]"; + test_ldtr_wzr_x2_0, ldtr(WZR, (X2, 0)).unwrap(), "ldtr wzr, [x2, 0]"; + test_ldtr_wzr_sp_m1, ldtr(WZR, (SP, -1)).unwrap(), "ldtr wzr, [sp, -1]"; + test_ldtr_wzr_sp_1, ldtr(WZR, (SP, 1)).unwrap(), "ldtr wzr, [sp, 1]"; + test_ldtr_wzr_sp_255, ldtr(WZR, (SP, 255)).unwrap(), "ldtr wzr, [sp, 255]"; + test_ldtr_wzr_sp_m256, ldtr(WZR, (SP, -256)).unwrap(), "ldtr wzr, [sp, -256]"; + test_ldtr_wzr_sp_0, ldtr(WZR, (SP, 0)).unwrap(), "ldtr wzr, [sp, 0]"; + } +} diff --git a/harm/src/instructions/ldst/unprivileged/ldtrb.rs b/harm/src/instructions/ldst/unprivileged/ldtrb.rs new file mode 100644 index 0000000..ce3aed1 --- /dev/null +++ b/harm/src/instructions/ldst/unprivileged/ldtrb.rs @@ -0,0 +1,112 @@ +/* Copyright (C) 2026 Ivan Boldyrev + * + * This document is licensed under the BSD 3-clause license. + */ + +use aarchmrs_instructions::A64::ldst::ldst_unpriv::LDTRB_32_ldst_unpriv::LDTRB_32_ldst_unpriv; + +use crate::bits::BitError; +use crate::instructions::RawInstruction; +use crate::register::{IntoReg, RegOrSp64, RegOrZero32, Register}; +use crate::sealed::Sealed; + +use super::UnscaledOffset; + +/// A `LDTRB` instruction with a destination and an address. +pub struct Ldtrb { + rt: Rt, + addr: Addr, +} + +impl Ldtrb { + pub fn rt(&self) -> &Rt { + &self.rt + } + + pub fn addr(&self) -> &Addr { + &self.addr + } +} + +impl Sealed for Ldtrb {} + +/// Defines possible ways to construct a `ldtrb` instruction. +pub trait MakeLdtrb: Sealed { + /// Allows defining both fallible and infallible constructors. + type Output; + + fn new(rt: Rt, addr: Addr) -> Self::Output; +} + +define_unscaled_imm_offset_rules!(Ldtrb, MakeLdtrb, LDTRB, RegOrZero32, 32, "ldst_unpriv"); + +pub fn ldtrb( + dst: TargetInp, + addr: AddrInp, +) -> as MakeLdtrb>::Output +where + Ldtrb: MakeLdtrb, +{ + Ldtrb::new(dst, addr) +} + +#[cfg(test)] +mod tests { + use harm_test_utils::test_cases; + + use super::*; + use crate::instructions::InstructionSeq; + use crate::register::Reg32::*; + use crate::register::Reg64::*; + use RegOrSp64::SP; + use RegOrZero32::WZR; + + const LDTRB_DB: &str = " +385ff841 ldtrb w1, [x2, -1] +38401841 ldtrb w1, [x2, 1] +384ff841 ldtrb w1, [x2, 255] +38500841 ldtrb w1, [x2, -256] +38400841 ldtrb w1, [x2, 0] +38400841 ldtrb w1, [x2] +385ffbe1 ldtrb w1, [sp, -1] +38401be1 ldtrb w1, [sp, 1] +384ffbe1 ldtrb w1, [sp, 255] +38500be1 ldtrb w1, [sp, -256] +38400be1 ldtrb w1, [sp, 0] +385ff85f ldtrb wzr, [x2, -1] +3840185f ldtrb wzr, [x2, 1] +384ff85f ldtrb wzr, [x2, 255] +3850085f ldtrb wzr, [x2, -256] +3840085f ldtrb wzr, [x2, 0] +385ffbff ldtrb wzr, [sp, -1] +38401bff ldtrb wzr, [sp, 1] +384ffbff ldtrb wzr, [sp, 255] +38500bff ldtrb wzr, [sp, -256] +38400bff ldtrb wzr, [sp, 0] +"; + + test_cases! { + LDTRB_DB, untested_ldtrb_cases; + test_ldtrb_w1_x2_m1, ldtrb(W1, (X2, -1)).unwrap(), "ldtrb w1, [x2, -1]"; + test_ldtrb_w1_x2_1, ldtrb(W1, (X2, 1)).unwrap(), "ldtrb w1, [x2, 1]"; + test_ldtrb_w1_x2_255, ldtrb(W1, (X2, 255)).unwrap(), "ldtrb w1, [x2, 255]"; + test_ldtrb_w1_x2_m256, ldtrb(W1, (X2, -256)).unwrap(), "ldtrb w1, [x2, -256]"; + test_ldtrb_w1_x2_0, ldtrb(W1, (X2, 0)).unwrap(), "ldtrb w1, [x2, 0]"; + test_ldtrb_w1_x2_simple, ldtrb(W1, (X2,)), "ldtrb w1, [x2]"; + test_ldtrb_w1_sp_m1, ldtrb(W1, (SP, -1)).unwrap(), "ldtrb w1, [sp, -1]"; + test_ldtrb_w1_sp_1, ldtrb(W1, (SP, 1)).unwrap(), "ldtrb w1, [sp, 1]"; + test_ldtrb_w1_sp_255, ldtrb(W1, (SP, 255)).unwrap(), "ldtrb w1, [sp, 255]"; + test_ldtrb_w1_sp_m256, ldtrb(W1, (SP, -256)).unwrap(), "ldtrb w1, [sp, -256]"; + test_ldtrb_w1_sp_0, ldtrb(W1, (SP, 0)).unwrap(), "ldtrb w1, [sp, 0]"; + test_ldtrb_wzr_x2_m1, ldtrb(WZR, (X2, -1)).unwrap(), "ldtrb wzr, [x2, -1]"; + test_ldtrb_wzr_x2_1, ldtrb(WZR, (X2, 1)).unwrap(), "ldtrb wzr, [x2, 1]"; + test_ldtrb_wzr_x2_255, ldtrb(WZR, (X2, 255)).unwrap(), "ldtrb wzr, [x2, 255]"; + test_ldtrb_wzr_x2_m256, ldtrb(WZR, (X2, -256)).unwrap(), "ldtrb wzr, [x2, -256]"; + test_ldtrb_wzr_x2_0, ldtrb(WZR, (X2, 0)).unwrap(), "ldtrb wzr, [x2, 0]"; + test_ldtrb_wzr_sp_m1, ldtrb(WZR, (SP, -1)).unwrap(), "ldtrb wzr, [sp, -1]"; + test_ldtrb_wzr_sp_1, ldtrb(WZR, (SP, 1)).unwrap(), "ldtrb wzr, [sp, 1]"; + test_ldtrb_wzr_sp_255, ldtrb(WZR, (SP, 255)).unwrap(), "ldtrb wzr, [sp, 255]"; + test_ldtrb_wzr_sp_m256, ldtrb(WZR, (SP, -256)).unwrap(), "ldtrb wzr, [sp, -256]"; + test_ldtrb_wzr_sp_0, ldtrb(WZR, (SP, 0)).unwrap(), "ldtrb wzr, [sp, 0]"; + } +} diff --git a/harm/src/instructions/ldst/unprivileged/ldtrh.rs b/harm/src/instructions/ldst/unprivileged/ldtrh.rs new file mode 100644 index 0000000..98b11da --- /dev/null +++ b/harm/src/instructions/ldst/unprivileged/ldtrh.rs @@ -0,0 +1,112 @@ +/* Copyright (C) 2026 Ivan Boldyrev + * + * This document is licensed under the BSD 3-clause license. + */ + +use aarchmrs_instructions::A64::ldst::ldst_unpriv::LDTRH_32_ldst_unpriv::LDTRH_32_ldst_unpriv; + +use crate::bits::BitError; +use crate::instructions::RawInstruction; +use crate::register::{IntoReg, RegOrSp64, RegOrZero32, Register}; +use crate::sealed::Sealed; + +use super::UnscaledOffset; + +/// A `LDTRH` instruction with a destination and an address. +pub struct Ldtrh { + rt: Rt, + addr: Addr, +} + +impl Ldtrh { + pub fn rt(&self) -> &Rt { + &self.rt + } + + pub fn addr(&self) -> &Addr { + &self.addr + } +} + +impl Sealed for Ldtrh {} + +/// Defines possible ways to construct a `ldtrh` instruction. +pub trait MakeLdtrh: Sealed { + /// Allows defining both fallible and infallible constructors. + type Output; + + fn new(rt: Rt, addr: Addr) -> Self::Output; +} + +define_unscaled_imm_offset_rules!(Ldtrh, MakeLdtrh, LDTRH, RegOrZero32, 32, "ldst_unpriv"); + +pub fn ldtrh( + dst: TargetInp, + addr: AddrInp, +) -> as MakeLdtrh>::Output +where + Ldtrh: MakeLdtrh, +{ + Ldtrh::new(dst, addr) +} + +#[cfg(test)] +mod tests { + use harm_test_utils::test_cases; + + use super::*; + use crate::instructions::InstructionSeq; + use crate::register::Reg32::*; + use crate::register::Reg64::*; + use RegOrSp64::SP; + use RegOrZero32::WZR; + + const LDTRH_DB: &str = " +785ff841 ldtrh w1, [x2, -1] +78401841 ldtrh w1, [x2, 1] +784ff841 ldtrh w1, [x2, 255] +78500841 ldtrh w1, [x2, -256] +78400841 ldtrh w1, [x2, 0] +78400841 ldtrh w1, [x2] +785ffbe1 ldtrh w1, [sp, -1] +78401be1 ldtrh w1, [sp, 1] +784ffbe1 ldtrh w1, [sp, 255] +78500be1 ldtrh w1, [sp, -256] +78400be1 ldtrh w1, [sp, 0] +785ff85f ldtrh wzr, [x2, -1] +7840185f ldtrh wzr, [x2, 1] +784ff85f ldtrh wzr, [x2, 255] +7850085f ldtrh wzr, [x2, -256] +7840085f ldtrh wzr, [x2, 0] +785ffbff ldtrh wzr, [sp, -1] +78401bff ldtrh wzr, [sp, 1] +784ffbff ldtrh wzr, [sp, 255] +78500bff ldtrh wzr, [sp, -256] +78400bff ldtrh wzr, [sp, 0] +"; + + test_cases! { + LDTRH_DB, untested_ldtrh_cases; + test_ldtrh_w1_x2_m1, ldtrh(W1, (X2, -1)).unwrap(), "ldtrh w1, [x2, -1]"; + test_ldtrh_w1_x2_1, ldtrh(W1, (X2, 1)).unwrap(), "ldtrh w1, [x2, 1]"; + test_ldtrh_w1_x2_255, ldtrh(W1, (X2, 255)).unwrap(), "ldtrh w1, [x2, 255]"; + test_ldtrh_w1_x2_m256, ldtrh(W1, (X2, -256)).unwrap(), "ldtrh w1, [x2, -256]"; + test_ldtrh_w1_x2_0, ldtrh(W1, (X2, 0)).unwrap(), "ldtrh w1, [x2, 0]"; + test_ldtrh_w1_x2_simple, ldtrh(W1, (X2,)), "ldtrh w1, [x2]"; + test_ldtrh_w1_sp_m1, ldtrh(W1, (SP, -1)).unwrap(), "ldtrh w1, [sp, -1]"; + test_ldtrh_w1_sp_1, ldtrh(W1, (SP, 1)).unwrap(), "ldtrh w1, [sp, 1]"; + test_ldtrh_w1_sp_255, ldtrh(W1, (SP, 255)).unwrap(), "ldtrh w1, [sp, 255]"; + test_ldtrh_w1_sp_m256, ldtrh(W1, (SP, -256)).unwrap(), "ldtrh w1, [sp, -256]"; + test_ldtrh_w1_sp_0, ldtrh(W1, (SP, 0)).unwrap(), "ldtrh w1, [sp, 0]"; + test_ldtrh_wzr_x2_m1, ldtrh(WZR, (X2, -1)).unwrap(), "ldtrh wzr, [x2, -1]"; + test_ldtrh_wzr_x2_1, ldtrh(WZR, (X2, 1)).unwrap(), "ldtrh wzr, [x2, 1]"; + test_ldtrh_wzr_x2_255, ldtrh(WZR, (X2, 255)).unwrap(), "ldtrh wzr, [x2, 255]"; + test_ldtrh_wzr_x2_m256, ldtrh(WZR, (X2, -256)).unwrap(), "ldtrh wzr, [x2, -256]"; + test_ldtrh_wzr_x2_0, ldtrh(WZR, (X2, 0)).unwrap(), "ldtrh wzr, [x2, 0]"; + test_ldtrh_wzr_sp_m1, ldtrh(WZR, (SP, -1)).unwrap(), "ldtrh wzr, [sp, -1]"; + test_ldtrh_wzr_sp_1, ldtrh(WZR, (SP, 1)).unwrap(), "ldtrh wzr, [sp, 1]"; + test_ldtrh_wzr_sp_255, ldtrh(WZR, (SP, 255)).unwrap(), "ldtrh wzr, [sp, 255]"; + test_ldtrh_wzr_sp_m256, ldtrh(WZR, (SP, -256)).unwrap(), "ldtrh wzr, [sp, -256]"; + test_ldtrh_wzr_sp_0, ldtrh(WZR, (SP, 0)).unwrap(), "ldtrh wzr, [sp, 0]"; + } +} diff --git a/harm/src/instructions/ldst/unprivileged/ldtrsb.rs b/harm/src/instructions/ldst/unprivileged/ldtrsb.rs new file mode 100644 index 0000000..61450be --- /dev/null +++ b/harm/src/instructions/ldst/unprivileged/ldtrsb.rs @@ -0,0 +1,157 @@ +/* Copyright (C) 2026 Ivan Boldyrev + * + * This document is licensed under the BSD 3-clause license. + */ + +use aarchmrs_instructions::A64::ldst::ldst_unpriv::{ + LDTRSB_32_ldst_unpriv::LDTRSB_32_ldst_unpriv, LDTRSB_64_ldst_unpriv::LDTRSB_64_ldst_unpriv, +}; + +use crate::bits::BitError; +use crate::instructions::RawInstruction; +use crate::register::{IntoReg, RegOrSp64, RegOrZero32, RegOrZero64, Register}; +use crate::sealed::Sealed; + +use super::UnscaledOffset; + +/// A `LDTRSB` instruction with a destination and an address. +pub struct Ldtrsb { + rt: Rt, + addr: Addr, +} + +impl Ldtrsb { + pub fn rt(&self) -> &Rt { + &self.rt + } + + pub fn addr(&self) -> &Addr { + &self.addr + } +} + +impl Sealed for Ldtrsb {} + +/// Defines possible ways to construct a `ldtrsb` instruction. +pub trait MakeLdtrsb: Sealed { + /// Allows defining both fallible and infallible constructors. + type Output; + + fn new(rt: Rt, addr: Addr) -> Self::Output; +} + +define_unscaled_imm_offset_rules!(Ldtrsb, MakeLdtrsb, LDTRSB, RegOrZero64, 64, "ldst_unpriv"); +define_unscaled_imm_offset_rules!(Ldtrsb, MakeLdtrsb, LDTRSB, RegOrZero32, 32, "ldst_unpriv"); + +pub fn ldtrsb( + dst: TargetInp, + addr: AddrInp, +) -> as MakeLdtrsb>::Output +where + Ldtrsb: MakeLdtrsb, +{ + Ldtrsb::new(dst, addr) +} + +#[cfg(test)] +mod tests { + use harm_test_utils::test_cases; + + use super::*; + use crate::instructions::InstructionSeq; + use crate::register::Reg32::*; + use crate::register::Reg64::*; + use RegOrSp64::SP; + use RegOrZero32::WZR; + use RegOrZero64::XZR; + + // 'ldtrsb (x1|w1|xzr|wzr), [(x2|sp), (-1|1|255|-256|0)] + const LDTRSB_DB: &str = " +389ff841 ldtrsb x1, [x2, -1] +38801841 ldtrsb x1, [x2, 1] +388ff841 ldtrsb x1, [x2, 255] +38900841 ldtrsb x1, [x2, -256] +38800841 ldtrsb x1, [x2, 0] +38800841 ldtrsb x1, [x2] +389ffbe1 ldtrsb x1, [sp, -1] +38801be1 ldtrsb x1, [sp, 1] +388ffbe1 ldtrsb x1, [sp, 255] +38900be1 ldtrsb x1, [sp, -256] +38800be1 ldtrsb x1, [sp, 0] +38dff841 ldtrsb w1, [x2, -1] +38c01841 ldtrsb w1, [x2, 1] +38cff841 ldtrsb w1, [x2, 255] +38d00841 ldtrsb w1, [x2, -256] +38c00841 ldtrsb w1, [x2, 0] +38dffbe1 ldtrsb w1, [sp, -1] +38c01be1 ldtrsb w1, [sp, 1] +38cffbe1 ldtrsb w1, [sp, 255] +38d00be1 ldtrsb w1, [sp, -256] +38c00be1 ldtrsb w1, [sp, 0] +389ff85f ldtrsb xzr, [x2, -1] +3880185f ldtrsb xzr, [x2, 1] +388ff85f ldtrsb xzr, [x2, 255] +3890085f ldtrsb xzr, [x2, -256] +3880085f ldtrsb xzr, [x2, 0] +389ffbff ldtrsb xzr, [sp, -1] +38801bff ldtrsb xzr, [sp, 1] +388ffbff ldtrsb xzr, [sp, 255] +38900bff ldtrsb xzr, [sp, -256] +38800bff ldtrsb xzr, [sp, 0] +38dff85f ldtrsb wzr, [x2, -1] +38c0185f ldtrsb wzr, [x2, 1] +38cff85f ldtrsb wzr, [x2, 255] +38d0085f ldtrsb wzr, [x2, -256] +38c0085f ldtrsb wzr, [x2, 0] +38dffbff ldtrsb wzr, [sp, -1] +38c01bff ldtrsb wzr, [sp, 1] +38cffbff ldtrsb wzr, [sp, 255] +38d00bff ldtrsb wzr, [sp, -256] +38c00bff ldtrsb wzr, [sp, 0] +"; + + test_cases! { + LDTRSB_DB, untested_ldtrsb_cases; + test_ldtrsb_x1_x2_m1, ldtrsb(X1, (X2, -1)).unwrap(), "ldtrsb x1, [x2, -1]"; + test_ldtrsb_x1_x2_1, ldtrsb(X1, (X2, 1)).unwrap(), "ldtrsb x1, [x2, 1]"; + test_ldtrsb_x1_x2_255, ldtrsb(X1, (X2, 255)).unwrap(), "ldtrsb x1, [x2, 255]"; + test_ldtrsb_x1_x2_m256, ldtrsb(X1, (X2, -256)).unwrap(), "ldtrsb x1, [x2, -256]"; + test_ldtrsb_x1_x2_0, ldtrsb(X1, (X2, 0)).unwrap(), "ldtrsb x1, [x2, 0]"; + test_ldtrsb_x1_x2_simple, ldtrsb(X1, (X2,)), "ldtrsb x1, [x2]"; + test_ldtrsb_x1_sp_m1, ldtrsb(X1, (SP, -1)).unwrap(), "ldtrsb x1, [sp, -1]"; + test_ldtrsb_x1_sp_1, ldtrsb(X1, (SP, 1)).unwrap(), "ldtrsb x1, [sp, 1]"; + test_ldtrsb_x1_sp_255, ldtrsb(X1, (SP, 255)).unwrap(), "ldtrsb x1, [sp, 255]"; + test_ldtrsb_x1_sp_m256, ldtrsb(X1, (SP, -256)).unwrap(), "ldtrsb x1, [sp, -256]"; + test_ldtrsb_x1_sp_0, ldtrsb(X1, (SP, 0)).unwrap(), "ldtrsb x1, [sp, 0]"; + test_ldtrsb_w1_x2_m1, ldtrsb(W1, (X2, -1)).unwrap(), "ldtrsb w1, [x2, -1]"; + test_ldtrsb_w1_x2_1, ldtrsb(W1, (X2, 1)).unwrap(), "ldtrsb w1, [x2, 1]"; + test_ldtrsb_w1_x2_255, ldtrsb(W1, (X2, 255)).unwrap(), "ldtrsb w1, [x2, 255]"; + test_ldtrsb_w1_x2_m256, ldtrsb(W1, (X2, -256)).unwrap(), "ldtrsb w1, [x2, -256]"; + test_ldtrsb_w1_x2_0, ldtrsb(W1, (X2, 0)).unwrap(), "ldtrsb w1, [x2, 0]"; + test_ldtrsb_w1_sp_m1, ldtrsb(W1, (SP, -1)).unwrap(), "ldtrsb w1, [sp, -1]"; + test_ldtrsb_w1_sp_1, ldtrsb(W1, (SP, 1)).unwrap(), "ldtrsb w1, [sp, 1]"; + test_ldtrsb_w1_sp_255, ldtrsb(W1, (SP, 255)).unwrap(), "ldtrsb w1, [sp, 255]"; + test_ldtrsb_w1_sp_m256, ldtrsb(W1, (SP, -256)).unwrap(), "ldtrsb w1, [sp, -256]"; + test_ldtrsb_w1_sp_0, ldtrsb(W1, (SP, 0)).unwrap(), "ldtrsb w1, [sp, 0]"; + test_ldtrsb_xzr_x2_m1, ldtrsb(XZR, (X2, -1)).unwrap(), "ldtrsb xzr, [x2, -1]"; + test_ldtrsb_xzr_x2_1, ldtrsb(XZR, (X2, 1)).unwrap(), "ldtrsb xzr, [x2, 1]"; + test_ldtrsb_xzr_x2_255, ldtrsb(XZR, (X2, 255)).unwrap(), "ldtrsb xzr, [x2, 255]"; + test_ldtrsb_xzr_x2_m256, ldtrsb(XZR, (X2, -256)).unwrap(), "ldtrsb xzr, [x2, -256]"; + test_ldtrsb_xzr_x2_0, ldtrsb(XZR, (X2, 0)).unwrap(), "ldtrsb xzr, [x2, 0]"; + test_ldtrsb_xzr_sp_m1, ldtrsb(XZR, (SP, -1)).unwrap(), "ldtrsb xzr, [sp, -1]"; + test_ldtrsb_xzr_sp_1, ldtrsb(XZR, (SP, 1)).unwrap(), "ldtrsb xzr, [sp, 1]"; + test_ldtrsb_xzr_sp_255, ldtrsb(XZR, (SP, 255)).unwrap(), "ldtrsb xzr, [sp, 255]"; + test_ldtrsb_xzr_sp_m256, ldtrsb(XZR, (SP, -256)).unwrap(), "ldtrsb xzr, [sp, -256]"; + test_ldtrsb_xzr_sp_0, ldtrsb(XZR, (SP, 0)).unwrap(), "ldtrsb xzr, [sp, 0]"; + test_ldtrsb_wzr_x2_m1, ldtrsb(WZR, (X2, -1)).unwrap(), "ldtrsb wzr, [x2, -1]"; + test_ldtrsb_wzr_x2_1, ldtrsb(WZR, (X2, 1)).unwrap(), "ldtrsb wzr, [x2, 1]"; + test_ldtrsb_wzr_x2_255, ldtrsb(WZR, (X2, 255)).unwrap(), "ldtrsb wzr, [x2, 255]"; + test_ldtrsb_wzr_x2_m256, ldtrsb(WZR, (X2, -256)).unwrap(), "ldtrsb wzr, [x2, -256]"; + test_ldtrsb_wzr_x2_0, ldtrsb(WZR, (X2, 0)).unwrap(), "ldtrsb wzr, [x2, 0]"; + test_ldtrsb_wzr_sp_m1, ldtrsb(WZR, (SP, -1)).unwrap(), "ldtrsb wzr, [sp, -1]"; + test_ldtrsb_wzr_sp_1, ldtrsb(WZR, (SP, 1)).unwrap(), "ldtrsb wzr, [sp, 1]"; + test_ldtrsb_wzr_sp_255, ldtrsb(WZR, (SP, 255)).unwrap(), "ldtrsb wzr, [sp, 255]"; + test_ldtrsb_wzr_sp_m256, ldtrsb(WZR, (SP, -256)).unwrap(), "ldtrsb wzr, [sp, -256]"; + test_ldtrsb_wzr_sp_0, ldtrsb(WZR, (SP, 0)).unwrap(), "ldtrsb wzr, [sp, 0]"; + } +} diff --git a/harm/src/instructions/ldst/unprivileged/ldtrsh.rs b/harm/src/instructions/ldst/unprivileged/ldtrsh.rs new file mode 100644 index 0000000..5b6c463 --- /dev/null +++ b/harm/src/instructions/ldst/unprivileged/ldtrsh.rs @@ -0,0 +1,157 @@ +/* Copyright (C) 2026 Ivan Boldyrev + * + * This document is licensed under the BSD 3-clause license. + */ + +use aarchmrs_instructions::A64::ldst::ldst_unpriv::{ + LDTRSH_32_ldst_unpriv::LDTRSH_32_ldst_unpriv, LDTRSH_64_ldst_unpriv::LDTRSH_64_ldst_unpriv, +}; + +use crate::bits::BitError; +use crate::instructions::RawInstruction; +use crate::register::{IntoReg, RegOrSp64, RegOrZero32, RegOrZero64, Register}; +use crate::sealed::Sealed; + +use super::UnscaledOffset; + +/// A `LDTRSH` instruction with a destination and an address. +pub struct Ldtrsh { + rt: Rt, + addr: Addr, +} + +impl Ldtrsh { + pub fn rt(&self) -> &Rt { + &self.rt + } + + pub fn addr(&self) -> &Addr { + &self.addr + } +} + +impl Sealed for Ldtrsh {} + +/// Defines possible ways to construct a `ldtrsh` instruction. +pub trait MakeLdtrsh: Sealed { + /// Allows defining both fallible and infallible constructors. + type Output; + + fn new(rt: Rt, addr: Addr) -> Self::Output; +} + +define_unscaled_imm_offset_rules!(Ldtrsh, MakeLdtrsh, LDTRSH, RegOrZero64, 64, "ldst_unpriv"); +define_unscaled_imm_offset_rules!(Ldtrsh, MakeLdtrsh, LDTRSH, RegOrZero32, 32, "ldst_unpriv"); + +pub fn ldtrsh( + dst: TargetInp, + addr: AddrInp, +) -> as MakeLdtrsh>::Output +where + Ldtrsh: MakeLdtrsh, +{ + Ldtrsh::new(dst, addr) +} + +#[cfg(test)] +mod tests { + use harm_test_utils::test_cases; + + use super::*; + use crate::instructions::InstructionSeq; + use crate::register::Reg32::*; + use crate::register::Reg64::*; + use RegOrSp64::SP; + use RegOrZero32::WZR; + use RegOrZero64::XZR; + + // 'ldtrsh (x1|w1|xzr|wzr), [(x2|sp), (-1|1|255|-256|0)] + const LDTRSH_DB: &str = " +789ff841 ldtrsh x1, [x2, -1] +78801841 ldtrsh x1, [x2, 1] +788ff841 ldtrsh x1, [x2, 255] +78900841 ldtrsh x1, [x2, -256] +78800841 ldtrsh x1, [x2, 0] +78800841 ldtrsh x1, [x2] +789ffbe1 ldtrsh x1, [sp, -1] +78801be1 ldtrsh x1, [sp, 1] +788ffbe1 ldtrsh x1, [sp, 255] +78900be1 ldtrsh x1, [sp, -256] +78800be1 ldtrsh x1, [sp, 0] +78dff841 ldtrsh w1, [x2, -1] +78c01841 ldtrsh w1, [x2, 1] +78cff841 ldtrsh w1, [x2, 255] +78d00841 ldtrsh w1, [x2, -256] +78c00841 ldtrsh w1, [x2, 0] +78dffbe1 ldtrsh w1, [sp, -1] +78c01be1 ldtrsh w1, [sp, 1] +78cffbe1 ldtrsh w1, [sp, 255] +78d00be1 ldtrsh w1, [sp, -256] +78c00be1 ldtrsh w1, [sp, 0] +789ff85f ldtrsh xzr, [x2, -1] +7880185f ldtrsh xzr, [x2, 1] +788ff85f ldtrsh xzr, [x2, 255] +7890085f ldtrsh xzr, [x2, -256] +7880085f ldtrsh xzr, [x2, 0] +789ffbff ldtrsh xzr, [sp, -1] +78801bff ldtrsh xzr, [sp, 1] +788ffbff ldtrsh xzr, [sp, 255] +78900bff ldtrsh xzr, [sp, -256] +78800bff ldtrsh xzr, [sp, 0] +78dff85f ldtrsh wzr, [x2, -1] +78c0185f ldtrsh wzr, [x2, 1] +78cff85f ldtrsh wzr, [x2, 255] +78d0085f ldtrsh wzr, [x2, -256] +78c0085f ldtrsh wzr, [x2, 0] +78dffbff ldtrsh wzr, [sp, -1] +78c01bff ldtrsh wzr, [sp, 1] +78cffbff ldtrsh wzr, [sp, 255] +78d00bff ldtrsh wzr, [sp, -256] +78c00bff ldtrsh wzr, [sp, 0] +"; + + test_cases! { + LDTRSH_DB, untested_ldtrsh_cases; + test_ldtrsh_x1_x2_m1, ldtrsh(X1, (X2, -1)).unwrap(), "ldtrsh x1, [x2, -1]"; + test_ldtrsh_x1_x2_1, ldtrsh(X1, (X2, 1)).unwrap(), "ldtrsh x1, [x2, 1]"; + test_ldtrsh_x1_x2_255, ldtrsh(X1, (X2, 255)).unwrap(), "ldtrsh x1, [x2, 255]"; + test_ldtrsh_x1_x2_m256, ldtrsh(X1, (X2, -256)).unwrap(), "ldtrsh x1, [x2, -256]"; + test_ldtrsh_x1_x2_0, ldtrsh(X1, (X2, 0)).unwrap(), "ldtrsh x1, [x2, 0]"; + test_ldtrsh_x1_x2_simple, ldtrsh(X1, (X2,)), "ldtrsh x1, [x2]"; + test_ldtrsh_x1_sp_m1, ldtrsh(X1, (SP, -1)).unwrap(), "ldtrsh x1, [sp, -1]"; + test_ldtrsh_x1_sp_1, ldtrsh(X1, (SP, 1)).unwrap(), "ldtrsh x1, [sp, 1]"; + test_ldtrsh_x1_sp_255, ldtrsh(X1, (SP, 255)).unwrap(), "ldtrsh x1, [sp, 255]"; + test_ldtrsh_x1_sp_m256, ldtrsh(X1, (SP, -256)).unwrap(), "ldtrsh x1, [sp, -256]"; + test_ldtrsh_x1_sp_0, ldtrsh(X1, (SP, 0)).unwrap(), "ldtrsh x1, [sp, 0]"; + test_ldtrsh_w1_x2_m1, ldtrsh(W1, (X2, -1)).unwrap(), "ldtrsh w1, [x2, -1]"; + test_ldtrsh_w1_x2_1, ldtrsh(W1, (X2, 1)).unwrap(), "ldtrsh w1, [x2, 1]"; + test_ldtrsh_w1_x2_255, ldtrsh(W1, (X2, 255)).unwrap(), "ldtrsh w1, [x2, 255]"; + test_ldtrsh_w1_x2_m256, ldtrsh(W1, (X2, -256)).unwrap(), "ldtrsh w1, [x2, -256]"; + test_ldtrsh_w1_x2_0, ldtrsh(W1, (X2, 0)).unwrap(), "ldtrsh w1, [x2, 0]"; + test_ldtrsh_w1_sp_m1, ldtrsh(W1, (SP, -1)).unwrap(), "ldtrsh w1, [sp, -1]"; + test_ldtrsh_w1_sp_1, ldtrsh(W1, (SP, 1)).unwrap(), "ldtrsh w1, [sp, 1]"; + test_ldtrsh_w1_sp_255, ldtrsh(W1, (SP, 255)).unwrap(), "ldtrsh w1, [sp, 255]"; + test_ldtrsh_w1_sp_m256, ldtrsh(W1, (SP, -256)).unwrap(), "ldtrsh w1, [sp, -256]"; + test_ldtrsh_w1_sp_0, ldtrsh(W1, (SP, 0)).unwrap(), "ldtrsh w1, [sp, 0]"; + test_ldtrsh_xzr_x2_m1, ldtrsh(XZR, (X2, -1)).unwrap(), "ldtrsh xzr, [x2, -1]"; + test_ldtrsh_xzr_x2_1, ldtrsh(XZR, (X2, 1)).unwrap(), "ldtrsh xzr, [x2, 1]"; + test_ldtrsh_xzr_x2_255, ldtrsh(XZR, (X2, 255)).unwrap(), "ldtrsh xzr, [x2, 255]"; + test_ldtrsh_xzr_x2_m256, ldtrsh(XZR, (X2, -256)).unwrap(), "ldtrsh xzr, [x2, -256]"; + test_ldtrsh_xzr_x2_0, ldtrsh(XZR, (X2, 0)).unwrap(), "ldtrsh xzr, [x2, 0]"; + test_ldtrsh_xzr_sp_m1, ldtrsh(XZR, (SP, -1)).unwrap(), "ldtrsh xzr, [sp, -1]"; + test_ldtrsh_xzr_sp_1, ldtrsh(XZR, (SP, 1)).unwrap(), "ldtrsh xzr, [sp, 1]"; + test_ldtrsh_xzr_sp_255, ldtrsh(XZR, (SP, 255)).unwrap(), "ldtrsh xzr, [sp, 255]"; + test_ldtrsh_xzr_sp_m256, ldtrsh(XZR, (SP, -256)).unwrap(), "ldtrsh xzr, [sp, -256]"; + test_ldtrsh_xzr_sp_0, ldtrsh(XZR, (SP, 0)).unwrap(), "ldtrsh xzr, [sp, 0]"; + test_ldtrsh_wzr_x2_m1, ldtrsh(WZR, (X2, -1)).unwrap(), "ldtrsh wzr, [x2, -1]"; + test_ldtrsh_wzr_x2_1, ldtrsh(WZR, (X2, 1)).unwrap(), "ldtrsh wzr, [x2, 1]"; + test_ldtrsh_wzr_x2_255, ldtrsh(WZR, (X2, 255)).unwrap(), "ldtrsh wzr, [x2, 255]"; + test_ldtrsh_wzr_x2_m256, ldtrsh(WZR, (X2, -256)).unwrap(), "ldtrsh wzr, [x2, -256]"; + test_ldtrsh_wzr_x2_0, ldtrsh(WZR, (X2, 0)).unwrap(), "ldtrsh wzr, [x2, 0]"; + test_ldtrsh_wzr_sp_m1, ldtrsh(WZR, (SP, -1)).unwrap(), "ldtrsh wzr, [sp, -1]"; + test_ldtrsh_wzr_sp_1, ldtrsh(WZR, (SP, 1)).unwrap(), "ldtrsh wzr, [sp, 1]"; + test_ldtrsh_wzr_sp_255, ldtrsh(WZR, (SP, 255)).unwrap(), "ldtrsh wzr, [sp, 255]"; + test_ldtrsh_wzr_sp_m256, ldtrsh(WZR, (SP, -256)).unwrap(), "ldtrsh wzr, [sp, -256]"; + test_ldtrsh_wzr_sp_0, ldtrsh(WZR, (SP, 0)).unwrap(), "ldtrsh wzr, [sp, 0]"; + } +} diff --git a/harm/src/instructions/ldst/unprivileged/ldtrsw.rs b/harm/src/instructions/ldst/unprivileged/ldtrsw.rs new file mode 100644 index 0000000..8a20a1a --- /dev/null +++ b/harm/src/instructions/ldst/unprivileged/ldtrsw.rs @@ -0,0 +1,112 @@ +/* Copyright (C) 2026 Ivan Boldyrev + * + * This document is licensed under the BSD 3-clause license. + */ + +use aarchmrs_instructions::A64::ldst::ldst_unpriv::LDTRSW_64_ldst_unpriv::LDTRSW_64_ldst_unpriv; + +use crate::bits::BitError; +use crate::instructions::RawInstruction; +use crate::register::{IntoReg, RegOrSp64, RegOrZero64, Register}; +use crate::sealed::Sealed; + +use super::UnscaledOffset; + +/// A `LDTRSW` instruction with a destination and an address. +pub struct Ldtrsw { + rt: Rt, + addr: Addr, +} + +impl Ldtrsw { + pub fn rt(&self) -> &Rt { + &self.rt + } + + pub fn addr(&self) -> &Addr { + &self.addr + } +} + +impl Sealed for Ldtrsw {} + +/// Defines possible ways to construct a `ldtrsw` instruction. +pub trait MakeLdtrsw: Sealed { + /// Allows defining both fallible and infallible constructors. + type Output; + + fn new(rt: Rt, addr: Addr) -> Self::Output; +} + +define_unscaled_imm_offset_rules!(Ldtrsw, MakeLdtrsw, LDTRSW, RegOrZero64, 64, "ldst_unpriv"); + +pub fn ldtrsw( + dst: TargetInp, + addr: AddrInp, +) -> as MakeLdtrsw>::Output +where + Ldtrsw: MakeLdtrsw, +{ + Ldtrsw::new(dst, addr) +} + +#[cfg(test)] +mod tests { + use harm_test_utils::test_cases; + + use super::*; + use crate::instructions::InstructionSeq; + use crate::register::Reg64::*; + use RegOrSp64::SP; + use RegOrZero64::XZR; + + // 'ldtrsw (x1|w1|xzr|wzr), [(x2|sp), (-1|1|255|-256|0)] + const LDTRSW_DB: &str = " +b89ff841 ldtrsw x1, [x2, -1] +b8801841 ldtrsw x1, [x2, 1] +b88ff841 ldtrsw x1, [x2, 255] +b8900841 ldtrsw x1, [x2, -256] +b8800841 ldtrsw x1, [x2, 0] +b8800841 ldtrsw x1, [x2] +b89ffbe1 ldtrsw x1, [sp, -1] +b8801be1 ldtrsw x1, [sp, 1] +b88ffbe1 ldtrsw x1, [sp, 255] +b8900be1 ldtrsw x1, [sp, -256] +b8800be1 ldtrsw x1, [sp, 0] +b89ff85f ldtrsw xzr, [x2, -1] +b880185f ldtrsw xzr, [x2, 1] +b88ff85f ldtrsw xzr, [x2, 255] +b890085f ldtrsw xzr, [x2, -256] +b880085f ldtrsw xzr, [x2, 0] +b89ffbff ldtrsw xzr, [sp, -1] +b8801bff ldtrsw xzr, [sp, 1] +b88ffbff ldtrsw xzr, [sp, 255] +b8900bff ldtrsw xzr, [sp, -256] +b8800bff ldtrsw xzr, [sp, 0] +"; + + test_cases! { + LDTRSW_DB, untested_ldtrsw_cases; + test_ldtrsw_x1_x2_m1, ldtrsw(X1, (X2, -1)).unwrap(), "ldtrsw x1, [x2, -1]"; + test_ldtrsw_x1_x2_1, ldtrsw(X1, (X2, 1)).unwrap(), "ldtrsw x1, [x2, 1]"; + test_ldtrsw_x1_x2_255, ldtrsw(X1, (X2, 255)).unwrap(), "ldtrsw x1, [x2, 255]"; + test_ldtrsw_x1_x2_m256, ldtrsw(X1, (X2, -256)).unwrap(), "ldtrsw x1, [x2, -256]"; + test_ldtrsw_x1_x2_0, ldtrsw(X1, (X2, 0)).unwrap(), "ldtrsw x1, [x2, 0]"; + test_ldtrsw_x1_x2_simple, ldtrsw(X1, (X2,)), "ldtrsw x1, [x2]"; + test_ldtrsw_x1_sp_m1, ldtrsw(X1, (SP, -1)).unwrap(), "ldtrsw x1, [sp, -1]"; + test_ldtrsw_x1_sp_1, ldtrsw(X1, (SP, 1)).unwrap(), "ldtrsw x1, [sp, 1]"; + test_ldtrsw_x1_sp_255, ldtrsw(X1, (SP, 255)).unwrap(), "ldtrsw x1, [sp, 255]"; + test_ldtrsw_x1_sp_m256, ldtrsw(X1, (SP, -256)).unwrap(), "ldtrsw x1, [sp, -256]"; + test_ldtrsw_x1_sp_0, ldtrsw(X1, (SP, 0)).unwrap(), "ldtrsw x1, [sp, 0]"; + test_ldtrsw_xzr_x2_m1, ldtrsw(XZR, (X2, -1)).unwrap(), "ldtrsw xzr, [x2, -1]"; + test_ldtrsw_xzr_x2_1, ldtrsw(XZR, (X2, 1)).unwrap(), "ldtrsw xzr, [x2, 1]"; + test_ldtrsw_xzr_x2_255, ldtrsw(XZR, (X2, 255)).unwrap(), "ldtrsw xzr, [x2, 255]"; + test_ldtrsw_xzr_x2_m256, ldtrsw(XZR, (X2, -256)).unwrap(), "ldtrsw xzr, [x2, -256]"; + test_ldtrsw_xzr_x2_0, ldtrsw(XZR, (X2, 0)).unwrap(), "ldtrsw xzr, [x2, 0]"; + test_ldtrsw_xzr_sp_m1, ldtrsw(XZR, (SP, -1)).unwrap(), "ldtrsw xzr, [sp, -1]"; + test_ldtrsw_xzr_sp_1, ldtrsw(XZR, (SP, 1)).unwrap(), "ldtrsw xzr, [sp, 1]"; + test_ldtrsw_xzr_sp_255, ldtrsw(XZR, (SP, 255)).unwrap(), "ldtrsw xzr, [sp, 255]"; + test_ldtrsw_xzr_sp_m256, ldtrsw(XZR, (SP, -256)).unwrap(), "ldtrsw xzr, [sp, -256]"; + test_ldtrsw_xzr_sp_0, ldtrsw(XZR, (SP, 0)).unwrap(), "ldtrsw xzr, [sp, 0]"; + } +} diff --git a/harm/src/instructions/ldst/unprivileged/sttr.rs b/harm/src/instructions/ldst/unprivileged/sttr.rs new file mode 100644 index 0000000..8565482 --- /dev/null +++ b/harm/src/instructions/ldst/unprivileged/sttr.rs @@ -0,0 +1,157 @@ +/* Copyright (C) 2026 Ivan Boldyrev + * + * This document is licensed under the BSD 3-clause license. + */ + +use aarchmrs_instructions::A64::ldst::ldst_unpriv::{ + STTR_32_ldst_unpriv::STTR_32_ldst_unpriv, STTR_64_ldst_unpriv::STTR_64_ldst_unpriv, +}; + +use crate::bits::BitError; +use crate::instructions::RawInstruction; +use crate::register::{IntoReg, RegOrSp64, RegOrZero32, RegOrZero64, Register}; +use crate::sealed::Sealed; + +use super::UnscaledOffset; + +/// A `sttr` instruction with a destination and an address. +pub struct Sttr { + rt: Rt, + addr: Addr, +} + +impl Sttr { + pub fn rt(&self) -> &Rt { + &self.rt + } + + pub fn addr(&self) -> &Addr { + &self.addr + } +} + +impl Sealed for Sttr {} + +/// Defines possible ways to construct a `sttr` instruction. +pub trait MakeSttr: Sealed { + /// Allows defining both fallible and infallible constructors. + type Output; + + fn new(rt: Rt, addr: Addr) -> Self::Output; +} + +define_unscaled_imm_offset_rules!(Sttr, MakeSttr, STTR, RegOrZero64, 64, "ldst_unpriv"); +define_unscaled_imm_offset_rules!(Sttr, MakeSttr, STTR, RegOrZero32, 32, "ldst_unpriv"); + +pub fn sttr( + dst: TargetInp, + addr: AddrInp, +) -> as MakeSttr>::Output +where + Sttr: MakeSttr, +{ + Sttr::new(dst, addr) +} + +#[cfg(test)] +mod tests { + use harm_test_utils::test_cases; + + use super::*; + use crate::instructions::InstructionSeq; + use crate::register::Reg32::*; + use crate::register::Reg64::*; + use RegOrSp64::SP; + use RegOrZero32::WZR; + use RegOrZero64::XZR; + + // 'sttr (x1|w1|xzr|wzr), [(x2|sp), (-1|1|255|-256|0)] + const STTR_DB: &str = " +f81ff841 sttr x1, [x2, -1] +f8001841 sttr x1, [x2, 1] +f80ff841 sttr x1, [x2, 255] +f8100841 sttr x1, [x2, -256] +f8000841 sttr x1, [x2, 0] +f8000841 sttr x1, [x2] +f81ffbe1 sttr x1, [sp, -1] +f8001be1 sttr x1, [sp, 1] +f80ffbe1 sttr x1, [sp, 255] +f8100be1 sttr x1, [sp, -256] +f8000be1 sttr x1, [sp, 0] +b81ff841 sttr w1, [x2, -1] +b8001841 sttr w1, [x2, 1] +b80ff841 sttr w1, [x2, 255] +b8100841 sttr w1, [x2, -256] +b8000841 sttr w1, [x2, 0] +b81ffbe1 sttr w1, [sp, -1] +b8001be1 sttr w1, [sp, 1] +b80ffbe1 sttr w1, [sp, 255] +b8100be1 sttr w1, [sp, -256] +b8000be1 sttr w1, [sp, 0] +f81ff85f sttr xzr, [x2, -1] +f800185f sttr xzr, [x2, 1] +f80ff85f sttr xzr, [x2, 255] +f810085f sttr xzr, [x2, -256] +f800085f sttr xzr, [x2, 0] +f81ffbff sttr xzr, [sp, -1] +f8001bff sttr xzr, [sp, 1] +f80ffbff sttr xzr, [sp, 255] +f8100bff sttr xzr, [sp, -256] +f8000bff sttr xzr, [sp, 0] +b81ff85f sttr wzr, [x2, -1] +b800185f sttr wzr, [x2, 1] +b80ff85f sttr wzr, [x2, 255] +b810085f sttr wzr, [x2, -256] +b800085f sttr wzr, [x2, 0] +b81ffbff sttr wzr, [sp, -1] +b8001bff sttr wzr, [sp, 1] +b80ffbff sttr wzr, [sp, 255] +b8100bff sttr wzr, [sp, -256] +b8000bff sttr wzr, [sp, 0] +"; + + test_cases! { + STTR_DB, untested_sttr_cases; + test_sttr_x1_x2_m1, sttr(X1, (X2, -1)).unwrap(), "sttr x1, [x2, -1]"; + test_sttr_x1_x2_1, sttr(X1, (X2, 1)).unwrap(), "sttr x1, [x2, 1]"; + test_sttr_x1_x2_255, sttr(X1, (X2, 255)).unwrap(), "sttr x1, [x2, 255]"; + test_sttr_x1_x2_m256, sttr(X1, (X2, -256)).unwrap(), "sttr x1, [x2, -256]"; + test_sttr_x1_x2_0, sttr(X1, (X2, 0)).unwrap(), "sttr x1, [x2, 0]"; + test_sttr_x1_x2_simple, sttr(X1, (X2,)), "sttr x1, [x2]"; + test_sttr_x1_sp_m1, sttr(X1, (SP, -1)).unwrap(), "sttr x1, [sp, -1]"; + test_sttr_x1_sp_1, sttr(X1, (SP, 1)).unwrap(), "sttr x1, [sp, 1]"; + test_sttr_x1_sp_255, sttr(X1, (SP, 255)).unwrap(), "sttr x1, [sp, 255]"; + test_sttr_x1_sp_m256, sttr(X1, (SP, -256)).unwrap(), "sttr x1, [sp, -256]"; + test_sttr_x1_sp_0, sttr(X1, (SP, 0)).unwrap(), "sttr x1, [sp, 0]"; + test_sttr_w1_x2_m1, sttr(W1, (X2, -1)).unwrap(), "sttr w1, [x2, -1]"; + test_sttr_w1_x2_1, sttr(W1, (X2, 1)).unwrap(), "sttr w1, [x2, 1]"; + test_sttr_w1_x2_255, sttr(W1, (X2, 255)).unwrap(), "sttr w1, [x2, 255]"; + test_sttr_w1_x2_m256, sttr(W1, (X2, -256)).unwrap(), "sttr w1, [x2, -256]"; + test_sttr_w1_x2_0, sttr(W1, (X2, 0)).unwrap(), "sttr w1, [x2, 0]"; + test_sttr_w1_sp_m1, sttr(W1, (SP, -1)).unwrap(), "sttr w1, [sp, -1]"; + test_sttr_w1_sp_1, sttr(W1, (SP, 1)).unwrap(), "sttr w1, [sp, 1]"; + test_sttr_w1_sp_255, sttr(W1, (SP, 255)).unwrap(), "sttr w1, [sp, 255]"; + test_sttr_w1_sp_m256, sttr(W1, (SP, -256)).unwrap(), "sttr w1, [sp, -256]"; + test_sttr_w1_sp_0, sttr(W1, (SP, 0)).unwrap(), "sttr w1, [sp, 0]"; + test_sttr_xzr_x2_m1, sttr(XZR, (X2, -1)).unwrap(), "sttr xzr, [x2, -1]"; + test_sttr_xzr_x2_1, sttr(XZR, (X2, 1)).unwrap(), "sttr xzr, [x2, 1]"; + test_sttr_xzr_x2_255, sttr(XZR, (X2, 255)).unwrap(), "sttr xzr, [x2, 255]"; + test_sttr_xzr_x2_m256, sttr(XZR, (X2, -256)).unwrap(), "sttr xzr, [x2, -256]"; + test_sttr_xzr_x2_0, sttr(XZR, (X2, 0)).unwrap(), "sttr xzr, [x2, 0]"; + test_sttr_xzr_sp_m1, sttr(XZR, (SP, -1)).unwrap(), "sttr xzr, [sp, -1]"; + test_sttr_xzr_sp_1, sttr(XZR, (SP, 1)).unwrap(), "sttr xzr, [sp, 1]"; + test_sttr_xzr_sp_255, sttr(XZR, (SP, 255)).unwrap(), "sttr xzr, [sp, 255]"; + test_sttr_xzr_sp_m256, sttr(XZR, (SP, -256)).unwrap(), "sttr xzr, [sp, -256]"; + test_sttr_xzr_sp_0, sttr(XZR, (SP, 0)).unwrap(), "sttr xzr, [sp, 0]"; + test_sttr_wzr_x2_m1, sttr(WZR, (X2, -1)).unwrap(), "sttr wzr, [x2, -1]"; + test_sttr_wzr_x2_1, sttr(WZR, (X2, 1)).unwrap(), "sttr wzr, [x2, 1]"; + test_sttr_wzr_x2_255, sttr(WZR, (X2, 255)).unwrap(), "sttr wzr, [x2, 255]"; + test_sttr_wzr_x2_m256, sttr(WZR, (X2, -256)).unwrap(), "sttr wzr, [x2, -256]"; + test_sttr_wzr_x2_0, sttr(WZR, (X2, 0)).unwrap(), "sttr wzr, [x2, 0]"; + test_sttr_wzr_sp_m1, sttr(WZR, (SP, -1)).unwrap(), "sttr wzr, [sp, -1]"; + test_sttr_wzr_sp_1, sttr(WZR, (SP, 1)).unwrap(), "sttr wzr, [sp, 1]"; + test_sttr_wzr_sp_255, sttr(WZR, (SP, 255)).unwrap(), "sttr wzr, [sp, 255]"; + test_sttr_wzr_sp_m256, sttr(WZR, (SP, -256)).unwrap(), "sttr wzr, [sp, -256]"; + test_sttr_wzr_sp_0, sttr(WZR, (SP, 0)).unwrap(), "sttr wzr, [sp, 0]"; + } +} diff --git a/harm/src/instructions/ldst/unprivileged/sttrb.rs b/harm/src/instructions/ldst/unprivileged/sttrb.rs new file mode 100644 index 0000000..747a272 --- /dev/null +++ b/harm/src/instructions/ldst/unprivileged/sttrb.rs @@ -0,0 +1,112 @@ +/* Copyright (C) 2026 Ivan Boldyrev + * + * This document is licensed under the BSD 3-clause license. + */ + +use aarchmrs_instructions::A64::ldst::ldst_unpriv::STTRB_32_ldst_unpriv::STTRB_32_ldst_unpriv; + +use crate::bits::BitError; +use crate::instructions::RawInstruction; +use crate::register::{IntoReg, RegOrSp64, RegOrZero32, Register}; +use crate::sealed::Sealed; + +use super::UnscaledOffset; + +/// A `STTRB` instruction with a destination and an address. +pub struct Sttrb { + rt: Rt, + addr: Addr, +} + +impl Sttrb { + pub fn rt(&self) -> &Rt { + &self.rt + } + + pub fn addr(&self) -> &Addr { + &self.addr + } +} + +impl Sealed for Sttrb {} + +/// Defines possible ways to construct a `sttrb` instruction. +pub trait MakeSttrb: Sealed { + /// Allows defining both fallible and infallible constructors. + type Output; + + fn new(rt: Rt, addr: Addr) -> Self::Output; +} + +define_unscaled_imm_offset_rules!(Sttrb, MakeSttrb, STTRB, RegOrZero32, 32, "ldst_unpriv"); + +pub fn sttrb( + dst: TargetInp, + addr: AddrInp, +) -> as MakeSttrb>::Output +where + Sttrb: MakeSttrb, +{ + Sttrb::new(dst, addr) +} + +#[cfg(test)] +mod tests { + use harm_test_utils::test_cases; + + use super::*; + use crate::instructions::InstructionSeq; + use crate::register::Reg32::*; + use crate::register::Reg64::*; + use RegOrSp64::SP; + use RegOrZero32::WZR; + + const STTRB_DB: &str = " +381ff841 sttrb w1, [x2, -1] +38001841 sttrb w1, [x2, 1] +380ff841 sttrb w1, [x2, 255] +38100841 sttrb w1, [x2, -256] +38000841 sttrb w1, [x2, 0] +38000841 sttrb w1, [x2] +381ffbe1 sttrb w1, [sp, -1] +38001be1 sttrb w1, [sp, 1] +380ffbe1 sttrb w1, [sp, 255] +38100be1 sttrb w1, [sp, -256] +38000be1 sttrb w1, [sp, 0] +381ff85f sttrb wzr, [x2, -1] +3800185f sttrb wzr, [x2, 1] +380ff85f sttrb wzr, [x2, 255] +3810085f sttrb wzr, [x2, -256] +3800085f sttrb wzr, [x2, 0] +381ffbff sttrb wzr, [sp, -1] +38001bff sttrb wzr, [sp, 1] +380ffbff sttrb wzr, [sp, 255] +38100bff sttrb wzr, [sp, -256] +38000bff sttrb wzr, [sp, 0] +"; + + test_cases! { + STTRB_DB, untested_sttrb_cases; + test_sttrb_w1_x2_m1, sttrb(W1, (X2, -1)).unwrap(), "sttrb w1, [x2, -1]"; + test_sttrb_w1_x2_1, sttrb(W1, (X2, 1)).unwrap(), "sttrb w1, [x2, 1]"; + test_sttrb_w1_x2_255, sttrb(W1, (X2, 255)).unwrap(), "sttrb w1, [x2, 255]"; + test_sttrb_w1_x2_m256, sttrb(W1, (X2, -256)).unwrap(), "sttrb w1, [x2, -256]"; + test_sttrb_w1_x2_0, sttrb(W1, (X2, 0)).unwrap(), "sttrb w1, [x2, 0]"; + test_sttrb_w1_x2_simple, sttrb(W1, (X2,)), "sttrb w1, [x2]"; + test_sttrb_w1_sp_m1, sttrb(W1, (SP, -1)).unwrap(), "sttrb w1, [sp, -1]"; + test_sttrb_w1_sp_1, sttrb(W1, (SP, 1)).unwrap(), "sttrb w1, [sp, 1]"; + test_sttrb_w1_sp_255, sttrb(W1, (SP, 255)).unwrap(), "sttrb w1, [sp, 255]"; + test_sttrb_w1_sp_m256, sttrb(W1, (SP, -256)).unwrap(), "sttrb w1, [sp, -256]"; + test_sttrb_w1_sp_0, sttrb(W1, (SP, 0)).unwrap(), "sttrb w1, [sp, 0]"; + test_sttrb_wzr_x2_m1, sttrb(WZR, (X2, -1)).unwrap(), "sttrb wzr, [x2, -1]"; + test_sttrb_wzr_x2_1, sttrb(WZR, (X2, 1)).unwrap(), "sttrb wzr, [x2, 1]"; + test_sttrb_wzr_x2_255, sttrb(WZR, (X2, 255)).unwrap(), "sttrb wzr, [x2, 255]"; + test_sttrb_wzr_x2_m256, sttrb(WZR, (X2, -256)).unwrap(), "sttrb wzr, [x2, -256]"; + test_sttrb_wzr_x2_0, sttrb(WZR, (X2, 0)).unwrap(), "sttrb wzr, [x2, 0]"; + test_sttrb_wzr_sp_m1, sttrb(WZR, (SP, -1)).unwrap(), "sttrb wzr, [sp, -1]"; + test_sttrb_wzr_sp_1, sttrb(WZR, (SP, 1)).unwrap(), "sttrb wzr, [sp, 1]"; + test_sttrb_wzr_sp_255, sttrb(WZR, (SP, 255)).unwrap(), "sttrb wzr, [sp, 255]"; + test_sttrb_wzr_sp_m256, sttrb(WZR, (SP, -256)).unwrap(), "sttrb wzr, [sp, -256]"; + test_sttrb_wzr_sp_0, sttrb(WZR, (SP, 0)).unwrap(), "sttrb wzr, [sp, 0]"; + } +} diff --git a/harm/src/instructions/ldst/unprivileged/sttrh.rs b/harm/src/instructions/ldst/unprivileged/sttrh.rs new file mode 100644 index 0000000..567c97f --- /dev/null +++ b/harm/src/instructions/ldst/unprivileged/sttrh.rs @@ -0,0 +1,112 @@ +/* Copyright (C) 2026 Ivan Boldyrev + * + * This document is licensed under the BSD 3-clause license. + */ + +use aarchmrs_instructions::A64::ldst::ldst_unpriv::STTRH_32_ldst_unpriv::STTRH_32_ldst_unpriv; + +use crate::bits::BitError; +use crate::instructions::RawInstruction; +use crate::register::{IntoReg, RegOrSp64, RegOrZero32, Register}; +use crate::sealed::Sealed; + +use super::UnscaledOffset; + +/// A `STTRH` instruction with a destination and an address. +pub struct Sttrh { + rt: Rt, + addr: Addr, +} + +impl Sttrh { + pub fn rt(&self) -> &Rt { + &self.rt + } + + pub fn addr(&self) -> &Addr { + &self.addr + } +} + +impl Sealed for Sttrh {} + +/// Defines possible ways to construct a `sttrh` instruction. +pub trait MakeSttrh: Sealed { + /// Allows defining both fallible and infallible constructors. + type Output; + + fn new(rt: Rt, addr: Addr) -> Self::Output; +} + +define_unscaled_imm_offset_rules!(Sttrh, MakeSttrh, STTRH, RegOrZero32, 32, "ldst_unpriv"); + +pub fn sttrh( + dst: TargetInp, + addr: AddrInp, +) -> as MakeSttrh>::Output +where + Sttrh: MakeSttrh, +{ + Sttrh::new(dst, addr) +} + +#[cfg(test)] +mod tests { + use harm_test_utils::test_cases; + + use super::*; + use crate::instructions::InstructionSeq; + use crate::register::Reg32::*; + use crate::register::Reg64::*; + use RegOrSp64::SP; + use RegOrZero32::WZR; + + const STTRH_DB: &str = " +781ff841 sttrh w1, [x2, -1] +78001841 sttrh w1, [x2, 1] +780ff841 sttrh w1, [x2, 255] +78100841 sttrh w1, [x2, -256] +78000841 sttrh w1, [x2, 0] +78000841 sttrh w1, [x2] +781ffbe1 sttrh w1, [sp, -1] +78001be1 sttrh w1, [sp, 1] +780ffbe1 sttrh w1, [sp, 255] +78100be1 sttrh w1, [sp, -256] +78000be1 sttrh w1, [sp, 0] +781ff85f sttrh wzr, [x2, -1] +7800185f sttrh wzr, [x2, 1] +780ff85f sttrh wzr, [x2, 255] +7810085f sttrh wzr, [x2, -256] +7800085f sttrh wzr, [x2, 0] +781ffbff sttrh wzr, [sp, -1] +78001bff sttrh wzr, [sp, 1] +780ffbff sttrh wzr, [sp, 255] +78100bff sttrh wzr, [sp, -256] +78000bff sttrh wzr, [sp, 0] +"; + + test_cases! { + STTRH_DB, untested_sttrh_cases; + test_sttrh_w1_x2_m1, sttrh(W1, (X2, -1)).unwrap(), "sttrh w1, [x2, -1]"; + test_sttrh_w1_x2_1, sttrh(W1, (X2, 1)).unwrap(), "sttrh w1, [x2, 1]"; + test_sttrh_w1_x2_255, sttrh(W1, (X2, 255)).unwrap(), "sttrh w1, [x2, 255]"; + test_sttrh_w1_x2_m256, sttrh(W1, (X2, -256)).unwrap(), "sttrh w1, [x2, -256]"; + test_sttrh_w1_x2_0, sttrh(W1, (X2, 0)).unwrap(), "sttrh w1, [x2, 0]"; + test_sttrh_w1_x2_simple, sttrh(W1, (X2,)), "sttrh w1, [x2]"; + test_sttrh_w1_sp_m1, sttrh(W1, (SP, -1)).unwrap(), "sttrh w1, [sp, -1]"; + test_sttrh_w1_sp_1, sttrh(W1, (SP, 1)).unwrap(), "sttrh w1, [sp, 1]"; + test_sttrh_w1_sp_255, sttrh(W1, (SP, 255)).unwrap(), "sttrh w1, [sp, 255]"; + test_sttrh_w1_sp_m256, sttrh(W1, (SP, -256)).unwrap(), "sttrh w1, [sp, -256]"; + test_sttrh_w1_sp_0, sttrh(W1, (SP, 0)).unwrap(), "sttrh w1, [sp, 0]"; + test_sttrh_wzr_x2_m1, sttrh(WZR, (X2, -1)).unwrap(), "sttrh wzr, [x2, -1]"; + test_sttrh_wzr_x2_1, sttrh(WZR, (X2, 1)).unwrap(), "sttrh wzr, [x2, 1]"; + test_sttrh_wzr_x2_255, sttrh(WZR, (X2, 255)).unwrap(), "sttrh wzr, [x2, 255]"; + test_sttrh_wzr_x2_m256, sttrh(WZR, (X2, -256)).unwrap(), "sttrh wzr, [x2, -256]"; + test_sttrh_wzr_x2_0, sttrh(WZR, (X2, 0)).unwrap(), "sttrh wzr, [x2, 0]"; + test_sttrh_wzr_sp_m1, sttrh(WZR, (SP, -1)).unwrap(), "sttrh wzr, [sp, -1]"; + test_sttrh_wzr_sp_1, sttrh(WZR, (SP, 1)).unwrap(), "sttrh wzr, [sp, 1]"; + test_sttrh_wzr_sp_255, sttrh(WZR, (SP, 255)).unwrap(), "sttrh wzr, [sp, 255]"; + test_sttrh_wzr_sp_m256, sttrh(WZR, (SP, -256)).unwrap(), "sttrh wzr, [sp, -256]"; + test_sttrh_wzr_sp_0, sttrh(WZR, (SP, 0)).unwrap(), "sttrh wzr, [sp, 0]"; + } +}