diff options
Diffstat (limited to 'src/arm32/instruction_codec')
-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 |
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, - ], - ) -} |