diff options
Diffstat (limited to 'src/arm32')
-rw-r--r-- | src/arm32/arm_opcode/mod.rs | 1 | ||||
-rw-r--r-- | src/arm32/flag/mod.rs | 7 | ||||
-rw-r--r-- | src/arm32/instruction/display.rs | 254 | ||||
-rw-r--r-- | src/arm32/instruction/test.rs | 12 | ||||
-rw-r--r-- | src/arm32/instruction_codec/encode_arm.rs | 87 | ||||
-rw-r--r-- | src/arm32/instruction_codec/encode_thumb.rs | 92 | ||||
-rw-r--r-- | src/arm32/instruction_codec/mod.rs | 3 | ||||
-rw-r--r-- | src/arm32/register/mod.rs | 111 | ||||
-rw-r--r-- | src/arm32/shifter/mod.rs | 6 | ||||
-rw-r--r-- | src/arm32/thumb_opcode/mod.rs | 3 |
10 files changed, 458 insertions, 118 deletions
diff --git a/src/arm32/arm_opcode/mod.rs b/src/arm32/arm_opcode/mod.rs index 8fba39d..9791856 100644 --- a/src/arm32/arm_opcode/mod.rs +++ b/src/arm32/arm_opcode/mod.rs @@ -27,6 +27,7 @@ use core::fmt::{Debug, Display, Formatter}; pub struct ArmOpcode(u32); impl ArmOpcode { + /// The size of any Arm opcode, in bytes. pub const SIZE: u32 = 0x4; /// Creates a new opcode from a primitive. diff --git a/src/arm32/flag/mod.rs b/src/arm32/flag/mod.rs index b24e103..fe564e4 100644 --- a/src/arm32/flag/mod.rs +++ b/src/arm32/flag/mod.rs @@ -19,6 +19,11 @@ // fero General Public License along with Pollex. // If not, see <https://www.gnu.org/licenses/>. +// TODO: This type is usefule for easily being for- +// matted, but is otherwise quite akwardly imple- +// mented (see the constant generic). Could we re- +// it? + use core::fmt::Display; /// A flag. @@ -30,10 +35,12 @@ pub enum Flag<const C: char> { } impl<const C: char> Flag<C> { + /// Checks if the flag is off. #[inline(always)] #[must_use] pub const fn is_off(self) -> bool { self as u8 == Self::Off as u8 } + /// Checks if the flag is on. #[inline(always)] #[must_use] pub const fn is_on(self) -> bool { self as u8 == Self::On as u8 } diff --git a/src/arm32/instruction/display.rs b/src/arm32/instruction/display.rs index 93f5e32..f610782 100644 --- a/src/arm32/instruction/display.rs +++ b/src/arm32/instruction/display.rs @@ -19,7 +19,7 @@ // fero General Public License along with Pollex. // If not, see <https://www.gnu.org/licenses/>. -use crate::arm32::Instruction; +use crate::arm32::{Flag, Instruction, Shifter}; use core::fmt::Display; @@ -28,80 +28,184 @@ impl Display for Instruction { use Instruction::*; match *self { - Add { predicate, destination, base, source, s } - => write!(f, "ADD{predicate}{s} {destination}, {base}, {source}"), - - AddCarry { predicate, destination, base, source, s } - => write!(f, "ADC{predicate}{s} {destination}, {base}, {source}"), - - And { predicate, destination, base, source, s } - => write!(f, "AND{predicate}{s} {destination}, {base}, {source}"), - - BitClear { predicate, destination, base, source, s } - => write!(f, "BIC{predicate}{s} {destination}, {base}, {source}"), - - Branch { predicate, immediate } - => write!(f, "B{predicate} <#{immediate}>"), - - BranchExchange { predicate, register } - => write!(f, "BX{predicate} {register}"), - - BranchLink { predicate, immediate } - => write!(f, "BL{predicate} <#{immediate}>"), - - Breakpoint { immediate } - => write!(f, "BKPT #{immediate}"), - - CountLeadingZeroes { predicate, destination, source } - => write!(f, "CLZ{predicate} {destination}, {source}"), - - Compare { predicate, lhs, rhs } - => write!(f, "CMP{predicate} {lhs}, {rhs}"), - - CompareNegated { predicate, lhs, rhs } - => write!(f, "CMN{predicate} {lhs}, {rhs}"), - - ExclusiveOr { predicate, destination, base, source, s } - => write!(f, "EOR{predicate}{s} {destination}, {base}, {source}"), - - InclusiveOr { predicate, destination, base, source, s } - => write!(f, "ORR{predicate}{s} {destination}, {base}, {source}"), - - Move { predicate, destination, source, s } - => write!(f, "MOV{predicate}{s} {destination}, {source}"), - - MoveNot { predicate, destination, source, s } - => write!(f, "MVN{predicate}{s} {destination}, {source}"), - - Multiply { predicate, destination, base, source, s } - => write!(f, "MUL{predicate}{s} {destination}, {base}, {source}"), - - MultiplyAccumulate { predicate, destination, base, source, shift, s } - => write!(f, "MLA{predicate}{s} {destination}, {base}, {source}, {shift}"), - - Reverse { predicate, destination, source } - => write!(f, "REV{predicate} {destination}, {source}"), - - ReverseSubtract { predicate, destination, base, source, s } - => write!(f, "RSB{predicate}{s} {destination}, {base}, {source}"), - - ReverseSubtractCarry { predicate, destination, base, source, s } - => write!(f, "RSC{predicate}{s} {destination}, {base}, {source}"), - - SaturatingAdd { predicate, destination, base, source } - => write!(f, "QADD{predicate} {destination}, {base}, {source}"), - - SaturatingSubtract { predicate, destination, base, source } - => write!(f, "QSUB{predicate} {destination}, {base}, {source}"), - - SoftwareInterrupt { predicate, immediate } - => write!(f, "SWI{predicate} #{immediate}"), - - Subtract { predicate, destination, base, source, s } - => write!(f, "SUB{predicate}{s} {destination}, {base}, {source}"), - - SubtractCarry { predicate, destination, base, source, s } - => write!(f, "SBC{predicate}{s} {destination}, {base}, {source}"), + Add { + predicate, + destination, + base, + source, + s, + } => write!(f, "ADD{predicate}{s} {destination}, {base}, {source}"), + + AddCarry { + predicate, + destination, + base, + source, + s, + } => write!(f, "ADC{predicate}{s} {destination}, {base}, {source}"), + + And { + predicate, + destination, + base, + source, + s, + } => write!(f, "AND{predicate}{s} {destination}, {base}, {source}"), + + BitClear { + predicate, + destination, + base, + source, + s, + } => write!(f, "BIC{predicate}{s} {destination}, {base}, {source}"), + + Branch { + predicate, + immediate, + } => write!(f, "B{predicate} <#{immediate}>"), + + BranchExchange { + predicate, + register, + } => write!(f, "BX{predicate} {register}"), + + BranchLink { + predicate, + immediate, + } => write!(f, "BL{predicate} <#{immediate}>"), + + Breakpoint { + immediate } => write!(f, "BKPT #{immediate}"), + + CountLeadingZeroes { + predicate, + destination, + source, + } => write!(f, "CLZ{predicate} {destination}, {source}"), + + Compare { + predicate, + lhs, + rhs, + } => write!(f, "CMP{predicate} {lhs}, {rhs}"), + + CompareNegated { + predicate, + lhs, + rhs, + } => write!(f, "CMN{predicate} {lhs}, {rhs}"), + + ExclusiveOr { + predicate, + destination, + base, + source, + s, + } => write!(f, "EOR{predicate}{s} {destination}, {base}, {source}"), + + InclusiveOr { + predicate, + destination, + base, + source, + s, + } => write!(f, "ORR{predicate}{s} {destination}, {base}, {source}"), + + Move { + predicate, + destination, + source: Shifter::LogicalShiftLeftImmediate { source, shift: 0x0 }, + s: Flag::Off, + } => write!(f, "CPY{predicate} {destination}, {source}"), + + Move { + predicate, + destination, + source, + s, + } => write!(f, "MOV{predicate}{s} {destination}, {source}"), + + MoveNot { + predicate, + destination, + source, + s, + } => write!(f, "MVN{predicate}{s} {destination}, {source}"), + + Multiply { + predicate, + destination, + base, + source, + s, + } => write!(f, "MUL{predicate}{s} {destination}, {base}, {source}"), + + MultiplyAccumulate { + predicate, + destination, + base, + source, + shift, + s, + } => write!(f, "MLA{predicate}{s} {destination}, {base}, {source}, {shift}"), + + Reverse { + predicate, + destination, + source, + } => write!(f, "REV{predicate} {destination}, {source}"), + + ReverseSubtract { + predicate, + destination, + base, + source, + s, + } => write!(f, "RSB{predicate}{s} {destination}, {base}, {source}"), + + ReverseSubtractCarry { + predicate, + destination, + base, + source, + s, + } => write!(f, "RSC{predicate}{s} {destination}, {base}, {source}"), + + SaturatingAdd { + predicate, + destination, + base, + source, + } => write!(f, "QADD{predicate} {destination}, {base}, {source}"), + + SaturatingSubtract { + predicate, + destination, + base, + source, + } => write!(f, "QSUB{predicate} {destination}, {base}, {source}"), + + SoftwareInterrupt { + predicate, + immediate, + } => write!(f, "SWI{predicate} #{immediate}"), + + Subtract { + predicate, + destination, + base, + source, + s, + } => write!(f, "SUB{predicate}{s} {destination}, {base}, {source}"), + + SubtractCarry { + predicate, + destination, + base, + source, + s, + } => write!(f, "SBC{predicate}{s} {destination}, {base}, {source}"), } } } diff --git a/src/arm32/instruction/test.rs b/src/arm32/instruction/test.rs index 6137795..d9067e9 100644 --- a/src/arm32/instruction/test.rs +++ b/src/arm32/instruction/test.rs @@ -52,7 +52,7 @@ fn test_arm32_instruction() { predicate: Predicate::Always, destination: Register::R7, base: Register::R8, - source: Shifter::LogicalShiftLeftImmediate { source: Register::Sb, shift: 0x0 }, + source: Shifter::LogicalShiftLeftImmediate { source: Register::R9, shift: 0x0 }, s: Flag::On, }, @@ -64,6 +64,13 @@ fn test_arm32_instruction() { shift: Register::Lr, s: Flag::Off, }, + + Instruction::Move { + predicate: Predicate::NotEqual, + destination: Register::R0, + source: Shifter::LogicalShiftLeftImmediate { source: Register::Pc, shift: 0x0 }, + s: Flag::Off, + }, ]; let mut displays = Vec::with_capacity(tree.len()); @@ -74,8 +81,9 @@ fn test_arm32_instruction() { [ "ADDGE r1, r2, r3, ROR #2", "QSUBLT r4, r5, r6", - "ORRS r7, r8, sb", + "ORRS r7, r8, r9", "MLAEQ r0, pc, pc, lr", + "CPYNE r0, pc" ], ); } diff --git a/src/arm32/instruction_codec/encode_arm.rs b/src/arm32/instruction_codec/encode_arm.rs index 73dc3d9..9c63f81 100644 --- a/src/arm32/instruction_codec/encode_arm.rs +++ b/src/arm32/instruction_codec/encode_arm.rs @@ -19,7 +19,7 @@ // fero General Public License along with Pollex. // If not, see <https://www.gnu.org/licenses/>. -use crate::Result; +use crate::{assert_or_err, Error, Result}; use crate::arm32::{ ArmOpcode, Instruction, @@ -36,35 +36,51 @@ impl InstructionCodec { pub fn encode_arm(&mut self, instruction: Instruction) -> Result<ArmOpcode> { use Instruction::*; - let mut opcode = 0b00000000_00000000_00000000_00000000; + let mut opcode = 0b00000000_00000000_00000000_00000000_u32; match instruction { - Branch { predicate, .. } => { + Branch { + predicate, + .. + } => { opcode |= 0b00001011_00000000_00000000_00000000; opcode |= (predicate as u32) << 0x1C; }, - BranchLink { predicate, .. } => { + BranchLink { + predicate, + .. + } => { opcode |= 0b00001010_00000000_00000000_00000000; opcode |= (predicate as u32) << 0x1C; }, - Breakpoint { immediate } => { + Breakpoint { + immediate, + } => { opcode |= 0b11100001_00100000_00000000_01110000; opcode |= immediate & 0b00000000_00000000_00000000_00001111; opcode |= (immediate & 0b00000000_00000000_11111111_11110000) << 0x4; }, - Move { predicate, destination, source, s } => { + Move { + predicate, + destination, + source, + s, + } => { opcode |= 0b00000001_10100000_00000000_00000000; opcode |= (destination as u32) << 0xC; opcode |= u32::from(s) << 0x14; opcode |= (predicate as u32) << 0x1C; - opcode = add_shifter(opcode, source); + opcode = add_shifter(opcode, source)?; }, - SoftwareInterrupt { predicate, immediate } => { + SoftwareInterrupt { + predicate, + immediate, + } => { opcode |= 0b00001111_00000000_00000000_00000000; opcode |= immediate & 0b00000000_11111111_11111111_11111111; opcode |= (predicate as u32) << 0x1C; @@ -78,41 +94,50 @@ impl InstructionCodec { } } -fn add_shifter(mut opcode: u32, shifter: Shifter) -> u32 { +fn add_shifter(mut opcode: u32, shifter: Shifter) -> Result<u32> { + use Shifter::*; + match shifter { - | Shifter::ArithmeticShiftRightImmediate { source, shift } - | Shifter::LogicalShiftLeftImmediate { source, shift } - | Shifter::LogicalShiftRightImmediate { source, shift } - | Shifter::RotateRightImmediate { source, shift } + LogicalShiftLeftImmediate { source, shift: 0x0 } + => { + opcode |= source as u32; + }, + + | ArithmeticShiftRightImmediate { source, shift } + | LogicalShiftLeftImmediate { source, shift } + | LogicalShiftRightImmediate { source, shift } + | RotateRightImmediate { source, shift } => { let code = match shifter { - Shifter::LogicalShiftLeftImmediate { .. } => 0b00, - Shifter::LogicalShiftRightImmediate { .. } => 0b01, - Shifter::ArithmeticShiftRightImmediate { .. } => 0b10, - Shifter::RotateRightImmediate { .. } => 0b11, + LogicalShiftLeftImmediate { .. } => 0b00, + LogicalShiftRightImmediate { .. } => 0b01, + ArithmeticShiftRightImmediate { .. } => 0b10, + RotateRightImmediate { .. } => 0b11, _ => unreachable!(), }; + assert_or_err!(shift != 0x0, Error::IllegalImmediate); + opcode |= source as u32; opcode |= code << 0x5; opcode |= shift << 0x7; }, - Shifter::RotateRightExtend { .. } => { + RotateRightExtend { .. } => { todo!() }, - | Shifter::ArithmeticShiftRightRegister { source, .. } - | Shifter::LogicalShiftLeftRegister { source, .. } - | Shifter::LogicalShiftRightRegister { source, .. } - | Shifter::RotateRightRegister { source, .. } + | ArithmeticShiftRightRegister { source, .. } + | LogicalShiftLeftRegister { source, .. } + | LogicalShiftRightRegister { source, .. } + | RotateRightRegister { source, .. } => { let _code = match shifter { - Shifter::LogicalShiftLeftRegister { .. } => 0b00, - Shifter::LogicalShiftRightRegister { .. } => 0b01, - Shifter::ArithmeticShiftRightRegister { .. } => 0b10, - Shifter::RotateRightRegister { .. } => 0b11, + LogicalShiftLeftRegister { .. } => 0b00, + LogicalShiftRightRegister { .. } => 0b01, + ArithmeticShiftRightRegister { .. } => 0b10, + RotateRightRegister { .. } => 0b11, _ => unreachable!(), }; @@ -121,18 +146,18 @@ fn add_shifter(mut opcode: u32, shifter: Shifter) -> u32 { opcode |= source as u32; }, - Shifter::Immediate { immediate } => { - let (immediate, rotate) = if immediate <= 0xFF { - (immediate, 0x00) + Immediate { source } => { + let (source, rotate) = if source <= 0xFF { + (source, 0x00) } else { todo!() }; opcode |= 0b00000010_00000000_00000000_00000000; - opcode |= immediate; + opcode |= source; opcode |= rotate << 0x8; }, } - opcode + Ok(opcode) } diff --git a/src/arm32/instruction_codec/encode_thumb.rs b/src/arm32/instruction_codec/encode_thumb.rs new file mode 100644 index 0000000..f2fa0b8 --- /dev/null +++ b/src/arm32/instruction_codec/encode_thumb.rs @@ -0,0 +1,92 @@ +// Copyright 2024 Gabriel Bjørnager Jensen. +// +// This file is part of Pollex. +// +// Pollex is free software: you can redistribute it +// and/or modify it under the terms of the GNU Af- +// fero General Public License as published by the +// Free Software Foundation, either version 3 of +// the License, or (at your option) any later ver- +// sion. +// +// Pollex is distributed in the hope that it will +// be useful, but WITHOUT ANY WARRANTY; without +// even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Affero General Public License for more details. +// +// You should have received a copy of the GNU Af- +// fero General Public License along with Pollex. +// If not, see <https://www.gnu.org/licenses/>. + +use crate::{assert_or_err, Error, Result}; +use crate::arm32::{ + Instruction, + InstructionCodec, + Predicate, + Shifter, + ThumbOpcode, +}; + +impl InstructionCodec { + /// Encodes the given Thumb instruction. + /// + /// # Errors + /// + /// If the given instruction cannot be encoded for Thumb, an error is returned. + pub fn encode_thumb(&mut self, instruction: Instruction) -> Result<ThumbOpcode> { + use Instruction::*; + + let mut opcode = 0b00000000_00000000_u16; + + match instruction { + Move { + predicate, + destination, + source, + s, + } => { + assert_or_err!(predicate == Predicate::Always, Error::IllegalPredicate); + + if s.is_on() { + if let Shifter::Immediate { source } = source { + assert_or_err!(destination.is_low(), Error::IllegalRegister); + assert_or_err!(source <= 0xFF, Error::IllegalRegister); + + opcode |= 0b00100000_00000000; + opcode |= source as u16; + opcode |= (destination as u16) << 0x8; + + } else if let Shifter::LogicalShiftLeftImmediate { source, shift: 0x0 } = source { + assert_or_err!(destination.is_low(), Error::IllegalRegister); + assert_or_err!(source.is_low(), Error::IllegalRegister); + + opcode |= 0b00100000_00000000; + opcode |= destination as u16; + opcode |= (source as u16) << 0x3; + } else { + return Err(Error::IllegalInstruction); + } + } else if let Shifter::LogicalShiftLeftImmediate { source, shift: 0x0 } = source { + opcode |= 0b01000110_00000000; + + let h0 = destination.is_high(); + let h1 = source.is_high(); + + opcode |= destination as u16 & 0b00000000_00000111; + opcode |= (source as u16 & 0b00000000_00000111) << 0x3; + opcode |= u16::from(h0) << 0x6; + opcode |= u16::from(h1) << 0x7; + } else { + // TODO: Shifters &c. + todo!(); + } + } + + _ => return Err(Error::IllegalInstruction), + } + + self.address += ThumbOpcode::SIZE; + Ok(opcode.into()) + } +} diff --git a/src/arm32/instruction_codec/mod.rs b/src/arm32/instruction_codec/mod.rs index f8d2a8c..841d335 100644 --- a/src/arm32/instruction_codec/mod.rs +++ b/src/arm32/instruction_codec/mod.rs @@ -23,13 +23,14 @@ mod test; mod encode_arm; +mod encode_thumb; use core::num::Wrapping; /// Codec for encoding and decoding instruction. /// /// Arm instructions can be encoded/decoded using the [`encode_arm`](InstructionCodec::encode_arm) and `decode_arm` (soon). -/// Thumb instruction will similarly be manipulated using `encode_thumb` and `decode_thumb`. +/// Thumb instruction will similarly be manipulated using [`encode_thumb`](InstructionCodec::encode_thumb) and `decode_thumb`. /// /// This structure keeps track of the adress at which instructions are to be placed (see *Rationale*). /// If encoding causes this internal address to go past `0xFFFFFFFF`, the value is safely wrapped to the origin (i.e. `0x00000000`). diff --git a/src/arm32/register/mod.rs b/src/arm32/register/mod.rs index 2df521b..0013c46 100644 --- a/src/arm32/register/mod.rs +++ b/src/arm32/register/mod.rs @@ -19,7 +19,10 @@ // fero General Public License along with Pollex. // If not, see <https://www.gnu.org/licenses/>. +use crate::Error; + use core::fmt::Display; +use core::str::FromStr; /// An Arm register. /// @@ -38,15 +41,33 @@ pub enum Register { R6 = 0b0110, R7 = 0b0111, R8 = 0b1000, - Sb = 0b1001, // R9 - Sl = 0b1010, // R10 + R9 = 0b1001, // Sb + R10 = 0b1010, // Sl R11 = 0b1011, - Ip = 0b1100, // R12 + R12 = 0b1100, // Ip Sp = 0b1101, // R13 Lr = 0b1110, // R14 Pc = 0b1111, // R15 } +impl Register { + /// Checks if the register is a low register. + /// + /// That is, it is any of `r0`, `r1`, `r2`, `r3`, `r4`, `r5`, `r6`, or `r7` -- or any alias herof. + /// See also [`is_high`](Self::is_high). + #[inline(always)] + #[must_use] + pub const fn is_low(self) -> bool { (self as u8) <= 0x7 } + + /// Checks if the register is a high register. + /// + /// That is, it is any of `r8`, `r9`, `r10`, `r11`, `r12`, `sp`, `lr`, or `pc` -- or any alias herof. + /// See also [`is_low`](Self::is_low). + #[inline(always)] + #[must_use] + pub const fn is_high(self) -> bool { (self as u8) > 0x7 } +} + impl Display for Register { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { use Register::*; @@ -61,13 +82,91 @@ impl Display for Register { R6 => write!(f, "r6"), R7 => write!(f, "r7"), R8 => write!(f, "r8"), - Sb => write!(f, "sb"), - Sl => write!(f, "sl"), + R9 => write!(f, "r9"), + R10 => write!(f, "r10"), R11 => write!(f, "r11"), - Ip => write!(f, "ip"), + R12 => write!(f, "r12"), Sp => write!(f, "sp"), Lr => write!(f, "lr"), Pc => write!(f, "pc"), } } } + +impl FromStr for Register { + type Err = Error; + + fn from_str(s: &str) -> Result<Self, Self::Err> { + use Register::*; + + match s.to_lowercase().as_str() { + | "r0" + | "a1" + => Ok(R0), + + | "r1" + | "a2" + => Ok(R1), + + | "r2" + | "a3" + => Ok(R2), + + | "r3" + | "a4" + => Ok(R3), + + | "r4" + | "v1" + => Ok(R4), + + | "r5" + | "v2" + => Ok(R5), + + | "r6" + | "v3" + => Ok(R6), + + | "r7" + | "v4" + => Ok(R7), + + | "r8" + | "v5" + => Ok(R8), + + | "r9" + | "sb" + | "v6" + => Ok(R9), + + | "r10" + | "sl" + | "v7" + => Ok(R10), + + | "r11" + | "v8" + => Ok(R11), + + | "ip" + | "r12" + => Ok(R12), + + | "r13" + | "sp" + => Ok(Sp), + + | "lr" + | "r14" + => Ok(Lr), + + | "pc" + | "r15" + => Ok(Pc), + + _ => Err(Error::UnknownRegister) + } + } +} diff --git a/src/arm32/shifter/mod.rs b/src/arm32/shifter/mod.rs index f658c2b..ae0b1f9 100644 --- a/src/arm32/shifter/mod.rs +++ b/src/arm32/shifter/mod.rs @@ -50,7 +50,7 @@ pub enum Shifter { ArithmeticShiftRightRegister { source: Register, shift: Register }, - Immediate { immediate: u32 }, + Immediate { source: u32 }, LogicalShiftLeftImmediate { source: Register, shift: u32 }, @@ -80,8 +80,8 @@ impl Display for Shifter { write!(f, "{source}, ASR {shift}") }, - Immediate { immediate } => { - write!(f, "#{immediate}<") + Immediate { source } => { + write!(f, "#{source}") }, LogicalShiftLeftImmediate { source, shift: 0x0 } => { diff --git a/src/arm32/thumb_opcode/mod.rs b/src/arm32/thumb_opcode/mod.rs index 1bd9529..c327696 100644 --- a/src/arm32/thumb_opcode/mod.rs +++ b/src/arm32/thumb_opcode/mod.rs @@ -27,6 +27,9 @@ use core::fmt::{Debug, Display, Formatter}; pub struct ThumbOpcode(u16); impl ThumbOpcode { + /// The size of any Thumb opcode, in bytes. + /// + /// This value is not including `BL` prefixes and suffixes, which count as two opcodes in total. pub const SIZE: u32 = 0x2; /// Creates a new opcode from a primitive. |