summaryrefslogtreecommitdiff
path: root/src/arm32/instruction_codec
diff options
context:
space:
mode:
Diffstat (limited to 'src/arm32/instruction_codec')
-rw-r--r--src/arm32/instruction_codec/decode_thumb.rs146
-rw-r--r--src/arm32/instruction_codec/encode_arm.rs155
-rw-r--r--src/arm32/instruction_codec/encode_thumb.rs493
-rw-r--r--src/arm32/instruction_codec/mod.rs31
-rw-r--r--src/arm32/instruction_codec/test.rs72
5 files changed, 714 insertions, 183 deletions
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,
- ],
- )
-}