aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoriximeow <me@iximeow.net>2022-01-02 00:01:55 -0800
committeriximeow <me@iximeow.net>2022-01-02 00:01:55 -0800
commit39f183cb5ea8f5514a0010b9b9a43f2d449567cd (patch)
tree8e99e821be5b732cb580eeed70489796ad4aa651
parente25377ab89d6c767d9b7947831a967bbbb93dc92 (diff)
document the crate
-rw-r--r--CHANGELOG7
-rw-r--r--src/armv7.rs141
-rw-r--r--src/armv8/a64.rs356
-rw-r--r--src/armv8/mod.rs1
-rw-r--r--src/lib.rs50
-rw-r--r--test/armv8/a64.rs14
6 files changed, 478 insertions, 91 deletions
diff --git a/CHANGELOG b/CHANGELOG
index fb1addc..6054a16 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,10 @@
+## 0.2.0
+* added documentation to all public members.
+* fix tests incorrectly expecting `lsl #0` in memory operands where it would have no effect
+* remove some redundant `a64` operand kinds:
+ - `RegOffset` is now `RegPreIndex` with no writeback.
+ - `Offset` was a duplicate of `PCOffset`, and all uses have been so-renamed.
+
## 0.1.4
* add or fix many a64 instructions, extend support up through armv8.5.
- some disassembly and optional elements of instruction text does not match
diff --git a/src/armv7.rs b/src/armv7.rs
index 5f826ac..c2254a5 100644
--- a/src/armv7.rs
+++ b/src/armv7.rs
@@ -12,6 +12,10 @@ use yaxpeax_arch::{Arch, AddressDiff, Colorize, Decoder, LengthedInstruction, Re
mod thumb;
// opcode, s, w, cond
+/// a struct for the combined display of an opcode and possible suffixes.
+///
+/// this includes the opcode, its optional `.s` suffix, optional `.w` suffix, and condition code,
+/// if any.
pub struct ConditionedOpcode(pub Opcode, pub bool, pub bool, pub ConditionCode);
impl Display for ConditionedOpcode {
@@ -20,6 +24,9 @@ impl Display for ConditionedOpcode {
}
}
+/// a context impl to display `arm` instructions with no additional context (no symbol name
+/// information, offset names, etc). this impl results in `some_instruction.contextualize(...)`
+/// displaying an instruction the same way its `Display` impl would.
pub struct NoContext;
fn reg_name_colorize<Y: YaxColors>(reg: Reg, colors: &Y) -> impl fmt::Display {
@@ -850,6 +857,7 @@ impl Display for Opcode {
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[allow(non_camel_case_types)]
+#[allow(missing_docs)]
pub enum Opcode {
Invalid,
/*
@@ -1088,6 +1096,8 @@ static DATA_PROCESSING_OPCODES: [Opcode; 16] = [
Opcode::MVN
];
+/// a struct describiing a shifted register operand. this is primarily interesting in that it can
+/// be translated to a `RegShiftStyle` for further interpretation.
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
#[repr(transparent)]
pub struct RegShift {
@@ -1103,26 +1113,37 @@ impl RegShift {
}
}
+ /// don't use this. it's for armv7 testing only.
+ #[doc(hidden)]
pub fn from_raw(data: u16) -> Self {
RegShift { data }
}
}
+/// an enum describing one of two ways a shifted register operand may be shifted.
pub enum RegShiftStyle {
+ /// a register shifted by an immediate.
RegImm(RegImmShift),
+ /// a register shifted by a register.
RegReg(RegRegShift),
}
+/// a register shifted by a register.
#[repr(transparent)]
pub struct RegRegShift {
data: u16
}
+/// the way a shift operation is carried out.
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum ShiftStyle {
+ /// left-shift the value, filling in zeroes.
LSL = 0,
+ /// right-shift the value, filling in zeroes.
LSR = 1,
+ /// arithmetic shift right, filling with the top bit of the value (sign-extending).
ASR = 2,
+ /// rotate-right, filling with bits shifted out of the value.
ROR = 3,
}
@@ -1150,34 +1171,42 @@ impl ShiftStyle {
}
impl RegRegShift {
+ /// the general-purpose register, an amount to shift the shiftee.
pub fn shifter(&self) -> Reg {
Reg::from_u8((self.data >> 8) as u8 & 0b1111)
}
+ /// the way in which this register is shifted.
pub fn stype(&self) -> ShiftStyle {
ShiftStyle::from((self.data >> 5) as u8 & 0b11)
}
+ /// the general-purpose register to be shifted.
pub fn shiftee(&self) -> Reg {
Reg::from_u8(self.data as u8 & 0b1111)
}
}
+/// a register shifted by an immediate.
#[repr(transparent)]
pub struct RegImmShift {
data: u16
}
impl RegImmShift {
+ /// the immediate this register is shifted by.
pub fn imm(&self) -> u8 {
(self.data >> 7) as u8 & 0b11111
}
+ /// the way in which this register is shifted.
pub fn stype(&self) -> ShiftStyle {
ShiftStyle::from((self.data >> 5) as u8 & 0b11)
}
+ /// the general-purpose register to be shifted.
pub fn shiftee(&self) -> Reg {
Reg::from_u8(self.data as u8 & 0b1111)
}
}
+/// a struct describing an `arm` register.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[repr(transparent)]
pub struct Reg {
@@ -1252,6 +1281,9 @@ impl Reg {
}
}
+ /// create a new `Reg` with the specified number.
+ ///
+ /// panics if `bits` is out of range (16 or above).
pub fn from_u8(bits: u8) -> Reg {
if bits > 0b1111 {
panic!("register number out of range");
@@ -1260,11 +1292,13 @@ impl Reg {
Reg { bits }
}
+ /// get the number of this register. the returned value will be between 0 and 15.
pub fn number(&self) -> u8 {
self.bits
}
}
+/// a control register.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[repr(transparent)]
pub struct CReg {
@@ -1278,6 +1312,9 @@ impl Display for CReg {
}
impl CReg {
+ /// create a new `CReg` with the specified number.
+ ///
+ /// panics if `bits` is out of range (16 or above).
pub fn from_u8(bits: u8) -> CReg {
if bits > 0b1111 {
panic!("register number out of range");
@@ -1286,6 +1323,7 @@ impl CReg {
CReg { bits }
}
+ /// get the number of this register. the returned value will be between 0 and 15.
pub fn number(&self) -> u8 {
self.bits
}
@@ -1331,6 +1369,7 @@ impl Display for StatusRegMask {
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[allow(non_camel_case_types)]
+#[allow(missing_docs)]
pub enum StatusRegMask {
// Note 0b0000 is unused (as is 0b10000)
CPSR_C = 0b0001,
@@ -1409,36 +1448,82 @@ impl StatusRegMask {
}
}
+/// an operand in an `arm` instruction.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Operand {
+ /// a general-purpose register.
Reg(Reg),
+ /// a general-purpose register, with writeback. these generally imply an increment by width of
+ /// a memort operand, depending on the instruction.
RegWBack(Reg, bool),
+ /// a list of registers specified as a bitmask from bits 0 to 15.
RegList(u16),
+ /// a memory access, dereferencing a general-purpose register.
RegDeref(Reg),
+ /// a memory access, dereferencing a shifted general-purpose register with register or
+ /// immediate offset.
RegShift(RegShift),
+ /// a memory access of a register, post-indexed a register shifted by register or immediate.
+ /// the first bool indicates if the shifted-register is added or subtracted ot the base
+ /// register, while the second bool indicates if the resulting address is written back to the
+ /// base register.
RegDerefPostindexRegShift(Reg, RegShift, bool, bool), // add/sub, wback
+ /// a memory access of a register, pre-indexed with a register shifted by register or
+ /// immediate. the first bool indicates if the shifted-register is added or subtracted ot the
+ /// base register, while the second bool indicates if the resulting address is written back to
+ /// the base register.
RegDerefPreindexRegShift(Reg, RegShift, bool, bool), // add/sub, wback
+ /// a memory access of a register, post-indexed with an immediate. the first bool indicates if
+ /// the shifted-register is added or subtracted ot the base register, while the second bool
+ /// indicates if the resulting address is written back to the base register.
RegDerefPostindexOffset(Reg, u16, bool, bool), // add/sub, wback
+ /// a memory access of a register, pre-indexed with an immediate. the first bool indicates if
+ /// the shifted-register is added or subtracted ot the base register, while the second bool
+ /// indicates if the resulting address is written back to the base register.
RegDerefPreindexOffset(Reg, u16, bool, bool), // add/sub, wback
+ /// a memory access of a register, post-indexed with a register. the first bool indicates if the
+ /// shifted-register is added or subtracted ot the base register, while the second bool
+ /// indicates if the resulting address is written back to the base register.
RegDerefPostindexReg(Reg, Reg, bool, bool), // add/sub, wback
+ /// a memory access of a register, pre-indexed with a register. the first bool indicates if the
+ /// shifted-register is added or subtracted ot the base register, while the second bool
+ /// indicates if the resulting address is written back to the base register.
RegDerefPreindexReg(Reg, Reg, bool, bool), // add/sub, wback
+ /// a 12-bit immediate, stored in a `u16`.
Imm12(u16),
+ /// a 32-bit immediate, stored in a `u32`.
Imm32(u32),
+ /// a pc-relative branch, with 32-bit signed offset, left-shifted by 2.
BranchOffset(i32),
+ /// a pc-relative branch, with 32-bit signed offset, left-shifted by 1.
BranchThumbOffset(i32),
+ /// a coprocessor index.
Coprocessor(u8),
+ /// a coprocessor option number.
CoprocOption(u8),
+ /// an `arm` control register.
CReg(CReg),
+ /// an `arm` banked register, either `usr` (general-purpose) bank or one of the alternate sets
+ /// of `arm` registers.
BankedReg(Bank, Reg),
+ /// `spsr` in some `arm` register bank.
BankedSPSR(Bank),
+ /// a mask of bits for the `spsr` register.
StatusRegMask(StatusRegMask),
+ /// the `apsr` register.
APSR,
+ /// the `spsr` register.
SPSR,
+ /// the `cpsr` register.
CPSR,
+ /// "no operand". since an instruction's `operands` array is always four entries, this is used
+ /// to fill space, if any, after recording an instruction's extant operands.
Nothing,
}
+/// a register bank for a register in `armv7` or below.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+#[allow(missing_docs)]
pub enum Bank {
Usr,
Fiq,
@@ -1551,10 +1636,16 @@ impl <T: fmt::Write, Y: YaxColors> Colorize<T, Y> for Operand {
}
}
+/// a `armv7` or below instruction.
#[derive(Debug, PartialEq, Eq)]
pub struct Instruction {
+ /// the condition code for this instruction, defaults to `AL` if the instruction is
+ /// unconditional.
pub condition: ConditionCode,
+ /// the opcode of this instruction.
pub opcode: Opcode,
+ /// operands for the decoded instruction. operands are populated from index 0, to 1, 2, and 3.
+ /// operands from the instruction are non-`Operand::Nothing`.
pub operands: [Operand; 4],
/// does this instruction update flags, while variants that do not update flags exist?
pub s: bool,
@@ -1566,14 +1657,28 @@ pub struct Instruction {
pub thumb: bool,
}
+/// the kinds of errors possibly encountered in trying to decode an `armv7` or below instruction.
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum DecodeError {
+ /// the input was insufficient to decode a full instruction. for non-thumb instructions, this means
+ /// the input was not at least four bytes long. for thumb instructions, the input was either
+ /// not two bytes, or not four bytes, depending on how much the instruction would need.
ExhaustedInput,
+ /// the instruction encodes an opcode that is not valid.
InvalidOpcode,
+ /// the instruction encodes an operand that is not valid.
InvalidOperand,
+ /// `yaxpeax-arm` doesn't know how to decode this, but it may be a valid instruction. the
+ /// instruction decoder is not complete, sorry. :(
+ ///
+ /// in practice this typically indicates some kinds of coprocessor instruction, or `ARMv7` SIMD
+ /// instruction.
Incomplete,
+ /// the instruction includes reserved bits that were not set as required.
Nonconforming,
+ /// the input encodes an instruction that is explicitly undefined.
Undefined,
+ /// the input encodes an instruction with unpredictable behavior.
Unpredictable,
}
@@ -1641,18 +1746,23 @@ impl Instruction {
fn set_s(&mut self, value: bool) {
self.s = value;
}
+ /// does this instruction set status flags?
pub fn s(&self) -> bool { self.s }
pub(crate) fn set_w(&mut self, value: bool) {
self.thumb_w = value;
}
+ /// was this instruction encoded in `thumb` mode and still 4 bytes, *and* requires a `.w`
+ /// suffix on the opcode?
pub fn w(&self) -> bool { self.thumb_w }
pub(crate) fn set_wide(&mut self, value: bool) {
self.wide = value;
}
+ /// was this instruction encoded in `thumb` mode and still 4 bytes?
pub fn wide(&self) -> bool { self.wide }
pub(crate) fn set_thumb(&mut self, value: bool) {
self.thumb = value;
}
+ /// was this instruction encoded in `thumb` mode?
pub fn thumb(&self) -> bool { self.thumb }
}
@@ -1769,7 +1879,9 @@ impl LengthedInstruction for Instruction {
}
}
+/// a condition code for am `armv7` or below instruction.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+#[allow(missing_docs)]
pub enum ConditionCode {
EQ,
NE,
@@ -1811,7 +1923,7 @@ impl Display for ConditionCode {
}
impl ConditionCode {
- pub fn build(value: u8) -> ConditionCode {
+ fn build(value: u8) -> ConditionCode {
match value {
0b0000 => ConditionCode::EQ,
0b0001 => ConditionCode::NE,
@@ -1918,6 +2030,14 @@ impl Default for ARMVersion {
}
// nothing checks/rejects by arm version yet, but.. soon....
+/// a struct with decode configuration for `ARMv7` and below. the same decoder is used for `thumb`
+/// and non-`thumb` modes, and the same instruction struct is used for decoded instructions in
+/// either mode.
+///
+/// NOTE: helper functions here create `InstDecoder` for specific revisions, extensions, or lack
+/// thereof, in the supported instruction set. `yaxpeax-arm` does not actually honor these settings
+/// yet. this means any `InstDecoder` will decode all known instructions through the latest `ARMv7`
+/// extensions.
#[allow(unused)]
#[derive(Debug)]
pub struct InstDecoder {
@@ -1939,19 +2059,28 @@ impl Default for InstDecoder {
}
impl InstDecoder {
+ /// set the decoder to decoding in thumb mode as the specified bool provides; `true` means
+ /// "yes, decode in `thumb` mode", where `false` means to decode as a normal `arm` instruction.
pub fn set_thumb_mode(&mut self, thumb: bool) {
self.thumb = thumb;
}
+ /// set the decoder to decoding in thumb mode as the specified bool provides; `true` means
+ /// "yes, decode in `thumb` mode", where `false` means to decode as a normal `arm` instruction.
+ ///
+ /// (this consumes and returns the `InstDecoder` to support use in chained calls.)`
pub fn with_thumb_mode(mut self, thumb: bool) -> Self {
self.set_thumb_mode(thumb);
self
}
+ /// initialize a new `arm` `InstDecoder` with default ("everything") support, but in `thumb`
+ /// mode.
pub fn default_thumb() -> Self {
Self::default().with_thumb_mode(true)
}
+ /// create an `InstDecoder` that supports only instructions through to `ARMv4`.
pub fn armv4() -> Self {
Self {
mode: DecodeMode::Any,
@@ -1961,6 +2090,7 @@ impl InstDecoder {
}
}
+ /// create an `InstDecoder` that supports only instructions through to `ARMv5`.
pub fn armv5() -> Self {
Self {
mode: DecodeMode::Any,
@@ -1970,6 +2100,7 @@ impl InstDecoder {
}
}
+ /// create an `InstDecoder` that supports only instructions through to `ARMv6`.
pub fn armv6() -> Self {
Self {
mode: DecodeMode::Any,
@@ -1979,6 +2110,7 @@ impl InstDecoder {
}
}
+ /// create an `InstDecoder` that supports only instructions through to `ARMv6t2`.
pub fn armv6t2() -> Self {
Self {
mode: DecodeMode::Any,
@@ -1988,6 +2120,7 @@ impl InstDecoder {
}
}
+ /// create an `InstDecoder` that supports only instructions through to `ARMv6t2` in thumb mode.
pub fn armv6t2_thumb() -> Self {
Self {
mode: DecodeMode::Any,
@@ -1997,6 +2130,7 @@ impl InstDecoder {
}
}
+ /// create an `InstDecoder` that supports only instructions through to `ARMv7`.
pub fn armv7() -> Self {
Self {
mode: DecodeMode::Any,
@@ -2006,6 +2140,7 @@ impl InstDecoder {
}
}
+ /// create an `InstDecoder` that supports only instructions through to `ARMv7` in thumb mode.
pub fn armv7_thumb() -> Self {
Self {
mode: DecodeMode::Any,
@@ -2015,6 +2150,7 @@ impl InstDecoder {
}
}
+ /// create an `InstDecoder` that supports only instructions through to `ARMv7ve`.
pub fn armv7ve() -> Self {
Self {
mode: DecodeMode::Any,
@@ -2024,6 +2160,7 @@ impl InstDecoder {
}
}
+ /// create an `InstDecoder` that supports only instructions through to `ARMv7ve` in thumb mode.
pub fn armv7ve_thumb() -> Self {
Self {
mode: DecodeMode::Any,
@@ -2033,6 +2170,7 @@ impl InstDecoder {
}
}
+ /// create an `InstDecoder` that supports only instructions through to `ARMv7vese`.
pub fn armv7vese() -> Self {
Self {
mode: DecodeMode::Any,
@@ -3709,6 +3847,7 @@ impl Decoder<ARMv7> for InstDecoder {
}
}
+/// a struct with a summary of the `ARMv7` instruction set in an associated `impl Arch for ARMv7`.
#[cfg_attr(feature="use-serde", derive(Serialize, Deserialize))]
#[derive(Debug)]
pub struct ARMv7;
diff --git a/src/armv8/a64.rs b/src/armv8/a64.rs
index 0ca7bff..dc7d399 100644
--- a/src/armv8/a64.rs
+++ b/src/armv8/a64.rs
@@ -161,11 +161,21 @@ mod docs {
}
}
+/// the kinds of errors possibly encountered in trying to decode an `aarch64` instruction
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum DecodeError {
+ /// the input was insufficient to decode a full instruction. for `a64` instructions, this means
+ /// the input was not at least four bytes long.
ExhaustedInput,
+ /// the instruction encodes an opcode that is not valid.
InvalidOpcode,
+ /// the instruction encodes an operand that is not valid.
InvalidOperand,
+ /// `yaxpeax-arm` doesn't know how to decode this, but it may be a valid instruction. the
+ /// instruction decoder is not complete, sorry. :(
+ ///
+ /// in practice this means that the instruction is likely either in the `SVE` or `SVE2`
+ /// additions.
IncompleteDecoder,
}
@@ -210,6 +220,9 @@ impl yaxpeax_arch::Instruction for Instruction {
fn well_defined(&self) -> bool { true }
}
+/// a context impl to display `a64` instructions with no additional context (no symbol name
+/// information, offset names, etc). this impl results in `some_instruction.contextualize(...)`
+/// displaying an instruction the same way its `Display` impl would.
pub struct NoContext;
impl <T: fmt::Write, Y: YaxColors> ShowContextual<u64, NoContext, T, Y> for Instruction {
@@ -218,10 +231,14 @@ impl <T: fmt::Write, Y: YaxColors> ShowContextual<u64, NoContext, T, Y> for Inst
}
}
+/// a struct with a summary of the `ARMv8`/`aarch64` instruction set in an associated `impl Arch
+/// for ARMv8`.
#[cfg(feature="use-serde")]
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
pub struct ARMv8 { }
+/// a struct with a summary of the `ARMv8`/`aarch64` instruction set in an associated `impl Arch
+/// for ARMv8`.
#[cfg(not(feature="use-serde"))]
#[derive(Copy, Clone, Debug)]
pub struct ARMv8 { }
@@ -235,13 +252,31 @@ impl Arch for ARMv8 {
type Operand = Operand;
}
+/// a size marker for a register.
#[derive(Copy, Clone, Debug, PartialEq)]
#[repr(u8)]
-pub enum SizeCode { X, W }
+pub enum SizeCode {
+ /// an x-size (64-bits) register.
+ X,
+ /// a w-size (32-bits) register.
+ W
+}
+/// a size marker for a SIMD register, a full scalar size, or vector element size.
#[derive(Copy, Clone, Debug, PartialEq)]
#[repr(u8)]
-pub enum SIMDSizeCode { B, H, S, D, Q }
+pub enum SIMDSizeCode {
+ /// a byte (8-bits) scalar or element.
+ B,
+ /// a halfword (16-bits) scalar or element.
+ H,
+ /// a single(?) word (32-bits) scalar or element.
+ S,
+ /// a dword (64-bits) scalar or element.
+ D,
+ /// a qword (128-bits) scalar or element.
+ Q
+}
impl SIMDSizeCode {
fn width(&self) -> u16 {
@@ -265,10 +300,14 @@ impl SIMDSizeCode {
}
}
+/// an `aarch64` instruction.
#[derive(Copy, Clone, Debug, PartialEq)]
#[repr(C)]
pub struct Instruction {
+ /// the operation of the decoded instruction.
pub opcode: Opcode,
+ /// operands for the decoded instruction. operands are populated from index 0, to 1, 2, and 3.
+ /// operands from the instruction are non-`Operand::Nothing`.
pub operands: [Operand; 4],
}
@@ -1078,6 +1117,10 @@ impl Default for Instruction {
}
}
+/// a descriptor for the operation in a `sys` or `sysl` instruction.
+///
+/// there are two fields of interest, `op1` and `op2`. for a description of how to interpret these
+/// fields of the `sys{,l}` opcodes, consult the `ARM Reference Manual`.
#[derive(Copy, Clone, Debug, PartialEq)]
#[repr(transparent)]
pub struct SysOps {
@@ -1091,10 +1134,12 @@ impl SysOps {
}
}
+ /// retrieve the `op1` field encoded in this `SysOps`.
#[inline]
pub fn op1(&self) -> u8 {
self.data & 0b1111
}
+ /// retrieve the `op2` field encoded in this `SysOps`.
#[inline]
pub fn op2(&self) -> u8 {
(self.data >> 4) & 0b1111
@@ -1103,6 +1148,7 @@ impl SysOps {
#[derive(Copy, Clone, Debug, PartialEq)]
#[repr(u16)]
+#[allow(missing_docs)]
pub enum Opcode {
Invalid,
MOVN,
@@ -2609,19 +2655,32 @@ impl Display for Opcode {
}
}
+/// the way a shift operation is carried out.
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum ShiftStyle {
+ /// left-shift the value, filling in zeroes.
LSL,
+ /// right-shift the value, filling in zeroes.
LSR,
+ /// arithmetic shift right, filling with the top bit of the value (sign-extending).
ASR,
+ /// rotate-right, filling with bits shifted out of the value.
ROR,
+ /// unsigned extend byte to 64-bit word.
UXTB,
+ /// unsigned extend half-word (16-bit) to 64-bit word.
UXTH,
+ /// unsigned extend word (32-bit) to 64-bit word.
UXTW,
+ /// unsigned extend 64-bit word to 64-bit word (functionally equivalent to left-shift).
UXTX,
+ /// signed extend byte to 64-bit word.
SXTB,
+ /// signed extend half-word (16-bit) to 64-bit word.
SXTH,
+ /// signed extend word (32-bit) to 64-bit word.
SXTW,
+ /// signed extend 64-bit word to 64-bit word (functionally equivalent to left-shift).
SXTX,
}
@@ -2644,37 +2703,179 @@ impl Display for ShiftStyle {
}
}
+/// an operand for an `aarch64` instruction.
+///
+/// an instruction's `operands` array allows many more combination of `Operand` than are possible
+/// in practice; no `aarch64` instruction has multiple `Operand::PCOffset` entries, for example.
#[derive(Copy, Clone, Debug, PartialEq)]
#[repr(C)]
pub enum Operand {
+ /// "no operand". since an instruction's `operands` array is always four entries, this is used
+ /// to fill space, if any, after recording an instruction's extant operands.
Nothing,
+ /// a general-purpose register.
+ ///
+ /// for example, `x5` would be represented as `Operand::Register(SizeCode::X, 5)`. the register
+ /// number currently does not extend beyond 31. `Operand::Register(_, 31)` is the zero
+ /// register, either `wzr` or `xzr`.
Register(SizeCode, u16),
+ /// a general-purpose register.
+ ///
+ /// for example, `x5` would be represented as `Operand::Register(SizeCode::X, 5)`. the register
+ /// number ranges between 0 and 31. unlike `Operand::Register`, `Operand::Register(_, 31)` is
+ /// the stack pointer, either `wsp` or `xsp`.
RegisterPair(SizeCode, u16),
+ /// a SIMD register, used as a scalar element.
+ ///
+ /// for example, `d3` would be represented as `Operand::SIMDRegister(SIMDSizeCode::D, 3)`. the
+ /// register number ranges between 0 and 31.
SIMDRegister(SIMDSizeCode, u16),
+ /// a SIMD register, used as a vector of elements.
+ ///
+ /// for example, `v10.4h` would be represented as
+ /// `Operand::SIMDRegisterElements(SIMDSizeCode::D, 10, SIMDSizeCode::H)`.
+ ///
+ /// SIMD registers are either `SIMDSizeCode::Q` or `SIMDSizeCode::D` wide, and can have
+ /// elements of width `SIMDSizeCode::{B, H, S, D}`. the register number ranges between 0 and 31.
SIMDRegisterElements(SIMDSizeCode, u16, SIMDSizeCode),
+ /// one lane in a SIMD vector of elements.
+ ///
+ /// for example, `v10.h[3]` would be represented as
+ /// `Operand::SIMDRegisterElementsLane(SIMDSizeCode::D, 10, SIMDSizeCode::H, 3)`.
+ ///
+ /// SIMD registers are either `SIMDSizeCode::Q` or `SIMDSizeCode::D` wide, and can have
+ /// elements of width `SIMDSizeCode::{B, H, S, D}`. the register number ranges between 0 and 31.
+ /// the lane number can range between 0 and `element.width() / vector.width()`.
SIMDRegisterElementsLane(SIMDSizeCode, u16, SIMDSizeCode, u8),
+ /// multiple lanes in a SIMD vector of elements.
+ ///
+ /// for example, `v10.4h[3]` would be represented as
+ /// `Operand::SIMDRegisterElementsLane(SIMDSizeCode::Q, 10, SIMDSizeCode::H, 3, 4)`.
+ ///
+ /// SIMD registers are either `SIMDSizeCode::Q` or `SIMDSizeCode::D` wide, and can have
+ /// elements of width `SIMDSizeCode::{B, H, S, D}`. the register number ranges between 0 and 31.
+ /// the lane number can range between 0 and `element.width() / vector.width()`. the number of
+ /// elements is, in practice, always 4.
SIMDRegisterElementsMultipleLane(SIMDSizeCode, u16, SIMDSizeCode, u8, u8),
+ /// multiple full SIMD vectors.
+ ///
+ /// for example, `{v2.2d, v3.2d, v4.2d}` would be represented as
+ /// `Operand::SIMDRegisterGroup(SIMDSizeCode::Q, 2, SIMDSizeCode::D, 3)`.
+ ///
+ /// SIMD registers are either `SIMDSizeCode::Q` or `SIMDSizeCode::D` wide, and can have
+ /// elements of width `SIMDSizeCode::{B, H, S, D}`. the register number ranges between 0 and 31.
+ /// the number of SIMD registers ranges from 1 to 4.
SIMDRegisterGroup(SIMDSizeCode, u16, SIMDSizeCode, u8),
+ /// a lane in multiple SIMD vectors.
+ ///
+ /// for example, `{v2.2d, v3.2d, v4.2d}[1]` would be represented as
+ /// `Operand::SIMDRegisterGroup(SIMDSizeCode::Q, 2, SIMDSizeCode::D, 3, 1)`.
+ ///
+ /// SIMD registers are either `SIMDSizeCode::Q` or `SIMDSizeCode::D` wide, and can have
+ /// elements of width `SIMDSizeCode::{B, H, S, D}`. the register number ranges between 0 and 31.
+ /// the number of SIMD registers ranges from 1 to 4. the lane number can range between 0 and
+ /// `element.width() / vector.width()`.
SIMDRegisterGroupLane(u16, SIMDSizeCode, u8, u8),
+ /// a general-purpose register, where register 31 is `sp`, rather than `zr`.
+ ///
+ /// for example, `x5` would be represented as `Operand::Register(SizeCode::X, 5)`. the register
+ /// number currently does not extend beyond 31. `Operand::Register(_, 31)` is the stack pointer
+ /// register, either `wsp` or `xsp`.
RegisterOrSP(SizeCode, u16),
+ /// a condition code describing some comparison.
+ ///
+ /// there are 16 condition codes with number 0 to 15: `eq, hs, mi, vs, hi, ge, gt, al, ne, lo,
+ /// pl, vc, ls, lt, le, nv`. the `nv` condition code is identical to `al`, and only has a
+ /// different condition code number in encoded instructions.
ConditionCode(u8),
- Offset(i64),
+ /// a pc-relative offset.
+ ///
+ /// this is the addressing mode for instructions like, `bl $+0x1234`, or `adrp $-0x123000`.
PCOffset(i64),
+ /// a 32-bit immediate.
+ ///
+ /// this immediate may be signed or unsigned, depending on the instruction in question. if the
+ /// immediate is signed, it was sign-extended to 32-bits and that extended value is reported
+ /// here.
Immediate(u32),
- ImmediateDouble(f64),
+ /// a 64-bit immediate.
+ ///
+ /// this immediate may be signed or unsigned, depending on the instruction in question. if the
+ /// immediate is signed, it was sign-extended to 64-bits and that extended value is reported
+ /// here.
Imm64(u64),
+ /// a 16-bit immediate.
+ ///
+ /// this immediate may be signed or unsigned, depending on the instruction in question. if the
+ /// immediate is signed, it was sign-extended to 16-bits and that extended value is reported
+ /// here.
Imm16(u16),
+ /// a 64-bit floating-point immediate.
+ ///
+ /// the immediate as recorded in the instruction may be `f16`, `f32`, or `f64`, but is
+ /// converted to an `f64` for this operand.
+ ImmediateDouble(f64),
+ /// an immediate, a base number left-shifted by some amount.
+ ///
+ /// this is displayed as, for example, `#0x1234, lsl #12`. the base number may be any 16-bit
+ /// value, and can be shifted by up to 64 bits.
ImmShift(u16, u8),
+ /// an immediate, a base number left-shifted by some amount, filling with 1's.
+ ///
+ /// this is displayed as, for example, `#0x1234, msl #12`. the base number may be any 16-bit
+ /// value, and can be shifted by up to 64 bits.
ImmShiftMSL(u16, u8),
+ /// a register, shifted by some constant number of bits.
+ ///
+ /// this is displayed as, for example, `x15, lsl #3`. the operand would be represented by
+ /// `Operand::RegShift(ShiftStyle::LSL, 3, Sizecode::X, 15)`.
RegShift(ShiftStyle, u8, SizeCode, u16),
- RegOffset(u16, i16),
+ /// a memory access, with base register offset by another register shifted by some amount.
+ ///
+ /// this is displayed as, for example, `[x6, w9, lsl #5]`, and would be represented by
+ /// `Operand::RegRegOffset(6, 9, SizeCode::W, ShiftStyle::LSL, 5)`.
RegRegOffset(u16, u16, SizeCode, ShiftStyle, u8),
+ /// a memory access, with a register base address and constant offset, and optional writeback.
+ ///
+ /// this is displayed as, for example:
+ /// * `[x13]` (no writeback, offset of 0)
+ /// - Offset::RegPreIndex(13, 0, false)`
+ /// * `[x13, #0x80]` (no writeback, offset of 0x80)
+ /// - Offset::RegPreIndex(13, 0x80, false)`
+ /// * `[x13, #0x80]!` (writeback, offset of 0x80)
+ /// - Offset::RegPreIndex(13, 0x80, true)`
RegPreIndex(u16, i32, bool),
+ /// a memory access, with register base address and constant offset, base+offset written back
+ /// to the base register after access.
+ ///
+ /// this is displayed as, for example, `[x5], #-0x40`. the operand would be represented by
+ /// `Operand::RegPostIndex(5, -0x40)`.
RegPostIndex(u16, i32),
+ /// a memory access, with register base address and register offset, base+offset written back
+ /// to the base register after access.
+ ///
+ /// this is displayed as, for example, `[x5], x9`. the operand would be represented by
+ /// `Operand::RegPostIndex(5, 9)`.
RegPostIndexReg(u16, u16),
+ /// some kind of operation for a `prfm` or `prfum` operation.
+ ///
+ /// if the operation has a specified name, it will be `{pld,pli,pst}{l1,l2,l3}{keep,strm}`.
+ /// otherwise, the prefetch operation will be shown as the underlying integer from the encoded
+ /// instruction.
PrefetchOp(u16),
+ /// a system register for an `msr` or `mrs` operation.
+ ///
+ /// if the register is a standard system register, and `yaxpeax-arm` knows about it, its name
+ /// will be used instead. otherwise, this will display as, for example, `sysreg:a6f4`.
SystemReg(u16),
+ /// a control register for an `sys` or `sysl` operation.
+ ///
+ /// this operand will display as, for example, `cr5`.
ControlReg(u16),
+ /// a selector for a field of the `pstate` control register.
+ ///
+ /// `yaxpeax-arm` does not name specific fields of `pstate` yet, so this operand displays as
+ /// `pstate.0x50`.
PstateField(u8),
}
@@ -2738,7 +2939,7 @@ impl Display for Operand {
Operand::PstateField(reg) => {
// `MSR (immediate)` writes to the `PSTATE` register, setting a few bit patterns as
// selected by `reg`.
- write!(fmt, "pstate.{:x}", reg)
+ write!(fmt, "pstate.{:#x}", reg)
}
Operand::SIMDRegister(size, reg) => {
match size {
@@ -2834,13 +3035,6 @@ impl Display for Operand {
_ => { unreachable!(); }
}
}
- Operand::Offset(offs) => {
- if *offs < 0 {
- write!(fmt, "$-{:#x}", offs.wrapping_neg())
- } else {
- write!(fmt, "$+{:#x}", offs)
- }
- }
Operand::PCOffset(offs) => {
if *offs < 0 {
write!(fmt, "$-{:#x}", offs.wrapping_neg())
@@ -2906,17 +3100,6 @@ impl Display for Operand {
}
}
}
- Operand::RegOffset(reg, offset) => {
- if *offset != 0 {
- if *offset < 0 {
- write!(fmt, "[{}, #-{:#x}]", Operand::RegisterOrSP(SizeCode::X, *reg), -*offset)
- } else {
- write!(fmt, "[{}, #{:#x}]", Operand::RegisterOrSP(SizeCode::X, *reg), offset)
- }
- } else {
- write!(fmt, "[{}]", Operand::RegisterOrSP(SizeCode::X, *reg))
- }
- }
Operand::RegRegOffset(reg, index_reg, index_size, extend, amount) => {
if extend == &ShiftStyle::LSL && *amount == 0 {
write!(fmt, "[{}, {}]", Operand::RegisterOrSP(SizeCode::X, *reg), Operand::Register(*index_size, *index_reg))
@@ -2958,7 +3141,14 @@ impl Display for Operand {
}
}
-#[derive(Default, Debug)]
+/// an `aarch64` instruction decoder.
+///
+/// there are no options or levels of decoding support, yet. as a result, any
+/// `armv8::a64::InstDecoder` will decode as much of the a64 instruction set as is implemented.
+///
+/// `InstDecoder` is currently zero-size, but users should not rely on this being the case in the
+/// future.
+#[derive(Default, Debug, PartialEq, Eq, Copy, Clone, Hash, PartialOrd, Ord)]
pub struct InstDecoder {}
#[allow(non_snake_case)]
@@ -7684,7 +7874,7 @@ impl Decoder<ARMv8> for InstDecoder {
let extended_offset = (offset << 11) >> 11;
inst.operands = [
Operand::Register(SizeCode::X, (word & 0x1f) as u16),
- Operand::Offset((extended_offset as i64) << 12),
+ Operand::PCOffset((extended_offset as i64) << 12),
Operand::Nothing,
Operand::Nothing
];
@@ -7694,7 +7884,7 @@ impl Decoder<ARMv8> for InstDecoder {
let extended_offset = (offset << 11) >> 11;
inst.operands = [
Operand::Register(SizeCode::X, (word & 0x1f) as u16),
- Operand::Offset(extended_offset as i64),
+ Operand::PCOffset(extended_offset as i64),
Operand::Nothing,
Operand::Nothing
];
@@ -8022,7 +8212,7 @@ impl Decoder<ARMv8> for InstDecoder {
inst.operands = [
Operand::RegisterPair(size_code, Rs),
Operand::RegisterPair(size_code, Rt),
- Operand::RegOffset(Rn, 0),
+ Operand::RegPreIndex(Rn, 0, false),
Operand::Nothing,
];
return Ok(());
@@ -8035,14 +8225,14 @@ impl Decoder<ARMv8> for InstDecoder {
inst.operands = [
Operand::Register(SizeCode::W, Rs),
Operand::Register(SizeCode::W, Rt),
- Operand::RegOffset(Rn, 0),
+ Operand::RegPreIndex(Rn, 0, false),
Operand::Nothing,
];
} else if Lo1 == 0b10 {
// load ops
inst.operands = [
Operand::Register(SizeCode::W, Rt),
- Operand::RegOffset(Rn, 0),
+ Operand::RegPreIndex(Rn, 0, false),
Operand::Nothing,
Operand::Nothing,
];
@@ -8088,7 +8278,7 @@ impl Decoder<ARMv8> for InstDecoder {
inst.operands = [
Operand::Register(SizeCode::W, Rs),
Operand::Register(size_code, Rt),
- Operand::RegOffset(Rn, 0),
+ Operand::RegPreIndex(Rn, 0, false),
Operand::Nothing,
];
match o0 {
@@ -8102,7 +8292,7 @@ impl Decoder<ARMv8> for InstDecoder {
Operand::Register(SizeCode::W, Rs),
Operand::Register(size_code, Rt),
Operand::Register(size_code, Rt2),
- Operand::RegOffset(Rn, 0),
+ Operand::RegPreIndex(Rn, 0, false),
];
match o0 {
0b0 => Opcode::STXP,
@@ -8113,7 +8303,7 @@ impl Decoder<ARMv8> for InstDecoder {
0b10 => {
inst.operands = [
Operand::Register(size_code, Rt),
- Operand::RegOffset(Rn, 0),
+ Operand::RegPreIndex(Rn, 0, false),
Operand::Nothing,
Operand::Nothing,
];
@@ -8127,7 +8317,7 @@ impl Decoder<ARMv8> for InstDecoder {
inst.operands = [
Operand::Register(size_code, Rt),
Operand::Register(size_code, Rt2),
- Operand::RegOffset(Rn, 0),
+ Operand::RegPreIndex(Rn, 0, false),
Operand::Nothing,
];
match o0 {
@@ -8176,7 +8366,7 @@ impl Decoder<ARMv8> for InstDecoder {
inst.operands = [
Operand::Register(size_code, Rs),
Operand::Register(size_code, Rt),
- Operand::RegOffset(Rn, 0),
+ Operand::RegPreIndex(Rn, 0, false),
Operand::Nothing,
];
return Ok(());
@@ -8219,7 +8409,7 @@ impl Decoder<ARMv8> for InstDecoder {
inst.operands = [
Operand::Register(size_code, Rt),
- Operand::RegOffset(Rn, 0),
+ Operand::RegPreIndex(Rn, 0, false),
Operand::Nothing,
Operand::Nothing,
];
@@ -8283,7 +8473,7 @@ impl Decoder<ARMv8> for InstDecoder {
let Rn = ((word >> 5) & 0b11111) as u16;
let Rt = ((word >> 0) & 0b11111) as u16;
- let simm = ((imm9 as i16) << 7) >> 7;
+ let simm = (((imm9 as i16) << 7) >> 7) as i32;
let index = (size << 2) | opc;
if index == 0b1011 || index >= 0b1110 {
@@ -8306,7 +8496,7 @@ impl Decoder<ARMv8> for InstDecoder {
inst.opcode = *opcode;
inst.operands = [
Operand::Register(size, Rt),
- Operand::RegOffset(Rn, simm),
+ Operand::RegPreIndex(Rn, simm, false),
Operand::Nothing,
Operand::Nothing,
];
@@ -8321,7 +8511,7 @@ impl Decoder<ARMv8> for InstDecoder {
let Rn = ((word >> 5) & 0b11111) as u16;
let Rt = ((word >> 0) & 0b11111) as u16;
- let simm = ((imm9 as i16) << 7) >> 7;
+ let simm = (((imm9 as i16) << 7) >> 7) as i32;
let opcode = &[
Opcode::STZGM, Opcode::STG, Opcode::STG, Opcode::STG,
@@ -8337,7 +8527,7 @@ impl Decoder<ARMv8> for InstDecoder {
// ldg
inst.operands = [
Operand::Register(SizeCode::X, Rt),
- Operand::RegOffset(Rn, simm),
+ Operand::RegPreIndex(Rn, simm, false),
Operand::Nothing,
Operand::Nothing,
];
@@ -8354,13 +8544,13 @@ impl Decoder<ARMv8> for InstDecoder {
Operand::RegisterOrSP(SizeCode::X, Rt)
},
if op2 == 0b00 {
- Operand::RegOffset(Rn, 0)
+ Operand::RegPreIndex(Rn, 0, false)
} else if op2 == 0b01 {
- Operand::RegPostIndex(Rn, simm as i32)
+ Operand::RegPostIndex(Rn, simm)
} else if op2 == 0b10 {
- Operand::RegOffset(Rn, simm)
+ Operand::RegPreIndex(Rn, simm, false)
} else {
- Operand::RegPreIndex(Rn, simm as i32, true)
+ Operand::RegPreIndex(Rn, simm, true)
},
Operand::Nothing,
Operand::Nothing,
@@ -8585,7 +8775,7 @@ impl Decoder<ARMv8> for InstDecoder {
let Rt = (word & 0x1f) as u16;
let Rn = ((word >> 5) & 0x1f) as u16;
let Rt2 = ((word >> 10) & 0x1f) as u16;
- let mut imm7 = ((((word >> 15) & 0x7f) as i16) << 9) >> 9;
+ let mut imm7 = (((((word >> 15) & 0x7f) as i16) << 9) >> 9) as i32;
let opc_L = ((word >> 22) & 1) | ((word >> 29) & 0x6);
let size = match opc_L {
0b000 => {
@@ -8627,7 +8817,7 @@ impl Decoder<ARMv8> for InstDecoder {
inst.operands = [
Operand::Register(size, Rt),
Operand::Register(size, Rt2),
- Operand::RegOffset(Rn, imm7),
+ Operand::RegPreIndex(Rn, imm7, false),
Operand::Nothing
];
},
@@ -8863,7 +9053,7 @@ impl Decoder<ARMv8> for InstDecoder {
inst.operands = [
Operand::Register(size, Rs as u16),
Operand::Register(size, Rt as u16),
- Operand::RegOffset(Rn, 0),
+ Operand::RegPreIndex(Rn, 0, false),
Operand::Nothing,
];
} else {
@@ -8881,7 +9071,7 @@ impl Decoder<ARMv8> for InstDecoder {
inst.operands = [
Operand::Register(size, Rs as u16),
Operand::Register(size, Rt as u16),
- Operand::RegOffset(Rn, 0),
+ Operand::RegPreIndex(Rn, 0, false),
Operand::Nothing,
];
} else if opcode == 0b100 {
@@ -8900,7 +9090,7 @@ impl Decoder<ARMv8> for InstDecoder {
// TOOD: should_is_must, Rs = 11111
inst.operands = [
Operand::Register(size, Rt as u16),
- Operand::RegOffset(Rn, 0),
+ Operand::RegPreIndex(Rn, 0, false),
Operand::Nothing,
Operand::Nothing,
];
@@ -8915,7 +9105,7 @@ impl Decoder<ARMv8> for InstDecoder {
// V==0
let w = (word >> 11) & 1;
let imm9 = ((word >> 12) & 0b1_1111_1111) as i16;
- let imm9 = (imm9 << 7) >> 7;
+ let imm9 = ((imm9 << 7) >> 7) as i32;
let imm9 = imm9 << 3; // `simm` is stored as a multiple of 8
let m = (word >> 23) & 1;
let size = (word >> 30) & 0b11;
@@ -8932,9 +9122,9 @@ impl Decoder<ARMv8> for InstDecoder {
inst.operands = [
Operand::Register(SizeCode::X, Rt),
if w == 0 {
- Operand::RegOffset(Rn, imm9)
+ Operand::RegPreIndex(Rn, imm9, false)
} else {
- Operand::RegPreIndex(Rn, imm9 as i32, true)
+ Operand::RegPreIndex(Rn, imm9, true)
},
Operand::Nothing,
Operand::Nothing,
@@ -9008,7 +9198,7 @@ impl Decoder<ARMv8> for InstDecoder {
}
}
} else {
- let imm9 = ((((word >> 12) & 0x1ff) as i16) << 7) >> 7;
+ let imm9 = (((((word >> 12) & 0x1ff) as i16) << 7) >> 7) as i32;
match category {
0b00 => {
// Load/store register (unscaled immediate)
@@ -9040,7 +9230,7 @@ impl Decoder<ARMv8> for InstDecoder {
} else {
Operand::Register(size, Rt)
},
- Operand::RegOffset(Rn, imm9),
+ Operand::RegPreIndex(Rn, imm9, false),
Operand::Nothing,
Operand::Nothing,
];
@@ -9320,14 +9510,14 @@ impl Decoder<ARMv8> for InstDecoder {
// V == 0
let Rt = (word & 0x1f) as u16;
let Rn = ((word >> 5) & 0x1f) as u16;
- let imm12 = ((word >> 10) & 0x0fff) as i16;
+ let imm12 = ((word >> 10) & 0x0fff) as i16 as i32;
let size_opc = ((word >> 22) & 0x3) | ((word >> 28) & 0xc);
match size_opc {
0b0000 => {
inst.opcode = Opcode::STRB;
inst.operands = [
Operand::Register(SizeCode::W, Rt),
- Operand::RegOffset(Rn, imm12),
+ Operand::RegPreIndex(Rn, imm12, false),
Operand::Nothing,
Operand::Nothing,
];
@@ -9336,7 +9526,7 @@ impl Decoder<ARMv8> for InstDecoder {
inst.opcode = Opcode::LDRB;
inst.operands = [
Operand::Register(SizeCode::W, Rt),
- Operand::RegOffset(Rn, imm12),
+ Operand::RegPreIndex(Rn, imm12, false),
Operand::Nothing,
Operand::Nothing,
];
@@ -9345,7 +9535,7 @@ impl Decoder<ARMv8> for InstDecoder {
inst.opcode = Opcode::LDRSB;
inst.operands = [
Operand::Register(SizeCode::X, Rt),
- Operand::RegOffset(Rn, imm12),
+ Operand::RegPreIndex(Rn, imm12, false),
Operand::Nothing,
Operand::Nothing,
];
@@ -9354,7 +9544,7 @@ impl Decoder<ARMv8> for InstDecoder {
inst.opcode = Opcode::LDRSB;
inst.operands = [
Operand::Register(SizeCode::W, Rt),
- Operand::RegOffset(Rn, imm12),
+ Operand::RegPreIndex(Rn, imm12, false),
Operand::Nothing,
Operand::Nothing,
];
@@ -9363,7 +9553,7 @@ impl Decoder<ARMv8> for InstDecoder {
inst.opcode = Opcode::STRH;
inst.operands = [
Operand::Register(SizeCode::W, Rt),
- Operand::RegOffset(Rn, imm12 << 1),
+ Operand::RegPreIndex(Rn, imm12 << 1, false),
Operand::Nothing,
Operand::Nothing,
];
@@ -9372,7 +9562,7 @@ impl Decoder<ARMv8> for InstDecoder {
inst.opcode = Opcode::LDRH;
inst.operands = [
Operand::Register(SizeCode::W, Rt),
- Operand::RegOffset(Rn, imm12 << 1),
+ Operand::RegPreIndex(Rn, imm12 << 1, false),
Operand::Nothing,
Operand::Nothing,
];
@@ -9381,7 +9571,7 @@ impl Decoder<ARMv8> for InstDecoder {
inst.opcode = Opcode::LDRSH;
inst.operands = [
Operand::Register(SizeCode::X, Rt),
- Operand::RegOffset(Rn, imm12 << 1),
+ Operand::RegPreIndex(Rn, imm12 << 1, false),
Operand::Nothing,
Operand::Nothing,
];
@@ -9390,7 +9580,7 @@ impl Decoder<ARMv8> for InstDecoder {
inst.opcode = Opcode::LDRSH;
inst.operands = [
Operand::Register(SizeCode::W, Rt),
- Operand::RegOffset(Rn, imm12 << 1),
+ Operand::RegPreIndex(Rn, imm12 << 1, false),
Operand::Nothing,
Operand::Nothing,
];
@@ -9399,7 +9589,7 @@ impl Decoder<ARMv8> for InstDecoder {
inst.opcode = Opcode::STR;
inst.operands = [
Operand::Register(SizeCode::W, Rt),
- Operand::RegOffset(Rn, imm12 << 2),
+ Operand::RegPreIndex(Rn, imm12 << 2, false),
Operand::Nothing,
Operand::Nothing,
];
@@ -9408,7 +9598,7 @@ impl Decoder<ARMv8> for InstDecoder {
inst.opcode = Opcode::LDR;
inst.operands = [
Operand::Register(SizeCode::W, Rt),
- Operand::RegOffset(Rn, imm12 << 2),
+ Operand::RegPreIndex(Rn, imm12 << 2, false),
Operand::Nothing,
Operand::Nothing,
];
@@ -9417,7 +9607,7 @@ impl Decoder<ARMv8> for InstDecoder {
inst.opcode = Opcode::LDRSW;
inst.operands = [
Operand::Register(SizeCode::X, Rt),
- Operand::RegOffset(Rn, imm12 << 2),
+ Operand::RegPreIndex(Rn, imm12 << 2, false),
Operand::Nothing,
Operand::Nothing,
];
@@ -9427,7 +9617,7 @@ impl Decoder<ARMv8> for InstDecoder {
inst.opcode = Opcode::STR;
inst.operands = [
Operand::Register(SizeCode::X, Rt),
- Operand::RegOffset(Rn, imm12 << 3),
+ Operand::RegPreIndex(Rn, imm12 << 3, false),
Operand::Nothing,
Operand::Nothing,
];
@@ -9436,7 +9626,7 @@ impl Decoder<ARMv8> for InstDecoder {
inst.opcode = Opcode::LDR;
inst.operands = [
Operand::Register(SizeCode::X, Rt),
- Operand::RegOffset(Rn, imm12 << 3),
+ Operand::RegPreIndex(Rn, imm12 << 3, false),
Operand::Nothing,
Operand::Nothing,
];
@@ -9445,7 +9635,7 @@ impl Decoder<ARMv8> for InstDecoder {
inst.opcode = Opcode::PRFM;
inst.operands = [
Operand::PrefetchOp(Rt),
- Operand::RegOffset(Rn, imm12 << 3),
+ Operand::RegPreIndex(Rn, imm12 << 3, false),
Operand::Nothing,
Operand::Nothing,
];
@@ -9571,7 +9761,7 @@ impl Decoder<ARMv8> for InstDecoder {
];
inst.operands = [
Operand::SIMDRegisterGroup(datasize, Rt as u16, SIZES[size as usize], num_regs),
- Operand::RegOffset(Rn as u16, 0),
+ Operand::RegPreIndex(Rn as u16, 0, false),
Operand::Nothing,
Operand::Nothing,
];
@@ -9702,7 +9892,7 @@ impl Decoder<ARMv8> for InstDecoder {
];
inst.operands = [
Operand::SIMDRegisterGroup(datasize, Rt as u16, SIZES[size as usize], opc_idx as u8 + 1),
- Operand::RegOffset(Rn as u16, 0),
+ Operand::RegPreIndex(Rn as u16, 0, false),
Operand::Nothing,
Operand::Nothing,
];
@@ -9766,7 +9956,7 @@ impl Decoder<ARMv8> for InstDecoder {
};
inst.operands = [
Operand::SIMDRegisterGroupLane(Rt as u16, item_size, group_size, index as u8),
- Operand::RegOffset(Rn as u16, 0),
+ Operand::RegPreIndex(Rn as u16, 0, false),
Operand::Nothing,
Operand::Nothing,
];
@@ -9922,7 +10112,7 @@ impl Decoder<ARMv8> for InstDecoder {
let extended_offset = (offset << 4) >> 4;
inst.opcode = Opcode::B;
inst.operands = [
- Operand::Offset(extended_offset as i64),
+ Operand::PCOffset(extended_offset as i64),
Operand::Nothing,
Operand::Nothing,
Operand::Nothing
@@ -9935,7 +10125,7 @@ impl Decoder<ARMv8> for InstDecoder {
let Rt = word & 0x1f;
inst.operands = [
Operand::Register(SizeCode::W, Rt as u16),
- Operand::Offset(extended_offset as i64),
+ Operand::PCOffset(extended_offset as i64),
Operand::Nothing,
Operand::Nothing
];
@@ -9947,7 +10137,7 @@ impl Decoder<ARMv8> for InstDecoder {
let Rt = word & 0x1f;
inst.operands = [
Operand::Register(SizeCode::W, Rt as u16),
- Operand::Offset(extended_offset as i64),
+ Operand::PCOffset(extended_offset as i64),
Operand::Nothing,
Operand::Nothing
];
@@ -9961,7 +10151,7 @@ impl Decoder<ARMv8> for InstDecoder {
inst.operands = [
Operand::Register(SizeCode::W, Rt as u16),
Operand::Imm16(b as u16),
- Operand::Offset(extended_offset as i64),
+ Operand::PCOffset(extended_offset as i64),
Operand::Nothing
];
},
@@ -9974,7 +10164,7 @@ impl Decoder<ARMv8> for InstDecoder {
inst.operands = [
Operand::Register(SizeCode::W, Rt as u16),
Operand::Imm16(b as u16),
- Operand::Offset(extended_offset as i64),
+ Operand::PCOffset(extended_offset as i64),
Operand::Nothing
];
},
@@ -9984,7 +10174,7 @@ impl Decoder<ARMv8> for InstDecoder {
let cond = word & 0x0f;
inst.opcode = Opcode::Bcc(cond as u8);
inst.operands = [
- Operand::Offset(extended_offset as i64),
+ Operand::PCOffset(extended_offset as i64),
Operand::Nothing,
Operand::Nothing,
Operand::Nothing
@@ -10003,7 +10193,7 @@ impl Decoder<ARMv8> for InstDecoder {
let extended_offset = (offset << 4) >> 4;
inst.opcode = Opcode::BL;
inst.operands = [
- Operand::Offset(extended_offset as i64),
+ Operand::PCOffset(extended_offset as i64),
Operand::Nothing,
Operand::Nothing,
Operand::Nothing
@@ -10016,7 +10206,7 @@ impl Decoder<ARMv8> for InstDecoder {
let Rt = word & 0x1f;
inst.operands = [
Operand::Register(SizeCode::X, Rt as u16),
- Operand::Offset(extended_offset as i64),
+ Operand::PCOffset(extended_offset as i64),
Operand::Nothing,
Operand::Nothing
];
@@ -10028,7 +10218,7 @@ impl Decoder<ARMv8> for InstDecoder {
let Rt = word & 0x1f;
inst.operands = [
Operand::Register(SizeCode::X, Rt as u16),
- Operand::Offset(extended_offset as i64),
+ Operand::PCOffset(extended_offset as i64),
Operand::Nothing,
Operand::Nothing
];
@@ -10042,7 +10232,7 @@ impl Decoder<ARMv8> for InstDecoder {
inst.operands = [
Operand::Register(SizeCode::X, Rt as u16),
Operand::Imm16((b as u16) | 0x20),
- Operand::Offset(extended_offset as i64),
+ Operand::PCOffset(extended_offset as i64),
Operand::Nothing
];
},
@@ -10055,7 +10245,7 @@ impl Decoder<ARMv8> for InstDecoder {
inst.operands = [
Operand::Register(SizeCode::X, Rt as u16),
Operand::Imm16((b as u16) | 0x20),
- Operand::Offset(extended_offset as i64),
+ Operand::PCOffset(extended_offset as i64),
Operand::Nothing
];
},
diff --git a/src/armv8/mod.rs b/src/armv8/mod.rs
index e3fc54a..3d46f33 100644
--- a/src/armv8/mod.rs
+++ b/src/armv8/mod.rs
@@ -1 +1,2 @@
+/// `yaxpeax-arm`'s `ARMv8/aarch64` decoder and `Arch` implementation.
pub mod a64;
diff --git a/src/lib.rs b/src/lib.rs
index ad2eb01..58a7667 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,4 +1,52 @@
+//! # `yaxpeax-arm`, a decoder for `arm` instruction sets.
+//!
+//! `yaxpeax-arm` provides `armv7` (and below) decoders, including `thumb` support, as well a
+//! decoder for `armv8`/`a64`.
+//!
+//! ## usage
+//!
+//! `yaxpeax-arm` is currently only usable through `yaxpeax-arch` traits:
+//! ```
+//! mod decoder {
+//! use yaxpeax_arch::{Arch, AddressDisplay, Decoder, Reader, ReaderBuilder};
+//!
+//! pub fn decode_stream<
+//! 'data,
+//! A: yaxpeax_arch::Arch,
+//! U: ReaderBuilder<A::Address, A::Word>,
+//! >(data: U) where
+//! A::Instruction: std::fmt::Display,
+//! {
+//! let mut reader = ReaderBuilder::read_from(data);
+//! let mut address: A::Address = reader.total_offset();
+//!
+//! let decoder = A::Decoder::default();
+//! let mut decode_res = decoder.decode(&mut reader);
+//! loop {
+//! match decode_res {
+//! Ok(ref inst) => {
+//! println!("{}: {}", address.show(), inst);
+//! decode_res = decoder.decode(&mut reader);
+//! address = reader.total_offset();
+//! }
+//! Err(e) => {
+//! println!("{}: decode error: {}", address.show(), e);
+//! break;
+//! }
+//! }
+//! }
+//! }
+//! }
+//!
+//! use yaxpeax_arm::armv8::a64::Arch;
+//! use yaxpeax_arch::{ReaderBuilder, U8Reader};
+//! let data: &[u8] = &[0x94, 0x02, 0x1e, 0x32];
+//! // would display `orr w20, w20, #0x4`.
+//! decoder::decode_stream::<x86_64, _>(data);
+//! ```
+
#![no_std]
+#![deny(missing_docs)]
#[cfg(feature="use-serde")]
#[macro_use] extern crate serde_derive;
@@ -7,5 +55,7 @@ extern crate serde;
extern crate yaxpeax_arch;
extern crate bitvec;
+/// `yaxpeax-arm`'s `ARMv7` decoder and `Arch` implementation.
pub mod armv7;
+/// `yaxpeax-arm`'s `ARMv8` decoder and `Arch` implementation.
pub mod armv8;
diff --git a/test/armv8/a64.rs b/test/armv8/a64.rs
index 19256d1..cdb1265 100644
--- a/test/armv8/a64.rs
+++ b/test/armv8/a64.rs
@@ -77,7 +77,7 @@ fn test_decode_str_ldr() {
opcode: Opcode::LDR,
operands: [
Operand::Register(SizeCode::X, 17),
- Operand::RegOffset(25, 0),
+ Operand::RegPreIndex(25, 0, false),
Operand::Nothing,
Operand::Nothing,
]
@@ -89,7 +89,7 @@ fn test_decode_str_ldr() {
opcode: Opcode::STR,
operands: [
Operand::Register(SizeCode::X, 21),
- Operand::RegOffset(31, 0xb0),
+ Operand::RegPreIndex(31, 0xb0, false),
Operand::Nothing,
Operand::Nothing,
]
@@ -101,7 +101,7 @@ fn test_decode_str_ldr() {
opcode: Opcode::STRB,
operands: [
Operand::Register(SizeCode::W, 0),
- Operand::RegOffset(19, 0x280),
+ Operand::RegPreIndex(19, 0x280, false),
Operand::Nothing,
Operand::Nothing,
]
@@ -129,7 +129,7 @@ fn test_decode_misc() {
opcode: Opcode::ADRP,
operands: [
Operand::Register(SizeCode::X, 2),
- Operand::Offset(0x8725000),
+ Operand::PCOffset(0x8725000),
Operand::Nothing,
Operand::Nothing
]
@@ -214,7 +214,7 @@ fn test_decode_branch() {
opcode: Opcode::CBZ,
operands: [
Operand::Register(SizeCode::X, 1),
- Operand::Offset(8),
+ Operand::PCOffset(8),
Operand::Nothing,
Operand::Nothing
]
@@ -226,7 +226,7 @@ fn test_decode_branch() {
opcode: Opcode::CBZ,
operands: [
Operand::Register(SizeCode::X, 2),
- Operand::Offset(12),
+ Operand::PCOffset(12),
Operand::Nothing,
Operand::Nothing
]
@@ -4866,7 +4866,7 @@ fn test_misc() {
([0x00, 0x30, 0xc0, 0x9a], "pacga x0, x0, x0"),
([0x00, 0x00, 0xae, 0x9e], "fmov x0, v0.d[1]"),
([0x00, 0x00, 0xe6, 0x9e], "fmov x0, h0"),
- ([0x7f, 0x41, 0x00, 0xd5], "msr pstate.58, #0x1"),
+ ([0x7f, 0x41, 0x00, 0xd5], "msr pstate.0x58, #0x1"),
([0x00, 0x68, 0x20, 0x38], "strb w0, [x0, x0]"),
];
let errs = run_tests(TESTS);