use super::super::arg::truncate_imm; /// The upper bit of the bit number to test. #[derive(Debug)] enum B5 { /// When the bit number is below 32. B532 = 0, /// When the bit number is equal to or above 32. B564 = 1 } /// A convenience function so that we can convert the bit number directly into a /// B5 variant. impl From for B5 { fn from(bit_num: u8) -> Self { match bit_num { 0..=31 => B5::B532, 32..=63 => B5::B564, _ => panic!("Invalid bit number: {}", bit_num) } } } /// The operation to perform for this instruction. enum Op { /// The test bit zero operation. TBZ = 0, /// The test bit not zero operation. TBNZ = 1 } /// The struct that represents an A64 test bit instruction that can be encoded. /// /// TBNZ/TBZ /// +-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ /// | 31 30 29 28 | 27 26 25 24 | 23 22 21 20 | 19 18 17 16 | 15 14 13 12 | 11 10 09 08 | 07 06 05 04 | 03 02 01 00 | /// | 0 1 1 0 1 1 | /// | b5 op b40............. imm14.......................................... rt.............. | /// +-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ /// pub struct TestBit { /// The number of the register to test. rt: u8, /// The PC-relative offset to the target instruction in term of number of /// instructions. imm14: i16, /// The lower 5 bits of the bit number to be tested. b40: u8, /// The operation to perform for this instruction. op: Op, /// The upper bit of the bit number to test. b5: B5 } impl TestBit { /// TBNZ /// https://developer.arm.com/documentation/ddi0596/2021-12/Base-Instructions/TBNZ--Test-bit-and-Branch-if-Nonzero-?lang=en pub fn tbnz(rt: u8, bit_num: u8, offset: i16) -> Self { Self { rt, imm14: offset, b40: bit_num & 0b11111, op: Op::TBNZ, b5: bit_num.into() } } /// TBZ /// https://developer.arm.com/documentation/ddi0596/2021-12/Base-Instructions/TBZ--Test-bit-and-Branch-if-Zero-?lang=en pub fn tbz(rt: u8, bit_num: u8, offset: i16) -> Self { Self { rt, imm14: offset, b40: bit_num & 0b11111, op: Op::TBZ, b5: bit_num.into() } } } /// https://developer.arm.com/documentation/ddi0602/2022-03/Index-by-Encoding/Branches--Exception-Generating-and-System-instructions?lang=en const FAMILY: u32 = 0b11011; impl From for u32 { /// Convert an instruction into a 32-bit value. fn from(inst: TestBit) -> Self { let b40 = (inst.b40 & 0b11111) as u32; let imm14 = truncate_imm::<_, 14>(inst.imm14); 0 | ((inst.b5 as u32) << 31) | (FAMILY << 25) | ((inst.op as u32) << 24) | (b40 << 19) | (imm14 << 5) | inst.rt as u32 } } impl From for [u8; 4] { /// Convert an instruction into a 4 byte array. fn from(inst: TestBit) -> [u8; 4] { let result: u32 = inst.into(); result.to_le_bytes() } } #[cfg(test)] mod tests { use super::*; #[test] fn test_tbnz() { let inst = TestBit::tbnz(0, 0, 0); let result: u32 = inst.into(); assert_eq!(0x37000000, result); } #[test] fn test_tbnz_negative() { let inst = TestBit::tbnz(0, 0, -1); let result: u32 = inst.into(); assert_eq!(0x3707ffe0, result); } #[test] fn test_tbz() { let inst = TestBit::tbz(0, 0, 0); let result: u32 = inst.into(); assert_eq!(0x36000000, result); } #[test] fn test_tbz_negative() { let inst = TestBit::tbz(0, 0, -1); let result: u32 = inst.into(); assert_eq!(0x3607ffe0, result); } }