From bd0a15c054ede4d58b9394ad3f951c8cfe3a5094 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 16 Feb 2024 21:35:49 -0500 Subject: [PATCH 01/14] Start working on ARM2 decoding. --- InstructionSets/ARM/Decoder.hpp | 77 +++++++++++++++++++ InstructionSets/ARM/Instruction.hpp | 31 ++++++++ InstructionSets/ARM/Model.hpp | 17 ++++ InstructionSets/ARM/Operation.hpp | 39 ++++++++++ InstructionSets/PowerPC/Instruction.hpp | 6 +- .../Clock Signal.xcodeproj/project.pbxproj | 20 +++++ 6 files changed, 187 insertions(+), 3 deletions(-) create mode 100644 InstructionSets/ARM/Decoder.hpp create mode 100644 InstructionSets/ARM/Instruction.hpp create mode 100644 InstructionSets/ARM/Model.hpp create mode 100644 InstructionSets/ARM/Operation.hpp diff --git a/InstructionSets/ARM/Decoder.hpp b/InstructionSets/ARM/Decoder.hpp new file mode 100644 index 000000000..f68be950f --- /dev/null +++ b/InstructionSets/ARM/Decoder.hpp @@ -0,0 +1,77 @@ +// +// Decoder.hpp +// Clock Signal +// +// Created by Thomas Harte on 16/02/2024. +// Copyright © 2024 Thomas Harte. All rights reserved. +// + +#pragma once + +#include "Model.hpp" +#include "Operation.hpp" + +#include + +namespace InstructionSet::ARM { + +template +constexpr std::array operation_table() { + std::array result{}; + for(int c = 0; c < 256; c++) { + const uint32_t opcode = c << 20; + + if(((opcode >> 26) & 0b11) == 0b00) { + result[c] = Operation((c >> 21) & 0xf); + continue; + } + + if(((opcode >> 25) & 0b111) == 0b101) { + result[c] = + ((opcode >> 24) & 1) ? Operation::BranchWithLink : Operation::Branch; + continue; + } + + if(((opcode >> 22) & 0b111'111) == 0b000'000) { + result[c] = + ((opcode >> 21) & 1) ? Operation::MultiplyWithAccumulate : Operation::Multiply; + continue; + } + + if(((opcode >> 26) & 0b11) == 0b01) { + result[c] = Operation::SingleDataTransfer; + continue; + } + + if(((opcode >> 25) & 0b111) == 0b100) { + result[c] = Operation::BlockDataTransfer; + continue; + } + + if(((opcode >> 24) & 0b1111) == 0b1111) { + result[c] = Operation::SoftwareInterrupt; + continue; + } + + if(((opcode >> 24) & 0b1111) == 0b1110) { + result[c] = Operation::CoprocessorDataOperationOrRegisterTransfer; + continue; + } + + if(((opcode >> 25) & 0b111) == 0b110) { + result[c] = Operation::CoprocessorDataTransfer; + continue; + } + + result[c] = Operation::Undefined; + } + return result; +} + +template +constexpr Operation operation(uint32_t opcode) { + constexpr std::array operations = operation_table(); + return operations[(opcode >> 20) & 0xff]; +} + +} diff --git a/InstructionSets/ARM/Instruction.hpp b/InstructionSets/ARM/Instruction.hpp new file mode 100644 index 000000000..300e00a50 --- /dev/null +++ b/InstructionSets/ARM/Instruction.hpp @@ -0,0 +1,31 @@ +// +// Instruction.hpp +// Clock Signal +// +// Created by Thomas Harte on 16/02/2024. +// Copyright © 2024 Thomas Harte. All rights reserved. +// + +#pragma once + +#include "Decoder.hpp" +#include "Model.hpp" +#include "Operation.hpp" + +namespace InstructionSet::ARM { + +template +class Instruction { + public: + constexpr Instruction(uint32_t opcode) noexcept : opcode_(opcode) {} + + Condition condition() const { return Condition(opcode_ >> 28); } + Operation operation() const { + return InstructionSet::ARM::operation(opcode_); + } + + private: + uint32_t opcode_; +}; + +} diff --git a/InstructionSets/ARM/Model.hpp b/InstructionSets/ARM/Model.hpp new file mode 100644 index 000000000..ce6104137 --- /dev/null +++ b/InstructionSets/ARM/Model.hpp @@ -0,0 +1,17 @@ +// +// Model.hpp +// Clock Signal +// +// Created by Thomas Harte on 16/02/2024. +// Copyright © 2024 Thomas Harte. All rights reserved. +// + +#pragma once + +namespace InstructionSet::ARM { + +enum class Model { + ARM2, +}; + +} diff --git a/InstructionSets/ARM/Operation.hpp b/InstructionSets/ARM/Operation.hpp new file mode 100644 index 000000000..978441f68 --- /dev/null +++ b/InstructionSets/ARM/Operation.hpp @@ -0,0 +1,39 @@ +// +// Operation.hpp +// Clock Signal +// +// Created by Thomas Harte on 16/02/2024. +// Copyright © 2024 Thomas Harte. All rights reserved. +// + +#pragma once + +namespace InstructionSet::ARM { + +enum class Operation { + AND, EOR, SUB, RSB, + ADD, ADC, SBC, RSC, + TST, TEQ, CMP, CMN, + ORR, MOV, BIC, MVN, + + Branch, BranchWithLink, + Multiply, MultiplyWithAccumulate, + + SingleDataTransfer, + BlockDataTransfer, + SoftwareInterrupt, + + CoprocessorDataOperationOrRegisterTransfer, + CoprocessorDataTransfer, + + Undefined, +}; + +enum class Condition { + EQ, NE, CS, CC, + MI, PL, VS, VC, + HI, LS, GE, LT, + GT, LE, AL, NV, +}; + +} diff --git a/InstructionSets/PowerPC/Instruction.hpp b/InstructionSets/PowerPC/Instruction.hpp index 7116d3a0b..73895e4e1 100644 --- a/InstructionSets/PowerPC/Instruction.hpp +++ b/InstructionSets/PowerPC/Instruction.hpp @@ -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. diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index 069031998..eeabd41c1 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -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,11 @@ 4B1EC714255398B000A1F44B /* Sound.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Sound.cpp; sourceTree = ""; }; 4B1EC715255398B000A1F44B /* Sound.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Sound.hpp; sourceTree = ""; }; 4B1EDB431E39A0AC009D6819 /* chip.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = chip.png; sourceTree = ""; }; + 4B20053E2B804A4F00420C5C /* Instruction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Instruction.hpp; sourceTree = ""; }; + 4B2005402B804AA300420C5C /* Decoder.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Decoder.hpp; sourceTree = ""; }; + 4B2005422B804D6400420C5C /* ARMDecoderTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ARMDecoderTests.mm; sourceTree = ""; }; + 4B2005442B804DC900420C5C /* Model.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Model.hpp; sourceTree = ""; }; + 4B2005452B804DF600420C5C /* Operation.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Operation.hpp; sourceTree = ""; }; 4B2130E0273A7A0A008A77B4 /* Audio.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Audio.cpp; sourceTree = ""; }; 4B2130E1273A7A0A008A77B4 /* Audio.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Audio.hpp; sourceTree = ""; }; 4B228CD424D773B30077EF25 /* CSScanTarget.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = CSScanTarget.mm; sourceTree = ""; }; @@ -2752,6 +2758,17 @@ path = Icons; sourceTree = ""; }; + 4B20053D2B804A4F00420C5C /* ARM */ = { + isa = PBXGroup; + children = ( + 4B2005402B804AA300420C5C /* Decoder.hpp */, + 4B20053E2B804A4F00420C5C /* Instruction.hpp */, + 4B2005442B804DC900420C5C /* Model.hpp */, + 4B2005452B804DF600420C5C /* Operation.hpp */, + ); + path = ARM; + sourceTree = ""; + }; 4B228CD324D773B30077EF25 /* ScanTarget */ = { isa = PBXGroup; children = ( @@ -4471,6 +4488,7 @@ 4B9D0C4E22C7E0CF00DE1AD3 /* 68000RollShiftTests.mm */, 4BD388872239E198002D14B5 /* 68000Tests.mm */, 4BF7019F26FFD32300996424 /* AmigaBlitterTests.mm */, + 4B2005422B804D6400420C5C /* ARMDecoderTests.mm */, 4B924E981E74D22700B76AF1 /* AtariStaticAnalyserTests.mm */, 4BE34437238389E10058E78F /* AtariSTVideoTests.mm */, 4BB2A9AE1E13367E001A5C23 /* CRCTests.mm */, @@ -5082,6 +5100,7 @@ 4BE8EB5425C0E9D40040BC40 /* Disassembler.hpp */, 4BEDA3B625B25563000C2DBD /* README.md */, 4BDA7F7F29C4C179007A10A5 /* 6809 */, + 4B20053D2B804A4F00420C5C /* ARM */, 4B79629B2819681F008130F9 /* M68k */, 4BEDA40925B2844B000C2DBD /* M50740 */, 4BEDA3B325B25563000C2DBD /* PowerPC */, @@ -6334,6 +6353,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 */, From 9a74ab6a8e9f430c040e66156f5cf094a25f8a71 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 17 Feb 2024 15:41:57 -0500 Subject: [PATCH 02/14] Switch to actual mnenomics, temporarily(?) shrink table. --- InstructionSets/ARM/Decoder.hpp | 40 ++++++++++++++++++------------- InstructionSets/ARM/Operation.hpp | 8 +++---- 2 files changed, 27 insertions(+), 21 deletions(-) diff --git a/InstructionSets/ARM/Decoder.hpp b/InstructionSets/ARM/Decoder.hpp index f68be950f..b11300373 100644 --- a/InstructionSets/ARM/Decoder.hpp +++ b/InstructionSets/ARM/Decoder.hpp @@ -16,25 +16,25 @@ namespace InstructionSet::ARM { template -constexpr std::array operation_table() { - std::array result{}; - for(int c = 0; c < 256; c++) { - const uint32_t opcode = c << 20; +using OperationTable = std::array; + +template +constexpr OperationTable operation_table() { + OperationTable result{}; + for(std::size_t c = 0; c < result.size(); c++) { + const auto opcode = static_cast(c << 21); + + // Cf. the ARM2 datasheet, p45. Tests below match its ordering + // other than that 'undefined' is the fallthrough case. if(((opcode >> 26) & 0b11) == 0b00) { result[c] = Operation((c >> 21) & 0xf); continue; } - if(((opcode >> 25) & 0b111) == 0b101) { - result[c] = - ((opcode >> 24) & 1) ? Operation::BranchWithLink : Operation::Branch; - continue; - } - if(((opcode >> 22) & 0b111'111) == 0b000'000) { result[c] = - ((opcode >> 21) & 1) ? Operation::MultiplyWithAccumulate : Operation::Multiply; + ((opcode >> 21) & 1) ? Operation::MLA : Operation::MUL; continue; } @@ -48,8 +48,14 @@ constexpr std::array operation_table() { continue; } - if(((opcode >> 24) & 0b1111) == 0b1111) { - result[c] = Operation::SoftwareInterrupt; + if(((opcode >> 25) & 0b111) == 0b101) { + result[c] = + ((opcode >> 24) & 1) ? Operation::BL : Operation::B; + continue; + } + + if(((opcode >> 25) & 0b111) == 0b110) { + result[c] = Operation::CoprocessorDataTransfer; continue; } @@ -58,8 +64,8 @@ constexpr std::array operation_table() { continue; } - if(((opcode >> 25) & 0b111) == 0b110) { - result[c] = Operation::CoprocessorDataTransfer; + if(((opcode >> 24) & 0b1111) == 0b1111) { + result[c] = Operation::SoftwareInterrupt; continue; } @@ -70,8 +76,8 @@ constexpr std::array operation_table() { template constexpr Operation operation(uint32_t opcode) { - constexpr std::array operations = operation_table(); - return operations[(opcode >> 20) & 0xff]; + constexpr OperationTable operations = operation_table(); + return operations[(opcode >> 21) & 0x7f]; } } diff --git a/InstructionSets/ARM/Operation.hpp b/InstructionSets/ARM/Operation.hpp index 978441f68..6b2450513 100644 --- a/InstructionSets/ARM/Operation.hpp +++ b/InstructionSets/ARM/Operation.hpp @@ -16,11 +16,11 @@ enum class Operation { TST, TEQ, CMP, CMN, ORR, MOV, BIC, MVN, - Branch, BranchWithLink, - Multiply, MultiplyWithAccumulate, + B, BL, + MUL, MLA, - SingleDataTransfer, - BlockDataTransfer, + SingleDataTransfer, // TODO: LDR or STR? + BlockDataTransfer, // TODO: LDM or STM? SoftwareInterrupt, CoprocessorDataOperationOrRegisterTransfer, From d639dc8bcb3eec80b2aa84a13513ca897feeca64 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 17 Feb 2024 15:42:31 -0500 Subject: [PATCH 03/14] Hit up some more `= default` opportunities. --- Components/9918/Implementation/Storage.hpp | 2 +- InstructionSets/x86/Instruction.hpp | 4 ++-- Outputs/ScanTargets/BufferingScanTarget.hpp | 2 +- Processors/68000/68000.hpp | 2 +- Storage/Disk/Encodings/MFM/Sector.hpp | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Components/9918/Implementation/Storage.hpp b/Components/9918/Implementation/Storage.hpp index 1d3389781..c1260eb99 100644 --- a/Components/9918/Implementation/Storage.hpp +++ b/Components/9918/Implementation/Storage.hpp @@ -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. diff --git a/InstructionSets/x86/Instruction.hpp b/InstructionSets/x86/Instruction.hpp index a1e475152..10d1f18f4 100644 --- a/InstructionSets/x86/Instruction.hpp +++ b/InstructionSets/x86/Instruction.hpp @@ -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 class Instruction { using ImmediateT = typename std::conditional::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( diff --git a/Outputs/ScanTargets/BufferingScanTarget.hpp b/Outputs/ScanTargets/BufferingScanTarget.hpp index 896aa8286..9d43c22c7 100644 --- a/Outputs/ScanTargets/BufferingScanTarget.hpp +++ b/Outputs/ScanTargets/BufferingScanTarget.hpp @@ -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. diff --git a/Processors/68000/68000.hpp b/Processors/68000/68000.hpp index 0b271292b..013d36145 100644 --- a/Processors/68000/68000.hpp +++ b/Processors/68000/68000.hpp @@ -145,7 +145,7 @@ struct Microcycle: public MicrocycleOperationStorage { */ SlicedInt16 *value = nullptr; - constexpr Microcycle() noexcept {} + constexpr Microcycle() noexcept = default; constexpr Microcycle(OperationT dynamic_operation) noexcept { if constexpr (op == Operation::DecodeDynamically) { MicrocycleOperationStorage::operation = dynamic_operation; diff --git a/Storage/Disk/Encodings/MFM/Sector.hpp b/Storage/Disk/Encodings/MFM/Sector.hpp index 646b7d3ea..d07d05777 100644 --- a/Storage/Disk/Encodings/MFM/Sector.hpp +++ b/Storage/Disk/Encodings/MFM/Sector.hpp @@ -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), From 57b45076c545ab6c83bde37114441b3f3e3ba120 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 17 Feb 2024 22:13:51 -0500 Subject: [PATCH 04/14] Start dealing with per-instruction fields. --- InstructionSets/ARM/Decoder.hpp | 35 +++++++++++++---- InstructionSets/ARM/Instruction.hpp | 60 +++++++++++++++++++++++++++++ InstructionSets/ARM/Operation.hpp | 40 ++++++++++++++++--- 3 files changed, 122 insertions(+), 13 deletions(-) diff --git a/InstructionSets/ARM/Decoder.hpp b/InstructionSets/ARM/Decoder.hpp index b11300373..c9f1a7b7c 100644 --- a/InstructionSets/ARM/Decoder.hpp +++ b/InstructionSets/ARM/Decoder.hpp @@ -16,38 +16,47 @@ namespace InstructionSet::ARM { template -using OperationTable = std::array; +using OperationTable = std::array; template constexpr OperationTable operation_table() { OperationTable result{}; for(std::size_t c = 0; c < result.size(); c++) { - const auto opcode = static_cast(c << 21); + const auto opcode = static_cast(c << 20); - // Cf. the ARM2 datasheet, p45. Tests below match its ordering - // other than that 'undefined' is the fallthrough case. + // 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(((opcode >> 26) & 0b11) == 0b00) { result[c] = Operation((c >> 21) & 0xf); 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; continue; } + // Single data transfer (LDR, STR); cf. p.25. if(((opcode >> 26) & 0b11) == 0b01) { - result[c] = Operation::SingleDataTransfer; + result[c] = + ((opcode >> 20) & 1) ? Operation::LDR : Operation::STR; continue; } + // Block data transfer (LDM, STM); cf. p.29. if(((opcode >> 25) & 0b111) == 0b100) { - result[c] = Operation::BlockDataTransfer; + result[c] = + ((opcode >> 20) & 1) ? Operation::LDM : Operation::STM; continue; } + // Branch and branch with link (B, BL); cf. p.15. if(((opcode >> 25) & 0b111) == 0b101) { result[c] = ((opcode >> 24) & 1) ? Operation::BL : Operation::B; @@ -77,7 +86,19 @@ constexpr OperationTable operation_table() { template constexpr Operation operation(uint32_t opcode) { constexpr OperationTable operations = operation_table(); - return operations[(opcode >> 21) & 0x7f]; + + const auto op = operations[(opcode >> 21) & 0x7f]; + + // MUL and MLA have an extra constraint that doesn't fit the neat + // 256-entry table format as above. + if( + (op == Operation::MUL || op == Operation::MLA) + && ((opcode >> 4) & 0b1111) != 0b1001 + ) { + return Operation::Undefined; + } + + return op; } } diff --git a/InstructionSets/ARM/Instruction.hpp b/InstructionSets/ARM/Instruction.hpp index 300e00a50..e26d5c585 100644 --- a/InstructionSets/ARM/Instruction.hpp +++ b/InstructionSets/ARM/Instruction.hpp @@ -14,6 +14,13 @@ namespace InstructionSet::ARM { +enum class ShiftType { + LogicalLeft = 0b00, + LogicalRight = 0b01, + ArithmeticRight = 0b10, + RotateRight = 0b11, +}; + template class Instruction { public: @@ -24,8 +31,61 @@ class Instruction { return InstructionSet::ARM::operation(opcode_); } + // + // B and BL. + // + + /// 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; } + + // + // Data processing (i.e. AND to MVN). + // + + /// @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 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); } + + // + // 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_; }; +// TODO: do MUL and MLA really transpose Rd and Rn as per the data sheet? +// ARM: Assembly Language Programming by Cockerell thinks not. + } diff --git a/InstructionSets/ARM/Operation.hpp b/InstructionSets/ARM/Operation.hpp index 6b2450513..af06081a3 100644 --- a/InstructionSets/ARM/Operation.hpp +++ b/InstructionSets/ARM/Operation.hpp @@ -11,16 +11,44 @@ namespace InstructionSet::ARM { enum class Operation { - AND, EOR, SUB, RSB, - ADD, ADC, SBC, RSC, - TST, TEQ, CMP, CMN, - ORR, MOV, BIC, MVN, + /// 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, B, BL, MUL, MLA, - SingleDataTransfer, // TODO: LDR or STR? - BlockDataTransfer, // TODO: LDM or STM? + LDR, STR, + LDM, STM, SoftwareInterrupt, CoprocessorDataOperationOrRegisterTransfer, From 954d920b9ef1bf8f92a33b955683b51c5d159fc2 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 20 Feb 2024 14:14:18 -0500 Subject: [PATCH 05/14] Extend what's held in the operation enum. --- InstructionSets/ARM/Decoder.hpp | 20 ++++- InstructionSets/ARM/Instruction.hpp | 119 ++++++++++++++++++++-------- InstructionSets/ARM/Operation.hpp | 47 ++++++++++- 3 files changed, 150 insertions(+), 36 deletions(-) diff --git a/InstructionSets/ARM/Decoder.hpp b/InstructionSets/ARM/Decoder.hpp index c9f1a7b7c..8e1ede1f2 100644 --- a/InstructionSets/ARM/Decoder.hpp +++ b/InstructionSets/ARM/Decoder.hpp @@ -31,14 +31,23 @@ constexpr OperationTable 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; diff --git a/InstructionSets/ARM/Instruction.hpp b/InstructionSets/ARM/Instruction.hpp index e26d5c585..c9b8fec0f 100644 --- a/InstructionSets/ARM/Instruction.hpp +++ b/InstructionSets/ARM/Instruction.hpp @@ -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(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_; diff --git a/InstructionSets/ARM/Operation.hpp b/InstructionSets/ARM/Operation.hpp index af06081a3..4c78b06c6 100644 --- a/InstructionSets/ARM/Operation.hpp +++ b/InstructionSets/ARM/Operation.hpp @@ -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, From 0fe2c1406b61a8fd8dd4b25336d132a34f5d009f Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 21 Feb 2024 14:17:01 -0500 Subject: [PATCH 06/14] Start mutating towards a form that owns the switch. --- InstructionSets/ARM/Decoder.hpp | 143 ++++++++++++++++++++++++++++ InstructionSets/ARM/Instruction.hpp | 4 +- InstructionSets/ARM/Operation.hpp | 47 +-------- 3 files changed, 146 insertions(+), 48 deletions(-) diff --git a/InstructionSets/ARM/Decoder.hpp b/InstructionSets/ARM/Decoder.hpp index 8e1ede1f2..febe92584 100644 --- a/InstructionSets/ARM/Decoder.hpp +++ b/InstructionSets/ARM/Decoder.hpp @@ -11,10 +11,151 @@ #include "Model.hpp" #include "Operation.hpp" +#include "../../Reflection/Dispatcher.hpp" + #include namespace InstructionSet::ARM { +enum class ShiftType { + LogicalLeft = 0b00, + LogicalRight = 0b01, + ArithmeticRight = 0b10, + RotateRight = 0b11, +}; + + +static constexpr int FlagsStartBit = 20; + +template +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 void dispatch(uint32_t instruction, SchedulerT &scheduler) { + constexpr auto partial = static_cast(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( + 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( + Multiply(instruction) + ); + } + + return; + } + } +}; + +template void dispatch(uint32_t instruction, SchedulerT &scheduler) { + OperationMapper mapper; + Reflection::dispatch(mapper, (instruction >> 20) & 0xff, instruction, scheduler); +} + +/* template using OperationTable = std::array; @@ -113,4 +254,6 @@ constexpr Operation operation(uint32_t opcode) { return op; } +*/ + } diff --git a/InstructionSets/ARM/Instruction.hpp b/InstructionSets/ARM/Instruction.hpp index c9b8fec0f..d242f041d 100644 --- a/InstructionSets/ARM/Instruction.hpp +++ b/InstructionSets/ARM/Instruction.hpp @@ -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. diff --git a/InstructionSets/ARM/Operation.hpp b/InstructionSets/ARM/Operation.hpp index 4c78b06c6..e9adcf2a2 100644 --- a/InstructionSets/ARM/Operation.hpp +++ b/InstructionSets/ARM/Operation.hpp @@ -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, From 2bbaf73aa27c5a51181ee8316dea569df22c3f49 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 21 Feb 2024 14:18:41 -0500 Subject: [PATCH 07/14] Delete was is now duplicated. --- InstructionSets/ARM/Decoder.hpp | 63 ++-------------- InstructionSets/ARM/Instruction.hpp | 112 +--------------------------- 2 files changed, 8 insertions(+), 167 deletions(-) diff --git a/InstructionSets/ARM/Decoder.hpp b/InstructionSets/ARM/Decoder.hpp index febe92584..e4ffe0e57 100644 --- a/InstructionSets/ARM/Decoder.hpp +++ b/InstructionSets/ARM/Decoder.hpp @@ -123,6 +123,11 @@ struct OperationMapper { template void dispatch(uint32_t instruction, SchedulerT &scheduler) { constexpr auto partial = static_cast(i << 20); + // 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)); @@ -150,47 +155,15 @@ struct OperationMapper { } }; +/// Decodes @c instruction, making an appropriate call into @c scheduler. template void dispatch(uint32_t instruction, SchedulerT &scheduler) { OperationMapper mapper; Reflection::dispatch(mapper, (instruction >> 20) & 0xff, instruction, scheduler); } /* -template -using OperationTable = std::array; -template -constexpr OperationTable operation_table() { - OperationTable result{}; - for(std::size_t c = 0; c < result.size(); c++) { - const auto opcode = static_cast(c << 20); - // 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(((opcode >> 26) & 0b11) == 0b00) { - 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) { - 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; - } // Single data transfer (LDR, STR); cf. p.25. if(((opcode >> 26) & 0b11) == 0b01) { @@ -229,30 +202,6 @@ constexpr OperationTable operation_table() { } result[c] = Operation::Undefined; - } - return result; -} - -template -constexpr Operation operation(uint32_t opcode) { - constexpr OperationTable operations = operation_table(); - - const auto op = operations[(opcode >> 21) & 0x7f]; - - // 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( - is_multiply(op) - && ((opcode >> 4) & 0b1111) != 0b1001 - ) { - return Operation::Undefined; - } - - return op; -} */ diff --git a/InstructionSets/ARM/Instruction.hpp b/InstructionSets/ARM/Instruction.hpp index d242f041d..5bcefa5a0 100644 --- a/InstructionSets/ARM/Instruction.hpp +++ b/InstructionSets/ARM/Instruction.hpp @@ -14,104 +14,7 @@ namespace InstructionSet::ARM { -/*enum class ShiftType { - LogicalLeft = 0b00, - LogicalRight = 0b01, - ArithmeticRight = 0b10, - RotateRight = 0b11, -}; - -template -class Instruction { - public: - constexpr Instruction(uint32_t opcode) noexcept : opcode_(opcode) {} - - constexpr Condition condition() const { return Condition(opcode_ >> 28); } - constexpr Operation operation() const { - return InstructionSet::ARM::operation(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 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) {} - - /// The destination register index. - int destination() const { return (opcode_ >> 12) & 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); } - - // - // 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_); } - - // - // MUL and MLA. - // - 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_; - }; - Multiply multiply() const { return Multiply(opcode_); } +/* // // LDR and STR. @@ -132,17 +35,6 @@ class Instruction { int offset() const { return opcode_ & 0xfff; } // TODO: P, U, B, W, L, I. - - private: - uint32_t opcode_; - - }; - - 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. +*/ } From b2696450d5958ce895128b30cfc1056d65b7f223 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 21 Feb 2024 14:51:51 -0500 Subject: [PATCH 08/14] Bring forwards single data transfers. --- InstructionSets/ARM/Decoder.hpp | 97 ++++++++++++++++++++++----------- 1 file changed, 65 insertions(+), 32 deletions(-) diff --git a/InstructionSets/ARM/Decoder.hpp b/InstructionSets/ARM/Decoder.hpp index e4ffe0e57..257fba017 100644 --- a/InstructionSets/ARM/Decoder.hpp +++ b/InstructionSets/ARM/Decoder.hpp @@ -33,6 +33,25 @@ constexpr bool flag_bit(uint8_t flags) { return flags & (1 << (position - FlagsStartBit)); } +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_; +}; + // // Data processing (i.e. AND to MVN). // @@ -49,8 +68,8 @@ private: uint8_t flags_; }; -struct DataProcessing { - constexpr DataProcessing(uint32_t opcode) noexcept : opcode_(opcode) {} +struct DataProcessing: public WithShiftControlBits { + using WithShiftControlBits::WithShiftControlBits; /// The destination register index. i.e. Rd. int destination() const { return (opcode_ >> 12) & 0xf; } @@ -58,22 +77,6 @@ struct DataProcessing { /// 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. // @@ -82,9 +85,6 @@ struct DataProcessing { 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_; }; // @@ -118,6 +118,38 @@ 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; } +}; + struct OperationMapper { template void dispatch(uint32_t instruction, SchedulerT &scheduler) { @@ -135,11 +167,12 @@ struct OperationMapper { scheduler.template data_processing( DataProcessing(instruction) ); - return; } // Multiply and multiply-accumulate (MUL, MLA); cf. p.23. - if(((partial >> 22) & 0b111'111) == 0b000'000) { + 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 { @@ -149,8 +182,15 @@ struct OperationMapper { 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( + SingleDataTransfer(instruction) + ); } } }; @@ -158,20 +198,13 @@ struct OperationMapper { /// Decodes @c instruction, making an appropriate call into @c scheduler. template void dispatch(uint32_t instruction, SchedulerT &scheduler) { OperationMapper mapper; - Reflection::dispatch(mapper, (instruction >> 20) & 0xff, instruction, scheduler); + Reflection::dispatch(mapper, (instruction >> FlagsStartBit) & 0xff, instruction, scheduler); } /* - // Single data transfer (LDR, STR); cf. p.25. - if(((opcode >> 26) & 0b11) == 0b01) { - result[c] = - ((opcode >> 20) & 1) ? Operation::LDR : Operation::STR; - continue; - } - // Block data transfer (LDM, STM); cf. p.29. if(((opcode >> 25) & 0b111) == 0b100) { result[c] = From e986ae2878ed53d7c90b3aa0b1ef54650486c3d4 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 21 Feb 2024 15:25:57 -0500 Subject: [PATCH 09/14] Add coprocessor data operations and register transfers. --- InstructionSets/ARM/Decoder.hpp | 145 ++++++++++++++---- InstructionSets/ARM/Instruction.hpp | 40 ----- InstructionSets/ARM/Operation.hpp | 5 +- .../Clock Signal.xcodeproj/project.pbxproj | 2 - 4 files changed, 117 insertions(+), 75 deletions(-) delete mode 100644 InstructionSets/ARM/Instruction.hpp diff --git a/InstructionSets/ARM/Decoder.hpp b/InstructionSets/ARM/Decoder.hpp index 257fba017..e2dd01866 100644 --- a/InstructionSets/ARM/Decoder.hpp +++ b/InstructionSets/ARM/Decoder.hpp @@ -47,7 +47,6 @@ struct WithShiftControlBits { /// 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_; }; @@ -150,10 +149,69 @@ struct SingleDataTransfer: public WithShiftControlBits { 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 and register transfer. +// +struct CoprocessorDataOperationFlags { + constexpr CoprocessorDataOperationFlags(uint8_t flags) noexcept : flags_(flags) {} + + constexpr int operation() const { return (flags_ >> (FlagsStartBit - 20)) & 0xf; } + +private: + uint8_t flags_; +}; + +struct CoprocessorRegisterTransferFlags { + constexpr CoprocessorRegisterTransferFlags(uint8_t flags) noexcept : flags_(flags) {} + + constexpr int operation() const { return (flags_ >> (FlagsStartBit - 20)) & 0x7; } + +private: + uint8_t flags_; +}; + +struct CoprocessorOperationOrRegisterTransfer { + constexpr CoprocessorOperationOrRegisterTransfer(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; } + +protected: + uint32_t opcode_; +}; struct OperationMapper { template void dispatch(uint32_t instruction, SchedulerT &scheduler) { - constexpr auto partial = static_cast(i << 20); + 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 @@ -165,6 +223,7 @@ struct OperationMapper { constexpr auto operation = Operation(int(Operation::AND) + ((partial >> 21) & 0xf)); constexpr auto flags = DataProcessingFlags(i); scheduler.template data_processing( + condition, DataProcessing(instruction) ); } @@ -179,6 +238,7 @@ struct OperationMapper { constexpr bool is_mla = partial & (1 << 21); constexpr auto flags = MultiplyFlags(i); scheduler.template multiply( + condition, Multiply(instruction) ); } @@ -189,9 +249,61 @@ struct OperationMapper { constexpr bool is_ldr = partial & (1 << 20); constexpr auto flags = SingleDataTransferFlags(i); scheduler.template single_data_transfer( + condition, SingleDataTransfer(instruction) ); } + + // 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( + condition, + BlockDataTransfer(instruction) + ); + } + + // 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( + condition, + (instruction & 0xf'ffff) << 2 + ); + } + + // Software interreupt; cf. p.35. + if constexpr (((partial >> 24) & 0b1111) == 0b1111) { + scheduler.software_interrupt(condition); + } + + + // 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); + constexpr bool is_mrc = partial & (1 << 20); + scheduler.template coprocessor_register_transfer( + condition, + parameters + ); + } else { + // Data operation. + constexpr auto flags = CoprocessorDataOperationFlags(i); + scheduler.template coprocessor_data_operation( + condition, + parameters + ); + } + } } }; @@ -202,40 +314,11 @@ template void dispatch(uint32_t instruction, SchedulerT &s } /* - - - - // Block data transfer (LDM, STM); cf. p.29. - if(((opcode >> 25) & 0b111) == 0b100) { - result[c] = - ((opcode >> 20) & 1) ? Operation::LDM : Operation::STM; - continue; - } - - // Branch and branch with link (B, BL); cf. p.15. - if(((opcode >> 25) & 0b111) == 0b101) { - result[c] = - ((opcode >> 24) & 1) ? Operation::BL : Operation::B; - continue; - } - if(((opcode >> 25) & 0b111) == 0b110) { result[c] = Operation::CoprocessorDataTransfer; continue; } - if(((opcode >> 24) & 0b1111) == 0b1110) { - result[c] = Operation::CoprocessorDataOperationOrRegisterTransfer; - continue; - } - - if(((opcode >> 24) & 0b1111) == 0b1111) { - result[c] = Operation::SoftwareInterrupt; - continue; - } - - result[c] = Operation::Undefined; - */ } diff --git a/InstructionSets/ARM/Instruction.hpp b/InstructionSets/ARM/Instruction.hpp deleted file mode 100644 index 5bcefa5a0..000000000 --- a/InstructionSets/ARM/Instruction.hpp +++ /dev/null @@ -1,40 +0,0 @@ -// -// Instruction.hpp -// Clock Signal -// -// Created by Thomas Harte on 16/02/2024. -// Copyright © 2024 Thomas Harte. All rights reserved. -// - -#pragma once - -#include "Decoder.hpp" -#include "Model.hpp" -#include "Operation.hpp" - -namespace InstructionSet::ARM { - -/* - - // - // LDR and STR. - // - struct SingleDataTransfer { - constexpr SingleDataTransfer(uint32_t opcode) noexcept : opcode_(opcode) {} - - /// 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. -*/ - -} diff --git a/InstructionSets/ARM/Operation.hpp b/InstructionSets/ARM/Operation.hpp index e9adcf2a2..218881845 100644 --- a/InstructionSets/ARM/Operation.hpp +++ b/InstructionSets/ARM/Operation.hpp @@ -49,9 +49,10 @@ enum class Operation { LDR, STR, LDM, STM, - SoftwareInterrupt, + SWI, - CoprocessorDataOperationOrRegisterTransfer, + CDP, + MRC, MCR, CoprocessorDataTransfer, Undefined, diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index eeabd41c1..752aab031 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -1333,7 +1333,6 @@ 4B1EC714255398B000A1F44B /* Sound.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Sound.cpp; sourceTree = ""; }; 4B1EC715255398B000A1F44B /* Sound.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Sound.hpp; sourceTree = ""; }; 4B1EDB431E39A0AC009D6819 /* chip.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = chip.png; sourceTree = ""; }; - 4B20053E2B804A4F00420C5C /* Instruction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Instruction.hpp; sourceTree = ""; }; 4B2005402B804AA300420C5C /* Decoder.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Decoder.hpp; sourceTree = ""; }; 4B2005422B804D6400420C5C /* ARMDecoderTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ARMDecoderTests.mm; sourceTree = ""; }; 4B2005442B804DC900420C5C /* Model.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Model.hpp; sourceTree = ""; }; @@ -2762,7 +2761,6 @@ isa = PBXGroup; children = ( 4B2005402B804AA300420C5C /* Decoder.hpp */, - 4B20053E2B804A4F00420C5C /* Instruction.hpp */, 4B2005442B804DC900420C5C /* Model.hpp */, 4B2005452B804DF600420C5C /* Operation.hpp */, ); From 6577f68efc04a8110d30a7f626377bb11f00b7eb Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 21 Feb 2024 15:32:27 -0500 Subject: [PATCH 10/14] Complete instruction set; consolidate mapper. --- InstructionSets/ARM/Model.hpp | 17 --- InstructionSets/ARM/Operation.hpp | 68 ---------- .../ARM/{Decoder.hpp => OperationMapper.hpp} | 117 +++++++++++++++--- .../Clock Signal.xcodeproj/project.pbxproj | 8 +- 4 files changed, 104 insertions(+), 106 deletions(-) delete mode 100644 InstructionSets/ARM/Model.hpp delete mode 100644 InstructionSets/ARM/Operation.hpp rename InstructionSets/ARM/{Decoder.hpp => OperationMapper.hpp} (82%) diff --git a/InstructionSets/ARM/Model.hpp b/InstructionSets/ARM/Model.hpp deleted file mode 100644 index ce6104137..000000000 --- a/InstructionSets/ARM/Model.hpp +++ /dev/null @@ -1,17 +0,0 @@ -// -// Model.hpp -// Clock Signal -// -// Created by Thomas Harte on 16/02/2024. -// Copyright © 2024 Thomas Harte. All rights reserved. -// - -#pragma once - -namespace InstructionSet::ARM { - -enum class Model { - ARM2, -}; - -} diff --git a/InstructionSets/ARM/Operation.hpp b/InstructionSets/ARM/Operation.hpp deleted file mode 100644 index 218881845..000000000 --- a/InstructionSets/ARM/Operation.hpp +++ /dev/null @@ -1,68 +0,0 @@ -// -// Operation.hpp -// Clock Signal -// -// Created by Thomas Harte on 16/02/2024. -// Copyright © 2024 Thomas Harte. All rights reserved. -// - -#pragma once - -namespace InstructionSet::ARM { - -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, - - MUL, MLA, - B, BL, - - LDR, STR, - LDM, STM, - SWI, - - CDP, - MRC, MCR, - CoprocessorDataTransfer, - - Undefined, -}; - -enum class Condition { - EQ, NE, CS, CC, - MI, PL, VS, VC, - HI, LS, GE, LT, - GT, LE, AL, NV, -}; - -} diff --git a/InstructionSets/ARM/Decoder.hpp b/InstructionSets/ARM/OperationMapper.hpp similarity index 82% rename from InstructionSets/ARM/Decoder.hpp rename to InstructionSets/ARM/OperationMapper.hpp index e2dd01866..5c5847957 100644 --- a/InstructionSets/ARM/Decoder.hpp +++ b/InstructionSets/ARM/OperationMapper.hpp @@ -1,5 +1,5 @@ // -// Decoder.hpp +// OperationMapper.hpp // Clock Signal // // Created by Thomas Harte on 16/02/2024. @@ -8,15 +8,69 @@ #pragma once -#include "Model.hpp" -#include "Operation.hpp" - #include "../../Reflection/Dispatcher.hpp" -#include - namespace InstructionSet::ARM { +enum class Model { + ARM2, +}; + +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, + + MUL, MLA, + B, BL, + + LDR, STR, + LDM, STM, + SWI, + + CDP, + MRC, MCR, + LDC, STC, + + 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, @@ -204,10 +258,41 @@ struct CoprocessorOperationOrRegisterTransfer { int coprocessor() { return (opcode_ >> 8) & 0xf; } int information() { return (opcode_ >> 5) & 0x7; } -protected: +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 void dispatch(uint32_t instruction, SchedulerT &scheduler) { constexpr auto partial = uint32_t(i << 20); @@ -304,6 +389,16 @@ struct OperationMapper { ); } } + + // 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( + condition, + CoprocessorDataTransfer(instruction) + ); + } } }; @@ -313,12 +408,4 @@ template void dispatch(uint32_t instruction, SchedulerT &s Reflection::dispatch(mapper, (instruction >> FlagsStartBit) & 0xff, instruction, scheduler); } -/* - if(((opcode >> 25) & 0b111) == 0b110) { - result[c] = Operation::CoprocessorDataTransfer; - continue; - } - -*/ - } diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index 752aab031..5534d4fbe 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -1333,10 +1333,8 @@ 4B1EC714255398B000A1F44B /* Sound.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Sound.cpp; sourceTree = ""; }; 4B1EC715255398B000A1F44B /* Sound.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Sound.hpp; sourceTree = ""; }; 4B1EDB431E39A0AC009D6819 /* chip.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = chip.png; sourceTree = ""; }; - 4B2005402B804AA300420C5C /* Decoder.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Decoder.hpp; sourceTree = ""; }; + 4B2005402B804AA300420C5C /* OperationMapper.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = OperationMapper.hpp; sourceTree = ""; }; 4B2005422B804D6400420C5C /* ARMDecoderTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ARMDecoderTests.mm; sourceTree = ""; }; - 4B2005442B804DC900420C5C /* Model.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Model.hpp; sourceTree = ""; }; - 4B2005452B804DF600420C5C /* Operation.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Operation.hpp; sourceTree = ""; }; 4B2130E0273A7A0A008A77B4 /* Audio.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Audio.cpp; sourceTree = ""; }; 4B2130E1273A7A0A008A77B4 /* Audio.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Audio.hpp; sourceTree = ""; }; 4B228CD424D773B30077EF25 /* CSScanTarget.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = CSScanTarget.mm; sourceTree = ""; }; @@ -2760,9 +2758,7 @@ 4B20053D2B804A4F00420C5C /* ARM */ = { isa = PBXGroup; children = ( - 4B2005402B804AA300420C5C /* Decoder.hpp */, - 4B2005442B804DC900420C5C /* Model.hpp */, - 4B2005452B804DF600420C5C /* Operation.hpp */, + 4B2005402B804AA300420C5C /* OperationMapper.hpp */, ); path = ARM; sourceTree = ""; From f9cbec668b3818504fd6eb173a3fd7ed7af8d8b8 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 21 Feb 2024 15:43:24 -0500 Subject: [PATCH 11/14] Add empty shell for tests. --- .../Mac/Clock SignalTests/ARMDecoderTests.mm | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 OSBindings/Mac/Clock SignalTests/ARMDecoderTests.mm diff --git a/OSBindings/Mac/Clock SignalTests/ARMDecoderTests.mm b/OSBindings/Mac/Clock SignalTests/ARMDecoderTests.mm new file mode 100644 index 000000000..a3eba5edf --- /dev/null +++ b/OSBindings/Mac/Clock SignalTests/ARMDecoderTests.mm @@ -0,0 +1,25 @@ +// +// ARMDecoderTests.m +// Clock Signal +// +// Created by Thomas Harte on 16/02/2024. +// Copyright 2024 Thomas Harte. All rights reserved. +// + +#import + +#include "../../../InstructionSets/ARM/OperationMapper.hpp" + +using namespace InstructionSet::ARM; + +@interface ARMDecoderTests : XCTestCase +@end + +@implementation ARMDecoderTests + +- (void)testXYX { +// const auto intr = Instruction(1); +// NSLog(@"%d", intr.operation()); +} + +@end From d205e538e1c2f9538363f3fedd9cc37dd258c89f Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 22 Feb 2024 10:16:54 -0500 Subject: [PATCH 12/14] Accept the C++ I'm in; clarify and simplify interface. --- InstructionSets/ARM/OperationMapper.hpp | 189 +++++++++++------- .../Mac/Clock SignalTests/ARMDecoderTests.mm | 21 ++ 2 files changed, 142 insertions(+), 68 deletions(-) diff --git a/InstructionSets/ARM/OperationMapper.hpp b/InstructionSets/ARM/OperationMapper.hpp index 5c5847957..025572d6a 100644 --- a/InstructionSets/ARM/OperationMapper.hpp +++ b/InstructionSets/ARM/OperationMapper.hpp @@ -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 1–16 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 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( + scheduler.template perform( 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( + scheduler.template perform( 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( + scheduler.template perform( 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( + scheduler.template perform( 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( + scheduler.template perform( 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( + scheduler.template perform( condition, parameters ); } else { // Data operation. - constexpr auto flags = CoprocessorDataOperationFlags(i); - scheduler.template coprocessor_data_operation( + const auto parameters = CoprocessorDataOperation(instruction); + scheduler.template perform( 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( + scheduler.template perform( 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 void perform(Condition, DataProcessing); + template void perform(Condition, Multiply); + template void perform(Condition, SingleDataTransfer); + template void perform(Condition, BlockDataTransfer); + template void perform(Condition, Branch); + template void perform(Condition, CoprocessorRegisterTransfer); + template void perform(Condition, CoprocessorDataOperation); + template 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 void dispatch(uint32_t instruction, SchedulerT &scheduler) { OperationMapper mapper; Reflection::dispatch(mapper, (instruction >> FlagsStartBit) & 0xff, instruction, scheduler); diff --git a/OSBindings/Mac/Clock SignalTests/ARMDecoderTests.mm b/OSBindings/Mac/Clock SignalTests/ARMDecoderTests.mm index a3eba5edf..80db06897 100644 --- a/OSBindings/Mac/Clock SignalTests/ARMDecoderTests.mm +++ b/OSBindings/Mac/Clock SignalTests/ARMDecoderTests.mm @@ -12,12 +12,33 @@ using namespace InstructionSet::ARM; +namespace { + +struct Scheduler { + template void perform(Condition, DataProcessing) {} + template void perform(Condition, Multiply) {} + template void perform(Condition, SingleDataTransfer) {} + template void perform(Condition, BlockDataTransfer) {} + template void perform(Condition, Branch) {} + template void perform(Condition, CoprocessorRegisterTransfer) {} + template void perform(Condition, CoprocessorDataOperation) {} + template 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(1); // NSLog(@"%d", intr.operation()); } From 56a5df37837c5a829dc17699031fead8e3c29a65 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 22 Feb 2024 10:48:19 -0500 Subject: [PATCH 13/14] Do the least possible manual test. --- OSBindings/Mac/Clock SignalTests/ARMDecoderTests.mm | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/OSBindings/Mac/Clock SignalTests/ARMDecoderTests.mm b/OSBindings/Mac/Clock SignalTests/ARMDecoderTests.mm index 80db06897..3cfeffbce 100644 --- a/OSBindings/Mac/Clock SignalTests/ARMDecoderTests.mm +++ b/OSBindings/Mac/Clock SignalTests/ARMDecoderTests.mm @@ -19,7 +19,10 @@ struct Scheduler { template void perform(Condition, Multiply) {} template void perform(Condition, SingleDataTransfer) {} template void perform(Condition, BlockDataTransfer) {} - template void perform(Condition, Branch) {} + + template void perform(Condition condition, Branch branch) { + printf("Branch %sif %d; add %08x\n", op == Operation::BL ? "with link " : "", int(condition), branch.offset()); + } template void perform(Condition, CoprocessorRegisterTransfer) {} template void perform(Condition, CoprocessorDataOperation) {} template void perform(Condition, CoprocessorDataTransfer) {} @@ -38,7 +41,7 @@ struct Scheduler { - (void)testXYX { Scheduler scheduler; - InstructionSet::ARM::dispatch(1, scheduler); + InstructionSet::ARM::dispatch(0xEAE06900, scheduler); // const auto intr = Instruction(1); // NSLog(@"%d", intr.operation()); } From 73d2acca12643292e922f192bea22425c2cb6264 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 22 Feb 2024 11:20:22 -0500 Subject: [PATCH 14/14] Moderately improve comments. --- InstructionSets/ARM/OperationMapper.hpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/InstructionSets/ARM/OperationMapper.hpp b/InstructionSets/ARM/OperationMapper.hpp index 025572d6a..83c04a12a 100644 --- a/InstructionSets/ARM/OperationMapper.hpp +++ b/InstructionSets/ARM/OperationMapper.hpp @@ -80,6 +80,9 @@ constexpr bool flag_bit(uint8_t flags) { return flags & (1 << (position - FlagsStartBit)); } +// +// Methods common to data processing and data transfer. +// struct WithShiftControlBits { constexpr WithShiftControlBits(uint32_t opcode) noexcept : opcode_(opcode) {} @@ -121,6 +124,7 @@ struct DataProcessingFlags { /// @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: @@ -152,6 +156,7 @@ struct DataProcessing: public WithShiftControlBits { 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: