summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md22
-rw-r--r--Cargo.toml2
-rw-r--r--pollex-wordmark.svg47
-rw-r--r--pollex.svg33
-rw-r--r--src/arm32/arm_opcode/mod.rs1
-rw-r--r--src/arm32/flag/mod.rs7
-rw-r--r--src/arm32/instruction/display.rs254
-rw-r--r--src/arm32/instruction/test.rs12
-rw-r--r--src/arm32/instruction_codec/encode_arm.rs87
-rw-r--r--src/arm32/instruction_codec/encode_thumb.rs92
-rw-r--r--src/arm32/instruction_codec/mod.rs3
-rw-r--r--src/arm32/register/mod.rs111
-rw-r--r--src/arm32/shifter/mod.rs6
-rw-r--r--src/arm32/thumb_opcode/mod.rs3
-rw-r--r--src/arm64/mod.rs (renamed from src/arch/mod.rs)9
-rw-r--r--src/error/mod.rs28
-rw-r--r--src/lib.rs9
17 files changed, 596 insertions, 130 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7f90c2f..b98a238 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,28 @@
This is the changelog of Pollex.
See `"README.md"` for more information.
+## 0.4.0
+
+* Bump minor version
+* Add logo
+* Add more errors
+* Add macro `assert_or_err`
+* Document errors
+* Remove `Arch`
+
+### Arm32
+
+* Add `encode_thumb` method to `InstructionCodec`
+* Add `is_low` and `is_high` methods to `Register`
+* Implement `FromStr` for `Register`
+* Rename `Sb`, `Sl`, and `Ip` in `Register` to `R9`, `R10`, and `R12`
+* Display instruction synonyms
+* Document `Flag`
+
+### Arm64
+
+* Add module
+
## 0.3.0
* Bump minor
diff --git a/Cargo.toml b/Cargo.toml
index 44a8a40..8c024e7 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "pollex"
-version = "0.3.0"
+version = "0.4.0"
authors = ["Gabriel Bjørnager Jensen"]
edition = "2021"
description = "Arm instruction manipulator."
diff --git a/pollex-wordmark.svg b/pollex-wordmark.svg
new file mode 100644
index 0000000..4add967
--- /dev/null
+++ b/pollex-wordmark.svg
@@ -0,0 +1,47 @@
+<!-- Copyright 2024 Gabriel Bjørnager Jensen. -->
+
+<!--
+ This work is licensed under Creative Commons At-
+ tribution-NoDerivatives 4.0 International. To
+ view a copy of this license, visit:
+
+ <https://creativecommons.org/licenses/by-nd/4.0/>
+-->
+
+<svg height="112" width="256" xmlns="http://www.w3.org/2000/svg">
+ <!-- gradients: -->
+
+ <!-- clips: -->
+
+ <!-- masks: -->
+
+ <mask id="z">
+ <polygon fill="white" points="16,16 68,16 68,40 32,40 32,48 16,48" />
+ <circle cx="68" cy="28" fill="white" r="12" />
+
+ <polygon fill="black" points="32,24 60,24 60,32 32,32" />
+ <circle cx="60" cy="28" fill="black" r="4" />
+
+ <polygon fill="white" points="104,16 152,16 160,24 160,40 152,48 104,48 96,40 96,24" />
+ <circle cx="104" cy="24" fill="white" r="8" />
+ <circle cx="152" cy="24" fill="white" r="8" />
+ <circle cx="152" cy="40" fill="white" r="8" />
+ <circle cx="104" cy="40" fill="white" r="8" />
+
+ <polygon fill="black" points="112,24 144,24 144,40 112,40" />
+
+ <polygon fill="white" points="176,16 192,16 192,40 240,40 240,48 176,48" />
+
+ <polygon fill="white" points="16,64 32,64 32,88 80,88 80,96 16,96" />
+
+ <polygon fill="white" points="96,64 160,64 160,72 112,72 112,76 144,76 144,84 112,84 112,88 160,88 160,96 96,96" />
+
+ <polygon fill="white" points="176,64 192,64 240,96 224,96" />
+ <polygon fill="white" points="224,64 240,64 192,96 176,96" />
+ </mask>
+
+ <!-- fills: -->
+
+ <rect fill="#4D4084" height="112" width="496" x="0" y="0" /> <!-- 7672 C -->
+ <rect fill="#FFFFFF" mask="url(#z)" height="112" width="496" x="0" y="0" />
+</svg>
diff --git a/pollex.svg b/pollex.svg
new file mode 100644
index 0000000..1c7d050
--- /dev/null
+++ b/pollex.svg
@@ -0,0 +1,33 @@
+<!-- Copyright 2024 Gabriel Bjørnager Jensen. -->
+
+<!--
+ This work is licensed under Creative Commons At-
+ tribution-NoDerivatives 4.0 International. To
+ view a copy of this license, visit:
+
+ <https://creativecommons.org/licenses/by-nd/4.0/>
+-->
+
+<svg height="96" width="96" xmlns="http://www.w3.org/2000/svg">
+ <!-- gradients: -->
+
+ <!-- clips: -->
+
+ <!-- masks: -->
+
+ <mask id="z">
+ <polygon fill="white" points="16,16 71,16 71,34 32,34 32,16 32,40 16,40" />
+ <circle cx="71" cy="25" fill="white" r="9" />
+
+ <polygon fill="black" points="32,22 61,22 61,28 32,28" />
+ <circle cx="61" cy="25" fill="black" r="3" />
+
+ <polygon fill="white" points="16,56 32,56 80,80 64,80" />
+ <polygon fill="white" points="64,56 80,56 32,80 16,80" />
+ </mask>
+
+ <!-- fills: -->
+
+ <rect fill="#4D4084" mask="" height="96" width="96" x="0" y="0" /> <!-- 7672 C -->
+ <rect fill="#FFFFFF" mask="url(#z)" height="96" width="96" x="0" y="0" />
+</svg>
diff --git a/src/arm32/arm_opcode/mod.rs b/src/arm32/arm_opcode/mod.rs
index 8fba39d..9791856 100644
--- a/src/arm32/arm_opcode/mod.rs
+++ b/src/arm32/arm_opcode/mod.rs
@@ -27,6 +27,7 @@ use core::fmt::{Debug, Display, Formatter};
pub struct ArmOpcode(u32);
impl ArmOpcode {
+ /// The size of any Arm opcode, in bytes.
pub const SIZE: u32 = 0x4;
/// Creates a new opcode from a primitive.
diff --git a/src/arm32/flag/mod.rs b/src/arm32/flag/mod.rs
index b24e103..fe564e4 100644
--- a/src/arm32/flag/mod.rs
+++ b/src/arm32/flag/mod.rs
@@ -19,6 +19,11 @@
// fero General Public License along with Pollex.
// If not, see <https://www.gnu.org/licenses/>.
+// TODO: This type is usefule for easily being for-
+// matted, but is otherwise quite akwardly imple-
+// mented (see the constant generic). Could we re-
+// it?
+
use core::fmt::Display;
/// A flag.
@@ -30,10 +35,12 @@ pub enum Flag<const C: char> {
}
impl<const C: char> Flag<C> {
+ /// Checks if the flag is off.
#[inline(always)]
#[must_use]
pub const fn is_off(self) -> bool { self as u8 == Self::Off as u8 }
+ /// Checks if the flag is on.
#[inline(always)]
#[must_use]
pub const fn is_on(self) -> bool { self as u8 == Self::On as u8 }
diff --git a/src/arm32/instruction/display.rs b/src/arm32/instruction/display.rs
index 93f5e32..f610782 100644
--- a/src/arm32/instruction/display.rs
+++ b/src/arm32/instruction/display.rs
@@ -19,7 +19,7 @@
// fero General Public License along with Pollex.
// If not, see <https://www.gnu.org/licenses/>.
-use crate::arm32::Instruction;
+use crate::arm32::{Flag, Instruction, Shifter};
use core::fmt::Display;
@@ -28,80 +28,184 @@ impl Display for Instruction {
use Instruction::*;
match *self {
- Add { predicate, destination, base, source, s }
- => write!(f, "ADD{predicate}{s} {destination}, {base}, {source}"),
-
- AddCarry { predicate, destination, base, source, s }
- => write!(f, "ADC{predicate}{s} {destination}, {base}, {source}"),
-
- And { predicate, destination, base, source, s }
- => write!(f, "AND{predicate}{s} {destination}, {base}, {source}"),
-
- BitClear { predicate, destination, base, source, s }
- => write!(f, "BIC{predicate}{s} {destination}, {base}, {source}"),
-
- Branch { predicate, immediate }
- => write!(f, "B{predicate} <#{immediate}>"),
-
- BranchExchange { predicate, register }
- => write!(f, "BX{predicate} {register}"),
-
- BranchLink { predicate, immediate }
- => write!(f, "BL{predicate} <#{immediate}>"),
-
- Breakpoint { immediate }
- => write!(f, "BKPT #{immediate}"),
-
- CountLeadingZeroes { predicate, destination, source }
- => write!(f, "CLZ{predicate} {destination}, {source}"),
-
- Compare { predicate, lhs, rhs }
- => write!(f, "CMP{predicate} {lhs}, {rhs}"),
-
- CompareNegated { predicate, lhs, rhs }
- => write!(f, "CMN{predicate} {lhs}, {rhs}"),
-
- ExclusiveOr { predicate, destination, base, source, s }
- => write!(f, "EOR{predicate}{s} {destination}, {base}, {source}"),
-
- InclusiveOr { predicate, destination, base, source, s }
- => write!(f, "ORR{predicate}{s} {destination}, {base}, {source}"),
-
- Move { predicate, destination, source, s }
- => write!(f, "MOV{predicate}{s} {destination}, {source}"),
-
- MoveNot { predicate, destination, source, s }
- => write!(f, "MVN{predicate}{s} {destination}, {source}"),
-
- Multiply { predicate, destination, base, source, s }
- => write!(f, "MUL{predicate}{s} {destination}, {base}, {source}"),
-
- MultiplyAccumulate { predicate, destination, base, source, shift, s }
- => write!(f, "MLA{predicate}{s} {destination}, {base}, {source}, {shift}"),
-
- Reverse { predicate, destination, source }
- => write!(f, "REV{predicate} {destination}, {source}"),
-
- ReverseSubtract { predicate, destination, base, source, s }
- => write!(f, "RSB{predicate}{s} {destination}, {base}, {source}"),
-
- ReverseSubtractCarry { predicate, destination, base, source, s }
- => write!(f, "RSC{predicate}{s} {destination}, {base}, {source}"),
-
- SaturatingAdd { predicate, destination, base, source }
- => write!(f, "QADD{predicate} {destination}, {base}, {source}"),
-
- SaturatingSubtract { predicate, destination, base, source }
- => write!(f, "QSUB{predicate} {destination}, {base}, {source}"),
-
- SoftwareInterrupt { predicate, immediate }
- => write!(f, "SWI{predicate} #{immediate}"),
-
- Subtract { predicate, destination, base, source, s }
- => write!(f, "SUB{predicate}{s} {destination}, {base}, {source}"),
-
- SubtractCarry { predicate, destination, base, source, s }
- => write!(f, "SBC{predicate}{s} {destination}, {base}, {source}"),
+ Add {
+ predicate,
+ destination,
+ base,
+ source,
+ s,
+ } => write!(f, "ADD{predicate}{s} {destination}, {base}, {source}"),
+
+ AddCarry {
+ predicate,
+ destination,
+ base,
+ source,
+ s,
+ } => write!(f, "ADC{predicate}{s} {destination}, {base}, {source}"),
+
+ And {
+ predicate,
+ destination,
+ base,
+ source,
+ s,
+ } => write!(f, "AND{predicate}{s} {destination}, {base}, {source}"),
+
+ BitClear {
+ predicate,
+ destination,
+ base,
+ source,
+ s,
+ } => write!(f, "BIC{predicate}{s} {destination}, {base}, {source}"),
+
+ Branch {
+ predicate,
+ immediate,
+ } => write!(f, "B{predicate} <#{immediate}>"),
+
+ BranchExchange {
+ predicate,
+ register,
+ } => write!(f, "BX{predicate} {register}"),
+
+ BranchLink {
+ predicate,
+ immediate,
+ } => write!(f, "BL{predicate} <#{immediate}>"),
+
+ Breakpoint {
+ immediate } => write!(f, "BKPT #{immediate}"),
+
+ CountLeadingZeroes {
+ predicate,
+ destination,
+ source,
+ } => write!(f, "CLZ{predicate} {destination}, {source}"),
+
+ Compare {
+ predicate,
+ lhs,
+ rhs,
+ } => write!(f, "CMP{predicate} {lhs}, {rhs}"),
+
+ CompareNegated {
+ predicate,
+ lhs,
+ rhs,
+ } => write!(f, "CMN{predicate} {lhs}, {rhs}"),
+
+ ExclusiveOr {
+ predicate,
+ destination,
+ base,
+ source,
+ s,
+ } => write!(f, "EOR{predicate}{s} {destination}, {base}, {source}"),
+
+ InclusiveOr {
+ predicate,
+ destination,
+ base,
+ source,
+ s,
+ } => write!(f, "ORR{predicate}{s} {destination}, {base}, {source}"),
+
+ Move {
+ predicate,
+ destination,
+ source: Shifter::LogicalShiftLeftImmediate { source, shift: 0x0 },
+ s: Flag::Off,
+ } => write!(f, "CPY{predicate} {destination}, {source}"),
+
+ Move {
+ predicate,
+ destination,
+ source,
+ s,
+ } => write!(f, "MOV{predicate}{s} {destination}, {source}"),
+
+ MoveNot {
+ predicate,
+ destination,
+ source,
+ s,
+ } => write!(f, "MVN{predicate}{s} {destination}, {source}"),
+
+ Multiply {
+ predicate,
+ destination,
+ base,
+ source,
+ s,
+ } => write!(f, "MUL{predicate}{s} {destination}, {base}, {source}"),
+
+ MultiplyAccumulate {
+ predicate,
+ destination,
+ base,
+ source,
+ shift,
+ s,
+ } => write!(f, "MLA{predicate}{s} {destination}, {base}, {source}, {shift}"),
+
+ Reverse {
+ predicate,
+ destination,
+ source,
+ } => write!(f, "REV{predicate} {destination}, {source}"),
+
+ ReverseSubtract {
+ predicate,
+ destination,
+ base,
+ source,
+ s,
+ } => write!(f, "RSB{predicate}{s} {destination}, {base}, {source}"),
+
+ ReverseSubtractCarry {
+ predicate,
+ destination,
+ base,
+ source,
+ s,
+ } => write!(f, "RSC{predicate}{s} {destination}, {base}, {source}"),
+
+ SaturatingAdd {
+ predicate,
+ destination,
+ base,
+ source,
+ } => write!(f, "QADD{predicate} {destination}, {base}, {source}"),
+
+ SaturatingSubtract {
+ predicate,
+ destination,
+ base,
+ source,
+ } => write!(f, "QSUB{predicate} {destination}, {base}, {source}"),
+
+ SoftwareInterrupt {
+ predicate,
+ immediate,
+ } => write!(f, "SWI{predicate} #{immediate}"),
+
+ Subtract {
+ predicate,
+ destination,
+ base,
+ source,
+ s,
+ } => write!(f, "SUB{predicate}{s} {destination}, {base}, {source}"),
+
+ SubtractCarry {
+ predicate,
+ destination,
+ base,
+ source,
+ s,
+ } => write!(f, "SBC{predicate}{s} {destination}, {base}, {source}"),
}
}
}
diff --git a/src/arm32/instruction/test.rs b/src/arm32/instruction/test.rs
index 6137795..d9067e9 100644
--- a/src/arm32/instruction/test.rs
+++ b/src/arm32/instruction/test.rs
@@ -52,7 +52,7 @@ fn test_arm32_instruction() {
predicate: Predicate::Always,
destination: Register::R7,
base: Register::R8,
- source: Shifter::LogicalShiftLeftImmediate { source: Register::Sb, shift: 0x0 },
+ source: Shifter::LogicalShiftLeftImmediate { source: Register::R9, shift: 0x0 },
s: Flag::On,
},
@@ -64,6 +64,13 @@ fn test_arm32_instruction() {
shift: Register::Lr,
s: Flag::Off,
},
+
+ Instruction::Move {
+ predicate: Predicate::NotEqual,
+ destination: Register::R0,
+ source: Shifter::LogicalShiftLeftImmediate { source: Register::Pc, shift: 0x0 },
+ s: Flag::Off,
+ },
];
let mut displays = Vec::with_capacity(tree.len());
@@ -74,8 +81,9 @@ fn test_arm32_instruction() {
[
"ADDGE r1, r2, r3, ROR #2",
"QSUBLT r4, r5, r6",
- "ORRS r7, r8, sb",
+ "ORRS r7, r8, r9",
"MLAEQ r0, pc, pc, lr",
+ "CPYNE r0, pc"
],
);
}
diff --git a/src/arm32/instruction_codec/encode_arm.rs b/src/arm32/instruction_codec/encode_arm.rs
index 73dc3d9..9c63f81 100644
--- a/src/arm32/instruction_codec/encode_arm.rs
+++ b/src/arm32/instruction_codec/encode_arm.rs
@@ -19,7 +19,7 @@
// fero General Public License along with Pollex.
// If not, see <https://www.gnu.org/licenses/>.
-use crate::Result;
+use crate::{assert_or_err, Error, Result};
use crate::arm32::{
ArmOpcode,
Instruction,
@@ -36,35 +36,51 @@ impl InstructionCodec {
pub fn encode_arm(&mut self, instruction: Instruction) -> Result<ArmOpcode> {
use Instruction::*;
- let mut opcode = 0b00000000_00000000_00000000_00000000;
+ let mut opcode = 0b00000000_00000000_00000000_00000000_u32;
match instruction {
- Branch { predicate, .. } => {
+ Branch {
+ predicate,
+ ..
+ } => {
opcode |= 0b00001011_00000000_00000000_00000000;
opcode |= (predicate as u32) << 0x1C;
},
- BranchLink { predicate, .. } => {
+ BranchLink {
+ predicate,
+ ..
+ } => {
opcode |= 0b00001010_00000000_00000000_00000000;
opcode |= (predicate as u32) << 0x1C;
},
- Breakpoint { immediate } => {
+ Breakpoint {
+ immediate,
+ } => {
opcode |= 0b11100001_00100000_00000000_01110000;
opcode |= immediate & 0b00000000_00000000_00000000_00001111;
opcode |= (immediate & 0b00000000_00000000_11111111_11110000) << 0x4;
},
- Move { predicate, destination, source, s } => {
+ Move {
+ predicate,
+ destination,
+ source,
+ s,
+ } => {
opcode |= 0b00000001_10100000_00000000_00000000;
opcode |= (destination as u32) << 0xC;
opcode |= u32::from(s) << 0x14;
opcode |= (predicate as u32) << 0x1C;
- opcode = add_shifter(opcode, source);
+ opcode = add_shifter(opcode, source)?;
},
- SoftwareInterrupt { predicate, immediate } => {
+ SoftwareInterrupt {
+ predicate,
+ immediate,
+ } => {
opcode |= 0b00001111_00000000_00000000_00000000;
opcode |= immediate & 0b00000000_11111111_11111111_11111111;
opcode |= (predicate as u32) << 0x1C;
@@ -78,41 +94,50 @@ impl InstructionCodec {
}
}
-fn add_shifter(mut opcode: u32, shifter: Shifter) -> u32 {
+fn add_shifter(mut opcode: u32, shifter: Shifter) -> Result<u32> {
+ use Shifter::*;
+
match shifter {
- | Shifter::ArithmeticShiftRightImmediate { source, shift }
- | Shifter::LogicalShiftLeftImmediate { source, shift }
- | Shifter::LogicalShiftRightImmediate { source, shift }
- | Shifter::RotateRightImmediate { source, shift }
+ LogicalShiftLeftImmediate { source, shift: 0x0 }
+ => {
+ opcode |= source as u32;
+ },
+
+ | ArithmeticShiftRightImmediate { source, shift }
+ | LogicalShiftLeftImmediate { source, shift }
+ | LogicalShiftRightImmediate { source, shift }
+ | RotateRightImmediate { source, shift }
=> {
let code = match shifter {
- Shifter::LogicalShiftLeftImmediate { .. } => 0b00,
- Shifter::LogicalShiftRightImmediate { .. } => 0b01,
- Shifter::ArithmeticShiftRightImmediate { .. } => 0b10,
- Shifter::RotateRightImmediate { .. } => 0b11,
+ LogicalShiftLeftImmediate { .. } => 0b00,
+ LogicalShiftRightImmediate { .. } => 0b01,
+ ArithmeticShiftRightImmediate { .. } => 0b10,
+ RotateRightImmediate { .. } => 0b11,
_ => unreachable!(),
};
+ assert_or_err!(shift != 0x0, Error::IllegalImmediate);
+
opcode |= source as u32;
opcode |= code << 0x5;
opcode |= shift << 0x7;
},
- Shifter::RotateRightExtend { .. } => {
+ RotateRightExtend { .. } => {
todo!()
},
- | Shifter::ArithmeticShiftRightRegister { source, .. }
- | Shifter::LogicalShiftLeftRegister { source, .. }
- | Shifter::LogicalShiftRightRegister { source, .. }
- | Shifter::RotateRightRegister { source, .. }
+ | ArithmeticShiftRightRegister { source, .. }
+ | LogicalShiftLeftRegister { source, .. }
+ | LogicalShiftRightRegister { source, .. }
+ | RotateRightRegister { source, .. }
=> {
let _code = match shifter {
- Shifter::LogicalShiftLeftRegister { .. } => 0b00,
- Shifter::LogicalShiftRightRegister { .. } => 0b01,
- Shifter::ArithmeticShiftRightRegister { .. } => 0b10,
- Shifter::RotateRightRegister { .. } => 0b11,
+ LogicalShiftLeftRegister { .. } => 0b00,
+ LogicalShiftRightRegister { .. } => 0b01,
+ ArithmeticShiftRightRegister { .. } => 0b10,
+ RotateRightRegister { .. } => 0b11,
_ => unreachable!(),
};
@@ -121,18 +146,18 @@ fn add_shifter(mut opcode: u32, shifter: Shifter) -> u32 {
opcode |= source as u32;
},
- Shifter::Immediate { immediate } => {
- let (immediate, rotate) = if immediate <= 0xFF {
- (immediate, 0x00)
+ Immediate { source } => {
+ let (source, rotate) = if source <= 0xFF {
+ (source, 0x00)
} else {
todo!()
};
opcode |= 0b00000010_00000000_00000000_00000000;
- opcode |= immediate;
+ opcode |= source;
opcode |= rotate << 0x8;
},
}
- opcode
+ Ok(opcode)
}
diff --git a/src/arm32/instruction_codec/encode_thumb.rs b/src/arm32/instruction_codec/encode_thumb.rs
new file mode 100644
index 0000000..f2fa0b8
--- /dev/null
+++ b/src/arm32/instruction_codec/encode_thumb.rs
@@ -0,0 +1,92 @@
+// Copyright 2024 Gabriel Bjørnager Jensen.
+//
+// This file is part of Pollex.
+//
+// Pollex is free software: you can redistribute it
+// and/or modify it under the terms of the GNU Af-
+// fero General Public License as published by the
+// Free Software Foundation, either version 3 of
+// the License, or (at your option) any later ver-
+// sion.
+//
+// Pollex is distributed in the hope that it will
+// be useful, but WITHOUT ANY WARRANTY; without
+// even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Af-
+// fero General Public License along with Pollex.
+// If not, see <https://www.gnu.org/licenses/>.
+
+use crate::{assert_or_err, Error, Result};
+use crate::arm32::{
+ Instruction,
+ InstructionCodec,
+ Predicate,
+ Shifter,
+ ThumbOpcode,
+};
+
+impl InstructionCodec {
+ /// Encodes the given Thumb instruction.
+ ///
+ /// # Errors
+ ///
+ /// If the given instruction cannot be encoded for Thumb, an error is returned.
+ pub fn encode_thumb(&mut self, instruction: Instruction) -> Result<ThumbOpcode> {
+ use Instruction::*;
+
+ let mut opcode = 0b00000000_00000000_u16;
+
+ match instruction {
+ Move {
+ predicate,
+ destination,
+ source,
+ s,
+ } => {
+ assert_or_err!(predicate == Predicate::Always, Error::IllegalPredicate);
+
+ if s.is_on() {
+ if let Shifter::Immediate { source } = source {
+ assert_or_err!(destination.is_low(), Error::IllegalRegister);
+ assert_or_err!(source <= 0xFF, Error::IllegalRegister);
+
+ opcode |= 0b00100000_00000000;
+ opcode |= source as u16;
+ opcode |= (destination as u16) << 0x8;
+
+ } else if let Shifter::LogicalShiftLeftImmediate { source, shift: 0x0 } = source {
+ assert_or_err!(destination.is_low(), Error::IllegalRegister);
+ assert_or_err!(source.is_low(), Error::IllegalRegister);
+
+ opcode |= 0b00100000_00000000;
+ opcode |= destination as u16;
+ opcode |= (source as u16) << 0x3;
+ } else {
+ return Err(Error::IllegalInstruction);
+ }
+ } else if let Shifter::LogicalShiftLeftImmediate { source, shift: 0x0 } = source {
+ opcode |= 0b01000110_00000000;
+
+ let h0 = destination.is_high();
+ let h1 = source.is_high();
+
+ opcode |= destination as u16 & 0b00000000_00000111;
+ opcode |= (source as u16 & 0b00000000_00000111) << 0x3;
+ opcode |= u16::from(h0) << 0x6;
+ opcode |= u16::from(h1) << 0x7;
+ } else {
+ // TODO: Shifters &c.
+ todo!();
+ }
+ }
+
+ _ => return Err(Error::IllegalInstruction),
+ }
+
+ self.address += ThumbOpcode::SIZE;
+ Ok(opcode.into())
+ }
+}
diff --git a/src/arm32/instruction_codec/mod.rs b/src/arm32/instruction_codec/mod.rs
index f8d2a8c..841d335 100644
--- a/src/arm32/instruction_codec/mod.rs
+++ b/src/arm32/instruction_codec/mod.rs
@@ -23,13 +23,14 @@
mod test;
mod encode_arm;
+mod encode_thumb;
use core::num::Wrapping;
/// Codec for encoding and decoding instruction.
///
/// Arm instructions can be encoded/decoded using the [`encode_arm`](InstructionCodec::encode_arm) and `decode_arm` (soon).
-/// Thumb instruction will similarly be manipulated using `encode_thumb` and `decode_thumb`.
+/// Thumb instruction will similarly be manipulated using [`encode_thumb`](InstructionCodec::encode_thumb) and `decode_thumb`.
///
/// This structure keeps track of the adress at which instructions are to be placed (see *Rationale*).
/// If encoding causes this internal address to go past `0xFFFFFFFF`, the value is safely wrapped to the origin (i.e. `0x00000000`).
diff --git a/src/arm32/register/mod.rs b/src/arm32/register/mod.rs
index 2df521b..0013c46 100644
--- a/src/arm32/register/mod.rs
+++ b/src/arm32/register/mod.rs
@@ -19,7 +19,10 @@
// fero General Public License along with Pollex.
// If not, see <https://www.gnu.org/licenses/>.
+use crate::Error;
+
use core::fmt::Display;
+use core::str::FromStr;
/// An Arm register.
///
@@ -38,15 +41,33 @@ pub enum Register {
R6 = 0b0110,
R7 = 0b0111,
R8 = 0b1000,
- Sb = 0b1001, // R9
- Sl = 0b1010, // R10
+ R9 = 0b1001, // Sb
+ R10 = 0b1010, // Sl
R11 = 0b1011,
- Ip = 0b1100, // R12
+ R12 = 0b1100, // Ip
Sp = 0b1101, // R13
Lr = 0b1110, // R14
Pc = 0b1111, // R15
}
+impl Register {
+ /// Checks if the register is a low register.
+ ///
+ /// That is, it is any of `r0`, `r1`, `r2`, `r3`, `r4`, `r5`, `r6`, or `r7` -- or any alias herof.
+ /// See also [`is_high`](Self::is_high).
+ #[inline(always)]
+ #[must_use]
+ pub const fn is_low(self) -> bool { (self as u8) <= 0x7 }
+
+ /// Checks if the register is a high register.
+ ///
+ /// That is, it is any of `r8`, `r9`, `r10`, `r11`, `r12`, `sp`, `lr`, or `pc` -- or any alias herof.
+ /// See also [`is_low`](Self::is_low).
+ #[inline(always)]
+ #[must_use]
+ pub const fn is_high(self) -> bool { (self as u8) > 0x7 }
+}
+
impl Display for Register {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
use Register::*;
@@ -61,13 +82,91 @@ impl Display for Register {
R6 => write!(f, "r6"),
R7 => write!(f, "r7"),
R8 => write!(f, "r8"),
- Sb => write!(f, "sb"),
- Sl => write!(f, "sl"),
+ R9 => write!(f, "r9"),
+ R10 => write!(f, "r10"),
R11 => write!(f, "r11"),
- Ip => write!(f, "ip"),
+ R12 => write!(f, "r12"),
Sp => write!(f, "sp"),
Lr => write!(f, "lr"),
Pc => write!(f, "pc"),
}
}
}
+
+impl FromStr for Register {
+ type Err = Error;
+
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ use Register::*;
+
+ match s.to_lowercase().as_str() {
+ | "r0"
+ | "a1"
+ => Ok(R0),
+
+ | "r1"
+ | "a2"
+ => Ok(R1),
+
+ | "r2"
+ | "a3"
+ => Ok(R2),
+
+ | "r3"
+ | "a4"
+ => Ok(R3),
+
+ | "r4"
+ | "v1"
+ => Ok(R4),
+
+ | "r5"
+ | "v2"
+ => Ok(R5),
+
+ | "r6"
+ | "v3"
+ => Ok(R6),
+
+ | "r7"
+ | "v4"
+ => Ok(R7),
+
+ | "r8"
+ | "v5"
+ => Ok(R8),
+
+ | "r9"
+ | "sb"
+ | "v6"
+ => Ok(R9),
+
+ | "r10"
+ | "sl"
+ | "v7"
+ => Ok(R10),
+
+ | "r11"
+ | "v8"
+ => Ok(R11),
+
+ | "ip"
+ | "r12"
+ => Ok(R12),
+
+ | "r13"
+ | "sp"
+ => Ok(Sp),
+
+ | "lr"
+ | "r14"
+ => Ok(Lr),
+
+ | "pc"
+ | "r15"
+ => Ok(Pc),
+
+ _ => Err(Error::UnknownRegister)
+ }
+ }
+}
diff --git a/src/arm32/shifter/mod.rs b/src/arm32/shifter/mod.rs
index f658c2b..ae0b1f9 100644
--- a/src/arm32/shifter/mod.rs
+++ b/src/arm32/shifter/mod.rs
@@ -50,7 +50,7 @@ pub enum Shifter {
ArithmeticShiftRightRegister { source: Register, shift: Register },
- Immediate { immediate: u32 },
+ Immediate { source: u32 },
LogicalShiftLeftImmediate { source: Register, shift: u32 },
@@ -80,8 +80,8 @@ impl Display for Shifter {
write!(f, "{source}, ASR {shift}")
},
- Immediate { immediate } => {
- write!(f, "#{immediate}<")
+ Immediate { source } => {
+ write!(f, "#{source}")
},
LogicalShiftLeftImmediate { source, shift: 0x0 } => {
diff --git a/src/arm32/thumb_opcode/mod.rs b/src/arm32/thumb_opcode/mod.rs
index 1bd9529..c327696 100644
--- a/src/arm32/thumb_opcode/mod.rs
+++ b/src/arm32/thumb_opcode/mod.rs
@@ -27,6 +27,9 @@ use core::fmt::{Debug, Display, Formatter};
pub struct ThumbOpcode(u16);
impl ThumbOpcode {
+ /// The size of any Thumb opcode, in bytes.
+ ///
+ /// This value is not including `BL` prefixes and suffixes, which count as two opcodes in total.
pub const SIZE: u32 = 0x2;
/// Creates a new opcode from a primitive.
diff --git a/src/arch/mod.rs b/src/arm64/mod.rs
index 8a28b67..fc679eb 100644
--- a/src/arch/mod.rs
+++ b/src/arm64/mod.rs
@@ -19,9 +19,6 @@
// fero General Public License along with Pollex.
// If not, see <https://www.gnu.org/licenses/>.
-/// An Arm architecture.
-#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
-pub enum Arch {
- Arm32,
- Arm64,
-}
+//! Arm64-related facilities.
+//!
+//! This module is currently empty.
diff --git a/src/error/mod.rs b/src/error/mod.rs
index 99f78bb..b6c1e24 100644
--- a/src/error/mod.rs
+++ b/src/error/mod.rs
@@ -27,9 +27,23 @@ pub type Result<T> = core::result::Result<T, Error>;
/// A crate error.
#[derive(Clone, Debug)]
pub enum Error {
- ImmediateTooLarge,
+ /// The given immediate cannot be encoded.
+ IllegalImmediate,
- InvalidImmediateValue,
+ /// 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,
+
+ /// The given instruction predicate cannot be encoded.
+ IllegalPredicate,
+
+ /// The given register can currently not be used.
+ IllegalRegister,
+
+ /// Register name is not known.
+ UnknownRegister,
}
impl Display for Error {
@@ -37,9 +51,15 @@ impl Display for Error {
use Error::*;
let message = match *self {
- ImmediateTooLarge => "immediate too large",
+ IllegalImmediate => "illegal immediate value",
+
+ IllegalInstruction => "illegal instruction",
+
+ IllegalPredicate => "illegal instruction predicate",
+
+ IllegalRegister => "register not permitted here",
- InvalidImmediateValue => "invalid immediate value",
+ UnknownRegister => "unknown register",
};
f.write_str(message)
diff --git a/src/lib.rs b/src/lib.rs
index 6efb3c6..83a7b5a 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -31,6 +31,7 @@
extern crate alloc;
pub mod arm32;
+pub mod arm64;
macro_rules! use_mod {
($vis:vis $name:ident) => {
@@ -40,5 +41,11 @@ macro_rules! use_mod {
}
pub(in crate) use use_mod;
-use_mod!(pub arch);
use_mod!(pub error);
+
+#[macro_export]
+macro_rules! assert_or_err {
+ ($predicate:expr, $error:expr) => {{
+ if !($predicate) { return Err($error) };
+ }};
+}