Skip to main content

ab_riscv_interpreter/rv32/zce/zcmp/
rv32_zcmp_helpers.rs

1//! Opaque helpers for Zcmp extension
2
3use crate::{ExecutionError, RegisterFile, VirtualMemory};
4use ab_riscv_primitives::prelude::*;
5use core::ops::ControlFlow;
6
7/// Execute CM.PUSH: store registers below sp, then decrement sp
8#[inline]
9#[doc(hidden)]
10#[expect(clippy::type_complexity, reason = "Internal helper")]
11#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
12pub fn do_push<Reg, Regs, Memory, CustomError>(
13    regs: &mut Regs,
14    memory: &mut Memory,
15    urlist: ZcmpUrlist<Reg>,
16    stack_adj: u8,
17) -> Result<ControlFlow<(), (Reg, u32)>, ExecutionError<Reg::Type, CustomError>>
18where
19    Reg: ZcmpRegister<Type = u32>,
20    Regs: RegisterFile<Reg>,
21    Memory: VirtualMemory,
22{
23    let sp = regs.read(Reg::SP);
24    // Store from sp-4 downward, highest-priority register first
25    let mut store_addr = u64::from(sp.wrapping_sub(size_of::<Reg::Type>() as u32));
26    for reg in urlist.reg_list() {
27        memory.write(store_addr, regs.read(reg))?;
28        store_addr = store_addr.wrapping_sub(size_of::<Reg::Type>() as u64);
29    }
30    Ok(ControlFlow::Continue((
31        Reg::SP,
32        sp.wrapping_sub(u32::from(stack_adj)),
33    )))
34}
35
36/// Execute CM.POP and variants: restore registers and increment sp.
37/// Returns the value of ra (x1) for use with popret/popretz.
38#[inline]
39#[doc(hidden)]
40#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
41pub fn do_pop<Reg, Regs, Memory, CustomError>(
42    regs: &mut Regs,
43    memory: &mut Memory,
44    urlist: ZcmpUrlist<Reg>,
45    stack_adj: u8,
46) -> Result<u32, ExecutionError<Reg::Type, CustomError>>
47where
48    Reg: ZcmpRegister<Type = u32>,
49    Regs: RegisterFile<Reg>,
50    Memory: VirtualMemory,
51{
52    let sp = regs.read(Reg::SP);
53    let new_sp = sp.wrapping_add(u32::from(stack_adj));
54    // Restore from [new_sp-4, new_sp-8, ...], matching push order
55    let mut load_addr = u64::from(new_sp.wrapping_sub(size_of::<Reg::Type>() as u32));
56    for reg in urlist.reg_list() {
57        let value = memory.read::<u32>(load_addr)?;
58        regs.write(reg, value);
59        load_addr = load_addr.wrapping_sub(size_of::<Reg::Type>() as u64);
60    }
61    regs.write(Reg::SP, new_sp);
62    Ok(regs.read(Reg::RA))
63}