1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-11-25 16:31:42 +00:00

Add experimental 6809 opcode decoder.

Just a pleasant distraction, for now.
This commit is contained in:
Thomas Harte 2023-03-17 21:20:35 -04:00
parent f0b1c34db2
commit 315e0b4545
3 changed files with 402 additions and 0 deletions

View File

@ -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<Operation,AddressingMode> to describe the instruction
defined by opcode @c i on page @c page.
*/
template <Page page> struct OperationMapper {
template <int i, typename SchedulerT> void dispatch(SchedulerT &scheduler);
};
template <>
template <int i, typename SchedulerT> void OperationMapper<Page::Page0>::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<operations[lower], modes[lower]>();
} 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<operations[lower], AM::Relative>();
} 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<op, AM::Indexed>();
break;
case 0x4: case 0x5: case 0x6: case 0x7: case 0xc:
s.template schedule<op, AM::Immediate>();
break;
case 0x8:
s.template schedule<op, AM::Illegal>();
break;
default:
s.template schedule<op, AM::Inherent>();
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<op, op == O::None ? AM::Illegal : AM::Inherent>();
} 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<op, op == O::None ? AM::Illegal : AM::Inherent>();
} 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<op, op == O::None ? AM::Illegal : upper == 0 ? AM::Direct : mode>();
} 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<O::BSR, AM::Relative>();
else s.template schedule<operations[lower], mode>();
} 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<operations[lower], mode>();
} break;
}
}
template <>
template <int i, typename SchedulerT> void OperationMapper<Page::Page1>::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<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;
case 0x83: case 0x93: case 0xa3: case 0xb3:
s.template schedule<O::CMPD, mode>();
break;
case 0x8c: case 0x9c: case 0xac: case 0xbc:
s.template schedule<O::CMPY, mode>();
break;
case 0x8e: case 0x9e: case 0xae: case 0xbe:
s.template schedule<O::LDY, mode>();
break;
case 0x9f: case 0xaf: case 0xbf:
s.template schedule<O::STY, mode>();
break;
case 0xce: case 0xde: case 0xee: case 0xfe:
s.template schedule<O::LDS, mode>();
break;
case 0xdf: case 0xef: case 0xff:
s.template schedule<O::STS, mode>();
break;
}
}
template <>
template <int i, typename SchedulerT> void OperationMapper<Page::Page2>::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<O::None, AM::Illegal>(); break;
case 0x3f: s.template schedule<O::SWI3, AM::Inherent>(); break;
case 0x83: case 0x93: case 0xa3: case 0xb3:
s.template schedule<O::CMPU, mode>();
break;
case 0x8c: case 0x9c: case 0xac: case 0xbc:
s.template schedule<O::CMPS, mode>();
break;
}
}
}
}
#endif /* InstructionSets_M6809_OperationMapper_hpp */

View File

@ -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 <XCTest/XCTest.h>
#include "Dispatcher.hpp"
#include "../../../InstructionSets/6809/OperationMapper.hpp"
using namespace InstructionSet::M6809;
namespace {
struct OperationCapture {
template <Operation operation, AddressingMode mode> void schedule() {
this->operation = operation;
this->mode = mode;
}
Operation operation;
AddressingMode mode;
};
template <Page page> OperationCapture capture(uint8_t opcode) {
OperationCapture catcher;
OperationMapper<page> 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<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)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

39
Reflection/Dispatcher.hpp Normal file
View File

@ -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 <cstdint>
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
#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 */