2024-02-17 02:35:49 +00:00
|
|
|
|
//
|
2024-02-21 20:32:27 +00:00
|
|
|
|
// OperationMapper.hpp
|
2024-02-17 02:35:49 +00:00
|
|
|
|
// Clock Signal
|
|
|
|
|
//
|
|
|
|
|
// Created by Thomas Harte on 16/02/2024.
|
|
|
|
|
// Copyright © 2024 Thomas Harte. All rights reserved.
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
#pragma once
|
|
|
|
|
|
2024-02-21 19:17:01 +00:00
|
|
|
|
#include "../../Reflection/Dispatcher.hpp"
|
2024-02-28 18:53:00 +00:00
|
|
|
|
#include "BarrelShifter.hpp"
|
2024-02-21 19:17:01 +00:00
|
|
|
|
|
2024-02-17 02:35:49 +00:00
|
|
|
|
namespace InstructionSet::ARM {
|
|
|
|
|
|
2024-02-21 20:32:27 +00:00
|
|
|
|
enum class Model {
|
2024-03-03 02:47:09 +00:00
|
|
|
|
ARMv2,
|
2024-02-21 20:32:27 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
enum class Condition {
|
|
|
|
|
EQ, NE, CS, CC,
|
|
|
|
|
MI, PL, VS, VC,
|
|
|
|
|
HI, LS, GE, LT,
|
|
|
|
|
GT, LE, AL, NV,
|
|
|
|
|
};
|
|
|
|
|
|
2024-02-22 15:16:54 +00:00
|
|
|
|
//
|
|
|
|
|
// Implementation details.
|
|
|
|
|
//
|
2024-02-21 19:17:01 +00:00
|
|
|
|
static constexpr int FlagsStartBit = 20;
|
2024-02-22 15:16:54 +00:00
|
|
|
|
using Flags = uint8_t;
|
2024-02-21 19:17:01 +00:00
|
|
|
|
|
|
|
|
|
template <int position>
|
|
|
|
|
constexpr bool flag_bit(uint8_t flags) {
|
|
|
|
|
static_assert(position >= 20 && position < 28);
|
|
|
|
|
return flags & (1 << (position - FlagsStartBit));
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-22 16:20:22 +00:00
|
|
|
|
//
|
|
|
|
|
// Methods common to data processing and data transfer.
|
|
|
|
|
//
|
2024-02-21 19:51:51 +00:00
|
|
|
|
struct WithShiftControlBits {
|
|
|
|
|
constexpr WithShiftControlBits(uint32_t opcode) noexcept : opcode_(opcode) {}
|
|
|
|
|
|
|
|
|
|
/// The operand 2 register index if @c operand2_is_immediate() is @c false; meaningless otherwise.
|
2024-02-27 20:30:51 +00:00
|
|
|
|
int operand2() const { return opcode_ & 0xf; }
|
2024-02-21 19:51:51 +00:00
|
|
|
|
/// The type of shift to apply to operand 2 if @c operand2_is_immediate() is @c false; meaningless otherwise.
|
|
|
|
|
ShiftType shift_type() const { return ShiftType((opcode_ >> 5) & 3); }
|
|
|
|
|
/// @returns @c true if the amount to shift by should be taken from a register; @c false if it is an immediate value.
|
|
|
|
|
bool shift_count_is_register() const { return opcode_ & (1 << 4); }
|
|
|
|
|
/// The shift amount register index if @c shift_count_is_register() is @c true; meaningless otherwise.
|
|
|
|
|
int shift_register() const { return (opcode_ >> 8) & 0xf; }
|
|
|
|
|
/// The amount to shift by if @c shift_count_is_register() is @c false; meaningless otherwise.
|
|
|
|
|
int shift_amount() const { return (opcode_ >> 7) & 0x1f; }
|
|
|
|
|
|
|
|
|
|
protected:
|
|
|
|
|
uint32_t opcode_;
|
|
|
|
|
};
|
|
|
|
|
|
2024-02-22 15:16:54 +00:00
|
|
|
|
//
|
|
|
|
|
// Branch (i.e. B and BL).
|
|
|
|
|
//
|
2024-02-28 16:33:28 +00:00
|
|
|
|
struct BranchFlags {
|
|
|
|
|
constexpr BranchFlags(uint8_t flags) noexcept : flags_(flags) {}
|
|
|
|
|
|
2024-02-29 02:28:19 +00:00
|
|
|
|
enum class Operation {
|
|
|
|
|
B, /// Add offset to PC; programmer allows for PC being two words ahead.
|
|
|
|
|
BL, /// Copy PC and PSR to R14, then branch. Copied PC points to next instruction.
|
|
|
|
|
};
|
|
|
|
|
|
2024-02-28 16:33:28 +00:00
|
|
|
|
/// @returns The operation to apply.
|
2024-02-29 02:28:19 +00:00
|
|
|
|
constexpr Operation operation() const {
|
|
|
|
|
return flag_bit<24>(flags_) ? Operation::BL : Operation::B;
|
2024-02-28 16:33:28 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
uint8_t flags_;
|
|
|
|
|
};
|
|
|
|
|
|
2024-02-22 15:16:54 +00:00
|
|
|
|
struct Branch {
|
|
|
|
|
constexpr Branch(uint32_t opcode) noexcept : opcode_(opcode) {}
|
|
|
|
|
|
|
|
|
|
/// The 26-bit offset to add to the PC.
|
2024-03-04 18:53:46 +00:00
|
|
|
|
uint32_t offset() const { return (opcode_ & 0xff'ffff) << 2; }
|
2024-02-22 15:16:54 +00:00
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
uint32_t opcode_;
|
|
|
|
|
};
|
|
|
|
|
|
2024-02-21 19:17:01 +00:00
|
|
|
|
//
|
|
|
|
|
// Data processing (i.e. AND to MVN).
|
|
|
|
|
//
|
2024-02-29 02:28:19 +00:00
|
|
|
|
enum class DataProcessingOperation {
|
|
|
|
|
AND, /// Rd = Op1 AND Op2.
|
|
|
|
|
EOR, /// Rd = Op1 EOR Op2.
|
|
|
|
|
SUB, /// Rd = Op1 - Op2.
|
|
|
|
|
RSB, /// Rd = Op2 - Op1.
|
|
|
|
|
ADD, /// Rd = Op1 + Op2.
|
|
|
|
|
ADC, /// Rd = Op1 + Ord2 + C.
|
|
|
|
|
SBC, /// Rd = Op1 - Op2 + C.
|
|
|
|
|
RSC, /// Rd = Op2 - Op1 + C.
|
|
|
|
|
TST, /// Set condition codes on Op1 AND Op2.
|
|
|
|
|
TEQ, /// Set condition codes on Op1 EOR Op2.
|
|
|
|
|
CMP, /// Set condition codes on Op1 - Op2.
|
|
|
|
|
CMN, /// Set condition codes on Op1 + Op2.
|
|
|
|
|
ORR, /// Rd = Op1 OR Op2.
|
|
|
|
|
MOV, /// Rd = Op2
|
|
|
|
|
BIC, /// Rd = Op1 AND NOT Op2.
|
|
|
|
|
MVN, /// Rd = NOT Op2.
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
constexpr bool is_logical(DataProcessingOperation operation) {
|
|
|
|
|
switch(operation) {
|
|
|
|
|
case DataProcessingOperation::AND:
|
|
|
|
|
case DataProcessingOperation::EOR:
|
|
|
|
|
case DataProcessingOperation::TST:
|
|
|
|
|
case DataProcessingOperation::TEQ:
|
|
|
|
|
case DataProcessingOperation::ORR:
|
|
|
|
|
case DataProcessingOperation::MOV:
|
|
|
|
|
case DataProcessingOperation::BIC:
|
|
|
|
|
case DataProcessingOperation::MVN:
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
default: return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
constexpr bool is_comparison(DataProcessingOperation operation) {
|
|
|
|
|
switch(operation) {
|
|
|
|
|
case DataProcessingOperation::TST:
|
|
|
|
|
case DataProcessingOperation::TEQ:
|
|
|
|
|
case DataProcessingOperation::CMP:
|
|
|
|
|
case DataProcessingOperation::CMN:
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
default: return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-21 19:17:01 +00:00
|
|
|
|
struct DataProcessingFlags {
|
|
|
|
|
constexpr DataProcessingFlags(uint8_t flags) noexcept : flags_(flags) {}
|
|
|
|
|
|
2024-02-26 19:30:26 +00:00
|
|
|
|
/// @returns The operation to apply.
|
|
|
|
|
constexpr DataProcessingOperation operation() const {
|
2024-03-03 04:27:37 +00:00
|
|
|
|
return DataProcessingOperation((flags_ >> (21 - FlagsStartBit)) & 0xf);
|
2024-02-26 19:30:26 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-02-21 19:17:01 +00:00
|
|
|
|
/// @returns @c true if operand 2 is defined by the @c rotate() and @c immediate() fields;
|
|
|
|
|
/// @c false if it is defined by the @c shift_*() and @c operand2() fields.
|
2024-02-26 19:30:26 +00:00
|
|
|
|
constexpr bool operand2_is_immediate() const { return flag_bit<25>(flags_); }
|
2024-02-21 19:17:01 +00:00
|
|
|
|
|
2024-02-22 16:20:22 +00:00
|
|
|
|
/// @c true if the status register should be updated; @c false otherwise.
|
2024-02-26 19:30:26 +00:00
|
|
|
|
constexpr bool set_condition_codes() const { return flag_bit<20>(flags_); }
|
2024-02-21 19:17:01 +00:00
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
uint8_t flags_;
|
|
|
|
|
};
|
|
|
|
|
|
2024-02-21 19:51:51 +00:00
|
|
|
|
struct DataProcessing: public WithShiftControlBits {
|
|
|
|
|
using WithShiftControlBits::WithShiftControlBits;
|
2024-02-21 19:17:01 +00:00
|
|
|
|
|
|
|
|
|
/// The destination register index. i.e. Rd.
|
|
|
|
|
int destination() const { return (opcode_ >> 12) & 0xf; }
|
|
|
|
|
|
|
|
|
|
/// The operand 1 register index. i.e. Rn.
|
|
|
|
|
int operand1() const { return (opcode_ >> 16) & 0xf; }
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Immediate values for operand 2.
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
/// An 8-bit value to rotate right @c rotate() places if @c operand2_is_immediate() is @c true; meaningless otherwise.
|
2024-03-04 18:53:46 +00:00
|
|
|
|
uint32_t immediate() const { return opcode_ & 0xff; }
|
2024-02-21 19:17:01 +00:00
|
|
|
|
/// The number of bits to rotate @c immediate() by to the right if @c operand2_is_immediate() is @c true; meaningless otherwise.
|
2024-03-05 14:31:42 +00:00
|
|
|
|
uint32_t rotate() const { return (opcode_ >> 7) & 0x1e; }
|
2024-02-21 19:17:01 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// MUL and MLA.
|
|
|
|
|
//
|
|
|
|
|
struct MultiplyFlags {
|
|
|
|
|
constexpr MultiplyFlags(uint8_t flags) noexcept : flags_(flags) {}
|
|
|
|
|
|
2024-02-22 16:20:22 +00:00
|
|
|
|
/// @c true if the status register should be updated; @c false otherwise.
|
2024-02-28 16:27:27 +00:00
|
|
|
|
constexpr bool set_condition_codes() const { return flag_bit<20>(flags_); }
|
|
|
|
|
|
2024-02-29 02:28:19 +00:00
|
|
|
|
enum class Operation {
|
|
|
|
|
MUL, /// Rd = Rm * Rs
|
|
|
|
|
MLA, /// Rd = Rm * Rs + Rn
|
|
|
|
|
};
|
|
|
|
|
|
2024-02-28 16:27:27 +00:00
|
|
|
|
/// @returns The operation to apply.
|
2024-02-29 02:28:19 +00:00
|
|
|
|
constexpr Operation operation() const {
|
|
|
|
|
return flag_bit<21>(flags_) ? Operation::MLA : Operation::MUL;
|
2024-02-28 16:27:27 +00:00
|
|
|
|
}
|
2024-02-21 19:17:01 +00:00
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
uint8_t flags_;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct Multiply {
|
|
|
|
|
constexpr Multiply(uint32_t opcode) noexcept : opcode_(opcode) {}
|
|
|
|
|
|
|
|
|
|
/// The destination register index. i.e. 'Rd'.
|
2024-03-05 15:56:09 +00:00
|
|
|
|
uint32_t destination() const { return (opcode_ >> 16) & 0xf; }
|
2024-02-21 19:17:01 +00:00
|
|
|
|
|
|
|
|
|
/// The accumulator register index for multiply-add. i.e. 'Rn'.
|
2024-03-05 15:56:09 +00:00
|
|
|
|
uint32_t accumulator() const { return (opcode_ >> 12) & 0xf; }
|
2024-02-21 19:17:01 +00:00
|
|
|
|
|
|
|
|
|
/// The multiplicand register index. i.e. 'Rs'.
|
2024-03-05 15:56:09 +00:00
|
|
|
|
uint32_t multiplicand() const { return (opcode_ >> 8) & 0xf; }
|
2024-02-21 19:17:01 +00:00
|
|
|
|
|
|
|
|
|
/// The multiplier register index. i.e. 'Rm'.
|
2024-03-05 15:56:09 +00:00
|
|
|
|
uint32_t multiplier() const { return opcode_ & 0xf; }
|
2024-02-21 19:17:01 +00:00
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
uint32_t opcode_;
|
|
|
|
|
};
|
|
|
|
|
|
2024-02-21 19:51:51 +00:00
|
|
|
|
//
|
|
|
|
|
// Single data transfer (LDR, STR).
|
|
|
|
|
//
|
|
|
|
|
struct SingleDataTransferFlags {
|
|
|
|
|
constexpr SingleDataTransferFlags(uint8_t flags) noexcept : flags_(flags) {}
|
|
|
|
|
|
2024-02-29 02:28:19 +00:00
|
|
|
|
enum class Operation {
|
|
|
|
|
LDR, /// Read single byte or word from [base + offset], possibly mutating the base.
|
|
|
|
|
STR, /// Write a single byte or word to [base + offset], possibly mutating the base.
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
constexpr Operation operation() const {
|
|
|
|
|
return flag_bit<20>(flags_) ? Operation::LDR : Operation::STR;
|
2024-02-29 02:23:57 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-03-03 19:34:21 +00:00
|
|
|
|
constexpr bool offset_is_register() const { return flag_bit<25>(flags_); }
|
2024-02-28 19:43:31 +00:00
|
|
|
|
constexpr bool pre_index() const { return flag_bit<24>(flags_); }
|
|
|
|
|
constexpr bool add_offset() const { return flag_bit<23>(flags_); }
|
|
|
|
|
constexpr bool transfer_byte() const { return flag_bit<22>(flags_); }
|
|
|
|
|
constexpr bool write_back_address() const { return flag_bit<21>(flags_); }
|
2024-02-21 19:51:51 +00:00
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
uint8_t flags_;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct SingleDataTransfer: public WithShiftControlBits {
|
|
|
|
|
using WithShiftControlBits::WithShiftControlBits;
|
|
|
|
|
|
|
|
|
|
/// The destination register index. i.e. 'Rd' for LDR.
|
|
|
|
|
int destination() const { return (opcode_ >> 12) & 0xf; }
|
|
|
|
|
|
|
|
|
|
/// The destination register index. i.e. 'Rd' for STR.
|
|
|
|
|
int source() const { return (opcode_ >> 12) & 0xf; }
|
|
|
|
|
|
|
|
|
|
/// The base register index. i.e. 'Rn'.
|
|
|
|
|
int base() const { return (opcode_ >> 16) & 0xf; }
|
|
|
|
|
|
2024-03-03 19:40:05 +00:00
|
|
|
|
/// The immediate offset, if @c offset_is_register() was @c false; meaningless otherwise.
|
2024-03-04 18:53:46 +00:00
|
|
|
|
uint32_t immediate() const { return opcode_ & 0xfff; }
|
2024-02-21 19:51:51 +00:00
|
|
|
|
};
|
|
|
|
|
|
2024-02-21 20:25:57 +00:00
|
|
|
|
//
|
|
|
|
|
// Block data transfer (LDR, STR).
|
|
|
|
|
//
|
|
|
|
|
struct BlockDataTransferFlags {
|
|
|
|
|
constexpr BlockDataTransferFlags(uint8_t flags) noexcept : flags_(flags) {}
|
|
|
|
|
|
2024-02-29 02:28:19 +00:00
|
|
|
|
enum class Operation {
|
|
|
|
|
LDM, /// Read 1–16 words from [base], possibly mutating it.
|
|
|
|
|
STM, /// Write 1-16 words to [base], possibly mutating it.
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
constexpr Operation operation() const {
|
|
|
|
|
return flag_bit<20>(flags_) ? Operation::LDM : Operation::STM;
|
2024-02-29 02:23:57 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-02-28 19:43:31 +00:00
|
|
|
|
constexpr bool pre_index() const { return flag_bit<24>(flags_); }
|
|
|
|
|
constexpr bool add_offset() const { return flag_bit<23>(flags_); }
|
|
|
|
|
constexpr bool load_psr() const { return flag_bit<22>(flags_); }
|
|
|
|
|
constexpr bool write_back_address() const { return flag_bit<21>(flags_); }
|
2024-02-21 20:25:57 +00:00
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
uint8_t flags_;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct BlockDataTransfer: public WithShiftControlBits {
|
|
|
|
|
using WithShiftControlBits::WithShiftControlBits;
|
|
|
|
|
|
|
|
|
|
/// The base register index. i.e. 'Rn'.
|
2024-03-05 15:56:09 +00:00
|
|
|
|
uint32_t base() const { return (opcode_ >> 16) & 0xf; }
|
2024-02-21 20:25:57 +00:00
|
|
|
|
|
|
|
|
|
/// A bitfield indicating which registers to load or store.
|
2024-03-04 18:53:46 +00:00
|
|
|
|
uint16_t register_list() const { return static_cast<uint16_t>(opcode_); }
|
2024-02-21 20:25:57 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//
|
2024-02-22 15:16:54 +00:00
|
|
|
|
// Coprocessor data operation.
|
2024-02-21 20:25:57 +00:00
|
|
|
|
//
|
|
|
|
|
struct CoprocessorDataOperationFlags {
|
|
|
|
|
constexpr CoprocessorDataOperationFlags(uint8_t flags) noexcept : flags_(flags) {}
|
|
|
|
|
|
2024-02-28 19:43:31 +00:00
|
|
|
|
constexpr int coprocessor_operation() const { return (flags_ >> (FlagsStartBit - 20)) & 0xf; }
|
2024-02-21 20:25:57 +00:00
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
uint8_t flags_;
|
|
|
|
|
};
|
|
|
|
|
|
2024-02-22 15:16:54 +00:00
|
|
|
|
struct CoprocessorDataOperation {
|
|
|
|
|
constexpr CoprocessorDataOperation(uint32_t opcode) noexcept : opcode_(opcode) {}
|
|
|
|
|
|
2024-02-28 19:43:31 +00:00
|
|
|
|
int operand1() const { return (opcode_ >> 16) & 0xf; }
|
|
|
|
|
int operand2() const { return opcode_ & 0xf; }
|
|
|
|
|
int destination() const { return (opcode_ >> 12) & 0xf; }
|
|
|
|
|
int coprocessor() const { return (opcode_ >> 8) & 0xf; }
|
|
|
|
|
int information() const { return (opcode_ >> 5) & 0x7; }
|
2024-02-22 15:16:54 +00:00
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
uint32_t opcode_;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Coprocessor register transfer.
|
|
|
|
|
//
|
2024-02-21 20:25:57 +00:00
|
|
|
|
struct CoprocessorRegisterTransferFlags {
|
|
|
|
|
constexpr CoprocessorRegisterTransferFlags(uint8_t flags) noexcept : flags_(flags) {}
|
|
|
|
|
|
2024-02-29 02:28:19 +00:00
|
|
|
|
enum class Operation {
|
|
|
|
|
MRC, /// Move from coprocessor register to ARM register.
|
|
|
|
|
MCR, /// Move from ARM register to coprocessor register.
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
constexpr Operation operation() const {
|
|
|
|
|
return flag_bit<20>(flags_) ? Operation::MRC : Operation::MCR;
|
2024-02-28 19:43:31 +00:00
|
|
|
|
}
|
|
|
|
|
constexpr int coprocessor_operation() const { return (flags_ >> (FlagsStartBit - 20)) & 0x7; }
|
2024-02-21 20:25:57 +00:00
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
uint8_t flags_;
|
|
|
|
|
};
|
|
|
|
|
|
2024-02-22 15:16:54 +00:00
|
|
|
|
struct CoprocessorRegisterTransfer {
|
|
|
|
|
constexpr CoprocessorRegisterTransfer(uint32_t opcode) noexcept : opcode_(opcode) {}
|
2024-02-21 20:25:57 +00:00
|
|
|
|
|
2024-02-28 19:43:31 +00:00
|
|
|
|
int operand1() const { return (opcode_ >> 16) & 0xf; }
|
|
|
|
|
int operand2() const { return opcode_ & 0xf; }
|
|
|
|
|
int destination() const { return (opcode_ >> 12) & 0xf; }
|
|
|
|
|
int coprocessor() const { return (opcode_ >> 8) & 0xf; }
|
|
|
|
|
int information() const { return (opcode_ >> 5) & 0x7; }
|
2024-02-21 20:25:57 +00:00
|
|
|
|
|
2024-02-21 20:32:27 +00:00
|
|
|
|
private:
|
|
|
|
|
uint32_t opcode_;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Coprocessor data transfer.
|
|
|
|
|
//
|
|
|
|
|
struct CoprocessorDataTransferFlags {
|
|
|
|
|
constexpr CoprocessorDataTransferFlags(uint8_t flags) noexcept : flags_(flags) {}
|
|
|
|
|
|
2024-02-29 02:28:19 +00:00
|
|
|
|
enum class Operation {
|
|
|
|
|
LDC, /// Coprocessor data transfer load.
|
|
|
|
|
STC, /// Coprocessor data transfer store.
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
constexpr Operation operation() const {
|
|
|
|
|
return flag_bit<20>(flags_) ? Operation::LDC : Operation::STC;
|
2024-02-28 19:43:31 +00:00
|
|
|
|
}
|
|
|
|
|
constexpr bool pre_index() const { return flag_bit<24>(flags_); }
|
|
|
|
|
constexpr bool add_offset() const { return flag_bit<23>(flags_); }
|
|
|
|
|
constexpr bool transfer_length() const { return flag_bit<22>(flags_); }
|
|
|
|
|
constexpr bool write_back_address() const { return flag_bit<21>(flags_); }
|
2024-02-21 20:32:27 +00:00
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
uint8_t flags_;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct CoprocessorDataTransfer {
|
|
|
|
|
constexpr CoprocessorDataTransfer(uint32_t opcode) noexcept : opcode_(opcode) {}
|
|
|
|
|
|
2024-02-28 19:43:31 +00:00
|
|
|
|
int base() const { return (opcode_ >> 16) & 0xf; }
|
2024-02-21 20:32:27 +00:00
|
|
|
|
|
2024-02-28 19:43:31 +00:00
|
|
|
|
int source() const { return (opcode_ >> 12) & 0xf; }
|
|
|
|
|
int destination() const { return (opcode_ >> 12) & 0xf; }
|
2024-02-21 20:32:27 +00:00
|
|
|
|
|
2024-02-28 19:43:31 +00:00
|
|
|
|
int coprocessor() const { return (opcode_ >> 8) & 0xf; }
|
|
|
|
|
int offset() const { return opcode_ & 0xff; }
|
2024-02-21 20:32:27 +00:00
|
|
|
|
|
|
|
|
|
private:
|
2024-02-21 20:25:57 +00:00
|
|
|
|
uint32_t opcode_;
|
|
|
|
|
};
|
2024-02-21 19:17:01 +00:00
|
|
|
|
|
2024-02-21 20:32:27 +00:00
|
|
|
|
/// Operation mapper; use the free function @c dispatch as defined below.
|
2024-03-03 02:47:09 +00:00
|
|
|
|
template <Model>
|
2024-02-21 19:17:01 +00:00
|
|
|
|
struct OperationMapper {
|
2024-02-27 02:36:23 +00:00
|
|
|
|
static Condition condition(uint32_t instruction) {
|
|
|
|
|
return Condition(instruction >> 28);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <int i, typename SchedulerT>
|
|
|
|
|
static void dispatch(uint32_t instruction, SchedulerT &scheduler) {
|
|
|
|
|
// Put the 8-bit segment of instruction back into its proper place;
|
|
|
|
|
// this allows all the tests below to be written so as to coordinate
|
|
|
|
|
// properly with the data sheet, and since it's all compile-time work
|
|
|
|
|
// it doesn't cost anything.
|
2024-02-21 20:25:57 +00:00
|
|
|
|
constexpr auto partial = uint32_t(i << 20);
|
2024-02-21 19:17:01 +00:00
|
|
|
|
|
2024-02-21 19:18:41 +00:00
|
|
|
|
// Cf. the ARM2 datasheet, p.45. Tests below match its ordering
|
|
|
|
|
// other than that 'undefined' is the fallthrough case. More specific
|
|
|
|
|
// page references are provided were more detailed versions of the
|
|
|
|
|
// decoding are depicted.
|
|
|
|
|
|
2024-02-21 19:17:01 +00:00
|
|
|
|
// Multiply and multiply-accumulate (MUL, MLA); cf. p.23.
|
2024-03-07 16:45:39 +00:00
|
|
|
|
//
|
|
|
|
|
// This usurps a potential data processing decoding, so needs priority.
|
2024-02-21 19:51:51 +00:00
|
|
|
|
if constexpr (((partial >> 22) & 0b111'111) == 0b000'000) {
|
|
|
|
|
// This implementation provides only eight bits baked into the template parameters so
|
|
|
|
|
// an additional dynamic test is required to check whether this is really, really MUL or MLA.
|
2024-02-22 15:16:54 +00:00
|
|
|
|
if(((instruction >> 4) & 0b1111) == 0b1001) {
|
2024-02-28 16:27:27 +00:00
|
|
|
|
scheduler.template perform<i>(Multiply(instruction));
|
2024-02-22 15:16:54 +00:00
|
|
|
|
return;
|
2024-02-21 19:17:01 +00:00
|
|
|
|
}
|
2024-02-21 19:51:51 +00:00
|
|
|
|
}
|
2024-02-21 19:17:01 +00:00
|
|
|
|
|
2024-03-07 16:45:39 +00:00
|
|
|
|
// Data processing; cf. p.17.
|
|
|
|
|
if constexpr (((partial >> 26) & 0b11) == 0b00) {
|
2024-03-07 16:48:44 +00:00
|
|
|
|
// TODO: This decoding technically requires that either b4 is 0 or, failing that, that b7 is 0.
|
|
|
|
|
// i.e. b4 and b7 set should be rejected. Which is not quite fully tested by the multiply
|
|
|
|
|
// condition above.
|
2024-03-07 16:45:39 +00:00
|
|
|
|
scheduler.template perform<i>(DataProcessing(instruction));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-21 19:51:51 +00:00
|
|
|
|
// Single data transfer (LDR, STR); cf. p.25.
|
|
|
|
|
if constexpr (((partial >> 26) & 0b11) == 0b01) {
|
2024-02-29 02:23:57 +00:00
|
|
|
|
scheduler.template perform<i>(SingleDataTransfer(instruction));
|
2024-02-22 15:16:54 +00:00
|
|
|
|
return;
|
2024-02-21 19:17:01 +00:00
|
|
|
|
}
|
2024-02-21 20:25:57 +00:00
|
|
|
|
|
|
|
|
|
// Block data transfer (LDM, STM); cf. p.29.
|
|
|
|
|
if constexpr (((partial >> 25) & 0b111) == 0b100) {
|
2024-02-29 02:23:57 +00:00
|
|
|
|
scheduler.template perform<i>(BlockDataTransfer(instruction));
|
2024-02-22 15:16:54 +00:00
|
|
|
|
return;
|
2024-02-21 20:25:57 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Branch and branch with link (B, BL); cf. p.15.
|
|
|
|
|
if constexpr (((partial >> 25) & 0b111) == 0b101) {
|
2024-02-28 16:33:28 +00:00
|
|
|
|
scheduler.template perform<i>(Branch(instruction));
|
2024-02-22 15:16:54 +00:00
|
|
|
|
return;
|
2024-02-21 20:25:57 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Software interreupt; cf. p.35.
|
|
|
|
|
if constexpr (((partial >> 24) & 0b1111) == 0b1111) {
|
2024-02-28 16:42:33 +00:00
|
|
|
|
scheduler.software_interrupt();
|
2024-02-22 15:16:54 +00:00
|
|
|
|
return;
|
2024-02-21 20:25:57 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Both:
|
|
|
|
|
// Coprocessor data operation; cf. p. 37; and
|
|
|
|
|
// Coprocessor register transfers; cf. p. 42.
|
|
|
|
|
if constexpr (((partial >> 24) & 0b1111) == 0b1110) {
|
|
|
|
|
if(instruction & (1 << 4)) {
|
|
|
|
|
// Register transfer.
|
2024-02-28 19:43:31 +00:00
|
|
|
|
scheduler.template perform<i>(CoprocessorRegisterTransfer(instruction));
|
2024-02-21 20:25:57 +00:00
|
|
|
|
} else {
|
|
|
|
|
// Data operation.
|
2024-02-28 19:43:31 +00:00
|
|
|
|
scheduler.template perform<i>(CoprocessorDataOperation(instruction));
|
2024-02-21 20:25:57 +00:00
|
|
|
|
}
|
2024-02-22 15:16:54 +00:00
|
|
|
|
return;
|
2024-02-21 20:25:57 +00:00
|
|
|
|
}
|
2024-02-21 20:32:27 +00:00
|
|
|
|
|
|
|
|
|
// Coprocessor data transfers; cf. p.39.
|
|
|
|
|
if constexpr (((partial >> 25) & 0b111) == 0b110) {
|
2024-02-28 19:43:31 +00:00
|
|
|
|
scheduler.template perform<i>(CoprocessorDataTransfer(instruction));
|
2024-02-22 15:16:54 +00:00
|
|
|
|
return;
|
2024-02-21 20:32:27 +00:00
|
|
|
|
}
|
2024-02-22 15:16:54 +00:00
|
|
|
|
|
|
|
|
|
// Fallback position.
|
2024-02-28 19:43:31 +00:00
|
|
|
|
scheduler.unknown();
|
2024-02-21 19:17:01 +00:00
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2024-02-22 15:16:54 +00:00
|
|
|
|
/// A brief documentation of the interface expected by @c dispatch below; will be a concept if/when this project adopts C++20.
|
|
|
|
|
struct SampleScheduler {
|
2024-02-29 02:23:57 +00:00
|
|
|
|
/// @returns @c true if the rest of the instruction should be decoded and supplied
|
|
|
|
|
/// to the scheduler as defined below; @c false otherwise.
|
|
|
|
|
bool should_schedule(Condition condition);
|
|
|
|
|
|
|
|
|
|
// Template argument:
|
2024-02-22 15:16:54 +00:00
|
|
|
|
//
|
2024-02-29 02:23:57 +00:00
|
|
|
|
// Flags, an opaque type which can be converted into a DataProcessingFlags, MultiplyFlags, etc,
|
|
|
|
|
// by simple construction, to provide all flags that can be baked into the template parameters.
|
2024-02-22 15:16:54 +00:00
|
|
|
|
//
|
2024-02-29 02:23:57 +00:00
|
|
|
|
// Function argument:
|
2024-02-22 15:16:54 +00:00
|
|
|
|
//
|
2024-02-29 02:23:57 +00:00
|
|
|
|
// An operation-specific encapsulation of the operation code for decoding of fields that didn't
|
2024-02-22 15:16:54 +00:00
|
|
|
|
// fit into the template parameters.
|
2024-02-29 02:23:57 +00:00
|
|
|
|
//
|
|
|
|
|
// Either or both may be omitted if unnecessary.
|
2024-02-27 02:36:23 +00:00
|
|
|
|
template <Flags> void perform(DataProcessing);
|
2024-02-28 16:42:33 +00:00
|
|
|
|
template <Flags> void perform(Multiply);
|
2024-02-29 02:23:57 +00:00
|
|
|
|
template <Flags> void perform(SingleDataTransfer);
|
|
|
|
|
template <Flags> void perform(BlockDataTransfer);
|
2024-02-28 16:42:33 +00:00
|
|
|
|
template <Flags> void perform(Branch);
|
2024-02-28 19:43:31 +00:00
|
|
|
|
template <Flags> void perform(CoprocessorRegisterTransfer);
|
|
|
|
|
template <Flags> void perform(CoprocessorDataOperation);
|
2024-02-29 02:23:57 +00:00
|
|
|
|
template <Flags> void perform(CoprocessorDataTransfer);
|
2024-02-22 15:16:54 +00:00
|
|
|
|
|
|
|
|
|
// Irregular operations.
|
2024-02-28 16:42:33 +00:00
|
|
|
|
void software_interrupt();
|
2024-02-29 02:23:57 +00:00
|
|
|
|
void unknown();
|
2024-02-22 15:16:54 +00:00
|
|
|
|
};
|
|
|
|
|
|
2024-02-21 19:18:41 +00:00
|
|
|
|
/// Decodes @c instruction, making an appropriate call into @c scheduler.
|
2024-02-22 15:16:54 +00:00
|
|
|
|
///
|
2024-02-29 02:23:57 +00:00
|
|
|
|
/// In lieu of C++20, see the sample definition of SampleScheduler above for the expected interface.
|
2024-03-03 02:47:09 +00:00
|
|
|
|
template <Model model, typename SchedulerT> void dispatch(uint32_t instruction, SchedulerT &scheduler) {
|
|
|
|
|
OperationMapper<model> mapper;
|
2024-02-27 02:36:23 +00:00
|
|
|
|
|
|
|
|
|
// Test condition.
|
|
|
|
|
const auto condition = mapper.condition(instruction);
|
|
|
|
|
if(!scheduler.should_schedule(condition)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Dispatch body.
|
2024-02-21 19:51:51 +00:00
|
|
|
|
Reflection::dispatch(mapper, (instruction >> FlagsStartBit) & 0xff, instruction, scheduler);
|
2024-02-21 19:17:01 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-02-17 02:35:49 +00:00
|
|
|
|
}
|