diff options
Diffstat (limited to 'src/arm32/instruction')
-rw-r--r-- | src/arm32/instruction/display.rs | 61 | ||||
-rw-r--r-- | src/arm32/instruction/encode_arm.rs | 130 | ||||
-rw-r--r-- | src/arm32/instruction/mod.rs | 175 | ||||
-rw-r--r-- | src/arm32/instruction/test.rs | 67 |
4 files changed, 247 insertions, 186 deletions
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/encode_arm.rs b/src/arm32/instruction/encode_arm.rs deleted file mode 100644 index a24ec28..0000000 --- a/src/arm32/instruction/encode_arm.rs +++ /dev/null @@ -1,130 +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::Result; -use crate::arm32::{ArmOpcode, Instruction, Shifter}; - -impl Instruction { - pub fn encode_arm(&self) -> Result<ArmOpcode> { - use Instruction::*; - - let mut opcode = 0b00000000_00000000_00000000_00000000; - - match *self { - Branch { predicate, .. } => { - opcode |= 0b00001011_00000000_00000000_00000000; - opcode |= (predicate as u32) << 0x1C; - }, - - 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; - }, - - Move { predicate, destination, source, s } => { - opcode |= 0b00000001_10100000_00000000_00000000; - opcode |= (destination as u32) << 0xC; - opcode |= u32::from(s.is_on()) << 0x14; - opcode |= (predicate as u32) << 0x1C; - - opcode = add_shifter(opcode, source); - }, - - SoftwareInterrupt { predicate, immediate } => { - opcode |= 0b00001111_00000000_00000000_00000000; - opcode |= immediate.get() & 0b00000000_11111111_11111111_11111111; - opcode |= (predicate as u32) << 0x1C; - }, - - _ => todo!(), - } - - Ok(ArmOpcode::from_u32(opcode)) - } -} - -fn add_shifter(mut opcode: u32, shifter: Shifter) -> u32 { - match shifter { - | Shifter::ArithmeticShiftRightImmediate { source, shift } - | Shifter::LogicalShiftLeftImmediate { source, shift } - | Shifter::LogicalShiftRightImmediate { source, shift } - | Shifter::RotateRightImmediate { source, shift } - => { - let code = match shifter { - Shifter::LogicalShiftLeftImmediate { .. } => 0b00, - Shifter::LogicalShiftRightImmediate { .. } => 0b01, - Shifter::ArithmeticShiftRightImmediate { .. } => 0b10, - Shifter::RotateRightImmediate { .. } => 0b11, - - _ => unreachable!(), - }; - - opcode |= source as u32; - opcode |= code << 0x5; - opcode |= shift.get() << 0x7; - }, - - Shifter::RotateRightExtend { .. } => { - todo!() - }, - - | Shifter::ArithmeticShiftRightRegister { source, .. } - | Shifter::LogicalShiftLeftRegister { source, .. } - | Shifter::LogicalShiftRightRegister { source, .. } - | Shifter::RotateRightRegister { source, .. } - => { - let _code = match shifter { - Shifter::LogicalShiftLeftRegister { .. } => 0b00, - Shifter::LogicalShiftRightRegister { .. } => 0b01, - Shifter::ArithmeticShiftRightRegister { .. } => 0b10, - Shifter::RotateRightRegister { .. } => 0b11, - - _ => unreachable!(), - }; - - opcode |= 0b00000000_00000000_00000000_00010000; - opcode |= source as u32; - }, - - Shifter::Immediate { immediate } => { - let (immediate, rotate) = if immediate <= 0xFF { - let immediate = immediate.get(); - - (immediate, 0x00) - } else { - todo!() - }; - - opcode |= 0b00000010_00000000_00000000_00000000; - opcode |= immediate; - opcode |= rotate << 0x8; - }, - } - - opcode -} 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 |