mirror of
https://github.com/TomHarte/CLK.git
synced 2026-04-20 10:17:05 +00:00
Merge pull request #1716 from TomHarte/6809
Eliminate need for intermediate storage in acquiring 6809 decodings.
This commit is contained in:
@@ -71,11 +71,12 @@ enum class Page {
|
||||
defined by opcode @c i on page @c page.
|
||||
*/
|
||||
template <Page page> struct OperationMapper {
|
||||
template <int i, typename SchedulerT> void dispatch(SchedulerT &scheduler);
|
||||
template <int i, typename SchedulerT> auto dispatch(SchedulerT &scheduler);
|
||||
};
|
||||
|
||||
template <>
|
||||
template <int i, typename SchedulerT> void OperationMapper<Page::Page0>::dispatch(SchedulerT &s) {
|
||||
template <int i, typename SchedulerT>
|
||||
auto OperationMapper<Page::Page0>::dispatch(SchedulerT &s) {
|
||||
using AM = AddressingMode;
|
||||
using O = Operation;
|
||||
|
||||
@@ -101,15 +102,15 @@ template <int i, typename SchedulerT> void OperationMapper<Page::Page0>::dispatc
|
||||
AM::Illegal, AM::Inherent, AM::Immediate, AM::Illegal,
|
||||
AM::Immediate, AM::Inherent, AM::Inherent, AM::Inherent,
|
||||
};
|
||||
s.template schedule<operations[lower], modes[lower]>();
|
||||
} break;
|
||||
return s.template schedule<operations[lower], modes[lower]>();
|
||||
}
|
||||
case 0x2: {
|
||||
static constexpr Operation operations[] = {
|
||||
O::BRA, O::BRN, O::BHI, O::BLS, O::BCC, O::BCS, O::BNE, O::BEQ,
|
||||
O::BVC, O::BVS, O::BPL, O::BMI, O::BGE, O::BLT, O::BGT, O::BLE,
|
||||
};
|
||||
s.template schedule<operations[lower], AM::Relative>();
|
||||
} break;
|
||||
return s.template schedule<operations[lower], AM::Relative>();
|
||||
}
|
||||
case 0x3: {
|
||||
static constexpr Operation operations[] = {
|
||||
O::LEAX, O::LEAY, O::LEAS, O::LEAU, O::PSHS, O::PULS, O::PSHU, O::PULU,
|
||||
@@ -118,63 +119,64 @@ template <int i, typename SchedulerT> void OperationMapper<Page::Page0>::dispatc
|
||||
static constexpr auto op = operations[lower];
|
||||
switch(lower) {
|
||||
case 0x0: case 0x1: case 0x2: case 0x3:
|
||||
s.template schedule<op, AM::Indexed>();
|
||||
break;
|
||||
return s.template schedule<op, AM::Indexed>();
|
||||
|
||||
case 0x4: case 0x5: case 0x6: case 0x7: case 0xc:
|
||||
s.template schedule<op, AM::Immediate>();
|
||||
break;
|
||||
return s.template schedule<op, AM::Immediate>();
|
||||
|
||||
case 0x8:
|
||||
s.template schedule<op, AM::Illegal>();
|
||||
break;
|
||||
return s.template schedule<op, AM::Illegal>();
|
||||
|
||||
default:
|
||||
s.template schedule<op, AM::Inherent>();
|
||||
break;
|
||||
return s.template schedule<op, AM::Inherent>();
|
||||
}
|
||||
} break;
|
||||
}
|
||||
case 0x4: {
|
||||
static constexpr Operation operations[] = {
|
||||
O::NEGA, O::None, O::None, O::COMA, O::LSRA, O::None, O::RORA, O::ASRA,
|
||||
O::LSLA, O::ROLA, O::DECA, O::None, O::INCA, O::TSTA, O::None, O::CLRA,
|
||||
};
|
||||
static constexpr auto op = operations[lower];
|
||||
s.template schedule<op, op == O::None ? AM::Illegal : AM::Inherent>();
|
||||
} break;
|
||||
return s.template schedule<op, op == O::None ? AM::Illegal : AM::Inherent>();
|
||||
}
|
||||
case 0x5: {
|
||||
static constexpr Operation operations[] = {
|
||||
O::NEGB, O::None, O::None, O::COMB, O::LSRB, O::None, O::RORB, O::ASRB,
|
||||
O::LSLB, O::ROLB, O::DECB, O::None, O::INCB, O::TSTB, O::None, O::CLRB,
|
||||
};
|
||||
static constexpr auto op = operations[lower];
|
||||
s.template schedule<op, op == O::None ? AM::Illegal : AM::Inherent>();
|
||||
} break;
|
||||
return s.template schedule<op, op == O::None ? AM::Illegal : AM::Inherent>();
|
||||
}
|
||||
case 0x0: case 0x6: case 0x7: {
|
||||
static constexpr Operation operations[] = {
|
||||
O::NEG, O::None, O::None, O::COM, O::LSR, O::None, O::ROR, O::ASR,
|
||||
O::LSL, O::ROL, O::DEC, O::None, O::INC, O::TST, O::JMP, O::CLR,
|
||||
};
|
||||
static constexpr auto op = operations[lower];
|
||||
s.template schedule<op, op == O::None ? AM::Illegal : upper == 0 ? AM::Direct : mode>();
|
||||
} break;
|
||||
return s.template schedule<op, op == O::None ? AM::Illegal : upper == 0 ? AM::Direct : mode>();
|
||||
}
|
||||
case 0x8: case 0x9: case 0xa: case 0xb: {
|
||||
static constexpr Operation operations[] = {
|
||||
O::SUBA, O::CMPA, O::SBCA, O::SUBD, O::ANDA, O::BITA, O::LDA, O::STA,
|
||||
O::EORA, O::ADCA, O::ORA, O::ADDA, O::CMPX, O::JSR, O::LDX, O::STX,
|
||||
};
|
||||
if(i == 0x8d) s.template schedule<O::BSR, AM::Relative>();
|
||||
else s.template schedule<operations[lower], mode>();
|
||||
} break;
|
||||
if(i == 0x8d) return s.template schedule<O::BSR, AM::Relative>();
|
||||
else return s.template schedule<operations[lower], mode>();
|
||||
}
|
||||
case 0xc: case 0xd: case 0xe: case 0xf: {
|
||||
static constexpr Operation operations[] = {
|
||||
O::SUBB, O::CMPB, O::SBCB, O::ADDD, O::ANDB, O::BITB, O::LDB, O::STB,
|
||||
O::EORB, O::ADCB, O::ORB, O::ADDB, O::LDD, O::STD, O::LDU, O::STU,
|
||||
};
|
||||
s.template schedule<operations[lower], mode>();
|
||||
} break;
|
||||
return s.template schedule<operations[lower], mode>();
|
||||
}
|
||||
}
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
template <>
|
||||
template <int i, typename SchedulerT> void OperationMapper<Page::Page1>::dispatch(SchedulerT &s) {
|
||||
template <int i, typename SchedulerT>
|
||||
auto OperationMapper<Page::Page1>::dispatch(SchedulerT &s) {
|
||||
using AM = AddressingMode;
|
||||
using O = Operation;
|
||||
|
||||
@@ -188,34 +190,35 @@ template <int i, typename SchedulerT> void OperationMapper<Page::Page1>::dispatc
|
||||
O::LBRN, O::LBHI, O::LBLS, O::LBCC, O::LBCS, O::LBNE, O::LBEQ,
|
||||
O::LBVC, O::LBVS, O::LBPL, O::LBMI, O::LBGE, O::LBLT, O::LBGT, O::LBLE,
|
||||
};
|
||||
s.template schedule<operations[i - 0x21], AM::Relative>();
|
||||
return s.template schedule<operations[i - 0x21], AM::Relative>();
|
||||
} else switch(i) {
|
||||
default: s.template schedule<O::None, AM::Illegal>(); break;
|
||||
case 0x3f: s.template schedule<O::SWI2, AM::Inherent>(); break;
|
||||
default: return s.template schedule<O::None, AM::Illegal>();
|
||||
case 0x3f: return s.template schedule<O::SWI2, AM::Inherent>();
|
||||
|
||||
case 0x83: case 0x93: case 0xa3: case 0xb3:
|
||||
s.template schedule<O::CMPD, mode>();
|
||||
break;
|
||||
return s.template schedule<O::CMPD, mode>();
|
||||
|
||||
case 0x8c: case 0x9c: case 0xac: case 0xbc:
|
||||
s.template schedule<O::CMPY, mode>();
|
||||
break;
|
||||
return s.template schedule<O::CMPY, mode>();
|
||||
|
||||
case 0x8e: case 0x9e: case 0xae: case 0xbe:
|
||||
s.template schedule<O::LDY, mode>();
|
||||
break;
|
||||
return s.template schedule<O::LDY, mode>();
|
||||
|
||||
case 0x9f: case 0xaf: case 0xbf:
|
||||
s.template schedule<O::STY, mode>();
|
||||
break;
|
||||
return s.template schedule<O::STY, mode>();
|
||||
|
||||
case 0xce: case 0xde: case 0xee: case 0xfe:
|
||||
s.template schedule<O::LDS, mode>();
|
||||
break;
|
||||
return s.template schedule<O::LDS, mode>();
|
||||
|
||||
case 0xdf: case 0xef: case 0xff:
|
||||
s.template schedule<O::STS, mode>();
|
||||
break;
|
||||
return s.template schedule<O::STS, mode>();
|
||||
}
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
template <>
|
||||
template <int i, typename SchedulerT> void OperationMapper<Page::Page2>::dispatch(SchedulerT &s) {
|
||||
template <int i, typename SchedulerT>
|
||||
auto OperationMapper<Page::Page2>::dispatch(SchedulerT &s) {
|
||||
using AM = AddressingMode;
|
||||
using O = Operation;
|
||||
|
||||
@@ -225,16 +228,16 @@ template <int i, typename SchedulerT> void OperationMapper<Page::Page2>::dispatc
|
||||
static constexpr auto mode = modes[(i >> 4) & 3];
|
||||
|
||||
switch(i) {
|
||||
default: s.template schedule<O::None, AM::Illegal>(); break;
|
||||
case 0x3f: s.template schedule<O::SWI3, AM::Inherent>(); break;
|
||||
default: return s.template schedule<O::None, AM::Illegal>();
|
||||
case 0x3f: return s.template schedule<O::SWI3, AM::Inherent>();
|
||||
|
||||
case 0x83: case 0x93: case 0xa3: case 0xb3:
|
||||
s.template schedule<O::CMPU, mode>();
|
||||
break;
|
||||
return s.template schedule<O::CMPU, mode>();
|
||||
|
||||
case 0x8c: case 0x9c: case 0xac: case 0xbc:
|
||||
s.template schedule<O::CMPS, mode>();
|
||||
break;
|
||||
return s.template schedule<O::CMPS, mode>();
|
||||
}
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -757,7 +757,6 @@
|
||||
4B96DECC2EBEE7D100505298 /* SID.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B96DECA2EBEE7D100505298 /* SID.cpp */; };
|
||||
4B96DECD2EBEE7D100505298 /* SID.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B96DECA2EBEE7D100505298 /* SID.cpp */; };
|
||||
4B96DECE2EBEE7D100505298 /* SID.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B96DECA2EBEE7D100505298 /* SID.cpp */; };
|
||||
4B96DED32EC3ECDA00505298 /* SIDTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B96DED22EC3ECDA00505298 /* SIDTests.mm */; };
|
||||
4B96F7CE263E33B10092AEE1 /* DSK.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B96F7CC263E33B10092AEE1 /* DSK.cpp */; };
|
||||
4B96F7CF263E33B10092AEE1 /* DSK.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B96F7CC263E33B10092AEE1 /* DSK.cpp */; };
|
||||
4B98A05E1FFAD3F600ADF63B /* CSROMFetcher.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B98A05D1FFAD3F600ADF63B /* CSROMFetcher.mm */; };
|
||||
@@ -1980,7 +1979,6 @@
|
||||
4B96DEC32EBEA88C00505298 /* TubeProcessor.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = TubeProcessor.hpp; sourceTree = "<group>"; };
|
||||
4B96DEC92EBEE7D100505298 /* SID.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = SID.hpp; sourceTree = "<group>"; };
|
||||
4B96DECA2EBEE7D100505298 /* SID.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = SID.cpp; sourceTree = "<group>"; };
|
||||
4B96DED22EC3ECDA00505298 /* SIDTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = SIDTests.mm; sourceTree = "<group>"; };
|
||||
4B96DED42EC53BC300505298 /* BiquadFilter.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = BiquadFilter.hpp; sourceTree = "<group>"; };
|
||||
4B96F7CB263E30B00092AEE1 /* RawSectorDump.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = RawSectorDump.hpp; sourceTree = "<group>"; };
|
||||
4B96F7CC263E33B10092AEE1 /* DSK.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = DSK.cpp; sourceTree = "<group>"; };
|
||||
@@ -4968,7 +4966,6 @@
|
||||
4BD4A8CF1E077FD20020D856 /* PCMTrackTests.mm */,
|
||||
4B3F76B825A1635300178AEC /* PowerPCDecoderTests.mm */,
|
||||
4BE76CF822641ED300ACD6FA /* QLTests.mm */,
|
||||
4B96DED22EC3ECDA00505298 /* SIDTests.mm */,
|
||||
4B8DD3672633B2D400B3C866 /* SpectrumVideoContentionTests.mm */,
|
||||
4B2AF8681E513FC20027EE29 /* TIATests.mm */,
|
||||
4B1D08051E0F7A1100763741 /* TimeTests.mm */,
|
||||
@@ -6931,7 +6928,6 @@
|
||||
4B778F4623A5F1D80000D260 /* StaticAnalyser.cpp in Sources */,
|
||||
4B778F1323A5EC890000D260 /* Z80Base.cpp in Sources */,
|
||||
4B778F2923A5EF030000D260 /* CommodoreROM.cpp in Sources */,
|
||||
4B96DED32EC3ECDA00505298 /* SIDTests.mm in Sources */,
|
||||
4B06AADC2C645F720034D014 /* BD500.cpp in Sources */,
|
||||
4B778F4823A5F1E70000D260 /* StaticAnalyser.cpp in Sources */,
|
||||
4B92EACA1B7C112B00246143 /* 6502TimingTests.swift in Sources */,
|
||||
|
||||
@@ -17,19 +17,15 @@ using namespace InstructionSet::M6809;
|
||||
namespace {
|
||||
|
||||
struct OperationCapture {
|
||||
template <Operation operation, AddressingMode mode> void schedule() {
|
||||
this->operation = operation;
|
||||
this->mode = mode;
|
||||
template <Operation operation, AddressingMode mode> auto schedule() {
|
||||
return std::make_pair(operation, mode);
|
||||
}
|
||||
Operation operation;
|
||||
AddressingMode mode;
|
||||
};
|
||||
|
||||
template <Page page> OperationCapture capture(uint8_t opcode) {
|
||||
template <Page page> std::pair<Operation, AddressingMode> capture(const uint8_t opcode) {
|
||||
OperationCapture catcher;
|
||||
OperationMapper<page> mapper;
|
||||
Reflection::dispatch(mapper, opcode, catcher);
|
||||
return catcher;
|
||||
return Reflection::dispatch(mapper, opcode, catcher);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -39,17 +35,21 @@ template <Page page> OperationCapture capture(uint8_t opcode) {
|
||||
|
||||
@implementation M6809OperationMapperTests
|
||||
|
||||
- (void)testOpcode:(uint8_t)opcode page:(Page)page isOperation:(Operation)operation addressingMode:(AddressingMode)mode {
|
||||
OperationCapture catcher;
|
||||
|
||||
switch(page) {
|
||||
case Page::Page0: catcher = capture<Page::Page0>(opcode); break;
|
||||
case Page::Page1: catcher = capture<Page::Page1>(opcode); break;
|
||||
case Page::Page2: catcher = capture<Page::Page2>(opcode); break;
|
||||
}
|
||||
|
||||
XCTAssertEqual(catcher.operation, operation, "Operation didn't match for opcode 0x%02x in page %d", opcode, int(page));
|
||||
XCTAssertEqual(catcher.mode, mode, "Mode didn't match for opcode 0x%02x in page %d", opcode, int(page));
|
||||
- (void)
|
||||
testOpcode:(uint8_t)opcode
|
||||
page:(Page)page
|
||||
isOperation:(Operation)expectedOperation
|
||||
addressingMode:(AddressingMode)expectedMode
|
||||
{
|
||||
const auto &[operation, mode] = [&]() {
|
||||
switch(page) {
|
||||
case Page::Page0: return capture<Page::Page0>(opcode);
|
||||
case Page::Page1: return capture<Page::Page1>(opcode);
|
||||
case Page::Page2: return capture<Page::Page2>(opcode);
|
||||
}
|
||||
} ();
|
||||
XCTAssertEqual(expectedOperation, operation, "Operation didn't match for opcode 0x%02x in page %d", opcode, int(page));
|
||||
XCTAssertEqual(expectedMode, mode, "Mode didn't match for opcode 0x%02x in page %d", opcode, int(page));
|
||||
}
|
||||
|
||||
- (void)testMap {
|
||||
|
||||
@@ -16,8 +16,9 @@ namespace Reflection {
|
||||
/*!
|
||||
Calls @c t.dispatch<c>(args...) as efficiently as possible.
|
||||
*/
|
||||
template <typename TargetT, typename... Args> void dispatch(TargetT &t, uint8_t c, Args &&... args) {
|
||||
#define Opt(x) case x: t.template dispatch<x>(std::forward<Args>(args)...); break
|
||||
template <typename TargetT, typename... Args>
|
||||
auto dispatch(TargetT &t, const uint8_t c, Args &&... args) {
|
||||
#define Opt(x) case x: return t.template dispatch<x>(std::forward<Args>(args)...);
|
||||
#define Opt4(x) Opt(x + 0x00); Opt(x + 0x01); Opt(x + 0x02); Opt(x + 0x03)
|
||||
#define Opt16(x) Opt4(x + 0x00); Opt4(x + 0x04); Opt4(x + 0x08); Opt4(x + 0x0c)
|
||||
#define Opt64(x) Opt16(x + 0x00); Opt16(x + 0x10); Opt16(x + 0x20); Opt16(x + 0x30)
|
||||
@@ -26,6 +27,7 @@ template <typename TargetT, typename... Args> void dispatch(TargetT &t, uint8_t
|
||||
switch(c) {
|
||||
Opt256(0);
|
||||
}
|
||||
__builtin_unreachable();
|
||||
|
||||
#undef Opt256
|
||||
#undef Opt64
|
||||
@@ -68,20 +70,21 @@ struct RangeDispatcher {
|
||||
|
||||
/// Perform @c target.perform<n>() for the input range `begin <= n < end`.
|
||||
template <typename... Args>
|
||||
static void dispatch(SequencerT &target, int begin, int end, Args&&... args) {
|
||||
static auto dispatch(SequencerT &target, const int begin, const int end, Args&&... args) {
|
||||
|
||||
// Minor optimisation: do a comparison with end once outside the loop and if it implies so
|
||||
// then do no further comparisons within the loop. This is somewhat targetted at expected
|
||||
// use cases.
|
||||
if(end < SequencerT::max) {
|
||||
dispatch<true>(target, begin, end, args...);
|
||||
return dispatch<true>(target, begin, end, args...);
|
||||
} else {
|
||||
dispatch<false>(target, begin, end, args...);
|
||||
return dispatch<false>(target, begin, end, args...);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
template <bool use_end, typename... Args> static void dispatch(SequencerT &target, int begin, int end, Args&&... args) {
|
||||
template <bool use_end, typename... Args>
|
||||
static auto dispatch(SequencerT &target, const int begin, const int end, Args&&... args) {
|
||||
#define index(n) \
|
||||
case n: \
|
||||
if constexpr (n <= SequencerT::max) { \
|
||||
@@ -141,13 +144,13 @@ struct SubrangeDispatcher {
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr int find_begin(int n) {
|
||||
static consteval int find_begin(int n) {
|
||||
const auto type = ClassifierT::region(n);
|
||||
while(n && ClassifierT::region(n - 1) == type) --n;
|
||||
return n;
|
||||
}
|
||||
|
||||
static constexpr int find_end(int n) {
|
||||
static consteval int find_end(int n) {
|
||||
const auto type = ClassifierT::region(n);
|
||||
while(n < ClassifierT::max && ClassifierT::region(n) == type) ++n;
|
||||
return n;
|
||||
|
||||
Reference in New Issue
Block a user