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