summaryrefslogtreecommitdiff
path: root/src/arm32/shifter
diff options
context:
space:
mode:
Diffstat (limited to 'src/arm32/shifter')
-rw-r--r--src/arm32/shifter/mod.rs136
1 files changed, 99 insertions, 37 deletions
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}"),
}
}
}