use std::mem::offset_of;
use cranelift_codegen::ir::{InstBuilder, condcodes::IntCC};
use super::super::family::Family;
use super::super::registers::{control, get, mask, masks, set, xpsr};
use super::super::state::State;
use super::{Emitter, Fault, Instruction, Translated, Undefined, bits};
const APSR: u32 = APSR_NZCVQ | mask(xpsr::GE);
const APSR_NZCVQ: u32 =
mask(xpsr::N) | mask(xpsr::Z) | mask(xpsr::C) | mask(xpsr::V) | mask(xpsr::Q);
pub(super) fn ignores_it_condition(instruction: Instruction) -> bool {
match instruction {
Instruction::Narrow(hw) => {
let hw = u32::from(hw);
match bits(hw, 15, 12) {
0b1101 => bits(hw, 11, 9) != 0b111,
0b1011 => {
let op = bits(hw, 11, 8);
op == 0b1110
|| op & 0b0101 == 0b0001
|| bits(hw, 11, 5) == 0b011_0011
|| (op == 0b1111 && bits(hw, 3, 0) != 0)
}
_ => false,
}
}
Instruction::Wide(hw1, hw2) => {
let (hw1, hw2) = (u32::from(hw1), u32::from(hw2));
bits(hw1, 15, 11) == 0b11110
&& bits(hw2, 15, 14) == 0b10
&& bits(hw2, 12, 12) == 0
&& bits(hw1, 9, 7) != 0b111
}
}
}
pub(super) fn conditional_branch_supervisor_call(e: &mut Emitter<'_, '_>, hw: u16) -> Translated {
let hw = u32::from(hw);
match bits(hw, 11, 8) {
0b1110 => Err(Undefined),
0b1111 => {
e.raise(Fault::SupervisorCall);
Ok(())
}
cond => {
let target = relative(e, sign_extend(bits(hw, 7, 0) << 1, 9));
let holds = e.condition(cond);
e.branch_if(holds, target);
Ok(())
}
}
}
pub(super) fn branch16(e: &mut Emitter<'_, '_>, hw: u16) {
let target = relative(e, sign_extend(bits(u32::from(hw), 10, 0) << 1, 12));
e.branch_to(target);
}
pub(super) fn change_processor_state(e: &mut Emitter<'_, '_>, hw: u16) {
let hw = u32::from(hw);
let affected = bits(hw, 1, 0);
if affected == 0 {
return;
}
let disable = e.constant(bits(hw, 4, 4));
let affected = e.constant(affected);
e.call_plain(change_masks as *const () as usize, &[disable, affected]);
e.end_block();
}
pub(super) fn compare_branch(e: &mut Emitter<'_, '_>, hw: u16) {
let hw = u32::from(hw);
let imm32 = (bits(hw, 9, 9) << 6) | (bits(hw, 7, 3) << 1);
let value = e.reg(bits(hw, 2, 0));
let taken = if bits(hw, 11, 11) == 1 {
IntCC::NotEqual
} else {
IntCC::Equal
};
let holds = e.b.ins().icmp_imm_s(taken, value, 0);
let target = relative(e, imm32);
e.branch_if(holds, target);
}
pub(super) fn breakpoint(e: &mut Emitter<'_, '_>, hw: u16) {
if bits(u32::from(hw), 7, 0) == SEMIHOSTING_TRAP {
e.raise(Fault::HostCall);
} else {
e.raise(Fault::Breakpoint);
}
}
const SEMIHOSTING_TRAP: u32 = 0xAB;
pub(super) fn if_then_hints(e: &mut Emitter<'_, '_>, hw: u16) {
let hw = u32::from(hw);
if bits(hw, 3, 0) == 0 {
hint(e, bits(hw, 7, 4));
} else {
e.set_next_it(bits(hw, 7, 0));
}
}
pub(super) fn branches_miscellaneous_control(
e: &mut Emitter<'_, '_>,
hw1: u16,
hw2: u16,
) -> Translated {
let (hw1, hw2) = (u32::from(hw1), u32::from(hw2));
let op = bits(hw1, 10, 4);
match bits(hw2, 14, 12) {
0b000 | 0b010 => match op {
_ if op & 0b011_1000 != 0b011_1000 => {
let imm = (bits(hw1, 10, 10) << 20)
| (bits(hw2, 11, 11) << 19)
| (bits(hw2, 13, 13) << 18)
| (bits(hw1, 5, 0) << 12)
| (bits(hw2, 10, 0) << 1);
let target = relative(e, sign_extend(imm, 21));
let holds = e.condition(bits(hw1, 9, 6));
e.branch_if(holds, target);
Ok(())
}
0b011_1000 | 0b011_1001 => {
move_to_special(e, hw1, hw2);
Ok(())
}
0b011_1010 if bits(hw2, 10, 8) == 0 => {
hint(e, bits(hw2, 7, 0));
Ok(())
}
0b011_1011 => miscellaneous_control(e, hw2),
0b011_1110 | 0b011_1111 => move_from_special(e, hw2),
_ => Err(Undefined),
},
0b001 | 0b011 => {
let target = relative(e, long_offset(hw1, hw2));
e.branch_to(target);
Ok(())
}
0b101 | 0b111 => {
let target = relative(e, long_offset(hw1, hw2));
let link = e.constant(e.next_pc() | 1);
e.set_reg(14, link);
e.branch_to(target);
Ok(())
}
_ => Err(Undefined),
}
}
fn hint(e: &mut Emitter<'_, '_>, op: u32) {
match op {
0b0010 => e.wait(true),
0b0011 => e.wait(false),
0b0100 => {
let one = e.constant(1);
e.store_field(offset_of!(State, event), one);
}
_ => {}
}
}
fn miscellaneous_control(e: &mut Emitter<'_, '_>, hw2: u32) -> Translated {
match bits(hw2, 7, 4) {
0b0010 => super::memory::clear_exclusive(e),
0b0100 | 0b0101 => {}
0b0110 => e.end_block(),
_ => return Err(Undefined),
}
Ok(())
}
fn move_from_special(e: &mut Emitter<'_, '_>, hw2: u32) -> Translated {
let d = bits(hw2, 11, 8);
if d == 15 {
return Err(Undefined);
}
let sysm = bits(hw2, 7, 0);
let which = e.constant(sysm);
let read = read_special as *const () as usize;
let value = if sysm >> 3 < 0b00010 {
e.call(read, &[which])
} else {
e.call_plain(read, &[which])
};
let value = if d == 13 {
e.b.ins().band_imm_s(value, !3)
} else {
value
};
e.set_reg(d, value);
Ok(())
}
fn move_to_special(e: &mut Emitter<'_, '_>, hw1: u32, hw2: u32) {
let sysm = bits(hw2, 7, 0);
let value = e.reg(bits(hw1, 3, 0));
let which = e.constant((bits(hw2, 11, 10) << 8) | sysm);
let write = write_special as *const () as usize;
if (16..=19).contains(&sysm) {
e.call_plain(write, &[which, value]);
} else {
e.call(write, &[which, value]);
}
if matches!(sysm >> 3, 0b00001 | 0b00010) {
e.end_block();
}
}
fn relative(e: &Emitter<'_, '_>, imm32: u32) -> u32 {
e.pc().wrapping_add(4).wrapping_add(imm32)
}
fn sign_extend(value: u32, width: u32) -> u32 {
let above = 32 - width;
((value << above).cast_signed() >> above).cast_unsigned()
}
fn long_offset(hw1: u32, hw2: u32) -> u32 {
let s = bits(hw1, 10, 10);
let i1 = !(bits(hw2, 13, 13) ^ s) & 1;
let i2 = !(bits(hw2, 11, 11) ^ s) & 1;
let imm =
(s << 24) | (i1 << 23) | (i2 << 22) | (bits(hw1, 9, 0) << 12) | (bits(hw2, 10, 0) << 1);
sign_extend(imm, 25)
}
extern "C" fn change_masks(core: &mut Family, disable: u32, affected: u32) -> u32 {
if !core.state.privileged() {
return 0;
}
let faultmask = affected & 0b01 != 0 && (disable == 0 || !core.below_priority_zero());
if affected & 0b10 != 0 {
core.state.primask = disable;
}
if faultmask {
core.state.faultmask = disable;
}
0
}
extern "C" fn read_special(core: &mut Family, sysm: u32) -> u32 {
let state = &core.state;
let privileged = state.privileged();
match (sysm >> 3, sysm & 0b111) {
(0b00000, low) => {
let ipsr = if low & 0b001 == 0 { 0 } else { state.ipsr };
let apsr = if low & 0b100 != 0 {
0
} else if core.features.dsp {
state.xpsr() & APSR
} else {
state.xpsr() & APSR_NZCVQ
};
ipsr | apsr
}
(0b00001, 0b000) if privileged => state.msp(),
(0b00001, 0b001) if privileged => state.psp(),
(0b00010, 0b000) if privileged => get(masks::PM, state.primask),
(0b00010, 0b001 | 0b010) if privileged => u32::from(core.scs.basepri()),
(0b00010, 0b011) if privileged => get(masks::FM, state.faultmask),
(0b00010, 0b100) => state.control & core.features.control(),
_ => 0,
}
}
fn basepri(value: u32) -> u8 {
u8::try_from(get(masks::BASEPRI, value)).expect("eight bits")
}
extern "C" fn write_special(core: &mut Family, which: u32, value: u32) -> u32 {
let (mask, sysm) = (bits(which, 9, 8), bits(which, 7, 0));
let privileged = core.state.privileged();
let above_hard_fault = !core.below_priority_zero();
let features = core.features;
let state = &mut core.state;
match (sysm >> 3, sysm & 0b111) {
(0b00000, low) if low & 0b100 == 0 => {
if mask & 0b01 != 0 && features.dsp {
state.ge = get(xpsr::GE, value);
}
if mask & 0b10 != 0 {
state.n = get(xpsr::N, value);
state.z = get(xpsr::Z, value);
state.c = get(xpsr::C, value);
state.v = get(xpsr::V, value);
state.q = get(xpsr::Q, value);
}
}
(0b00001, 0b000) if privileged => state.set_msp(value),
(0b00001, 0b001) if privileged => state.set_psp(value),
(0b00010, 0b000) if privileged => state.primask = get(masks::PM, value),
(0b00010, 0b001) if privileged => core.scs.write_basepri(basepri(value)),
(0b00010, 0b010) if privileged => {
let (asked, held) = (get(masks::BASEPRI, value), u32::from(core.scs.basepri()));
if asked != 0 && (asked < held || held == 0) {
core.scs.write_basepri(basepri(value));
}
}
(0b00010, 0b011) if privileged && above_hard_fault => {
state.faultmask = get(masks::FM, value);
}
(0b00010, 0b100) if privileged => {
let mut written = features.control();
if state.handler() {
written = set(control::SPSEL, written, 0);
}
let control = (state.control & !written) | (value & written);
state.set_mode(state.ipsr, control);
}
_ => {}
}
0
}
#[cfg(test)]
mod tests {
use cranelift_codegen::ir::{self, types};
use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext};
use super::super::super::scs::exception::{HARD_FAULT, NMI};
use super::super::testing::spec_of;
use super::super::testing::{Bench, CODE};
use super::super::{decode16, decode32, emit_one};
use super::*;
use crate::emulator::Exit;
use crate::machine::armv7m::{Fpu, MpuRegions, Variant};
#[test]
fn the_apsr_masks_are_the_manuals() {
assert_eq!(APSR_NZCVQ, 0xF800_0000);
assert_eq!(APSR, 0xF80F_0000);
}
fn mrs(d: u16, sysm: u16) -> [u16; 2] {
[0xF3EF, 0x8000 | (d << 8) | sysm]
}
fn msr(n: u16, mask: u16, sysm: u16) -> [u16; 2] {
[0xF380 | n, 0x8000 | (mask << 10) | sysm]
}
const IPSR: u16 = 5;
const MSP: u16 = 8;
const PSP: u16 = 9;
const PRIMASK: u16 = 16;
const BASEPRI: u16 = 17;
const BASEPRI_MAX: u16 = 18;
const FAULTMASK: u16 = 19;
const CONTROL: u16 = 20;
fn after(code: &[u16], instructions: u32, prepare: impl FnOnce(&mut Family)) -> (Bench, Exit) {
let mut bench = Bench::new(code);
prepare(&mut bench.core);
let (exit, ran) = bench.run(instructions);
assert_eq!(ran, u64::from(instructions), "every instruction retires");
(bench, exit)
}
fn state(code: &[u16], instructions: u32, prepare: impl FnOnce(&mut Family)) -> State {
after(code, instructions, prepare).0.core.state.clone()
}
fn next(code: &[u16], prepare: impl FnOnce(&mut Family)) -> u32 {
state(code, 1, prepare).r[15]
}
fn lowered<T>(
halfwords: &[u16],
it: u32,
lower: impl FnOnce(&mut Emitter<'_, '_>, Instruction) -> T,
) -> (T, bool, ir::Function) {
let instruction = match *halfwords {
[hw] => Instruction::Narrow(hw),
[hw1, hw2] => Instruction::Wide(hw1, hw2),
_ => panic!("an instruction is one or two halfwords"),
};
let mut function = ir::Function::new();
function
.signature
.params
.push(ir::AbiParam::new(types::I64));
let mut context = FunctionBuilderContext::new();
let mut builder = FunctionBuilder::new(&mut function, &mut context);
let entry = builder.create_block();
builder.append_block_params_for_function_params(entry);
builder.switch_to_block(entry);
let mut e = Emitter::new(&mut builder, types::I64);
e.begin(0, CODE, instruction.length(), it);
let answer = lower(&mut e, instruction);
let ended = e.ended();
(answer, ended, function)
}
fn decoded(halfwords: &[u16], it: u32) -> (Translated, bool) {
let (answer, ended, _) = lowered(halfwords, it, |e, instruction| match instruction {
Instruction::Narrow(hw) => decode16(e, hw),
Instruction::Wide(hw1, hw2) => decode32(e, hw1, hw2),
});
(answer, ended)
}
fn exits(halfwords: &[u16], it: u32) -> usize {
let ((), _, function) = lowered(halfwords, it, emit_one);
super::super::exits_of(&function)
}
fn handling(core: &mut Family, number: u16) {
core.scs.pend(number);
assert_eq!(core.scs.acknowledge(core.masks()), Some(number));
core.state.ipsr = u32::from(number);
}
fn undefined(halfwords: &[u16]) -> bool {
decoded(halfwords, 0).0 == Err(Undefined)
}
fn unprivileged(core: &mut Family) {
core.state.control = mask(control::NPRIV);
}
#[test]
fn b_t1_branches_when_its_condition_holds() {
assert_eq!(next(&[0xD002], |core| core.state.z = 1), 8);
assert_eq!(next(&[0xD002], |core| core.state.z = 0), 2);
let mut bench = Bench::new(&[]);
bench.place(0x100, &[0xD1FC]);
bench.core.state.r[15] = 0x100;
bench.run(1);
assert_eq!(bench.core.state.r[15], 0xFC, "backwards when it's negative");
}
#[test]
fn b_t1_to_itself_waits_for_an_interrupt() {
let mut bench = Bench::new(&[0xD0FE]);
bench.core.state.z = 1;
assert_eq!(bench.run(10), (Exit::Waiting, 1));
assert_eq!(bench.core.state.r[15], CODE);
}
#[test]
fn udf_and_svc_take_the_conditional_branch_space_of_al_and_nv() {
assert!(undefined(&[0xDE00]));
assert!(undefined(&[0xDEFF]));
assert_eq!(decoded(&[0xDF05], 0), (Ok(()), true));
after(&[0xDF05], 1, |_| {});
}
#[test]
fn b_t2_reaches_both_ends_of_its_range() {
assert_eq!(next(&[0xE3FF], |_| {}), 0x802);
let mut bench = Bench::new(&[]);
bench.place(0x1000, &[0xE400]);
bench.core.state.r[15] = 0x1000;
bench.run(1);
assert_eq!(bench.core.state.r[15], 0x804);
}
#[test]
fn b_t2_last_in_an_it_block_runs_under_its_condition() {
let code = [0xBF18, 0xE002];
assert_eq!(state(&code, 2, |core| core.state.z = 1).r[15], 4);
assert_eq!(state(&code, 2, |core| core.state.z = 0).r[15], 10);
}
#[test]
fn cbz_and_cbnz_branch_forward_on_zero_and_nonzero() {
let flags = |core: &mut Family| {
core.state.n = 1;
core.state.c = 1;
};
assert_eq!(next(&[0xB100], flags), 4);
assert_eq!(
next(&[0xB100], |core| {
flags(core);
core.state.r[0] = 1;
}),
2
);
let taken = state(&[0xBBF9], 1, |core| {
flags(core);
core.state.r[1] = 5;
});
assert_eq!(taken.r[15], 4 + 126);
assert_eq!(
(taken.n, taken.z, taken.c, taken.v),
(1, 0, 1, 0),
"the flags stay"
);
assert_eq!(next(&[0xBBF9], |_| {}), 2);
}
#[test]
fn cbz_inside_an_it_block_ignores_the_blocks_condition() {
let code = [0xBF08, 0xB108];
assert_eq!(state(&code, 2, |core| core.state.z = 1).r[15], 8);
assert_eq!(state(&code, 2, |core| core.state.z = 0).r[15], 8);
let nonzero = state(&code, 2, |core| {
core.state.z = 1;
core.state.r[0] = 1;
});
assert_eq!(nonzero.r[15], 4);
}
#[test]
fn cbnz_inside_an_it_block_ignores_the_blocks_condition() {
let code = [0xBF08, 0xB908];
for z in [0, 1] {
let nonzero = state(&code, 2, |core| {
core.state.z = z;
core.state.r[0] = 1;
});
assert_eq!(nonzero.r[15], 8, "Z {z}");
assert_eq!(state(&code, 2, |core| core.state.z = z).r[15], 4, "Z {z}");
}
}
#[test]
fn b_t1_and_t3_inside_an_it_block_test_only_their_own_condition() {
for (code, fallthrough) in [(&[0xBF08, 0xD102][..], 4), (&[0xBF08, 0xF040, 0x8002], 6)] {
assert_eq!(
state(code, 2, |core| core.state.z = 0).r[15],
10,
"{code:04x?}"
);
assert_eq!(
state(code, 2, |core| core.state.z = 1).r[15],
fallthrough,
"{code:04x?}"
);
}
}
#[test]
fn udf_whose_it_condition_fails_is_a_nop() {
let ran = state(&[0xBF08, 0xDE00], 2, |_| {});
assert_eq!((ran.r[15], ran.it), (4, 0));
let ran = state(&[0xBF08, 0xF7F0, 0xA000], 2, |_| {});
assert_eq!((ran.r[15], ran.it), (6, 0));
}
#[test]
fn b_t2_t4_and_bl_inside_an_it_block_but_not_last_run_under_its_condition() {
for (code, fallthrough) in [
(&[0xBF04, 0xE002][..], 4),
(&[0xBF04, 0xF000, 0xB802], 6),
(&[0xBF04, 0xF000, 0xF802], 6),
] {
let taken = state(code, 2, |core| core.state.z = 1);
assert_eq!((taken.r[15], taken.it), (10, 0x08), "{code:04x?}");
let skipped = state(code, 2, |core| core.state.r[14] = 0xEEEE);
assert_eq!(
(skipped.r[15], skipped.it, skipped.r[14]),
(fallthrough, 0x08, 0xEEEE),
"{code:04x?}"
);
}
assert_eq!(
state(&[0xBF04, 0xF000, 0xF802], 2, |core| core.state.z = 1).r[14],
7
);
}
#[test]
fn cps_inside_an_it_block_ignores_the_blocks_condition() {
let code = [0xBF08, 0xB672];
assert_eq!(state(&code, 2, |core| core.state.z = 0).primask, 1);
assert_eq!(state(&code, 2, |core| core.state.z = 1).primask, 1);
}
#[test]
fn cps_sets_and_clears_primask_and_faultmask() {
let masks = |state: State| (state.primask, state.faultmask);
assert_eq!(masks(state(&[0xB672], 1, |_| {})), (1, 0));
assert_eq!(masks(state(&[0xB671], 1, |_| {})), (0, 1));
assert_eq!(masks(state(&[0xB673], 1, |_| {})), (1, 1));
let both = |core: &mut Family| {
core.state.primask = 1;
core.state.faultmask = 1;
};
assert_eq!(masks(state(&[0xB662], 1, both)), (0, 1));
assert_eq!(masks(state(&[0xB661], 1, both)), (1, 0));
assert_eq!(masks(state(&[0xB663], 1, both)), (0, 0));
}
#[test]
fn cps_respects_privilege_and_the_execution_priority() {
let masks = |state: State| (state.primask, state.faultmask);
assert_eq!(masks(state(&[0xB673], 1, unprivileged)), (0, 0));
for number in [NMI, HARD_FAULT] {
let handler = |core: &mut Family| handling(core, number);
assert_eq!(masks(state(&[0xB673], 1, handler)), (1, 0));
let set = |core: &mut Family| {
handler(core);
core.state.faultmask = 1;
};
assert_eq!(masks(state(&[0xB661], 1, set)), (0, 0));
}
let svcall = |core: &mut Family| core.state.ipsr = 11;
assert_eq!(masks(state(&[0xB671], 1, svcall)), (0, 1));
}
#[test]
fn cps_with_neither_mask_changes_nothing() {
let mut expected = Bench::new(&[]).core.state.clone();
expected.r[15] = 2;
assert_eq!(state(&[0xB670], 1, |_| {}), expected);
let both = |core: &mut Family| {
core.state.primask = 1;
core.state.faultmask = 1;
};
assert_eq!(state(&[0xB660], 1, both).primask, 1);
}
#[test]
fn bkpt_raises_and_ends_the_block() {
assert_eq!(decoded(&[0xBE00], 0), (Ok(()), true));
assert_eq!(decoded(&[0xBEAB], 0), (Ok(()), true));
}
#[test]
fn bkpt_inside_an_it_block_raises_whatever_the_condition() {
assert_eq!(exits(&[0xBE00], 0), 1);
assert_eq!(exits(&[0xBE00], 0x08), 1);
assert_eq!(exits(&[0xBE00], 0x14), 1);
assert_eq!(exits(&[0xDF00], 0x08), 3);
}
#[test]
fn it_sets_itstate_for_the_instructions_after_it() {
let after_it = state(&[0xBF0C], 1, |_| {});
assert_eq!(after_it.it, 0x0C);
assert_eq!(after_it.r[15], 2);
assert_eq!(decoded(&[0xBF0C], 0), (Ok(()), false));
}
#[test]
fn an_it_block_runs_each_instruction_under_its_condition() {
let [a, b] = mrs(0, 0);
let [c, d] = mrs(1, 0);
let [f, g] = mrs(2, 0);
let code = [0xBF0C, a, b, c, d, f, g];
let preset = |core: &mut Family, z: u32| {
core.state.z = z;
core.state.r[0] = 0xAAAA;
core.state.r[1] = 0xBBBB;
core.state.r[2] = 0xCCCC;
};
let equal = state(&code, 4, |core| preset(core, 1));
assert_eq!(
equal.r[..3],
[0x4000_0000, 0xBBBB, 0x4000_0000],
"the then runs"
);
assert_eq!(equal.it, 0);
let unequal = state(&code, 4, |core| preset(core, 0));
assert_eq!(unequal.r[..3], [0xAAAA, 0, 0], "the else runs");
assert_eq!(unequal.r[15], 14);
}
#[test]
fn it_inside_an_it_block_sets_itstate_whatever_the_outer_condition() {
let [a, b] = mrs(0, 0);
let code = [0xBF08, 0xBF18, a, b];
let preset = |core: &mut Family, z: u32| {
core.state.z = z;
core.state.n = 1;
core.state.r[0] = 0xAAAA;
};
assert_eq!(state(&code, 2, |core| preset(core, 0)).it, 0x18);
assert_eq!(state(&code, 2, |core| preset(core, 1)).it, 0x18);
assert_eq!(state(&code, 3, |core| preset(core, 0)).r[0], 0x8000_0000);
assert_eq!(state(&code, 3, |core| preset(core, 1)).r[0], 0xAAAA);
}
#[test]
fn it_of_al_with_an_else_runs_every_instruction() {
let mut code = vec![0];
for d in 0..4 {
code.extend(mrs(d, 0));
}
for it in [0xBFEC, 0xBFE6, 0xBFE3, 0xBFEA, 0xBFEF, 0xBFF4] {
code[0] = it;
let ran = state(&code, 5, |core| {
core.state.c = 1;
core.state.r[..4].fill(7);
});
assert_eq!(ran.r[..4], [0x2000_0000; 4], "{it:#06x}");
}
}
#[test]
fn nop_yield_dbg_and_the_unallocated_hints_change_nothing() {
let code = [
0xBF00, 0xBF10, 0xBF50, 0xBFF0, 0xF3AF, 0x8000, 0xF3AF, 0x8001, 0xF3AF, 0x80F3, 0xF3AF,
0x8005, 0xF3AF, 0x80EF,
];
let mut expected = Bench::new(&[]).core.state.clone();
expected.r[15] = 28;
assert_eq!(state(&code, 9, |_| {}), expected);
}
#[test]
fn wfi_waits_for_an_interrupt() {
for (code, length) in [(&[0xBF30][..], 2), (&[0xF3AF, 0x8003][..], 4)] {
let (bench, exit) = after(code, 1, |_| {});
assert_eq!(exit, Exit::Waiting);
assert_eq!(bench.core.state.sleeping, 1);
assert_eq!(bench.core.state.r[15], length);
}
}
#[test]
fn wfe_consumes_an_event_or_waits() {
for code in [&[0xBF20, 0xBF20][..], &[0xF3AF, 0x8002, 0xF3AF, 0x8002][..]] {
let (bench, exit) = after(code, 2, |core| core.state.event = 1);
assert_eq!(exit, Exit::Waiting);
assert_eq!(bench.core.state.event, 0);
}
}
#[test]
fn sev_registers_an_event_the_next_wfe_consumes() {
assert_eq!(state(&[0xBF40], 1, |_| {}).event, 1);
assert_eq!(state(&[0xF3AF, 0x8004], 1, |_| {}).event, 1);
let (bench, exit) = after(&[0xBF40, 0xBF20, 0xBF00], 3, |_| {});
assert_eq!(exit, Exit::Budget);
assert_eq!((bench.core.state.event, bench.core.state.sleeping), (0, 0));
}
#[test]
fn a_32_bit_hint_with_op1_set_is_undefined() {
for op1 in 1..8 {
assert!(undefined(&[0xF3AF, 0x8000 | (op1 << 8)]), "op1 {op1}");
}
}
#[test]
fn clrex_clears_the_local_monitor() {
let cleared = state(&[0xF3BF, 0x8F2F], 1, |core| {
core.state.exclusive_valid = 1;
});
assert_eq!(cleared.exclusive_valid, 0);
}
#[test]
fn the_barriers_change_nothing_and_isb_ends_the_block() {
let code = [
0xF3BF, 0x8F4F, 0xF3BF, 0x8F5F, 0xF3BF, 0x8F6F, 0xF3BF, 0x8F43,
];
let mut expected = Bench::new(&[]).core.state.clone();
expected.r[15] = 16;
assert_eq!(state(&code, 4, |_| {}), expected);
assert_eq!(decoded(&[0xF3BF, 0x8F4F], 0), (Ok(()), false));
assert_eq!(decoded(&[0xF3BF, 0x8F5F], 0), (Ok(()), false));
assert_eq!(decoded(&[0xF3BF, 0x8F6F], 0), (Ok(()), true));
assert_eq!(decoded(&[0xF3BF, 0x8F60], 0), (Ok(()), true));
}
#[test]
fn bits_marked_0_or_1_are_not_checked() {
const CPSID_I: u16 = 0xB67E;
const MRS_R0_IPSR: [u16; 2] = [0xF3F0, 0xA005];
const MSR_PRIMASK_R1: [u16; 2] = [0xF391, 0xAB10];
const CLREX: [u16; 2] = [0xF3B0, 0xA020];
const CPSIE_I: u16 = 0xB662;
let code = [
CPSID_I,
MRS_R0_IPSR[0],
MRS_R0_IPSR[1],
CLREX[0],
CLREX[1],
CPSIE_I,
MSR_PRIMASK_R1[0],
MSR_PRIMASK_R1[1],
];
let ran = state(&code, 3, |core| {
core.state.ipsr = 11;
core.state.exclusive_valid = 1;
});
assert_eq!((ran.primask, ran.r[0], ran.exclusive_valid), (1, 11, 0));
let ran = state(&code, 5, |core| core.state.r[1] = 1);
assert_eq!(ran.primask, 1, "cleared by CPSIE and written by MSR");
}
#[test]
fn the_unallocated_miscellaneous_control_ops_are_undefined() {
for op in [0, 1, 3, 7, 8, 9, 10, 11, 12, 13, 14, 15] {
assert!(undefined(&[0xF3BF, 0x8F0F | (op << 4)]), "op {op:#06b}");
}
}
#[test]
fn b_t3_builds_its_offset_from_s_j2_j1_imm6_imm11() {
let equal = |core: &mut Family| core.state.z = 1;
assert_eq!(next(&[0xF000, 0x8080], equal), 0x104);
assert_eq!(next(&[0xF000, 0x8080], |_| {}), 4);
assert_eq!(next(&[0xF000, 0xA000], equal), 0x4_0004, "J1 is bit 18");
assert_eq!(next(&[0xF000, 0x8800], equal), 0x8_0004, "J2 is bit 19");
assert_eq!(
next(&[0xF03F, 0x8000], equal),
0x3_F004,
"imm6 is bits 17 to 12"
);
let mut bench = Bench::new(&[]);
bench.place(0x10, &[0xF47F, 0xAFFF]);
bench.core.state.r[15] = 0x10;
bench.run(1);
assert_eq!(bench.core.state.r[15], 0x12, "S sign-extends");
}
#[test]
fn b_t4_builds_its_offset_with_i1_and_i2() {
assert_eq!(next(&[0xF001, 0xB800], |_| {}), 0x1004);
assert_eq!(next(&[0xF400, 0x9000], |_| {}), 0xFF00_0004);
assert_eq!(next(&[0xF000, 0x9800], |_| {}), 0x0080_0004);
assert_eq!(next(&[0xF000, 0xB000], |_| {}), 0x0040_0004);
let mut bench = Bench::new(&[0xF7FF, 0xBFFE]);
assert_eq!(bench.run(5), (Exit::Waiting, 1), "a branch to itself waits");
assert_eq!(bench.core.state.r[15], CODE);
}
#[test]
fn bl_links_the_next_instruction_and_branches() {
let called = state(&[0xF001, 0xF800], 1, |_| {});
assert_eq!((called.r[15], called.r[14]), (0x1004, 5));
let called = state(&[0xF400, 0xD000], 1, |_| {});
assert_eq!((called.r[15], called.r[14]), (0xFF00_0004, 5));
}
#[test]
fn bl_last_in_an_it_block_runs_under_its_condition() {
let code = [0xBF18, 0xF001, 0xF800];
let skipped = state(&code, 2, |core| {
core.state.z = 1;
core.state.r[14] = 0xEEEE;
});
assert_eq!((skipped.r[15], skipped.r[14]), (6, 0xEEEE));
let called = state(&code, 2, |_| {});
assert_eq!((called.r[15], called.r[14]), (0x1006, 7));
}
#[test]
fn the_unallocated_branch_and_control_encodings_are_undefined() {
for code in [
[0xF7F0, 0xA000],
[0xF7FF, 0xAFFF],
[0xF000, 0xC000],
[0xF000, 0xE000],
[0xF3C0, 0x8000],
[0xF3D0, 0x8000],
[0xF780, 0x8000],
[0xF7F0, 0x8000],
] {
assert!(undefined(&code), "{code:04x?}");
}
}
#[test]
fn mrs_reads_each_view_of_the_program_status_registers() {
let mut code = Vec::new();
for (d, sysm) in [(0, 0), (1, 1), (2, 2), (3, 3), (4, IPSR), (5, 6), (6, 7)] {
code.extend(mrs(d, sysm));
}
let read = state(&code, 7, |core| {
core.state.set_xpsr(0xA80A_0023 | mask(xpsr::T));
core.state.r[5] = 0x5555;
});
assert_eq!(
read.r[..7],
[
0xA80A_0000,
0xA80A_0023,
0xA80A_0000,
0xA80A_0023,
0x23,
0,
0x23
]
);
}
#[test]
fn mrs_reads_the_stack_pointers_and_masks_privileged() {
let mut code = Vec::new();
for (d, sysm) in [
(0, MSP),
(1, PSP),
(2, PRIMASK),
(3, BASEPRI),
(4, BASEPRI_MAX),
(5, FAULTMASK),
(6, CONTROL),
] {
code.extend(mrs(d, sysm));
}
let (bench, _) = after(&code, 7, |core| {
core.state.r[13] = 0x2000_1000;
core.state.other_sp = 0x2000_2000;
core.state.primask = 1;
core.state.faultmask = 1;
core.state.control = mask(control::FPCA);
core.scs.write_basepri(0x40);
});
assert_eq!(
bench.core.state.r[..7],
[0x2000_1000, 0x2000_2000, 1, 0x40, 0x40, 1, 4]
);
}
#[test]
fn mrs_reads_zero_for_the_stack_pointers_and_masks_unprivileged() {
let mut code = Vec::new();
for (d, sysm) in [
(0, MSP),
(1, PSP),
(2, PRIMASK),
(3, BASEPRI),
(4, BASEPRI_MAX),
(5, FAULTMASK),
(6, CONTROL),
] {
code.extend(mrs(d, sysm));
}
let (bench, _) = after(&code, 7, |core| {
unprivileged(core);
core.state.other_sp = 0x2000_2000;
core.state.primask = 1;
core.state.faultmask = 1;
core.scs.write_basepri(0x40);
core.state.r[..7].fill(0xFFFF);
});
assert_eq!(bench.core.state.r[..7], [0, 0, 0, 0, 0, 0, 1]);
}
#[test]
fn mrs_of_a_register_v7m_lacks_reads_zero() {
for sysm in [4, 10, 15, 21, 24, 0xFF] {
let read = state(&mrs(0, sysm), 1, |core| {
core.state.r[0] = 0xFFFF;
core.state.set_xpsr(APSR | mask(xpsr::T));
});
assert_eq!(read.r[0], 0, "SYSm {sysm}");
}
}
#[test]
fn mrs_to_the_sp_clears_bits_1_and_0_and_to_the_pc_is_undefined() {
let read = state(&mrs(13, IPSR), 1, |core| core.state.ipsr = 0x23);
assert_eq!(read.r[13], 0x20);
assert!(undefined(&mrs(15, 0)));
}
#[test]
fn mrs_to_the_sp_of_a_stack_pointer_copies_it_privileged() {
let read = state(&mrs(13, PSP), 1, |core| core.state.other_sp = 0x2000_2000);
assert_eq!((read.r[13], read.other_sp), (0x2000_2000, 0x2000_2000));
let read = state(&mrs(13, MSP), 1, |core| core.state.r[13] = 0x2000_1000);
assert_eq!(read.r[13], 0x2000_1000);
}
#[test]
fn msr_writes_the_apsr_fields_its_mask_names() {
let written = |mask: u16, sysm: u16| {
let [a, b] = msr(0, mask, sysm);
let [c, d] = mrs(1, 3);
state(&[a, b, c, d], 2, |core| {
unprivileged(core);
core.state.r[0] = u32::MAX;
})
};
assert_eq!(written(0b10, 0).r[1], APSR_NZCVQ);
assert_eq!(written(0b01, 0).r[1], mask(xpsr::GE));
assert_eq!(written(0b11, 0).r[1], APSR);
assert_eq!(written(0b11, 1).r[1], APSR, "IAPSR");
assert_eq!(written(0b11, 3).r[1], APSR, "XPSR keeps IPSR and EPSR");
let xpsr = written(0b11, 3);
assert_eq!((xpsr.ipsr, xpsr.it, xpsr.t), (0, 0, 1));
assert_eq!(
written(0b00, 0).r[1],
0,
"mask 00 is UNPREDICTABLE and writes nothing"
);
}
#[test]
fn msr_past_the_apsr_writes_whatever_the_mask() {
for mask in [0b00, 0b01, 0b11] {
let written = state(&msr(0, mask, PRIMASK), 1, |core| core.state.r[0] = 1);
assert_eq!(written.primask, 1, "mask {mask:02b}");
let written = state(&msr(0, mask, PSP), 1, |core| core.state.r[0] = 0x2000_0100);
assert_eq!(written.other_sp, 0x2000_0100, "mask {mask:02b}");
}
}
#[test]
fn msr_to_ipsr_epsr_and_unlisted_registers_changes_nothing() {
for (mask, sysm) in [
(0b10, 5),
(0b11, 6),
(0b11, 7),
(0b10, 4),
(0b10, 10),
(0b10, 21),
(0b10, 0xFF),
] {
let prepare = |core: &mut Family| {
core.state.ipsr = 11;
core.state.r[0] = u32::MAX;
};
let mut expected = Bench::new(&[]);
prepare(&mut expected.core);
expected.core.state.r[15] = 4;
assert_eq!(
state(&msr(0, mask, sysm), 1, prepare),
expected.core.state,
"SYSm {sysm}"
);
}
}
#[test]
fn msr_writes_the_stack_pointers_privileged() {
let [a, b] = msr(0, 0b10, MSP);
let [c, d] = msr(1, 0b10, PSP);
let code = [a, b, c, d];
let values = |core: &mut Family| {
core.state.r[0] = 0x2000_0103;
core.state.r[1] = 0x2000_0207;
core.state.other_sp = 0x2000_3000;
};
let written = state(&code, 2, values);
assert_eq!(
(written.r[13], written.other_sp),
(0x2000_0100, 0x2000_0204)
);
let ignored = state(&code, 2, |core| {
values(core);
unprivileged(core);
});
assert_eq!(
(ignored.r[13], ignored.other_sp),
(Bench::new(&[]).core.state.r[13], 0x2000_3000)
);
}
#[test]
fn msr_writes_the_masks_privileged() {
let mut code = Vec::new();
for (n, sysm) in [(0, PRIMASK), (1, BASEPRI), (2, FAULTMASK)] {
code.extend(msr(n, 0b10, sysm));
}
let values = |core: &mut Family| {
core.state.r[0] = 0xFFFF_FFFF;
core.state.r[1] = 0x45;
core.state.r[2] = 0xFFFF_FFFF;
};
let (bench, _) = after(&code, 3, values);
let state = &bench.core.state;
assert_eq!((state.primask, state.faultmask), (1, 1));
assert_eq!(bench.core.scs.basepri(), 0x40);
let (bench, _) = after(&code, 3, |core| {
values(core);
unprivileged(core);
});
let state = &bench.core.state;
assert_eq!((state.primask, state.faultmask), (0, 0));
assert_eq!(bench.core.scs.basepri(), 0);
}
#[test]
fn basepri_max_only_raises_the_masked_priority() {
let mut code = Vec::new();
for n in 0..4 {
code.extend(msr(n, 0b10, BASEPRI_MAX));
}
let mut bench = Bench::new(&code);
bench.core.state.r[..4].copy_from_slice(&[0x80, 0xA0, 0x40, 0]);
for held in [0x80, 0x80, 0x40, 0x40] {
bench.run(1);
assert_eq!(bench.core.scs.basepri(), held);
}
}
#[test]
fn faultmask_takes_no_write_at_priority_minus_one() {
let code = msr(0, 0b10, FAULTMASK);
for number in [NMI, HARD_FAULT] {
let written = state(&code, 1, |core| {
handling(core, number);
core.state.r[0] = 1;
});
assert_eq!(written.faultmask, 0);
}
let held = state(&code, 1, |core| core.state.faultmask = 1);
assert_eq!(held.faultmask, 1);
let svcall = state(&code, 1, |core| {
core.state.ipsr = 11;
core.state.r[0] = 1;
});
assert_eq!(svcall.faultmask, 1);
}
#[test]
fn faultmask_takes_no_write_with_hard_fault_or_nmi_active_in_thread_mode() {
for number in [NMI, HARD_FAULT] {
let active = |core: &mut Family| {
handling(core, number);
core.state.ipsr = 0;
core.state.r[0] = 1;
};
assert_eq!(state(&msr(0, 0b10, FAULTMASK), 1, active).faultmask, 0);
assert_eq!(state(&[0xB671], 1, active).faultmask, 0);
}
}
#[test]
fn msr_from_the_sp_or_the_pc_writes_what_r_n_reads() {
let from_sp = state(&msr(13, 0b10, PSP), 1, |_| {});
assert_eq!(from_sp.other_sp, from_sp.r[13]);
let mut bench = Bench::new(&[]);
bench.place(0x100, &msr(15, 0b10, PSP));
bench.core.state.r[15] = 0x100;
bench.run(1);
assert_eq!(bench.core.state.other_sp, 0x104);
let flags = state(&msr(15, 0b10, 0), 1, |core| core.state.n = 1);
assert_eq!(
(flags.n, flags.z, flags.c, flags.v, flags.q),
(0, 0, 0, 0, 0)
);
}
#[test]
fn msr_control_switches_the_stack_and_drops_privilege_in_thread_mode() {
let mut code = Vec::new();
for n in 0..3 {
code.extend(msr(n, 0b10, CONTROL));
}
let mut bench = Bench::new(&code);
bench.core.state.r[13] = 0x2000_1000;
bench.core.state.other_sp = 0x2000_2000;
bench.core.state.r[..3].copy_from_slice(&[
mask(control::SPSEL) | mask(control::FPCA),
mask(control::NPRIV),
mask(control::SPSEL),
]);
bench.run(1);
let state = &bench.core.state;
assert_eq!(state.control, mask(control::SPSEL) | mask(control::FPCA));
assert_eq!((state.r[13], state.other_sp), (0x2000_2000, 0x2000_1000));
bench.run(2);
let state = &bench.core.state;
assert_eq!(state.control, mask(control::NPRIV));
assert_eq!((state.r[13], state.other_sp), (0x2000_1000, 0x2000_2000));
}
#[test]
fn a_core_without_the_extensions_has_no_ge_and_no_fpca() {
let mut code = Vec::new();
code.extend(msr(0, 0b11, 0));
code.extend(mrs(1, 0));
code.extend(msr(0, 0b10, CONTROL));
code.extend(mrs(2, CONTROL));
let mut bench = Bench::of(&spec_of(Variant::M3, Fpu::None, MpuRegions::Eight), &code);
bench.core.state.ge = 0b0101;
bench.core.state.r[0] = u32::MAX;
assert_eq!(bench.run(4).1, 4);
let state = &bench.core.state;
assert_eq!((state.r[1], state.ge), (APSR_NZCVQ, 0b0101));
assert_eq!(state.r[2], mask(control::NPRIV) | mask(control::SPSEL));
}
#[test]
fn msr_control_leaves_spsel_in_handler_mode() {
let written = state(&msr(0, 0b10, CONTROL), 1, |core| {
core.state.ipsr = 11;
core.state.r[13] = 0x2000_1000;
core.state.r[0] = mask(control::NPRIV) | mask(control::SPSEL) | mask(control::FPCA);
});
assert_eq!(written.control, mask(control::NPRIV) | mask(control::FPCA));
assert_eq!(written.r[13], 0x2000_1000);
}
#[test]
fn msr_ends_the_block_after_what_interrupts_and_translation_depend_on() {
assert_eq!(decoded(&msr(0, 0b10, 0), 0), (Ok(()), false));
for sysm in [MSP, PSP, PRIMASK, BASEPRI, BASEPRI_MAX, FAULTMASK, CONTROL] {
assert_eq!(decoded(&msr(0, 0b10, sysm), 0), (Ok(()), true), "{sysm}");
}
}
}