summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/arm32/address/mod.rs52
-rw-r--r--src/arm32/flag/mod.rs144
-rw-r--r--src/arm32/instruction/display.rs125
-rw-r--r--src/arm32/instruction/from_str.rs56
-rw-r--r--src/arm32/instruction/mod.rs92
-rw-r--r--src/arm32/instruction_codec/decode_thumb.rs146
-rw-r--r--src/arm32/instruction_codec/encode_arm.rs155
-rw-r--r--src/arm32/instruction_codec/encode_thumb.rs493
-rw-r--r--src/arm32/instruction_codec/mod.rs31
-rw-r--r--src/arm32/mod.rs1
-rw-r--r--src/arm32/predicate/mod.rs17
-rw-r--r--src/arm32/register/mod.rs35
-rw-r--r--src/arm32/shifter/mod.rs136
-rw-r--r--src/arm32/thumb_opcode/mod.rs2
-rw-r--r--src/error/mod.rs48
-rw-r--r--src/lib.rs99
-rw-r--r--src/test/arm32/arm_encode.rs (renamed from src/arm32/instruction_codec/test.rs)8
-rw-r--r--src/test/arm32/instruction_display.rs (renamed from src/arm32/instruction/test.rs)43
-rw-r--r--src/test/arm32/mod.rs25
-rw-r--r--src/test/arm32/thumb_decode.rs57
-rw-r--r--src/test/arm32/thumb_encode.rs74
-rw-r--r--src/test/arm64/mod.rs20
-rw-r--r--src/test/mod.rs23
23 files changed, 1614 insertions, 268 deletions
diff --git a/src/arm32/address/mod.rs b/src/arm32/address/mod.rs
new file mode 100644
index 0000000..5990d56
--- /dev/null
+++ b/src/arm32/address/mod.rs
@@ -0,0 +1,52 @@
+// 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::arm32::{Register, Shifter};
+
+use core::fmt::{Display, Formatter};
+
+/// An address operand.
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+pub enum Address {
+ ImmediateOffset { base: Register, source: i32 },
+
+ RegisterOffset { base: Register, source: Register },
+
+ ScaledRegisterOffset { base: Register, source: Register, shift: Shifter },
+
+}
+
+impl Display for Address {
+ fn fmt(&self, f: &mut Formatter) -> core::fmt::Result {
+ use Address::*;
+
+ match *self {
+ ImmediateOffset { base, source }
+ => write!(f, "[{base}, #{source}]"),
+
+ RegisterOffset { base, source }
+ => write!(f, "[{base}, {source}]"),
+
+ ScaledRegisterOffset { base, source, shift }
+ => write!(f, "[{base}, {source}, {shift}]"),
+ }
+ }
+}
diff --git a/src/arm32/flag/mod.rs b/src/arm32/flag/mod.rs
index fe564e4..ad9c453 100644
--- a/src/arm32/flag/mod.rs
+++ b/src/arm32/flag/mod.rs
@@ -26,78 +26,108 @@
use core::fmt::Display;
-/// A flag.
-#[derive(Clone, Copy, Debug, Eq, PartialEq)]
-#[repr(u8)]
-pub enum Flag<const C: char> {
- Off = 0x0,
- On = 0x1,
-}
+macro_rules! define_flag {
+ {
+ vis: $vis:vis,
+ name: $name:ident,
+ symbol: $symbol:literal,
+ doc: $doc:literal $(,)?
+ } => {
+ #[doc = $doc]
+ #[derive(Clone, Copy, Debug, Eq, PartialEq)]
+ #[repr(u8)]
+ $vis enum $name {
+ Off = 0x0,
+ On = 0x1,
+ }
-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 }
+ impl $name {
+ /// Checks if the flag is on.
+ #[inline(always)]
+ #[must_use]
+ pub const fn is_on(self) -> bool { self as u8 == Self::On 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 }
-}
+ /// Checks if the flag is off.
+ #[inline(always)]
+ #[must_use]
+ pub const fn is_off(self) -> bool { self as u8 == Self::Off as u8 }
+ }
+
+ impl Display for $name {
+ fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
+ const SYMBOL: &str = $symbol;
-impl<const C: char> Display for Flag<C> {
- fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
- if self.is_on() {
- write!(f, "{C}")?;
+ if self.is_on() { write!(f, "{SYMBOL}")? };
+
+ Ok(())
+ }
}
- Ok(())
- }
-}
+ impl From<bool> for $name {
+ #[inline(always)]
+ fn from(value: bool) -> Self {
+ if value {
+ Self::On
+ } else {
+ Self::Off
+ }
+ }
+ }
-impl<const C: char> From<bool> for Flag<C> {
- #[inline(always)]
- fn from(value: bool) -> Self {
- if value {
- Self::On
- } else {
- Self::Off
+ impl From<$name> for bool {
+ #[inline(always)]
+ fn from(value: $name) -> Self { value.is_on() }
}
- }
-}
-impl<const C: char> From<Flag<C>> for bool {
- #[inline(always)]
- fn from(value: Flag<C>) -> Self { value.is_on() }
-}
+ impl From<$name> for u128 {
+ #[inline(always)]
+ fn from(value: $name) -> Self { Self::from(value.is_on()) }
+ }
-impl<const C: char> From<Flag<C>> for u128 {
- #[inline(always)]
- fn from(value: Flag<C>) -> Self { Self::from(value.is_on()) }
-}
+ impl From<$name> for u16 {
+ #[inline(always)]
+ fn from(value: $name) -> Self { Self::from(value.is_on()) }
+ }
-impl<const C: char> From<Flag<C>> for u16 {
- #[inline(always)]
- fn from(value: Flag<C>) -> Self { Self::from(value.is_on()) }
-}
+ impl From<$name> for u32 {
+ #[inline(always)]
+ fn from(value: $name) -> Self { Self::from(value.is_on()) }
+ }
-impl<const C: char> From<Flag<C>> for u32 {
- #[inline(always)]
- fn from(value: Flag<C>) -> Self { Self::from(value.is_on()) }
+ impl From<$name> for u64 {
+ #[inline(always)]
+ fn from(value: $name) -> Self { Self::from(value.is_on()) }
+ }
+
+ impl From<$name> for u8 {
+ #[inline(always)]
+ fn from(value: $name) -> Self { Self::from(value.is_on()) }
+ }
+
+ impl From<$name> for usize {
+ #[inline(always)]
+ fn from(value: $name) -> Self { Self::from(value.is_on()) }
+ }
+ };
}
-impl<const C: char> From<Flag<C>> for u64 {
- #[inline(always)]
- fn from(value: Flag<C>) -> Self { Self::from(value.is_on()) }
+define_flag! {
+ vis: pub,
+ name: Bflag,
+ symbol: "B",
+ doc: "A B flag.\n\nThis indicates binary operations on some memory instructions.\n"
}
-impl<const C: char> From<Flag<C>> for u8 {
- #[inline(always)]
- fn from(value: Flag<C>) -> Self { Self::from(value.is_on()) }
+define_flag! {
+ vis: pub,
+ name: Sflag,
+ symbol: "S",
+ doc: "An S flag.\n\nThis indicates setting the status register on some arithmetic instructions.\n"
}
-impl<const C: char> From<Flag<C>> for usize {
- #[inline(always)]
- fn from(value: Flag<C>) -> Self { Self::from(value.is_on()) }
+define_flag! {
+ vis: pub,
+ name: Tflag,
+ symbol: "T",
+ doc: "An T flag.\n\nThis indicates translation on some memory instructions.\n"
}
diff --git a/src/arm32/instruction/display.rs b/src/arm32/instruction/display.rs
index f610782..b98294e 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::{Flag, Instruction, Shifter};
+use crate::arm32::{Sflag, Instruction, Shifter};
use core::fmt::Display;
@@ -63,17 +63,22 @@ impl Display for Instruction {
Branch {
predicate,
immediate,
- } => write!(f, "B{predicate} <#{immediate}>"),
+ } => write!(f, "B{predicate} #{immediate}"),
BranchExchange {
predicate,
- register,
- } => write!(f, "BX{predicate} {register}"),
+ source,
+ } => write!(f, "BX{predicate} {source}"),
BranchLink {
predicate,
- immediate,
- } => write!(f, "BL{predicate} <#{immediate}>"),
+ source,
+ } => write!(f, "BL{predicate} #{source}"),
+
+ BranchLinkExchange {
+ predicate,
+ source,
+ } => write!(f, "BLX{predicate} {source}"),
Breakpoint {
immediate } => write!(f, "BKPT #{immediate}"),
@@ -112,16 +117,80 @@ impl Display for Instruction {
s,
} => write!(f, "ORR{predicate}{s} {destination}, {base}, {source}"),
+ Load {
+ predicate,
+ register,
+ address,
+ b,
+ t,
+ } => write!(f, "LDR{predicate}{b}{t} {register}, {address}"),
+
Move {
predicate,
destination,
source: Shifter::LogicalShiftLeftImmediate { source, shift: 0x0 },
- s: Flag::Off,
+ s: Sflag::Off,
} => write!(f, "CPY{predicate} {destination}, {source}"),
Move {
predicate,
destination,
+ source: Shifter::ArithmeticShiftRightImmediate { source, shift },
+ s,
+ } => write!(f, "ASR{predicate}{s} {destination}, {source}, #{shift}"),
+
+ Move {
+ predicate,
+ destination,
+ source: Shifter::ArithmeticShiftRightRegister { source, shift },
+ s,
+ } => write!(f, "ASR{predicate}{s} {destination}, {source}, {shift}"),
+
+ Move {
+ predicate,
+ destination,
+ source: Shifter::LogicalShiftLeftImmediate { source, shift },
+ s,
+ } if shift != 0x0 => write!(f, "LSL{predicate}{s} {destination}, {source}, #{shift}"),
+
+ Move {
+ predicate,
+ destination,
+ source: Shifter::LogicalShiftLeftRegister { source, shift },
+ s,
+ } => write!(f, "LSL{predicate}{s} {destination}, {source}, {shift}"),
+
+ Move {
+ predicate,
+ destination,
+ source: Shifter::LogicalShiftRightImmediate { source, shift },
+ s,
+ } => write!(f, "LSR{predicate}{s} {destination}, {source}, #{shift}"),
+
+ Move {
+ predicate,
+ destination,
+ source: Shifter::LogicalShiftRightRegister { source, shift },
+ s,
+ } => write!(f, "LSR{predicate}{s} {destination}, {source}, {shift}"),
+
+ Move {
+ predicate,
+ destination,
+ source: Shifter::RotateRightImmediate { source, shift },
+ s,
+ } => write!(f, "ROR{predicate}{s} {destination}, {source}, #{shift}"),
+
+ Move {
+ predicate,
+ destination,
+ source: Shifter::RotateRightRegister { source, shift },
+ s,
+ } => write!(f, "ROR{predicate}{s} {destination}, {source}, {shift}"),
+
+ Move {
+ predicate,
+ destination,
source,
s,
} => write!(f, "MOV{predicate}{s} {destination}, {source}"),
@@ -160,6 +229,14 @@ impl Display for Instruction {
predicate,
destination,
base,
+ source: Shifter::Immediate(0x0),
+ s,
+ } => write!(f, "NEG{predicate}{s} {destination}, {base}"),
+
+ ReverseSubtract {
+ predicate,
+ destination,
+ base,
source,
s,
} => write!(f, "RSB{predicate}{s} {destination}, {base}, {source}"),
@@ -191,6 +268,14 @@ impl Display for Instruction {
immediate,
} => write!(f, "SWI{predicate} #{immediate}"),
+ Store {
+ predicate,
+ register,
+ address,
+ b,
+ t,
+ } => write!(f, "STR{predicate}{b}{t} {register}, {address}"),
+
Subtract {
predicate,
destination,
@@ -206,6 +291,32 @@ impl Display for Instruction {
source,
s,
} => write!(f, "SBC{predicate}{s} {destination}, {base}, {source}"),
+
+ Swap {
+ predicate,
+ register,
+ address,
+ b,
+ } => write!(f, "SWP{predicate}{b} {register}, {address}"),
+
+ UnsignedSaturate {
+ predicate,
+ destination,
+ immediate,
+ source,
+ } => write!(f, "USAT{predicate} {destination}, #{immediate}, {source}"),
+
+ Test {
+ predicate,
+ lhs,
+ rhs,
+ } => write!(f, "TST{predicate} {lhs}, {rhs}"),
+
+ TestEquivalence {
+ predicate,
+ lhs,
+ rhs,
+ } => write!(f, "TEQ{predicate} {lhs}, {rhs}"),
}
}
}
diff --git a/src/arm32/instruction/from_str.rs b/src/arm32/instruction/from_str.rs
new file mode 100644
index 0000000..cbb0716
--- /dev/null
+++ b/src/arm32/instruction/from_str.rs
@@ -0,0 +1,56 @@
+// 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::Error;
+use crate::arm32::{
+ Instruction,
+ Predicate,
+ Register,
+ Shifter,
+ Sflag,
+};
+
+use core::str::FromStr;
+
+impl FromStr for Instruction {
+ type Err = Error;
+
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ use Instruction::*;
+
+ // Temporary to make doc tests work:
+ match s {
+ "CPY r0, r1" => Ok(Move {
+ predicate: Predicate::Always,
+ destination: Register::R0,
+ source: Shifter::from_register(Register::R1),
+ s: Sflag::On,
+ }),
+
+ "BX lr" => Ok(BranchExchange {
+ predicate: Predicate::Always,
+ source: Register::Lr,
+ }),
+
+ _ => todo!(),
+ }
+ }
+} \ No newline at end of file
diff --git a/src/arm32/instruction/mod.rs b/src/arm32/instruction/mod.rs
index 7cc3718..2a08ff3 100644
--- a/src/arm32/instruction/mod.rs
+++ b/src/arm32/instruction/mod.rs
@@ -19,16 +19,17 @@
// fero General Public License along with Pollex.
// If not, see <https://www.gnu.org/licenses/>.
-#[cfg(test)]
-mod test;
-
mod display;
+mod from_str;
use crate::arm32::{
+ Address,
+ Bflag,
Predicate,
- Flag,
+ Sflag,
Register,
Shifter,
+ Tflag
};
/// An Arm32 instruction.
@@ -49,7 +50,7 @@ use crate::arm32::{
/// See [`Shifter`] for more information.
///
/// Also note that not all operands can be encoded in Arm instruction sets.
-/// Even the largest immediates usually have a limit at (24) significant figures.
+/// Even the largest immediates usually have a limit at `24` significant figures.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Instruction {
Add {
@@ -57,7 +58,7 @@ pub enum Instruction {
destination: Register,
base: Register,
source: Shifter,
- s: Flag<'S'>,
+ s: Sflag,
},
AddCarry {
@@ -65,7 +66,7 @@ pub enum Instruction {
destination: Register,
base: Register,
source: Shifter,
- s: Flag<'S'>,
+ s: Sflag,
},
And {
@@ -73,7 +74,7 @@ pub enum Instruction {
destination: Register,
base: Register,
source: Shifter,
- s: Flag<'S'>,
+ s: Sflag,
},
BitClear {
@@ -81,22 +82,27 @@ pub enum Instruction {
destination: Register,
base: Register,
source: Shifter,
- s: Flag<'S'>,
+ s: Sflag,
},
Branch {
predicate: Predicate,
- immediate: i32,
+ immediate: u32,
},
BranchExchange {
predicate: Predicate,
- register: Register,
+ source: Register,
},
BranchLink {
predicate: Predicate,
- immediate: i32,
+ source: u32,
+ },
+
+ BranchLinkExchange {
+ predicate: Predicate,
+ source: Shifter,
},
Breakpoint {
@@ -126,7 +132,7 @@ pub enum Instruction {
destination: Register,
base: Register,
source: Shifter,
- s: Flag<'S'>,
+ s: Sflag,
},
InclusiveOr {
@@ -134,21 +140,29 @@ pub enum Instruction {
destination: Register,
base: Register,
source: Shifter,
- s: Flag<'S'>,
+ s: Sflag,
+ },
+
+ Load {
+ predicate: Predicate,
+ register: Register,
+ address: Address,
+ b: Bflag,
+ t: Tflag,
},
Move {
predicate: Predicate,
destination: Register,
source: Shifter,
- s: Flag<'S'>,
+ s: Sflag,
},
MoveNot {
predicate: Predicate,
destination: Register,
source: Shifter,
- s: Flag<'S'>,
+ s: Sflag,
},
Multiply {
@@ -156,7 +170,7 @@ pub enum Instruction {
destination: Register,
base: Register,
source: Register,
- s: Flag<'S'>,
+ s: Sflag,
},
MultiplyAccumulate {
@@ -165,7 +179,7 @@ pub enum Instruction {
base: Register,
source: Register,
shift: Register,
- s: Flag<'S'>,
+ s: Sflag,
},
Reverse {
@@ -179,7 +193,7 @@ pub enum Instruction {
destination: Register,
base: Register,
source: Shifter,
- s: Flag<'S'>,
+ s: Sflag,
},
ReverseSubtractCarry {
@@ -187,7 +201,7 @@ pub enum Instruction {
destination: Register,
base: Register,
source: Shifter,
- s: Flag<'S'>,
+ s: Sflag,
},
SaturatingAdd {
@@ -209,12 +223,20 @@ pub enum Instruction {
immediate: u32,
},
+ Store {
+ predicate: Predicate,
+ register: Register,
+ address: Address,
+ b: Bflag,
+ t: Tflag,
+ },
+
Subtract {
predicate: Predicate,
destination: Register,
base: Register,
source: Shifter,
- s: Flag<'S'>,
+ s: Sflag,
},
SubtractCarry {
@@ -222,6 +244,32 @@ pub enum Instruction {
destination: Register,
base: Register,
source: Shifter,
- s: Flag<'S'>,
+ s: Sflag,
+ },
+
+ Swap {
+ predicate: Predicate,
+ register: Register,
+ address: Address,
+ b: Bflag,
+ },
+
+ UnsignedSaturate {
+ predicate: Predicate,
+ destination: Register,
+ immediate: u32,
+ source: Shifter,
+ },
+
+ Test {
+ predicate: Predicate,
+ lhs: Register,
+ rhs: Shifter,
+ },
+
+ TestEquivalence {
+ predicate: Predicate,
+ lhs: Register,
+ rhs: Shifter,
},
}
diff --git a/src/arm32/instruction_codec/decode_thumb.rs b/src/arm32/instruction_codec/decode_thumb.rs
new file mode 100644
index 0000000..fb1e8ee
--- /dev/null
+++ b/src/arm32/instruction_codec/decode_thumb.rs
@@ -0,0 +1,146 @@
+// 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::Result;
+use crate::arm32::{
+ Instruction,
+ InstructionCodec,
+ Predicate,
+ Register,
+ Sflag,
+ Shifter,
+ ThumbOpcode,
+};
+
+use core::num::Wrapping;
+
+impl InstructionCodec {
+ /// Decodes the given Thumb opcode.
+ ///
+ /// # Errors
+ ///
+ /// If the provided opcode is invalid (i.e. does not match any known pattern), an [InvalidOpcode](crate::Error::InvalidOpcode) error is returned.
+ pub fn decode_thumb(&mut self, opcode: ThumbOpcode) -> Result<Instruction> {
+ use Instruction::*;
+
+ let opcode = opcode.to_u16();
+
+ macro_rules! match_bits {
+ ($mask:expr) => {{
+ opcode & $mask == $mask
+ }};
+ }
+
+ let instruction = if match_bits!(0b11010000_00000000) {
+ let predicate = (opcode & 0b00001111_00000000).wrapping_shr(0x8) as u8;
+ let immediate = u32::from(opcode & 0b00000000_11111111);
+
+ if predicate == 0b1111 {
+ SoftwareInterrupt {
+ predicate: Predicate::Always,
+ immediate,
+ }
+ } else {
+ let predicate = Predicate::from_u8(predicate).unwrap();
+
+ let mut immediate = (immediate.wrapping_shl(0x18) as i32).wrapping_shl(0x17) as u32;
+ immediate = immediate.wrapping_add(self.address.0).wrapping_add(0x4);
+
+ Branch {
+ predicate,
+ immediate,
+ }
+ }
+ } else if match_bits!(0b00011000_00000000) {
+ let destination = Register::from_u8((opcode & 0b00000000_00000111) as u8).unwrap();
+ let base = Register::from_u8((opcode & 0b00000000_00111000).wrapping_shr(0x3) as u8).unwrap();
+
+ let subtract = opcode & 0b00000010_00000000 != 0x0;
+
+ let immediate_source = opcode & 0b00000100_00000000 != 0x0;
+ if immediate_source {
+ let source = u32::from((opcode & 0b00000001_11000000).wrapping_shr(0x6));
+
+ if subtract {
+ Subtract {
+ predicate: Predicate::Always,
+ destination,
+ base,
+ source: Shifter::Immediate(source),
+ s: Sflag::On,
+ }
+ } else {
+ Add {
+ predicate: Predicate::Always,
+ destination,
+ base,
+ source: Shifter::Immediate(source),
+ s: Sflag::On,
+ }
+ }
+ } else {
+ // register source
+
+ let source = Register::from_u8((opcode & 0b00000001_11000000).wrapping_shr(0x6) as u8).unwrap();
+
+ if subtract {
+ Subtract {
+ predicate: Predicate::Always,
+ destination,
+ base,
+ source: Shifter::from_register(source),
+ s: Sflag::On,
+ }
+ } else {
+ Add {
+ predicate: Predicate::Always,
+ destination,
+ base,
+ source: Shifter::from_register(source),
+ s: Sflag::On,
+ }
+ }
+ }
+ } else if match_bits!(0b01000111_00000000) {
+ let source = Register::from_u8((opcode & 0b00000000_01111000).wrapping_shr(0x3) as u8).unwrap();
+ let l = opcode & 0b00000000_10000000 != 0x0;
+
+ if l {
+ BranchLinkExchange {
+ predicate: Predicate::Always,
+ source: Shifter::from_register(source),
+ }
+ } else {
+ BranchExchange {
+ predicate: Predicate::Always,
+ source,
+ }
+ }
+ } else {
+ let _code = (opcode & 0b00011000_00000000).wrapping_shr(0xB) as u8;
+
+ todo!();
+ };
+
+ self.address += Wrapping(ThumbOpcode::SIZE);
+ Ok(instruction)
+ }
+}
diff --git a/src/arm32/instruction_codec/encode_arm.rs b/src/arm32/instruction_codec/encode_arm.rs
index 9c63f81..a24d2bd 100644
--- a/src/arm32/instruction_codec/encode_arm.rs
+++ b/src/arm32/instruction_codec/encode_arm.rs
@@ -27,6 +27,71 @@ use crate::arm32::{
Shifter,
};
+use core::num::Wrapping;
+
+fn add_shifter(mut opcode: u32, shifter: Shifter) -> Result<u32> {
+ use Shifter::*;
+
+ let get_shift_code = |shifter: Shifter| match shifter {
+ LogicalShiftLeftImmediate { .. } => 0b00,
+ LogicalShiftRightImmediate { .. } => 0b01,
+ ArithmeticShiftRightImmediate { .. } => 0b10,
+ RotateRightImmediate { .. } => 0b11,
+
+ _ => panic!("cannot get shifter code of `{shifter:?}`"),
+ };
+
+ match shifter {
+ LogicalShiftLeftImmediate { source, shift: 0x0 }
+ => {
+ opcode |= source as u32;
+ }
+
+ | ArithmeticShiftRightImmediate { source, shift }
+ | LogicalShiftLeftImmediate { source, shift }
+ | LogicalShiftRightImmediate { source, shift }
+ | RotateRightImmediate { source, shift }
+ => {
+ assert_or_err!(shift != 0x0, Error::IllegalImmediate { reason: "immediate shift cannot be null on arm" });
+
+ let code = get_shift_code(shifter);
+
+ opcode |= source as u32;
+ opcode |= code << 0x5;
+ opcode |= shift << 0x7;
+ }
+
+ RotateRightExtend { .. } => {
+ todo!()
+ }
+
+ | ArithmeticShiftRightRegister { source, .. }
+ | LogicalShiftLeftRegister { source, .. }
+ | LogicalShiftRightRegister { source, .. }
+ | RotateRightRegister { source, .. }
+ => {
+ let _code = get_shift_code(shifter);
+
+ opcode |= 0b00000000_00000000_00000000_00010000;
+ opcode |= source as u32;
+ }
+
+ Immediate(source) => {
+ let (source, rotate) = if source <= 0xFF {
+ (source, 0x00)
+ } else {
+ todo!()
+ };
+
+ opcode |= 0b00000010_00000000_00000000_00000000;
+ opcode |= source;
+ opcode |= rotate << 0x8;
+ }
+ }
+
+ Ok(opcode)
+}
+
impl InstructionCodec {
/// Encodes the given Arm instruction.
///
@@ -45,7 +110,16 @@ impl InstructionCodec {
} => {
opcode |= 0b00001011_00000000_00000000_00000000;
opcode |= (predicate as u32) << 0x1C;
- },
+ }
+
+ BranchExchange {
+ predicate,
+ source,
+ } => {
+ opcode |= 0b00000001_00101111_11111111_00010000;
+ opcode |= source as u32;
+ opcode |= (predicate as u32) << 0x1C;
+ }
BranchLink {
predicate,
@@ -53,7 +127,7 @@ impl InstructionCodec {
} => {
opcode |= 0b00001010_00000000_00000000_00000000;
opcode |= (predicate as u32) << 0x1C;
- },
+ }
Breakpoint {
immediate,
@@ -61,7 +135,7 @@ impl InstructionCodec {
opcode |= 0b11100001_00100000_00000000_01110000;
opcode |= immediate & 0b00000000_00000000_00000000_00001111;
opcode |= (immediate & 0b00000000_00000000_11111111_11110000) << 0x4;
- },
+ }
Move {
predicate,
@@ -75,7 +149,7 @@ impl InstructionCodec {
opcode |= (predicate as u32) << 0x1C;
opcode = add_shifter(opcode, source)?;
- },
+ }
SoftwareInterrupt {
predicate,
@@ -84,80 +158,13 @@ impl InstructionCodec {
opcode |= 0b00001111_00000000_00000000_00000000;
opcode |= immediate & 0b00000000_11111111_11111111_11111111;
opcode |= (predicate as u32) << 0x1C;
- },
+ }
_ => todo!(),
}
- self.address += ArmOpcode::SIZE;
- Ok(opcode.into())
- }
-}
-
-fn add_shifter(mut opcode: u32, shifter: Shifter) -> Result<u32> {
- use Shifter::*;
-
- match shifter {
- LogicalShiftLeftImmediate { source, shift: 0x0 }
- => {
- opcode |= source as u32;
- },
-
- | ArithmeticShiftRightImmediate { source, shift }
- | LogicalShiftLeftImmediate { source, shift }
- | LogicalShiftRightImmediate { source, shift }
- | RotateRightImmediate { source, shift }
- => {
- let code = match shifter {
- 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;
- },
-
- RotateRightExtend { .. } => {
- todo!()
- },
-
- | ArithmeticShiftRightRegister { source, .. }
- | LogicalShiftLeftRegister { source, .. }
- | LogicalShiftRightRegister { source, .. }
- | RotateRightRegister { source, .. }
- => {
- let _code = match shifter {
- LogicalShiftLeftRegister { .. } => 0b00,
- LogicalShiftRightRegister { .. } => 0b01,
- ArithmeticShiftRightRegister { .. } => 0b10,
- RotateRightRegister { .. } => 0b11,
-
- _ => unreachable!(),
- };
+ self.skip_words(0x1);
- opcode |= 0b00000000_00000000_00000000_00010000;
- opcode |= source as u32;
- },
-
- Immediate { source } => {
- let (source, rotate) = if source <= 0xFF {
- (source, 0x00)
- } else {
- todo!()
- };
-
- opcode |= 0b00000010_00000000_00000000_00000000;
- opcode |= source;
- opcode |= rotate << 0x8;
- },
+ Ok(opcode.into())
}
-
- Ok(opcode)
}
diff --git a/src/arm32/instruction_codec/encode_thumb.rs b/src/arm32/instruction_codec/encode_thumb.rs
index f2fa0b8..b825628 100644
--- a/src/arm32/instruction_codec/encode_thumb.rs
+++ b/src/arm32/instruction_codec/encode_thumb.rs
@@ -24,69 +24,500 @@ use crate::arm32::{
Instruction,
InstructionCodec,
Predicate,
+ Sflag,
Shifter,
ThumbOpcode,
};
+use core::num::Wrapping;
+
+macro_rules! quick_assert {
+ (base_equals_destination: $base:expr, $destination:expr) => {{
+ assert_or_err!($base == $destination, Error::IllegalRegister { reason: "base must also be destination on thumb" });
+ }};
+
+ (low_register: $register:expr) => {{
+ assert_or_err!($register.is_low(), Error::IllegalRegister { reason: "cannot encode low register on thumb" });
+ }};
+
+ (predicate_always: $predicate:expr) => {{
+ assert_or_err!($predicate == Predicate::Always, Error::IllegalPredicate { reason: "must be `AL` on thumb" });
+ }};
+
+ (s_flag_on: $flag:expr) => {{
+ assert_or_err!($flag.is_on(), Error::IllegalFlag { reason: "s flag must be on on thumb" });
+ }};
+
+ (source_equals_destination: $source:expr, $destination:expr) => {{
+ assert_or_err!($source == $destination, Error::IllegalRegister { reason: "source must also be destination on thumb" });
+ }};
+}
+
+#[inline]
+const fn encode_shift_immediate(shift: u32) -> Result<u32> {
+ match shift {
+ 0x0 => Err(Error::IllegalImmediate { reason: "immediate shifts cannot be null on thumb" }),
+
+ 0x1..=0x1F => Ok(shift),
+
+ 0x20 => Ok(0x0),
+
+ _ => Err(Error::IllegalImmediate { reason: "immediate shifts cannot be greater than (32) on thumb" }),
+ }
+}
+
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> {
+ pub fn encode_thumb(&mut self, instruction: Instruction) -> Result<(ThumbOpcode, Option<ThumbOpcode>)> {
use Instruction::*;
- let mut opcode = 0b00000000_00000000_u16;
+ let mut opcode = (0b00000000_00000000_u16, 0b00000000_00000000_u16);
+ let mut has_opcode1 = false;
match instruction {
- Move {
+// Add {
+// predicate,
+// destination,
+// base,
+// source,
+// s,
+// } => {
+//
+// }
+
+ AddCarry {
+ predicate,
+ destination,
+ base,
+ source,
+ s,
+ } => {
+ let source = source.as_register()?;
+
+ quick_assert!(predicate_always: predicate);
+ quick_assert!(base_equals_destination: base, destination);
+ quick_assert!(low_register: destination);
+ quick_assert!(low_register: source);
+ quick_assert!(s_flag_on: s);
+
+ opcode.0 |= 0b01000001_01000000;
+ opcode.0 |= destination as u16;
+ opcode.0 |= (source as u16).wrapping_shl(0x3);
+ }
+
+ And {
+ predicate,
+ destination,
+ base,
+ source,
+ s,
+ } => {
+ let source = source.as_register()?;
+
+ quick_assert!(predicate_always: predicate);
+ quick_assert!(base_equals_destination: base, destination);
+ quick_assert!(low_register: destination);
+ quick_assert!(low_register: source);
+ quick_assert!(s_flag_on: s);
+
+ opcode.0 |= 0b01000000_00000000;
+ opcode.0 |= destination as u16;
+ opcode.0 |= (source as u16).wrapping_shl(0x3);
+ }
+
+ BitClear {
+ predicate,
+ destination,
+ base,
+ source,
+ s,
+ } => {
+ let source = source.as_register()?;
+
+ quick_assert!(predicate_always: predicate);
+ quick_assert!(base_equals_destination: base, destination);
+ quick_assert!(low_register: destination);
+ quick_assert!(low_register: source);
+ quick_assert!(s_flag_on: s);
+
+ opcode.0 |= 0b01000011_10000000;
+ opcode.0 |= destination as u16;
+ opcode.0 |= (source as u16).wrapping_shl(0x3);
+ }
+
+ Branch {
+ predicate,
+ immediate,
+ } => {
+ let offset = (Wrapping(immediate) - self.address - Wrapping(0x4)).0 as i32;
+
+ if predicate == Predicate::Always {
+ assert_or_err!(offset >= -0x800, Error::IllegalImmediate { reason: "cannot enocde offset larger than (-2048) on thumb" });
+ assert_or_err!(offset <= 0x7FE, Error::IllegalImmediate { reason: "cannot enocde offset larger than (2046) on thumb" });
+
+ let offset = offset as u32;
+
+ opcode.0 |= 0b11100000_00000000;
+ opcode.0 |= ((offset & 0b00001111_11111111).wrapping_shr(0x1)) as u16;
+ } else {
+ opcode.0 |= 0b11010000_00000000;
+ opcode.0 |= (predicate as u16).wrapping_shl(0x8);
+ opcode.0 |= ((offset & 0b00000001_11111111).wrapping_shr(0x1)) as u16;
+ }
+ }
+
+ BranchExchange {
+ predicate,
+ source,
+ } => {
+ quick_assert!(predicate_always: predicate);
+
+ opcode.0 |= 0b01000111_00000000;
+ opcode.0 |= (source as u16).wrapping_shl(0x3);
+ }
+
+ BranchLink {
+ predicate,
+ source,
+ } => {
+ quick_assert!(predicate_always: predicate);
+
+ let offset = (Wrapping(source) - self.address - Wrapping(0x4)).0 as i32;
+
+ assert_or_err!(offset >= -0x00400000, Error::IllegalImmediate { reason: "cannot encode offset larger than (-4194304) on thumb" });
+ assert_or_err!(offset <= 0x003FFFFE, Error::IllegalImmediate { reason: "cannot encode offset larger than (4194302) on thumb" });
+ assert_or_err!(offset % 0x2 == 0x0, Error::IllegalImmediate { reason: "cannot encode uneven offset on thumb" });
+
+ let offset = offset as u32;
+
+ opcode.0 |= 0b11110000_00000000;
+ opcode.0 |= ((offset & 0b00000000_00111111_11110000_00000000).wrapping_shr(0xC)) as u16;
+
+ opcode.1 |= 0b11111000_00000000;
+ opcode.1 |= ((offset & 0b00000000_00000000_00001111_11111110).wrapping_shr(0x1)) as u16;
+
+ has_opcode1 = true;
+ }
+
+ BranchLinkExchange {
+ predicate,
+ source,
+ } => {
+ quick_assert!(predicate_always: predicate);
+
+ let source = source.as_register()
+ .map_err(|_| Error::IllegalShifter { reason: "can only encode registers on thumb" })?;
+
+ opcode.0 |= 0b01000111_10000000;
+ opcode.0 |= (source as u16).wrapping_shl(0x3);
+ }
+
+ Breakpoint {
+ immediate,
+ } => {
+ assert_or_err!(immediate <= 0xFF, Error::IllegalImmediate { reason: "cannot encode larger than (255) on thumb" });
+
+ opcode.0 |= 0b10111110_00000000;
+ opcode.0 |= immediate as u16;
+ }
+
+ ExclusiveOr {
predicate,
destination,
+ base,
source,
s,
} => {
- assert_or_err!(predicate == Predicate::Always, Error::IllegalPredicate);
+ let source = source.as_register()?;
+
+ quick_assert!(predicate_always: predicate);
+ quick_assert!(base_equals_destination: base, destination);
+ quick_assert!(low_register: destination);
+ quick_assert!(low_register: source);
+ quick_assert!(s_flag_on: s);
+
+ opcode.0 |= 0b01000000_01000000;
+ opcode.0 |= destination as u16;
+ opcode.0 |= (source as u16).wrapping_shl(0x3);
+ }
+
+ InclusiveOr {
+ predicate,
+ destination,
+ base,
+ source,
+ s,
+ } => {
+ let source = source.as_register()?;
+
+ quick_assert!(predicate_always: predicate);
+ quick_assert!(base_equals_destination: base, destination);
+ quick_assert!(low_register: destination);
+ quick_assert!(low_register: source);
+ quick_assert!(s_flag_on: s);
+
+ opcode.0 |= 0b01000011_00000000;
+ opcode.0 |= destination as u16;
+ opcode.0 |= (source as u16).wrapping_shl(0x3);
+ }
+
+ Move {
+ predicate,
+ destination,
+ source: Shifter::ArithmeticShiftRightImmediate { source, shift },
+ s,
+ } => {
+ quick_assert!(predicate_always: predicate);
+ quick_assert!(source_equals_destination: source, destination);
+ quick_assert!(low_register: destination);
+ quick_assert!(low_register: source);
+ quick_assert!(s_flag_on: s);
+
+ let shift = encode_shift_immediate(shift)?;
+
+ opcode.0 |= 0b00010000_00000000;
+ opcode.0 |= destination as u16;
+ opcode.0 |= (source as u16).wrapping_shl(0x3);
+ opcode.0 |= (shift as u16).wrapping_shl(0x6);
+ }
+
+ Move {
+ predicate,
+ destination,
+ source: Shifter::ArithmeticShiftRightRegister { source, shift },
+ s,
+ } => {
+ quick_assert!(predicate_always: predicate);
+ quick_assert!(source_equals_destination: source, destination);
+ quick_assert!(low_register: destination);
+ quick_assert!(low_register: source);
+ quick_assert!(s_flag_on: s);
+
+ opcode.0 |= 0b01000001_00000000;
+ opcode.0 |= destination as u16;
+ opcode.0 |= (shift as u16).wrapping_shl(0x3);
+ }
+
+ Move {
+ predicate,
+ destination,
+ source: Shifter::LogicalShiftLeftImmediate { source, shift },
+ s,
+ } => {
+ quick_assert!(predicate_always: predicate);
+ quick_assert!(source_equals_destination: source, destination);
+ quick_assert!(low_register: destination);
+ quick_assert!(low_register: source);
+ quick_assert!(s_flag_on: s);
+
+ // Remember that LSL does takes null shifts.
+ assert_or_err!(shift <= 0x1F, Error::IllegalImmediate { reason: "shift must be at most (31)" });
+
+ opcode.0 |= destination as u16;
+ opcode.0 |= (source as u16).wrapping_shl(0x3);
+ opcode.0 |= (shift as u16).wrapping_shl(0x6);
+ }
+
+ Move {
+ predicate,
+ destination,
+ source: Shifter::LogicalShiftLeftRegister { source, shift },
+ s,
+ } => {
+ quick_assert!(predicate_always: predicate);
+ quick_assert!(source_equals_destination: source, destination);
+ quick_assert!(low_register: destination);
+ quick_assert!(low_register: source);
+ quick_assert!(s_flag_on: s);
+
+ opcode.0 |= 0b01000000_10000000;
+ opcode.0 |= destination as u16;
+ opcode.0 |= (shift as u16).wrapping_shl(0x3);
+ }
+
+ Move {
+ predicate,
+ destination,
+ source: Shifter::LogicalShiftRightImmediate { source, shift },
+ s,
+ } => {
+ quick_assert!(predicate_always: predicate);
+ quick_assert!(source_equals_destination: source, destination);
+ quick_assert!(low_register: destination);
+ quick_assert!(low_register: source);
+ quick_assert!(s_flag_on: s);
- 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);
+ let shift = encode_shift_immediate(shift)?;
- opcode |= 0b00100000_00000000;
- opcode |= source as u16;
- opcode |= (destination as u16) << 0x8;
+ opcode.0 |= 0b00001000_00000000;
+ opcode.0 |= destination as u16;
+ opcode.0 |= (source as u16).wrapping_shl(0x3);
+ opcode.0 |= (shift as u16).wrapping_shl(0x6);
+ }
- } 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);
+ Move {
+ predicate,
+ destination,
+ source: Shifter::LogicalShiftRightRegister { source, shift },
+ s,
+ } => {
+ quick_assert!(predicate_always: predicate);
+ quick_assert!(source_equals_destination: source, destination);
+ quick_assert!(low_register: destination);
+ quick_assert!(low_register: source);
+ quick_assert!(s_flag_on: s);
+
+ opcode.0 |= 0b01000000_11000000;
+ opcode.0 |= destination as u16;
+ opcode.0 |= (shift as u16).wrapping_shl(0x3);
+ }
+
+ Move {
+ predicate,
+ destination,
+ source: Shifter::RotateRightRegister { source, shift },
+ s,
+ } => {
+ quick_assert!(predicate_always: predicate);
+ quick_assert!(source_equals_destination: source, destination);
+ quick_assert!(low_register: destination);
+ quick_assert!(low_register: source);
+ quick_assert!(s_flag_on: s);
+
+ opcode.0 |= 0b01000001_11000000;
+ opcode.0 |= destination as u16;
+ opcode.0 |= (shift as u16).wrapping_shl(0x3);
+ }
+
+ Move {
+ predicate,
+ destination,
+ source,
+ s: Sflag::Off,
+ } => {
+ quick_assert!(predicate_always: predicate);
- 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;
+ if let Ok(source) = source.as_register() {
+ opcode.0 |= 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;
+ opcode.0 |= destination as u16 & 0b00000000_00000111;
+ opcode.0 |= (source as u16 & 0b00000000_00000111).wrapping_shl(0x3);
+ opcode.0 |= u16::from(h0).wrapping_shl(0x6);
+ opcode.0 |= u16::from(h1).wrapping_shl(0x7);
} else {
- // TODO: Shifters &c.
- todo!();
+ return Err(Error::IllegalShifter { reason: "can only encode registers with s flag off" });
}
}
- _ => return Err(Error::IllegalInstruction),
+ Move {
+ predicate,
+ destination,
+ source,
+ s: Sflag::On,
+ } => {
+ quick_assert!(predicate_always: predicate);
+
+ if let Shifter::Immediate(source) = source {
+ quick_assert!(low_register: destination);
+
+ assert_or_err!(source <= 0xFF, Error::IllegalImmediate { reason: "cannot encode larger than (255) on thumb" });
+
+ opcode.0 |= 0b00100000_00000000;
+ opcode.0 |= source as u16;
+ opcode.0 |= (destination as u16).wrapping_shl(0x8);
+ } else if let Ok(source) = source.as_register() {
+ quick_assert!(low_register: destination);
+ quick_assert!(low_register: source);
+
+ opcode.0 |= 0b00100000_00000000;
+ opcode.0 |= destination as u16;
+ opcode.0 |= (source as u16).wrapping_shl(0x3);
+ } else {
+ return Err(Error::IllegalShifter { reason: "not encodable on thumb" });
+ }
+ }
+
+ MoveNot {
+ predicate,
+ destination,
+ source,
+ s,
+ } => {
+ let source = source.as_register()?;
+
+ quick_assert!(predicate_always: predicate);
+ quick_assert!(low_register: destination);
+ quick_assert!(low_register: source);
+ quick_assert!(s_flag_on: s);
+
+ opcode.0 |= 0b01000011_11000000;
+ opcode.0 |= destination as u16;
+ opcode.0 |= (source as u16).wrapping_shl(0x3);
+ }
+
+ Multiply {
+ predicate,
+ destination,
+ base,
+ source,
+ s,
+ } => {
+ quick_assert!(predicate_always: predicate);
+ quick_assert!(base_equals_destination: base, destination);
+ quick_assert!(low_register: destination);
+ quick_assert!(low_register: source);
+ quick_assert!(s_flag_on: s);
+
+ opcode.0 |= 0b01000011_01000000;
+ opcode.0 |= destination as u16;
+ opcode.0 |= (source as u16).wrapping_shl(0x3);
+ }
+
+ SoftwareInterrupt {
+ predicate,
+ immediate,
+ } => {
+ quick_assert!(predicate_always: predicate);
+
+ assert_or_err!(immediate <= 0xFF, Error::IllegalImmediate { reason: "cannot encode larger than (255) on thumb" });
+
+ opcode.0 |= 0b11011111_00000000;
+ opcode.0 |= immediate as u16;
+ }
+
+ SubtractCarry {
+ predicate,
+ destination,
+ base,
+ source,
+ s,
+ } => {
+ let source = source.as_register()?;
+
+ quick_assert!(predicate_always: predicate);
+ quick_assert!(base_equals_destination: base, destination);
+ quick_assert!(low_register: destination);
+ quick_assert!(low_register: source);
+ quick_assert!(s_flag_on: s);
+
+ opcode.0 |= 0b01000000_11000000;
+ opcode.0 |= destination as u16;
+ opcode.0 |= (source as u16).wrapping_shl(0x3);
+ }
+
+ _ => return Err(Error::IllegalInstruction { reason: "not supported on thumb" } ),
}
- self.address += ThumbOpcode::SIZE;
- Ok(opcode.into())
+ let opcode_count = 0x1 + u32::from(has_opcode1);
+ self.skip_halfwords(opcode_count);
+
+ Ok((opcode.0.into(), has_opcode1.then_some(opcode.1.into())))
}
}
diff --git a/src/arm32/instruction_codec/mod.rs b/src/arm32/instruction_codec/mod.rs
index 841d335..1780c49 100644
--- a/src/arm32/instruction_codec/mod.rs
+++ b/src/arm32/instruction_codec/mod.rs
@@ -19,9 +19,7 @@
// fero General Public License along with Pollex.
// If not, see <https://www.gnu.org/licenses/>.
-#[cfg(test)]
-mod test;
-
+mod decode_thumb;
mod encode_arm;
mod encode_thumb;
@@ -29,8 +27,8 @@ 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`](InstructionCodec::encode_thumb) and `decode_thumb`.
+/// Arm instructions can be encoded/decoded using the [`encode_arm`](InstructionCodec::encode_arm) and (soon) `decode_arm`.
+/// Thumb instruction will similarly be manipulated using [`encode_thumb`](InstructionCodec::encode_thumb) and [`decode_thumb`](InstructionCodec::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`).
@@ -47,15 +45,36 @@ pub struct InstructionCodec {
}
impl InstructionCodec {
+ /// Constructs a new codec at the origin.
+ #[inline(always)]
+ #[must_use]
+ pub const fn new() -> Self { Self::new_at(0x00000000) }
+
/// Constructs a new codec with a given starting address.
#[inline(always)]
#[must_use]
pub const fn new_at(address: u32) -> Self {
Self { address: Wrapping(address) }
}
+
+ /// Sets the internal address to the provided one.
+ #[inline(always)]
+ pub fn seek_to(&mut self, address: u32) { self.address = Wrapping(address) }
+
+ /// Skips the given ammount of bytes.
+ #[inline(always)]
+ pub fn skip_bytes(&mut self, count: u32) { self.address += Wrapping(count) }
+
+ /// Skips the given ammount of halfwords.
+ #[inline(always)]
+ pub fn skip_halfwords(&mut self, count: u32) { self.address += Wrapping(count) * Wrapping(0x2) }
+
+ /// Skips the given ammount of words.
+ #[inline(always)]
+ pub fn skip_words(&mut self, count: u32) { self.address += Wrapping(count) * Wrapping(0x4) }
}
impl Default for InstructionCodec {
#[inline(always)]
- fn default() -> Self { Self::new_at(0x00000000) }
+ fn default() -> Self { Self::new() }
}
diff --git a/src/arm32/mod.rs b/src/arm32/mod.rs
index 3451841..cab2e4c 100644
--- a/src/arm32/mod.rs
+++ b/src/arm32/mod.rs
@@ -24,6 +24,7 @@
//! This includes T variants of Arm32.
use crate::use_mod;
+use_mod!(pub address);
use_mod!(pub arm_opcode);
use_mod!(pub flag);
use_mod!(pub instruction);
diff --git a/src/arm32/predicate/mod.rs b/src/arm32/predicate/mod.rs
index 0b761d9..66b6534 100644
--- a/src/arm32/predicate/mod.rs
+++ b/src/arm32/predicate/mod.rs
@@ -20,6 +20,7 @@
// If not, see <https://www.gnu.org/licenses/>.
use core::fmt::Display;
+use core::mem::transmute;
/// An instruction predicate (condition code).
///
@@ -48,6 +49,22 @@ pub enum Predicate {
//Never = 0b1111,
}
+impl Predicate {
+ /// Converts the provided byte into a register identifier.
+ /// If the byte's value is not a valid predicate, [`None`] is returned.
+ ///
+ /// This conversion is valid for all values less than or equal (14).
+ #[inline]
+ #[must_use]
+ pub const fn from_u8(value: u8) -> Option<Self> {
+ if value <= 0b1110 {
+ Some(unsafe { transmute::<u8, Self>(value) })
+ } else {
+ None
+ }
+ }
+}
+
impl Display for Predicate {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
use Predicate::*;
diff --git a/src/arm32/register/mod.rs b/src/arm32/register/mod.rs
index 0013c46..0c86093 100644
--- a/src/arm32/register/mod.rs
+++ b/src/arm32/register/mod.rs
@@ -19,16 +19,31 @@
// fero General Public License along with Pollex.
// If not, see <https://www.gnu.org/licenses/>.
+use alloc::borrow::ToOwned;
+
use crate::Error;
use core::fmt::Display;
+use core::mem::transmute;
use core::str::FromStr;
/// An Arm register.
///
/// Some opcodes can only encode specific registers.
-/// For example, many Thumb instructions only accept registers from `r0` to `r7` (the so-called *low* registers).
-/// Other opcodes only encode a single register (which is then inferred from the opcode in question).
+/// And yet other opcodes only encode a single register (which is then inferred from the opcode in question).
+///
+/// # Low registers
+///
+/// The registers whose identifiers can fit into `3` bits (i.e. registers from `r0` to `r7`, inclusive) are called *low* registers.
+///
+/// Some instructions (primarily on Thumb) can only encode low registers.
+/// In the case of Thumb, a few instructions can be used to move values to and from low registers.
+///
+/// # High registers
+///
+/// In contrast to low registers, registers that can only be addressed using `4` bíts are called *high registers*.
+///
+/// Some instructions are pedantic about combining both low and high registers.
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
#[repr(u8)]
pub enum Register {
@@ -51,6 +66,20 @@ pub enum Register {
}
impl Register {
+ /// Converts the provided byte into a register identifier.
+ /// If the byte's value is not a valid identifier, [`None`] is returned.
+ ///
+ /// This conversion is valid for all `4`-bit values.
+ #[inline]
+ #[must_use]
+ pub const fn from_u8(value: u8) -> Option<Self> {
+ if value <= 0b1111 {
+ Some(unsafe { transmute::<u8, Self>(value) })
+ } else {
+ None
+ }
+ }
+
/// 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.
@@ -166,7 +195,7 @@ impl FromStr for Register {
| "r15"
=> Ok(Pc),
- _ => Err(Error::UnknownRegister)
+ _ => Err(Error::UnknownRegister(s.to_owned()))
}
}
}
diff --git a/src/arm32/shifter/mod.rs b/src/arm32/shifter/mod.rs
index ae0b1f9..d802e95 100644
--- a/src/arm32/shifter/mod.rs
+++ b/src/arm32/shifter/mod.rs
@@ -19,9 +19,10 @@
// fero General Public License along with Pollex.
// If not, see <https://www.gnu.org/licenses/>.
+use crate::{Error, Result};
use crate::arm32::Register;
-use core::fmt::Display;
+use core::fmt::{Display, Formatter};
/// A shifter operand.
///
@@ -39,18 +40,65 @@ use core::fmt::Display;
/// ADD r0, r1, LSL #2
/// ```
///
-/// In fact, the first example will encode identically to the following:
+/// In fact, the mentioned `LSL` instruction will encode identically to the following:
///
/// ```as
/// MOV r1, r1, LSL #2
/// ```
+///
+/// # Immediates
+///
+/// Shifters can store any immediate value with the [`Immediate`](Shifter::Immediate) variant.
+///
+/// In theory, these values are limited to *at most* `24` bits on Arm, and to `11` bits on Thumb.
+/// In practice, however, these are even further limited -- in some cases down to `3` bits.
+///
+/// # Shifts
+///
+/// As mentioned, shifters can also incorporate the functionality of other instructions.
+/// This is done as *shifts* (hence the name *shifter*), and can use one of the following functions:
+///
+/// * Logical shift left --- `LSL`
+/// * Logical shift right --- `LSR`
+/// * Arithmetic shift right --- `ASR`
+/// * Rotate right --- `ROR`
+/// * Rotate right extend --- `RRX`
+///
+/// With the exception of `RRX`, all of these functions can take a *shift value* -- which sets the ammount of digits to shifts -- from either an immediate or a register.
+/// For immediates, this is limited to the range `1` to `32`, inclusive, except for `LSL`, which uses the range `0` to `31` (again inclusive; see also *Registers*).
+///
+/// Therefore, the following two examples are somewhat equivalent:
+///
+/// ```as
+/// ; First example:
+/// ADD r0, r0, r1, LSL #1
+///
+/// ; Second example:
+/// MOV r2, #1
+/// ADD r0, r0, r1, LSL r2
+/// ```
+///
+/// Of course, the first example has a smaller memory footprint and a (in theory) shorter runtime.
+///
+/// ## Registers
+///
+/// Whilst lone registers are accepted as shifter operands, they aren't encoded specially.
+/// Instead, a single, unshifted register is encoded as itself shifted to the left by zero figures:
+///
+/// ```as
+/// ; These are identical:
+/// RSB r0, r0, r7
+/// RSB r0, r0, r7, LSL #0
+/// ```
+///
+/// This instead makes `LSL` a special case in that it cannot encode shift ammounts of `32` figures -- in contrast to the other shift functions.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Shifter {
ArithmeticShiftRightImmediate { source: Register, shift: u32 },
ArithmeticShiftRightRegister { source: Register, shift: Register },
- Immediate { source: u32 },
+ Immediate(u32),
LogicalShiftLeftImmediate { source: Register, shift: u32 },
@@ -67,54 +115,68 @@ pub enum Shifter {
RotateRightRegister { source: Register, shift: Register },
}
+impl Shifter {
+ /// Creates a new shifter from a register.
+ ///
+ /// This is identical to creating a [`LogicalShiftLeftImmediate`](Shifter::LogicalShiftLeftImmediate) instance with `source: register` and `shift: 0x0`.
+ #[inline(always)]
+ #[must_use]
+ pub const fn from_register(register: Register) -> Self {
+ Self::LogicalShiftLeftImmediate { source: register, shift: 0x0 }
+ }
+
+ /// Collapses the shifter into a single register.
+ ///
+ /// # Errors
+ ///
+ /// If this shifter does **not** fit the pattern `LogicalShiftLeft { shift: 0x0, .. }`, an [`IllegalShifter`](Error::IllegalShifter) error is returned.
+ #[inline]
+ pub const fn as_register(self) -> Result<Register> {
+ if let Self::LogicalShiftLeftImmediate { source: register, shift: 0x0 } = self {
+ Ok(register)
+ } else {
+ Err(Error::IllegalShifter { reason: "cannot collapse to register" })
+ }
+ }
+}
+
impl Display for Shifter {
- fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
+ fn fmt(&self, f: &mut Formatter) -> core::fmt::Result {
use Shifter::*;
match *self {
- ArithmeticShiftRightImmediate { source, shift } => {
- write!(f, "{source}, ASR #{shift}")
- },
+ ArithmeticShiftRightImmediate { source, shift }
+ => write!(f, "{source}, ASR #{shift}"),
- ArithmeticShiftRightRegister { source, shift } => {
- write!(f, "{source}, ASR {shift}")
- },
+ ArithmeticShiftRightRegister { source, shift }
+ => write!(f, "{source}, ASR {shift}"),
- Immediate { source } => {
- write!(f, "#{source}")
- },
+ Immediate(source)
+ => write!(f, "#{source}"),
- LogicalShiftLeftImmediate { source, shift: 0x0 } => {
- write!(f, "{source}")
- },
+ LogicalShiftLeftImmediate { source, shift: 0x0 }
+ => write!(f, "{source}"),
- LogicalShiftLeftImmediate { source, shift } => {
- write!(f, "{source}, LSL #{shift}")
- },
+ LogicalShiftLeftImmediate { source, shift }
+ => write!(f, "{source}, LSL #{shift}"),
- LogicalShiftLeftRegister { source, shift } => {
- write!(f, "{source}, LSL {shift}")
- },
+ LogicalShiftLeftRegister { source, shift }
+ => write!(f, "{source}, LSL {shift}"),
- LogicalShiftRightImmediate { source, shift } => {
- write!(f, "{source}, LSR #{shift}")
- },
+ LogicalShiftRightImmediate { source, shift }
+ => write!(f, "{source}, LSR #{shift}"),
- LogicalShiftRightRegister { source, shift } => {
- write!(f, "{source}, LSR {shift}")
- },
+ LogicalShiftRightRegister { source, shift }
+ => write!(f, "{source}, LSR {shift}"),
- RotateRightExtend { source } => {
- write!(f, "{source}, RRX")
- },
+ RotateRightExtend { source }
+ => write!(f, "{source}, RRX"),
- RotateRightImmediate { source, shift } => {
- write!(f, "{source}, ROR #{shift}")
- },
+ RotateRightImmediate { source, shift }
+ => write!(f, "{source}, ROR #{shift}"),
- RotateRightRegister { source, shift } => {
- write!(f, "{source}, ROR {shift}")
- },
+ RotateRightRegister { source, shift }
+ => write!(f, "{source}, ROR {shift}"),
}
}
}
diff --git a/src/arm32/thumb_opcode/mod.rs b/src/arm32/thumb_opcode/mod.rs
index c327696..06a73b5 100644
--- a/src/arm32/thumb_opcode/mod.rs
+++ b/src/arm32/thumb_opcode/mod.rs
@@ -29,7 +29,7 @@ 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.
+ /// This is **not** including `BL` prefixes and suffixes, which instead count as *two* opcodes.
pub const SIZE: u32 = 0x2;
/// Creates a new opcode from a primitive.
diff --git a/src/error/mod.rs b/src/error/mod.rs
index b6c1e24..e561072 100644
--- a/src/error/mod.rs
+++ b/src/error/mod.rs
@@ -19,6 +19,7 @@
// fero General Public License along with Pollex.
// If not, see <https://www.gnu.org/licenses/>.
+use alloc::string::String;
use core::fmt::{Display, Formatter};
/// Wrapping of [`core::result::Result`].
@@ -27,42 +28,63 @@ pub type Result<T> = core::result::Result<T, Error>;
/// A crate error.
#[derive(Clone, Debug)]
pub enum Error {
+ /// The given flag is currently not support.
+ ///
+ /// Some Thumb instructions require the S flag to be on, for example.
+ /// These yield this error if encoded with the S flag is off.
+ IllegalFlag { reason: &'static str },
+
/// The given immediate cannot be encoded.
- IllegalImmediate,
+ IllegalImmediate { reason: &'static str },
/// The given instruction cannot be encoded.
///
/// Even valid instructions are not valid in *every* case.
/// For example, most shifter operands and instruction predicates are **not** allowed in a Thumb context (with exceptions).
- IllegalInstruction,
+ IllegalInstruction { reason: &'static str },
/// The given instruction predicate cannot be encoded.
- IllegalPredicate,
+ IllegalPredicate { reason: &'static str },
/// The given register can currently not be used.
- IllegalRegister,
+ IllegalRegister { reason: &'static str },
+
+ /// The given shifter operand cannot be encoded.
+ IllegalShifter { reason: &'static str },
+
+ /// The provided opcode could not be decoded.
+ InvalidOpcode,
+
+ /// Mnemonic is not known.
+ UnknownMnemonic(String),
/// Register name is not known.
- UnknownRegister,
+ UnknownRegister(String),
}
impl Display for Error {
fn fmt(&self, f: &mut Formatter) -> core::fmt::Result {
use Error::*;
- let message = match *self {
- IllegalImmediate => "illegal immediate value",
+ match *self {
+ IllegalFlag { reason } => write!(f, "illegal flag value: {reason}"),
+
+ IllegalImmediate { reason } => write!(f, "illegal immediate value: {reason}"),
+
+ IllegalInstruction { reason } => write!(f, "illegal instruction: {reason}"),
+
+ IllegalPredicate { reason } => write!(f, "illegal instruction predicate: {reason}"),
- IllegalInstruction => "illegal instruction",
+ IllegalRegister { reason } => write!(f, "register not permitted here: {reason}"),
- IllegalPredicate => "illegal instruction predicate",
+ IllegalShifter { reason } => write!(f, "shifter operand not permitted here: {reason}"),
- IllegalRegister => "register not permitted here",
+ InvalidOpcode => write!(f, "invalid opcode"),
- UnknownRegister => "unknown register",
- };
+ UnknownMnemonic(ref s) => write!(f, "unknown mnemonic `{s}`"),
- f.write_str(message)
+ UnknownRegister(ref s) => write!(f, "unknown register `{s}`"),
+ }
}
}
diff --git a/src/lib.rs b/src/lib.rs
index 83a7b5a..2207359 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -19,17 +19,105 @@
// fero General Public License along with Pollex.
// If not, see <https://www.gnu.org/licenses/>.
-//! Arm instruction manipulator.
+//! [Pollex](https://crates.io/crates/pollex/) is a Rust-written library for manipulating instructions of Arm ISAs.
//!
-//! This library is meant to be used by assemblers and disassembler, emulators, etc.
-//! That is to leverage encoding and decoding of instructions.
+//! # Support
//!
-//! The goal of this project is to fully implement all Arm32 instruction set architectures, and, hopefully, Arm64 architectures as well.
+//! Pollex supports encoding of instructions to both Arm and Thumb on Arm32 targets.
+//! Arm64 support is planned.
+//!
+//! # Usage
+//!
+//! Instructions can be created directly using the [`Instruction`](arm32::Instruction) type:
+//!
+//! ```
+//! use pollex::arm32::{
+//! Instruction,
+//! Predicate,
+//! Register,
+//! Sflag,
+//! Shifter,
+//! };
+//!
+//! // MOVS r0, r1
+//! let instr = Instruction::Move {
+//! predicate: Predicate::Always,
+//! destination: Register::R0,
+//! source: Shifter::from_register(Register::R1),
+//! s: Sflag::On,
+//! };
+//! ```
+//!
+//! Instructions can also be parsed from strings:
+//!
+//! ```
+//! use pollex::arm32::{
+//! Instruction,
+//! Predicate,
+//! Register,
+//! Sflag,
+//! Shifter,
+//! };
+//!
+//! let instr: Instruction = "CPY r0, r1".parse()?;
+//!
+//! // Is equivalent to:
+//!
+//! let instr = Instruction::Move {
+//! predicate: Predicate::Always,
+//! destination: Register::R0,
+//! source: Shifter::from_register(Register::R1),
+//! s: Sflag::Off,
+//! };
+//!
+//! # Ok::<(), Box<dyn std::error::Error>>(())
+//! ```
+//!
+//! But do note that the latter is currently **not** implemented.
+//!
+//! Instructions can be encoded to both Arm and Thumb using the [`InstructionCodec`](arm32::InstructionCodec) type:
+//!
+//! ```
+//! use pollex::arm32::{Instruction, InstructionCodec};
+//!
+//! let instr: Instruction = "BX lr".parse()?;
+//!
+//! let mut codec = InstructionCodec::new();
+//!
+//! let arm_opcode = codec.encode_arm(instr)?;
+//! let thumb_opcode = codec.encode_thumb(instr)?;
+//!
+//! assert_eq!(arm_opcode, 0b11100001_00101111_11111111_00011110);
+//! assert_eq!(thumb_opcode.0, 0b01000111_01110000);
+//! assert_eq!(thumb_opcode.1, None);
+//!
+//! # Ok::<(), Box<dyn std::error::Error>>(())
+//! ```
+//!
+//! # Copyright & Licensing
+//!
+//! Copyright 2024 Gabriel Bjørnager Jensen.
+//!
+//! This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+//!
+//! This program 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 Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
+//!
+//! # Trademarks
+//!
+//! Arm and Thumb are registered trademarks or trademarks of Arm Limited (or its subsidiaries or affiliates).
+
+#![doc(html_logo_url = "https://gitlab.com/bjoernager/pollex/-/raw/master/pollex-monochrome.svg?ref_type=heads")]
#![no_std]
extern crate alloc;
+#[cfg(test)]
+#[doc(hidden)]
+mod test;
+
pub mod arm32;
pub mod arm64;
@@ -43,6 +131,9 @@ pub(in crate) use use_mod;
use_mod!(pub error);
+/// Asserts the given predicate.
+///
+/// In contrast to [`assert`], this macro makes the caller return an error instead of panicking.
#[macro_export]
macro_rules! assert_or_err {
($predicate:expr, $error:expr) => {{
diff --git a/src/arm32/instruction_codec/test.rs b/src/test/arm32/arm_encode.rs
index 0194a33..c16a93f 100644
--- a/src/arm32/instruction_codec/test.rs
+++ b/src/test/arm32/arm_encode.rs
@@ -20,7 +20,7 @@
// If not, see <https://www.gnu.org/licenses/>.
use crate::arm32::{
- Flag,
+ Sflag,
Instruction,
InstructionCodec,
Predicate,
@@ -31,11 +31,11 @@ use crate::arm32::{
use alloc::vec::Vec;
#[test]
-fn test_arm32_encoder() {
+fn test_arm_encode() {
let tree = [
Instruction::BranchLink {
predicate: Predicate::HigherOrSame,
- immediate: 0x1F,
+ source: 0x1F,
},
Instruction::Breakpoint {
@@ -51,7 +51,7 @@ fn test_arm32_encoder() {
predicate: Predicate::Plus,
destination: Register::Pc,
source: Shifter::ArithmeticShiftRightImmediate { source: Register::R3, shift: 0x20 },
- s: Flag::On,
+ s: Sflag::On,
},
];
diff --git a/src/arm32/instruction/test.rs b/src/test/arm32/instruction_display.rs
index d9067e9..4f834ef 100644
--- a/src/arm32/instruction/test.rs
+++ b/src/test/arm32/instruction_display.rs
@@ -21,24 +21,24 @@
use crate::arm32::{
Predicate,
- Flag,
+ Sflag,
Instruction,
Register,
Shifter,
};
-use alloc::format;
+use alloc::string::ToString;
use alloc::vec::Vec;
#[test]
-fn test_arm32_instruction() {
+fn test_instruction_display() {
let tree = [
Instruction::Add {
predicate: Predicate::GreaterThanOrEqual,
destination: Register::R1,
base: Register::R2,
source: Shifter::RotateRightImmediate { source: Register::R3, shift: 0x2 },
- s: Flag::Off,
+ s: Sflag::Off,
},
Instruction::SaturatingSubtract {
@@ -53,7 +53,7 @@ fn test_arm32_instruction() {
destination: Register::R7,
base: Register::R8,
source: Shifter::LogicalShiftLeftImmediate { source: Register::R9, shift: 0x0 },
- s: Flag::On,
+ s: Sflag::On,
},
Instruction::MultiplyAccumulate {
@@ -62,19 +62,41 @@ fn test_arm32_instruction() {
base: Register::Pc,
source: Register::Pc,
shift: Register::Lr,
- s: Flag::Off,
+ s: Sflag::Off,
},
Instruction::Move {
predicate: Predicate::NotEqual,
destination: Register::R0,
source: Shifter::LogicalShiftLeftImmediate { source: Register::Pc, shift: 0x0 },
- s: Flag::Off,
+ s: Sflag::Off,
+ },
+
+ Instruction::ReverseSubtract {
+ predicate: Predicate::Always,
+ destination: Register::R0,
+ base: Register::R0,
+ source: Shifter::Immediate(0x0),
+ s: Sflag::On,
+ },
+
+ Instruction::Move {
+ predicate: Predicate::GreaterThan,
+ destination: Register::R0,
+ source: Shifter::LogicalShiftRightImmediate { source: Register::R7, shift: 0x20 },
+ s: Sflag::On,
+ },
+
+ Instruction::Move {
+ predicate: Predicate::Always,
+ destination: Register::R0,
+ source: Shifter::LogicalShiftLeftImmediate { source: Register::R0, shift: 0x0 },
+ s: Sflag::On,
},
];
let mut displays = Vec::with_capacity(tree.len());
- for instruction in tree { displays.push(format!("{instruction}")) }
+ for instruction in tree { displays.push(instruction.to_string()) }
assert_eq!(
displays,
@@ -83,7 +105,10 @@ fn test_arm32_instruction() {
"QSUBLT r4, r5, r6",
"ORRS r7, r8, r9",
"MLAEQ r0, pc, pc, lr",
- "CPYNE r0, pc"
+ "CPYNE r0, pc",
+ "NEGS r0, r0",
+ "LSRGTS r0, r7, #32",
+ "MOVS r0, r0",
],
);
}
diff --git a/src/test/arm32/mod.rs b/src/test/arm32/mod.rs
new file mode 100644
index 0000000..f7270a9
--- /dev/null
+++ b/src/test/arm32/mod.rs
@@ -0,0 +1,25 @@
+// 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/>.
+
+mod arm_encode;
+mod instruction_display;
+mod thumb_decode;
+mod thumb_encode; \ No newline at end of file
diff --git a/src/test/arm32/thumb_decode.rs b/src/test/arm32/thumb_decode.rs
new file mode 100644
index 0000000..eff61d2
--- /dev/null
+++ b/src/test/arm32/thumb_decode.rs
@@ -0,0 +1,57 @@
+// 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::arm32::{
+ Instruction,
+ InstructionCodec,
+ Predicate,
+ Register,
+};
+
+use alloc::vec::Vec;
+
+#[test]
+fn test_thumb_decode() {
+ let binary = [
+ 0b11011111_10101010,
+ 0b01000111_01110000,
+ ];
+
+ let mut codec = InstructionCodec::new();
+
+ let mut programme = Vec::new();
+ for opcode in binary { programme.push(codec.decode_thumb(opcode.into()).unwrap()) }
+
+ assert_eq!(
+ programme,
+ [
+ Instruction::SoftwareInterrupt {
+ predicate: Predicate::Always,
+ immediate: 0b10101010,
+ },
+
+ Instruction::BranchExchange {
+ predicate: Predicate::Always,
+ source: Register::Lr,
+ },
+ ],
+ )
+}
diff --git a/src/test/arm32/thumb_encode.rs b/src/test/arm32/thumb_encode.rs
new file mode 100644
index 0000000..178b938
--- /dev/null
+++ b/src/test/arm32/thumb_encode.rs
@@ -0,0 +1,74 @@
+// 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::arm32::{
+ Instruction,
+ InstructionCodec,
+ Predicate,
+ Register,
+ Sflag,
+ Shifter,
+};
+
+use alloc::vec::Vec;
+
+#[test]
+fn test_thumb_encode() {
+ let programme = [
+ Instruction::BranchLink {
+ predicate: Predicate::Always,
+ source: 0x08000044,
+ },
+
+ Instruction::And {
+ predicate: Predicate::Always,
+ destination: Register::R0,
+ base: Register::R0,
+ source: Shifter::LogicalShiftLeftImmediate { source: Register::R7, shift: 0x0 },
+ s: Sflag::On,
+ },
+
+ Instruction::Branch {
+ predicate: Predicate::Always,
+ immediate: 0x08000008,
+ },
+ ];
+
+ let mut codec = InstructionCodec::new_at(0x08000000);
+
+ let mut opcodes = Vec::new();
+ for instruction in programme {
+ let opcode = codec.encode_thumb(instruction).unwrap();
+
+ opcodes.push(opcode.0);
+ if let Some(opcode) = opcode.1 { opcodes.push(opcode) };
+ }
+
+ assert_eq!(
+ opcodes,
+ [
+ 0b11110000_00000000,
+ 0b11111000_00100000,
+ 0b01000000_00111000,
+ 0b11100000_00000000,
+ ],
+ )
+}
diff --git a/src/test/arm64/mod.rs b/src/test/arm64/mod.rs
new file mode 100644
index 0000000..8336d6c
--- /dev/null
+++ b/src/test/arm64/mod.rs
@@ -0,0 +1,20 @@
+// 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/>.
diff --git a/src/test/mod.rs b/src/test/mod.rs
new file mode 100644
index 0000000..d2b26ce
--- /dev/null
+++ b/src/test/mod.rs
@@ -0,0 +1,23 @@
+// 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/>.
+
+mod arm32;
+mod arm64; \ No newline at end of file