mirror of
https://github.com/TomHarte/CLK.git
synced 2024-11-25 16:31:42 +00:00
Start mutating towards a form that owns the switch.
This commit is contained in:
parent
954d920b9e
commit
0fe2c1406b
@ -11,10 +11,151 @@
|
||||
#include "Model.hpp"
|
||||
#include "Operation.hpp"
|
||||
|
||||
#include "../../Reflection/Dispatcher.hpp"
|
||||
|
||||
#include <array>
|
||||
|
||||
namespace InstructionSet::ARM {
|
||||
|
||||
enum class ShiftType {
|
||||
LogicalLeft = 0b00,
|
||||
LogicalRight = 0b01,
|
||||
ArithmeticRight = 0b10,
|
||||
RotateRight = 0b11,
|
||||
};
|
||||
|
||||
|
||||
static constexpr int FlagsStartBit = 20;
|
||||
|
||||
template <int position>
|
||||
constexpr bool flag_bit(uint8_t flags) {
|
||||
static_assert(position >= 20 && position < 28);
|
||||
return flags & (1 << (position - FlagsStartBit));
|
||||
}
|
||||
|
||||
//
|
||||
// Data processing (i.e. AND to MVN).
|
||||
//
|
||||
struct DataProcessingFlags {
|
||||
constexpr DataProcessingFlags(uint8_t flags) noexcept : flags_(flags) {}
|
||||
|
||||
/// @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.
|
||||
constexpr bool operand2_is_immediate() { return flag_bit<25>(flags_); }
|
||||
|
||||
constexpr bool set_condition_codes() { return flag_bit<20>(flags_); }
|
||||
|
||||
private:
|
||||
uint8_t flags_;
|
||||
};
|
||||
|
||||
struct DataProcessing {
|
||||
constexpr DataProcessing(uint32_t opcode) noexcept : opcode_(opcode) {}
|
||||
|
||||
/// 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; }
|
||||
|
||||
|
||||
//
|
||||
// Register values for operand 2.
|
||||
//
|
||||
|
||||
/// The operand 2 register index if @c operand2_is_immediate() is @c false; meaningless otherwise.
|
||||
int operand2() const { return opcode_ & 0xf; }
|
||||
/// 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; }
|
||||
|
||||
//
|
||||
// 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.
|
||||
int immediate() const { return opcode_ & 0xff; }
|
||||
/// The number of bits to rotate @c immediate() by to the right if @c operand2_is_immediate() is @c true; meaningless otherwise.
|
||||
int rotate() const { return (opcode_ >> 7) & 0x1e; }
|
||||
|
||||
private:
|
||||
uint32_t opcode_;
|
||||
};
|
||||
|
||||
//
|
||||
// MUL and MLA.
|
||||
//
|
||||
struct MultiplyFlags {
|
||||
constexpr MultiplyFlags(uint8_t flags) noexcept : flags_(flags) {}
|
||||
|
||||
constexpr bool set_condition_codes() { return flag_bit<20>(flags_); }
|
||||
|
||||
private:
|
||||
uint8_t flags_;
|
||||
};
|
||||
|
||||
struct Multiply {
|
||||
constexpr Multiply(uint32_t opcode) noexcept : opcode_(opcode) {}
|
||||
|
||||
/// The destination register index. i.e. 'Rd'.
|
||||
int destination() const { return (opcode_ >> 16) & 0xf; }
|
||||
|
||||
/// The accumulator register index for multiply-add. i.e. 'Rn'.
|
||||
int accumulator() const { return (opcode_ >> 12) & 0xf; }
|
||||
|
||||
/// The multiplicand register index. i.e. 'Rs'.
|
||||
int multiplicand() const { return (opcode_ >> 8) & 0xf; }
|
||||
|
||||
/// The multiplier register index. i.e. 'Rm'.
|
||||
int multiplier() const { return opcode_ & 0xf; }
|
||||
|
||||
private:
|
||||
uint32_t opcode_;
|
||||
};
|
||||
|
||||
|
||||
struct OperationMapper {
|
||||
template <int i, typename SchedulerT> void dispatch(uint32_t instruction, SchedulerT &scheduler) {
|
||||
constexpr auto partial = static_cast<uint32_t>(i << 20);
|
||||
|
||||
// Data processing; cf. p.17.
|
||||
if constexpr (((partial >> 26) & 0b11) == 0b00) {
|
||||
constexpr auto operation = Operation(int(Operation::AND) + ((partial >> 21) & 0xf));
|
||||
constexpr auto flags = DataProcessingFlags(i);
|
||||
scheduler.template data_processing<operation, flags>(
|
||||
DataProcessing(instruction)
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Multiply and multiply-accumulate (MUL, MLA); cf. p.23.
|
||||
if(((partial >> 22) & 0b111'111) == 0b000'000) {
|
||||
if(((instruction >> 4) & 0b1111) != 0b1001) {
|
||||
scheduler.unknown(instruction);
|
||||
} else {
|
||||
constexpr bool is_mla = partial & (1 << 21);
|
||||
constexpr auto flags = MultiplyFlags(i);
|
||||
scheduler.template multiply<is_mla ? Operation::MLA : Operation::MUL, flags>(
|
||||
Multiply(instruction)
|
||||
);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <typename SchedulerT> void dispatch(uint32_t instruction, SchedulerT &scheduler) {
|
||||
OperationMapper mapper;
|
||||
Reflection::dispatch(mapper, (instruction >> 20) & 0xff, instruction, scheduler);
|
||||
}
|
||||
|
||||
/*
|
||||
template <Model model>
|
||||
using OperationTable = std::array<Operation, 256>;
|
||||
|
||||
@ -113,4 +254,6 @@ constexpr Operation operation(uint32_t opcode) {
|
||||
return op;
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
}
|
||||
|
@ -14,7 +14,7 @@
|
||||
|
||||
namespace InstructionSet::ARM {
|
||||
|
||||
enum class ShiftType {
|
||||
/*enum class ShiftType {
|
||||
LogicalLeft = 0b00,
|
||||
LogicalRight = 0b01,
|
||||
ArithmeticRight = 0b10,
|
||||
@ -140,7 +140,7 @@ class Instruction {
|
||||
|
||||
private:
|
||||
uint32_t opcode_;
|
||||
};
|
||||
};*/
|
||||
|
||||
// TODO: do MUL and MLA really transpose Rd and Rn as per the data sheet?
|
||||
// ARM: Assembly Language Programming by Cockerell thinks not.
|
||||
|
@ -44,39 +44,7 @@ enum class Operation {
|
||||
/// Rd = NOT Op2.
|
||||
MVN,
|
||||
|
||||
/// Rd = Op1 AND Op2.
|
||||
ANDS,
|
||||
/// Rd = Op1 EOR Op2.
|
||||
EORS,
|
||||
/// Rd = Op1 - Op2.
|
||||
SUBS,
|
||||
/// Rd = Op2 - Op1.
|
||||
RSBS,
|
||||
/// Rd = Op1 + Op2.
|
||||
ADDS,
|
||||
/// Rd = Op1 + Ord2 + C.
|
||||
ADCS,
|
||||
/// Rd = Op1 - Op2 + C.
|
||||
SBCS,
|
||||
/// Rd = Op2 - Op1 + C.
|
||||
RSCS,
|
||||
/// Set condition codes on Op1 AND Op2.
|
||||
TSTS,
|
||||
/// Set condition codes on Op1 EOR Op2.
|
||||
TEQS,
|
||||
/// Set condition codes on Op1 - Op2.
|
||||
CMPS,
|
||||
/// Set condition codes on Op1 + Op2.
|
||||
CMNS,
|
||||
/// Rd = Op1 OR Op2.
|
||||
ORRS,
|
||||
/// Rd = Op2
|
||||
MOVS,
|
||||
/// Rd = Op1 AND NOT Op2.
|
||||
BICS,
|
||||
/// Rd = NOT Op2.
|
||||
MVNS,
|
||||
|
||||
MUL, MLA,
|
||||
B, BL,
|
||||
|
||||
LDR, STR,
|
||||
@ -87,21 +55,8 @@ enum class Operation {
|
||||
CoprocessorDataTransfer,
|
||||
|
||||
Undefined,
|
||||
|
||||
// These are kept at the end for a minor decoding win; they can be only partially decoded
|
||||
// with the table-based decoder used elsewhere so a special case checks more bits upon
|
||||
// a MUL or MLA and keeping these at the end of the enum allows a single conditional to
|
||||
// determine whether the extra decoding is needed.
|
||||
//
|
||||
// See is_multiply below.
|
||||
MUL, MLA,
|
||||
MULS, MLAS,
|
||||
};
|
||||
|
||||
constexpr bool is_multiply(Operation op) {
|
||||
return op >= Operation::MUL;
|
||||
}
|
||||
|
||||
enum class Condition {
|
||||
EQ, NE, CS, CC,
|
||||
MI, PL, VS, VC,
|
||||
|
Loading…
Reference in New Issue
Block a user