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
41impl const 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
50impl const 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
59impl const 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
68impl const 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    pub const fn from_u32(v: u32) -> Self {
97        let b = v.to_le_bytes();
98        debug_assert!(
99            (v << u8::BITS) >> u8::BITS == v,
100            "Input value exceeds 24 bits"
101        );
102        Self([b[0], b[1], b[2]])
103    }
104
105    /// Convert to an unsigned 32-bit integer
106    #[inline(always)]
107    pub const fn to_u32(self) -> u32 {
108        let [a, b, c] = self.0;
109        u32::from_le_bytes([a, b, c, 0])
110    }
111}
112
113/// New type for signed integers that stores 24-bit numbers
114#[derive(Default, Clone, Copy, PartialEq, Eq, Hash)]
115pub struct I24([u8; 3]);
116
117impl fmt::Debug for I24 {
118    #[inline(always)]
119    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
120        fmt::Debug::fmt(&self.to_i32(), f)
121    }
122}
123
124impl fmt::Display for I24 {
125    #[inline(always)]
126    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
127        fmt::Display::fmt(&self.to_i32(), f)
128    }
129}
130
131impl fmt::LowerHex for I24 {
132    #[inline(always)]
133    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
134        fmt::LowerHex::fmt(&self.to_i32(), f)
135    }
136}
137
138impl fmt::UpperHex for I24 {
139    #[inline(always)]
140    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
141        fmt::UpperHex::fmt(&self.to_i32(), f)
142    }
143}
144
145impl const Shl<u8> for I24 {
146    type Output = Self;
147
148    #[inline(always)]
149    fn shl(self, rhs: u8) -> Self::Output {
150        Self::from_i32(self.to_i32().shl(rhs))
151    }
152}
153
154impl const Shr<u8> for I24 {
155    type Output = Self;
156
157    #[inline(always)]
158    fn shr(self, rhs: u8) -> Self::Output {
159        Self::from_i32(self.to_i32().shr(rhs))
160    }
161}
162
163impl const Shl<u8> for &I24 {
164    type Output = I24;
165
166    #[inline(always)]
167    fn shl(self, rhs: u8) -> Self::Output {
168        I24::from_i32(self.to_i32().shl(rhs))
169    }
170}
171
172impl const Shr<u8> for &I24 {
173    type Output = I24;
174
175    #[inline(always)]
176    fn shr(self, rhs: u8) -> Self::Output {
177        I24::from_i32(self.to_i32().shr(rhs))
178    }
179}
180
181impl From<I24> for i32 {
182    #[inline(always)]
183    fn from(v: I24) -> Self {
184        v.to_i32()
185    }
186}
187
188impl From<I24> for i64 {
189    #[inline(always)]
190    fn from(v: I24) -> Self {
191        i64::from(v.to_i32())
192    }
193}
194
195impl I24 {
196    /// Create a new `I24` from a signed 32-bit integer.
197    ///
198    /// The input value is truncated to 24 bits, providing larger value panics in a debug build.
199    #[inline(always)]
200    pub const fn from_i32(v: i32) -> Self {
201        let b = v.to_le_bytes();
202        debug_assert!(
203            (v << u8::BITS) >> u8::BITS == v,
204            "Input value exceeds 24 bits"
205        );
206        Self([b[0], b[1], b[2]])
207    }
208
209    /// Convert to a signed 32-bit integer
210    #[inline(always)]
211    pub const fn to_i32(self) -> i32 {
212        let [a, b, c] = self.0;
213        // Sign-extend
214        i32::from_le_bytes([a, b, c, 0]) << u8::BITS >> u8::BITS
215    }
216}
217
218/// New type for signed integers that stores 32-bit numbers with `LOW_ZEROED_BITS` low bits zeroed
219/// and truncated to 24-bits
220#[derive(Default, Clone, Copy, PartialEq, Eq, Hash)]
221pub struct I24WithZeroedBits<const LOW_ZEROED_BITS: u8>([u8; 3]);
222
223impl<const LOW_ZEROED_BITS: u8> fmt::Debug for I24WithZeroedBits<LOW_ZEROED_BITS> {
224    #[inline(always)]
225    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
226        fmt::Debug::fmt(&self.to_i32(), f)
227    }
228}
229
230impl<const LOW_ZEROED_BITS: u8> fmt::Display for I24WithZeroedBits<LOW_ZEROED_BITS> {
231    #[inline(always)]
232    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
233        fmt::Display::fmt(&self.to_i32(), f)
234    }
235}
236
237impl<const LOW_ZEROED_BITS: u8> fmt::LowerHex for I24WithZeroedBits<LOW_ZEROED_BITS> {
238    #[inline(always)]
239    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
240        fmt::LowerHex::fmt(&self.to_i32(), f)
241    }
242}
243
244impl<const LOW_ZEROED_BITS: u8> fmt::UpperHex for I24WithZeroedBits<LOW_ZEROED_BITS> {
245    #[inline(always)]
246    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
247        fmt::UpperHex::fmt(&self.to_i32(), f)
248    }
249}
250
251impl<const LOW_ZEROED_BITS: u8> From<I24WithZeroedBits<LOW_ZEROED_BITS>> for i32 {
252    #[inline(always)]
253    fn from(v: I24WithZeroedBits<LOW_ZEROED_BITS>) -> Self {
254        v.to_i32()
255    }
256}
257
258impl<const LOW_ZEROED_BITS: u8> From<I24WithZeroedBits<LOW_ZEROED_BITS>> for i64 {
259    #[inline(always)]
260    fn from(v: I24WithZeroedBits<LOW_ZEROED_BITS>) -> Self {
261        i64::from(v.to_i32())
262    }
263}
264
265impl<const LOW_ZEROED_BITS: u8> I24WithZeroedBits<LOW_ZEROED_BITS> {
266    /// Create a new `I24WithZeroedBits` from a signed 32-bit integer.
267    ///
268    /// The input value is shifted right arithmetically by `LOW_ZEROED_BITS` before being stored.
269    /// When converted back with [`Self::to_i32`], the value is shifted back with low bits being
270    /// zero.
271    #[inline(always)]
272    pub const fn from_i32(v_original: i32) -> Self {
273        let v = v_original >> LOW_ZEROED_BITS;
274        let b = v.to_le_bytes();
275        let return_value = Self([b[0], b[1], b[2]]);
276
277        debug_assert!(
278            return_value.to_i32() == v_original,
279            "Input has non-zero low bits"
280        );
281
282        return_value
283    }
284
285    /// Convert to a signed 32-bit integer
286    #[inline(always)]
287    pub const fn to_i32(self) -> i32 {
288        let [a, b, c] = self.0;
289        // Sign-extend and shift back
290        (((i32::from_le_bytes([a, b, c, 0]) << u8::BITS >> u8::BITS) << LOW_ZEROED_BITS)
291            .cast_unsigned()
292            & (u32::MAX << LOW_ZEROED_BITS))
293            .cast_signed()
294    }
295}