use super::super::arg::{InstructionOffset, truncate_imm}; /// The operation to perform for this instruction. enum Op { /// Branch directly, with a hint that this is not a subroutine call or /// return. Branch = 0, /// Branch directly, with a hint that this is a subroutine call or return. BranchWithLink = 1 } /// The struct that represents an A64 branch with our without link instruction /// that can be encoded. /// /// +-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ /// | 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 0 1 0 1 | /// | op imm26.................................................................................... | /// +-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ /// pub struct Call { /// The PC-relative offset to jump to in terms of number of instructions. offset: InstructionOffset, /// The operation to perform for this instruction. op: Op } impl Call { /// B /// https://developer.arm.com/documentation/ddi0596/2021-12/Base-Instructions/B--Branch- pub fn b(offset: InstructionOffset) -> Self { Self { offset, op: Op::Branch } } /// BL /// https://developer.arm.com/documentation/ddi0596/2021-12/Base-Instructions/BL--Branch-with-Link-?lang=en pub fn bl(offset: InstructionOffset) -> Self { Self { offset, op: Op::BranchWithLink } } } /// https://developer.arm.com/documentation/ddi0602/2022-03/Index-by-Encoding/Branches--Exception-Generating-and-System-instructions?lang=en const FAMILY: u32 = 0b101; impl From for u32 { /// Convert an instruction into a 32-bit value. fn from(inst: Call) -> Self { 0 | ((inst.op as u32) << 31) | (FAMILY << 26) | truncate_imm::<_, 26>(inst.offset) } } impl From for [u8; 4] { /// Convert an instruction into a 4 byte array. fn from(inst: Call) -> [u8; 4] { let result: u32 = inst.into(); result.to_le_bytes() } } #[cfg(test)] mod tests { use super::*; #[test] fn test_bl() { let result: u32 = Call::bl(0.into()).into(); assert_eq!(0x94000000, result); } #[test] fn test_bl_positive() { let result: u32 = Call::bl(256.into()).into(); assert_eq!(0x94000100, result); } #[test] fn test_bl_negative() { let result: u32 = Call::bl((-256).into()).into(); assert_eq!(0x97ffff00, result); } #[test] fn test_b() { let result: u32 = Call::b(0.into()).into(); assert_eq!(0x14000000, result); } #[test] fn test_b_positive() { let result: u32 = Call::b(((1 << 25) - 1).into()).into(); assert_eq!(0x15ffffff, result); } #[test] fn test_b_negative() { let result: u32 = Call::b((-(1 << 25)).into()).into(); assert_eq!(0x16000000, result); } }