diff options
Diffstat (limited to 'src/arm32/shifter')
-rw-r--r-- | src/arm32/shifter/mod.rs | 136 |
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}"), } } } |