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