Commit 95d00202 authored by Tim Allen's avatar Tim Allen

Update to v106r75 release.

byuu says:

So tired ... so much left to do still ... sigh.

If someone's up for some code golf, open to suggestions on how to handle
the INTNEST control register. It's the only pure 16-bit register on the
system, and it breaks my `map`/`load`/`store<uint8,16,32>` abstraction.
Basically what I suspect happens is when you access INTNEST in 32-bit
mode, the upper 16-bits are just undefined (probably zero.) But
`map<uint32>(INTNEST)` must return a uint32& or nothing at all. So for the
time being, I'm just making store(ControlRegister) check if it's the
INTNEST ID, and clearing the upper bits of the written byte in that
case. It's hacky, but ... it's the best I can think of.

I added LDX, which is a 900H-only instruction, and the control register
map is for the 900/H CPU. I found the detailed differences between the
CPUs, and it doesn't look likely that I'm gonna support the 900 or
900/H1 at all. Not that there was a reason to anyway, but it's nice to
support more stuff when possible. Oh well.

The 4-byte instruction fetch queue is going to have to get implemented
inside fetch, or just not implemented at all ... not like I'd be able to
figure out the details of it anyway.

The manual isn't clear on how the MULA flags are calculated, but since
MUL doesn't set any flags, I assume the flags are based on the addition
after the multiplication, eg:

    uint32 a = indirect<int16>(XDE) * indirect<int16>(XHL);
    uint32 b = reg16; //opcode parameter
    uint32 c = a + b; //flags set based on a+b

No idea if it's right or not. It doesn't set carry or half-carry, so
it's not just simply the same as calling algorithmAdd.

