#![expect(clippy::identity_op, reason = "Test readability")]
use crate::instructions::Instruction;
use crate::instructions::rv32::zce::zcb::Rv32ZcbInstruction;
use crate::registers::general_purpose::Reg;
const fn make_zcb_q00(sub: u16, rs1p: u16, bit6: u16, bit5: u16, rd_rs2p: u16) -> u16 {
(0b100 << 13) | (sub << 10) | (rs1p << 7) | (bit6 << 6) | (bit5 << 5) | (rd_rs2p << 2)
}
const fn make_zcb_q01(rd_rs1p: u16, funct2b: u16, rs2_sub: u16) -> u16 {
(0b100 << 13)
| (1 << 12)
| (0b11 << 10)
| (rd_rs1p << 7)
| (funct2b << 5)
| (rs2_sub << 2)
| 0b01
}
#[test]
fn test_clbu_all_uimm_values() {
for (bit6, bit5, expected_uimm) in [(0, 0, 0), (1, 0, 1), (0, 1, 2), (1, 1, 3)] {
let inst = make_zcb_q00(0b000, 0, bit6, bit5, 0);
let decoded = Rv32ZcbInstruction::<Reg<u32>>::try_decode(u32::from(inst)).unwrap();
assert_eq!(
decoded,
Rv32ZcbInstruction::CLbu {
rd: Reg::S0,
rs1: Reg::S0,
uimm: expected_uimm
}
);
}
}
#[test]
fn test_clbu_all_prime_regs() {
let inst = make_zcb_q00(0b000, 7, 0, 0, 6);
let decoded = Rv32ZcbInstruction::<Reg<u32>>::try_decode(u32::from(inst)).unwrap();
assert_eq!(
decoded,
Rv32ZcbInstruction::CLbu {
rd: Reg::A4,
rs1: Reg::A5,
uimm: 0
}
);
}
#[test]
fn test_clhu_uimm0() {
let inst = make_zcb_q00(0b001, 0, 0, 0, 0);
let decoded = Rv32ZcbInstruction::<Reg<u32>>::try_decode(u32::from(inst)).unwrap();
assert_eq!(
decoded,
Rv32ZcbInstruction::CLhu {
rd: Reg::S0,
rs1: Reg::S0,
uimm: 0
}
);
}
#[test]
fn test_clhu_uimm2() {
let inst = make_zcb_q00(0b001, 0, 0, 1, 0);
let decoded = Rv32ZcbInstruction::<Reg<u32>>::try_decode(u32::from(inst)).unwrap();
assert_eq!(
decoded,
Rv32ZcbInstruction::CLhu {
rd: Reg::S0,
rs1: Reg::S0,
uimm: 2
}
);
}
#[test]
fn test_clh_uimm0() {
let inst = make_zcb_q00(0b001, 0, 1, 0, 0);
let decoded = Rv32ZcbInstruction::<Reg<u32>>::try_decode(u32::from(inst)).unwrap();
assert_eq!(
decoded,
Rv32ZcbInstruction::CLh {
rd: Reg::S0,
rs1: Reg::S0,
uimm: 0
}
);
}
#[test]
fn test_clh_uimm2() {
let inst = make_zcb_q00(0b001, 0, 1, 1, 0);
let decoded = Rv32ZcbInstruction::<Reg<u32>>::try_decode(u32::from(inst)).unwrap();
assert_eq!(
decoded,
Rv32ZcbInstruction::CLh {
rd: Reg::S0,
rs1: Reg::S0,
uimm: 2
}
);
}
#[test]
fn test_csb_uimm0() {
let inst = make_zcb_q00(0b010, 0, 0, 0, 0);
let decoded = Rv32ZcbInstruction::<Reg<u32>>::try_decode(u32::from(inst)).unwrap();
assert_eq!(
decoded,
Rv32ZcbInstruction::CSb {
rs1: Reg::S0,
rs2: Reg::S0,
uimm: 0
}
);
}
#[test]
fn test_csb_uimm3() {
let inst = make_zcb_q00(0b010, 0, 1, 1, 1);
let decoded = Rv32ZcbInstruction::<Reg<u32>>::try_decode(u32::from(inst)).unwrap();
assert_eq!(
decoded,
Rv32ZcbInstruction::CSb {
rs1: Reg::S0,
rs2: Reg::S1,
uimm: 3
}
);
}
#[test]
fn test_csh_uimm0() {
let inst = make_zcb_q00(0b011, 0, 0, 0, 0);
let decoded = Rv32ZcbInstruction::<Reg<u32>>::try_decode(u32::from(inst)).unwrap();
assert_eq!(
decoded,
Rv32ZcbInstruction::CSh {
rs1: Reg::S0,
rs2: Reg::S0,
uimm: 0
}
);
}
#[test]
fn test_csh_uimm2() {
let inst = make_zcb_q00(0b011, 0, 0, 1, 1);
let decoded = Rv32ZcbInstruction::<Reg<u32>>::try_decode(u32::from(inst)).unwrap();
assert_eq!(
decoded,
Rv32ZcbInstruction::CSh {
rs1: Reg::S0,
rs2: Reg::S1,
uimm: 2
}
);
}
#[test]
fn test_csh_funct1_1_reserved() {
let inst = make_zcb_q00(0b011, 0, 1, 0, 0);
assert!(Rv32ZcbInstruction::<Reg<u32>>::try_decode(u32::from(inst)).is_none());
}
#[test]
fn test_czext_b() {
let inst = make_zcb_q01(0, 0b11, 0b000);
let decoded = Rv32ZcbInstruction::<Reg<u32>>::try_decode(u32::from(inst)).unwrap();
assert_eq!(decoded, Rv32ZcbInstruction::CZextB { rd: Reg::S0 });
}
#[test]
fn test_csext_b() {
let inst = make_zcb_q01(0, 0b11, 0b001);
let decoded = Rv32ZcbInstruction::<Reg<u32>>::try_decode(u32::from(inst)).unwrap();
assert_eq!(decoded, Rv32ZcbInstruction::CSextB { rd: Reg::S0 });
}
#[test]
fn test_czext_h() {
let inst = make_zcb_q01(0, 0b11, 0b010);
let decoded = Rv32ZcbInstruction::<Reg<u32>>::try_decode(u32::from(inst)).unwrap();
assert_eq!(decoded, Rv32ZcbInstruction::CZextH { rd: Reg::S0 });
}
#[test]
fn test_csext_h() {
let inst = make_zcb_q01(0, 0b11, 0b011);
let decoded = Rv32ZcbInstruction::<Reg<u32>>::try_decode(u32::from(inst)).unwrap();
assert_eq!(decoded, Rv32ZcbInstruction::CSextH { rd: Reg::S0 });
}
#[test]
fn test_czext_w_absent_in_rv32() {
let inst = make_zcb_q01(0, 0b11, 0b100);
assert!(
Rv32ZcbInstruction::<Reg<u32>>::try_decode(u32::from(inst)).is_none(),
"C.ZEXT.W should not exist in RV32"
);
}
#[test]
fn test_cnot() {
let inst = make_zcb_q01(0, 0b11, 0b101);
let decoded = Rv32ZcbInstruction::<Reg<u32>>::try_decode(u32::from(inst)).unwrap();
assert_eq!(decoded, Rv32ZcbInstruction::CNot { rd: Reg::S0 });
}
#[test]
fn test_unary_reserved_sub_110() {
let inst = make_zcb_q01(0, 0b11, 0b110);
assert!(Rv32ZcbInstruction::<Reg<u32>>::try_decode(u32::from(inst)).is_none());
}
#[test]
fn test_unary_reserved_sub_111() {
let inst = make_zcb_q01(0, 0b11, 0b111);
assert!(Rv32ZcbInstruction::<Reg<u32>>::try_decode(u32::from(inst)).is_none());
}
#[test]
fn test_unary_all_prime_regs() {
for r in 0..8 {
let inst = make_zcb_q01(r, 0b11, 0b101);
let decoded = Rv32ZcbInstruction::<Reg<u32>>::try_decode(u32::from(inst)).unwrap();
assert!(matches!(decoded, Rv32ZcbInstruction::CNot { .. }));
}
}
#[test]
fn test_cmul() {
let inst = make_zcb_q01(0, 0b10, 0b001);
let decoded = Rv32ZcbInstruction::<Reg<u32>>::try_decode(u32::from(inst)).unwrap();
assert_eq!(
decoded,
Rv32ZcbInstruction::CMul {
rd: Reg::S0,
rs2: Reg::S1
}
);
}
#[test]
fn test_cmul_same_reg() {
let inst = make_zcb_q01(0, 0b10, 0b000);
let decoded = Rv32ZcbInstruction::<Reg<u32>>::try_decode(u32::from(inst)).unwrap();
assert_eq!(
decoded,
Rv32ZcbInstruction::CMul {
rd: Reg::S0,
rs2: Reg::S0
}
);
}
#[test]
fn test_cmul_all_prime_reg_pairs() {
for rd in 0..8 {
for rs2 in 0..8 {
let inst = make_zcb_q01(rd, 0b10, rs2);
let decoded = Rv32ZcbInstruction::<Reg<u32>>::try_decode(u32::from(inst)).unwrap();
assert!(matches!(decoded, Rv32ZcbInstruction::CMul { .. }));
}
}
}
#[test]
fn test_reserved_funct2b_00() {
let inst = make_zcb_q01(0, 0b00, 0);
assert!(Rv32ZcbInstruction::<Reg<u32>>::try_decode(u32::from(inst)).is_none());
}
#[test]
fn test_reserved_funct2b_01() {
let inst = make_zcb_q01(0, 0b01, 0);
assert!(Rv32ZcbInstruction::<Reg<u32>>::try_decode(u32::from(inst)).is_none());
}
#[test]
fn test_non_zcb_q10_returns_none() {
let inst = (0b000 << 13) | (1 << 12) | (10 << 7) | (3 << 2) | 0b10;
assert!(Rv32ZcbInstruction::<Reg<u32>>::try_decode(inst).is_none());
}
#[test]
fn test_zca_q01_funct3_000_returns_none() {
let inst = (0b000 << 13) | (0 << 12) | (10 << 7) | (5 << 2) | 0b01;
assert!(Rv32ZcbInstruction::<Reg<u32>>::try_decode(inst).is_none());
}