Skip to main content

ab_riscv_primitives/instructions/
utils.rs

1//! Utility types
2
3#[cfg(test)]
4mod tests;
5
6use core::fmt;
7use core::ops::{Shl, Shr};
8
9/// New type for unsigned integers that stores 24-bit numbers
10#[derive(Default, Clone, Copy, PartialEq, Eq, Hash)]
11pub struct U24([u8; 3]);
12
13impl fmt::Debug for U24 {
14    #[inline(always)]
15    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
16        fmt::Debug::fmt(&self.to_u32(), f)
17    }
18}
19
20impl fmt::Display for U24 {
21    #[inline(always)]
22    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
23        fmt::Display::fmt(&self.to_u32(), f)
24    }
25}
26
27impl fmt::LowerHex for U24 {
28    #[inline(always)]
29    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
30        fmt::LowerHex::fmt(&self.to_u32(), f)
31    }
32}
33
34impl fmt::UpperHex for U24 {
35    #[inline(always)]
36    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
37        fmt::UpperHex::fmt(&self.to_u32(), f)
38    }
39}
40
41const impl Shl<u8> for U24 {
42    type Output = Self;
43
44    #[inline(always)]
45    fn shl(self, rhs: u8) -> Self::Output {
46        Self::from_u32(self.to_u32().shl(rhs))
47    }
48}
49
50const impl Shr<u8> for U24 {
51    type Output = Self;
52
53    #[inline(always)]
54    fn shr(self, rhs: u8) -> Self::Output {
55        Self::from_u32(self.to_u32().shr(rhs))
56    }
57}
58
59const impl Shl<u8> for &U24 {
60    type Output = U24;
61
62    #[inline(always)]
63    fn shl(self, rhs: u8) -> Self::Output {
64        U24::from_u32(self.to_u32().shl(rhs))
65    }
66}
67
68const impl Shr<u8> for &U24 {
69    type Output = U24;
70
71    #[inline(always)]
72    fn shr(self, rhs: u8) -> Self::Output {
73        U24::from_u32(self.to_u32().shr(rhs))
74    }
75}
76
77impl From<U24> for u32 {
78    #[inline(always)]
79    fn from(v: U24) -> Self {
80        v.to_u32()
81    }
82}
83
84impl From<U24> for u64 {
85    #[inline(always)]
86    fn from(v: U24) -> Self {
87        u64::from(v.to_u32())
88    }
89}
90
91impl U24 {
92    /// Create a new `U24` from an unsigned 32-bit integer.
93    ///
94    /// The input value is truncated to 24 bits, providing larger value panics in a debug build.
95    #[inline(always)]
96    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
97    pub const fn from_u32(v: u32) -> Self {
98        let b = v.to_le_bytes();
99        debug_assert!(
100            (v << u8::BITS) >> u8::BITS == v,
101            "Input value exceeds 24 bits"
102        );
103        Self([b[0], b[1], b[2]])
104    }
105
106    /// Convert to an unsigned 32-bit integer
107    #[inline(always)]
108    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
109    pub const fn to_u32(self) -> u32 {
110        let [a, b, c] = self.0;
111        u32::from_le_bytes([a, b, c, 0])
112    }
113}
114
115/// New type for signed integers that stores 24-bit numbers
116#[derive(Default, Clone, Copy, PartialEq, Eq, Hash)]
117pub struct I24([u8; 3]);
118
119impl fmt::Debug for I24 {
120    #[inline(always)]
121    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
122        fmt::Debug::fmt(&self.to_i32(), f)
123    }
124}
125
126impl fmt::Display for I24 {
127    #[inline(always)]
128    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
129        fmt::Display::fmt(&self.to_i32(), f)
130    }
131}
132
133impl fmt::LowerHex for I24 {
134    #[inline(always)]
135    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
136        fmt::LowerHex::fmt(&self.to_i32(), f)
137    }
138}
139
140impl fmt::UpperHex for I24 {
141    #[inline(always)]
142    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
143        fmt::UpperHex::fmt(&self.to_i32(), f)
144    }
145}
146
147const impl Shl<u8> for I24 {
148    type Output = Self;
149
150    #[inline(always)]
151    fn shl(self, rhs: u8) -> Self::Output {
152        Self::from_i32(self.to_i32().shl(rhs))
153    }
154}
155
156const impl Shr<u8> for I24 {
157    type Output = Self;
158
159    #[inline(always)]
160    fn shr(self, rhs: u8) -> Self::Output {
161        Self::from_i32(self.to_i32().shr(rhs))
162    }
163}
164
165const impl Shl<u8> for &I24 {
166    type Output = I24;
167
168    #[inline(always)]
169    fn shl(self, rhs: u8) -> Self::Output {
170        I24::from_i32(self.to_i32().shl(rhs))
171    }
172}
173
174const impl Shr<u8> for &I24 {
175    type Output = I24;
176
177    #[inline(always)]
178    fn shr(self, rhs: u8) -> Self::Output {
179        I24::from_i32(self.to_i32().shr(rhs))
180    }
181}
182
183impl From<I24> for i32 {
184    #[inline(always)]
185    fn from(v: I24) -> Self {
186        v.to_i32()
187    }
188}
189
190impl From<I24> for i64 {
191    #[inline(always)]
192    fn from(v: I24) -> Self {
193        i64::from(v.to_i32())
194    }
195}
196
197impl I24 {
198    /// Create a new `I24` from a signed 32-bit integer.
199    ///
200    /// The input value is truncated to 24 bits, providing larger value panics in a debug build.
201    #[inline(always)]
202    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
203    pub const fn from_i32(v: i32) -> Self {
204        let b = v.to_le_bytes();
205        debug_assert!(
206            (v << u8::BITS) >> u8::BITS == v,
207            "Input value exceeds 24 bits"
208        );
209        Self([b[0], b[1], b[2]])
210    }
211
212    /// Convert to a signed 32-bit integer
213    #[inline(always)]
214    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
215    pub const fn to_i32(self) -> i32 {
216        let [a, b, c] = self.0;
217        // Sign-extend
218        i32::from_le_bytes([a, b, c, 0]) << u8::BITS >> u8::BITS
219    }
220}
221
222/// New type for signed integers that stores 32-bit numbers with `LOW_ZEROED_BITS` low bits zeroed
223/// and truncated to 24-bits
224#[derive(Default, Clone, Copy, PartialEq, Eq, Hash)]
225pub struct I24WithZeroedBits<const LOW_ZEROED_BITS: u8>([u8; 3]);
226
227impl<const LOW_ZEROED_BITS: u8> fmt::Debug for I24WithZeroedBits<LOW_ZEROED_BITS> {
228    #[inline(always)]
229    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
230        fmt::Debug::fmt(&self.to_i32(), f)
231    }
232}
233
234impl<const LOW_ZEROED_BITS: u8> fmt::Display for I24WithZeroedBits<LOW_ZEROED_BITS> {
235    #[inline(always)]
236    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
237        fmt::Display::fmt(&self.to_i32(), f)
238    }
239}
240
241impl<const LOW_ZEROED_BITS: u8> fmt::LowerHex for I24WithZeroedBits<LOW_ZEROED_BITS> {
242    #[inline(always)]
243    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
244        fmt::LowerHex::fmt(&self.to_i32(), f)
245    }
246}
247
248impl<const LOW_ZEROED_BITS: u8> fmt::UpperHex for I24WithZeroedBits<LOW_ZEROED_BITS> {
249    #[inline(always)]
250    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
251        fmt::UpperHex::fmt(&self.to_i32(), f)
252    }
253}
254
255impl<const LOW_ZEROED_BITS: u8> From<I24WithZeroedBits<LOW_ZEROED_BITS>> for i32 {
256    #[inline(always)]
257    fn from(v: I24WithZeroedBits<LOW_ZEROED_BITS>) -> Self {
258        v.to_i32()
259    }
260}
261
262impl<const LOW_ZEROED_BITS: u8> From<I24WithZeroedBits<LOW_ZEROED_BITS>> for i64 {
263    #[inline(always)]
264    fn from(v: I24WithZeroedBits<LOW_ZEROED_BITS>) -> Self {
265        i64::from(v.to_i32())
266    }
267}
268
269impl<const LOW_ZEROED_BITS: u8> I24WithZeroedBits<LOW_ZEROED_BITS> {
270    /// Create a new `I24WithZeroedBits` from a signed 32-bit integer.
271    ///
272    /// The input value is shifted right arithmetically by `LOW_ZEROED_BITS` before being stored.
273    /// When converted back with [`Self::to_i32`], the value is shifted back with low bits being
274    /// zero.
275    #[inline(always)]
276    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
277    pub const fn from_i32(v_original: i32) -> Self {
278        let v = v_original >> LOW_ZEROED_BITS;
279        let b = v.to_le_bytes();
280        let return_value = Self([b[0], b[1], b[2]]);
281
282        debug_assert!(
283            return_value.to_i32() == v_original,
284            "Input has non-zero low bits"
285        );
286
287        return_value
288    }
289
290    /// Convert to a signed 32-bit integer
291    #[inline(always)]
292    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
293    pub const fn to_i32(self) -> i32 {
294        let [a, b, c] = self.0;
295        // Sign-extend and shift back
296        (((i32::from_le_bytes([a, b, c, 0]) << u8::BITS >> u8::BITS) << LOW_ZEROED_BITS)
297            .cast_unsigned()
298            & (u32::MAX << LOW_ZEROED_BITS))
299            .cast_signed()
300    }
301}