diff options
Diffstat (limited to 'src/arm32')
-rw-r--r-- | src/arm32/address/mod.rs | 52 | ||||
-rw-r--r-- | src/arm32/flag/mod.rs | 144 | ||||
-rw-r--r-- | src/arm32/instruction/display.rs | 125 | ||||
-rw-r--r-- | src/arm32/instruction/from_str.rs | 56 | ||||
-rw-r--r-- | src/arm32/instruction/mod.rs | 92 | ||||
-rw-r--r-- | src/arm32/instruction/test.rs | 89 | ||||
-rw-r--r-- | src/arm32/instruction_codec/decode_thumb.rs | 146 | ||||
-rw-r--r-- | src/arm32/instruction_codec/encode_arm.rs | 155 | ||||
-rw-r--r-- | src/arm32/instruction_codec/encode_thumb.rs | 493 | ||||
-rw-r--r-- | src/arm32/instruction_codec/mod.rs | 31 | ||||
-rw-r--r-- | src/arm32/instruction_codec/test.rs | 72 | ||||
-rw-r--r-- | src/arm32/mod.rs | 1 | ||||
-rw-r--r-- | src/arm32/predicate/mod.rs | 17 | ||||
-rw-r--r-- | src/arm32/register/mod.rs | 35 | ||||
-rw-r--r-- | src/arm32/shifter/mod.rs | 136 | ||||
-rw-r--r-- | src/arm32/thumb_opcode/mod.rs | 2 |
16 files changed, 1247 insertions, 399 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/test.rs b/src/arm32/instruction/test.rs deleted file mode 100644 index d9067e9..0000000 --- a/src/arm32/instruction/test.rs +++ /dev/null @@ -1,89 +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::{ - Predicate, - Flag, - Instruction, - Register, - Shifter, -}; - -use alloc::format; -use alloc::vec::Vec; - -#[test] -fn test_arm32_instruction() { - let tree = [ - Instruction::Add { - predicate: Predicate::GreaterThanOrEqual, - destination: Register::R1, - base: Register::R2, - source: Shifter::RotateRightImmediate { source: Register::R3, shift: 0x2 }, - s: Flag::Off, - }, - - Instruction::SaturatingSubtract { - predicate: Predicate::LessThan, - destination: Register::R4, - base: Register::R5, - source: Register::R6, - }, - - Instruction::InclusiveOr { - predicate: Predicate::Always, - destination: Register::R7, - base: Register::R8, - source: Shifter::LogicalShiftLeftImmediate { source: Register::R9, shift: 0x0 }, - s: Flag::On, - }, - - Instruction::MultiplyAccumulate { - predicate: Predicate::Equal, - destination: Register::R0, - base: Register::Pc, - source: Register::Pc, - 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()); - for instruction in tree { displays.push(format!("{instruction}")) } - - assert_eq!( - displays, - [ - "ADDGE r1, r2, r3, ROR #2", - "QSUBLT r4, r5, r6", - "ORRS r7, r8, r9", - "MLAEQ r0, pc, pc, lr", - "CPYNE r0, pc" - ], - ); -} 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/instruction_codec/test.rs b/src/arm32/instruction_codec/test.rs deleted file mode 100644 index 0194a33..0000000 --- a/src/arm32/instruction_codec/test.rs +++ /dev/null @@ -1,72 +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::{ - 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 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. |