summaryrefslogtreecommitdiff
path: root/src/arm32/instruction
diff options
context:
space:
mode:
Diffstat (limited to 'src/arm32/instruction')
-rw-r--r--src/arm32/instruction/display.rs61
-rw-r--r--src/arm32/instruction/encode_arm.rs130
-rw-r--r--src/arm32/instruction/mod.rs175
-rw-r--r--src/arm32/instruction/test.rs67
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