1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-06-25 03:29:45 +00:00

Accept the C++ I'm in; clarify and simplify interface.

This commit is contained in:
Thomas Harte 2024-02-22 10:16:54 -05:00
parent f9cbec668b
commit d205e538e1
2 changed files with 142 additions and 68 deletions

View File

@ -17,49 +17,39 @@ enum class Model {
};
enum class Operation {
/// Rd = Op1 AND Op2.
AND,
/// Rd = Op1 EOR Op2.
EOR,
/// Rd = Op1 - Op2.
SUB,
/// Rd = Op2 - Op1.
RSB,
/// Rd = Op1 + Op2.
ADD,
/// Rd = Op1 + Ord2 + C.
ADC,
/// Rd = Op1 - Op2 + C.
SBC,
/// Rd = Op2 - Op1 + C.
RSC,
/// Set condition codes on Op1 AND Op2.
TST,
/// Set condition codes on Op1 EOR Op2.
TEQ,
/// Set condition codes on Op1 - Op2.
CMP,
/// Set condition codes on Op1 + Op2.
CMN,
/// Rd = Op1 OR Op2.
ORR,
/// Rd = Op2
MOV,
/// Rd = Op1 AND NOT Op2.
BIC,
/// Rd = NOT Op2.
MVN,
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.
MUL, MLA,
B, BL,
MUL, /// Rd = Rm * Rs
MLA, /// Rd = Rm * Rs + Rn
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.
LDR, STR,
LDM, STM,
SWI,
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.
LDM, /// Read 116 words from [base], possibly mutating it.
STM, /// Write 1-16 words to [base], possibly mutating it.
SWI, /// Perform a software interrupt.
CDP,
MRC, MCR,
LDC, STC,
CDP, /// Coprocessor data operation.
MRC, /// Move from coprocessor register to ARM register.
MCR, /// Move from ARM register to coprocessor register.
LDC, /// Coprocessor data transfer load.
STC, /// Coprocessor data transfer store.
Undefined,
};
@ -78,8 +68,11 @@ enum class ShiftType {
RotateRight = 0b11,
};
//
// Implementation details.
//
static constexpr int FlagsStartBit = 20;
using Flags = uint8_t;
template <int position>
constexpr bool flag_bit(uint8_t flags) {
@ -105,6 +98,19 @@ protected:
uint32_t opcode_;
};
//
// Branch (i.e. B and BL).
//
struct Branch {
constexpr Branch(uint32_t opcode) noexcept : opcode_(opcode) {}
/// The 26-bit offset to add to the PC.
int offset() const { return (opcode_ & 0xff'ffff) << 2; }
private:
uint32_t opcode_;
};
//
// Data processing (i.e. AND to MVN).
//
@ -229,7 +235,7 @@ struct BlockDataTransfer: public WithShiftControlBits {
};
//
// Coprocessor data operation and register transfer.
// Coprocessor data operation.
//
struct CoprocessorDataOperationFlags {
constexpr CoprocessorDataOperationFlags(uint8_t flags) noexcept : flags_(flags) {}
@ -240,6 +246,22 @@ private:
uint8_t flags_;
};
struct CoprocessorDataOperation {
constexpr CoprocessorDataOperation(uint32_t opcode) noexcept : opcode_(opcode) {}
int operand1() { return (opcode_ >> 16) & 0xf; }
int operand2() { return opcode_ & 0xf; }
int destination() { return (opcode_ >> 12) & 0xf; }
int coprocessor() { return (opcode_ >> 8) & 0xf; }
int information() { return (opcode_ >> 5) & 0x7; }
private:
uint32_t opcode_;
};
//
// Coprocessor register transfer.
//
struct CoprocessorRegisterTransferFlags {
constexpr CoprocessorRegisterTransferFlags(uint8_t flags) noexcept : flags_(flags) {}
@ -249,8 +271,8 @@ private:
uint8_t flags_;
};
struct CoprocessorOperationOrRegisterTransfer {
constexpr CoprocessorOperationOrRegisterTransfer(uint32_t opcode) noexcept : opcode_(opcode) {}
struct CoprocessorRegisterTransfer {
constexpr CoprocessorRegisterTransfer(uint32_t opcode) noexcept : opcode_(opcode) {}
int operand1() { return (opcode_ >> 16) & 0xf; }
int operand2() { return opcode_ & 0xf; }
@ -306,103 +328,134 @@ struct OperationMapper {
// 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>(
scheduler.template perform<operation, i>(
condition,
DataProcessing(instruction)
);
return;
}
// Multiply and multiply-accumulate (MUL, MLA); cf. p.23.
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.
if(((instruction >> 4) & 0b1111) != 0b1001) {
scheduler.unknown(instruction);
} else {
if(((instruction >> 4) & 0b1111) == 0b1001) {
constexpr bool is_mla = partial & (1 << 21);
constexpr auto flags = MultiplyFlags(i);
scheduler.template multiply<is_mla ? Operation::MLA : Operation::MUL, flags>(
scheduler.template perform<is_mla ? Operation::MLA : Operation::MUL, i>(
condition,
Multiply(instruction)
);
return;
}
}
// Single data transfer (LDR, STR); cf. p.25.
if constexpr (((partial >> 26) & 0b11) == 0b01) {
constexpr bool is_ldr = partial & (1 << 20);
constexpr auto flags = SingleDataTransferFlags(i);
scheduler.template single_data_transfer<is_ldr ? Operation::LDR : Operation::STR, flags>(
scheduler.template perform<is_ldr ? Operation::LDR : Operation::STR, i>(
condition,
SingleDataTransfer(instruction)
);
return;
}
// Block data transfer (LDM, STM); cf. p.29.
if constexpr (((partial >> 25) & 0b111) == 0b100) {
constexpr bool is_ldm = partial & (1 << 20);
constexpr auto flags = BlockDataTransferFlags(i);
scheduler.template block_data_transfer<is_ldm ? Operation::LDM : Operation::STM, flags>(
scheduler.template perform<is_ldm ? Operation::LDM : Operation::STM, i>(
condition,
BlockDataTransfer(instruction)
);
return;
}
// Branch and branch with link (B, BL); cf. p.15.
if constexpr (((partial >> 25) & 0b111) == 0b101) {
constexpr bool is_bl = partial & (1 << 24);
scheduler.template branch<is_bl ? Operation::BL : Operation::B>(
scheduler.template perform<is_bl ? Operation::BL : Operation::B>(
condition,
(instruction & 0xf'ffff) << 2
Branch(instruction)
);
return;
}
// Software interreupt; cf. p.35.
if constexpr (((partial >> 24) & 0b1111) == 0b1111) {
scheduler.software_interrupt(condition);
return;
}
// Both:
// Coprocessor data operation; cf. p. 37; and
// Coprocessor register transfers; cf. p. 42.
if constexpr (((partial >> 24) & 0b1111) == 0b1110) {
const auto parameters = CoprocessorOperationOrRegisterTransfer(instruction);
// TODO: parameters should probably vary.
if(instruction & (1 << 4)) {
// Register transfer.
constexpr auto flags = CoprocessorRegisterTransferFlags(i);
const auto parameters = CoprocessorRegisterTransfer(instruction);
constexpr bool is_mrc = partial & (1 << 20);
scheduler.template coprocessor_register_transfer<is_mrc ? Operation::MRC : Operation::MCR, flags>(
scheduler.template perform<is_mrc ? Operation::MRC : Operation::MCR, i>(
condition,
parameters
);
} else {
// Data operation.
constexpr auto flags = CoprocessorDataOperationFlags(i);
scheduler.template coprocessor_data_operation<flags>(
const auto parameters = CoprocessorDataOperation(instruction);
scheduler.template perform<i>(
condition,
parameters
);
}
return;
}
// Coprocessor data transfers; cf. p.39.
if constexpr (((partial >> 25) & 0b111) == 0b110) {
constexpr bool is_ldc = partial & (1 << 20);
constexpr auto flags = CoprocessorDataTransferFlags(i);
scheduler.template coprocessor_data_transfer<is_ldc ? Operation::LDC : Operation::STC, flags>(
scheduler.template perform<is_ldc ? Operation::LDC : Operation::STC, i>(
condition,
CoprocessorDataTransfer(instruction)
);
return;
}
// Fallback position.
scheduler.unknown(instruction);
}
};
/// A brief documentation of the interface expected by @c dispatch below; will be a concept if/when this project adopts C++20.
struct SampleScheduler {
// General template arguments:
//
// (1) Operation, telling the function which operation to perform. Will always be from the subset
// implied by the operation category; and
// (2) Flags, an opaque type which can be converted into a DataProcessingFlags, MultiplyFlags, etc,
// by simply construction, to provide all flags that can be baked into the template parameters.
//
// Arguments are ommitted if not relevant.
//
// Function arguments:
//
// (1) Condition, indicating the condition code associated with this operation; and
// (2) An operation-specific encapsulation of the operation code for decoding of fields that didn't
// fit into the template parameters.
template <Operation, Flags> void perform(Condition, DataProcessing);
template <Operation, Flags> void perform(Condition, Multiply);
template <Operation, Flags> void perform(Condition, SingleDataTransfer);
template <Operation, Flags> void perform(Condition, BlockDataTransfer);
template <Operation> void perform(Condition, Branch);
template <Operation, Flags> void perform(Condition, CoprocessorRegisterTransfer);
template <Flags> void perform(Condition, CoprocessorDataOperation);
template<Operation, Flags> void perform(Condition, CoprocessorDataTransfer);
// Irregular operations.
void software_interrupt(Condition);
void unknown(uint32_t opcode);
};
/// Decodes @c instruction, making an appropriate call into @c scheduler.
///
/// In lieue of C++20, see the sample definition of SampleScheduler above for the expected interface.
template <typename SchedulerT> void dispatch(uint32_t instruction, SchedulerT &scheduler) {
OperationMapper mapper;
Reflection::dispatch(mapper, (instruction >> FlagsStartBit) & 0xff, instruction, scheduler);

View File

@ -12,12 +12,33 @@
using namespace InstructionSet::ARM;
namespace {
struct Scheduler {
template <Operation, Flags> void perform(Condition, DataProcessing) {}
template <Operation, Flags> void perform(Condition, Multiply) {}
template <Operation, Flags> void perform(Condition, SingleDataTransfer) {}
template <Operation, Flags> void perform(Condition, BlockDataTransfer) {}
template <Operation> void perform(Condition, Branch) {}
template <Operation, Flags> void perform(Condition, CoprocessorRegisterTransfer) {}
template <Flags> void perform(Condition, CoprocessorDataOperation) {}
template<Operation, Flags> void perform(Condition, CoprocessorDataTransfer) {}
void software_interrupt(Condition) {}
void unknown(uint32_t) {}
};
}
@interface ARMDecoderTests : XCTestCase
@end
@implementation ARMDecoderTests
- (void)testXYX {
Scheduler scheduler;
InstructionSet::ARM::dispatch(1, scheduler);
// const auto intr = Instruction<Model::ARM2>(1);
// NSLog(@"%d", intr.operation());
}