Skip to main content

ab_riscv_interpreter/rv64/zce/
zcmp.rs

1//! RV64 Zcmp extension
2
3pub mod rv64_zcmp_helpers;
4#[cfg(test)]
5mod tests;
6
7use crate::{
8    ExecutableInstruction, ExecutableInstructionCsr, ExecutableInstructionOperands, ExecutionError,
9    ProgramCounter, RegisterFile, Rs1Rs2OperandValues, Rs1Rs2Operands, SystemInstructionHandler,
10    VirtualMemory,
11};
12use ab_riscv_macros::instruction_execution;
13use ab_riscv_primitives::prelude::*;
14use core::ops::ControlFlow;
15
16#[instruction_execution]
17impl<Reg> ExecutableInstructionOperands for Rv64ZcmpInstruction<Reg> where
18    Reg: ZcmpRegister<Type = u64>
19{
20}
21
22#[instruction_execution]
23impl<Reg, ExtState, CustomError> ExecutableInstructionCsr<ExtState, CustomError>
24    for Rv64ZcmpInstruction<Reg>
25where
26    Reg: ZcmpRegister<Type = u64>,
27{
28}
29
30#[instruction_execution]
31impl<Reg, Regs, ExtState, Memory, PC, InstructionHandler, CustomError>
32    ExecutableInstruction<Regs, ExtState, Memory, PC, InstructionHandler, CustomError>
33    for Rv64ZcmpInstruction<Reg>
34where
35    Reg: ZcmpRegister<Type = u64>,
36    Regs: RegisterFile<Reg>,
37    Memory: VirtualMemory,
38    PC: ProgramCounter<Reg::Type, Memory, CustomError>,
39    InstructionHandler: SystemInstructionHandler<Reg, Regs, Memory, PC, CustomError>,
40{
41    #[inline(always)]
42    fn execute(
43        self,
44        Rs1Rs2OperandValues {
45            rs1_value,
46            rs2_value,
47        }: Rs1Rs2OperandValues<<Self::Reg as Register>::Type>,
48        regs: &mut Regs,
49        _ext_state: &mut ExtState,
50        memory: &mut Memory,
51        program_counter: &mut PC,
52        _system_instruction_handler: &mut InstructionHandler,
53    ) -> Result<
54        ControlFlow<(), (Self::Reg, <Self::Reg as Register>::Type)>,
55        ExecutionError<Reg::Type, CustomError>,
56    > {
57        match self {
58            Self::CmPush { urlist, stack_adj } => {
59                rv64_zcmp_helpers::do_push(regs, memory, urlist, stack_adj)
60            }
61            Self::CmPop { urlist, stack_adj } => {
62                rv64_zcmp_helpers::do_pop(regs, memory, urlist, stack_adj)?;
63                Ok(ControlFlow::Continue(Default::default()))
64            }
65            Self::CmPopretz { urlist, stack_adj } => {
66                let ra_val = rv64_zcmp_helpers::do_pop(regs, memory, urlist, stack_adj)?;
67                // Zero a0 before returning
68                regs.write(Reg::A0, 0);
69                // Jump to ra with LSB cleared (RISC-V mode bit)
70                let target = ra_val & !1;
71                program_counter
72                    .set_pc(memory, target)
73                    .map(|control_flow| control_flow.map_continue(|()| Default::default()))
74                    .map_err(ExecutionError::from)
75            }
76            Self::CmPopret { urlist, stack_adj } => {
77                let ra_val = rv64_zcmp_helpers::do_pop(regs, memory, urlist, stack_adj)?;
78                // Jump to ra with LSB cleared (RISC-V mode bit)
79                let target = ra_val & !1;
80                program_counter
81                    .set_pc(memory, target)
82                    .map(|control_flow| control_flow.map_continue(|()| Default::default()))
83                    .map_err(ExecutionError::from)
84            }
85            Self::CmMva01s { rs1: _, rs2: _ } => {
86                // Read both sources before any write to avoid aliasing
87                let v1 = rs1_value;
88                let v2 = rs2_value;
89                regs.write(Reg::A0, v1);
90                Ok(ControlFlow::Continue((Reg::A1, v2)))
91            }
92            Self::CmMvsa01 { rs1, rs2 } => {
93                // Read both sources before any write to avoid aliasing
94                let a0_val = regs.read(Reg::A0);
95                let a1_val = regs.read(Reg::A1);
96                regs.write(rs1, a0_val);
97                Ok(ControlFlow::Continue((rs2, a1_val)))
98            }
99        }
100    }
101}