Skip to main content

ab_riscv_primitives/instructions/rv64/c/
zca.rs

1//! RV64 Zca extension
2
3#[cfg(test)]
4mod tests;
5
6use crate::instructions::Instruction;
7use crate::instructions::utils::I24;
8use crate::registers::general_purpose::Register;
9use ab_riscv_macros::instruction;
10use core::fmt;
11
12/// RISC-V RV64 Zca compressed instruction set
13#[instruction]
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15#[rustfmt::skip]
16pub enum Rv64ZcaInstruction<Reg> {
17    // Quadrant 00
18    /// C.ADDI4SPN  rd' = sp + nzuimm  (nzuimm ∈ 4..1020 step 4)
19    CAddi4spn { rd: Reg, nzuimm: u16 },
20    /// C.LW  rd' = sext(mem32\[rs1' + uimm])
21    CLw { rd: Reg, rs1: Reg, uimm: u8 },
22    /// C.LD  rd' = mem64\[rs1' + uimm]
23    CLd { rd: Reg, rs1: Reg, uimm: u8 },
24    /// C.SW  mem32\[rs1' + uimm] = rs2'
25    CSw { rs1: Reg, rs2: Reg, uimm: u8 },
26    /// C.SD  mem64\[rs1' + uimm] = rs2'
27    CSd { rs1: Reg, rs2: Reg, uimm: u8 },
28
29    // Quadrant 01
30    /// C.NOP  (ADDI x0, x0, 0 with rd==x0 and nzimm==0)
31    CNop,
32    /// C.ADDI  rd += nzimm  (rd != x0)
33    CAddi { rd: Reg, nzimm: i8 },
34    /// C.ADDIW  rd = sext((rd\[31:0] + imm)\[31:0])  (rd != x0)
35    CAddiw { rd: Reg, imm: i8 },
36    /// C.LI  rd = sext(imm)  (rd=x0 is a HINT)
37    CLi { rd: Reg, imm: i8 },
38    /// C.ADDI16SP  sp += nzimm*16  (nzimm != 0)
39    CAddi16sp { nzimm: i16 },
40    /// C.LUI  rd = sext(nzimm << 12)  (rd != x0, rd != x2, nzimm != 0)
41    CLui { rd: Reg, nzimm: I24 },
42    /// C.SRLI  rd' >>= shamt  (logical right shift; shamt=0 with rd'=x0 is a HINT)
43    CSrli { rd: Reg, shamt: u8 },
44    /// C.SRAI  rd' >>= shamt  (arithmetic right shift; shamt=0 with rd'=x0 is a HINT)
45    CSrai { rd: Reg, shamt: u8 },
46    /// C.ANDI  rd' &= sext(imm)
47    CAndi { rd: Reg, imm: i8 },
48    /// C.SUB  rd' -= rs2'
49    CSub { rd: Reg, rs2: Reg },
50    /// C.XOR  rd' ^= rs2'
51    CXor { rd: Reg, rs2: Reg },
52    /// C.OR   rd' |= rs2'
53    COr { rd: Reg, rs2: Reg },
54    /// C.AND  rd' &= rs2'
55    CAnd { rd: Reg, rs2: Reg },
56    /// C.SUBW  rd' = sext((rd'\[31:0] - rs2'\[31:0])\[31:0])
57    CSubw { rd: Reg, rs2: Reg },
58    /// C.ADDW  rd' = sext((rd'\[31:0] + rs2'\[31:0])\[31:0])
59    CAddw { rd: Reg, rs2: Reg },
60    /// C.J  pc += sext(imm)
61    CJ { imm: i16 },
62    /// C.BEQZ  if rs1' == 0: pc += sext(imm)
63    CBeqz { rs1: Reg, imm: i16 },
64    /// C.BNEZ  if rs1' != 0: pc += sext(imm)
65    CBnez { rs1: Reg, imm: i16 },
66
67    // Quadrant 10
68    /// C.SLLI  rd <<= shamt  (rd=x0 or shamt=0 is a HINT)
69    CSlli { rd: Reg, shamt: u8 },
70    /// C.LWSP  rd = sext(mem32\[sp + uimm])  (rd != x0)
71    CLwsp { rd: Reg, uimm: u8 },
72    /// C.LDSP  rd = mem64\[sp + uimm]  (rd != x0)
73    CLdsp { rd: Reg, uimm: u16 },
74    /// C.JR  pc = rs1  (rs1 != x0)
75    CJr { rs1: Reg },
76    /// C.MV  rd = rs2  (rs2 != x0; rd=x0 is a HINT)
77    CMv { rd: Reg, rs2: Reg },
78    /// C.EBREAK
79    CEbreak,
80    /// C.JALR  ra = pc+2; pc = rs1  (rs1 != x0)
81    CJalr { rs1: Reg },
82    /// C.ADD  rd += rs2  (rs2 != x0; rd=x0 is a HINT)
83    CAdd { rd: Reg, rs2: Reg },
84    /// C.SWSP  mem32\[sp + uimm] = rs2
85    CSwsp { rs2: Reg, uimm: u8 },
86    /// C.SDSP  mem64\[sp + uimm] = rs2
87    CSdsp { rs2: Reg, uimm: u16 },
88
89    // Unimplemented/illegal
90    CUnimp,
91}
92
93#[instruction]
94const impl<Reg> Instruction for Rv64ZcaInstruction<Reg>
95where
96    Reg: [const] Register<Type = u64>,
97{
98    type Reg = Reg;
99
100    #[inline(always)]
101    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic(const))]
102    fn try_decode(instruction: u32) -> Option<Self> {
103        /// Map a 3-bit "prime" register field to an absolute register number
104        #[inline(always)]
105        const fn prime_reg_bits(bits: u8) -> u8 {
106            bits + 8
107        }
108
109        /// Reconstruct the CB-type branch offset used by C.BEQZ / C.BNEZ.
110        ///
111        /// Bit layout in the 16-bit instruction word:
112        /// ```text
113        ///   imm[8]   = inst[12]
114        ///   imm[4:3] = inst[11:10]
115        ///   imm[7:6] = inst[6:5]
116        ///   imm[2:1] = inst[4:3]
117        ///   imm[5]   = inst[2]
118        /// imm[0] is always 0 (2-byte aligned).
119        /// ```
120        #[inline(always)]
121        const fn decode_cb_branch_imm(inst: u16) -> i16 {
122            let imm8 = ((inst >> 12u8) & 1).cast_signed();
123            let imm4_3 = ((inst >> 10u8) & 0b11).cast_signed();
124            let imm7_6 = ((inst >> 5u8) & 0b11).cast_signed();
125            let imm2_1 = ((inst >> 3u8) & 0b11).cast_signed();
126            let imm5 = ((inst >> 2u8) & 1).cast_signed();
127            let raw =
128                (imm8 << 8u8) | (imm7_6 << 6u8) | (imm5 << 5u8) | (imm4_3 << 3u8) | (imm2_1 << 1u8);
129            // Sign-extend from bit 8 (9-bit immediate -> i16)
130            (raw << 7u8) >> 7u8
131        }
132
133        /// Reconstruct the CJ-type jump offset used by C.J.
134        ///
135        /// Bit layout:
136        /// ```text
137        ///   imm[11]  = inst[12]
138        ///   imm[4]   = inst[11]
139        ///   imm[9:8] = inst[10:9]
140        ///   imm[10]  = inst[8]
141        ///   imm[6]   = inst[7]
142        ///   imm[7]   = inst[6]
143        ///   imm[3:1] = inst[5:3]
144        ///   imm[5]   = inst[2]
145        /// imm[0] is always 0 (2-byte aligned).
146        /// ```
147        #[inline(always)]
148        const fn decode_cj_imm(inst: u16) -> i16 {
149            let imm11 = ((inst >> 12u8) & 1).cast_signed();
150            let imm4 = ((inst >> 11u8) & 1).cast_signed();
151            let imm9_8 = ((inst >> 9u8) & 0b11).cast_signed();
152            let imm10 = ((inst >> 8u8) & 1).cast_signed();
153            let imm6 = ((inst >> 7u8) & 1).cast_signed();
154            let imm7 = ((inst >> 6u8) & 1).cast_signed();
155            let imm3_1 = ((inst >> 3u8) & 0b111).cast_signed();
156            let imm5 = ((inst >> 2u8) & 1).cast_signed();
157            let raw = (imm11 << 11u8)
158                | (imm10 << 10u8)
159                | (imm9_8 << 8u8)
160                | (imm7 << 7u8)
161                | (imm6 << 6u8)
162                | (imm5 << 5u8)
163                | (imm4 << 4u8)
164                | (imm3_1 << 1u8);
165            // Sign-extend from bit 11 (12-bit immediate -> i16)
166            (raw << 4u8) >> 4u8
167        }
168
169        let inst = instruction as u16;
170        let quadrant = inst & 0b11;
171        let funct3 = ((inst >> 13u8) & 0b111) as u8;
172
173        match quadrant {
174            // Quadrant 00
175            0b00 => match funct3 {
176                // C.ADDI4SPN
177                // nzuimm[5:4]  = inst[12:11]
178                // nzuimm[9:6]  = inst[10:7]
179                // nzuimm[2]    = inst[6]
180                // nzuimm[3]    = inst[5]
181                0b000 => {
182                    let imm5_4 = (inst >> 11u8) & 0b11;
183                    let imm9_6 = (inst >> 7u8) & 0xf;
184                    let imm2 = (inst >> 6u8) & 1;
185                    let imm3 = (inst >> 5u8) & 1;
186                    let nzuimm = (imm9_6 << 6u8) | (imm5_4 << 4u8) | (imm3 << 3u8) | (imm2 << 2u8);
187                    if nzuimm == 0 {
188                        if inst == 0 {
189                            Some(Self::CUnimp)
190                        } else {
191                            // Reserved encoding
192                            None
193                        }
194                    } else {
195                        let rd_bits = prime_reg_bits(((inst >> 2u8) & 0b111) as u8);
196                        let rd = Reg::from_bits(rd_bits)?;
197                        Some(Self::CAddi4spn { rd, nzuimm })
198                    }
199                }
200                // C.LW
201                // uimm[5:3] = inst[12:10], uimm[2] = inst[6], uimm[6] = inst[5]
202                0b010 => {
203                    let uimm5_3 = ((inst >> 10u8) & 0b111) as u8;
204                    let uimm2 = ((inst >> 6u8) & 1) as u8;
205                    let uimm6 = ((inst >> 5u8) & 1) as u8;
206                    let uimm = (uimm6 << 6u8) | (uimm5_3 << 3u8) | (uimm2 << 2u8);
207                    let rs1_bits = prime_reg_bits(((inst >> 7u8) & 0b111) as u8);
208                    let rd_bits = prime_reg_bits(((inst >> 2u8) & 0b111) as u8);
209                    let rs1 = Reg::from_bits(rs1_bits)?;
210                    let rd = Reg::from_bits(rd_bits)?;
211                    Some(Self::CLw { rd, rs1, uimm })
212                }
213                // C.LD
214                // uimm[5:3] = inst[12:10], uimm[7:6] = inst[6:5]
215                0b011 => {
216                    let uimm5_3 = ((inst >> 10u8) & 0b111) as u8;
217                    let uimm7_6 = ((inst >> 5u8) & 0b11) as u8;
218                    let uimm = (uimm7_6 << 6u8) | (uimm5_3 << 3u8);
219                    let rs1_bits = prime_reg_bits(((inst >> 7u8) & 0b111) as u8);
220                    let rd_bits = prime_reg_bits(((inst >> 2u8) & 0b111) as u8);
221                    let rs1 = Reg::from_bits(rs1_bits)?;
222                    let rd = Reg::from_bits(rd_bits)?;
223                    Some(Self::CLd { rd, rs1, uimm })
224                }
225                // C.SW  (same uimm layout as C.LW)
226                0b110 => {
227                    let uimm5_3 = ((inst >> 10u8) & 0b111) as u8;
228                    let uimm2 = ((inst >> 6u8) & 1) as u8;
229                    let uimm6 = ((inst >> 5u8) & 1) as u8;
230                    let uimm = (uimm6 << 6u8) | (uimm5_3 << 3u8) | (uimm2 << 2u8);
231                    let rs1_bits = prime_reg_bits(((inst >> 7u8) & 0b111) as u8);
232                    let rs2_bits = prime_reg_bits(((inst >> 2u8) & 0b111) as u8);
233                    let rs1 = Reg::from_bits(rs1_bits)?;
234                    let rs2 = Reg::from_bits(rs2_bits)?;
235                    Some(Self::CSw { rs1, rs2, uimm })
236                }
237                // C.SD (same uimm layout as C.LD)
238                0b111 => {
239                    let uimm5_3 = ((inst >> 10u8) & 0b111) as u8;
240                    let uimm7_6 = ((inst >> 5u8) & 0b11) as u8;
241                    let uimm = (uimm7_6 << 6u8) | (uimm5_3 << 3u8);
242                    let rs1_bits = prime_reg_bits(((inst >> 7u8) & 0b111) as u8);
243                    let rs2_bits = prime_reg_bits(((inst >> 2u8) & 0b111) as u8);
244                    let rs1 = Reg::from_bits(rs1_bits)?;
245                    let rs2 = Reg::from_bits(rs2_bits)?;
246                    Some(Self::CSd { rs1, rs2, uimm })
247                }
248                // funct3 = 001/100/101 are reserved in Zca Q00 (used by Zcb)
249                _ => None,
250            },
251
252            // Quadrant 01
253            0b01 => match funct3 {
254                // C.NOP (rd=x0) / C.ADDI (rd!=x0)
255                // nzimm[5] = inst[12], nzimm[4:0] = inst[6:2]
256                0b000 => {
257                    let rd_bits = ((inst >> 7u8) & 0x1f) as u8;
258                    let imm5 = ((inst >> 12u8) & 1) as u8;
259                    let imm4_0 = ((inst >> 2u8) & 0x1f) as u8;
260                    let imm_raw = (imm5 << 5u8) | imm4_0;
261                    // Sign-extend 6-bit immediate to i8
262                    let nzimm = ((imm_raw.cast_signed()) << 2u8) >> 2u8;
263                    if rd_bits == 0 && nzimm == 0 {
264                        Some(Self::CNop)
265                    } else {
266                        let rd = Reg::from_bits(rd_bits)?;
267                        Some(Self::CAddi { rd, nzimm })
268                    }
269                }
270                // C.ADDIW
271                // imm[5] = inst[12], imm[4:0] = inst[6:2]
272                0b001 => {
273                    let rd_bits = ((inst >> 7u8) & 0x1f) as u8;
274                    // rd=x0 is reserved
275                    if rd_bits == 0 {
276                        None?;
277                    }
278                    let rd = Reg::from_bits(rd_bits)?;
279                    let imm5 = ((inst >> 12u8) & 1) as u8;
280                    let imm4_0 = ((inst >> 2u8) & 0x1f) as u8;
281                    let imm_raw = (imm5 << 5u8) | imm4_0;
282                    let imm = ((imm_raw.cast_signed()) << 2u8) >> 2u8;
283                    Some(Self::CAddiw { rd, imm })
284                }
285                // C.LI  rd = sext(imm)  (rd=x0 is a HINT, still decoded)
286                // imm[5] = inst[12], imm[4:0] = inst[6:2]
287                0b010 => {
288                    let rd_bits = ((inst >> 7u8) & 0x1f) as u8;
289                    let rd = Reg::from_bits(rd_bits)?;
290                    let imm5 = ((inst >> 12u8) & 1) as u8;
291                    let imm4_0 = ((inst >> 2u8) & 0x1f) as u8;
292                    let imm_raw = (imm5 << 5u8) | imm4_0;
293                    let imm = ((imm_raw.cast_signed()) << 2u8) >> 2u8;
294                    Some(Self::CLi { rd, imm })
295                }
296                // C.ADDI16SP (rd=x2) / C.LUI (rd!=x0, rd!=x2)
297                0b011 => {
298                    let rd_bits = ((inst >> 7u8) & 0x1f) as u8;
299                    if rd_bits == 2 {
300                        // C.ADDI16SP
301                        // nzimm[9]   = inst[12]
302                        // nzimm[4]   = inst[6]
303                        // nzimm[6]   = inst[5]
304                        // nzimm[8:7] = inst[4:3]
305                        // nzimm[5]   = inst[2]
306                        let imm9 = ((inst >> 12u8) & 1).cast_signed();
307                        let imm4 = ((inst >> 6u8) & 1).cast_signed();
308                        let imm6 = ((inst >> 5u8) & 1).cast_signed();
309                        let imm8_7 = ((inst >> 3u8) & 0b11).cast_signed();
310                        let imm5 = ((inst >> 2u8) & 1).cast_signed();
311                        let raw = (imm9 << 9u8)
312                            | (imm8_7 << 7u8)
313                            | (imm6 << 6u8)
314                            | (imm5 << 5u8)
315                            | (imm4 << 4u8);
316                        if raw == 0 {
317                            None?;
318                        }
319                        // Sign-extend from bit 9 (10-bit nzimm -> i16)
320                        let nzimm = (raw << 6u8) >> 6u8;
321                        Some(Self::CAddi16sp { nzimm })
322                    } else {
323                        // C.LUI (rd=x0 is a hint, still decoded)
324                        let rd = Reg::from_bits(rd_bits)?;
325                        // nzimm[17]    = inst[12]
326                        // nzimm[16:12] = inst[6:2]
327                        let imm17 = ((inst >> 12u8) & 1) as i32;
328                        let imm16_12 = ((inst >> 2u8) & 0x1f) as i32;
329                        let raw = (imm17 << 17u8) | (imm16_12 << 12u8);
330                        if raw == 0 {
331                            None?;
332                        }
333                        // Sign-extend from bit 17 (18-bit nzimm -> i32)
334                        let nzimm = I24::from_i32((raw << 14u8) >> 14u8);
335                        Some(Self::CLui { rd, nzimm })
336                    }
337                }
338                // C.SRLI / C.SRAI / C.ANDI / arithmetic pairs
339                0b100 => {
340                    let funct2 = ((inst >> 10u8) & 0b11) as u8;
341                    let rd_bits = prime_reg_bits(((inst >> 7u8) & 0b111) as u8);
342                    match funct2 {
343                        // C.SRLI  shamt[5]=inst[12], shamt[4:0]=inst[6:2]
344                        // shamt=0 is a HINT, still decoded
345                        0b00 => {
346                            let rd = Reg::from_bits(rd_bits)?;
347                            let shamt5 = ((inst >> 12u8) & 1) as u8;
348                            let shamt40 = ((inst >> 2u8) & 0x1f) as u8;
349                            Some(Self::CSrli {
350                                rd,
351                                shamt: (shamt5 << 5u8) | shamt40,
352                            })
353                        }
354                        // C.SRAI  (same shamt layout as C.SRLI)
355                        // shamt=0 is a HINT, still decoded
356                        0b01 => {
357                            let rd = Reg::from_bits(rd_bits)?;
358                            let shamt5 = ((inst >> 12u8) & 1) as u8;
359                            let shamt40 = ((inst >> 2u8) & 0x1f) as u8;
360                            Some(Self::CSrai {
361                                rd,
362                                shamt: (shamt5 << 5u8) | shamt40,
363                            })
364                        }
365                        // C.ANDI  imm[5]=inst[12], imm[4:0]=inst[6:2]
366                        0b10 => {
367                            let rd = Reg::from_bits(rd_bits)?;
368                            let imm5 = ((inst >> 12u8) & 1) as u8;
369                            let imm4_0 = ((inst >> 2u8) & 0x1f) as u8;
370                            let imm_raw = (imm5 << 5u8) | imm4_0;
371                            let imm = ((imm_raw.cast_signed()) << 2u8) >> 2u8;
372                            Some(Self::CAndi { rd, imm })
373                        }
374                        // Arithmetic: sub-selected by inst[12] and inst[6:5]
375                        0b11 => {
376                            let bit12 = (inst >> 12u8) & 1;
377                            let funct2b = ((inst >> 5u8) & 0b11) as u8;
378                            let rs2_bits = prime_reg_bits(((inst >> 2u8) & 0b111) as u8);
379                            let rd = Reg::from_bits(rd_bits)?;
380                            let rs2 = Reg::from_bits(rs2_bits)?;
381                            if bit12 == 0 {
382                                match funct2b {
383                                    0b00 => Some(Self::CSub { rd, rs2 }),
384                                    0b01 => Some(Self::CXor { rd, rs2 }),
385                                    0b10 => Some(Self::COr { rd, rs2 }),
386                                    0b11 => Some(Self::CAnd { rd, rs2 }),
387                                    _ => None,
388                                }
389                            } else {
390                                // funct2b=10/11 reserved in Zca
391                                match funct2b {
392                                    0b00 => Some(Self::CSubw { rd, rs2 }),
393                                    0b01 => Some(Self::CAddw { rd, rs2 }),
394                                    // 10/11 used by Zcb
395                                    _ => None,
396                                }
397                            }
398                        }
399                        _ => None,
400                    }
401                }
402                // C.J
403                0b101 => Some(Self::CJ {
404                    imm: decode_cj_imm(inst),
405                }),
406                // C.BEQZ
407                0b110 => {
408                    let rs1_bits = prime_reg_bits(((inst >> 7u8) & 0b111) as u8);
409                    let rs1 = Reg::from_bits(rs1_bits)?;
410                    Some(Self::CBeqz {
411                        rs1,
412                        imm: decode_cb_branch_imm(inst),
413                    })
414                }
415                // C.BNEZ
416                0b111 => {
417                    let rs1_bits = prime_reg_bits(((inst >> 7u8) & 0b111) as u8);
418                    let rs1 = Reg::from_bits(rs1_bits)?;
419                    Some(Self::CBnez {
420                        rs1,
421                        imm: decode_cb_branch_imm(inst),
422                    })
423                }
424                _ => None,
425            },
426
427            // Quadrant 10
428            0b10 => match funct3 {
429                // C.SLLI  shamt[5]=inst[12], shamt[4:0]=inst[6:2]
430                // rd=x0 or shamt=0 is a HINT, still decoded
431                0b000 => {
432                    let rd_bits = ((inst >> 7u8) & 0x1f) as u8;
433                    let rd = Reg::from_bits(rd_bits)?;
434                    let shamt5 = ((inst >> 12u8) & 1) as u8;
435                    let shamt40 = ((inst >> 2u8) & 0x1f) as u8;
436                    Some(Self::CSlli {
437                        rd,
438                        shamt: (shamt5 << 5u8) | shamt40,
439                    })
440                }
441                // C.LWSP  uimm[5]=inst[12], uimm[4:2]=inst[6:4], uimm[7:6]=inst[3:2]
442                // rd=x0 is reserved
443                0b010 => {
444                    let rd_bits = ((inst >> 7u8) & 0x1f) as u8;
445                    if rd_bits == 0 {
446                        None?;
447                    }
448                    let rd = Reg::from_bits(rd_bits)?;
449                    let uimm5 = ((inst >> 12u8) & 1) as u8;
450                    let uimm42 = ((inst >> 4u8) & 0b111) as u8;
451                    let uimm76 = ((inst >> 2u8) & 0b11) as u8;
452                    let uimm = (uimm76 << 6u8) | (uimm5 << 5u8) | (uimm42 << 2u8);
453                    Some(Self::CLwsp { rd, uimm })
454                }
455                // C.LDSP  uimm[5]=inst[12], uimm[4:3]=inst[6:5], uimm[8:6]=inst[4:2]
456                // rd=x0 is reserved
457                0b011 => {
458                    let rd_bits = ((inst >> 7u8) & 0x1f) as u8;
459                    if rd_bits == 0 {
460                        None?;
461                    }
462                    let rd = Reg::from_bits(rd_bits)?;
463                    let uimm5 = (inst >> 12u8) & 1;
464                    let uimm43 = (inst >> 5u8) & 0b11;
465                    let uimm86 = (inst >> 2u8) & 0b111;
466                    let uimm = (uimm86 << 6u8) | (uimm5 << 5u8) | (uimm43 << 3u8);
467                    Some(Self::CLdsp { rd, uimm })
468                }
469                // C.JR / C.MV / C.EBREAK / C.JALR / C.ADD
470                0b100 => {
471                    let rs1_bits = ((inst >> 7u8) & 0x1f) as u8;
472                    let rs2_bits = ((inst >> 2u8) & 0x1f) as u8;
473                    let bit12 = (inst >> 12u8) & 1;
474                    if bit12 == 0 {
475                        if rs2_bits == 0 {
476                            // C.JR  (rs1=x0 is reserved)
477                            if rs1_bits == 0 {
478                                None?;
479                            }
480                            let rs1 = Reg::from_bits(rs1_bits)?;
481                            Some(Self::CJr { rs1 })
482                        } else {
483                            // C.MV  (rs2!=x0; rd=x0 is a HINT, still decoded)
484                            let rd = Reg::from_bits(rs1_bits)?;
485                            let rs2 = Reg::from_bits(rs2_bits)?;
486                            Some(Self::CMv { rd, rs2 })
487                        }
488                    } else if rs2_bits == 0 {
489                        if rs1_bits == 0 {
490                            // C.EBREAK
491                            Some(Self::CEbreak)
492                        } else {
493                            // C.JALR  (rs1!=x0)
494                            let rs1 = Reg::from_bits(rs1_bits)?;
495                            Some(Self::CJalr { rs1 })
496                        }
497                    } else {
498                        // C.ADD  (rs2!=x0; rd=x0 is a HINT, still decoded)
499                        let rd = Reg::from_bits(rs1_bits)?;
500                        let rs2 = Reg::from_bits(rs2_bits)?;
501                        Some(Self::CAdd { rd, rs2 })
502                    }
503                }
504                // C.SWSP  uimm[5:2]=inst[12:9], uimm[7:6]=inst[8:7]
505                0b110 => {
506                    let rs2_bits = ((inst >> 2u8) & 0x1f) as u8;
507                    let rs2 = Reg::from_bits(rs2_bits)?;
508                    let uimm52 = ((inst >> 9u8) & 0xf) as u8;
509                    let uimm76 = ((inst >> 7u8) & 0b11) as u8;
510                    let uimm = (uimm76 << 6u8) | (uimm52 << 2u8);
511                    Some(Self::CSwsp { rs2, uimm })
512                }
513                // C.SDSP  uimm[5:3]=inst[12:10], uimm[8:6]=inst[9:7]
514                0b111 => {
515                    let rs2_bits = ((inst >> 2u8) & 0x1f) as u8;
516                    let rs2 = Reg::from_bits(rs2_bits)?;
517                    let uimm53 = (inst >> 10u8) & 0b111;
518                    let uimm86 = (inst >> 7u8) & 0b111;
519                    let uimm = (uimm86 << 6u8) | (uimm53 << 3u8);
520                    Some(Self::CSdsp { rs2, uimm })
521                }
522                _ => None,
523            },
524
525            // Quadrant 11 = 32-bit instructions
526            _ => None,
527        }
528    }
529
530    #[inline(always)]
531    fn alignment() -> u8 {
532        align_of::<u16>() as u8
533    }
534
535    #[inline(always)]
536    fn size(&self) -> u8 {
537        size_of::<u16>() as u8
538    }
539}
540
541#[instruction]
542impl<Reg> fmt::Display for Rv64ZcaInstruction<Reg>
543where
544    Reg: fmt::Display,
545{
546    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
547        match self {
548            Self::CAddi4spn { rd, nzuimm } => write!(f, "c.addi4spn {rd}, sp, {nzuimm}"),
549            Self::CLw { rd, rs1, uimm } => write!(f, "c.lw {rd}, {uimm}({rs1})"),
550            Self::CLd { rd, rs1, uimm } => write!(f, "c.ld {rd}, {uimm}({rs1})"),
551            Self::CSw { rs1, rs2, uimm } => write!(f, "c.sw {rs2}, {uimm}({rs1})"),
552            Self::CSd { rs1, rs2, uimm } => write!(f, "c.sd {rs2}, {uimm}({rs1})"),
553            Self::CNop => write!(f, "c.nop"),
554            Self::CAddi { rd, nzimm } => write!(f, "c.addi {rd}, {nzimm}"),
555            Self::CAddiw { rd, imm } => write!(f, "c.addiw {rd}, {imm}"),
556            Self::CLi { rd, imm } => write!(f, "c.li {rd}, {imm}"),
557            Self::CAddi16sp { nzimm } => write!(f, "c.addi16sp sp, {nzimm}"),
558            Self::CLui { rd, nzimm } => write!(f, "c.lui {rd}, 0x{:x}", nzimm >> 12u8),
559            Self::CSrli { rd, shamt } => write!(f, "c.srli {rd}, {shamt}"),
560            Self::CSrai { rd, shamt } => write!(f, "c.srai {rd}, {shamt}"),
561            Self::CAndi { rd, imm } => write!(f, "c.andi {rd}, {imm}"),
562            Self::CSub { rd, rs2 } => write!(f, "c.sub {rd}, {rs2}"),
563            Self::CXor { rd, rs2 } => write!(f, "c.xor {rd}, {rs2}"),
564            Self::COr { rd, rs2 } => write!(f, "c.or {rd}, {rs2}"),
565            Self::CAnd { rd, rs2 } => write!(f, "c.and {rd}, {rs2}"),
566            Self::CSubw { rd, rs2 } => write!(f, "c.subw {rd}, {rs2}"),
567            Self::CAddw { rd, rs2 } => write!(f, "c.addw {rd}, {rs2}"),
568            Self::CJ { imm } => write!(f, "c.j {imm}"),
569            Self::CBeqz { rs1, imm } => write!(f, "c.beqz {rs1}, {imm}"),
570            Self::CBnez { rs1, imm } => write!(f, "c.bnez {rs1}, {imm}"),
571            Self::CSlli { rd, shamt } => write!(f, "c.slli {rd}, {shamt}"),
572            Self::CLwsp { rd, uimm } => write!(f, "c.lwsp {rd}, {uimm}(sp)"),
573            Self::CLdsp { rd, uimm } => write!(f, "c.ldsp {rd}, {uimm}(sp)"),
574            Self::CJr { rs1 } => write!(f, "c.jr {rs1}"),
575            Self::CMv { rd, rs2 } => write!(f, "c.mv {rd}, {rs2}"),
576            Self::CEbreak => write!(f, "c.ebreak"),
577            Self::CJalr { rs1 } => write!(f, "c.jalr {rs1}"),
578            Self::CAdd { rd, rs2 } => write!(f, "c.add {rd}, {rs2}"),
579            Self::CSwsp { rs2, uimm } => write!(f, "c.swsp {rs2}, {uimm}(sp)"),
580            Self::CSdsp { rs2, uimm } => write!(f, "c.sdsp {rs2}, {uimm}(sp)"),
581            Self::CUnimp => write!(f, "c.unimp"),
582        }
583    }
584}