1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-06-09 17:29:36 +00:00

Merge pull request #1335 from TomHarte/ARMDecoding

Add ARM2 operation mapper.
This commit is contained in:
Thomas Harte 2024-02-22 11:44:03 -05:00 committed by GitHub
commit 4c45f4468e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 541 additions and 9 deletions

View File

@ -59,7 +59,7 @@ struct YamahaFetcher {
type(type),
id(id) {}
constexpr Event() noexcept {}
constexpr Event() noexcept = default;
};
// State that tracks fetching position within a line.

View File

@ -0,0 +1,469 @@
//
// OperationMapper.hpp
// Clock Signal
//
// Created by Thomas Harte on 16/02/2024.
// Copyright © 2024 Thomas Harte. All rights reserved.
//
#pragma once
#include "../../Reflection/Dispatcher.hpp"
namespace InstructionSet::ARM {
enum class Model {
ARM2,
};
enum class Operation {
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, /// 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, /// 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, /// 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,
};
enum class Condition {
EQ, NE, CS, CC,
MI, PL, VS, VC,
HI, LS, GE, LT,
GT, LE, AL, NV,
};
enum class ShiftType {
LogicalLeft = 0b00,
LogicalRight = 0b01,
ArithmeticRight = 0b10,
RotateRight = 0b11,
};
//
// Implementation details.
//
static constexpr int FlagsStartBit = 20;
using Flags = uint8_t;
template <int position>
constexpr bool flag_bit(uint8_t flags) {
static_assert(position >= 20 && position < 28);
return flags & (1 << (position - FlagsStartBit));
}
//
// Methods common to data processing and data transfer.
//
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.
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; }
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).
//
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_); }
/// @c true if the status register should be updated; @c false otherwise.
constexpr bool set_condition_codes() { return flag_bit<20>(flags_); }
private:
uint8_t flags_;
};
struct DataProcessing: public WithShiftControlBits {
using WithShiftControlBits::WithShiftControlBits;
/// 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.
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; }
};
//
// MUL and MLA.
//
struct MultiplyFlags {
constexpr MultiplyFlags(uint8_t flags) noexcept : flags_(flags) {}
/// @c true if the status register should be updated; @c false otherwise.
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_;
};
//
// Single data transfer (LDR, STR).
//
struct SingleDataTransferFlags {
constexpr SingleDataTransferFlags(uint8_t flags) noexcept : flags_(flags) {}
constexpr bool offset_is_immediate() { return flag_bit<25>(flags_); }
constexpr bool pre_index() { return flag_bit<24>(flags_); }
constexpr bool add_offset() { return flag_bit<23>(flags_); }
constexpr bool transfer_byte() { return flag_bit<22>(flags_); }
constexpr bool write_back_address() { return flag_bit<21>(flags_); }
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; }
/// The immediate offset, if @c offset_is_immediate() was @c true; meaningless otherwise.
int immediate() const { return opcode_ & 0xfff; }
};
//
// Block data transfer (LDR, STR).
//
struct BlockDataTransferFlags {
constexpr BlockDataTransferFlags(uint8_t flags) noexcept : flags_(flags) {}
constexpr bool pre_index() { return flag_bit<24>(flags_); }
constexpr bool add_offset() { return flag_bit<23>(flags_); }
constexpr bool load_psr() { return flag_bit<22>(flags_); }
constexpr bool write_back_address() { return flag_bit<21>(flags_); }
private:
uint8_t flags_;
};
struct BlockDataTransfer: public WithShiftControlBits {
using WithShiftControlBits::WithShiftControlBits;
/// The base register index. i.e. 'Rn'.
int base() const { return (opcode_ >> 16) & 0xf; }
/// A bitfield indicating which registers to load or store.
int register_list() const { return opcode_ & 0xffff; }
};
//
// Coprocessor data operation.
//
struct CoprocessorDataOperationFlags {
constexpr CoprocessorDataOperationFlags(uint8_t flags) noexcept : flags_(flags) {}
constexpr int operation() const { return (flags_ >> (FlagsStartBit - 20)) & 0xf; }
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) {}
constexpr int operation() const { return (flags_ >> (FlagsStartBit - 20)) & 0x7; }
private:
uint8_t flags_;
};
struct CoprocessorRegisterTransfer {
constexpr CoprocessorRegisterTransfer(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 data transfer.
//
struct CoprocessorDataTransferFlags {
constexpr CoprocessorDataTransferFlags(uint8_t flags) noexcept : flags_(flags) {}
constexpr bool pre_index() { return flag_bit<24>(flags_); }
constexpr bool add_offset() { return flag_bit<23>(flags_); }
constexpr bool transfer_length() { return flag_bit<22>(flags_); }
constexpr bool write_back_address() { return flag_bit<21>(flags_); }
private:
uint8_t flags_;
};
struct CoprocessorDataTransfer {
constexpr CoprocessorDataTransfer(uint32_t opcode) noexcept : opcode_(opcode) {}
int base() { return (opcode_ >> 16) & 0xf; }
int source() { return (opcode_ >> 12) & 0xf; }
int destination() { return (opcode_ >> 12) & 0xf; }
int coprocessor() { return (opcode_ >> 8) & 0xf; }
int offset() { return opcode_ & 0xff; }
private:
uint32_t opcode_;
};
/// Operation mapper; use the free function @c dispatch as defined below.
struct OperationMapper {
template <int i, typename SchedulerT> void dispatch(uint32_t instruction, SchedulerT &scheduler) {
constexpr auto partial = uint32_t(i << 20);
const auto condition = Condition(instruction >> 28);
// 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.
// Data processing; cf. p.17.
if constexpr (((partial >> 26) & 0b11) == 0b00) {
constexpr auto operation = Operation(int(Operation::AND) + ((partial >> 21) & 0xf));
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) {
constexpr bool is_mla = partial & (1 << 21);
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);
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);
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 perform<is_bl ? Operation::BL : Operation::B>(
condition,
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) {
if(instruction & (1 << 4)) {
// Register transfer.
const auto parameters = CoprocessorRegisterTransfer(instruction);
constexpr bool is_mrc = partial & (1 << 20);
scheduler.template perform<is_mrc ? Operation::MRC : Operation::MCR, i>(
condition,
parameters
);
} else {
// Data operation.
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);
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

@ -1362,9 +1362,9 @@ struct Instruction {
bool is_supervisor = false;
uint32_t opcode = 0;
Instruction() noexcept {}
Instruction(uint32_t opcode) noexcept : opcode(opcode) {}
Instruction(Operation operation, uint32_t opcode, bool is_supervisor = false) noexcept : operation(operation), is_supervisor(is_supervisor), opcode(opcode) {}
constexpr Instruction() noexcept = default;
constexpr Instruction(uint32_t opcode) noexcept : opcode(opcode) {}
constexpr Instruction(Operation operation, uint32_t opcode, bool is_supervisor = false) noexcept : operation(operation), is_supervisor(is_supervisor), opcode(opcode) {}
// Instruction fields are decoded below; naming is a compromise between
// Motorola's documentation and IBM's.

View File

@ -564,7 +564,7 @@ constexpr Operation rep_operation(Operation operation, Repetition repetition) {
/// It cannot natively describe a base of ::None.
class ScaleIndexBase {
public:
constexpr ScaleIndexBase() noexcept {}
constexpr ScaleIndexBase() noexcept = default;
constexpr ScaleIndexBase(uint8_t sib) noexcept : sib_(sib) {}
constexpr ScaleIndexBase(int scale, Source index, Source base) noexcept :
sib_(uint8_t(
@ -703,7 +703,7 @@ template<bool is_32bit> class Instruction {
using ImmediateT = typename std::conditional<is_32bit, uint32_t, uint16_t>::type;
using AddressT = ImmediateT;
constexpr Instruction() noexcept {}
constexpr Instruction() noexcept = default;
constexpr Instruction(Operation operation) noexcept :
Instruction(operation, Source::None, Source::None, ScaleIndexBase(), false, AddressSize::b16, Source::None, DataSize::None, 0, 0) {}
constexpr Instruction(

View File

@ -213,6 +213,7 @@
4B1EC716255398B000A1F44B /* Sound.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B1EC714255398B000A1F44B /* Sound.cpp */; };
4B1EC717255398B000A1F44B /* Sound.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B1EC714255398B000A1F44B /* Sound.cpp */; };
4B1EDB451E39A0AC009D6819 /* chip.png in Resources */ = {isa = PBXBuildFile; fileRef = 4B1EDB431E39A0AC009D6819 /* chip.png */; };
4B2005432B804D6400420C5C /* ARMDecoderTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B2005422B804D6400420C5C /* ARMDecoderTests.mm */; };
4B2130E2273A7A0A008A77B4 /* Audio.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B2130E0273A7A0A008A77B4 /* Audio.cpp */; };
4B2130E3273A7A0A008A77B4 /* Audio.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B2130E0273A7A0A008A77B4 /* Audio.cpp */; };
4B228CD524D773B40077EF25 /* CSScanTarget.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B228CD424D773B30077EF25 /* CSScanTarget.mm */; };
@ -1332,6 +1333,8 @@
4B1EC714255398B000A1F44B /* Sound.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Sound.cpp; sourceTree = "<group>"; };
4B1EC715255398B000A1F44B /* Sound.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Sound.hpp; sourceTree = "<group>"; };
4B1EDB431E39A0AC009D6819 /* chip.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = chip.png; sourceTree = "<group>"; };
4B2005402B804AA300420C5C /* OperationMapper.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = OperationMapper.hpp; sourceTree = "<group>"; };
4B2005422B804D6400420C5C /* ARMDecoderTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ARMDecoderTests.mm; sourceTree = "<group>"; };
4B2130E0273A7A0A008A77B4 /* Audio.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Audio.cpp; sourceTree = "<group>"; };
4B2130E1273A7A0A008A77B4 /* Audio.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Audio.hpp; sourceTree = "<group>"; };
4B228CD424D773B30077EF25 /* CSScanTarget.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = CSScanTarget.mm; sourceTree = "<group>"; };
@ -2752,6 +2755,14 @@
path = Icons;
sourceTree = "<group>";
};
4B20053D2B804A4F00420C5C /* ARM */ = {
isa = PBXGroup;
children = (
4B2005402B804AA300420C5C /* OperationMapper.hpp */,
);
path = ARM;
sourceTree = "<group>";
};
4B228CD324D773B30077EF25 /* ScanTarget */ = {
isa = PBXGroup;
children = (
@ -4471,6 +4482,7 @@
4B9D0C4E22C7E0CF00DE1AD3 /* 68000RollShiftTests.mm */,
4BD388872239E198002D14B5 /* 68000Tests.mm */,
4BF7019F26FFD32300996424 /* AmigaBlitterTests.mm */,
4B2005422B804D6400420C5C /* ARMDecoderTests.mm */,
4B924E981E74D22700B76AF1 /* AtariStaticAnalyserTests.mm */,
4BE34437238389E10058E78F /* AtariSTVideoTests.mm */,
4BB2A9AE1E13367E001A5C23 /* CRCTests.mm */,
@ -5082,6 +5094,7 @@
4BE8EB5425C0E9D40040BC40 /* Disassembler.hpp */,
4BEDA3B625B25563000C2DBD /* README.md */,
4BDA7F7F29C4C179007A10A5 /* 6809 */,
4B20053D2B804A4F00420C5C /* ARM */,
4B79629B2819681F008130F9 /* M68k */,
4BEDA40925B2844B000C2DBD /* M50740 */,
4BEDA3B325B25563000C2DBD /* PowerPC */,
@ -6334,6 +6347,7 @@
4B778F2823A5EEF80000D260 /* Cartridge.cpp in Sources */,
4B7752B528217ED30073E2C5 /* SNA.cpp in Sources */,
4BEDA3C025B25563000C2DBD /* Decoder.cpp in Sources */,
4B2005432B804D6400420C5C /* ARMDecoderTests.mm in Sources */,
4B778F4C23A5F2090000D260 /* StaticAnalyser.cpp in Sources */,
4B778F2623A5EE350000D260 /* Acorn.cpp in Sources */,
4B7752C128217F490073E2C5 /* FAT.cpp in Sources */,

View File

@ -0,0 +1,49 @@
//
// ARMDecoderTests.m
// Clock Signal
//
// Created by Thomas Harte on 16/02/2024.
// Copyright 2024 Thomas Harte. All rights reserved.
//
#import <XCTest/XCTest.h>
#include "../../../InstructionSets/ARM/OperationMapper.hpp"
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 op> void perform(Condition condition, Branch branch) {
printf("Branch %sif %d; add %08x\n", op == Operation::BL ? "with link " : "", int(condition), branch.offset());
}
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(0xEAE06900, scheduler);
// const auto intr = Instruction<Model::ARM2>(1);
// NSLog(@"%d", intr.operation());
}
@end

View File

@ -203,7 +203,7 @@ class BufferingScanTarget: public Outputs::Display::ScanTarget {
struct PointerSet {
// This constructor is here to appease GCC's interpretation of
// an ambiguity in the C++ standard; cf. https://stackoverflow.com/questions/17430377
PointerSet() noexcept {}
PointerSet() noexcept = default;
// Squeezing this struct into 64 bits makes the std::atomics more likely
// to be lock free; they are under LLVM x86-64.

View File

@ -145,7 +145,7 @@ struct Microcycle: public MicrocycleOperationStorage<op> {
*/
SlicedInt16 *value = nullptr;
constexpr Microcycle() noexcept {}
constexpr Microcycle() noexcept = default;
constexpr Microcycle(OperationT dynamic_operation) noexcept {
if constexpr (op == Operation::DecodeDynamically) {
MicrocycleOperationStorage<op>::operation = dynamic_operation;

View File

@ -40,7 +40,7 @@ struct Sector {
bool has_header_crc_error = false;
bool is_deleted = false;
Sector() noexcept {}
Sector() noexcept = default;
Sector(const Sector &&rhs) noexcept :
address(rhs.address),