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}