1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
|
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<Call> 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<Call> 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);
}
}
|