ab_riscv_primitives/instructions/
utils.rs1#[cfg(test)]
4mod tests;
5
6use crate::registers::general_purpose::RegType;
7use core::fmt;
8use core::hint::assert_unchecked;
9use core::marker::PhantomData;
10use core::ops::{Shl, Shr};
11
12#[derive(Clone, Copy, Hash)]
14#[derive_const(PartialEq, Eq)]
15pub struct U24([u8; 3]);
16
17const impl Default for U24 {
18 #[inline(always)]
19 fn default() -> Self {
20 Self([0; _])
21 }
22}
23
24impl fmt::Debug for U24 {
25 #[inline(always)]
26 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
27 fmt::Debug::fmt(&self.to_u32(), f)
28 }
29}
30
31impl fmt::Display for U24 {
32 #[inline(always)]
33 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
34 fmt::Display::fmt(&self.to_u32(), f)
35 }
36}
37
38impl fmt::LowerHex for U24 {
39 #[inline(always)]
40 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
41 fmt::LowerHex::fmt(&self.to_u32(), f)
42 }
43}
44
45impl fmt::UpperHex for U24 {
46 #[inline(always)]
47 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
48 fmt::UpperHex::fmt(&self.to_u32(), f)
49 }
50}
51
52const impl Shl<u8> for U24 {
53 type Output = Self;
54
55 #[inline(always)]
56 fn shl(self, rhs: u8) -> Self::Output {
57 Self::from_u32(self.to_u32().shl(rhs))
58 }
59}
60
61const impl Shr<u8> for U24 {
62 type Output = Self;
63
64 #[inline(always)]
65 fn shr(self, rhs: u8) -> Self::Output {
66 Self::from_u32(self.to_u32().shr(rhs))
67 }
68}
69
70const impl Shl<u8> for &U24 {
71 type Output = U24;
72
73 #[inline(always)]
74 fn shl(self, rhs: u8) -> Self::Output {
75 U24::from_u32(self.to_u32().shl(rhs))
76 }
77}
78
79const impl Shr<u8> for &U24 {
80 type Output = U24;
81
82 #[inline(always)]
83 fn shr(self, rhs: u8) -> Self::Output {
84 U24::from_u32(self.to_u32().shr(rhs))
85 }
86}
87
88const impl From<U24> for u32 {
89 #[inline(always)]
90 fn from(v: U24) -> Self {
91 v.to_u32()
92 }
93}
94
95const impl From<U24> for u64 {
96 #[inline(always)]
97 fn from(v: U24) -> Self {
98 u64::from(v.to_u32())
99 }
100}
101
102impl U24 {
103 #[inline(always)]
107 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
108 pub const fn from_u32(v: u32) -> Self {
109 let b = v.to_le_bytes();
110 debug_assert!(
111 (v << u8::BITS) >> u8::BITS == v,
112 "Input value exceeds 24 bits"
113 );
114 Self([b[0], b[1], b[2]])
115 }
116
117 #[inline(always)]
119 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
120 pub const fn to_u32(self) -> u32 {
121 let [a, b, c] = self.0;
122 u32::from_le_bytes([a, b, c, 0])
123 }
124}
125
126#[derive(Clone, Copy, Hash)]
128#[derive_const(PartialEq, Eq)]
129pub struct I24([u8; 3]);
130
131const impl Default for I24 {
132 #[inline(always)]
133 fn default() -> Self {
134 Self([0; _])
135 }
136}
137
138impl fmt::Debug for I24 {
139 #[inline(always)]
140 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
141 fmt::Debug::fmt(&self.to_i32(), f)
142 }
143}
144
145impl fmt::Display for I24 {
146 #[inline(always)]
147 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
148 fmt::Display::fmt(&self.to_i32(), f)
149 }
150}
151
152impl fmt::LowerHex for I24 {
153 #[inline(always)]
154 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
155 fmt::LowerHex::fmt(&self.to_i32(), f)
156 }
157}
158
159impl fmt::UpperHex for I24 {
160 #[inline(always)]
161 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
162 fmt::UpperHex::fmt(&self.to_i32(), f)
163 }
164}
165
166const impl Shl<u8> for I24 {
167 type Output = Self;
168
169 #[inline(always)]
170 fn shl(self, rhs: u8) -> Self::Output {
171 Self::from_i32(self.to_i32().shl(rhs))
172 }
173}
174
175const impl Shr<u8> for I24 {
176 type Output = Self;
177
178 #[inline(always)]
179 fn shr(self, rhs: u8) -> Self::Output {
180 Self::from_i32(self.to_i32().shr(rhs))
181 }
182}
183
184const impl Shl<u8> for &I24 {
185 type Output = I24;
186
187 #[inline(always)]
188 fn shl(self, rhs: u8) -> Self::Output {
189 I24::from_i32(self.to_i32().shl(rhs))
190 }
191}
192
193const impl Shr<u8> for &I24 {
194 type Output = I24;
195
196 #[inline(always)]
197 fn shr(self, rhs: u8) -> Self::Output {
198 I24::from_i32(self.to_i32().shr(rhs))
199 }
200}
201
202const impl From<I24> for i32 {
203 #[inline(always)]
204 fn from(v: I24) -> Self {
205 v.to_i32()
206 }
207}
208
209const impl From<I24> for i64 {
210 #[inline(always)]
211 fn from(v: I24) -> Self {
212 i64::from(v.to_i32())
213 }
214}
215
216impl I24 {
217 #[inline(always)]
221 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
222 pub const fn from_i32(v: i32) -> Self {
223 let b = v.to_le_bytes();
224 debug_assert!(
225 (v << u8::BITS) >> u8::BITS == v,
226 "Input value exceeds 24 bits"
227 );
228 Self([b[0], b[1], b[2]])
229 }
230
231 #[inline(always)]
233 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
234 pub const fn to_i32(self) -> i32 {
235 let [a, b, c] = self.0;
236 i32::from_le_bytes([a, b, c, 0]) << u8::BITS >> u8::BITS
238 }
239}
240
241const SUPPORTED_LOW_ZEROED_BITS<const LOW_ZEROED_BITS: u8>: usize = {
243 assert!(
244 LOW_ZEROED_BITS >= 8 && LOW_ZEROED_BITS < 32,
245 "Low zeroed bits must be in `8..32` range, so that the remaining bits of a 32-bit number fit \
246 into 24 bits and shifts don't overflow"
247 );
248 0
249};
250
251#[derive(Clone, Copy, Hash)]
256#[derive_const(PartialEq, Eq)]
257pub struct I24WithZeroedBits<const LOW_ZEROED_BITS: u8>([u8; 3]);
258
259const impl<const LOW_ZEROED_BITS: u8> Default for I24WithZeroedBits<LOW_ZEROED_BITS>
260where
261 [(); SUPPORTED_LOW_ZEROED_BITS::<LOW_ZEROED_BITS>]:,
262{
263 #[inline(always)]
264 fn default() -> Self {
265 Self([0; _])
266 }
267}
268
269impl<const LOW_ZEROED_BITS: u8> fmt::Debug for I24WithZeroedBits<LOW_ZEROED_BITS>
270where
271 [(); SUPPORTED_LOW_ZEROED_BITS::<LOW_ZEROED_BITS>]:,
272{
273 #[inline(always)]
274 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
275 fmt::Debug::fmt(&self.to_i32(), f)
276 }
277}
278
279impl<const LOW_ZEROED_BITS: u8> fmt::Display for I24WithZeroedBits<LOW_ZEROED_BITS>
280where
281 [(); SUPPORTED_LOW_ZEROED_BITS::<LOW_ZEROED_BITS>]:,
282{
283 #[inline(always)]
284 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
285 fmt::Display::fmt(&self.to_i32(), f)
286 }
287}
288
289impl<const LOW_ZEROED_BITS: u8> fmt::LowerHex for I24WithZeroedBits<LOW_ZEROED_BITS>
290where
291 [(); SUPPORTED_LOW_ZEROED_BITS::<LOW_ZEROED_BITS>]:,
292{
293 #[inline(always)]
294 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
295 fmt::LowerHex::fmt(&self.to_i32(), f)
296 }
297}
298
299impl<const LOW_ZEROED_BITS: u8> fmt::UpperHex for I24WithZeroedBits<LOW_ZEROED_BITS>
300where
301 [(); SUPPORTED_LOW_ZEROED_BITS::<LOW_ZEROED_BITS>]:,
302{
303 #[inline(always)]
304 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
305 fmt::UpperHex::fmt(&self.to_i32(), f)
306 }
307}
308
309const impl<const LOW_ZEROED_BITS: u8> From<I24WithZeroedBits<LOW_ZEROED_BITS>> for i32
310where
311 [(); SUPPORTED_LOW_ZEROED_BITS::<LOW_ZEROED_BITS>]:,
312{
313 #[inline(always)]
314 fn from(v: I24WithZeroedBits<LOW_ZEROED_BITS>) -> Self {
315 v.to_i32()
316 }
317}
318
319const impl<const LOW_ZEROED_BITS: u8> From<I24WithZeroedBits<LOW_ZEROED_BITS>> for i64
320where
321 [(); SUPPORTED_LOW_ZEROED_BITS::<LOW_ZEROED_BITS>]:,
322{
323 #[inline(always)]
324 fn from(v: I24WithZeroedBits<LOW_ZEROED_BITS>) -> Self {
325 i64::from(v.to_i32())
326 }
327}
328
329impl<const LOW_ZEROED_BITS: u8> I24WithZeroedBits<LOW_ZEROED_BITS>
330where
331 [(); SUPPORTED_LOW_ZEROED_BITS::<LOW_ZEROED_BITS>]:,
332{
333 #[inline(always)]
339 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
340 pub const fn from_i32(v_original: i32) -> Self {
341 let v = v_original >> LOW_ZEROED_BITS;
342 let b = v.to_le_bytes();
343 let return_value = Self([b[0], b[1], b[2]]);
344
345 debug_assert!(
346 return_value.to_i32() == v_original,
347 "Input has non-zero low bits"
348 );
349
350 return_value
351 }
352
353 #[inline(always)]
355 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
356 pub const fn to_i32(self) -> i32 {
357 let [a, b, c] = self.0;
358 (((i32::from_le_bytes([a, b, c, 0]) << u8::BITS >> u8::BITS) << LOW_ZEROED_BITS)
360 .cast_unsigned()
361 & (u32::MAX << LOW_ZEROED_BITS))
362 .cast_signed()
363 }
364}
365
366#[derive(Clone, Copy)]
372pub struct Shamt<Type> {
373 shamt: u8,
374 shifted_type: PhantomData<Type>,
375}
376
377const impl<Type> PartialEq for Shamt<Type> {
378 #[inline(always)]
379 fn eq(&self, other: &Self) -> bool {
380 self.shamt == other.shamt
381 }
382}
383
384const impl<Type> Eq for Shamt<Type> {}
385
386impl<Type> fmt::Debug for Shamt<Type> {
387 #[inline(always)]
388 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
389 fmt::Debug::fmt(&self.shamt, f)
390 }
391}
392
393impl<Type> fmt::Display for Shamt<Type> {
394 #[inline(always)]
395 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
396 fmt::Display::fmt(&self.shamt, f)
397 }
398}
399
400impl<Type> Shamt<Type>
401where
402 Type: RegType,
403{
404 #[inline(always)]
406 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
407 pub const fn new(shamt: u8) -> Option<Self> {
408 if shamt >= Type::BITS {
409 return None;
410 }
411
412 Some(Self {
413 shamt,
414 shifted_type: PhantomData,
415 })
416 }
417
418 #[inline(always)]
420 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
421 pub const fn get(self) -> u32 {
422 unsafe {
425 assert_unchecked(self.shamt < Type::BITS);
426 }
427 u32::from(self.shamt)
428 }
429}