Skip to main content

ab_riscv_interpreter/rv32/zce/zcmp/
rv32_zcmp_helpers.rs

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