From 315e0b4545e3151c8d10456f704d2aeab90be8ef Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 17 Mar 2023 21:20:35 -0400 Subject: [PATCH] Add experimental 6809 opcode decoder. Just a pleasant distraction, for now. --- InstructionSets/6809/OperationMapper.hpp | 245 ++++++++++++++++++ .../6809OperationMapperTests.mm | 118 +++++++++ Reflection/Dispatcher.hpp | 39 +++ 3 files changed, 402 insertions(+) create mode 100644 InstructionSets/6809/OperationMapper.hpp create mode 100644 OSBindings/Mac/Clock SignalTests/6809OperationMapperTests.mm create mode 100644 Reflection/Dispatcher.hpp diff --git a/InstructionSets/6809/OperationMapper.hpp b/InstructionSets/6809/OperationMapper.hpp new file mode 100644 index 000000000..8c6fafd45 --- /dev/null +++ b/InstructionSets/6809/OperationMapper.hpp @@ -0,0 +1,245 @@ +// +// OperationMapper.hpp +// Clock Signal +// +// Created by Thomas Harte on 17/03/2023. +// Copyright © 2023 Thomas Harte. All rights reserved. +// + +#ifndef InstructionSets_M6809_OperationMapper_hpp +#define InstructionSets_M6809_OperationMapper_hpp + +// Cf. https://techheap.packetizer.com/processors/6809/6809Instructions.html +// +// Subject to corrections: +// +// * CWAI and the pushes and pulls at 0x3x are immediate, not inherent. +namespace InstructionSet { +namespace M6809 { + +enum class AddressingMode { + Illegal, + + Inherent, + Immediate, + Direct, + Relative, // TODO: is it worth breaking this into 8- and 16-bit versions? + Variant, + Indexed, + Extended, +}; + +enum class Operation { + None, + + SUBB, CMPB, SBCB, ADDD, ANDB, BITB, LDB, STB, + EORB, ADCB, ORB, ADDB, LDD, STD, LDU, STU, + SUBA, CMPA, SBCA, SUBD, ANDA, BITA, LDA, STA, + EORA, ADCA, ORA, ADDA, CMPX, JSR, LDX, STX, + BSR, + + NEG, COM, LSR, ROR, ASR, + LSL, ROL, DEC, INC, TST, JMP, CLR, + NEGA, COMA, LSRA, RORA, ASRA, + LSLA, ROLA, DECA, INCA, TSTA, CLRA, + NEGB, COMB, LSRB, RORB, ASRB, + LSLB, ROLB, DECB, INCB, TSTB, CLRB, + + LEAX, LEAY, LEAS, LEAU, + PSHS, PULS, PSHU, PULU, + RTS, ABX, RTI, + CWAI, MUL, RESET, SWI, + + BRA, BRN, BHI, BLS, BCC, BCS, BNE, BEQ, + BVC, BVS, BPL, BMI, BGE, BLT, BGT, BLE, + + Page1, Page2, NOP, SYNC, LBRA, LBSR, + DAA, ORCC, ANDCC, SEX, EXG, TFR, + + LBRN, LBHI, LBLS, LBCC, LBCS, LBNE, LBEQ, + LBVC, LBVS, LBPL, LBMI, LBGE, LBLT, LBGT, LBLE, + + SWI2, CMPD, CMPY, LDY, STY, LDS, STS, + + SWI3, CMPU, CMPS, +}; + +enum class Page { + Page0, Page1, Page2, +}; + +/*! + Calls @c scheduler.schedule to describe the instruction + defined by opcode @c i on page @c page. +*/ +template struct OperationMapper { + template void dispatch(SchedulerT &scheduler); +}; + +template <> +template void OperationMapper::dispatch(SchedulerT &s) { + using AM = AddressingMode; + using O = Operation; + + constexpr auto upper = (i >> 4) & 0xf; + constexpr auto lower = (i >> 0) & 0xf; + + constexpr AddressingMode modes[] = { + AM::Immediate, AM::Direct, AM::Indexed, AM::Extended + }; + constexpr AddressingMode mode = modes[(i >> 4) & 3]; + + switch(upper) { + default: break; + + case 0x1: { + constexpr Operation operations[] = { + O::Page1, O::Page2, O::NOP, O::SYNC, O::None, O::None, O::LBRA, O::LBSR, + O::None, O::DAA, O::ORCC, O::None, O::ANDCC, O::SEX, O::EXG, O::TFR, + }; + constexpr AddressingMode modes[] = { + AM::Variant, AM::Variant, AM::Inherent, AM::Inherent, + AM::Illegal, AM::Illegal, AM::Relative, AM::Relative, + AM::Illegal, AM::Inherent, AM::Immediate, AM::Illegal, + AM::Immediate, AM::Inherent, AM::Inherent, AM::Inherent, + }; + s.template schedule(); + } break; + case 0x2: { + 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(); + } break; + case 0x3: { + constexpr Operation operations[] = { + O::LEAX, O::LEAY, O::LEAS, O::LEAU, O::PSHS, O::PULS, O::PSHU, O::PULU, + O::None, O::RTS, O::ABX, O::RTI, O::CWAI, O::MUL, O::RESET, O::SWI, + }; + constexpr auto op = operations[lower]; + switch(lower) { + case 0x0: case 0x1: case 0x2: case 0x3: + s.template schedule(); + break; + case 0x4: case 0x5: case 0x6: case 0x7: case 0xc: + s.template schedule(); + break; + case 0x8: + s.template schedule(); + break; + default: + s.template schedule(); + break; + } + } break; + case 0x4: { + 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, + }; + constexpr auto op = operations[lower]; + s.template schedule(); + } break; + case 0x5: { + 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, + }; + constexpr auto op = operations[lower]; + s.template schedule(); + } break; + case 0x0: case 0x6: case 0x7: { + 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, + }; + constexpr auto op = operations[lower]; + s.template schedule(); + } break; + case 0x8: case 0x9: case 0xa: case 0xb: { + 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(); + else s.template schedule(); + } break; + case 0xc: case 0xd: case 0xe: case 0xf: { + 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(); + } break; + } +} + +template <> +template void OperationMapper::dispatch(SchedulerT &s) { + using AM = AddressingMode; + using O = Operation; + + constexpr AddressingMode modes[] = { + AM::Immediate, AM::Direct, AM::Indexed, AM::Extended + }; + constexpr auto mode = modes[(i >> 4) & 3]; + + if constexpr (i >= 0x21 && i < 0x30) { + constexpr Operation operations[] = { + 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(); + } else switch(i) { + default: s.template schedule(); break; + case 0x3f: s.template schedule(); break; + + case 0x83: case 0x93: case 0xa3: case 0xb3: + s.template schedule(); + break; + case 0x8c: case 0x9c: case 0xac: case 0xbc: + s.template schedule(); + break; + case 0x8e: case 0x9e: case 0xae: case 0xbe: + s.template schedule(); + break; + case 0x9f: case 0xaf: case 0xbf: + s.template schedule(); + break; + case 0xce: case 0xde: case 0xee: case 0xfe: + s.template schedule(); + break; + case 0xdf: case 0xef: case 0xff: + s.template schedule(); + break; + } +} + +template <> +template void OperationMapper::dispatch(SchedulerT &s) { + using AM = AddressingMode; + using O = Operation; + + constexpr AddressingMode modes[] = { + AM::Immediate, AM::Direct, AM::Indexed, AM::Extended + }; + constexpr auto mode = modes[(i >> 4) & 3]; + + switch(i) { + default: s.template schedule(); break; + case 0x3f: s.template schedule(); break; + + case 0x83: case 0x93: case 0xa3: case 0xb3: + s.template schedule(); + break; + case 0x8c: case 0x9c: case 0xac: case 0xbc: + s.template schedule(); + break; + } +} + +} +} + +#endif /* InstructionSets_M6809_OperationMapper_hpp */ diff --git a/OSBindings/Mac/Clock SignalTests/6809OperationMapperTests.mm b/OSBindings/Mac/Clock SignalTests/6809OperationMapperTests.mm new file mode 100644 index 000000000..f8757a8c9 --- /dev/null +++ b/OSBindings/Mac/Clock SignalTests/6809OperationMapperTests.mm @@ -0,0 +1,118 @@ +// +// 68000BCDTests.m +// Clock SignalTests +// +// Created by Thomas Harte on 29/06/2019. +// +// Largely ported from the tests of the Portable 68k Emulator. +// + +#import + +#include "Dispatcher.hpp" +#include "../../../InstructionSets/6809/OperationMapper.hpp" + +using namespace InstructionSet::M6809; + +namespace { + +struct OperationCapture { + template void schedule() { + this->operation = operation; + this->mode = mode; + } + Operation operation; + AddressingMode mode; +}; + +template OperationCapture capture(uint8_t opcode) { + OperationCapture catcher; + OperationMapper mapper; + Reflection::dispatch(mapper, opcode, catcher); + return catcher; +} + +} + +@interface M6809OperationMapperTests : XCTestCase +@end + +@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(opcode); break; + case Page::Page1: catcher = capture(opcode); break; + case Page::Page2: catcher = capture(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)testMap { + // Spot tests only for now; will do for checking that code compiles and acts semi-reasonably, at least. + [self testOpcode:0x3a page:Page::Page0 isOperation:Operation::ABX addressingMode:AddressingMode::Inherent]; + [self testOpcode:0xd9 page:Page::Page0 isOperation:Operation::ADCB addressingMode:AddressingMode::Direct]; + [self testOpcode:0xab page:Page::Page0 isOperation:Operation::ADDA addressingMode:AddressingMode::Indexed]; + [self testOpcode:0xf3 page:Page::Page0 isOperation:Operation::ADDD addressingMode:AddressingMode::Extended]; + [self testOpcode:0xc4 page:Page::Page0 isOperation:Operation::ANDB addressingMode:AddressingMode::Immediate]; + [self testOpcode:0x1c page:Page::Page0 isOperation:Operation::ANDCC addressingMode:AddressingMode::Immediate]; + [self testOpcode:0x08 page:Page::Page0 isOperation:Operation::LSL addressingMode:AddressingMode::Direct]; + [self testOpcode:0x57 page:Page::Page0 isOperation:Operation::ASRB addressingMode:AddressingMode::Inherent]; + [self testOpcode:0x24 page:Page::Page0 isOperation:Operation::BCC addressingMode:AddressingMode::Relative]; + [self testOpcode:0xa5 page:Page::Page0 isOperation:Operation::BITA addressingMode:AddressingMode::Indexed]; + [self testOpcode:0x20 page:Page::Page0 isOperation:Operation::BRA addressingMode:AddressingMode::Relative]; + [self testOpcode:0x8d page:Page::Page0 isOperation:Operation::BSR addressingMode:AddressingMode::Relative]; + [self testOpcode:0x5f page:Page::Page0 isOperation:Operation::CLRB addressingMode:AddressingMode::Inherent]; + [self testOpcode:0x7f page:Page::Page0 isOperation:Operation::CLR addressingMode:AddressingMode::Extended]; + [self testOpcode:0x81 page:Page::Page0 isOperation:Operation::CMPA addressingMode:AddressingMode::Immediate]; + [self testOpcode:0xa3 page:Page::Page1 isOperation:Operation::CMPD addressingMode:AddressingMode::Indexed]; + [self testOpcode:0xa3 page:Page::Page2 isOperation:Operation::CMPU addressingMode:AddressingMode::Indexed]; + [self testOpcode:0x53 page:Page::Page0 isOperation:Operation::COMB addressingMode:AddressingMode::Inherent]; + [self testOpcode:0x73 page:Page::Page0 isOperation:Operation::COM addressingMode:AddressingMode::Extended]; + [self testOpcode:0x3c page:Page::Page0 isOperation:Operation::CWAI addressingMode:AddressingMode::Immediate]; + [self testOpcode:0x19 page:Page::Page0 isOperation:Operation::DAA addressingMode:AddressingMode::Inherent]; + [self testOpcode:0x4a page:Page::Page0 isOperation:Operation::DECA addressingMode:AddressingMode::Inherent]; + [self testOpcode:0x0a page:Page::Page0 isOperation:Operation::DEC addressingMode:AddressingMode::Direct]; + [self testOpcode:0xa8 page:Page::Page0 isOperation:Operation::EORA addressingMode:AddressingMode::Indexed]; + [self testOpcode:0xf8 page:Page::Page0 isOperation:Operation::EORB addressingMode:AddressingMode::Extended]; + [self testOpcode:0x4c page:Page::Page0 isOperation:Operation::INCA addressingMode:AddressingMode::Inherent]; + [self testOpcode:0x0c page:Page::Page0 isOperation:Operation::INC addressingMode:AddressingMode::Direct]; + [self testOpcode:0x6e page:Page::Page0 isOperation:Operation::JMP addressingMode:AddressingMode::Indexed]; + [self testOpcode:0x9d page:Page::Page0 isOperation:Operation::JSR addressingMode:AddressingMode::Direct]; + [self testOpcode:0x24 page:Page::Page1 isOperation:Operation::LBCC addressingMode:AddressingMode::Relative]; + [self testOpcode:0xe6 page:Page::Page0 isOperation:Operation::LDB addressingMode:AddressingMode::Indexed]; + [self testOpcode:0x8e page:Page::Page1 isOperation:Operation::LDY addressingMode:AddressingMode::Immediate]; + [self testOpcode:0x32 page:Page::Page0 isOperation:Operation::LEAS addressingMode:AddressingMode::Indexed]; + [self testOpcode:0x74 page:Page::Page0 isOperation:Operation::LSR addressingMode:AddressingMode::Extended]; + [self testOpcode:0x3d page:Page::Page0 isOperation:Operation::MUL addressingMode:AddressingMode::Inherent]; + [self testOpcode:0x40 page:Page::Page0 isOperation:Operation::NEGA addressingMode:AddressingMode::Inherent]; + [self testOpcode:0x00 page:Page::Page0 isOperation:Operation::NEG addressingMode:AddressingMode::Direct]; + [self testOpcode:0x12 page:Page::Page0 isOperation:Operation::NOP addressingMode:AddressingMode::Inherent]; + [self testOpcode:0x8a page:Page::Page0 isOperation:Operation::ORA addressingMode:AddressingMode::Immediate]; + [self testOpcode:0x1a page:Page::Page0 isOperation:Operation::ORCC addressingMode:AddressingMode::Immediate]; + [self testOpcode:0x34 page:Page::Page0 isOperation:Operation::PSHS addressingMode:AddressingMode::Immediate]; + [self testOpcode:0x36 page:Page::Page0 isOperation:Operation::PSHU addressingMode:AddressingMode::Immediate]; + [self testOpcode:0x37 page:Page::Page0 isOperation:Operation::PULU addressingMode:AddressingMode::Immediate]; + [self testOpcode:0x59 page:Page::Page0 isOperation:Operation::ROLB addressingMode:AddressingMode::Inherent]; + [self testOpcode:0x69 page:Page::Page0 isOperation:Operation::ROL addressingMode:AddressingMode::Indexed]; + [self testOpcode:0x76 page:Page::Page0 isOperation:Operation::ROR addressingMode:AddressingMode::Extended]; + [self testOpcode:0x3b page:Page::Page0 isOperation:Operation::RTI addressingMode:AddressingMode::Inherent]; + [self testOpcode:0x39 page:Page::Page0 isOperation:Operation::RTS addressingMode:AddressingMode::Inherent]; + [self testOpcode:0xc2 page:Page::Page0 isOperation:Operation::SBCB addressingMode:AddressingMode::Immediate]; + [self testOpcode:0x1d page:Page::Page0 isOperation:Operation::SEX addressingMode:AddressingMode::Inherent]; + [self testOpcode:0xe7 page:Page::Page0 isOperation:Operation::STB addressingMode:AddressingMode::Indexed]; + [self testOpcode:0xbf page:Page::Page1 isOperation:Operation::STY addressingMode:AddressingMode::Extended]; + [self testOpcode:0xe0 page:Page::Page0 isOperation:Operation::SUBB addressingMode:AddressingMode::Indexed]; + [self testOpcode:0x93 page:Page::Page0 isOperation:Operation::SUBD addressingMode:AddressingMode::Direct]; + [self testOpcode:0x3f page:Page::Page0 isOperation:Operation::SWI addressingMode:AddressingMode::Inherent]; + [self testOpcode:0x3f page:Page::Page1 isOperation:Operation::SWI2 addressingMode:AddressingMode::Inherent]; + [self testOpcode:0x3f page:Page::Page2 isOperation:Operation::SWI3 addressingMode:AddressingMode::Inherent]; + [self testOpcode:0x13 page:Page::Page0 isOperation:Operation::SYNC addressingMode:AddressingMode::Inherent]; + [self testOpcode:0x4d page:Page::Page0 isOperation:Operation::TSTA addressingMode:AddressingMode::Inherent]; +} + +@end diff --git a/Reflection/Dispatcher.hpp b/Reflection/Dispatcher.hpp new file mode 100644 index 000000000..adfb025f6 --- /dev/null +++ b/Reflection/Dispatcher.hpp @@ -0,0 +1,39 @@ +// +// Dispatcher.hpp +// Clock Signal +// +// Created by Thomas Harte on 17/03/2023. +// Copyright © 2023 Thomas Harte. All rights reserved. +// + +#ifndef Dispatcher_hpp +#define Dispatcher_hpp + +#include + +namespace Reflection { + +/*! + Calls @c t.dispatch(args...) as efficiently as possible. +*/ +template void dispatch(TargetT &t, uint8_t c, Args &&... args) { +#define Opt(x) case x: t.template dispatch(std::forward(args)...); break +#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) +#define Opt256(x) Opt64(x + 0x00); Opt64(x + 0x40); Opt64(x + 0x80); Opt64(x + 0xc0) + + switch(c) { + Opt256(0); + } + +#undef Opt256 +#undef Opt64 +#undef Opt16 +#undef Opt4 +#undef Opt +} + +} + +#endif /* Dispatcher_hpp */