use superh::{BranchTarget, Ins, Options, Reg, parse, parse_with_discriminant};
fn opts() -> Options {
Options::default()
}
fn dis(word: u16) -> String {
let ins = parse(word, 0, &opts());
format!("{}", ins.display(&opts()))
}
#[test]
fn mov_rm_rn() {
let ins = parse(0x6323, 0, &opts());
assert_eq!(ins, Ins::MovRmRn { rn: Reg::R3, rm: Reg::R2 });
assert_eq!(dis(0x6323), "mov r2, r3");
}
#[test]
fn mov_imm_rn() {
let ins = parse(0xe001, 0, &opts());
assert_eq!(ins, Ins::MovImmRn { rn: Reg::R0, imm: 1 });
assert_eq!(dis(0xe001), "mov #0x1, r0");
}
#[test]
fn mov_neg_imm_rn() {
let ins = parse(0xe0ff, 0, &opts());
assert_eq!(ins, Ins::MovImmRn { rn: Reg::R0, imm: -1i8 });
assert_eq!(dis(0xe0ff), "mov #-0x1, r0");
}
#[test]
fn movb_rm_at_rn() {
assert_eq!(dis(0x2210), "mov.b r1, @r2");
}
#[test]
fn movl_at_rm_rn() {
let ins = parse(0x6542, 0, &opts());
assert_eq!(ins, Ins::MovlAtRmRn { rn: Reg::R5, rm: Reg::R4 });
}
#[test]
fn movb_r0_at_disp_rn() {
assert_eq!(dis(0x8012), "mov.b r0, @(0x2, r1)");
}
#[test]
fn movl_rm_at_disp_rn() {
assert_eq!(dis(0x1323), "mov.l r2, @(0xc, r3)");
}
#[test]
fn movl_at_disp_rm_rn() {
assert_eq!(dis(0x5011), "mov.l @(0x4, r1), r0");
}
#[test]
fn movb_at_rm_inc_rn() {
assert_eq!(dis(0x6104), "mov.b @r0+, r1");
}
#[test]
fn mova() {
let ins = parse(0xc700, 0, &opts());
assert_eq!(ins, Ins::Mova { disp: 4 });
}
#[test]
fn movt() {
let ins = parse(0x0429, 0, &opts());
assert_eq!(ins, Ins::Movt { rn: Reg::R4 });
assert_eq!(dis(0x0429), "movt r4");
}
#[test]
fn swap_b() {
assert_eq!(dis(0x6218), "swap.b r1, r2");
}
#[test]
fn movl_at_disp_pc_rn_aligned() {
let ins = parse(0xd001, 0x1000, &opts());
assert_eq!(ins, Ins::MovlAtDispPcRn { rn: Reg::R0, disp: 8 });
assert_eq!(format!("{}", ins.display(&opts())), "mov.l @(0x8, pc), r0");
}
#[test]
fn movl_at_disp_pc_rn_unaligned() {
let ins = parse(0xd001, 0x1002, &opts());
assert_eq!(ins, Ins::MovlAtDispPcRn { rn: Reg::R0, disp: 6 });
assert_eq!(format!("{}", ins.display(&opts())), "mov.l @(0x6, pc), r0");
}
#[test]
fn movw_at_disp_pc_rn_no_alignment() {
let ins_a = parse(0x9001, 0x0000, &opts());
let ins_b = parse(0x9001, 0x1002, &opts());
assert_eq!(ins_a, Ins::MovwAtDispPcRn { rn: Reg::R0, disp: 1 });
assert_eq!(format!("{}", ins_a.display(&opts())), "mov.w @(0x6, pc), r0");
assert_eq!(format!("{}", ins_b.display(&opts())), "mov.w @(0x6, pc), r0");
}
#[test]
fn mova_unaligned_pc() {
let ins = parse(0xc700, 0x1002, &opts());
assert_eq!(ins, Ins::Mova { disp: 2 });
assert_eq!(format!("{}", ins.display(&opts())), "mova @(0x2, pc), r0");
}
#[test]
fn add_rm_rn() {
let ins = parse(0x321c, 0, &opts());
assert_eq!(ins, Ins::AddRmRn { rn: Reg::R2, rm: Reg::R1 });
assert_eq!(dis(0x321c), "add r1, r2");
}
#[test]
fn add_imm_rn() {
assert_eq!(dis(0x7004), "add #0x4, r0");
}
#[test]
fn cmp_eq_imm() {
assert_eq!(dis(0x8800), "cmp/eq #0x0, r0");
}
#[test]
fn cmp_eq_rm_rn() {
assert_eq!(dis(0x3430), "cmp/eq r3, r4");
}
#[test]
fn neg() {
assert_eq!(dis(0x621b), "neg r1, r2");
}
#[test]
fn exts_b() {
assert_eq!(dis(0x610e), "exts.b r0, r1");
}
#[test]
fn and_rm_rn() {
assert_eq!(dis(0x2329), "and r2, r3");
}
#[test]
fn or_imm_r0() {
assert_eq!(dis(0xcbff), "or #0xff, r0");
}
#[test]
fn xor_rm_rn() {
assert_eq!(dis(0x265a), "xor r5, r6");
}
#[test]
fn tst_imm_r0() {
assert_eq!(dis(0xc801), "tst #0x1, r0");
}
#[test]
fn shll() {
let ins = parse(0x4000, 0, &opts());
assert_eq!(ins, Ins::ShllRn { rn: Reg::R0 });
assert_eq!(dis(0x4000), "shll r0");
}
#[test]
fn shlr16() {
assert_eq!(dis(0x4329), "shlr16 r3");
}
#[test]
fn rotcl() {
assert_eq!(dis(0x4524), "rotcl r5");
}
#[test]
fn bt() {
let ins = parse(0x8903, 0, &opts());
assert_eq!(ins, Ins::Bt { disp: BranchTarget { addr: 10 } });
assert_eq!(dis(0x8903), "bt 0xa");
}
#[test]
fn bf() {
let ins = parse(0x8bfe, 0, &opts());
assert_eq!(ins, Ins::Bf { disp: BranchTarget { addr: 0 } });
}
#[test]
fn bra() {
let ins = parse(0xa000, 0, &opts());
assert_eq!(ins, Ins::Bra { disp: BranchTarget { addr: 4 } });
assert_eq!(dis(0xa000), "bra 0x4");
}
#[test]
fn jmp() {
let ins = parse(0x4e2b, 0, &opts());
assert_eq!(ins, Ins::JmpAtRn { rn: Reg::R14 });
assert_eq!(dis(0x4e2b), "jmp @r14");
}
#[test]
fn jsr() {
assert_eq!(dis(0x400b), "jsr @r0");
}
#[test]
fn rts() {
let ins = parse(0x000b, 0, &opts());
assert_eq!(ins, Ins::Rts);
assert_eq!(dis(0x000b), "rts");
}
#[test]
fn rte() {
let ins = parse(0x002b, 0, &opts());
assert_eq!(ins, Ins::Rte);
assert_eq!(dis(0x002b), "rte");
}
#[test]
fn nop() {
let ins = parse(0x0009, 0, &opts());
assert_eq!(ins, Ins::Nop);
assert_eq!(dis(0x0009), "nop");
}
#[test]
fn clrt() {
let ins = parse(0x0008, 0, &opts());
assert_eq!(ins, Ins::Clrt);
}
#[test]
fn sett() {
let ins = parse(0x0018, 0, &opts());
assert_eq!(ins, Ins::Sett);
}
#[test]
fn trapa() {
let ins = parse(0xc320, 0, &opts());
assert_eq!(ins, Ins::Trapa { imm: 0x20 });
assert_eq!(dis(0xc320), "trapa #0x20");
}
#[test]
fn stc_sr_rn() {
let ins = parse(0x0002, 0, &opts());
assert_eq!(ins, Ins::StcSrRn { rn: Reg::R0 });
assert_eq!(dis(0x0002), "stc sr, r0");
}
#[test]
fn sts_pr_rn() {
let ins = parse(0x0e2a, 0, &opts());
assert_eq!(ins, Ins::StsPrRn { rn: Reg::R14 });
assert_eq!(dis(0x0e2a), "sts pr, r14");
}
#[test]
fn lds_rm_pr() {
let ins = parse(0x4e2a, 0, &opts());
assert_eq!(ins, Ins::LdsRmPr { rm: Reg::R14 });
assert_eq!(dis(0x4e2a), "lds r14, pr");
}
#[test]
fn stsl_pr_at_dec_rn() {
let ins = parse(0x4f22, 0, &opts());
assert_eq!(ins, Ins::StslPrAtDecRn { rn: Reg::R15 });
assert_eq!(dis(0x4f22), "sts.l pr, @-r15");
}
#[test]
fn ldsl_at_rm_inc_pr() {
let ins = parse(0x4f26, 0, &opts());
assert_eq!(ins, Ins::LdslAtRmIncPr { rm: Reg::R15 });
assert_eq!(dis(0x4f26), "lds.l @r15+, pr");
}
#[test]
fn unknown_word() {
let ins = parse(0xffff, 0, &opts());
assert_eq!(ins, Ins::Word(0xffff));
assert_eq!(dis(0xffff), ".word 0xffff");
}
#[test]
fn parse_with_discriminant_roundtrip() {
let opts = opts();
for word in 0u16..=0xffff {
let ins = parse(word, 0, &opts);
let disc = ins.discriminant();
let again = parse_with_discriminant(word, disc, 0, &opts);
assert_eq!(ins, again, "round-trip failed for word 0x{word:04x}");
}
}
#[test]
fn parse_with_discriminant_unknown_falls_back() {
let opts = opts();
let result = parse_with_discriminant(0x6323, 0xffff, 0, &opts);
assert_eq!(result, Ins::Word(0x6323));
}
#[test]
fn imm_decimal_uimm() {
let opts = Options { imm_decimal: true, ..Options::default() };
let ins = parse(0x7308, 0, &opts);
assert_eq!(format!("{}", ins.display(&opts)), "add #8, r3");
}
#[test]
fn imm_decimal_simm() {
let opts = Options { imm_decimal: true, ..Options::default() };
let ins = parse(0xe064, 0, &opts);
assert_eq!(format!("{}", ins.display(&opts)), "mov #100, r0");
}
#[test]
fn imm_decimal_negative() {
let opts = Options { imm_decimal: true, ..Options::default() };
let ins = parse(0xe0ff, 0, &opts);
assert_eq!(format!("{}", ins.display(&opts)), "mov #-1, r0");
}
#[test]
fn imm_hex_default() {
let ins = parse(0xe064, 0, &opts());
assert_eq!(format!("{}", ins.display(&opts())), "mov #0x64, r0");
}
#[test]
fn parse_with_discriminant_specific() {
let opts = opts();
let ins = parse(0x6323, 0, &opts);
let disc = ins.discriminant();
let again = parse_with_discriminant(0x6323, disc, 0, &opts);
assert_eq!(again, Ins::MovRmRn { rn: Reg::R3, rm: Reg::R2 });
}