Coverage Report

Created: 2026-03-26 07:41

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/dynasmrt-5.0.0/src/riscv.rs
Line
Count
Source
1
//! Runtime support for the 32-bit and 64-bit RISC-V architecture assembling targets.
2
//!
3
//! The riscv instruction sets feature 16-bit and 32-bit width instructions. It features relocations
4
//! up to 20 bits in size in a single instruction, or 32 bits in size using sequences of two
5
//! instructions.
6
//!
7
//! The core relocation behaviour for these architecture is provided by the [`RiscvRelocation`] type.
8
//!
9
//! Next to that, this module contains the following:
10
//!
11
//! ## Type aliases
12
//!
13
//! Several specialized type aliases of the generic [`Assembler`] are provided as these are by far the most common usecase.
14
//!
15
//! ## Enums
16
//!
17
//! There are enumerations of every RISC-V register family. 
18
//! These enums implement the [`Register`] trait and their discriminant values match their numeric encoding in dynamic register literals.
19
//!
20
//! *Note: The presence of some registers listed here is purely what is encodable. Check the relevant architecture documentation to find what is architecturally valid.*
21
//!
22
//! ## Functions
23
//!
24
//! This module contains handlers for error conditions in the case where a dynamically selected register is invalid, or a dynamically encoded immediate is out of range.
25
//! These panic with a friendly error message if any of these conditions happen at runtime.
26
27
use crate::relocations::{ArchitectureRelocationEncoding, Relocation, RelocationType, RelocationSize, RelocationKind, RelocationEncoding, ImpossibleRelocation, fits_signed_bitfield};
28
use byteorder::{ByteOrder, LittleEndian};
29
use std::convert::TryFrom;
30
use crate::Register;
31
32
#[derive(Debug, Clone, Copy)]
33
#[allow(missing_docs)]
34
enum RiscvRelocationEncoding {
35
    // beq, beqz, bge, bgeu, bgez, bgt, bgtu, bgtz, ble, bleu, blez, blt, bltu, bltz, bne, bnez
36
    // 12 bits, 2-bit scaled
37
    B,
38
    // j, jal
39
    // 20 bits, 2-bit scaled
40
    J,
41
    // c.beqz, c.bnez
42
    // 9 bits, 2-bit scaled
43
    BC,
44
    // c.j, c.jal
45
    // 12 bits, 2-bit scaled
46
    JC,
47
    // auipc
48
    // 32 bits, 12-bit scaled
49
    HI20,
50
    // loads, addi.
51
    // 12 bits, no scaling
52
    LO12,
53
    // stores
54
    // 12 bits, no scaling
55
    LO12S,
56
    // pc-relative addrgen/load pseudo instructions
57
    // 32 bits, no scaling
58
    SPLIT32,
59
    // pc-relative store pseudo instructions
60
    // 32 bits, no scaling
61
    SPLIT32S,
62
}
63
64
impl ArchitectureRelocationEncoding for RiscvRelocationEncoding {
65
0
    fn decode(code: u8) -> Self {
66
0
        match code {
67
0
            0 => Self::B,
68
0
            1 => Self::J,
69
0
            2 => Self::BC,
70
0
            3 => Self::JC,
71
0
            4 => Self::HI20,
72
0
            5 => Self::LO12,
73
0
            6 => Self::LO12S,
74
0
            7 => Self::SPLIT32,
75
0
            8 => Self::SPLIT32S,
76
0
            n => panic!("Invalid complex relocation code {n} given for the current architecture")
77
        }
78
0
    }
Unexecuted instantiation: <dynasmrt::riscv::RiscvRelocationEncoding as dynasmrt::relocations::ArchitectureRelocationEncoding>::decode
Unexecuted instantiation: <dynasmrt::riscv::RiscvRelocationEncoding as dynasmrt::relocations::ArchitectureRelocationEncoding>::decode
79
}
80
81
impl RiscvRelocationEncoding {
82
0
    fn bitsize(&self) -> (u8, u8) {
83
0
        match self {
84
0
            Self::B => (12, 1),
85
0
            Self::J => (20, 1),
86
0
            Self::BC => (9, 1),
87
0
            Self::JC => (12, 1),
88
89
0
            Self::HI20 => (32, 0),
90
0
            Self::LO12 => (32, 0),
91
0
            Self::LO12S => (32, 0),
92
93
0
            Self::SPLIT32 => (32, 0),
94
0
            Self::SPLIT32S => (32, 0),
95
        }
96
0
    }
Unexecuted instantiation: <dynasmrt::riscv::RiscvRelocationEncoding>::bitsize
Unexecuted instantiation: <dynasmrt::riscv::RiscvRelocationEncoding>::bitsize
97
}
98
99
/// Relocation implementation for the RV32 and RV64 architectures.
100
#[derive(Debug, Clone, Copy)]
101
pub struct RiscvRelocation(RelocationType<RiscvRelocationEncoding>);
102
103
impl Relocation for RiscvRelocation {
104
0
    fn from_encoding(encoding: u8) -> Self {
105
0
        RiscvRelocation(RelocationType::decode(encoding))
106
0
    }
Unexecuted instantiation: <dynasmrt::riscv::RiscvRelocation as dynasmrt::relocations::Relocation>::from_encoding
Unexecuted instantiation: <dynasmrt::riscv::RiscvRelocation as dynasmrt::relocations::Relocation>::from_encoding
107
0
    fn from_size(kind: RelocationKind, size: RelocationSize) -> Self {
108
0
        RiscvRelocation(RelocationType::from_size(kind, size))
109
0
    }
Unexecuted instantiation: <dynasmrt::riscv::RiscvRelocation as dynasmrt::relocations::Relocation>::from_size
Unexecuted instantiation: <dynasmrt::riscv::RiscvRelocation as dynasmrt::relocations::Relocation>::from_size
110
0
    fn size(&self) -> usize {
111
0
        match self.0.encoding {
112
0
            RelocationEncoding::Simple(s) => s.size(),
113
0
            RelocationEncoding::ArchSpecific(c) => match c {
114
                RiscvRelocationEncoding::BC
115
0
                | RiscvRelocationEncoding::JC => 2,
116
                RiscvRelocationEncoding::B
117
                | RiscvRelocationEncoding::J
118
                | RiscvRelocationEncoding::HI20
119
                | RiscvRelocationEncoding::LO12
120
0
                | RiscvRelocationEncoding::LO12S => 4,
121
                RiscvRelocationEncoding::SPLIT32
122
0
                | RiscvRelocationEncoding::SPLIT32S => 8
123
            }
124
        }
125
0
    }
Unexecuted instantiation: <dynasmrt::riscv::RiscvRelocation as dynasmrt::relocations::Relocation>::size
Unexecuted instantiation: <dynasmrt::riscv::RiscvRelocation as dynasmrt::relocations::Relocation>::size
126
0
    fn write_value(&self, buf: &mut [u8], value: isize) -> Result<(), ImpossibleRelocation> {
127
0
        match self.0.encoding {
128
0
            RelocationEncoding::Simple(s) => s.write_value(buf, value),
129
0
            RelocationEncoding::ArchSpecific(c) => {
130
                // determine if the value fits
131
0
                let value = i64::try_from(value).map_err(|_| ImpossibleRelocation { } )?;
Unexecuted instantiation: <dynasmrt::riscv::RiscvRelocation as dynasmrt::relocations::Relocation>::write_value::{closure#0}
Unexecuted instantiation: <dynasmrt::riscv::RiscvRelocation as dynasmrt::relocations::Relocation>::write_value::{closure#0}
132
133
0
                let (bits, scaling) = c.bitsize();
134
0
                let mask = (1i64 << scaling) - 1;
135
                // special case: the 32-bit AUIPC-based offsets don't actually
136
                // range from -0x8000_0000 to 0x7FFF_FFFF on RV64 due to how
137
                // sign extension interacts between them, they range from
138
                // -0x8000_0800 to 0x7FFF_F7FF. But on RV32 they do span
139
                // from -0x8000_0000 to 0x7FFF_FFFF.
140
                // neither of these limits will ever occur in practical code,
141
                // so for sanity's sake we just clamp to between -0x8000_0000 and
142
                // 0x7FFF_F7FF
143
0
                match c {
144
                    RiscvRelocationEncoding::HI20
145
                    | RiscvRelocationEncoding::LO12
146
                    | RiscvRelocationEncoding::LO12S
147
                    | RiscvRelocationEncoding::SPLIT32
148
                    | RiscvRelocationEncoding::SPLIT32S => {
149
0
                        if value < -0x8000_0800 || value > 0x7FFF_F7FF {
150
0
                            return Err(ImpossibleRelocation { } );  
151
0
                        }
152
                    },
153
                    _ => {
154
0
                        if !fits_signed_bitfield(value, bits) || (value & mask) != 0 {
155
0
                            return Err(ImpossibleRelocation { } );
156
0
                        }
157
                    }
158
                }
159
160
                // we never encode any bit above the 31st so cast now
161
0
                let val_cast = value as u32;
162
163
0
                match c {
164
0
                    RiscvRelocationEncoding::B => {
165
0
                        let mut instr = LittleEndian::read_u32(buf);
166
0
                        instr &= 0x01FF_F07F;
167
0
168
0
                        instr |= ((val_cast >> 12) & 0x1) << 31;
169
0
                        instr |= ((val_cast >> 5) & 0x3F) << 25;
170
0
                        instr |= ((val_cast >> 1) & 0xF) << 8;
171
0
                        instr |= ((val_cast >> 11) & 0x1) << 7;
172
0
173
0
                        LittleEndian::write_u32(buf, instr);
174
0
                    },
175
0
                    RiscvRelocationEncoding::J => {
176
0
                        let mut instr = LittleEndian::read_u32(buf);
177
0
                        instr &= 0x0000_0FFF;
178
0
179
0
                        instr |= ((val_cast >> 20) & 0x1) << 31;
180
0
                        instr |= ((val_cast >> 1) & 0x3FF) << 21;
181
0
                        instr |= ((val_cast >> 11) & 0x1) << 20;
182
0
                        instr |= ((val_cast >> 12) & 0xFF) << 12;
183
0
184
0
                        LittleEndian::write_u32(buf, instr);
185
0
                    },
186
0
                    RiscvRelocationEncoding::BC => {
187
0
                        let mut instr = LittleEndian::read_u16(buf);
188
0
                        instr &= 0xE383;
189
0
190
0
                        instr |= (((val_cast >> 8) & 0x1) as u16) << 12;
191
0
                        instr |= (((val_cast >> 3) & 0x3) as u16) << 10;
192
0
                        instr |= (((val_cast >> 6) & 0x3) as u16) << 5;
193
0
                        instr |= (((val_cast >> 1) & 0x3) as u16) << 3;
194
0
                        instr |= (((val_cast >> 5) & 0x1) as u16) << 2;
195
0
196
0
                        LittleEndian::write_u16(buf, instr);
197
0
                    },
198
0
                    RiscvRelocationEncoding::JC => {
199
0
                        let mut instr = LittleEndian::read_u16(buf);
200
0
                        instr &= 0xE003;
201
0
202
0
                        instr |= (((val_cast >> 11) & 0x1) as u16) << 12;
203
0
                        instr |= (((val_cast >> 4) & 0x1) as u16) << 11;
204
0
                        instr |= (((val_cast >> 8) & 0x3) as u16) << 9;
205
0
                        instr |= (((val_cast >> 10) & 0x1) as u16) << 8;
206
0
                        instr |= (((val_cast >> 6) & 0x1) as u16) << 7;
207
0
                        instr |= (((val_cast >> 7) & 0x1) as u16) << 6;
208
0
                        instr |= (((val_cast >> 1) & 0x7) as u16) << 3;
209
0
                        instr |= (((val_cast >> 5) & 0x1) as u16) << 2;
210
0
211
0
                        LittleEndian::write_u16(buf, instr);
212
0
                    },
213
0
                    RiscvRelocationEncoding::HI20 => {
214
0
                        let mut instr = LittleEndian::read_u32(buf);
215
0
                        instr &= 0x0000_0FFF;
216
0
217
0
                        let val_round: u32 = val_cast.wrapping_add(0x800);
218
0
                        instr |= val_round & 0xFFFF_F000;
219
0
220
0
                        LittleEndian::write_u32(buf, instr);
221
0
                    },
222
0
                    RiscvRelocationEncoding::LO12 => {
223
0
                        let mut instr = LittleEndian::read_u32(buf);
224
0
                        instr &= 0x000F_FFFF;
225
0
226
0
                        instr |= (val_cast & 0xFFF) << 20;
227
0
228
0
                        LittleEndian::write_u32(buf, instr);
229
0
                    },
230
0
                    RiscvRelocationEncoding::LO12S => {
231
0
                        let mut instr = LittleEndian::read_u32(buf);
232
0
                        instr &= 0x01FF_F07F;
233
0
234
0
                        instr |= (val_cast & 0x1F) << 7;
235
0
                        instr |= ((val_cast >> 5) & 0x7F) << 25;
236
0
237
0
                        LittleEndian::write_u32(buf, instr);
238
0
                    },
239
0
                    RiscvRelocationEncoding::SPLIT32 => {
240
0
                        let mut instr1 = LittleEndian::read_u32(&buf[..4]);
241
0
                        let mut instr2 = LittleEndian::read_u32(&buf[4..]);
242
0
                        instr1 &= 0x0000_0FFF;
243
0
                        instr2 &= 0x000F_FFFF;
244
0
245
0
                        let val_round: u32 = val_cast.wrapping_add(0x800);
246
0
                        instr1 |= val_round & 0xFFFF_F000;
247
0
                        instr2 |= (val_cast & 0xFFF) << 20;
248
0
249
0
                        LittleEndian::write_u32(&mut buf[..4], instr1);
250
0
                        LittleEndian::write_u32(&mut buf[4..], instr2);
251
0
                    },
252
0
                    RiscvRelocationEncoding::SPLIT32S => {
253
0
                        let mut instr1 = LittleEndian::read_u32(&buf[..4]);
254
0
                        let mut instr2 = LittleEndian::read_u32(&buf[4..]);
255
0
                        instr1 &= 0x0000_0FFF;
256
0
                        instr2 &= 0x01FF_F07F;
257
0
258
0
                        let val_round: u32 = val_cast.wrapping_add(0x800);
259
0
                        instr1 |= val_round & 0xFFFF_F000;
260
0
                        instr2 |= (val_cast & 0x1F) << 7;
261
0
                        instr2 |= ((val_cast >> 5) & 0x7F) << 25;
262
0
263
0
                        LittleEndian::write_u32(&mut buf[..4], instr1);
264
0
                        LittleEndian::write_u32(&mut buf[4..], instr2);
265
0
                    },
266
                }
267
268
0
                Ok(())
269
            }
270
        }
271
0
    }
Unexecuted instantiation: <dynasmrt::riscv::RiscvRelocation as dynasmrt::relocations::Relocation>::write_value
Unexecuted instantiation: <dynasmrt::riscv::RiscvRelocation as dynasmrt::relocations::Relocation>::write_value
272
0
    fn read_value(&self, buf: &[u8]) -> isize {
273
0
        match self.0.encoding {
274
0
            RelocationEncoding::Simple(s) => s.read_value(buf),
275
0
            RelocationEncoding::ArchSpecific(c) => {
276
                let bits;
277
                let mut unpacked;
278
279
0
                match c {
280
0
                    RiscvRelocationEncoding::B => {
281
0
                        bits = 12;
282
0
                        let instr = LittleEndian::read_u32(buf);
283
0
284
0
                        unpacked = ((instr >> 31) & 0x1) << 12;
285
0
                        unpacked |= ((instr >> 25) & 0x3F) << 5;
286
0
                        unpacked |= ((instr >> 8) & 0xF) << 1;
287
0
                        unpacked |= ((instr >> 7) & 0x1) << 11;
288
0
                    },
289
0
                    RiscvRelocationEncoding::J => {
290
0
                        bits = 20;
291
0
                        let instr = LittleEndian::read_u32(buf);
292
0
293
0
                        unpacked = ((instr >> 31) & 0x1) << 20;
294
0
                        unpacked |= ((instr >> 21) & 0x3FF) << 1;
295
0
                        unpacked |= ((instr >> 20) & 0x1) << 11;
296
0
                        unpacked |= ((instr >> 12) & 0xFF) << 12;
297
0
                    },
298
0
                    RiscvRelocationEncoding::BC => {
299
0
                        bits = 9;
300
0
                        let instr = u32::from(LittleEndian::read_u16(buf));
301
0
302
0
                        unpacked = ((instr >> 12) & 0x1) << 8;
303
0
                        unpacked |= ((instr >> 10) & 0x3) << 3;
304
0
                        unpacked |= ((instr >> 5) & 0x3) << 6;
305
0
                        unpacked |= ((instr >> 3) & 0x3) << 1;
306
0
                        unpacked |= ((instr >> 2) & 0x1) << 5;
307
0
                    },
308
0
                    RiscvRelocationEncoding::JC => {
309
0
                        bits = 12;
310
0
                        let instr = u32::from(LittleEndian::read_u16(buf));
311
0
312
0
                        unpacked = ((instr >> 12) & 0x1) << 11;
313
0
                        unpacked |= ((instr >> 11) & 0x1) << 4;
314
0
                        unpacked |= ((instr >> 9) & 0x3) << 8;
315
0
                        unpacked |= ((instr >> 8) & 0x1) << 10;
316
0
                        unpacked |= ((instr >> 7) & 0x1) << 6;
317
0
                        unpacked |= ((instr >> 6) & 0x1) << 7;
318
0
                        unpacked |= ((instr >> 3) & 0x7) << 1;
319
0
                        unpacked |= ((instr >> 2) & 0x1) << 5;
320
0
                    },
321
0
                    RiscvRelocationEncoding::HI20 => {
322
0
                        bits = 32;
323
0
                        let instr = LittleEndian::read_u32(buf);
324
0
325
0
                        unpacked = ((instr >> 12) & 0xFFFFF) << 12;
326
0
                        // There's a problem here. We don't know the lower
327
0
                        // bits of the value that is being read, but they do matter
328
0
                        // if this thing would get relocated. luckily, riscv only does
329
0
                        // relative relocations so this should never happen, and we
330
0
                        // should be fine with just returning the value without adjustment
331
0
                    },
332
0
                    RiscvRelocationEncoding::LO12 => {
333
0
                        bits = 12;
334
0
                        let instr = LittleEndian::read_u32(buf);
335
0
336
0
                        unpacked = (instr >> 20) & 0xFFF;
337
0
                    },
338
0
                    RiscvRelocationEncoding::LO12S => {
339
0
                        bits = 12;
340
0
                        let instr = LittleEndian::read_u32(buf);
341
0
342
0
                        unpacked = (instr >> 7) & 0x1F;
343
0
                        unpacked |= ((instr >> 25) & 0x7F) << 5;
344
0
                    },
345
                    RiscvRelocationEncoding::SPLIT32 => {
346
0
                        bits = 32;
347
0
                        let instr1 = LittleEndian::read_u32(&buf[..4]);
348
0
                        let instr2 = LittleEndian::read_u32(&buf[4..]);
349
350
0
                        unpacked = ((instr1 >> 12) & 0xFFFFF) << 12;
351
0
                        let mut lower: u32 = (instr2 >> 20) & 0xFFF;
352
353
                        // sign extend the lower part and then add them
354
0
                        lower = (lower ^ 0x800).wrapping_sub(0x800);
355
0
                        unpacked = unpacked.wrapping_add(lower)
356
357
                    },
358
                    RiscvRelocationEncoding::SPLIT32S => {
359
0
                        bits = 32;
360
0
                        let instr1 = LittleEndian::read_u32(&buf[..4]);
361
0
                        let instr2 = LittleEndian::read_u32(&buf[4..]);
362
363
0
                        unpacked = ((instr1 >> 12) & 0xFFFFF) << 12;
364
0
                        let mut lower: u32 = (instr2 >> 7) & 0x1F;
365
0
                        lower |= ((instr2 >> 25) & 0x7F) << 5;
366
367
                        // sign extend the lower part and then add them
368
0
                        lower = (lower ^ 0x800).wrapping_sub(0x800);
369
0
                        unpacked = unpacked.wrapping_add(lower)
370
                    },
371
                }
372
373
                // sign extension
374
0
                let offset = 1u64 << (bits - 1);
375
0
                let value: u64 = (unpacked as u64 ^ offset).wrapping_sub(offset);
376
377
0
                value as i64 as isize
378
            }
379
        }
380
0
    }
Unexecuted instantiation: <dynasmrt::riscv::RiscvRelocation as dynasmrt::relocations::Relocation>::read_value
Unexecuted instantiation: <dynasmrt::riscv::RiscvRelocation as dynasmrt::relocations::Relocation>::read_value
381
0
    fn kind(&self) -> RelocationKind {
382
0
        self.0.kind
383
0
    }
Unexecuted instantiation: <dynasmrt::riscv::RiscvRelocation as dynasmrt::relocations::Relocation>::kind
Unexecuted instantiation: <dynasmrt::riscv::RiscvRelocation as dynasmrt::relocations::Relocation>::kind
384
0
    fn page_size() -> usize {
385
0
        4096
386
0
    }
Unexecuted instantiation: <dynasmrt::riscv::RiscvRelocation as dynasmrt::relocations::Relocation>::page_size
Unexecuted instantiation: <dynasmrt::riscv::RiscvRelocation as dynasmrt::relocations::Relocation>::page_size
387
}
388
389
/// A RISC-V Assembler. This is aliased here for backwards compatability.
390
pub type Assembler = crate::Assembler<RiscvRelocation>;
391
/// A RISC-V AssemblyModifier. This is aliased here for backwards compatability.
392
pub type AssemblyModifier<'a> = crate::Modifier<'a, RiscvRelocation>;
393
/// A RISC-V UncommittedModifier. This is aliased here for backwards compatability.
394
pub type UncommittedModifier<'a> = crate::UncommittedModifier<'a>;
395
396
// these should explicitly never be inlined, as this is the slow path.
397
// that's also why these aren't made generic.
398
399
/// Handler for `u32` out-of-range riscv64 & riscv32 immediates.
400
#[inline(never)]
401
0
pub fn immediate_out_of_range_unsigned_32(immediate: u32) -> ! {
402
0
    panic!("Cannot assemble this RISC-V instruction. Immediate {immediate} is out of range.")
Unexecuted instantiation: dynasmrt::riscv::immediate_out_of_range_unsigned_32
Unexecuted instantiation: dynasmrt::riscv::immediate_out_of_range_unsigned_32
403
}
404
405
/// Handler for `i32` out-of-range riscv64 & riscv32 immediates.
406
#[inline(never)]
407
0
pub fn immediate_out_of_range_signed_32(immediate: i32) -> ! {
408
0
    panic!("Cannot assemble this RISC-V instruction. Immediate {immediate} is out of range.")
Unexecuted instantiation: dynasmrt::riscv::immediate_out_of_range_signed_32
Unexecuted instantiation: dynasmrt::riscv::immediate_out_of_range_signed_32
409
}
410
/// Handler for `u64` out-of-range riscv64 & riscv32 immediates.
411
#[inline(never)]
412
0
pub fn immediate_out_of_range_unsigned_64(immediate: u64) -> ! {
413
0
    panic!("Cannot assemble this RISC-V instruction. Immediate {immediate} is out of range.")
Unexecuted instantiation: dynasmrt::riscv::immediate_out_of_range_unsigned_64
Unexecuted instantiation: dynasmrt::riscv::immediate_out_of_range_unsigned_64
414
}
415
416
/// Handler for `i64` out-of-range riscv64 & riscv32 immediates.
417
#[inline(never)]
418
0
pub fn immediate_out_of_range_signed_64(immediate: i64) -> ! {
419
0
    panic!("Cannot assemble this RISC-V instruction. Immediate {immediate} is out of range.")
Unexecuted instantiation: dynasmrt::riscv::immediate_out_of_range_signed_64
Unexecuted instantiation: dynasmrt::riscv::immediate_out_of_range_signed_64
420
}
421
422
/// Handler for invalid riscv64 & riscv32 registers.
423
#[inline(never)]
424
0
pub fn invalid_register(register: u8) -> ! {
425
0
    panic!("Cannot assemble this RISC-V instruction. Register x{register} cannot be encoded.")
Unexecuted instantiation: dynasmrt::riscv::invalid_register
Unexecuted instantiation: dynasmrt::riscv::invalid_register
426
}
427
428
429
/// 4 or 8-byte general purpopse registers, where X0 is the zero register
430
/// When using the RV32/64E profile, only the first 16 registers are valid
431
#[allow(missing_docs)]
432
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
433
pub enum RX {
434
    X0 = 0x00, X1 = 0x01, X2 = 0x02, X3 = 0x03,
435
    X4 = 0x04, X5 = 0x05, X6 = 0x06, X7 = 0x07,
436
    X8 = 0x08, X9 = 0x09, X10= 0x0A, X11= 0x0B,
437
    X12= 0x0C, X13= 0x0D, X14= 0x0E, X15= 0x0F,
438
    X16= 0x10, X17= 0x11, X18= 0x12, X19= 0x13,
439
    X20= 0x14, X21= 0x15, X22= 0x16, X23= 0x17,
440
    X24= 0x18, X25= 0x19, X26= 0x1A, X27= 0x1B,
441
    X28= 0x1C, X29= 0x1D, X30= 0x1E, X31= 0x1F,
442
}
443
reg_impls!(RX);
444
445
/// 4, 8 or 16-byte floating point registers
446
#[allow(missing_docs)]
447
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
448
pub enum RF {
449
    F0 = 0x00, F1 = 0x01, F2 = 0x02, F3 = 0x03,
450
    F4 = 0x04, F5 = 0x05, F6 = 0x06, F7 = 0x07,
451
    F8 = 0x08, F9 = 0x09, F10= 0x0A, F11= 0x0B,
452
    F12= 0x0C, F13= 0x0D, F14= 0x0E, F15= 0x0F,
453
    F16= 0x10, F17= 0x11, F18= 0x12, F19= 0x13,
454
    F20= 0x14, F21= 0x15, F22= 0x16, F23= 0x17,
455
    F24= 0x18, F25= 0x19, F26= 0x1A, F27= 0x1B,
456
    F28= 0x1C, F29= 0x1D, F30= 0x1E, F31= 0x1F,
457
}
458
reg_impls!(RF);
459
460
461
#[cfg(test)]
462
mod tests {
463
    use super::RX::*;
464
    use crate::Register;
465
466
    #[test]
467
    fn reg_code() {
468
        assert_eq!(X2.code(), 2);
469
    }
470
471
    #[test]
472
    fn reg_code_from() {
473
        assert_eq!(u8::from(X24), 0x18);
474
    }
475
}