diff --git a/harm/src/instructions/ldst.rs b/harm/src/instructions/ldst.rs index 4dd6c63..d3f6ec8 100644 --- a/harm/src/instructions/ldst.rs +++ b/harm/src/instructions/ldst.rs @@ -9,6 +9,8 @@ pub(crate) mod macros; mod increment; mod shift_extend; +mod args; +mod exclusive; mod ldnp; mod ldp; mod ldpsw; @@ -34,6 +36,7 @@ mod sturb; mod sturh; mod unprivileged; +pub use self::exclusive::*; pub use self::increment::*; pub use self::ldnp::*; pub use self::ldp::*; diff --git a/harm/src/instructions/ldst/args.rs b/harm/src/instructions/ldst/args.rs new file mode 100644 index 0000000..0d2686d --- /dev/null +++ b/harm/src/instructions/ldst/args.rs @@ -0,0 +1,6 @@ +/* Copyright (C) 2026 Ivan Boldyrev + * + * This document is licensed under the BSD 3-clause license. + */ + +pub(crate) mod reg_addr; diff --git a/harm/src/instructions/ldst/args/reg_addr.rs b/harm/src/instructions/ldst/args/reg_addr.rs new file mode 100644 index 0000000..20d2268 --- /dev/null +++ b/harm/src/instructions/ldst/args/reg_addr.rs @@ -0,0 +1,83 @@ +use crate::{ + register::{IntoReg, RegOrSp64, RegOrZero32, RegOrZero64}, + sealed::Sealed, +}; + +#[derive(Debug, Clone, Copy)] +pub struct RegAddr { + pub reg: Reg, + pub addr: RegOrSp64, +} + +impl Sealed for RegAddr {} +impl Sealed for RegAddr {} + +pub trait MakeRegAddr: Sealed { + fn new(reg: RegInp, addr: AddrRegInp) -> Self; +} + +impl, Addr: IntoReg> MakeRegAddr + for RegAddr +{ + fn new(reg: Reg, addr: Addr) -> Self { + Self { + reg: reg.into_reg(), + addr: addr.into_reg(), + } + } +} + +impl, Addr: IntoReg> MakeRegAddr + for RegAddr +{ + fn new(reg: Reg, addr: Addr) -> Self { + Self { + reg: reg.into_reg(), + addr: addr.into_reg(), + } + } +} + +#[derive(Debug, Clone, Copy)] +pub struct Reg32Addr { + pub reg: RegOrZero32, + pub addr: RegOrSp64, +} + +impl Sealed for Reg32Addr {} + +pub trait MakeReg32Addr: Sealed { + fn new(reg: Reg32Inp, addr: AddrRegInp) -> Self; +} + +impl, Addr: IntoReg> MakeReg32Addr for Reg32Addr { + fn new(reg: Reg, addr: Addr) -> Self { + Self { + reg: reg.into_reg(), + addr: addr.into_reg(), + } + } +} + +#[allow(dead_code)] +#[derive(Debug, Clone, Copy)] +pub struct Reg64Addr { + pub reg: RegOrZero64, + pub addr: RegOrSp64, +} + +impl Sealed for Reg64Addr {} + +#[allow(dead_code)] +pub trait MakeReg64Addr: Sealed { + fn new(reg: Reg64Inp, addr: AddrRegInp) -> Self; +} + +impl, Addr: IntoReg> MakeReg64Addr for Reg64Addr { + fn new(reg: Reg, addr: Addr) -> Self { + Self { + reg: reg.into_reg(), + addr: addr.into_reg(), + } + } +} diff --git a/harm/src/instructions/ldst/exclusive.rs b/harm/src/instructions/ldst/exclusive.rs new file mode 100644 index 0000000..bea65a5 --- /dev/null +++ b/harm/src/instructions/ldst/exclusive.rs @@ -0,0 +1,25 @@ +/* Copyright (C) 2026 Ivan Boldyrev + * + * This document is licensed under the BSD 3-clause license. + */ + +//! Load-Exclusive/Store-Exclusive (`LDXR`, `STXR`, `LDXRB`, etc.) instructions. + +pub mod exclusive_args; +mod ldxp; +mod ldxr; +mod ldxrb; +mod ldxrh; +mod stxp; +mod stxr; +mod stxrb; +mod stxrh; + +pub use self::ldxp::*; +pub use self::ldxr::*; +pub use self::ldxrb::*; +pub use self::ldxrh::*; +pub use self::stxp::*; +pub use self::stxr::*; +pub use self::stxrb::*; +pub use self::stxrh::*; diff --git a/harm/src/instructions/ldst/exclusive/exclusive_args.rs b/harm/src/instructions/ldst/exclusive/exclusive_args.rs new file mode 100644 index 0000000..968f49e --- /dev/null +++ b/harm/src/instructions/ldst/exclusive/exclusive_args.rs @@ -0,0 +1,173 @@ +use crate::{ + register::{IntoReg, RegOrSp64, RegOrZero32, RegOrZero64}, + sealed::Sealed, +}; + +pub struct ExclusiveStoreArgs { + pub status: RegOrZero32, + pub reg: Reg, + pub addr: RegOrSp64, +} + +impl Sealed for ExclusiveStoreArgs {} +impl Sealed for ExclusiveStoreArgs {} + +pub trait MakeExclusiveStoreArgs: Sealed { + fn new(status: StatusInp, reg: RegInp, addr: AddrRegInp) -> Self; +} + +impl MakeExclusiveStoreArgs + for ExclusiveStoreArgs +where + StatusInp: IntoReg, + RegInp: IntoReg, + AddrRegInp: IntoReg, +{ + fn new(status: StatusInp, reg: RegInp, addr: AddrRegInp) -> Self { + Self { + status: status.into_reg(), + reg: reg.into_reg(), + addr: addr.into_reg(), + } + } +} + +impl MakeExclusiveStoreArgs + for ExclusiveStoreArgs +where + StatusInp: IntoReg, + RegInp: IntoReg, + AddrRegInp: IntoReg, +{ + fn new(status: StatusInp, reg: RegInp, addr: AddrRegInp) -> Self { + Self { + status: status.into_reg(), + reg: reg.into_reg(), + addr: addr.into_reg(), + } + } +} +pub struct ExclusiveStore32Args { + pub status: RegOrZero32, + pub reg: RegOrZero32, + pub addr: RegOrSp64, +} + +impl Sealed for ExclusiveStore32Args {} + +pub trait MakeExclusiveStore32Args: Sealed { + fn new(status: StatusInp, reg: RegInp, addr: AddrRegInp) -> Self; +} + +impl MakeExclusiveStore32Args + for ExclusiveStore32Args +where + StatusInp: IntoReg, + RegInp: IntoReg, + AddrRegInp: IntoReg, +{ + fn new(status: StatusInp, reg: RegInp, addr: AddrRegInp) -> Self { + Self { + status: status.into_reg(), + reg: reg.into_reg(), + addr: addr.into_reg(), + } + } +} + +pub struct ExclusivePairLoadArgs { + pub reg1: Reg, + pub reg2: Reg, + pub addr: RegOrSp64, +} + +impl Sealed for ExclusivePairLoadArgs {} +impl Sealed for ExclusivePairLoadArgs {} + +pub trait MakeExclusivePairLoadArgs: Sealed { + fn new(reg1: Reg1Inp, reg2: Reg2Inp, addr: AddrRegInp) -> Self; +} + +impl MakeExclusivePairLoadArgs + for ExclusivePairLoadArgs +where + Reg1Inp: IntoReg, + Reg2Inp: IntoReg, + AddrRegInp: IntoReg, +{ + fn new(reg1: Reg1Inp, reg2: Reg2Inp, addr: AddrRegInp) -> Self { + Self { + reg1: reg1.into_reg(), + reg2: reg2.into_reg(), + addr: addr.into_reg(), + } + } +} + +impl MakeExclusivePairLoadArgs + for ExclusivePairLoadArgs +where + Reg1Inp: IntoReg, + Reg2Inp: IntoReg, + AddrRegInp: IntoReg, +{ + fn new(reg1: Reg1Inp, reg2: Reg2Inp, addr: AddrRegInp) -> Self { + Self { + reg1: reg1.into_reg(), + reg2: reg2.into_reg(), + addr: addr.into_reg(), + } + } +} + +pub struct ExclusivePairStoreArgs { + pub status: RegOrZero32, + pub reg1: Reg, + pub reg2: Reg, + pub addr: RegOrSp64, +} + +impl Sealed for ExclusivePairStoreArgs {} +impl Sealed for ExclusivePairStoreArgs {} + +pub trait MakeExclusivePairStoreArgs: Sealed { + fn new(status: StatusInp, reg1: Reg1Inp, reg2: Reg2Inp, addr: AddrRegInp) -> Self; +} + +impl + MakeExclusivePairStoreArgs + for ExclusivePairStoreArgs +where + StatusInp: IntoReg, + Reg1Inp: IntoReg, + Reg2Inp: IntoReg, + AddrRegInp: IntoReg, +{ + fn new(status: StatusInp, reg1: Reg1Inp, reg2: Reg2Inp, addr: AddrRegInp) -> Self { + Self { + status: status.into_reg(), + reg1: reg1.into_reg(), + reg2: reg2.into_reg(), + addr: addr.into_reg(), + } + } +} + +impl + MakeExclusivePairStoreArgs + for ExclusivePairStoreArgs +where + StatusInp: IntoReg, + Reg1Inp: IntoReg, + Reg2Inp: IntoReg, + AddrRegInp: IntoReg, +{ + fn new(status: StatusInp, reg1: Reg1Inp, reg2: Reg2Inp, addr: AddrRegInp) -> Self { + Self { + status: status.into_reg(), + reg1: reg1.into_reg(), + reg2: reg2.into_reg(), + addr: addr.into_reg(), + } + } +} diff --git a/harm/src/instructions/ldst/exclusive/ldxp.rs b/harm/src/instructions/ldst/exclusive/ldxp.rs new file mode 100644 index 0000000..0b6c086 --- /dev/null +++ b/harm/src/instructions/ldst/exclusive/ldxp.rs @@ -0,0 +1,87 @@ +/* Copyright (C) 2026 Ivan Boldyrev + * + * This document is licensed under the BSD 3-clause license. + */ + +use crate::register::{RegOrSp64, RegOrZero32, RegOrZero64, Register}; +use aarchmrs_instructions::A64::ldst::ldstexclp::{ + LDXP_LP32_ldstexclp::LDXP_LP32_ldstexclp, LDXP_LP64_ldstexclp::LDXP_LP64_ldstexclp, +}; + +use crate::instructions::RawInstruction; + +use super::exclusive_args::{ExclusivePairLoadArgs, MakeExclusivePairLoadArgs}; + +/// A `ldxp` instruction with a destination and an address. +pub struct Ldxp { + args: ExclusivePairLoadArgs, +} + +impl Ldxp { + pub fn rt1(&self) -> Rt { + self.args.reg1 + } + + pub fn rt2(&self) -> Rt { + self.args.reg2 + } + + pub fn addr(&self) -> RegOrSp64 { + self.args.addr + } +} + +pub fn ldxp( + r1: Reg1Inp, + r2: Reg2Inp, + addr: AddrRegInp, +) -> Ldxp +where + ExclusivePairLoadArgs: MakeExclusivePairLoadArgs, +{ + Ldxp { + args: as MakeExclusivePairLoadArgs< + Reg1Inp, + Reg2Inp, + AddrRegInp, + >>::new(r1, r2, addr), + } +} + +impl RawInstruction for Ldxp { + fn to_code(&self) -> aarchmrs_types::InstructionCode { + LDXP_LP32_ldstexclp(self.rt2().index(), self.addr().index(), self.rt1().index()) + } +} + +impl RawInstruction for Ldxp { + fn to_code(&self) -> aarchmrs_types::InstructionCode { + LDXP_LP64_ldstexclp(self.rt2().index(), self.addr().index(), self.rt1().index()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::instructions::InstructionSeq; + use crate::register::Reg32::*; + use crate::register::Reg64::*; + use crate::register::RegOrSp64::SP; + use crate::register::RegOrZero32::WZR; + use crate::register::RegOrZero64::XZR; + use harm_test_utils::test_cases; + + const LDXP_TEST_DB: &str = " +887f0903 ldxp w3, w2, [x8] +c87f0903 ldxp x3, x2, [x8] +887f13ff ldxp wzr, w4, [sp] +c87f13ff ldxp xzr, x4, [sp] +"; + test_cases! { + LDXP_TEST_DB, untested_ldxp_test_db; + test_ldxp_w2_x8, ldxp(W3, W2, X8), "ldxp w3, w2, [x8]"; + test_ldxp_x2_x8, ldxp(X3, X2, X8), "ldxp x3, x2, [x8]"; + test_ldxp_wzr_sp, ldxp(WZR, W4, SP), "ldxp wzr, w4, [sp]"; + test_ldxp_xzr_sp, ldxp(XZR, X4, SP), "ldxp xzr, x4, [sp]"; + } +} diff --git a/harm/src/instructions/ldst/exclusive/ldxr.rs b/harm/src/instructions/ldst/exclusive/ldxr.rs new file mode 100644 index 0000000..45e9230 --- /dev/null +++ b/harm/src/instructions/ldst/exclusive/ldxr.rs @@ -0,0 +1,75 @@ +/* Copyright (C) 2026 Ivan Boldyrev + * + * This document is licensed under the BSD 3-clause license. + */ + +use crate::register::{RegOrSp64, RegOrZero32, RegOrZero64, Register}; +use aarchmrs_instructions::A64::ldst::ldstexclr::{ + LDXR_LR32_ldstexclr::LDXR_LR32_ldstexclr, LDXR_LR64_ldstexclr::LDXR_LR64_ldstexclr, +}; + +use crate::instructions::RawInstruction; + +use super::super::args::reg_addr::{MakeRegAddr, RegAddr}; + +/// A `ldxr` instruction with a destination and an address. +pub struct Ldxr { + args: RegAddr, +} + +impl Ldxr { + pub fn rt(&self) -> Rt { + self.args.reg + } + + pub fn addr(&self) -> RegOrSp64 { + self.args.addr + } +} + +pub fn ldxr(dst: TargetInp, addr: AddrInp) -> Ldxr +where + RegAddr: MakeRegAddr, +{ + Ldxr { + args: as MakeRegAddr>::new(dst, addr), + } +} + +impl RawInstruction for Ldxr { + fn to_code(&self) -> aarchmrs_types::InstructionCode { + LDXR_LR32_ldstexclr(self.addr().index(), self.rt().index()) + } +} + +impl RawInstruction for Ldxr { + fn to_code(&self) -> aarchmrs_types::InstructionCode { + LDXR_LR64_ldstexclr(self.addr().index(), self.rt().index()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::instructions::InstructionSeq; + use crate::register::Reg32::*; + use crate::register::Reg64::*; + use crate::register::RegOrSp64::SP; + use crate::register::RegOrZero32::WZR; + use crate::register::RegOrZero64::XZR; + use harm_test_utils::test_cases; + + const LDXR_TEST_DB: &str = " +885f7d02 ldxr w2, [x8] +c85f7d02 ldxr x2, [x8] +885f7fff ldxr wzr, [sp] +c85f7fff ldxr xzr, [sp] +"; + test_cases! { + LDXR_TEST_DB, untested_ldxr_test_db; + test_ldxr_w2_x8, ldxr(W2, X8), "ldxr w2, [x8]"; + test_ldxr_x2_x8, ldxr(X2, X8), "ldxr x2, [x8]"; + test_ldxr_wzr_sp, ldxr(WZR, SP), "ldxr wzr, [sp]"; + test_ldxr_xzr_sp, ldxr(XZR, SP), "ldxr xzr, [sp]"; + } +} diff --git a/harm/src/instructions/ldst/exclusive/ldxrb.rs b/harm/src/instructions/ldst/exclusive/ldxrb.rs new file mode 100644 index 0000000..968f6ad --- /dev/null +++ b/harm/src/instructions/ldst/exclusive/ldxrb.rs @@ -0,0 +1,66 @@ +/* Copyright (C) 2026 Ivan Boldyrev + * + * This document is licensed under the BSD 3-clause license. + */ + +use crate::register::{RegOrSp64, RegOrZero32, Register as _}; +use aarchmrs_instructions::A64::ldst::ldstexclr::LDXRB_LR32_ldstexclr::LDXRB_LR32_ldstexclr; + +use crate::instructions::RawInstruction; + +use super::super::args::reg_addr::{MakeReg32Addr, Reg32Addr}; + +/// A `ldxrb` instruction with a destination and an address. +#[derive(Debug, Clone, Copy)] +pub struct Ldxrb { + args: Reg32Addr, +} + +impl Ldxrb { + #[inline] + pub fn rt(&self) -> RegOrZero32 { + self.args.reg + } + + #[inline] + pub fn addr(&self) -> RegOrSp64 { + self.args.addr + } +} + +pub fn ldxrb(dst: TargetInp, addr: AddrInp) -> Ldxrb +where + Reg32Addr: MakeReg32Addr, +{ + Ldxrb { + args: >::new(dst, addr), + } +} + +impl RawInstruction for Ldxrb { + #[inline] + fn to_code(&self) -> aarchmrs_types::InstructionCode { + LDXRB_LR32_ldstexclr(self.addr().index(), self.rt().index()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::instructions::InstructionSeq; + use crate::register::Reg32::*; + use crate::register::Reg64::*; + use crate::register::RegOrSp64::SP; + use crate::register::RegOrZero32::WZR; + use harm_test_utils::test_cases; + + const LDXRB_TEST_DB: &str = " +085f7d02 ldxrb w2, [x8] +085f7fff ldxrb wzr, [sp] +"; + test_cases! { + LDXRB_TEST_DB, untested_ldxrb_test_db; + test_ldxrb_w2_x8, ldxrb(W2, X8), "ldxrb w2, [x8]"; + test_ldxrb_wzr_sp, ldxrb(WZR, SP), "ldxrb wzr, [sp]"; + } +} diff --git a/harm/src/instructions/ldst/exclusive/ldxrh.rs b/harm/src/instructions/ldst/exclusive/ldxrh.rs new file mode 100644 index 0000000..37e21bf --- /dev/null +++ b/harm/src/instructions/ldst/exclusive/ldxrh.rs @@ -0,0 +1,66 @@ +/* Copyright (C) 2026 Ivan Boldyrev + * + * This document is licensed under the BSD 3-clause license. + */ + +use crate::register::{RegOrSp64, RegOrZero32, Register as _}; +use aarchmrs_instructions::A64::ldst::ldstexclr::LDXRH_LR32_ldstexclr::LDXRH_LR32_ldstexclr; + +use crate::instructions::RawInstruction; + +use super::super::args::reg_addr::{MakeReg32Addr, Reg32Addr}; + +/// A `ldxrh` instruction with a destination and an address. +#[derive(Debug, Clone, Copy)] +pub struct Ldxrh { + args: Reg32Addr, +} + +impl Ldxrh { + #[inline] + pub fn rt(&self) -> RegOrZero32 { + self.args.reg + } + + #[inline] + pub fn addr(&self) -> RegOrSp64 { + self.args.addr + } +} + +pub fn ldxrh(dst: TargetInp, addr: AddrInp) -> Ldxrh +where + Reg32Addr: MakeReg32Addr, +{ + Ldxrh { + args: >::new(dst, addr), + } +} + +impl RawInstruction for Ldxrh { + #[inline] + fn to_code(&self) -> aarchmrs_types::InstructionCode { + LDXRH_LR32_ldstexclr(self.addr().index(), self.rt().index()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::instructions::InstructionSeq; + use crate::register::Reg32::*; + use crate::register::Reg64::*; + use crate::register::RegOrSp64::SP; + use crate::register::RegOrZero32::WZR; + use harm_test_utils::test_cases; + + const LDXRH_TEST_DB: &str = " +485f7d02 ldxrh w2, [x8] +485f7fff ldxrh wzr, [sp] +"; + test_cases! { + LDXRH_TEST_DB, untested_ldxrh_test_db; + test_ldxrh_w2_x8, ldxrh(W2, X8), "ldxrh w2, [x8]"; + test_ldxrh_wzr_sp, ldxrh(WZR, SP), "ldxrh wzr, [sp]"; + } +} diff --git a/harm/src/instructions/ldst/exclusive/stxp.rs b/harm/src/instructions/ldst/exclusive/stxp.rs new file mode 100644 index 0000000..4b5edca --- /dev/null +++ b/harm/src/instructions/ldst/exclusive/stxp.rs @@ -0,0 +1,104 @@ +/* Copyright (C) 2026 Ivan Boldyrev + * + * This document is licensed under the BSD 3-clause license. + */ + +use crate::register::{RegOrSp64, RegOrZero32, RegOrZero64, Register}; +use aarchmrs_instructions::A64::ldst::ldstexclp::{ + STXP_SP32_ldstexclp::STXP_SP32_ldstexclp, STXP_SP64_ldstexclp::STXP_SP64_ldstexclp, +}; + +use crate::instructions::RawInstruction; + +use super::exclusive_args::{ExclusivePairStoreArgs, MakeExclusivePairStoreArgs}; + +/// A `stxp` instruction with a destination and an address. +pub struct Stxp { + args: ExclusivePairStoreArgs, +} + +impl Stxp { + pub fn status(&self) -> RegOrZero32 { + self.args.status + } + + pub fn rt1(&self) -> Rt { + self.args.reg1 + } + + pub fn rt2(&self) -> Rt { + self.args.reg2 + } + + pub fn addr(&self) -> RegOrSp64 { + self.args.addr + } +} + +pub fn stxp( + status: StatusInp, + r1: Reg1Inp, + r2: Reg2Inp, + addr: AddrRegInp, +) -> Stxp +where + ExclusivePairStoreArgs: + MakeExclusivePairStoreArgs, +{ + Stxp { + args: as MakeExclusivePairStoreArgs< + StatusInp, + Reg1Inp, + Reg2Inp, + AddrRegInp, + >>::new(status, r1, r2, addr), + } +} + +impl RawInstruction for Stxp { + fn to_code(&self) -> aarchmrs_types::InstructionCode { + STXP_SP32_ldstexclp( + self.status().index(), + self.rt2().index(), + self.addr().index(), + self.rt1().index(), + ) + } +} + +impl RawInstruction for Stxp { + fn to_code(&self) -> aarchmrs_types::InstructionCode { + STXP_SP64_ldstexclp( + self.status().index(), + self.rt2().index(), + self.addr().index(), + self.rt1().index(), + ) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::instructions::InstructionSeq; + use crate::register::Reg32::*; + use crate::register::Reg64::*; + use crate::register::RegOrSp64::SP; + use crate::register::RegOrZero32::WZR; + use crate::register::RegOrZero64::XZR; + use harm_test_utils::test_cases; + + const STXP_TEST_DB: &str = " +88250903 stxp w5, w3, w2, [x8] +c8250903 stxp w5, x3, x2, [x8] +882513ff stxp w5, wzr, w4, [sp] +c82513ff stxp w5, xzr, x4, [sp] +"; + test_cases! { + STXP_TEST_DB, untested_stxp_test_db; + test_stxp_w2_x8, stxp(W5, W3, W2, X8), "stxp w5, w3, w2, [x8]"; + test_stxp_x2_x8, stxp(W5, X3, X2, X8), "stxp w5, x3, x2, [x8]"; + test_stxp_wzr_sp, stxp(W5, WZR, W4, SP), "stxp w5, wzr, w4, [sp]"; + test_stxp_xzr_sp, stxp(W5, XZR, X4, SP), "stxp w5, xzr, x4, [sp]"; + } +} diff --git a/harm/src/instructions/ldst/exclusive/stxr.rs b/harm/src/instructions/ldst/exclusive/stxr.rs new file mode 100644 index 0000000..d0a18fe --- /dev/null +++ b/harm/src/instructions/ldst/exclusive/stxr.rs @@ -0,0 +1,98 @@ +/* Copyright (C) 2026 Ivan Boldyrev + * + * This document is licensed under the BSD 3-clause license. + */ + +use crate::register::{RegOrSp64, RegOrZero32, RegOrZero64, Register}; +use aarchmrs_instructions::A64::ldst::ldstexclr::{ + STXR_SR32_ldstexclr::STXR_SR32_ldstexclr, STXR_SR64_ldstexclr::STXR_SR64_ldstexclr, +}; + +use crate::instructions::RawInstruction; + +use super::exclusive_args::{ExclusiveStoreArgs, MakeExclusiveStoreArgs}; + +/// A `stxr` instruction with a destination and an address. +pub struct Stxr { + args: ExclusiveStoreArgs, +} + +impl Stxr { + #[inline] + pub fn rt(&self) -> Rt { + self.args.reg + } + + #[inline] + pub fn addr(&self) -> RegOrSp64 { + self.args.addr + } + + #[inline] + pub fn status(&self) -> RegOrZero32 { + self.args.status + } +} + +pub fn stxr( + status: StatusInp, + dst: TargetInp, + addr: AddrInp, +) -> Stxr +where + ExclusiveStoreArgs: MakeExclusiveStoreArgs, +{ + Stxr { + args: as MakeExclusiveStoreArgs< + StatusInp, + TargetInp, + AddrInp, + >>::new(status, dst, addr), + } +} + +impl RawInstruction for Stxr { + fn to_code(&self) -> aarchmrs_types::InstructionCode { + STXR_SR32_ldstexclr( + self.status().index(), + self.addr().index(), + self.rt().index(), + ) + } +} + +impl RawInstruction for Stxr { + fn to_code(&self) -> aarchmrs_types::InstructionCode { + STXR_SR64_ldstexclr( + self.status().index(), + self.addr().index(), + self.rt().index(), + ) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::instructions::InstructionSeq; + use crate::register::Reg32::*; + use crate::register::Reg64::*; + use crate::register::RegOrSp64::SP; + use crate::register::RegOrZero32::WZR; + use crate::register::RegOrZero64::XZR; + use harm_test_utils::test_cases; + + const STXR_TEST_DB: &str = " +88017d02 stxr w1, w2, [x8] +c8017d02 stxr w1, x2, [x8] +88017fff stxr w1, wzr, [sp] +c8017fff stxr w1, xzr, [sp] +"; + test_cases! { + STXR_TEST_DB, untested_stxr_test_db; + test_stxr_w2_x8, stxr(W1, W2, X8), "stxr w1, w2, [x8]"; + test_stxr_x2_x8, stxr(W1, X2, X8), "stxr w1, x2, [x8]"; + test_stxr_wzr_sp, stxr(W1, WZR, SP), "stxr w1, wzr, [sp]"; + test_stxr_xzr_sp, stxr(W1, XZR, SP), "stxr w1, xzr, [sp]"; + } +} diff --git a/harm/src/instructions/ldst/exclusive/stxrb.rs b/harm/src/instructions/ldst/exclusive/stxrb.rs new file mode 100644 index 0000000..098505a --- /dev/null +++ b/harm/src/instructions/ldst/exclusive/stxrb.rs @@ -0,0 +1,79 @@ +/* Copyright (C) 2026 Ivan Boldyrev + * + * This document is licensed under the BSD 3-clause license. + */ + +use aarchmrs_instructions::A64::ldst::ldstexclr::STXRB_SR32_ldstexclr::STXRB_SR32_ldstexclr; + +use super::exclusive_args::{ExclusiveStore32Args, MakeExclusiveStore32Args}; +use crate::instructions::RawInstruction; +use crate::register::{RegOrSp64, RegOrZero32, Register as _}; + +/// A `stxrb` instruction with a destination and an address. +pub struct Stxrb { + args: ExclusiveStore32Args, +} + +impl Stxrb { + #[inline] + pub fn rt(&self) -> RegOrZero32 { + self.args.reg + } + + #[inline] + pub fn addr(&self) -> RegOrSp64 { + self.args.addr + } + + #[inline] + pub fn status(&self) -> RegOrZero32 { + self.args.status + } +} + +pub fn stxrb( + status: StatusInp, + dst: TargetInp, + addr: AddrInp, +) -> Stxrb +where + ExclusiveStore32Args: MakeExclusiveStore32Args, +{ + Stxrb { + args: + >::new( + status, dst, addr, + ), + } +} + +impl RawInstruction for Stxrb { + fn to_code(&self) -> aarchmrs_types::InstructionCode { + STXRB_SR32_ldstexclr( + self.status().index(), + self.addr().index(), + self.rt().index(), + ) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::instructions::InstructionSeq; + use crate::register::Reg32::*; + use crate::register::Reg64::*; + use crate::register::RegOrSp64::SP; + use crate::register::RegOrZero32::WZR; + use harm_test_utils::test_cases; + + const STXRB_TEST_DB: &str = " +08017d02 stxrb w1, w2, [x8] +08017fff stxrb w1, wzr, [sp] +"; + test_cases! { + STXRB_TEST_DB, untested_stxrb_test_db; + test_stxrb_w2_x8, stxrb(W1, W2, X8), "stxrb w1, w2, [x8]"; + test_stxrb_wzr_sp, stxrb(W1, WZR, SP), "stxrb w1, wzr, [sp]"; + } +} diff --git a/harm/src/instructions/ldst/exclusive/stxrh.rs b/harm/src/instructions/ldst/exclusive/stxrh.rs new file mode 100644 index 0000000..adef7fb --- /dev/null +++ b/harm/src/instructions/ldst/exclusive/stxrh.rs @@ -0,0 +1,79 @@ +/* Copyright (C) 2026 Ivan Boldyrev + * + * This document is licensed under the BSD 3-clause license. + */ + +use aarchmrs_instructions::A64::ldst::ldstexclr::STXRH_SR32_ldstexclr::STXRH_SR32_ldstexclr; + +use super::exclusive_args::{ExclusiveStore32Args, MakeExclusiveStore32Args}; +use crate::instructions::RawInstruction; +use crate::register::{RegOrSp64, RegOrZero32, Register as _}; + +/// A `stxrh` instruction with a destination and an address. +pub struct Stxrh { + args: ExclusiveStore32Args, +} + +impl Stxrh { + #[inline] + pub fn rt(&self) -> RegOrZero32 { + self.args.reg + } + + #[inline] + pub fn addr(&self) -> RegOrSp64 { + self.args.addr + } + + #[inline] + pub fn status(&self) -> RegOrZero32 { + self.args.status + } +} + +pub fn stxrh( + status: StatusInp, + dst: TargetInp, + addr: AddrInp, +) -> Stxrh +where + ExclusiveStore32Args: MakeExclusiveStore32Args, +{ + Stxrh { + args: + >::new( + status, dst, addr, + ), + } +} + +impl RawInstruction for Stxrh { + fn to_code(&self) -> aarchmrs_types::InstructionCode { + STXRH_SR32_ldstexclr( + self.status().index(), + self.addr().index(), + self.rt().index(), + ) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::instructions::InstructionSeq; + use crate::register::Reg32::*; + use crate::register::Reg64::*; + use crate::register::RegOrSp64::SP; + use crate::register::RegOrZero32::WZR; + use harm_test_utils::test_cases; + + const STXRH_TEST_DB: &str = " +48017d02 stxrh w1, w2, [x8] +48017fff stxrh w1, wzr, [sp] +"; + test_cases! { + STXRH_TEST_DB, untested_stxrh_test_db; + test_stxrh_w2_x8, stxrh(W1, W2, X8), "stxrh w1, w2, [x8]"; + test_stxrh_wzr_sp, stxrh(W1, WZR, SP), "stxrh w1, wzr, [sp]"; + } +}