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