Skip to main content

ab_riscv_primitives/instructions/rv32/c/
zca.rs

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