1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-09-28 09:54:49 +00:00

Extend what's held in the operation enum.

This commit is contained in:
Thomas Harte 2024-02-20 14:14:18 -05:00
parent 57b45076c5
commit 954d920b9e
3 changed files with 150 additions and 36 deletions

View File

@ -31,14 +31,23 @@ constexpr OperationTable<model> operation_table() {
// Data processing; cf. p.17.
if(((opcode >> 26) & 0b11) == 0b00) {
result[c] = Operation((c >> 21) & 0xf);
const auto operation = (c >> 21) & 0xf;
if((opcode >> 20) & 1) {
result[c] = Operation(int(Operation::ANDS) + operation);
} else {
result[c] = Operation(int(Operation::AND) + operation);
}
continue;
}
// Multiply and multiply-accumulate (MUL, MLA); cf. p.23.
if(((opcode >> 22) & 0b111'111) == 0b000'000) {
result[c] =
((opcode >> 21) & 1) ? Operation::MLA : Operation::MUL;
switch((opcode >> 20) & 3) {
case 0: result[c] = Operation::MUL; break;
case 1: result[c] = Operation::MULS; break;
case 2: result[c] = Operation::MLA; break;
case 3: result[c] = Operation::MLAS; break;
}
continue;
}
@ -91,8 +100,11 @@ constexpr Operation operation(uint32_t opcode) {
// MUL and MLA have an extra constraint that doesn't fit the neat
// 256-entry table format as above.
//
// Hope: most instructions aren't MUL/MLA so relying on the branch predictor
// here is fine.
if(
(op == Operation::MUL || op == Operation::MLA)
is_multiply(op)
&& ((opcode >> 4) & 0b1111) != 0b1001
) {
return Operation::Undefined;

View File

@ -26,60 +26,117 @@ class Instruction {
public:
constexpr Instruction(uint32_t opcode) noexcept : opcode_(opcode) {}
Condition condition() const { return Condition(opcode_ >> 28); }
Operation operation() const {
constexpr Condition condition() const { return Condition(opcode_ >> 28); }
constexpr Operation operation() const {
return InstructionSet::ARM::operation<model>(opcode_);
}
//
// B and BL.
//
struct Branch {
constexpr Branch(uint32_t opcode) noexcept : opcode_(opcode) {}
/// Provides a 26-bit offset to add to the program counter for B and BL.
uint32_t b_offset() const { return (opcode_ & 0xff'ffff) << 2; }
/// Provides a 26-bit offset to add to the program counter for B and BL.
uint32_t offset() const { return (opcode_ & 0xff'ffff) << 2; }
private:
uint32_t opcode_;
};
Branch branch() const { return Branch(opcode_); }
//
// Data processing (i.e. AND to MVN).
//
struct DataProcessing {
constexpr DataProcessing(uint32_t opcode) noexcept : opcode_(opcode) {}
/// @returns @c true if this operation should set condition codes; @c false otherwise.
/// @note Valid for data processing and multiply/multiply-accumulate.
bool set_condition_codes() const { return opcode_ & (1 << 20); }
// TODO: could build this into the Operation?
/// The destination register index.
int destination() const { return (opcode_ >> 12) & 0xf; }
/// The destination register index.
int destination() const { return (opcode_ >> 12) & 0xf; }
/// The operand 1 register index.
int operand1() const { return (opcode_ >> 16) & 0xf; }
/// The operand 1 register index.
int operand1() const { return (opcode_ >> 16) & 0xf; }
/// @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.
bool operand2_is_immediate() const { return opcode_ & (1 << 25); }
/// @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.
bool operand2_is_immediate() const { return opcode_ & (1 << 25); }
//
// 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 if @c operand2_is_immediate() is @c true; meaningless otherwise.
int rotate() const { return (opcode_ >> 7) & 0x1e; }
private:
uint32_t opcode_;
};
DataProcessing data_processing() const { return DataProcessing(opcode_); }
//
// Register values for operand 2.
// MUL and MLA.
//
struct Multiply {
constexpr Multiply(uint32_t opcode) noexcept : opcode_(opcode) {}
/// 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; }
/// 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_;
};
Multiply multiply() const { return Multiply(opcode_); }
//
// Immediate values for operand 2.
// LDR and STR.
//
struct SingleDataTransfer {
constexpr SingleDataTransfer(uint32_t opcode) noexcept : opcode_(opcode) {}
/// 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 if @c operand2_is_immediate() is @c true; meaningless otherwise.
int rotate() const { return (opcode_ >> 7) & 0x1e; }
/// 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; }
///
int offset() const { return opcode_ & 0xfff; }
// TODO: P, U, B, W, L, I.
private:
uint32_t opcode_;
};
private:
uint32_t opcode_;

View File

@ -44,8 +44,40 @@ 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,
B, BL,
MUL, MLA,
LDR, STR,
LDM, STM,
@ -55,8 +87,21 @@ 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,