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