use superh::{ControlFlow, DecodeOptions, EffectContext, Reg, Resource, StatusBit, decode};
const T: Resource = Resource::Status(StatusBit::T);
fn gp(number: u8) -> Resource {
Resource::Gp(Reg::from_number(number).expect("register in range"))
}
fn assert_effects(word: u16, reads: &[Resource], writes: &[Resource]) {
let instruction = decode(word, &DecodeOptions::default())
.instruction()
.copied()
.expect("audited instruction must decode");
let effects = instruction.effects(EffectContext::default());
for (set, expected) in [
(effects.must_read(), reads),
(effects.may_read(), reads),
(effects.must_write(), writes),
(effects.may_write(), writes),
] {
let mut unique = Vec::new();
for resource in expected {
if !unique.contains(resource) {
unique.push(*resource);
}
}
assert_eq!(set.len(), unique.len(), "{word:#06x}: {set:?}, expected {unique:?}");
for resource in unique {
assert!(set.contains(resource), "{word:#06x}: missing {resource:?}");
}
}
assert_eq!(effects.memory().count(), 0, "{word:#06x}");
assert_eq!(effects.control_flow(), ControlFlow::Fallthrough, "{word:#06x}");
}
#[test]
fn carry_arithmetic_reads_t_and_only_required_operands() {
for m in 0_u8..16 {
for n in 0_u8..16 {
let fields = (u16::from(n) << 8) | (u16::from(m) << 4);
for opcode in [0x300e, 0x300a] {
assert_effects(opcode | fields, &[gp(m), gp(n), T], &[gp(n), T]);
}
assert_effects(0x600a | fields, &[gp(m), T], &[gp(n), T]);
}
}
}
#[test]
fn overflow_arithmetic_writes_t_without_reading_it() {
for m in 0_u8..16 {
for n in 0_u8..16 {
let fields = (u16::from(n) << 8) | (u16::from(m) << 4);
for opcode in [0x300f, 0x300b] {
assert_effects(opcode | fields, &[gp(m), gp(n)], &[gp(n), T]);
}
}
}
}
#[test]
fn rotates_and_single_bit_shifts_distinguish_t_inputs() {
for n in 0_u8..16 {
let fields = u16::from(n) << 8;
for opcode in [0x4024, 0x4025] {
assert_effects(opcode | fields, &[gp(n), T], &[gp(n), T]);
}
for opcode in [0x4004, 0x4005, 0x4020, 0x4021, 0x4000, 0x4001] {
assert_effects(opcode | fields, &[gp(n)], &[gp(n), T]);
}
}
}
#[test]
fn multi_bit_shifts_preserve_status() {
for n in 0_u8..16 {
for opcode in [0x4008, 0x4018, 0x4028, 0x4009, 0x4019, 0x4029] {
assert_effects(opcode | (u16::from(n) << 8), &[gp(n)], &[gp(n)]);
}
}
}
#[test]
#[cfg(feature = "sh3")]
fn dynamic_shifts_read_count_and_value_and_preserve_status() {
for m in 0_u8..16 {
for n in 0_u8..16 {
let fields = (u16::from(n) << 8) | (u16::from(m) << 4);
for opcode in [0x400c, 0x400d] {
assert_effects(opcode | fields, &[gp(m), gp(n)], &[gp(n)]);
}
}
}
}
#[test]
fn comparisons_read_operands_and_write_only_t() {
for m in 0_u8..16 {
for n in 0_u8..16 {
let fields = (u16::from(n) << 8) | (u16::from(m) << 4);
for opcode in [0x3000, 0x3003, 0x3007, 0x3006, 0x3002, 0x200c] {
assert_effects(opcode | fields, &[gp(m), gp(n)], &[T]);
}
}
for opcode in [0x4015, 0x4011] {
assert_effects(opcode | (u16::from(m) << 8), &[gp(m)], &[T]);
}
}
for imm in 0_u16..=255 {
assert_effects(0x8800 | imm, &[gp(0)], &[T]);
}
}
#[test]
#[cfg(feature = "sh2")]
fn decrement_and_test_reads_and_writes_destination() {
for n in 0_u8..16 {
assert_effects(0x4010 | (u16::from(n) << 8), &[gp(n)], &[gp(n), T]);
}
}