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, ExecutionError, ProgramCounter, RegisterFile, SystemInstructionHandler,
9    VirtualMemory,
10};
11use ab_riscv_macros::instruction_execution;
12use ab_riscv_primitives::prelude::*;
13use core::ops::ControlFlow;
14
15#[instruction_execution]
16impl<Reg, Regs, ExtState, Memory, PC, InstructionHandler, CustomError>
17    ExecutableInstruction<Regs, ExtState, Memory, PC, InstructionHandler, CustomError>
18    for Rv64ZcmpInstruction<Reg>
19where
20    Reg: ZcmpRegister<Type = u64>,
21    Regs: RegisterFile<Reg>,
22    Memory: VirtualMemory,
23    PC: ProgramCounter<Reg::Type, Memory, CustomError>,
24    InstructionHandler: SystemInstructionHandler<Reg, Regs, Memory, PC, CustomError>,
25{
26    #[inline(always)]
27    fn execute(
28        self,
29        regs: &mut Regs,
30        _ext_state: &mut ExtState,
31        memory: &mut Memory,
32        program_counter: &mut PC,
33        _system_instruction_handler: &mut InstructionHandler,
34    ) -> Result<ControlFlow<()>, ExecutionError<Reg::Type, CustomError>> {
35        match self {
36            Self::CmPush { urlist, stack_adj } => {
37                rv64_zcmp_helpers::do_push(regs, memory, urlist, stack_adj)?;
38            }
39            Self::CmPop { urlist, stack_adj } => {
40                rv64_zcmp_helpers::do_pop(regs, memory, urlist, stack_adj)?;
41            }
42            Self::CmPopretz { urlist, stack_adj } => {
43                let ra_val = rv64_zcmp_helpers::do_pop(regs, memory, urlist, stack_adj)?;
44                // Zero a0 before returning
45                regs.write(Reg::A0, 0);
46                // Jump to ra with LSB cleared (RISC-V mode bit)
47                let target = ra_val & !1;
48                return program_counter
49                    .set_pc(memory, target)
50                    .map_err(ExecutionError::from);
51            }
52            Self::CmPopret { urlist, stack_adj } => {
53                let ra_val = rv64_zcmp_helpers::do_pop(regs, memory, urlist, stack_adj)?;
54                // Jump to ra with LSB cleared (RISC-V mode bit)
55                let target = ra_val & !1;
56                return program_counter
57                    .set_pc(memory, target)
58                    .map_err(ExecutionError::from);
59            }
60            Self::CmMva01s { r1s, r2s } => {
61                // Read both sources before any write to avoid aliasing
62                let v1 = regs.read(r1s);
63                let v2 = regs.read(r2s);
64                regs.write(Reg::A0, v1);
65                regs.write(Reg::A1, v2);
66            }
67            Self::CmMvsa01 { r1s, r2s } => {
68                // Read both sources before any write to avoid aliasing
69                let a0_val = regs.read(Reg::A0);
70                let a1_val = regs.read(Reg::A1);
71                regs.write(r1s, a0_val);
72                regs.write(r2s, a1_val);
73            }
74        }
75
76        Ok(ControlFlow::Continue(()))
77    }
78}