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