Up to almost 70KB, not even halfway done, don't even have a disassembler
started yet. There's a real chance this could overtake the 68K for the
biggest CPU core in higan, although at this point I'm still thinking the
68K will end up larger.
parent 41148b10
Pipeline #42793691 passed with stage
in 23 minutes and 36 seconds
......@@ -30,7 +30,7 @@ using namespace nall;
namespace Emulator {
static const string Name = "higan";
static const string Version = "106.74";
static const string Version = "106.75";
static const string Author = "byuu";
static const string License = "GPLv3";
static const string Website = "https://byuu.org/";
......
......@@ -21,57 +21,57 @@ template<> auto TLCS900H::parity<uint32>(uint32 data) const -> bool {
template<typename T> auto TLCS900H::algorithmAdd(T target, T source, uint1 carry) -> T {
uint64 result = target + source + carry;
setCarry(result.bit(T::bits()));
setNegative(0);
setOverflow(T(~(target ^ source) & (target ^ result)).negative());
setHalfCarry(T(target ^ source ^ result).bit(4));
if constexpr(is_same<T, uint32>::value) setHalfCarry(Undefined);
setZero(T(result).zero());
setSign(result.negative());
CF = result.bit(T::bits());
NF = 0;
VF = T(~(target ^ source) & (target ^ result)).negative();
HF = T(target ^ source ^ result).bit(4);
if constexpr(is_same<T, uint32>::value) HF = Undefined;
ZF = T(result).zero();
SF = result.negative();
return result;
}
template<typename T> auto TLCS900H::algorithmAnd(T target, T source) -> T {
T result = target & source;
setCarry(0);
setNegative(0);
setParity(parity(result));
setHalfCarry(1);
setZero(result.zero());
setSign(result.negative());
CF = 0;
NF = 0;
PF = parity(result);
HF = 1;
ZF = result.zero();
SF = result.negative();
return result;
}
template<typename T> auto TLCS900H::algorithmOr(T target, T source) -> T {
T result = target | source;
setCarry(0);
setNegative(0);
setParity(parity(result));
setHalfCarry(0);
setZero(result.zero());
setSign(result.negative());
CF = 0;
NF = 0;
PF = parity(result);
HF = 0;
ZF = result.zero();
SF = result.negative();
return result;
}
template<typename T> auto TLCS900H::algorithmSubtract(T target, T source, uint1 carry) -> T {
uint64 result = target - source - carry;
setCarry(result.bit(T::bits()));
setNegative(1);
setOverflow(T((target ^ source) & (target ^ result)).negative());
setHalfCarry(T(target ^ source ^ result).bit(4));
if constexpr(is_same<T, uint32>::value) setHalfCarry(Undefined);
setZero(T(result).zero());
setSign(result.negative());
CF = result.bit(T::bits());
NF = 1;
VF = T((target ^ source) & (target ^ result)).negative();
HF = T(target ^ source ^ result).bit(4);
if constexpr(is_same<T, uint32>::value) HF = Undefined;
ZF = T(result).zero();
SF = result.negative();
return result;
}
template<typename T> auto TLCS900H::algorithmXor(T target, T source) -> T {
T result = target ^ source;
setCarry(0);
setNegative(0);
setParity(parity(result));
setHalfCarry(0);
setZero(result.zero());
setSign(result.negative());
CF = 0;
NF = 0;
PF = parity(result);
HF = 0;
ZF = result.zero();
SF = result.negative();
return result;
}
auto TLCS900H::condition(uint4 code) -> bool {
switch(code) {
case 0: return 0 == 1; //F (false)
case 1: return (sign() ^ overflow()) == 1; //LT (signed less than)
case 2: return (zero() | (sign() ^ overflow())) == 1; //LE (signed less than or equal)
case 3: return (carry() | zero()) == 1; //ULE (unsigned less than or equal)
case 4: return overflow() == 1; //OV (overflow)
case 5: return sign() == 1; //MI (minus)
case 6: return zero() == 1; //EQ (equal)
case 7: return carry() == 1; //ULT (unsigned less than)
case 8: return 0 == 0; //T (true)
case 9: return (sign() ^ overflow()) == 0; //GE (signed greater than or equal)
case 10: return (zero() | (sign() ^ overflow())) == 0; //GT (signed greater than)
case 11: return (carry() | zero()) == 0; //UGT (unsigned greater than)
case 12: return overflow() == 0; //NOV (no overflow)
case 13: return sign() == 0; //PL (plus)
case 14: return zero() == 0; //NE (not equal)
case 15: return carry() == 0; //UGE (unsigned greater than or equal)
case 0: return 0 == 1; //F (false)
case 1: return (SF ^ VF) == 1; //LT (signed less than)
case 2: return (ZF | (SF ^ VF)) == 1; //LE (signed less than or equal)
case 3: return (CF | ZF) == 1; //ULE (unsigned less than or equal)
case 4: return VF == 1; //OV (overflow)
case 5: return SF == 1; //MI (minus)
case 6: return ZF == 1; //EQ (equal)
case 7: return CF == 1; //ULT (unsigned less than)
case 8: return 0 == 0; //T (true)
case 9: return (SF ^ VF) == 0; //GE (signed greater than or equal)
case 10: return (ZF | (SF ^ VF)) == 0; //GT (signed greater than)
case 11: return (CF | ZF) == 0; //UGT (unsigned greater than)
case 12: return VF == 0; //NOV (no overflow)
case 13: return SF == 0; //PL (plus)
case 14: return ZF == 0; //NE (not equal)
case 15: return CF == 0; //UGE (unsigned greater than or equal)
} unreachable;
}
template<> auto TLCS900H::map(ControlRegister<uint8> register) -> maybe<uint8&> {
switch(register.id) {
#define r(id, name) case id: return r.name;
r(0x00, dmas[0].b.b0) r(0x01, dmas[0].b.b1) r(0x02, dmas[0].b.b2) r(0x03, dmas[0].b.b3)
r(0x04, dmas[1].b.b0) r(0x05, dmas[1].b.b1) r(0x06, dmas[1].b.b2) r(0x07, dmas[1].b.b3)
r(0x08, dmas[2].b.b0) r(0x09, dmas[2].b.b1) r(0x0a, dmas[2].b.b2) r(0x0b, dmas[2].b.b3)
r(0x0c, dmas[3].b.b0) r(0x0d, dmas[3].b.b1) r(0x0e, dmas[3].b.b2) r(0x0f, dmas[3].b.b3)
r(0x10, dmad[0].b.b0) r(0x11, dmad[0].b.b1) r(0x12, dmad[0].b.b2) r(0x13, dmad[0].b.b3)
r(0x14, dmad[1].b.b0) r(0x15, dmad[1].b.b1) r(0x16, dmad[1].b.b2) r(0x17, dmad[1].b.b3)
r(0x18, dmad[2].b.b0) r(0x19, dmad[2].b.b1) r(0x1a, dmad[2].b.b2) r(0x1b, dmad[2].b.b3)
r(0x1c, dmad[3].b.b0) r(0x1d, dmad[3].b.b1) r(0x1e, dmad[3].b.b2) r(0x1f, dmad[3].b.b3)
r(0x20, dmam[0].b.b0) r(0x21, dmam[0].b.b1) r(0x22, dmam[0].b.b2) r(0x23, dmam[0].b.b3)
r(0x24, dmam[1].b.b0) r(0x25, dmam[1].b.b1) r(0x26, dmam[1].b.b2) r(0x27, dmam[1].b.b3)
r(0x28, dmam[2].b.b0) r(0x29, dmam[2].b.b1) r(0x2a, dmam[2].b.b2) r(0x2b, dmam[2].b.b3)
r(0x2c, dmam[3].b.b0) r(0x2d, dmam[3].b.b1) r(0x2e, dmam[3].b.b3) r(0x2f, dmam[3].b.b3)
r(0x3c, intnest.b.b0) r(0x3d, intnest.b.b1)
#undef r
}
return nothing;
}
template<> auto TLCS900H::map(ControlRegister<uint16> register) -> maybe<uint16&> {
switch(register.id & ~1) {
#define r(id, name) case id: return r.name;
r(0x00, dmas[0].w.w0) r(0x02, dmas[0].w.w1)
r(0x04, dmas[1].w.w0) r(0x06, dmas[1].w.w1)
r(0x08, dmas[2].w.w0) r(0x0a, dmas[2].w.w1)
r(0x0c, dmas[3].w.w0) r(0x0e, dmas[3].w.w1)
r(0x10, dmad[0].w.w0) r(0x12, dmad[0].w.w1)
r(0x14, dmad[1].w.w0) r(0x16, dmad[1].w.w1)
r(0x18, dmad[2].w.w0) r(0x1a, dmad[2].w.w1)
r(0x1c, dmad[3].w.w0) r(0x1e, dmad[3].w.w1)
r(0x20, dmam[0].w.w0) r(0x22, dmam[0].w.w1)
r(0x24, dmam[1].w.w0) r(0x26, dmam[1].w.w1)
r(0x28, dmam[2].w.w0) r(0x2a, dmam[2].w.w1)
r(0x2c, dmam[3].w.w0) r(0x2e, dmam[3].w.w1)
r(0x3c, intnest.w.w0)
#undef r
}
return nothing;
}
template<> auto TLCS900H::map(ControlRegister<uint32> register) -> maybe<uint32&> {
switch(register.id & ~1) {
#define r(id, name) case id: return r.name;
r(0x00, dmas[0].l.l0)
r(0x04, dmas[1].l.l0)
r(0x08, dmas[2].l.l0)
r(0x0c, dmas[3].l.l0)
r(0x10, dmad[0].l.l0)
r(0x14, dmad[1].l.l0)
r(0x18, dmad[2].l.l0)
r(0x1c, dmad[3].l.l0)
r(0x20, dmam[0].l.l0)
r(0x24, dmam[1].l.l0)
r(0x28, dmam[2].l.l0)
r(0x2c, dmam[3].l.l0)
r(0x3c, intnest.l.l0)
#undef r
}
return nothing;
}
template<> auto TLCS900H::load< uint8>(ControlRegister< uint8> register) -> uint8 { return map(register)(Undefined); }
template<> auto TLCS900H::load<uint16>(ControlRegister<uint16> register) -> uint16 { return map(register)(Undefined); }
template<> auto TLCS900H::load<uint32>(ControlRegister<uint32> register) -> uint32 { return map(register)(Undefined); }
template<> auto TLCS900H::store<uint8>(ControlRegister<uint8> register, uint32 data) -> void {
if(auto r = map(register)) r() = data;
}
template<> auto TLCS900H::store<uint16>(ControlRegister<uint16> register, uint32 data) -> void {
if(auto r = map(register)) r() = data;
}
template<> auto TLCS900H::store<uint32>(ControlRegister<uint32> register, uint32 data) -> void {
//INTNEST is 16-bit: this isn't the nicest way to handle this, but ...
if((register.id & ~3) == 0x3c) data = (uint16)data;
if(auto r = map(register)) r() = data;
}
This diff is collapsed.
template<typename Target>
auto TLCS900H::toSigned(Target target) -> int32 {
if constexpr(Target::bits() == 8) return (int8)target;
if constexpr(Target::bits() == 16) return (int16)target;
if constexpr(Target::bits() == 32) return (int32)target;
return Undefined;
}
template<typename Target, typename Source>
auto TLCS900H::instructionAdd(Target target, Source source) -> void {
store(target, algorithmAdd(load(target), load(source)));
......@@ -5,7 +13,7 @@ auto TLCS900H::instructionAdd(Target target, Source source) -> void {
template<typename Target, typename Source>
auto TLCS900H::instructionAddCarry(Target target, Source source) -> void {
store(target, algorithmAdd(load(target), load(source), carry()));
store(target, algorithmAdd(load(target), load(source), CF));
}
template<typename Target, typename Source>
......@@ -19,13 +27,34 @@ auto TLCS900H::instructionCall(uint4 code, Source source) -> void {
if(condition(code)) push(PC), store(PC, address);
}
template<typename Source>
auto TLCS900H::instructionCallRelative(Source displacement) -> void {
push(PC);
store(PC, load(PC) + load(displacement));
}
template<typename Target, typename Source>
auto TLCS900H::instructionCompare(Target target, Source source) -> void {
algorithmSubtract(load(target), load(source));
}
auto TLCS900H::instructionComplementCarry() -> void {
setCarry(!carry());
template<typename Target>
auto TLCS900H::instructionComplement(Target target) -> void {
store(target, ~load(target));
NF = 1;
HF = 1;
}
template<typename Target, typename Source>
auto TLCS900H::instructionDivide(Target target, Source source) -> void {
//TODO: division by zero
store(expand(target), load(target) / load(source));
}
template<typename Target, typename Source>
auto TLCS900H::instructionDivideSigned(Target target, Source source) -> void {
//TODO: division by zero
store(expand(target), toSigned(load(target)) / toSigned(load(source)));
}
template<typename Target, typename Source>
......@@ -55,6 +84,44 @@ auto TLCS900H::instructionLoad(Target target, Source source) -> void {
store(target, load(source));
}
//reverse all bits in a 16-bit register
auto TLCS900H::instructionMirror(Register<uint16> register) -> void {
auto data = load(register);
uint8 lo = (data.byte(0) * 0x80200802ull & 0x884422110ull) * 0x101010101ull >> 32;
uint8 hi = (data.byte(1) * 0x80200802ull & 0x884422110ull) * 0x101010101ull >> 32;
store(register, lo << 8 | hi << 0);
}
template<typename Target, typename Source>
auto TLCS900H::instructionMultiply(Target target, Source source) -> void {
store(expand(target), load(target) * load(source));
}
auto TLCS900H::instructionMultiplyAdd(Register<uint16> register) -> void {
auto xde = toMemory<int16>(load(XDE));
auto xhl = toMemory<int16>(load(XHL));
auto source = load(expand(register));
auto target = load(xde) * load(xhl);
store(expand(register), source + target);
store(XHL, load(XHL) - 2);
auto result = load(expand(register));
VF = uint32(~(target ^ source) & (target ^ result)).negative();
ZF = result.zero();
SF = result.negative();
}
template<typename Target, typename Source>
auto TLCS900H::instructionMultiplySigned(Target target, Source source) -> void {
store(expand(target), toSigned(load(target)) * toSigned(load(source)));
}
template<typename Target>
auto TLCS900H::instructionNegate(Target target) -> void {
store(target, algorithmSubtract(typename Target::type{0}, load(target)));
}
auto TLCS900H::instructionNoOperation() -> void {
}
......@@ -73,10 +140,20 @@ auto TLCS900H::instructionPush(Source source) -> void {
push(source);
}
auto TLCS900H::instructionReturn(uint4 code) -> void {
if(condition(code)) pop(PC);
}
template<typename Source>
auto TLCS900H::instructionReturnDeallocate(Source displacement) -> void {
pop(PC);
store(XSP, load(XSP) + load(displacement));
}
auto TLCS900H::instructionReturnInterrupt() -> void {
pop(SR);
pop(PC);
//TODO: decrement INTNEST here
store(INTNEST, load(INTNEST) - 1);
}
template<typename Target>
......@@ -84,8 +161,16 @@ auto TLCS900H::instructionSetConditionCode(uint4 code, Target target) -> void {
store(target, condition(code));
}
auto TLCS900H::instructionSetInterruptFlags(uint3 flags) -> void {
setIFF(flags);
auto TLCS900H::instructionSetFlag(uint1& flag, uint1 value) -> void {
flag = value;
}
auto TLCS900H::instructionSetInterruptFlipFlop(uint3 value) -> void {
IFF = value;
}
auto TLCS900H::instructionSetRegisterFilePointer(uint2 value) -> void {
RFP = value;
}
auto TLCS900H::instructionSoftwareInterrupt(uint3 interrupt) -> void {
......@@ -99,7 +184,7 @@ auto TLCS900H::instructionSubtract(Target target, Source source) -> void {
template<typename Target, typename Source>
auto TLCS900H::instructionSubtractCarry(Target target, Source source) -> void {
store(target, algorithmSubtract(load(target), load(source), carry()));
store(target, algorithmSubtract(load(target), load(source), CF));
}
template<typename Target, typename Source>
......
......@@ -25,6 +25,10 @@ template<> auto TLCS900H::fetch<int16>() -> int16 { return (int16)fetch<uint16>(
template<> auto TLCS900H::fetch<int24>() -> int24 { return (int24)fetch<uint24>(); }
template<> auto TLCS900H::fetch<int32>() -> int32 { return (int32)fetch<uint32>(); }
template<typename T> auto TLCS900H::fetchRegister() -> Register<T> { return Register<T>{fetch<uint8>()}; }
template<typename T, typename U> auto TLCS900H::fetchMemory() -> Memory<T> { return Memory<T>{fetch<U>()}; }
template<typename T> auto TLCS900H::fetchImmediate() -> Immediate<T> { return Immediate<T>{fetch<T>()}; }
//
#define XSP r.xsp.l.l0
......@@ -66,6 +70,10 @@ template<> auto TLCS900H::load(Memory<uint32> memory) -> uint32 {
return data |= read(memory.address + 3) << 24;
}
template<> auto TLCS900H::load(Memory< int8> memory) -> int8 { return (int8)load< uint8>(Memory< uint8>{memory.address}); }
template<> auto TLCS900H::load(Memory<int16> memory) -> int16 { return (int16)load<uint16>(Memory<uint16>{memory.address}); }
template<> auto TLCS900H::load(Memory<int32> memory) -> int32 { return (int32)load<uint32>(Memory<uint32>{memory.address}); }
template<> auto TLCS900H::store(Memory< uint8> memory, uint32 data) -> void {
write(memory.address, data);
}
......
#define a r.rfp
#define p r.rfpp
#define a RFP
#define p RFP - 1 & 3
template<> auto TLCS900H::map(Register<uint8> register) -> maybe<uint8&> {
switch(register.id) {
......@@ -85,6 +85,10 @@ template<> auto TLCS900H::store< uint8>(Register< uint8> register, uint32 data)
template<> auto TLCS900H::store<uint16>(Register<uint16> register, uint32 data) -> void { if(auto r = map(register)) r() = data; }
template<> auto TLCS900H::store<uint32>(Register<uint32> register, uint32 data) -> void { if(auto r = map(register)) r() = data; }
auto TLCS900H::expand(Register< uint8> register) const -> Register<uint16> { return {register.id & ~1}; }
auto TLCS900H::expand(Register<uint16> register) const -> Register<uint32> { return {register.id & ~3}; }
auto TLCS900H::expand(Register<uint32> register) const -> Register<uint32> { return {Undefined}; }
auto TLCS900H::load(FlagRegister f) -> uint8 {
switch(f.id) {
case 0: return r.c << 0 | r.n << 1 | r.v << 2 | r.h << 4 | r.z << 6 | r.s << 7;
......@@ -107,7 +111,6 @@ auto TLCS900H::store(StatusRegister, uint16 data) -> void {
store(F, data);
r.rfp = data.bits( 8, 9);
r.iff = data.bits(12,14);
r.rfpp = r.rfp - 1;
}
auto TLCS900H::load(ProgramCounter) -> uint32 { return r.pc.l.l0; }
......
......@@ -3,7 +3,19 @@
namespace Processor {
#define CF r.c
#define NF r.n
#define VF r.v
#define PF r.v
#define HF r.h
#define ZF r.z
#define SF r.s
#define RFP r.rfp
#define IFF r.iff
#include "registers.cpp"
#include "control-registers.cpp"
#include "memory.cpp"
#include "conditions.cpp"
#include "algorithms.cpp"
......
......@@ -5,6 +5,7 @@
* what happens when a prohibited instruction operand size is used? (eg adc.l (memory),#immediate)
* what happens when %11 is used for pre-decrement and post-increment addressing?
* what happens when using 8-bit register indexing and d0 is set (Word) or d1/d0 is set (Long)?
* what happens during an LDX instruction when the three padding bytes aren't all 0x00?
* what value is read back from a non-existent 8-bit register ID? (eg 0x40-0xcf)
* many instructions are undefined, some are marked as dummy instructions ... what do each do?
*/
......@@ -14,18 +15,50 @@
namespace Processor {
struct TLCS900H {
virtual auto read(uint32 address) -> uint8 { return 0; }
virtual auto write(uint32 address, uint8 data) -> void {};
virtual auto read(uint24 address) -> uint8 { return 0; }
virtual auto write(uint24 address, uint8 data) -> void {};
TLCS900H();
struct FlagRegister { using type = uint8; enum : uint { bits = 8 }; uint1 id; };
struct StatusRegister { using type = uint16; enum : uint { bits = 16 }; };
struct ProgramCounter { using type = uint32; enum : uint { bits = 32 }; };
struct FlagRegister {
using type = uint8;
enum : uint { bits = 8 };
uint1 id;
};
struct StatusRegister {
using type = uint16;
enum : uint { bits = 16 };
};
struct ProgramCounter {
using type = uint32;
enum : uint { bits = 32 };
};
template<typename T> struct Register { using type = T; enum : uint { bits = 8 * sizeof(T) }; uint8 id; };
template<typename T> struct Memory { using type = T; enum : uint { bits = 8 * sizeof(T) }; T address; };
template<typename T> struct Immediate { using type = T; enum : uint { bits = 8 * sizeof(T) }; T constant; };
template<typename T> struct ControlRegister {
using type = T;
enum : uint { bits = 8 * sizeof(T) };
uint8 id;
};
template<typename T> struct Register {
using type = T;
enum : uint { bits = 8 * sizeof(T) };
uint8 id;
};
template<typename T> struct Memory {
using type = T;
enum : uint { bits = 8 * sizeof(T) };
T address;
};
template<typename T> struct Immediate {
using type = T;
enum : uint { bits = 8 * sizeof(T) };
T constant;
};
template<typename T> auto load(Immediate<T> immediate) const -> T { return immediate.constant; }
......@@ -36,6 +69,9 @@ struct TLCS900H {
template<typename T> auto map(Register<T>) -> maybe<T&>;
template<typename T> auto load(Register<T>) -> T;
template<typename T> auto store(Register<T>, uint32) -> void;
auto expand(Register< uint8>) const -> Register<uint16>;
auto expand(Register<uint16>) const -> Register<uint32>;
auto expand(Register<uint32>) const -> Register<uint32>;
auto load(FlagRegister) -> uint8;
auto store(FlagRegister, uint8) -> void;
auto load(StatusRegister) -> uint16;
......@@ -43,8 +79,16 @@ struct TLCS900H {
auto load(ProgramCounter) -> uint32;
auto store(ProgramCounter, uint32) -> void;
//control-registers.cpp
template<typename T> auto map(ControlRegister<T>) -> maybe<T&>;
template<typename T> auto load(ControlRegister<T>) -> T;
template<typename T> auto store(ControlRegister<T>, uint32) -> void;
//memory.cpp
template<typename T = uint8> auto fetch() -> T;
template<typename T> auto fetchRegister() -> Register<T>;
template<typename T, typename U> auto fetchMemory() -> Memory<T>;
template<typename T> auto fetchImmediate() -> Immediate<T>;
template<typename T> auto push(T) -> void;
template<typename T> auto pop(T) -> void;
template<typename T> auto load(Memory<T>) -> T;
......@@ -62,35 +106,54 @@ struct TLCS900H {
template<typename T> auto algorithmXor(T target, T source) -> T;
//instruction.cpp
template<typename T> auto registers(uint3) const -> Register<T>;
template<typename T> auto toRegister3(uint3) const -> Register<T>;
template<typename T> auto toRegister8(uint8) const -> Register<T>;
template<typename T> auto toControlRegister(uint8) const -> ControlRegister<T>;
template<typename T> auto toMemory(uint32 address) const -> Memory<T>;
template<typename T> auto toImmediate(uint32 constant) const -> Immediate<T>;
template<typename T> auto toImmediate3(uint3 constant) const -> Immediate<T>;
auto instruction() -> void;
template<typename Register> auto instructionRegister(Register) -> void;
template<typename Memory> auto instructionSourceMemory(Memory) -> void;
auto instructionTargetMemory(uint32 address) -> void;
//instructions.cpp