ab_riscv_primitives/
registers.rs

1#[cfg(test)]
2mod tests;
3
4use crate::registers::private::{PhantomRegister, RegisterInternal};
5use core::fmt;
6use core::hint::unreachable_unchecked;
7use core::marker::Destruct;
8use core::ops::{Add, AddAssign, Sub, SubAssign};
9
10mod private {
11    use core::marker::PhantomData;
12
13    pub const trait RegisterInternal<Type> {
14        /// Whether the register is a zero register
15        fn is_zero(&self) -> bool;
16
17        /// Offset in a set of registers
18        fn offset(self) -> usize;
19    }
20
21    #[derive(Debug, Clone, Copy)]
22    pub struct PhantomRegister<Type>(PhantomData<Type>);
23}
24
25/// Generic register
26pub const trait Register:
27    fmt::Display
28    + fmt::Debug
29    + [const] Eq
30    + [const] RegisterInternal<Self::Type>
31    + [const] Destruct
32    + Copy
33    + Sized
34{
35    /// The number of general purpose registers.
36    ///
37    /// Canonically 32 unless E extension is used, in which case 16.
38    const N: usize;
39    /// Register type.
40    ///
41    /// `u32` for RV32 and `u64` for RV64.
42    type Type: [const] Default
43        + [const] From<u8>
44        + [const] Into<u64>
45        + [const] Eq
46        + [const] Add
47        + [const] AddAssign
48        + [const] Sub
49        + [const] SubAssign
50        + fmt::Display
51        + fmt::Debug
52        + Copy
53        + Sized;
54
55    /// Create a register from its bit representation
56    fn from_bits(bits: u8) -> Option<Self>;
57}
58
59/// A set of RISC-V registers
60#[derive(Debug, Clone, Copy)]
61pub struct Registers<Reg>
62where
63    Reg: Register,
64    [(); Reg::N]:,
65{
66    regs: [Reg::Type; Reg::N],
67}
68
69impl<Reg> Default for Registers<Reg>
70where
71    Reg: Register,
72    [(); Reg::N]: Default,
73{
74    #[inline(always)]
75    fn default() -> Self {
76        Self {
77            regs: [Reg::Type::default(); Reg::N],
78        }
79    }
80}
81
82const impl<Reg> Registers<Reg>
83where
84    Reg: Register + [const] Eq,
85    [(); Reg::N]:,
86{
87    #[inline(always)]
88    pub fn read(&self, reg: Reg) -> Reg::Type
89    where
90        Reg: [const] Register,
91    {
92        if reg.is_zero() {
93            // Always zero
94            return Reg::Type::default();
95        }
96
97        // SAFETY: register offset is always within bounds
98        *unsafe { self.regs.get_unchecked(reg.offset()) }
99    }
100
101    #[inline(always)]
102    pub fn write(&mut self, reg: Reg, value: Reg::Type)
103    where
104        Reg: [const] Register,
105    {
106        if reg.is_zero() {
107            // Writes are ignored
108            return;
109        }
110
111        // SAFETY: register offset is always within bounds
112        *unsafe { self.regs.get_unchecked_mut(reg.offset()) } = value;
113    }
114}
115
116/// RISC-V register for RV32E/RV64E.
117///
118/// Use `Type = u32` for RV32E and `Type = u64` for RV64E.
119#[derive(Clone, Copy)]
120#[repr(u8)]
121pub enum EReg<Type> {
122    /// Always zero: `x0`
123    Zero = 0,
124    /// Return address: `x1`
125    Ra = 1,
126    /// Stack pointer: `x2`
127    Sp = 2,
128    /// Global pointer: `x3`
129    Gp = 3,
130    /// Thread pointer: `x4`
131    Tp = 4,
132    /// Temporary/alternate return address: `x5`
133    T0 = 5,
134    /// Temporary: `x6`
135    T1 = 6,
136    /// Temporary: `x7`
137    T2 = 7,
138    /// Saved register/frame pointer: `x8`
139    S0 = 8,
140    /// Saved register: `x9`
141    S1 = 9,
142    /// Function argument/return value: `x10`
143    A0 = 10,
144    /// Function argument/return value: `x11`
145    A1 = 11,
146    /// Function argument: `x12`
147    A2 = 12,
148    /// Function argument: `x13`
149    A3 = 13,
150    /// Function argument: `x14`
151    A4 = 14,
152    /// Function argument: `x15`
153    A5 = 15,
154    /// Phantom register that is never constructed and is only used due to type system limitations
155    #[doc(hidden)]
156    Phantom(PhantomRegister<Type>),
157}
158
159impl<Type> fmt::Display for EReg<Type> {
160    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
161        match self {
162            Self::Zero => write!(f, "zero"),
163            Self::Ra => write!(f, "ra"),
164            Self::Sp => write!(f, "sp"),
165            Self::Gp => write!(f, "gp"),
166            Self::Tp => write!(f, "tp"),
167            Self::T0 => write!(f, "t0"),
168            Self::T1 => write!(f, "t1"),
169            Self::T2 => write!(f, "t2"),
170            Self::S0 => write!(f, "s0"),
171            Self::S1 => write!(f, "s1"),
172            Self::A0 => write!(f, "a0"),
173            Self::A1 => write!(f, "a1"),
174            Self::A2 => write!(f, "a2"),
175            Self::A3 => write!(f, "a3"),
176            Self::A4 => write!(f, "a4"),
177            Self::A5 => write!(f, "a5"),
178            Self::Phantom(_) => {
179                // SAFETY: Phantom register is never constructed
180                unsafe { unreachable_unchecked() }
181            }
182        }
183    }
184}
185
186impl<Type> fmt::Debug for EReg<Type> {
187    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
188        fmt::Display::fmt(self, f)
189    }
190}
191
192impl<Type> const PartialEq for EReg<Type> {
193    #[inline(always)]
194    fn eq(&self, other: &Self) -> bool {
195        // This is quite ugly, but there doesn't seem to be a much better way with `Phantom` variant
196        matches!(
197            (self, other),
198            (Self::Zero, Self::Zero)
199                | (Self::Ra, Self::Ra)
200                | (Self::Sp, Self::Sp)
201                | (Self::Gp, Self::Gp)
202                | (Self::Tp, Self::Tp)
203                | (Self::T0, Self::T0)
204                | (Self::T1, Self::T1)
205                | (Self::T2, Self::T2)
206                | (Self::S0, Self::S0)
207                | (Self::S1, Self::S1)
208                | (Self::A0, Self::A0)
209                | (Self::A1, Self::A1)
210                | (Self::A2, Self::A2)
211                | (Self::A3, Self::A3)
212                | (Self::A4, Self::A4)
213                | (Self::A5, Self::A5)
214                | (Self::Phantom(_), Self::Phantom(_))
215        )
216    }
217}
218
219impl<Type> const Eq for EReg<Type> {}
220
221impl const RegisterInternal<u32> for EReg<u32> {
222    #[inline(always)]
223    fn is_zero(&self) -> bool {
224        matches!(self, Self::Zero)
225    }
226
227    #[inline(always)]
228    fn offset(self) -> usize {
229        // NOTE: `transmute()` is requited here, otherwise performance suffers A LOT for unknown
230        // reason
231        // SAFETY: Enum is `#[repr(u8)]` and doesn't have any fields
232        usize::from(unsafe { core::mem::transmute::<Self, u8>(self) })
233        // match self {
234        //     Self::Zero => 0,
235        //     Self::Ra => 1,
236        //     Self::Sp => 2,
237        //     Self::Gp => 3,
238        //     Self::Tp => 4,
239        //     Self::T0 => 5,
240        //     Self::T1 => 6,
241        //     Self::T2 => 7,
242        //     Self::S0 => 8,
243        //     Self::S1 => 9,
244        //     Self::A0 => 10,
245        //     Self::A1 => 11,
246        //     Self::A2 => 12,
247        //     Self::A3 => 13,
248        //     Self::A4 => 14,
249        //     Self::A5 => 15,
250        //     Self::Phantom(_) => {
251        //         // SAFETY: Phantom register is never constructed
252        //         unsafe { unreachable_unchecked() }
253        //     },
254        // }
255    }
256}
257
258impl const RegisterInternal<u64> for EReg<u64> {
259    #[inline(always)]
260    fn is_zero(&self) -> bool {
261        matches!(self, Self::Zero)
262    }
263
264    #[inline(always)]
265    fn offset(self) -> usize {
266        // NOTE: `transmute()` is requited here, otherwise performance suffers A LOT for unknown
267        // reason
268        // SAFETY: Enum is `#[repr(u8)]` and doesn't have any fields
269        usize::from(unsafe { core::mem::transmute::<Self, u8>(self) })
270        // match self {
271        //     Self::Zero => 0,
272        //     Self::Ra => 1,
273        //     Self::Sp => 2,
274        //     Self::Gp => 3,
275        //     Self::Tp => 4,
276        //     Self::T0 => 5,
277        //     Self::T1 => 6,
278        //     Self::T2 => 7,
279        //     Self::S0 => 8,
280        //     Self::S1 => 9,
281        //     Self::A0 => 10,
282        //     Self::A1 => 11,
283        //     Self::A2 => 12,
284        //     Self::A3 => 13,
285        //     Self::A4 => 14,
286        //     Self::A5 => 15,
287        //     Self::Phantom(_) => {
288        //         // SAFETY: Phantom register is never constructed
289        //         unsafe { unreachable_unchecked() }
290        //     },
291        // }
292    }
293}
294
295impl const Register for EReg<u32> {
296    const N: usize = 16;
297    type Type = u32;
298
299    #[inline(always)]
300    fn from_bits(bits: u8) -> Option<Self> {
301        match bits {
302            0 => Some(Self::Zero),
303            1 => Some(Self::Ra),
304            2 => Some(Self::Sp),
305            3 => Some(Self::Gp),
306            4 => Some(Self::Tp),
307            5 => Some(Self::T0),
308            6 => Some(Self::T1),
309            7 => Some(Self::T2),
310            8 => Some(Self::S0),
311            9 => Some(Self::S1),
312            10 => Some(Self::A0),
313            11 => Some(Self::A1),
314            12 => Some(Self::A2),
315            13 => Some(Self::A3),
316            14 => Some(Self::A4),
317            15 => Some(Self::A5),
318            _ => None,
319        }
320    }
321}
322
323impl const Register for EReg<u64> {
324    const N: usize = 16;
325    type Type = u64;
326
327    #[inline(always)]
328    fn from_bits(bits: u8) -> Option<Self> {
329        match bits {
330            0 => Some(Self::Zero),
331            1 => Some(Self::Ra),
332            2 => Some(Self::Sp),
333            3 => Some(Self::Gp),
334            4 => Some(Self::Tp),
335            5 => Some(Self::T0),
336            6 => Some(Self::T1),
337            7 => Some(Self::T2),
338            8 => Some(Self::S0),
339            9 => Some(Self::S1),
340            10 => Some(Self::A0),
341            11 => Some(Self::A1),
342            12 => Some(Self::A2),
343            13 => Some(Self::A3),
344            14 => Some(Self::A4),
345            15 => Some(Self::A5),
346            _ => None,
347        }
348    }
349}
350
351/// RISC-V register for RV32I/RV64I.
352///
353/// Use `Type = u32` for RV32I and `Type = u64` for RV64I.
354#[derive(Clone, Copy)]
355#[repr(u8)]
356pub enum Reg<Type> {
357    /// Always zero: `x0`
358    Zero = 0,
359    /// Return address: `x1`
360    Ra = 1,
361    /// Stack pointer: `x2`
362    Sp = 2,
363    /// Global pointer: `x3`
364    Gp = 3,
365    /// Thread pointer: `x4`
366    Tp = 4,
367    /// Temporary/alternate return address: `x5`
368    T0 = 5,
369    /// Temporary: `x6`
370    T1 = 6,
371    /// Temporary: `x7`
372    T2 = 7,
373    /// Saved register/frame pointer: `x8`
374    S0 = 8,
375    /// Saved register: `x9`
376    S1 = 9,
377    /// Function argument/return value: `x10`
378    A0 = 10,
379    /// Function argument/return value: `x11`
380    A1 = 11,
381    /// Function argument: `x12`
382    A2 = 12,
383    /// Function argument: `x13`
384    A3 = 13,
385    /// Function argument: `x14`
386    A4 = 14,
387    /// Function argument: `x15`
388    A5 = 15,
389    /// Function argument: `x16`
390    A6 = 16,
391    /// Function argument: `x17`
392    A7 = 17,
393    /// Saved register: `x18`
394    S2 = 18,
395    /// Saved register: `x19`
396    S3 = 19,
397    /// Saved register: `x20`
398    S4 = 20,
399    /// Saved register: `x21`
400    S5 = 21,
401    /// Saved register: `x22`
402    S6 = 22,
403    /// Saved register: `x23`
404    S7 = 23,
405    /// Saved register: `x24`
406    S8 = 24,
407    /// Saved register: `x25`
408    S9 = 25,
409    /// Saved register: `x26`
410    S10 = 26,
411    /// Saved register: `x27`
412    S11 = 27,
413    /// Temporary: `x28`
414    T3 = 28,
415    /// Temporary: `x29`
416    T4 = 29,
417    /// Temporary: `x30`
418    T5 = 30,
419    /// Temporary: `x31`
420    T6 = 31,
421    /// Phantom register that is never constructed and is only used due to type system limitations
422    #[doc(hidden)]
423    Phantom(PhantomRegister<Type>),
424}
425
426impl<Type> const From<EReg<u64>> for Reg<Type> {
427    #[inline(always)]
428    fn from(reg: EReg<u64>) -> Self {
429        match reg {
430            EReg::Zero => Self::Zero,
431            EReg::Ra => Self::Ra,
432            EReg::Sp => Self::Sp,
433            EReg::Gp => Self::Gp,
434            EReg::Tp => Self::Tp,
435            EReg::T0 => Self::T0,
436            EReg::T1 => Self::T1,
437            EReg::T2 => Self::T2,
438            EReg::S0 => Self::S0,
439            EReg::S1 => Self::S1,
440            EReg::A0 => Self::A0,
441            EReg::A1 => Self::A1,
442            EReg::A2 => Self::A2,
443            EReg::A3 => Self::A3,
444            EReg::A4 => Self::A4,
445            EReg::A5 => Self::A5,
446            EReg::Phantom(_) => {
447                // SAFETY: Phantom register is never constructed
448                unsafe { unreachable_unchecked() }
449            }
450        }
451    }
452}
453
454impl<Type> fmt::Display for Reg<Type> {
455    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
456        match self {
457            Self::Zero => write!(f, "zero"),
458            Self::Ra => write!(f, "ra"),
459            Self::Sp => write!(f, "sp"),
460            Self::Gp => write!(f, "gp"),
461            Self::Tp => write!(f, "tp"),
462            Self::T0 => write!(f, "t0"),
463            Self::T1 => write!(f, "t1"),
464            Self::T2 => write!(f, "t2"),
465            Self::S0 => write!(f, "s0"),
466            Self::S1 => write!(f, "s1"),
467            Self::A0 => write!(f, "a0"),
468            Self::A1 => write!(f, "a1"),
469            Self::A2 => write!(f, "a2"),
470            Self::A3 => write!(f, "a3"),
471            Self::A4 => write!(f, "a4"),
472            Self::A5 => write!(f, "a5"),
473            Self::A6 => write!(f, "a6"),
474            Self::A7 => write!(f, "a7"),
475            Self::S2 => write!(f, "s2"),
476            Self::S3 => write!(f, "s3"),
477            Self::S4 => write!(f, "s4"),
478            Self::S5 => write!(f, "s5"),
479            Self::S6 => write!(f, "s6"),
480            Self::S7 => write!(f, "s7"),
481            Self::S8 => write!(f, "s8"),
482            Self::S9 => write!(f, "s9"),
483            Self::S10 => write!(f, "s10"),
484            Self::S11 => write!(f, "s11"),
485            Self::T3 => write!(f, "t3"),
486            Self::T4 => write!(f, "t4"),
487            Self::T5 => write!(f, "t5"),
488            Self::T6 => write!(f, "t6"),
489            Self::Phantom(_) => {
490                // SAFETY: Phantom register is never constructed
491                unsafe { unreachable_unchecked() }
492            }
493        }
494    }
495}
496
497impl<Type> fmt::Debug for Reg<Type> {
498    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
499        fmt::Display::fmt(self, f)
500    }
501}
502
503impl<Type> const PartialEq for Reg<Type> {
504    #[inline(always)]
505    fn eq(&self, other: &Self) -> bool {
506        // This is quite ugly, but there doesn't seem to be a much better way with `Phantom` variant
507        matches!(
508            (self, other),
509            (Self::Zero, Self::Zero)
510                | (Self::Ra, Self::Ra)
511                | (Self::Sp, Self::Sp)
512                | (Self::Gp, Self::Gp)
513                | (Self::Tp, Self::Tp)
514                | (Self::T0, Self::T0)
515                | (Self::T1, Self::T1)
516                | (Self::T2, Self::T2)
517                | (Self::S0, Self::S0)
518                | (Self::S1, Self::S1)
519                | (Self::A0, Self::A0)
520                | (Self::A1, Self::A1)
521                | (Self::A2, Self::A2)
522                | (Self::A3, Self::A3)
523                | (Self::A4, Self::A4)
524                | (Self::A5, Self::A5)
525                | (Self::A6, Self::A6)
526                | (Self::A7, Self::A7)
527                | (Self::S2, Self::S2)
528                | (Self::S3, Self::S3)
529                | (Self::S4, Self::S4)
530                | (Self::S5, Self::S5)
531                | (Self::S6, Self::S6)
532                | (Self::S7, Self::S7)
533                | (Self::S8, Self::S8)
534                | (Self::S9, Self::S9)
535                | (Self::S10, Self::S10)
536                | (Self::S11, Self::S11)
537                | (Self::T3, Self::T3)
538                | (Self::T4, Self::T4)
539                | (Self::T5, Self::T5)
540                | (Self::T6, Self::T6)
541                | (Self::Phantom(_), Self::Phantom(_))
542        )
543    }
544}
545
546impl<Type> const Eq for Reg<Type> {}
547
548impl const RegisterInternal<u32> for Reg<u32> {
549    #[inline(always)]
550    fn is_zero(&self) -> bool {
551        matches!(self, Self::Zero)
552    }
553
554    #[inline(always)]
555    fn offset(self) -> usize {
556        // NOTE: `transmute()` is requited here, otherwise performance suffers A LOT for unknown
557        // reason
558        // SAFETY: Enum is `#[repr(u8)]` and doesn't have any fields
559        usize::from(unsafe { core::mem::transmute::<Self, u8>(self) })
560        // match self {
561        //     Self::Zero => 0,
562        //     Self::Ra => 1,
563        //     Self::Sp => 2,
564        //     Self::Gp => 3,
565        //     Self::Tp => 4,
566        //     Self::T0 => 5,
567        //     Self::T1 => 6,
568        //     Self::T2 => 7,
569        //     Self::S0 => 8,
570        //     Self::S1 => 9,
571        //     Self::A0 => 10,
572        //     Self::A1 => 11,
573        //     Self::A2 => 12,
574        //     Self::A3 => 13,
575        //     Self::A4 => 14,
576        //     Self::A5 => 15,
577        //     Self::A6 => 16,
578        //     Self::A7 => 17,
579        //     Self::S2 => 18,
580        //     Self::S3 => 19,
581        //     Self::S4 => 20,
582        //     Self::S5 => 21,
583        //     Self::S6 => 22,
584        //     Self::S7 => 23,
585        //     Self::S8 => 24,
586        //     Self::S9 => 25,
587        //     Self::S10 => 26,
588        //     Self::S11 => 27,
589        //     Self::T3 => 28,
590        //     Self::T4 => 29,
591        //     Self::T5 => 30,
592        //     Self::T6 => 31,
593        //     Self::Phantom(_) => {
594        //         // SAFETY: Phantom register is never constructed
595        //         unsafe { unreachable_unchecked() }
596        //     }
597        // }
598    }
599}
600
601impl const RegisterInternal<u64> for Reg<u64> {
602    #[inline(always)]
603    fn is_zero(&self) -> bool {
604        matches!(self, Self::Zero)
605    }
606
607    #[inline(always)]
608    fn offset(self) -> usize {
609        // NOTE: `transmute()` is requited here, otherwise performance suffers A LOT for unknown
610        // reason
611        // SAFETY: Enum is `#[repr(u8)]` and doesn't have any fields
612        usize::from(unsafe { core::mem::transmute::<Self, u8>(self) })
613        // match self {
614        //     Self::Zero => 0,
615        //     Self::Ra => 1,
616        //     Self::Sp => 2,
617        //     Self::Gp => 3,
618        //     Self::Tp => 4,
619        //     Self::T0 => 5,
620        //     Self::T1 => 6,
621        //     Self::T2 => 7,
622        //     Self::S0 => 8,
623        //     Self::S1 => 9,
624        //     Self::A0 => 10,
625        //     Self::A1 => 11,
626        //     Self::A2 => 12,
627        //     Self::A3 => 13,
628        //     Self::A4 => 14,
629        //     Self::A5 => 15,
630        //     Self::A6 => 16,
631        //     Self::A7 => 17,
632        //     Self::S2 => 18,
633        //     Self::S3 => 19,
634        //     Self::S4 => 20,
635        //     Self::S5 => 21,
636        //     Self::S6 => 22,
637        //     Self::S7 => 23,
638        //     Self::S8 => 24,
639        //     Self::S9 => 25,
640        //     Self::S10 => 26,
641        //     Self::S11 => 27,
642        //     Self::T3 => 28,
643        //     Self::T4 => 29,
644        //     Self::T5 => 30,
645        //     Self::T6 => 31,
646        //     Self::Phantom(_) => {
647        //         // SAFETY: Phantom register is never constructed
648        //         unsafe { unreachable_unchecked() }
649        //     }
650        // }
651    }
652}
653
654impl const Register for Reg<u32> {
655    const N: usize = 32;
656    type Type = u32;
657
658    #[inline(always)]
659    fn from_bits(bits: u8) -> Option<Self> {
660        match bits {
661            0 => Some(Self::Zero),
662            1 => Some(Self::Ra),
663            2 => Some(Self::Sp),
664            3 => Some(Self::Gp),
665            4 => Some(Self::Tp),
666            5 => Some(Self::T0),
667            6 => Some(Self::T1),
668            7 => Some(Self::T2),
669            8 => Some(Self::S0),
670            9 => Some(Self::S1),
671            10 => Some(Self::A0),
672            11 => Some(Self::A1),
673            12 => Some(Self::A2),
674            13 => Some(Self::A3),
675            14 => Some(Self::A4),
676            15 => Some(Self::A5),
677            16 => Some(Self::A6),
678            17 => Some(Self::A7),
679            18 => Some(Self::S2),
680            19 => Some(Self::S3),
681            20 => Some(Self::S4),
682            21 => Some(Self::S5),
683            22 => Some(Self::S6),
684            23 => Some(Self::S7),
685            24 => Some(Self::S8),
686            25 => Some(Self::S9),
687            26 => Some(Self::S10),
688            27 => Some(Self::S11),
689            28 => Some(Self::T3),
690            29 => Some(Self::T4),
691            30 => Some(Self::T5),
692            31 => Some(Self::T6),
693            _ => None,
694        }
695    }
696}
697
698impl const Register for Reg<u64> {
699    const N: usize = 32;
700    type Type = u64;
701
702    #[inline(always)]
703    fn from_bits(bits: u8) -> Option<Self> {
704        match bits {
705            0 => Some(Self::Zero),
706            1 => Some(Self::Ra),
707            2 => Some(Self::Sp),
708            3 => Some(Self::Gp),
709            4 => Some(Self::Tp),
710            5 => Some(Self::T0),
711            6 => Some(Self::T1),
712            7 => Some(Self::T2),
713            8 => Some(Self::S0),
714            9 => Some(Self::S1),
715            10 => Some(Self::A0),
716            11 => Some(Self::A1),
717            12 => Some(Self::A2),
718            13 => Some(Self::A3),
719            14 => Some(Self::A4),
720            15 => Some(Self::A5),
721            16 => Some(Self::A6),
722            17 => Some(Self::A7),
723            18 => Some(Self::S2),
724            19 => Some(Self::S3),
725            20 => Some(Self::S4),
726            21 => Some(Self::S5),
727            22 => Some(Self::S6),
728            23 => Some(Self::S7),
729            24 => Some(Self::S8),
730            25 => Some(Self::S9),
731            26 => Some(Self::S10),
732            27 => Some(Self::S11),
733            28 => Some(Self::T3),
734            29 => Some(Self::T4),
735            30 => Some(Self::T5),
736            31 => Some(Self::T6),
737            _ => None,
738        }
739    }
740}