use std::mem::offset_of;
use cranelift_codegen::ir::{InstBuilder, Value, types};
use super::super::state::State;
use super::alu::{
ShiftType, add_with_carry, decode_imm_shift, shift_c, shift_c_register, signed_sat_q,
thumb_expand_imm_c, unsigned_sat_q,
};
use super::{Emitter, Flag, Translated, Undefined, bits, multiply};
pub(super) fn modified_immediate(e: &mut Emitter<'_, '_>, hw1: u16, hw2: u16) -> Translated {
let (hw1, hw2) = (u32::from(hw1), u32::from(hw2));
let operation = Operation::from_op(bits(hw1, 8, 5))?;
let rn = bits(hw1, 3, 0);
let destination = Destination::of(operation, bits(hw2, 11, 8), bits(hw1, 4, 4), false)?;
let (imm32, carry) = thumb_expand_imm_c(imm12(hw1, hw2));
let operand = e.constant(imm32);
let carry = carry.map(|carry| e.constant(carry));
operate(e, operation, rn, destination, (operand, carry));
Ok(())
}
pub(super) fn plain_binary_immediate(e: &mut Emitter<'_, '_>, hw1: u16, hw2: u16) -> Translated {
let (hw1, hw2) = (u32::from(hw1), u32::from(hw2));
writable(bits(hw2, 11, 8))?;
let shifted = bits(hw2, 14, 12) != 0 || bits(hw2, 7, 6) != 0;
match bits(hw1, 8, 4) {
0b0_0000 | 0b0_1010 => add_wide(e, hw1, hw2),
0b0_0100 | 0b0_1100 => move_wide(e, hw1, hw2),
0b1_0010 | 0b1_1010 if !shifted => saturate16(e, hw1, hw2),
0b1_0000 | 0b1_0010 | 0b1_1000 | 0b1_1010 => saturate(e, hw1, hw2),
0b1_0100 | 0b1_1100 => return extract(e, hw1, hw2),
0b1_0110 => return insert(e, hw1, hw2),
_ => return Err(Undefined),
}
Ok(())
}
pub(super) fn shifted_register(e: &mut Emitter<'_, '_>, hw1: u16, hw2: u16) -> Translated {
let (hw1, hw2) = (u32::from(hw1), u32::from(hw2));
if bits(hw1, 8, 5) == 0b0110 {
return pack_halfword(e, hw1, hw2);
}
let operation = Operation::from_op(bits(hw1, 8, 5))?;
let (rn, rm) = (bits(hw1, 3, 0), bits(hw2, 3, 0));
let (shift_type, amount) = decode_imm_shift(bits(hw2, 5, 4), imm5(hw2));
let moves =
operation == Operation::Orr && rn == 15 && shift_type == ShiftType::Lsl && amount == 0;
let writes_pc = operation == Operation::Add || moves;
let destination = Destination::of(operation, bits(hw2, 11, 8), bits(hw1, 4, 4), writes_pc)?;
let value = read(e, rm);
let carry_in = e.flag(Flag::C);
let (operand, carry) = shift_c(e, value, shift_type, amount, carry_in);
operate(e, operation, rn, destination, (operand, Some(carry)));
Ok(())
}
pub(super) fn register(e: &mut Emitter<'_, '_>, hw1: u16, hw2: u16) -> Translated {
let (wide1, wide2) = (u32::from(hw1), u32::from(hw2));
if bits(wide2, 15, 12) != 0b1111 {
return Err(Undefined);
}
match (bits(wide1, 7, 4), bits(wide2, 7, 4)) {
(0b0000..=0b0111, 0b0000) => shift_by_register(e, wide1, wide2),
(0b0000..=0b0101, 0b1000..=0b1111) => extend(e, wide1, wide2),
(0b1000..=0b1111, 0b0000..=0b0011) => multiply::parallel_signed(e, hw1, hw2),
(0b1000..=0b1111, 0b0100..=0b0111) => multiply::parallel_unsigned(e, hw1, hw2),
(0b1000..=0b1011, 0b1000..=0b1011) => multiply::miscellaneous(e, hw1, hw2),
_ => Err(Undefined),
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum Operation {
And,
Bic,
Orr,
Orn,
Eor,
Add,
Adc,
Sbc,
Sub,
Rsb,
}
impl Operation {
fn from_op(op: u32) -> Result<Self, Undefined> {
Ok(match op {
0b0000 => Self::And,
0b0001 => Self::Bic,
0b0010 => Self::Orr,
0b0011 => Self::Orn,
0b0100 => Self::Eor,
0b1000 => Self::Add,
0b1010 => Self::Adc,
0b1011 => Self::Sbc,
0b1101 => Self::Sub,
0b1110 => Self::Rsb,
_ => return Err(Undefined),
})
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum Destination {
Register {
d: u32,
setflags: bool,
},
Flags,
Pc,
}
impl Destination {
fn of(operation: Operation, rd: u32, s: u32, writes_pc: bool) -> Result<Self, Undefined> {
let setflags = s == 1;
let tests = matches!(
operation,
Operation::And | Operation::Eor | Operation::Add | Operation::Sub
);
match rd {
15 if setflags && tests => Ok(Self::Flags),
15 if writes_pc => Ok(Self::Pc),
15 => Err(Undefined),
d => Ok(Self::Register { d, setflags }),
}
}
}
fn operate(
e: &mut Emitter<'_, '_>,
operation: Operation,
rn: u32,
destination: Destination,
(operand, shifter_carry): (Value, Option<Value>),
) {
let x = if rn == 15 && matches!(operation, Operation::Orr | Operation::Orn) {
e.constant(0)
} else {
read(e, rn)
};
let (result, carry, overflow) = match operation {
Operation::And => (e.b.ins().band(x, operand), shifter_carry, None),
Operation::Bic => (e.b.ins().band_not(x, operand), shifter_carry, None),
Operation::Orr => (e.b.ins().bor(x, operand), shifter_carry, None),
Operation::Orn => (e.b.ins().bor_not(x, operand), shifter_carry, None),
Operation::Eor => (e.b.ins().bxor(x, operand), shifter_carry, None),
_ => {
let (x, y, carry_in) = match operation {
Operation::Add => (x, operand, e.constant(0)),
Operation::Adc => (x, operand, e.flag(Flag::C)),
Operation::Sbc => (x, e.b.ins().bnot(operand), e.flag(Flag::C)),
Operation::Sub => (x, e.b.ins().bnot(operand), e.constant(1)),
_ => (e.b.ins().bnot(x), operand, e.constant(1)),
};
let (result, carry, overflow) = add_with_carry(e, x, y, carry_in);
(result, Some(carry), Some(overflow))
}
};
match destination {
Destination::Pc => e.branch(result),
Destination::Register { d, setflags } => {
write(e, d, result);
if setflags {
set_flags(e, result, carry, overflow);
}
}
Destination::Flags => set_flags(e, result, carry, overflow),
}
}
fn set_flags(
e: &mut Emitter<'_, '_>,
result: Value,
carry: Option<Value>,
overflow: Option<Value>,
) {
e.set_nz(result);
if let Some(carry) = carry {
e.set_flag(Flag::C, carry);
}
if let Some(overflow) = overflow {
e.set_flag(Flag::V, overflow);
}
}
fn add_wide(e: &mut Emitter<'_, '_>, hw1: u32, hw2: u32) {
let (rn, d) = (bits(hw1, 3, 0), bits(hw2, 11, 8));
let x = if rn == 15 {
e.constant(e.pc().wrapping_add(4) & !3)
} else {
read(e, rn)
};
let imm32 = e.constant(imm12(hw1, hw2));
let result = if bits(hw1, 7, 7) == 1 {
e.b.ins().isub(x, imm32)
} else {
e.b.ins().iadd(x, imm32)
};
write(e, d, result);
}
fn move_wide(e: &mut Emitter<'_, '_>, hw1: u32, hw2: u32) {
let d = bits(hw2, 11, 8);
let imm16 = bits(hw1, 3, 0) << 12 | imm12(hw1, hw2);
let result = if bits(hw1, 7, 7) == 1 {
let old = read(e, d);
let bottom = e.b.ins().band_imm_u(old, 0xFFFF);
e.b.ins().bor_imm_u(bottom, i64::from(imm16 << 16))
} else {
e.constant(imm16)
};
write(e, d, result);
}
fn saturate(e: &mut Emitter<'_, '_>, hw1: u32, hw2: u32) {
let (rn, d) = (bits(hw1, 3, 0), bits(hw2, 11, 8));
let unsigned = bits(hw1, 7, 7) == 1;
let (shift_type, amount) = decode_imm_shift(bits(hw1, 5, 5) << 1, imm5(hw2));
let value = read(e, rn);
let carry_in = e.flag(Flag::C);
let (operand, _) = shift_c(e, value, shift_type, amount, carry_in);
let operand = e.b.ins().sextend(types::I64, operand);
let sat_imm = bits(hw2, 4, 0);
let (result, saturated) = if unsigned {
unsigned_sat_q(e, operand, sat_imm)
} else {
let (result, saturated) = signed_sat_q(e, operand, sat_imm + 1);
(sign_extend(e, result, sat_imm + 1), saturated)
};
write(e, d, result);
set_q(e, saturated);
}
fn saturate16(e: &mut Emitter<'_, '_>, hw1: u32, hw2: u32) {
let (rn, d) = (bits(hw1, 3, 0), bits(hw2, 11, 8));
let unsigned = bits(hw1, 7, 7) == 1;
let sat_imm = bits(hw2, 3, 0);
let value = read(e, rn);
let mut result = e.constant(0);
let mut saturated = e.constant(0);
for at in [0, 16] {
let half = e.b.ins().ushr_imm_u(value, at);
let half = e.b.ins().ireduce(types::I16, half);
let half = e.b.ins().sextend(types::I64, half);
let (half, half_saturated) = if unsigned {
unsigned_sat_q(e, half, sat_imm)
} else {
let (half, half_saturated) = signed_sat_q(e, half, sat_imm + 1);
(sign_extend(e, half, sat_imm + 1), half_saturated)
};
let half = e.b.ins().band_imm_u(half, 0xFFFF);
let half = e.b.ins().ishl_imm_s(half, at);
result = e.b.ins().bor(result, half);
saturated = e.b.ins().bor(saturated, half_saturated);
}
write(e, d, result);
set_q(e, saturated);
}
fn extract(e: &mut Emitter<'_, '_>, hw1: u32, hw2: u32) -> Translated {
let (rn, d) = (bits(hw1, 3, 0), bits(hw2, 11, 8));
let (lsbit, widthminus1) = (imm5(hw2), bits(hw2, 4, 0));
let msbit = lsbit + widthminus1;
if msbit > 31 {
return Err(Undefined);
}
let value = read(e, rn);
let top = e.b.ins().ishl_imm_s(value, i64::from(31 - msbit));
let result = if bits(hw1, 7, 7) == 1 {
e.b.ins().ushr_imm_u(top, i64::from(31 - widthminus1))
} else {
e.b.ins().sshr_imm_u(top, i64::from(31 - widthminus1))
};
write(e, d, result);
Ok(())
}
fn insert(e: &mut Emitter<'_, '_>, hw1: u32, hw2: u32) -> Translated {
let (rn, d) = (bits(hw1, 3, 0), bits(hw2, 11, 8));
let (lsbit, msbit) = (imm5(hw2), bits(hw2, 4, 0));
if msbit < lsbit {
return Err(Undefined);
}
let field = (u32::MAX >> (31 - (msbit - lsbit))) << lsbit;
let old = read(e, d);
let mut result = e.b.ins().band_imm_u(old, i64::from(!field));
if rn != 15 {
let value = read(e, rn);
let moved = e.b.ins().ishl_imm_s(value, i64::from(lsbit));
let moved = e.b.ins().band_imm_u(moved, i64::from(field));
result = e.b.ins().bor(result, moved);
}
write(e, d, result);
Ok(())
}
fn pack_halfword(e: &mut Emitter<'_, '_>, hw1: u32, hw2: u32) -> Translated {
if bits(hw1, 4, 4) == 1 || bits(hw2, 4, 4) == 1 {
return Err(Undefined);
}
let d = writable(bits(hw2, 11, 8))?;
let (rn, rm) = (bits(hw1, 3, 0), bits(hw2, 3, 0));
let top_bottom = bits(hw2, 5, 5) == 1;
let (shift_type, amount) = decode_imm_shift(bits(hw2, 5, 5) << 1, imm5(hw2));
let value = read(e, rm);
let carry_in = e.flag(Flag::C);
let (operand, _) = shift_c(e, value, shift_type, amount, carry_in);
let n = read(e, rn);
let (bottom, top) = if top_bottom {
(operand, n)
} else {
(n, operand)
};
let bottom = e.b.ins().band_imm_u(bottom, 0xFFFF);
let top = e.b.ins().band_imm_u(top, 0xFFFF_0000_i64);
let result = e.b.ins().bor(top, bottom);
write(e, d, result);
Ok(())
}
fn shift_by_register(e: &mut Emitter<'_, '_>, hw1: u32, hw2: u32) -> Translated {
let d = writable(bits(hw2, 11, 8))?;
let (rn, rm) = (bits(hw1, 3, 0), bits(hw2, 3, 0));
let shift_type = [
ShiftType::Lsl,
ShiftType::Lsr,
ShiftType::Asr,
ShiftType::Ror,
][bits(hw1, 6, 5) as usize];
let value = read(e, rn);
let amount = read(e, rm);
let amount = e.b.ins().band_imm_u(amount, 0xFF);
let carry_in = e.flag(Flag::C);
let (result, carry) = shift_c_register(e, value, shift_type, amount, carry_in);
write(e, d, result);
if bits(hw1, 4, 4) == 1 {
set_flags(e, result, Some(carry), None);
}
Ok(())
}
fn extend(e: &mut Emitter<'_, '_>, hw1: u32, hw2: u32) -> Translated {
let d = writable(bits(hw2, 11, 8))?;
let (rn, rm) = (bits(hw1, 3, 0), bits(hw2, 3, 0));
let op1 = bits(hw1, 6, 4);
let signed = op1 & 1 == 0;
let value = read(e, rm);
let rotated = e.b.ins().rotr_imm_u(value, i64::from(bits(hw2, 5, 4) * 8));
let addend = (rn != 15).then(|| read(e, rn));
let result = if op1 >> 1 == 0b01 {
let mut result = e.constant(0);
for at in [0, 16] {
let byte = e.b.ins().ushr_imm_u(rotated, at);
let byte = e.b.ins().ireduce(types::I8, byte);
let mut lane = widen(e, byte, signed, types::I16);
if let Some(addend) = addend {
let half = e.b.ins().ushr_imm_u(addend, at);
let half = e.b.ins().ireduce(types::I16, half);
lane = e.b.ins().iadd(half, lane);
}
let lane = e.b.ins().uextend(types::I32, lane);
let lane = e.b.ins().ishl_imm_s(lane, at);
result = e.b.ins().bor(result, lane);
}
result
} else {
let narrow = if op1 >> 1 == 0b00 {
types::I16
} else {
types::I8
};
let part = e.b.ins().ireduce(narrow, rotated);
let extended = widen(e, part, signed, types::I32);
match addend {
Some(addend) => e.b.ins().iadd(addend, extended),
None => extended,
}
};
write(e, d, result);
Ok(())
}
fn widen(e: &mut Emitter<'_, '_>, value: Value, signed: bool, to: types::Type) -> Value {
if signed {
e.b.ins().sextend(to, value)
} else {
e.b.ins().uextend(to, value)
}
}
fn sign_extend(e: &mut Emitter<'_, '_>, value: Value, width: u32) -> Value {
if width == 32 {
return value;
}
let top = e.b.ins().ishl_imm_s(value, i64::from(32 - width));
e.b.ins().sshr_imm_u(top, i64::from(32 - width))
}
fn set_q(e: &mut Emitter<'_, '_>, saturated: Value) {
let q = e.load_field(offset_of!(State, q));
let q = e.b.ins().bor(q, saturated);
e.store_field(offset_of!(State, q), q);
}
fn read(e: &mut Emitter<'_, '_>, n: u32) -> Value {
let value = e.reg(n);
if n == 13 {
e.b.ins().band_imm_s(value, !3)
} else {
value
}
}
fn write(e: &mut Emitter<'_, '_>, d: u32, value: Value) {
let value = if d == 13 {
e.b.ins().band_imm_s(value, !3)
} else {
value
};
e.set_reg(d, value);
}
fn writable(rd: u32) -> Result<u32, Undefined> {
if rd == 15 { Err(Undefined) } else { Ok(rd) }
}
fn imm12(hw1: u32, hw2: u32) -> u32 {
bits(hw1, 10, 10) << 11 | bits(hw2, 14, 12) << 8 | bits(hw2, 7, 0)
}
fn imm5(hw2: u32) -> u32 {
bits(hw2, 14, 12) << 2 | bits(hw2, 7, 6)
}
#[cfg(test)]
mod tests {
use cranelift_codegen::ir::{self, AbiParam};
use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext};
use super::super::testing::Bench;
use super::*;
fn modified(op: u32, s: u32, [rd, rn]: [u32; 2], imm12: u32) -> [u16; 2] {
let hw1 = 0xF000 | (imm12 >> 11) << 10 | op << 5 | s << 4 | rn;
let hw2 = (imm12 >> 8 & 7) << 12 | rd << 8 | (imm12 & 0xFF);
[halfword(hw1), halfword(hw2)]
}
fn plain(op: u32, [rd, rn]: [u32; 2], imm12: u32) -> [u16; 2] {
let hw1 = 0xF200 | (imm12 >> 11) << 10 | op << 4 | rn;
let hw2 = (imm12 >> 8 & 7) << 12 | rd << 8 | (imm12 & 0xFF);
[halfword(hw1), halfword(hw2)]
}
fn field(imm5: u32, low: u32) -> u32 {
(imm5 >> 2) << 8 | (imm5 & 3) << 6 | low
}
fn shifted(
op: u32,
s: u32,
[rd, rn, rm]: [u32; 3],
(shift_type, imm5): (u32, u32),
) -> [u16; 2] {
let hw1 = 0xEA00 | op << 5 | s << 4 | rn;
let hw2 = (imm5 >> 2) << 12 | rd << 8 | (imm5 & 3) << 6 | shift_type << 4 | rm;
[halfword(hw1), halfword(hw2)]
}
fn register(op1: u32, [rd, rn, rm]: [u32; 3], op2: u32) -> [u16; 2] {
[
halfword(0xFA00 | op1 << 4 | rn),
halfword(0xF000 | rd << 8 | op2 << 4 | rm),
]
}
fn halfword(value: u32) -> u16 {
u16::try_from(value).unwrap()
}
fn run(code: &[u16], set: impl FnOnce(&mut State)) -> State {
let mut bench = Bench::new(code);
set(&mut bench.core.state);
assert_eq!(bench.run(1).1, 1, "one instruction retires");
bench.core.state.clone()
}
fn nzcv(state: &State) -> [u32; 4] {
[state.n, state.z, state.c, state.v]
}
fn translates([hw1, hw2]: [u16; 2]) -> Translated {
let mut function = ir::Function::new();
function.signature.params.push(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);
super::super::decode32(&mut e, hw1, hw2)
}
fn undefined(code: [u16; 2]) {
assert_eq!(
translates(code),
Err(Undefined),
"{:04X} {:04X}",
code[0],
code[1]
);
}
const AND: u32 = 0b0000;
const BIC: u32 = 0b0001;
const ORR: u32 = 0b0010;
const ORN: u32 = 0b0011;
const EOR: u32 = 0b0100;
const ADD: u32 = 0b1000;
const ADC: u32 = 0b1010;
const SBC: u32 = 0b1011;
const SUB: u32 = 0b1101;
const RSB: u32 = 0b1110;
const LSL: u32 = 0b00;
const LSR: u32 = 0b01;
const ASR: u32 = 0b10;
const ROR: u32 = 0b11;
#[test]
fn and_immediate_sets_carry_only_from_a_rotated_constant() {
let state = run(&modified(AND, 0, [0, 1], 0x2FF), |s| s.r[1] = 0x1234_5678);
assert_eq!((state.r[0], state.r[15]), (0x1200_5600, 4));
let state = run(&modified(AND, 1, [0, 1], 0x2FF), |s| {
s.r[1] = 0xF000_0000;
(s.c, s.v) = (0, 1);
});
assert_eq!((state.r[0], nzcv(&state)), (0xF000_0000, [1, 0, 0, 1]));
let state = run(&modified(AND, 1, [0, 1], 0x055), |s| {
s.r[1] = 0xFF;
s.c = 1;
});
assert_eq!((state.r[0], nzcv(&state)), (0x55, [0, 0, 1, 0]));
let state = run(&modified(AND, 1, [0, 1], 0x47F), |s| s.r[1] = 0x0012_3456);
assert_eq!((state.r[0], nzcv(&state)), (0, [0, 1, 1, 0]));
let state = run(&modified(AND, 1, [0, 1], 0x500), |s| {
s.r[1] = 0x3000_0000;
s.c = 1;
});
assert_eq!(
(state.r[0], nzcv(&state)),
(0x2000_0000, [0, 0, 0, 0]),
"ROR #10 clears C"
);
}
#[test]
fn tst_immediate_sets_the_flags_and_keeps_the_registers() {
let state = run(&modified(AND, 1, [15, 1], 0x47F), |s| {
(s.r[0], s.r[1]) = (7, 0x8000_0000);
});
assert_eq!((state.r[0], state.r[15]), (7, 4));
assert_eq!(nzcv(&state), [1, 0, 1, 0]);
}
#[test]
fn bic_immediate_clears_the_constant_s_bits() {
let state = run(&modified(BIC, 0, [2, 3], 0x0FF), |s| s.r[3] = 0x1234_56FF);
assert_eq!(state.r[2], 0x1234_5600);
let state = run(&modified(BIC, 1, [2, 3], 0x47F), |s| s.r[3] = 0xFFFF_FFFF);
assert_eq!((state.r[2], nzcv(&state)), (0x00FF_FFFF, [0, 0, 1, 0]));
}
#[test]
fn orr_and_mov_immediate_write_the_constant() {
let state = run(&modified(ORR, 0, [4, 5], 0x1AB), |s| s.r[5] = 0x1000_0001);
assert_eq!(state.r[4], 0x10AB_00AB);
let state = run(&modified(ORR, 1, [6, 15], 0x47F), |_| {});
assert_eq!((state.r[6], nzcv(&state)), (0xFF00_0000, [1, 0, 1, 0]));
let state = run(&modified(ORR, 1, [6, 15], 0), |s| {
s.r[6] = 5;
s.c = 1;
});
assert_eq!((state.r[6], nzcv(&state)), (0, [0, 1, 1, 0]));
assert_eq!(
modified(ORR, 0, [0, 15], 0x55),
[0xF04F, 0x0055],
"MOV.W R0, #0x55"
);
}
#[test]
fn orn_and_mvn_immediate_write_the_complement() {
let state = run(&modified(ORN, 0, [0, 1], 0x0FF), |s| s.r[1] = 0x100);
assert_eq!(state.r[0], 0xFFFF_FF00);
let state = run(&modified(ORN, 1, [0, 15], 0x0FF), |s| s.c = 1);
assert_eq!((state.r[0], nzcv(&state)), (0xFFFF_FF00, [1, 0, 1, 0]));
}
#[test]
fn eor_and_teq_immediate() {
let state = run(&modified(EOR, 0, [0, 1], 0x3AB), |s| s.r[1] = 0xFFFF_0000);
assert_eq!(state.r[0], 0x5454_ABAB);
let state = run(&modified(EOR, 1, [15, 1], 0x47F), |s| s.r[1] = 0xFF00_0000);
assert_eq!((state.r[1], nzcv(&state)), (0xFF00_0000, [0, 1, 1, 0]));
}
#[test]
fn add_and_cmn_immediate_set_carry_and_overflow() {
let state = run(&modified(ADD, 1, [0, 1], 1), |s| s.r[1] = 0x7FFF_FFFF);
assert_eq!((state.r[0], nzcv(&state)), (0x8000_0000, [1, 0, 0, 1]));
let state = run(&modified(ADD, 1, [0, 1], 1), |s| s.r[1] = 0xFFFF_FFFF);
assert_eq!((state.r[0], nzcv(&state)), (0, [0, 1, 1, 0]));
let state = run(&modified(ADD, 0, [0, 1], 0x47F), |s| s.r[1] = 1);
assert_eq!((state.r[0], nzcv(&state)), (0xFF00_0001, [0, 0, 0, 0]));
let state = run(&modified(ADD, 1, [15, 1], 1), |s| s.r[1] = 0xFFFF_FFFF);
assert_eq!((state.r[1], nzcv(&state)), (0xFFFF_FFFF, [0, 1, 1, 0]));
}
#[test]
fn add_immediate_from_and_to_the_sp() {
let state = run(&modified(ADD, 0, [0, 13], 3), |s| s.r[13] = 0x2000_0100);
assert_eq!(state.r[0], 0x2000_0103);
let state = run(&modified(ADD, 0, [13, 13], 0x13), |s| s.r[13] = 0x2000_0100);
assert_eq!(state.r[13], 0x2000_0110);
}
#[test]
fn adc_immediate_adds_the_carry() {
let state = run(&modified(ADC, 0, [0, 1], 0xFF), |s| {
s.r[1] = 1;
s.c = 1;
});
assert_eq!(state.r[0], 0x101);
let state = run(&modified(ADC, 1, [0, 1], 1), |s| {
s.r[1] = 0xFFFF_FFFE;
s.c = 1;
});
assert_eq!((state.r[0], nzcv(&state)), (0, [0, 1, 1, 0]));
}
#[test]
fn sbc_immediate_borrows_when_the_carry_is_clear() {
let state = run(&modified(SBC, 1, [0, 1], 1), |s| s.r[1] = 5);
assert_eq!((state.r[0], nzcv(&state)), (3, [0, 0, 1, 0]));
let state = run(&modified(SBC, 1, [0, 1], 1), |s| {
s.r[1] = 0;
s.c = 1;
});
assert_eq!((state.r[0], nzcv(&state)), (0xFFFF_FFFF, [1, 0, 0, 0]));
}
#[test]
fn sub_cmp_and_sub_from_the_sp_immediate() {
let state = run(&modified(SUB, 1, [0, 1], 1), |s| s.r[1] = 0x8000_0000);
assert_eq!((state.r[0], nzcv(&state)), (0x7FFF_FFFF, [0, 0, 1, 1]));
let state = run(&modified(SUB, 1, [15, 1], 5), |s| s.r[1] = 3);
assert_eq!((state.r[1], nzcv(&state)), (3, [1, 0, 0, 0]));
let state = run(&modified(SUB, 0, [13, 13], 8), |s| s.r[13] = 0x2000_0100);
assert_eq!(state.r[13], 0x2000_00F8);
}
#[test]
fn rsb_immediate_subtracts_from_the_constant() {
let state = run(&modified(RSB, 1, [0, 1], 0), |s| s.r[1] = 1);
assert_eq!((state.r[0], nzcv(&state)), (0xFFFF_FFFF, [1, 0, 0, 0]));
let state = run(&modified(RSB, 0, [0, 1], 0xFF), |s| s.r[1] = 0x10);
assert_eq!(state.r[0], 0xEF);
}
#[test]
fn modified_immediate_undefined_encodings() {
for op in [0b0101, 0b0110, 0b0111, 0b1001, 0b1100, 0b1111] {
undefined(modified(op, 0, [0, 1], 1));
}
for op in [AND, EOR, ADD, SUB] {
undefined(modified(op, 0, [15, 1], 1));
}
for op in [BIC, ORR, ORN, ADC, SBC, RSB] {
undefined(modified(op, 1, [15, 1], 1));
}
undefined(modified(ORR, 0, [15, 15], 1));
}
#[test]
fn modified_immediate_unpredictable_choices_run_the_pseudocode() {
let state = run(&modified(AND, 0, [0, 15], 0xFF), |_| {});
assert_eq!(state.r[0], 4);
let state = run(&modified(AND, 0, [13, 1], 0xFF), |s| s.r[1] = 0x2000_FFFF);
assert_eq!(state.r[13], 0xFC);
let state = run(&modified(ORR, 0, [0, 1], 0x100), |s| s.r[1] = 0x1234);
assert_eq!(state.r[0], 0x1234);
}
#[test]
fn addw_and_subw_add_twelve_bits_and_keep_the_flags() {
let all = |s: &mut State| {
(s.n, s.z, s.c, s.v) = (1, 1, 1, 1);
s.r[1] = 0x1000;
};
let state = run(&plain(0b0_0000, [0, 1], 0xFFF), all);
assert_eq!((state.r[0], nzcv(&state)), (0x1FFF, [1, 1, 1, 1]));
let state = run(&plain(0b0_1010, [0, 1], 0xFFF), all);
assert_eq!((state.r[0], nzcv(&state)), (1, [1, 1, 1, 1]));
let state = run(&plain(0b0_0000, [0, 13], 0x801), |s| s.r[13] = 0x2000_0000);
assert_eq!(state.r[0], 0x2000_0801);
let state = run(&plain(0b0_1010, [13, 13], 0x7), |s| s.r[13] = 0x2000_0100);
assert_eq!(state.r[13], 0x2000_00F8);
}
#[test]
fn adr_adds_to_the_aligned_pc() {
let at_two = |[hw1, hw2]: [u16; 2]| [0, hw1, hw2];
let state = run(&at_two(plain(0b0_0000, [0, 15], 0x10)), |s| s.r[15] = 2);
assert_eq!((state.r[0], state.r[15]), (0x14, 6));
let state = run(&at_two(plain(0b0_1010, [0, 15], 0x10)), |s| s.r[15] = 2);
assert_eq!(state.r[0], 0xFFFF_FFF4);
let state = run(&plain(0b0_0000, [0, 15], 0), |_| {});
assert_eq!(state.r[0], 4);
}
#[test]
fn movw_and_movt_write_each_half() {
let state = run(&[0xF64A, 0x30CD], |s| s.r[0] = 0xFFFF_FFFF);
assert_eq!(state.r[0], 0xABCD, "MOVW R0, #0xABCD");
let state = run(&plain(0b0_1100, [0, 1], 0x234), |s| s.r[0] = 0x5678_9ABC);
assert_eq!(state.r[0], 0x1234_9ABC);
let state = run(&plain(0b0_1100, [3, 0xF], 0xFFF), |s| s.r[3] = 0x1234_5678);
assert_eq!(state.r[3], 0xFFFF_5678);
}
#[test]
fn ssat_saturates_the_shifted_value_and_sets_q() {
let ssat = |sh: u32, imm5: u32| plain(0b1_0000 | sh << 1, [0, 1], field(imm5, 7));
let state = run(&ssat(0, 0), |s| s.r[1] = 200);
assert_eq!((state.r[0], state.q), (127, 1));
let state = run(&ssat(0, 0), |s| s.r[1] = 0xFFFF_FF38);
assert_eq!((state.r[0], state.q), (0xFFFF_FF80, 1));
let state = run(&ssat(0, 0), |s| s.r[1] = 100);
assert_eq!((state.r[0], state.q), (100, 0));
let state = run(&ssat(0, 0), |s| {
s.r[1] = 0xFFFF_FF9C;
s.q = 1;
});
assert_eq!((state.r[0], state.q), (0xFFFF_FF9C, 1), "Q stays set");
let state = run(&ssat(0, 4), |s| s.r[1] = 0x10);
assert_eq!((state.r[0], state.q), (127, 1), "LSL #4");
let state = run(&ssat(1, 4), |s| s.r[1] = 0xFFFF_F000);
assert_eq!((state.r[0], state.q), (0xFFFF_FF80, 1), "ASR #4");
let state = run(&ssat(1, 4), |s| s.r[1] = 0x0000_07F0);
assert_eq!((state.r[0], state.q), (0x7F, 0));
let state = run(&plain(0b1_0000, [0, 1], field(0, 31)), |s| {
s.r[1] = 0x8000_0000;
});
assert_eq!((state.r[0], state.q), (0x8000_0000, 0), "to 32 bits");
}
#[test]
fn ssat16_saturates_each_halfword() {
let state = run(&plain(0b1_0010, [0, 1], 7), |s| s.r[1] = 0x7FFF_FF00);
assert_eq!((state.r[0], state.q), (0x007F_FF80, 1));
let state = run(&plain(0b1_0010, [0, 1], 7), |s| s.r[1] = 0x0005_FFFB);
assert_eq!((state.r[0], state.q), (0x0005_FFFB, 0));
let state = run(&plain(0b1_0010, [0, 1], 15), |s| s.r[1] = 0x8000_7FFF);
assert_eq!((state.r[0], state.q), (0x8000_7FFF, 0), "to 16 bits");
}
#[test]
fn usat_saturates_to_an_unsigned_range() {
let usat = |sh: u32, imm5: u32, sat_imm: u32| {
plain(0b1_1000 | sh << 1, [0, 1], field(imm5, sat_imm))
};
let state = run(&usat(0, 0, 8), |s| s.r[1] = 300);
assert_eq!((state.r[0], state.q), (255, 1));
let state = run(&usat(0, 0, 8), |s| s.r[1] = 0xFFFF_FFFB);
assert_eq!((state.r[0], state.q), (0, 1));
let state = run(&usat(0, 0, 8), |s| s.r[1] = 77);
assert_eq!((state.r[0], state.q), (77, 0));
let state = run(&usat(0, 0, 0), |s| s.r[1] = 0);
assert_eq!((state.r[0], state.q), (0, 0));
let state = run(&usat(0, 0, 0), |s| s.r[1] = 1);
assert_eq!((state.r[0], state.q), (0, 1));
let state = run(&usat(1, 1, 8), |s| s.r[1] = 0x200);
assert_eq!((state.r[0], state.q), (255, 1), "ASR #1");
let state = run(&usat(0, 31, 31), |s| s.r[1] = 1);
assert_eq!((state.r[0], state.q), (0, 1), "LSL #31 makes it negative");
}
#[test]
fn usat16_saturates_each_halfword() {
let state = run(&plain(0b1_1010, [0, 1], 4), |s| s.r[1] = 0xFFFF_0020);
assert_eq!((state.r[0], state.q), (0x0000_000F, 1));
let state = run(&plain(0b1_1010, [0, 1], 15), |s| s.r[1] = 0x7FFF_0003);
assert_eq!((state.r[0], state.q), (0x7FFF_0003, 0));
}
#[test]
fn sbfx_and_ubfx_extract_a_field() {
let sbfx = |lsb: u32, widthm1: u32| plain(0b1_0100, [0, 1], field(lsb, widthm1));
let ubfx = |lsb: u32, widthm1: u32| plain(0b1_1100, [0, 1], field(lsb, widthm1));
let state = run(&sbfx(4, 7), |s| s.r[1] = 0x0000_0F80);
assert_eq!(state.r[0], 0xFFFF_FFF8);
let state = run(&sbfx(4, 7), |s| s.r[1] = 0x0000_0770);
assert_eq!(state.r[0], 0x77);
let state = run(&ubfx(4, 7), |s| s.r[1] = 0x0000_0F80);
assert_eq!(state.r[0], 0xF8);
let state = run(&ubfx(0, 31), |s| s.r[1] = 0x8765_4321);
assert_eq!(state.r[0], 0x8765_4321);
let state = run(&sbfx(31, 0), |s| s.r[1] = 0x8000_0000);
assert_eq!(state.r[0], 0xFFFF_FFFF);
let state = run(&ubfx(31, 0), |s| s.r[1] = 0x8000_0000);
assert_eq!(state.r[0], 1);
}
#[test]
fn bfi_and_bfc_write_a_field_and_keep_the_rest() {
let state = run(&plain(0b1_0110, [0, 1], field(8, 11)), |s| {
(s.r[0], s.r[1]) = (0xFFFF_FFFF, 0xFFFF_FFF5);
});
assert_eq!(state.r[0], 0xFFFF_F5FF);
let state = run(&plain(0b1_0110, [0, 1], field(0, 31)), |s| {
s.r[1] = 0x1234_5678;
});
assert_eq!(state.r[0], 0x1234_5678);
let state = run(&plain(0b1_0110, [0, 15], field(4, 11)), |s| {
s.r[0] = 0xFFFF_FFFF;
});
assert_eq!(state.r[0], 0xFFFF_F00F);
let state = run(&plain(0b1_0110, [0, 15], field(31, 31)), |s| {
s.r[0] = 0xFFFF_FFFF;
});
assert_eq!(state.r[0], 0x7FFF_FFFF);
}
#[test]
fn plain_binary_immediate_undefined_encodings() {
for op in [
0b0_0001, 0b0_0010, 0b0_0011, 0b0_0110, 0b0_1000, 0b0_1110, 0b1_0001, 0b1_1110,
0b1_1111,
] {
undefined(plain(op, [0, 1], 1));
}
for op in [
0b0_0000, 0b0_0100, 0b0_1010, 0b0_1100, 0b1_0000, 0b1_0010, 0b1_0100, 0b1_0110,
0b1_1000, 0b1_1010, 0b1_1100,
] {
undefined(plain(op, [15, 1], 1));
}
undefined(plain(0b1_0100, [0, 1], field(28, 7)));
undefined(plain(0b1_1100, [0, 1], field(1, 31)));
undefined(plain(0b1_0110, [0, 1], field(8, 7)));
undefined(plain(0b1_0110, [0, 15], field(1, 0)));
}
#[test]
fn plain_binary_immediate_unpredictable_choices_run_the_pseudocode() {
let sp = |value: u32| move |s: &mut State| s.r[13] = value;
let r1 = |value: u32| move |s: &mut State| s.r[1] = value;
let to_sp = |op: u32, rn: u32, imm12: u32, set: &dyn Fn(&mut State)| {
run(&plain(op, [13, rn], imm12), set).r[13]
};
assert_eq!(to_sp(0b0_0000, 1, 7, &r1(0x2000_0000)), 0x2000_0004, "ADDW");
assert_eq!(to_sp(0b0_1010, 1, 1, &r1(0x2000_0000)), 0x1FFF_FFFC, "SUBW");
assert_eq!(to_sp(0b0_0000, 15, 3, &|_| {}), 4, "ADR");
assert_eq!(to_sp(0b0_0100, 1, 0x237, &|_| {}), 0x1234, "MOVW");
assert_eq!(
to_sp(0b0_1100, 1, 0x234, &sp(0x2000_0107)),
0x1234_0104,
"MOVT"
);
assert_eq!(to_sp(0b1_0000, 1, field(0, 7), &r1(7)), 4, "SSAT");
assert_eq!(to_sp(0b1_1000, 1, field(0, 8), &r1(7)), 4, "USAT");
assert_eq!(
to_sp(0b1_0010, 1, 15, &r1(0x0001_0003)),
0x0001_0000,
"SSAT16"
);
assert_eq!(
to_sp(0b1_1010, 1, 15, &r1(0x0001_0003)),
0x0001_0000,
"USAT16"
);
assert_eq!(
to_sp(0b1_0100, 1, field(0, 31), &r1(0x2000_0103)),
0x2000_0100,
"SBFX"
);
assert_eq!(
to_sp(0b1_1100, 1, field(0, 31), &r1(0x2000_0103)),
0x2000_0100,
"UBFX"
);
let bfi = |s: &mut State| (s.r[13], s.r[1]) = (0x2000_0100, 3);
assert_eq!(to_sp(0b1_0110, 1, field(0, 1), &bfi), 0x2000_0100, "BFI");
assert_eq!(
to_sp(0b1_0110, 15, field(4, 7), &sp(0x2000_00F0)),
0x2000_0000,
"BFC"
);
let from = |op: u32, rn: u32, imm12: u32, set: &dyn Fn(&mut State)| {
let state = run(&plain(op, [0, rn], imm12), set);
(state.r[0], state.q)
};
let odd_sp = sp(0x2000_0103);
assert_eq!(from(0b1_0000, 13, field(0, 31), &odd_sp), (0x2000_0100, 0));
assert_eq!(from(0b1_0000, 15, field(0, 7), &|_| {}), (4, 0), "SSAT");
assert_eq!(from(0b1_1000, 13, field(0, 31), &odd_sp), (0x2000_0100, 0));
assert_eq!(from(0b1_1000, 15, field(0, 8), &|_| {}), (4, 0), "USAT");
let odd_halves = sp(0x1234_5677);
assert_eq!(from(0b1_0010, 13, 15, &odd_halves), (0x1234_5674, 0));
assert_eq!(from(0b1_0010, 15, 15, &|_| {}), (4, 0), "SSAT16");
assert_eq!(from(0b1_1010, 13, 15, &odd_halves), (0x1234_5674, 0));
assert_eq!(from(0b1_1010, 15, 15, &|_| {}), (4, 0), "USAT16");
let sbfx = from(0b1_0100, 13, field(0, 7), &sp(0x2000_00FF));
assert_eq!(sbfx, (0xFFFF_FFFC, 0), "SBFX");
assert_eq!(from(0b1_0100, 15, field(2, 0), &|_| {}), (0xFFFF_FFFF, 0));
assert_eq!(from(0b1_1100, 13, field(0, 1), &odd_sp), (0, 0), "UBFX");
assert_eq!(from(0b1_1100, 15, field(2, 0), &|_| {}), (1, 0));
let bfi = |s: &mut State| (s.r[0], s.r[13]) = (0xFFFF_FFFF, 0x2000_0013);
assert_eq!(from(0b1_0110, 13, field(0, 7), &bfi), (0xFFFF_FF10, 0));
}
#[test]
fn and_and_tst_register_take_the_shifter_s_carry() {
let state = run(&shifted(AND, 0, [0, 1, 2], (LSL, 4)), |s| {
(s.r[1], s.r[2]) = (0xFFFF_FFFF, 0x0F00_000F);
});
assert_eq!(state.r[0], 0xF000_00F0);
let state = run(&shifted(AND, 1, [0, 1, 2], (LSL, 4)), |s| {
(s.r[1], s.r[2]) = (0x8000_0000, 0x1F00_000F);
s.v = 1;
});
assert_eq!((state.r[0], nzcv(&state)), (0x8000_0000, [1, 0, 1, 1]));
let state = run(&shifted(AND, 1, [0, 1, 2], (LSR, 0)), |s| {
(s.r[1], s.r[2]) = (0xFFFF_FFFF, 0x8000_0000);
});
assert_eq!((state.r[0], nzcv(&state)), (0, [0, 1, 1, 0]), "LSR #32");
let state = run(&shifted(AND, 1, [15, 1, 2], (LSL, 0)), |s| {
(s.r[1], s.r[2]) = (1, 2);
s.c = 1;
});
assert_eq!((state.r[1], nzcv(&state)), (1, [0, 1, 1, 0]));
}
#[test]
fn bic_register() {
let state = run(&shifted(BIC, 1, [0, 1, 2], (ASR, 31)), |s| {
(s.r[1], s.r[2]) = (0x1234, 0x8000_0000);
s.c = 1;
});
assert_eq!((state.r[0], nzcv(&state)), (0, [0, 1, 0, 0]));
}
#[test]
fn orr_register() {
let state = run(&shifted(ORR, 1, [0, 1, 2], (ROR, 8)), |s| {
(s.r[1], s.r[2]) = (0xCD, 0xAB);
});
assert_eq!((state.r[0], nzcv(&state)), (0xAB00_00CD, [1, 0, 1, 0]));
}
#[test]
fn mov_register_moves_and_keeps_the_carry() {
let state = run(&shifted(ORR, 0, [0, 15, 1], (LSL, 0)), |s| {
s.r[1] = 0x1234_5678;
});
assert_eq!(state.r[0], 0x1234_5678);
let state = run(&shifted(ORR, 1, [0, 15, 1], (LSL, 0)), |s| {
s.r[0] = 5;
s.c = 1;
});
assert_eq!((state.r[0], nzcv(&state)), (0, [0, 1, 1, 0]));
let state = run(&shifted(ORR, 0, [13, 15, 0], (LSL, 0)), |s| {
s.r[0] = 0x2000_0103;
});
assert_eq!(state.r[13], 0x2000_0100);
let state = run(&shifted(ORR, 0, [0, 15, 13], (LSL, 0)), |s| {
s.r[13] = 0x2000_0103;
});
assert_eq!(state.r[0], 0x2000_0100);
}
#[test]
fn mov_and_add_register_to_the_pc_branch() {
let state = run(&shifted(ORR, 0, [15, 15, 0], (LSL, 0)), |s| s.r[0] = 0x101);
assert_eq!((state.r[15], state.t), (0x100, 1));
let state = run(&shifted(ADD, 0, [15, 1, 2], (LSL, 0)), |s| {
(s.r[1], s.r[2]) = (0x100, 0x21);
});
assert_eq!((state.r[15], nzcv(&state)), (0x120, [0, 0, 0, 0]));
}
#[test]
fn immediate_shifts_set_the_carry_out() {
let shift = |shift_type: u32, imm5: u32| shifted(ORR, 1, [0, 15, 1], (shift_type, imm5));
let state = run(&shift(LSL, 4), |s| s.r[1] = 0x1000_0001);
assert_eq!((state.r[0], nzcv(&state)), (0x10, [0, 0, 1, 0]));
let state = run(&shift(LSR, 0), |s| s.r[1] = 0x8000_0000);
assert_eq!((state.r[0], nzcv(&state)), (0, [0, 1, 1, 0]), "LSR #32");
let state = run(&shift(ASR, 4), |s| s.r[1] = 0x8000_0008);
assert_eq!((state.r[0], nzcv(&state)), (0xF800_0000, [1, 0, 1, 0]));
let state = run(&shift(ASR, 0), |s| s.r[1] = 0x8000_0000);
assert_eq!(
(state.r[0], nzcv(&state)),
(0xFFFF_FFFF, [1, 0, 1, 0]),
"ASR #32"
);
let state = run(&shift(ROR, 4), |s| {
s.r[1] = 0x12;
s.c = 1;
});
assert_eq!((state.r[0], nzcv(&state)), (0x2000_0001, [0, 0, 0, 0]));
let state = run(&shift(ROR, 0), |s| {
s.r[1] = 3;
s.c = 1;
});
assert_eq!(
(state.r[0], nzcv(&state)),
(0x8000_0001, [1, 0, 1, 0]),
"RRX"
);
}
#[test]
fn orn_and_mvn_register() {
let state = run(&shifted(ORN, 0, [0, 1, 2], (LSL, 0)), |s| {
s.r[2] = 0xFFFF_0000;
});
assert_eq!(state.r[0], 0x0000_FFFF);
let state = run(&shifted(ORN, 1, [0, 15, 2], (LSL, 1)), |s| {
s.r[2] = 0x8000_0001;
});
assert_eq!((state.r[0], nzcv(&state)), (0xFFFF_FFFD, [1, 0, 1, 0]));
}
#[test]
fn eor_and_teq_register() {
let state = run(&shifted(EOR, 1, [0, 1, 2], (LSL, 0)), |s| {
(s.r[1], s.r[2]) = (0x55, 0x55);
});
assert_eq!((state.r[0], nzcv(&state)), (0, [0, 1, 0, 0]));
let state = run(&shifted(EOR, 1, [15, 1, 2], (LSR, 1)), |s| s.r[2] = 1);
assert_eq!(nzcv(&state), [0, 1, 1, 0]);
}
#[test]
fn pkhbt_and_pkhtb_pack_halfwords() {
let operands = |s: &mut State| (s.r[1], s.r[2]) = (0x1111_2222, 0x3333_4444);
let state = run(&shifted(0b0110, 0, [0, 1, 2], (0b00, 16)), operands);
assert_eq!(state.r[0], 0x4444_2222);
let state = run(&shifted(0b0110, 0, [0, 1, 2], (0b10, 16)), operands);
assert_eq!(state.r[0], 0x1111_3333);
let state = run(&shifted(0b0110, 0, [0, 1, 2], (0b10, 0)), |s| {
(s.r[1], s.r[2]) = (0x1111_2222, 0x8000_0000);
});
assert_eq!(state.r[0], 0x1111_FFFF, "ASR #32");
}
#[test]
fn add_cmn_and_add_from_the_sp_register() {
let state = run(&shifted(ADD, 1, [0, 1, 2], (ASR, 1)), |s| {
(s.r[1], s.r[2]) = (0x7FFF_FFFF, 2);
});
assert_eq!((state.r[0], nzcv(&state)), (0x8000_0000, [1, 0, 0, 1]));
let state = run(&shifted(ADD, 1, [15, 1, 2], (LSL, 0)), |s| {
(s.r[1], s.r[2]) = (1, 0xFFFF_FFFF);
});
assert_eq!((state.r[15], nzcv(&state)), (4, [0, 1, 1, 0]));
let state = run(&shifted(ADD, 0, [0, 13, 1], (LSL, 2)), |s| {
(s.r[13], s.r[1]) = (0x2000_0000, 4);
});
assert_eq!(state.r[0], 0x2000_0010);
}
#[test]
fn adc_register_with_rrx() {
let state = run(&shifted(ADC, 1, [0, 1, 2], (ROR, 0)), |s| {
(s.r[1], s.r[2]) = (0x8000_0000, 2);
s.c = 1;
});
assert_eq!((state.r[0], nzcv(&state)), (2, [0, 0, 1, 1]));
}
#[test]
fn sbc_register() {
let state = run(&shifted(SBC, 1, [0, 1, 2], (LSL, 0)), |s| {
(s.r[1], s.r[2]) = (10, 3);
});
assert_eq!((state.r[0], nzcv(&state)), (6, [0, 0, 1, 0]));
}
#[test]
fn sub_cmp_and_sub_from_the_sp_register() {
let state = run(&shifted(SUB, 1, [0, 1, 2], (LSL, 1)), |s| {
(s.r[1], s.r[2]) = (4, 2);
});
assert_eq!((state.r[0], nzcv(&state)), (0, [0, 1, 1, 0]));
let state = run(&shifted(SUB, 1, [15, 1, 2], (LSL, 0)), |s| {
(s.r[1], s.r[2]) = (1, 2);
});
assert_eq!((state.r[1], nzcv(&state)), (1, [1, 0, 0, 0]));
let state = run(&shifted(SUB, 0, [13, 13, 1], (LSL, 2)), |s| {
(s.r[13], s.r[1]) = (0x2000_1000, 4);
});
assert_eq!(state.r[13], 0x2000_0FF0);
}
#[test]
fn rsb_register() {
let state = run(&shifted(RSB, 1, [0, 1, 2], (LSL, 0)), |s| {
(s.r[1], s.r[2]) = (5, 3);
});
assert_eq!((state.r[0], nzcv(&state)), (0xFFFF_FFFE, [1, 0, 0, 0]));
}
#[test]
fn shifted_register_undefined_encodings() {
for op in [0b0101, 0b0111, 0b1001, 0b1100, 0b1111] {
undefined(shifted(op, 0, [0, 1, 2], (LSL, 0)));
}
undefined(shifted(0b0110, 1, [0, 1, 2], (0b00, 0)));
undefined(shifted(0b0110, 0, [0, 1, 2], (0b01, 0)));
undefined(shifted(0b0110, 0, [0, 1, 2], (0b11, 0)));
undefined(shifted(0b0110, 0, [15, 1, 2], (0b00, 0)));
for op in [AND, EOR, SUB, BIC, ORN, ADC, SBC, RSB] {
undefined(shifted(op, 0, [15, 1, 2], (LSL, 0)));
}
undefined(shifted(ORR, 0, [15, 1, 2], (LSL, 0)));
undefined(shifted(ORR, 0, [15, 15, 2], (LSL, 1)));
undefined(shifted(ORR, 1, [15, 15, 2], (ROR, 0)));
}
#[test]
fn register_shifts_take_the_bottom_byte() {
let shift = |op1: u32| register(op1, [0, 1, 2], 0);
let state = run(&shift(0b0001), |s| (s.r[1], s.r[2]) = (0x8000_0001, 1));
assert_eq!((state.r[0], nzcv(&state)), (2, [0, 0, 1, 0]));
let state = run(&shift(0b0001), |s| (s.r[1], s.r[2]) = (1, 0xFFFF_FF20));
assert_eq!((state.r[0], nzcv(&state)), (0, [0, 1, 1, 0]), "LSL by 32");
let state = run(&shift(0b0001), |s| {
(s.r[1], s.r[2]) = (0xFFFF_FFFF, 0xFFFF_FF21);
s.c = 1;
});
assert_eq!((state.r[0], nzcv(&state)), (0, [0, 1, 0, 0]), "LSL by 33");
let state = run(&shift(0b0011), |s| (s.r[1], s.r[2]) = (0x8000_0000, 31));
assert_eq!((state.r[0], nzcv(&state)), (1, [0, 0, 0, 0]));
let state = run(&shift(0b0101), |s| (s.r[1], s.r[2]) = (0x8000_0000, 200));
assert_eq!((state.r[0], nzcv(&state)), (0xFFFF_FFFF, [1, 0, 1, 0]));
let state = run(&shift(0b0111), |s| (s.r[1], s.r[2]) = (0x12, 0x24));
assert_eq!((state.r[0], nzcv(&state)), (0x2000_0001, [0, 0, 0, 0]));
let state = run(&shift(0b0110), |s| {
(s.r[1], s.r[2]) = (0x12, 0);
s.c = 1;
});
assert_eq!((state.r[0], nzcv(&state)), (0x12, [0, 0, 1, 0]), "no S");
let state = run(&shift(0b0000), |s| (s.r[1], s.r[2]) = (0x8000_0001, 1));
assert_eq!((state.r[0], nzcv(&state)), (2, [0, 0, 0, 0]));
}
#[test]
fn halfword_extends() {
let state = run(®ister(0b0000, [0, 1, 2], 0b1010), |s| {
(s.r[1], s.r[2]) = (0x1_0000, 0x8001_1234);
});
assert_eq!(state.r[0], 0x0000_8001, "SXTAH ROR #16");
let state = run(®ister(0b0000, [0, 15, 2], 0b1000), |s| s.r[2] = 0x8000);
assert_eq!(state.r[0], 0xFFFF_8000, "SXTH");
let state = run(®ister(0b0001, [0, 1, 2], 0b1000), |s| {
(s.r[1], s.r[2]) = (1, 0xFFFF_8000);
});
assert_eq!(state.r[0], 0x8001, "UXTAH");
let state = run(®ister(0b0001, [0, 15, 2], 0b1001), |s| {
s.r[2] = 0x00AB_CD00;
});
assert_eq!(state.r[0], 0xABCD, "UXTH ROR #8");
}
#[test]
fn dual_byte_extends() {
let state = run(®ister(0b0010, [0, 1, 2], 0b1000), |s| {
(s.r[1], s.r[2]) = (0x0001_0002, 0x0080_0001);
});
assert_eq!(state.r[0], 0xFF81_0003, "SXTAB16");
let state = run(®ister(0b0010, [0, 15, 2], 0b1001), |s| {
s.r[2] = 0x8000_7F00;
});
assert_eq!(state.r[0], 0xFF80_007F, "SXTB16 ROR #8");
let state = run(®ister(0b0011, [0, 1, 2], 0b1000), |s| {
(s.r[1], s.r[2]) = (0xFFFF_0001, 0x0080_00FF);
});
assert_eq!(state.r[0], 0x007F_0100, "UXTAB16 wraps each lane");
let state = run(®ister(0b0011, [0, 15, 2], 0b1000), |s| {
s.r[2] = 0x1234_5678;
});
assert_eq!(state.r[0], 0x0034_0078, "UXTB16");
}
#[test]
fn byte_extends() {
let state = run(®ister(0b0100, [0, 1, 2], 0b1011), |s| {
(s.r[1], s.r[2]) = (0x100, 0x8012_3456);
});
assert_eq!(state.r[0], 0x80, "SXTAB ROR #24");
let state = run(®ister(0b0100, [0, 15, 2], 0b1000), |s| s.r[2] = 0xFF);
assert_eq!(state.r[0], 0xFFFF_FFFF, "SXTB");
let state = run(®ister(0b0100, [0, 15, 2], 0b1000), |s| s.r[2] = 0x7F);
assert_eq!(state.r[0], 0x7F, "SXTB");
let state = run(®ister(0b0101, [0, 1, 2], 0b1000), |s| {
(s.r[1], s.r[2]) = (0x1000, 0xFFFF_FFFF);
});
assert_eq!(state.r[0], 0x10FF, "UXTAB");
let state = run(®ister(0b0101, [0, 15, 2], 0b1010), |s| {
s.r[2] = 0x00AB_0000;
});
assert_eq!(state.r[0], 0xAB, "UXTB ROR #16");
}
#[test]
fn register_undefined_encodings() {
let [hw1, hw2] = register(0b0000, [0, 1, 2], 0);
undefined([hw1, hw2 & 0x0FFF]);
undefined([hw1, hw2 & 0x7FFF]);
for (op1, op2) in [
(0b0110, 0b1000),
(0b0111, 0b1000),
(0b0000, 0b0001),
(0b0001, 0b0100),
(0b0111, 0b0111),
(0b1100, 0b1000),
(0b1000, 0b1100),
(0b1111, 0b1111),
] {
undefined(register(op1, [0, 1, 2], op2));
}
undefined(register(0b0001, [15, 1, 2], 0));
undefined(register(0b0101, [15, 1, 2], 0b1000));
}
#[test]
fn register_unpredictable_choices_run_the_pseudocode() {
let state = run(®ister(0b0000, [13, 1, 2], 0), |s| {
(s.r[1], s.r[2]) = (0x81, 0);
});
assert_eq!(state.r[13], 0x80);
let state = run(®ister(0b0101, [0, 1, 15], 0b1000), |s| s.r[1] = 0x100);
assert_eq!(state.r[0], 0x104);
}
}