summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md14
-rw-r--r--Cargo.toml2
-rw-r--r--src/arm32/arm_opcode/mod.rs12
-rw-r--r--src/arm32/flag/mod.rs46
-rw-r--r--src/arm32/instruction/display.rs61
-rw-r--r--src/arm32/instruction/mod.rs175
-rw-r--r--src/arm32/instruction/test.rs67
-rw-r--r--src/arm32/instruction_codec/encode_arm.rs (renamed from src/arm32/instruction/encode_arm.rs)36
-rw-r--r--src/arm32/instruction_codec/mod.rs60
-rw-r--r--src/arm32/instruction_codec/test.rs72
-rw-r--r--src/arm32/mod.rs5
-rw-r--r--src/arm32/shifter/mod.rs44
-rw-r--r--src/arm32/signed/mod.rs128
-rw-r--r--src/arm32/thumb_opcode/mod.rs12
-rw-r--r--src/arm32/unsigned/mod.rs134
15 files changed, 522 insertions, 346 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c070b0d..7f90c2f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,20 @@
This is the changelog of Pollex.
See `"README.md"` for more information.
+## 0.3.0
+
+* Bump minor
+* Add `arm32::InstructionCodec`
+* Remove `arm32::Unsigned` and `arm32::Signed`
+* Add `SIZE` constant to `arm32::ArmOpcode` and `arm32::ThumbOpcode`
+* Implement `From<u32>` and `Into<u32>` for `ArmOpcode`
+* Implement `From<u16>` and `Into<u16>` for `ThumbOpcode`
+* Update documentation
+* Add new Arm32 instructions: `AddCarry`, `And`, `BitClear`, `CountLeadingZeroes`, `CompareNegated` `Compare`, `ExclusiveOr`, `MultiplyAccumulate`, `Multiply`, `InclusiveOr`, `SaturatingAdd`, `SaturatingSubtract`, `Reverse`, `ReverseSubtract`, `ReverseSubtractCarry`, `Subtract`, `SubtractCarry`,
+* Rename `MoveNegated` in `arm32::Instruction` to `MoveNot`
+* Implement `Into<bool>`, `Into<u8>`, `Into<u16>`, `Into<u32>`, `Into<u64>`, `Into<u128>`, and `Into<usize>` for `Flag`
+* Implement `From<bool>` for `Flag`
+
## 0.2.0
* Bump minor
diff --git a/Cargo.toml b/Cargo.toml
index 0d9d8ed..44a8a40 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "pollex"
-version = "0.2.0"
+version = "0.3.0"
authors = ["Gabriel Bjørnager Jensen"]
edition = "2021"
description = "Arm instruction manipulator."
diff --git a/src/arm32/arm_opcode/mod.rs b/src/arm32/arm_opcode/mod.rs
index 52f36aa..8fba39d 100644
--- a/src/arm32/arm_opcode/mod.rs
+++ b/src/arm32/arm_opcode/mod.rs
@@ -27,6 +27,8 @@ use core::fmt::{Debug, Display, Formatter};
pub struct ArmOpcode(u32);
impl ArmOpcode {
+ pub const SIZE: u32 = 0x4;
+
/// Creates a new opcode from a primitive.
#[inline(always)]
#[must_use]
@@ -52,7 +54,17 @@ impl Display for ArmOpcode {
}
}
+impl From<u32> for ArmOpcode {
+ #[inline(always)]
+ fn from(value: u32) -> Self { Self::from_u32(value) }
+}
+
impl PartialEq<u32> for ArmOpcode {
#[inline(always)]
fn eq(&self, other: &u32) -> bool { self.0 == *other }
}
+
+impl From<ArmOpcode> for u32 {
+ #[inline(always)]
+ fn from(value: ArmOpcode) -> Self { value.to_u32() }
+}
diff --git a/src/arm32/flag/mod.rs b/src/arm32/flag/mod.rs
index 33755b7..b24e103 100644
--- a/src/arm32/flag/mod.rs
+++ b/src/arm32/flag/mod.rs
@@ -48,3 +48,49 @@ impl<const C: char> Display for Flag<C> {
Ok(())
}
}
+
+impl<const C: char> From<bool> for Flag<C> {
+ #[inline(always)]
+ fn from(value: bool) -> Self {
+ if value {
+ Self::On
+ } else {
+ Self::Off
+ }
+ }
+}
+
+impl<const C: char> From<Flag<C>> for bool {
+ #[inline(always)]
+ fn from(value: Flag<C>) -> Self { 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<const C: char> From<Flag<C>> for u16 {
+ #[inline(always)]
+ fn from(value: Flag<C>) -> 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<const C: char> From<Flag<C>> for u64 {
+ #[inline(always)]
+ fn from(value: Flag<C>) -> Self { Self::from(value.is_on()) }
+}
+
+impl<const C: char> From<Flag<C>> for u8 {
+ #[inline(always)]
+ fn from(value: Flag<C>) -> Self { Self::from(value.is_on()) }
+}
+
+impl<const C: char> From<Flag<C>> for usize {
+ #[inline(always)]
+ fn from(value: Flag<C>) -> Self { Self::from(value.is_on()) }
+}
diff --git a/src/arm32/instruction/display.rs b/src/arm32/instruction/display.rs
index 9193edb..93f5e32 100644
--- a/src/arm32/instruction/display.rs
+++ b/src/arm32/instruction/display.rs
@@ -31,26 +31,77 @@ impl Display for Instruction {
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}>"),
+ => write!(f, "B{predicate} <#{immediate}>"),
BranchExchange { predicate, register }
=> write!(f, "BX{predicate} {register}"),
BranchLink { predicate, immediate }
- => write!(f, "BL{predicate} <{immediate}>"),
+ => write!(f, "BL{predicate} <#{immediate}>"),
Breakpoint { immediate }
- => write!(f, "BKPT {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}"),
- MoveNegated { predicate, destination, source, s }
+ 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}"),
+ => 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/mod.rs b/src/arm32/instruction/mod.rs
index 8e527d5..7cc3718 100644
--- a/src/arm32/instruction/mod.rs
+++ b/src/arm32/instruction/mod.rs
@@ -23,56 +23,205 @@
mod test;
mod display;
-mod encode_arm;
use crate::arm32::{
Predicate,
Flag,
Register,
Shifter,
- Signed,
- Unsigned,
};
/// An Arm32 instruction.
-#[derive(Clone, Debug, Eq, PartialEq)]
+///
+/// An instruction must be encoded before it can be used by a processor.
+/// This can be done using the [`InstructionCodec`](crate::arm32::InstructionCodec) type.
+///
+/// Do note that these enumerations do not exactly match instructions used in assembly.
+/// For example, the following two lines are completely identical (with respect to the final binary, disregarding optimisations):
+///
+/// ```as
+/// CPY r1, r0
+/// MOV r1, r0
+/// ```
+///
+/// Yet only `MOV` ([`Move`](Instruction::Move)) is provided as a variant.
+/// Similarly, some combinations of operands yield the same results as multiple instructions.
+/// 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.
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Instruction {
Add {
predicate: Predicate,
destination: Register,
base: Register,
source: Shifter,
- s: Flag<'S'> },
+ s: Flag<'S'>,
+ },
+
+ AddCarry {
+ predicate: Predicate,
+ destination: Register,
+ base: Register,
+ source: Shifter,
+ s: Flag<'S'>,
+ },
+
+ And {
+ predicate: Predicate,
+ destination: Register,
+ base: Register,
+ source: Shifter,
+ s: Flag<'S'>,
+ },
+
+ BitClear {
+ predicate: Predicate,
+ destination: Register,
+ base: Register,
+ source: Shifter,
+ s: Flag<'S'>,
+ },
Branch {
predicate: Predicate,
- immediate: Signed },
+ immediate: i32,
+ },
BranchExchange {
predicate: Predicate,
- register: Register },
+ register: Register,
+ },
BranchLink {
predicate: Predicate,
- immediate: Signed
+ immediate: i32,
},
Breakpoint {
- immediate: Unsigned },
+ immediate: u32,
+ },
+
+ CountLeadingZeroes {
+ predicate: Predicate,
+ destination: Register,
+ source: Register,
+ },
+
+ Compare {
+ predicate: Predicate,
+ lhs: Register,
+ rhs: Shifter,
+ },
+
+ CompareNegated {
+ predicate: Predicate,
+ lhs: Register,
+ rhs: Shifter,
+ },
+
+ ExclusiveOr {
+ predicate: Predicate,
+ destination: Register,
+ base: Register,
+ source: Shifter,
+ s: Flag<'S'>,
+ },
+
+ InclusiveOr {
+ predicate: Predicate,
+ destination: Register,
+ base: Register,
+ source: Shifter,
+ s: Flag<'S'>,
+ },
Move {
predicate: Predicate,
destination: Register,
source: Shifter,
- s: Flag<'S'> },
+ s: Flag<'S'>,
+ },
- MoveNegated {
+ MoveNot {
predicate: Predicate,
destination: Register,
source: Shifter,
- s: Flag<'S'> },
+ s: Flag<'S'>,
+ },
+
+ Multiply {
+ predicate: Predicate,
+ destination: Register,
+ base: Register,
+ source: Register,
+ s: Flag<'S'>,
+ },
+
+ MultiplyAccumulate {
+ predicate: Predicate,
+ destination: Register,
+ base: Register,
+ source: Register,
+ shift: Register,
+ s: Flag<'S'>,
+ },
+
+ Reverse {
+ predicate: Predicate,
+ destination: Register,
+ source: Register,
+ },
+
+ ReverseSubtract {
+ predicate: Predicate,
+ destination: Register,
+ base: Register,
+ source: Shifter,
+ s: Flag<'S'>,
+ },
+
+ ReverseSubtractCarry {
+ predicate: Predicate,
+ destination: Register,
+ base: Register,
+ source: Shifter,
+ s: Flag<'S'>,
+ },
+
+ SaturatingAdd {
+ predicate: Predicate,
+ destination: Register,
+ base: Register,
+ source: Register,
+ },
+
+ SaturatingSubtract {
+ predicate: Predicate,
+ destination: Register,
+ base: Register,
+ source: Register,
+ },
SoftwareInterrupt {
predicate: Predicate,
- immediate: Unsigned },
+ immediate: u32,
+ },
+
+ Subtract {
+ predicate: Predicate,
+ destination: Register,
+ base: Register,
+ source: Shifter,
+ s: Flag<'S'>,
+ },
+
+ SubtractCarry {
+ predicate: Predicate,
+ destination: Register,
+ base: Register,
+ source: Shifter,
+ s: Flag<'S'>,
+ },
}
diff --git a/src/arm32/instruction/test.rs b/src/arm32/instruction/test.rs
index 6977d46..6137795 100644
--- a/src/arm32/instruction/test.rs
+++ b/src/arm32/instruction/test.rs
@@ -25,8 +25,6 @@ use crate::arm32::{
Instruction,
Register,
Shifter,
- Signed,
- Unsigned,
};
use alloc::format;
@@ -35,56 +33,49 @@ use alloc::vec::Vec;
#[test]
fn test_arm32_instruction() {
let tree = [
- Instruction::BranchLink {
- predicate: Predicate::HigherOrSame,
- immediate: Signed::new(0x1F),
+ Instruction::Add {
+ predicate: Predicate::GreaterThanOrEqual,
+ destination: Register::R1,
+ base: Register::R2,
+ source: Shifter::RotateRightImmediate { source: Register::R3, shift: 0x2 },
+ s: Flag::Off,
},
- Instruction::Breakpoint {
- immediate: Unsigned::new(0x45),
+ Instruction::SaturatingSubtract {
+ predicate: Predicate::LessThan,
+ destination: Register::R4,
+ base: Register::R5,
+ source: Register::R6,
},
- Instruction::SoftwareInterrupt {
- predicate: Predicate::Always,
- immediate: Unsigned::new(0x54),
+ Instruction::InclusiveOr {
+ predicate: Predicate::Always,
+ destination: Register::R7,
+ base: Register::R8,
+ source: Shifter::LogicalShiftLeftImmediate { source: Register::Sb, shift: 0x0 },
+ s: Flag::On,
},
- Instruction::Move {
- predicate: Predicate::Plus,
- destination: Register::Pc,
- source: Shifter::ArithmeticShiftRightImmediate { source: Register::R3, shift: Unsigned::new(0x20) },
- s: Flag::On,
+ Instruction::MultiplyAccumulate {
+ predicate: Predicate::Equal,
+ destination: Register::R0,
+ base: Register::Pc,
+ source: Register::Pc,
+ shift: Register::Lr,
+ s: Flag::Off,
},
];
let mut displays = Vec::with_capacity(tree.len());
- let mut opcodes = Vec::with_capacity(tree.len());
-
- for instruction in tree {
- displays.push(format!("{instruction}"));
- opcodes.push(instruction.encode_arm().unwrap());
- }
+ for instruction in tree { displays.push(format!("{instruction}")) }
assert_eq!(
displays,
[
- "BLHS <#+31>",
- "BKPT #69",
- "SWI #84",
- "MOVPLS pc, r3, ASR #32",
+ "ADDGE r1, r2, r3, ROR #2",
+ "QSUBLT r4, r5, r6",
+ "ORRS r7, r8, sb",
+ "MLAEQ r0, pc, pc, lr",
],
);
-
- assert_eq!(
- opcodes,
- [
- 0b00101010_00000000_00000000_00000000,
- 0b11100001_00100000_00000100_01110101,
- 0b11101111_00000000_00000000_01010100,
- 0b01010001_10110000_11110000_01000011,
- ],
- )
}
-
-// 01010001101100001111000001000011
-// 01010001101100001111000001000011
diff --git a/src/arm32/instruction/encode_arm.rs b/src/arm32/instruction_codec/encode_arm.rs
index a24ec28..73dc3d9 100644
--- a/src/arm32/instruction/encode_arm.rs
+++ b/src/arm32/instruction_codec/encode_arm.rs
@@ -20,15 +20,25 @@
// If not, see <https://www.gnu.org/licenses/>.
use crate::Result;
-use crate::arm32::{ArmOpcode, Instruction, Shifter};
-
-impl Instruction {
- pub fn encode_arm(&self) -> Result<ArmOpcode> {
+use crate::arm32::{
+ ArmOpcode,
+ Instruction,
+ InstructionCodec,
+ Shifter,
+};
+
+impl InstructionCodec {
+ /// Encodes the given Arm instruction.
+ ///
+ /// # Errors
+ ///
+ /// If the operands of the provided instruction cannot be encoded in the given combination, or are incompatible with the mnemonic, an error is returned.
+ pub fn encode_arm(&mut self, instruction: Instruction) -> Result<ArmOpcode> {
use Instruction::*;
let mut opcode = 0b00000000_00000000_00000000_00000000;
- match *self {
+ match instruction {
Branch { predicate, .. } => {
opcode |= 0b00001011_00000000_00000000_00000000;
opcode |= (predicate as u32) << 0x1C;
@@ -37,19 +47,18 @@ impl Instruction {
BranchLink { predicate, .. } => {
opcode |= 0b00001010_00000000_00000000_00000000;
opcode |= (predicate as u32) << 0x1C;
- //opcode |= immediate.to_bits::<0x18>() >> 0x8
},
Breakpoint { immediate } => {
opcode |= 0b11100001_00100000_00000000_01110000;
- opcode |= immediate.get() & 0b00000000_00000000_00000000_00001111;
- opcode |= (immediate.get() & 0b00000000_00000000_11111111_11110000) << 0x4;
+ opcode |= immediate & 0b00000000_00000000_00000000_00001111;
+ opcode |= (immediate & 0b00000000_00000000_11111111_11110000) << 0x4;
},
Move { predicate, destination, source, s } => {
opcode |= 0b00000001_10100000_00000000_00000000;
opcode |= (destination as u32) << 0xC;
- opcode |= u32::from(s.is_on()) << 0x14;
+ opcode |= u32::from(s) << 0x14;
opcode |= (predicate as u32) << 0x1C;
opcode = add_shifter(opcode, source);
@@ -57,14 +66,15 @@ impl Instruction {
SoftwareInterrupt { predicate, immediate } => {
opcode |= 0b00001111_00000000_00000000_00000000;
- opcode |= immediate.get() & 0b00000000_11111111_11111111_11111111;
+ opcode |= immediate & 0b00000000_11111111_11111111_11111111;
opcode |= (predicate as u32) << 0x1C;
},
_ => todo!(),
}
- Ok(ArmOpcode::from_u32(opcode))
+ self.address += ArmOpcode::SIZE;
+ Ok(opcode.into())
}
}
@@ -86,7 +96,7 @@ fn add_shifter(mut opcode: u32, shifter: Shifter) -> u32 {
opcode |= source as u32;
opcode |= code << 0x5;
- opcode |= shift.get() << 0x7;
+ opcode |= shift << 0x7;
},
Shifter::RotateRightExtend { .. } => {
@@ -113,8 +123,6 @@ fn add_shifter(mut opcode: u32, shifter: Shifter) -> u32 {
Shifter::Immediate { immediate } => {
let (immediate, rotate) = if immediate <= 0xFF {
- let immediate = immediate.get();
-
(immediate, 0x00)
} else {
todo!()
diff --git a/src/arm32/instruction_codec/mod.rs b/src/arm32/instruction_codec/mod.rs
new file mode 100644
index 0000000..f8d2a8c
--- /dev/null
+++ b/src/arm32/instruction_codec/mod.rs
@@ -0,0 +1,60 @@
+// 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/>.
+
+#[cfg(test)]
+mod test;
+
+mod encode_arm;
+
+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`.
+///
+/// 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`).
+///
+/// # Rationale
+///
+/// This structure is useful for the few functions that encode differently according to their position.
+///
+/// For example `B 0x08000000` encodes the immediate (in this case `0x08000000`) as an offset from `PC`.
+/// This is despite taking an address as its operand in assembly.
+#[derive(Clone, Debug)]
+pub struct InstructionCodec {
+ address: Wrapping<u32>,
+}
+
+impl InstructionCodec {
+ /// 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) }
+ }
+}
+
+impl Default for InstructionCodec {
+ #[inline(always)]
+ fn default() -> Self { Self::new_at(0x00000000) }
+}
diff --git a/src/arm32/instruction_codec/test.rs b/src/arm32/instruction_codec/test.rs
new file mode 100644
index 0000000..0194a33
--- /dev/null
+++ b/src/arm32/instruction_codec/test.rs
@@ -0,0 +1,72 @@
+// 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::{
+ Flag,
+ Instruction,
+ InstructionCodec,
+ Predicate,
+ Register,
+ Shifter,
+};
+
+use alloc::vec::Vec;
+
+#[test]
+fn test_arm32_encoder() {
+ let tree = [
+ Instruction::BranchLink {
+ predicate: Predicate::HigherOrSame,
+ immediate: 0x1F,
+ },
+
+ Instruction::Breakpoint {
+ immediate: 0x45,
+ },
+
+ Instruction::SoftwareInterrupt {
+ predicate: Predicate::Always,
+ immediate: 0x54,
+ },
+
+ Instruction::Move {
+ predicate: Predicate::Plus,
+ destination: Register::Pc,
+ source: Shifter::ArithmeticShiftRightImmediate { source: Register::R3, shift: 0x20 },
+ s: Flag::On,
+ },
+ ];
+
+ let mut codec = InstructionCodec::new_at(0x08000000);
+
+ let mut opcodes = Vec::new();
+ for instruction in tree { opcodes.push(codec.encode_arm(instruction).unwrap()) }
+
+ assert_eq!(
+ opcodes,
+ [
+ 0b00101010_00000000_00000000_00000000,
+ 0b11100001_00100000_00000100_01110101,
+ 0b11101111_00000000_00000000_01010100,
+ 0b01010001_10110000_11110000_01000011,
+ ],
+ )
+}
diff --git a/src/arm32/mod.rs b/src/arm32/mod.rs
index bfc7a5d..3451841 100644
--- a/src/arm32/mod.rs
+++ b/src/arm32/mod.rs
@@ -25,11 +25,10 @@
use crate::use_mod;
use_mod!(pub arm_opcode);
-use_mod!(pub predicate);
use_mod!(pub flag);
use_mod!(pub instruction);
+use_mod!(pub instruction_codec);
+use_mod!(pub predicate);
use_mod!(pub register);
use_mod!(pub shifter);
-use_mod!(pub signed);
use_mod!(pub thumb_opcode);
-use_mod!(pub unsigned);
diff --git a/src/arm32/shifter/mod.rs b/src/arm32/shifter/mod.rs
index e5da07b..f658c2b 100644
--- a/src/arm32/shifter/mod.rs
+++ b/src/arm32/shifter/mod.rs
@@ -19,30 +19,50 @@
// fero General Public License along with Pollex.
// If not, see <https://www.gnu.org/licenses/>.
-use crate::arm32::{Register, Unsigned};
+use crate::arm32::Register;
use core::fmt::Display;
/// A shifter operand.
+///
+/// Some Arm instructions take these to minimise instruction usage.
+/// For example, the following code:
+///
+/// ```as
+/// LSL r1, r1, #2
+/// ADD r0, r1
+/// ```
+///
+/// is functionally equivalent to:
+///
+/// ```as
+/// ADD r0, r1, LSL #2
+/// ```
+///
+/// In fact, the first example will encode identically to the following:
+///
+/// ```as
+/// MOV r1, r1, LSL #2
+/// ```
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Shifter {
- ArithmeticShiftRightImmediate { source: Register, shift: Unsigned },
+ ArithmeticShiftRightImmediate { source: Register, shift: u32 },
ArithmeticShiftRightRegister { source: Register, shift: Register },
- Immediate { immediate: Unsigned },
+ Immediate { immediate: u32 },
- LogicalShiftLeftImmediate { source: Register, shift: Unsigned },
+ LogicalShiftLeftImmediate { source: Register, shift: u32 },
LogicalShiftLeftRegister { source: Register, shift: Register },
- LogicalShiftRightImmediate { source: Register, shift: Unsigned },
+ LogicalShiftRightImmediate { source: Register, shift: u32 },
LogicalShiftRightRegister { source: Register, shift: Register },
RotateRightExtend { source: Register },
- RotateRightImmediate { source: Register, shift: Unsigned },
+ RotateRightImmediate { source: Register, shift: u32 },
RotateRightRegister { source: Register, shift: Register },
}
@@ -53,7 +73,7 @@ impl Display for Shifter {
match *self {
ArithmeticShiftRightImmediate { source, shift } => {
- write!(f, "{source}, ASR {shift}")
+ write!(f, "{source}, ASR #{shift}")
},
ArithmeticShiftRightRegister { source, shift } => {
@@ -64,8 +84,12 @@ impl Display for Shifter {
write!(f, "#{immediate}<")
},
+ LogicalShiftLeftImmediate { source, shift: 0x0 } => {
+ write!(f, "{source}")
+ },
+
LogicalShiftLeftImmediate { source, shift } => {
- write!(f, "{source}, LSL {shift}")
+ write!(f, "{source}, LSL #{shift}")
},
LogicalShiftLeftRegister { source, shift } => {
@@ -73,7 +97,7 @@ impl Display for Shifter {
},
LogicalShiftRightImmediate { source, shift } => {
- write!(f, "{source}, LSR {shift}")
+ write!(f, "{source}, LSR #{shift}")
},
LogicalShiftRightRegister { source, shift } => {
@@ -85,7 +109,7 @@ impl Display for Shifter {
},
RotateRightImmediate { source, shift } => {
- write!(f, "{source}, ROR {shift}")
+ write!(f, "{source}, ROR #{shift}")
},
RotateRightRegister { source, shift } => {
diff --git a/src/arm32/signed/mod.rs b/src/arm32/signed/mod.rs
deleted file mode 100644
index bd07f43..0000000
--- a/src/arm32/signed/mod.rs
+++ /dev/null
@@ -1,128 +0,0 @@
-// 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::Unsigned;
-
-use core::cmp::Ordering;
-use core::fmt::{Display, Formatter};
-use core::ops::{Add, Div, Mul, Sub};
-
-/// A signed word.
-///
-/// This type defines the [`Add`], [`Sub`], [`Mul`], and [`Div`] traits for `Self` and [`i32`].
-/// Internally, these implementations use wrapping arithemtic.
-#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
-#[repr(transparent)]
-pub struct Signed(i32);
-
-impl Signed {
- /// Constructs a new word.
- #[inline(always)]
- #[must_use]
- pub const fn new(value: i32) -> Self { Self(value) }
-
- /// Retrieves the word's value.
- #[inline(always)]
- #[must_use]
- pub const fn get(self) -> i32 { self.0 }
-
- /// Sets the value of the word.
- #[inline(always)]
- pub fn set(&mut self, value: i32) { self.0 = value }
-
- /// Reinterprets the word as unsigned.
- #[inline(always)]
- #[must_use]
- pub const fn as_unsigned(self) -> Unsigned { Unsigned::new(self.0 as u32) }
-}
-
-impl Add<Self> for Signed {
- type Output = Self;
-
- #[inline(always)]
- fn add(self, rhs: Self) -> Self::Output { self + rhs.get() }
-}
-
-impl Add<i32> for Signed {
- type Output = Self;
-
- #[inline(always)]
- fn add(self, rhs: i32) -> Self::Output { Self(self.0.wrapping_add(rhs)) }
-}
-
-impl Display for Signed {
- #[inline(always)]
- fn fmt(&self, f: &mut Formatter) -> core::fmt::Result {
- write!(f, "#{:+}", self.0)
- }
-}
-
-impl Div<Self> for Signed {
- type Output = Self;
-
- #[inline(always)]
- fn div(self, rhs: Self) -> Self::Output { self / rhs.get() }
-}
-
-impl Div<i32> for Signed {
- type Output = Self;
-
- #[inline(always)]
- fn div(self, rhs: i32) -> Self::Output { Self(self.0.wrapping_div(rhs)) }
-}
-
-impl Mul<Self> for Signed {
- type Output = Self;
-
- #[inline(always)]
- fn mul(self, rhs: Self) -> Self::Output { self * rhs.get() }
-}
-
-impl Mul<i32> for Signed {
- type Output = Self;
-
- #[inline(always)]
- fn mul(self, rhs: i32) -> Self::Output { Self(self.0.wrapping_mul(rhs)) }
-}
-
-impl Sub<Self> for Signed {
- type Output = Self;
-
- #[inline(always)]
- fn sub(self, rhs: Self) -> Self::Output { self - rhs.get() }
-}
-
-impl Sub<i32> for Signed {
- type Output = Self;
-
- #[inline(always)]
- fn sub(self, rhs: i32) -> Self::Output { Self(self.0.wrapping_sub(rhs)) }
-}
-
-impl PartialEq<i32> for Signed {
- #[inline(always)]
- fn eq(&self, other: &i32) -> bool { self.0 == *other }
-}
-
-impl PartialOrd<i32> for Signed {
- #[inline(always)]
- fn partial_cmp(&self, other: &i32) -> Option<Ordering> { self.0.partial_cmp(other) }
-}
diff --git a/src/arm32/thumb_opcode/mod.rs b/src/arm32/thumb_opcode/mod.rs
index 8e603f8..1bd9529 100644
--- a/src/arm32/thumb_opcode/mod.rs
+++ b/src/arm32/thumb_opcode/mod.rs
@@ -27,6 +27,8 @@ use core::fmt::{Debug, Display, Formatter};
pub struct ThumbOpcode(u16);
impl ThumbOpcode {
+ pub const SIZE: u32 = 0x2;
+
/// Creates a new opcode from a primitive.
#[inline(always)]
#[must_use]
@@ -52,7 +54,17 @@ impl Display for ThumbOpcode {
}
}
+impl From<u16> for ThumbOpcode {
+ #[inline(always)]
+ fn from(value: u16) -> Self { Self::from_u16(value) }
+}
+
impl PartialEq<u16> for ThumbOpcode {
#[inline(always)]
fn eq(&self, other: &u16) -> bool { self.0 == *other }
}
+
+impl From<ThumbOpcode> for u16 {
+ #[inline(always)]
+ fn from(value: ThumbOpcode) -> Self { value.to_u16() }
+}
diff --git a/src/arm32/unsigned/mod.rs b/src/arm32/unsigned/mod.rs
deleted file mode 100644
index b195bf0..0000000
--- a/src/arm32/unsigned/mod.rs
+++ /dev/null
@@ -1,134 +0,0 @@
-// 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::Signed;
-
-use core::cmp::Ordering;
-use core::fmt::{Display, Formatter};
-use core::ops::{Add, Div, Mul, Sub};
-
-/// An unsigned word.
-///
-/// This type defines the [`Add`], [`Sub`], [`Mul`], and [`Div`] traits for `Self` and [`u32`].
-/// Internally, these implementations use wrapping arithemtic.
-#[derive(Clone, Copy, Debug, Eq, PartialEq)]
-#[repr(transparent)]
-pub struct Unsigned(u32);
-
-impl Unsigned {
- /// Constructs a new word.
- #[inline(always)]
- #[must_use]
- pub const fn new(value: u32) -> Self { Self(value) }
-
- /// Retrieves the word's value.
- #[inline(always)]
- #[must_use]
- pub const fn get(self) -> u32 { self.0 }
-
- /// Sets the value of the word.
- #[inline(always)]
- pub fn set(&mut self, value: u32) { self.0 = value }
-
- /// Reinterprets the word as signed.
- #[allow(clippy::cast_possible_wrap)]
- #[inline(always)]
- #[must_use]
- pub const fn as_signed(self) -> Signed { Signed::new(self.0 as i32) }
-
- /// Adds a signed offset to the word.
- #[inline(always)]
- #[must_use]
- pub const fn offset(self, value: i32) -> Self { Self(self.get().wrapping_add_signed(value)) }
-}
-
-impl Add<Self> for Unsigned {
- type Output = Self;
-
- #[inline(always)]
- fn add(self, rhs: Self) -> Self::Output { self + rhs.get() }
-}
-
-impl Add<u32> for Unsigned {
- type Output = Self;
-
- #[inline(always)]
- fn add(self, rhs: u32) -> Self::Output { Self(self.0.wrapping_add(rhs)) }
-}
-
-impl Display for Unsigned {
- #[inline(always)]
- fn fmt(&self, f: &mut Formatter) -> core::fmt::Result {
- write!(f, "#{}", self.0)
- }
-}
-
-impl Div<Self> for Unsigned {
- type Output = Self;
-
- #[inline(always)]
- fn div(self, rhs: Self) -> Self::Output { self / rhs.get() }
-}
-
-impl Div<u32> for Unsigned {
- type Output = Self;
-
- #[inline(always)]
- fn div(self, rhs: u32) -> Self::Output { Self(self.0.wrapping_div(rhs)) }
-}
-
-impl Mul<Self> for Unsigned {
- type Output = Self;
-
- #[inline(always)]
- fn mul(self, rhs: Self) -> Self::Output { self * rhs.get() }
-}
-
-impl Mul<u32> for Unsigned {
- type Output = Self;
-
- #[inline(always)]
- fn mul(self, rhs: u32) -> Self::Output { Self(self.0.wrapping_mul(rhs)) }
-}
-
-impl PartialEq<u32> for Unsigned {
- #[inline(always)]
- fn eq(&self, other: &u32) -> bool { self.0 == *other }
-}
-
-impl PartialOrd<u32> for Unsigned {
- #[inline(always)]
- fn partial_cmp(&self, other: &u32) -> Option<Ordering> { self.0.partial_cmp(other) }
-}
-
-impl Sub<Self> for Unsigned {
- type Output = Self;
-
- #[inline(always)]
- fn sub(self, rhs: Self) -> Self::Output { self - rhs.get() }
-}
-
-impl Sub<u32> for Unsigned {
- type Output = Self;
-
- #[inline(always)]
- fn sub(self, rhs: u32) -> Self::Output { Self(self.0.wrapping_sub(rhs)) }
-}