use crate::rv64::test_utils::{TEST_BASE_ADDR, execute, initialize_state};
use crate::{ExecutionError, ProgramCounter, RegisterFile, VirtualMemory};
use ab_riscv_primitives::prelude::*;
#[test]
fn test_cm_push_ra_only_decrements_sp() {
let mut state = initialize_state([Rv64ZcmpInstruction::CmPush {
urlist: ZcmpUrlist::try_from_raw(4).unwrap(),
stack_adj: 16,
}]);
let sp_start = TEST_BASE_ADDR + 0x400;
state.regs.write(Reg::Sp, sp_start);
state.regs.write(Reg::Ra, 0xDEAD_0001);
execute(&mut state).unwrap();
assert_eq!(state.regs.read(Reg::Sp), sp_start - 16);
assert_eq!(state.memory.read::<u64>(sp_start - 8).unwrap(), 0xDEAD_0001);
}
#[test]
fn test_cm_push_stack_adj_adds_extra() {
let mut state = initialize_state([Rv64ZcmpInstruction::CmPush {
urlist: ZcmpUrlist::try_from_raw(4).unwrap(),
stack_adj: 32,
}]);
let sp_start = TEST_BASE_ADDR + 0x400;
state.regs.write(Reg::Sp, sp_start);
state.regs.write(Reg::Ra, 0xABCD);
execute(&mut state).unwrap();
assert_eq!(state.regs.read(Reg::Sp), sp_start - 32);
assert_eq!(state.memory.read::<u64>(sp_start - 8).unwrap(), 0xABCD);
}
#[test]
fn test_cm_push_ra_s0() {
let mut state = initialize_state([Rv64ZcmpInstruction::CmPush {
urlist: ZcmpUrlist::try_from_raw(5).unwrap(),
stack_adj: 16,
}]);
let sp_start = TEST_BASE_ADDR + 0x400;
state.regs.write(Reg::Sp, sp_start);
state.regs.write(Reg::Ra, 0x1111);
state.regs.write(Reg::S0, 0x2222);
execute(&mut state).unwrap();
assert_eq!(state.regs.read(Reg::Sp), sp_start - 16);
assert_eq!(state.memory.read::<u64>(sp_start - 8).unwrap(), 0x1111);
assert_eq!(state.memory.read::<u64>(sp_start - 16).unwrap(), 0x2222);
}
#[test]
fn test_cm_push_ra_s0_s1() {
let mut state = initialize_state([Rv64ZcmpInstruction::CmPush {
urlist: ZcmpUrlist::try_from_raw(6).unwrap(),
stack_adj: 32,
}]);
let sp_start = TEST_BASE_ADDR + 0x500;
state.regs.write(Reg::Sp, sp_start);
state.regs.write(Reg::Ra, 0x1111);
state.regs.write(Reg::S0, 0x2222);
state.regs.write(Reg::S1, 0x3333);
execute(&mut state).unwrap();
assert_eq!(state.regs.read(Reg::Sp), sp_start - 32);
assert_eq!(state.memory.read::<u64>(sp_start - 8).unwrap(), 0x1111);
assert_eq!(state.memory.read::<u64>(sp_start - 16).unwrap(), 0x2222);
assert_eq!(state.memory.read::<u64>(sp_start - 24).unwrap(), 0x3333);
}
#[test]
fn test_cm_push_max_urlist() {
let mut state = initialize_state([Rv64ZcmpInstruction::CmPush {
urlist: ZcmpUrlist::try_from_raw(15).unwrap(),
stack_adj: 112,
}]);
let sp_start = TEST_BASE_ADDR + 0x800;
state.regs.write(Reg::Sp, sp_start);
state.regs.write(Reg::Ra, 0x1);
execute(&mut state).unwrap();
assert_eq!(state.regs.read(Reg::Sp), sp_start - 112);
}
#[test]
fn test_cm_pop_restores_and_increments_sp() {
let mut state = initialize_state([Rv64ZcmpInstruction::CmPop {
urlist: ZcmpUrlist::try_from_raw(4).unwrap(),
stack_adj: 16,
}]);
let sp_start = TEST_BASE_ADDR + 0x300;
state.regs.write(Reg::Sp, sp_start);
let new_sp = sp_start + 16;
state.memory.write::<u64>(new_sp - 8, 0xCAFE_BABE).unwrap();
execute(&mut state).unwrap();
assert_eq!(state.regs.read(Reg::Sp), new_sp);
assert_eq!(state.regs.read(Reg::Ra), 0xCAFE_BABE);
}
#[test]
fn test_cm_pop_ra_s0() {
let mut state = initialize_state([Rv64ZcmpInstruction::CmPop {
urlist: ZcmpUrlist::try_from_raw(5).unwrap(),
stack_adj: 16,
}]);
let sp_start = TEST_BASE_ADDR + 0x300;
state.regs.write(Reg::Sp, sp_start);
let new_sp = sp_start + 16;
state.memory.write::<u64>(new_sp - 8, 0x1111).unwrap();
state.memory.write::<u64>(new_sp - 16, 0x2222).unwrap();
execute(&mut state).unwrap();
assert_eq!(state.regs.read(Reg::Ra), 0x1111);
assert_eq!(state.regs.read(Reg::S0), 0x2222);
assert_eq!(state.regs.read(Reg::Sp), new_sp);
}
#[test]
fn test_cm_pop_stack_adj_extra() {
let mut state = initialize_state([Rv64ZcmpInstruction::CmPop {
urlist: ZcmpUrlist::try_from_raw(4).unwrap(),
stack_adj: 48,
}]);
let sp_start = TEST_BASE_ADDR + 0x300;
state.regs.write(Reg::Sp, sp_start);
let new_sp = sp_start + 48;
state.memory.write::<u64>(new_sp - 8, 0xABCD).unwrap();
execute(&mut state).unwrap();
assert_eq!(state.regs.read(Reg::Sp), new_sp);
assert_eq!(state.regs.read(Reg::Ra), 0xABCD);
}
#[test]
fn test_cm_popretz_zeros_a0_and_jumps() {
let mut state = initialize_state([Rv64ZcmpInstruction::CmPopretz {
urlist: ZcmpUrlist::try_from_raw(4).unwrap(),
stack_adj: 16,
}]);
let sp_start = TEST_BASE_ADDR + 0x300;
state.regs.write(Reg::Sp, sp_start);
state.regs.write(Reg::A0, 0xFFFF);
let new_sp = sp_start + 16;
let return_addr = TEST_BASE_ADDR + 0x500;
state.memory.write::<u64>(new_sp - 8, return_addr).unwrap();
execute(&mut state).unwrap();
assert_eq!(state.regs.read(Reg::A0), 0);
assert_eq!(state.instruction_fetcher.get_pc(), return_addr);
assert_eq!(state.regs.read(Reg::Sp), new_sp);
}
#[test]
fn test_cm_popretz_clears_lsb_of_return_addr() {
let mut state = initialize_state([Rv64ZcmpInstruction::CmPopretz {
urlist: ZcmpUrlist::try_from_raw(4).unwrap(),
stack_adj: 16,
}]);
let sp_start = TEST_BASE_ADDR + 0x300;
state.regs.write(Reg::Sp, sp_start);
let new_sp = sp_start + 16;
state
.memory
.write::<u64>(new_sp - 8, TEST_BASE_ADDR + 0x101)
.unwrap();
execute(&mut state).unwrap();
assert_eq!(state.instruction_fetcher.get_pc(), TEST_BASE_ADDR + 0x100);
}
#[test]
fn test_cm_popret_restores_and_jumps() {
let mut state = initialize_state([Rv64ZcmpInstruction::CmPopret {
urlist: ZcmpUrlist::try_from_raw(5).unwrap(),
stack_adj: 16,
}]);
let sp_start = TEST_BASE_ADDR + 0x300;
state.regs.write(Reg::Sp, sp_start);
let new_sp = sp_start + 16;
let return_addr = TEST_BASE_ADDR + 0x600;
state.memory.write::<u64>(new_sp - 8, return_addr).unwrap();
state.memory.write::<u64>(new_sp - 16, 0x9999).unwrap();
execute(&mut state).unwrap();
assert_eq!(state.regs.read(Reg::Ra), return_addr);
assert_eq!(state.regs.read(Reg::S0), 0x9999);
assert_eq!(state.regs.read(Reg::Sp), new_sp);
assert_eq!(state.instruction_fetcher.get_pc(), return_addr);
}
#[test]
fn test_cm_popret_clears_lsb_of_return_addr() {
let mut state = initialize_state([Rv64ZcmpInstruction::CmPopret {
urlist: ZcmpUrlist::try_from_raw(4).unwrap(),
stack_adj: 16,
}]);
let sp_start = TEST_BASE_ADDR + 0x300;
state.regs.write(Reg::Sp, sp_start);
let new_sp = sp_start + 16;
state
.memory
.write::<u64>(new_sp - 8, TEST_BASE_ADDR + 0x101)
.unwrap();
execute(&mut state).unwrap();
assert_eq!(state.instruction_fetcher.get_pc(), TEST_BASE_ADDR + 0x100);
}
#[test]
fn test_cm_mva01s_copies_to_a0_a1() {
let mut state = initialize_state([Rv64ZcmpInstruction::CmMva01s {
r1s: Reg::S2,
r2s: Reg::S3,
}]);
state.regs.write(Reg::S2, 0x1111);
state.regs.write(Reg::S3, 0x2222);
execute(&mut state).unwrap();
assert_eq!(state.regs.read(Reg::A0), 0x1111);
assert_eq!(state.regs.read(Reg::A1), 0x2222);
assert_eq!(state.regs.read(Reg::S2), 0x1111);
assert_eq!(state.regs.read(Reg::S3), 0x2222);
}
#[test]
fn test_cm_mva01s_reads_before_write() {
let mut state = initialize_state([Rv64ZcmpInstruction::CmMva01s {
r1s: Reg::S0,
r2s: Reg::S1,
}]);
state.regs.write(Reg::S0, 0xAAAA);
state.regs.write(Reg::S1, 0xBBBB);
execute(&mut state).unwrap();
assert_eq!(state.regs.read(Reg::A0), 0xAAAA);
assert_eq!(state.regs.read(Reg::A1), 0xBBBB);
}
#[test]
fn test_cm_mvsa01_copies_a0_a1_to_s_regs() {
let mut state = initialize_state([Rv64ZcmpInstruction::CmMvsa01 {
r1s: Reg::S4,
r2s: Reg::S5,
}]);
state.regs.write(Reg::A0, 0x3333);
state.regs.write(Reg::A1, 0x4444);
execute(&mut state).unwrap();
assert_eq!(state.regs.read(Reg::S4), 0x3333);
assert_eq!(state.regs.read(Reg::S5), 0x4444);
assert_eq!(state.regs.read(Reg::A0), 0x3333);
assert_eq!(state.regs.read(Reg::A1), 0x4444);
}
#[test]
fn test_push_pop_round_trip_ra_s0_s1() {
let sp_start = TEST_BASE_ADDR + 0x800;
let mut state = initialize_state([Rv64ZcmpInstruction::CmPush {
urlist: ZcmpUrlist::try_from_raw(6).unwrap(),
stack_adj: 32,
}]);
state.regs.write(Reg::Sp, sp_start);
state.regs.write(Reg::Ra, 0xAAAA);
state.regs.write(Reg::S0, 0xBBBB);
state.regs.write(Reg::S1, 0xCCCC);
execute(&mut state).unwrap();
let sp_after_push = state.regs.read(Reg::Sp);
state.regs.write(Reg::Ra, 0);
state.regs.write(Reg::S0, 0);
state.regs.write(Reg::S1, 0);
let mut state2 = initialize_state([Rv64ZcmpInstruction::CmPop {
urlist: ZcmpUrlist::try_from_raw(6).unwrap(),
stack_adj: 32,
}]);
state2.regs.write(Reg::Sp, sp_after_push);
for offset in (0u64..128).step_by(8) {
let addr = sp_after_push + offset;
if let Ok(v) = state.memory.read::<u64>(addr) {
state2.memory.write::<u64>(addr, v).unwrap();
}
}
execute(&mut state2).unwrap();
assert_eq!(state2.regs.read(Reg::Ra), 0xAAAA);
assert_eq!(state2.regs.read(Reg::S0), 0xBBBB);
assert_eq!(state2.regs.read(Reg::S1), 0xCCCC);
assert_eq!(state2.regs.read(Reg::Sp), sp_start);
}
#[test]
fn test_cm_push_oob_memory() {
let mut state = initialize_state([Rv64ZcmpInstruction::CmPush {
urlist: ZcmpUrlist::try_from_raw(4).unwrap(),
stack_adj: 16,
}]);
state.regs.write(Reg::Sp, 4);
let result = execute(&mut state);
assert!(matches!(result, Err(ExecutionError::MemoryAccess(_))));
}