From 1e8f09b84ff9f08040c10c4a3df8928fd743df78 Mon Sep 17 00:00:00 2001 From: Ivan Boldyrev Date: Fri, 10 Jul 2026 17:35:24 +0200 Subject: [PATCH] ADC/SBC --- harm/src/instructions/arith.rs | 2 + harm/src/instructions/arith/adc.rs | 94 +++++++++++++++++++++++++++ harm/src/instructions/arith/macros.rs | 46 +++++++++++++ harm/src/instructions/arith/sbc.rs | 94 +++++++++++++++++++++++++++ 4 files changed, 236 insertions(+) create mode 100644 harm/src/instructions/arith/adc.rs create mode 100644 harm/src/instructions/arith/sbc.rs diff --git a/harm/src/instructions/arith.rs b/harm/src/instructions/arith.rs index 8ce34b5..c1b9ed5 100644 --- a/harm/src/instructions/arith.rs +++ b/harm/src/instructions/arith.rs @@ -13,8 +13,10 @@ use crate::{ #[macro_use] pub(crate) mod macros; +pub mod adc; pub mod add; pub mod adds; +pub mod sbc; pub mod sub; pub mod subs; diff --git a/harm/src/instructions/arith/adc.rs b/harm/src/instructions/arith/adc.rs new file mode 100644 index 0000000..b49e842 --- /dev/null +++ b/harm/src/instructions/arith/adc.rs @@ -0,0 +1,94 @@ +/* Copyright (C) 2026 Ivan Boldyrev + * + * This document is licensed under the BSD 3-clause license. + */ + +use aarchmrs_instructions::A64::dpreg::addsub_carry::{ + ADC_32_addsub_carry::ADC_32_addsub_carry, ADC_64_addsub_carry::ADC_64_addsub_carry, +}; +use aarchmrs_types::InstructionCode; + +use crate::{ + instructions::RawInstruction, + register::{IntoReg, Reg32, Reg64, RegOrZero32, RegOrZero64, Register as _}, + sealed::Sealed, +}; + +pub fn adc( + dst: T, + src1: S1, + src2: S2, +) -> as MakeAdc>::Output +where + Adc: MakeAdc, +{ + Adc::::new(dst, src1, src2) +} + +pub trait MakeAdc: Sealed { + type Output; + + fn new(dst: T, src1: S1, src2: S2) -> Self::Output; +} + +pub struct Adc { + pub dst: T, + pub src1: S1, + pub src2: S2, +} + +impl Sealed for Adc {} + +impl MakeAdc for Adc { + type Output = Self; + + #[inline] + fn new(dst: Reg64, src1: Reg64, src2: Reg64) -> Self { + Self { dst, src1, src2 } + } +} + +impl MakeAdc for Adc { + type Output = Self; + + #[inline] + fn new(dst: Reg32, src1: Reg32, src2: Reg32) -> Self { + Self { dst, src1, src2 } + } +} + +define_arith_carry!(Adc, 32, addsub, RegOrZero32, Reg32); +define_arith_carry!(Adc, 64, addsub, RegOrZero64, Reg64); + +#[cfg(test)] +mod tests { + use harm_test_utils::test_cases; + + use super::*; + use crate::instructions::InstructionSeq; + use Reg32::*; + use Reg64::*; + use RegOrZero32::Reg as RegZ32; + use RegOrZero32::WZR; + use RegOrZero64::Reg as RegZ; + use RegOrZero64::XZR; + + const ADC_DB: &str = " +1a0c0041 adc w1, w2, w12 +1a1f0041 adc w1, w2, wzr +1a0c03e1 adc w1, wzr, w12 +9a0c0041 adc x1, x2, x12 +9a1f0041 adc x1, x2, xzr +9a0c03e1 adc x1, xzr, x12 +"; + + test_cases! { + ADC_DB, untested_adc_db; + test_adc_32, adc(W1, W2, W12), "adc w1, w2, w12"; + test_adc_64, adc(X1, X2, X12), "adc x1, x2, x12"; + test_adc_64_zero, adc(RegZ(X1), XZR, X12), "adc x1, xzr, x12"; + test_adc_32_zero, adc(RegZ32(W1), WZR, W12), "adc w1, wzr, w12"; + test_adc_zero_64, adc(RegZ(X1), X2, XZR), "adc x1, x2, xzr"; + test_adc_zero_32, adc(RegZ32(W1), W2, WZR), "adc w1, w2, wzr"; + } +} diff --git a/harm/src/instructions/arith/macros.rs b/harm/src/instructions/arith/macros.rs index 411b7fc..3fe6001 100644 --- a/harm/src/instructions/arith/macros.rs +++ b/harm/src/instructions/arith/macros.rs @@ -385,3 +385,49 @@ macro_rules! define_arith_extend { } }; } + +macro_rules! define_arith_carry { + ($name:ident, $bits:expr, $cmd:ident, $ztype:ty, $reg:ty) => { + ::paste::paste! { + impl RawInstruction for $name<$reg, $reg, $reg> { + #[inline] + fn to_code(&self) -> InstructionCode { + $name { + dst: $ztype::Reg(self.dst), + src1: $ztype::Reg(self.src1), + src2: $ztype::Reg(self.src2), + } + .to_code() + } + } + + impl []<$ztype, Src1, Src2> for $name<$ztype, $ztype, $ztype> + where + Src1: IntoReg<$ztype>, + Src2: IntoReg<$ztype>, + { + type Output = Self; + + #[inline] + fn new(dst: $ztype, src1: Src1, src2: Src2) -> Self { + Self { dst, src1: src1.into_reg(), src2: src2.into_reg() } + } + } + + impl RawInstruction for $name<$ztype, $ztype, $ztype> { + #[inline] + fn to_code(&self) -> InstructionCode { + let rm = self.src2.index(); + let rn = self.src1.index(); + let rd = self.dst.index(); + + [<$name:upper _ $bits _ $cmd _carry>]( + rm.into(), + rn.into(), + rd.into(), + ) + } + } + } + }; +} diff --git a/harm/src/instructions/arith/sbc.rs b/harm/src/instructions/arith/sbc.rs new file mode 100644 index 0000000..35b8fe5 --- /dev/null +++ b/harm/src/instructions/arith/sbc.rs @@ -0,0 +1,94 @@ +/* Copyright (C) 2026 Ivan Boldyrev + * + * This document is licensed under the BSD 3-clause license. + */ + +use aarchmrs_instructions::A64::dpreg::addsub_carry::{ + SBC_32_addsub_carry::SBC_32_addsub_carry, SBC_64_addsub_carry::SBC_64_addsub_carry, +}; +use aarchmrs_types::InstructionCode; + +use crate::{ + instructions::RawInstruction, + register::{IntoReg, Reg32, Reg64, RegOrZero32, RegOrZero64, Register as _}, + sealed::Sealed, +}; + +pub fn sbc( + dst: T, + src1: S1, + src2: S2, +) -> as MakeSbc>::Output +where + Sbc: MakeSbc, +{ + Sbc::::new(dst, src1, src2) +} + +pub trait MakeSbc: Sealed { + type Output; + + fn new(dst: T, src1: S1, src2: S2) -> Self::Output; +} + +pub struct Sbc { + pub dst: T, + pub src1: S1, + pub src2: S2, +} + +impl Sealed for Sbc {} + +impl MakeSbc for Sbc { + type Output = Self; + + #[inline] + fn new(dst: Reg64, src1: Reg64, src2: Reg64) -> Self { + Self { dst, src1, src2 } + } +} + +impl MakeSbc for Sbc { + type Output = Self; + + #[inline] + fn new(dst: Reg32, src1: Reg32, src2: Reg32) -> Self { + Self { dst, src1, src2 } + } +} + +define_arith_carry!(Sbc, 32, addsub, RegOrZero32, Reg32); +define_arith_carry!(Sbc, 64, addsub, RegOrZero64, Reg64); + +#[cfg(test)] +mod tests { + use harm_test_utils::test_cases; + + use super::*; + use crate::instructions::InstructionSeq; + use Reg32::*; + use Reg64::*; + use RegOrZero32::Reg as RegZ32; + use RegOrZero32::WZR; + use RegOrZero64::Reg as RegZ; + use RegOrZero64::XZR; + + const SBC_DB: &str = " +5a0c0041 sbc w1, w2, w12 +5a1f0041 sbc w1, w2, wzr +5a0c03e1 sbc w1, wzr, w12 +da0c0041 sbc x1, x2, x12 +da1f0041 sbc x1, x2, xzr +da0c03e1 sbc x1, xzr, x12 +"; + + test_cases! { + SBC_DB, untested_sbc_db; + test_sbc_32, sbc(W1, W2, W12), "sbc w1, w2, w12"; + test_sbc_64, sbc(X1, X2, X12), "sbc x1, x2, x12"; + test_sbc_64_zero, sbc(RegZ(X1), XZR, X12), "sbc x1, xzr, x12"; + test_sbc_32_zero, sbc(RegZ32(W1), WZR, W12), "sbc w1, wzr, w12"; + test_sbc_zero_64, sbc(RegZ(X1), X2, XZR), "sbc x1, x2, xzr"; + test_sbc_zero_32, sbc(RegZ32(W1), W2, WZR), "sbc w1, w2, wzr"; + } +}