Commit 41148b10 authored by Tim Allen's avatar Tim Allen

Update to v106r74 release.

byuu says:

So I spent the better part of eight hours refactoring the TLCS900H core
to be more flexible in light of new edge cases.

I'm now including the size information inside of the types (eg
Register<Byte>, Memory<Word>) rather than as parameters to the
instruction handlers. This allows me to eg implement RETI without
needing template arguments in all the instructions. pop(SR), pop(PC) can
deduce how much to pop off of the stack. It's still highly templated,
but not unrolling the 3-bit register indexes and instead going through
the switch table to access registers is going to hurt the performance a
good deal.

A benefit of this is that Register{A} != Register{WA} != Register{XWA}
anymore, despite them sharing IDs.

I also renamed read/write to load/store for the CPU core, because
implicit conversions are nasty. They all call the virtual read/write.

I added more instructions, improved the memory addressing mode support,
and some other things.

I got rid of Byte, Word, Long because there's too many alternate sizes
needed: int8, int16, uint24, etc.

Ran into a really annoying C++ case ...

    struct TLCS900H {
      template<typename T> auto store(Register<T> target, T source) -> void;
    };

If you call store(Register<uint32>(x), uint16(y)); it errors out since
the T types don't match. But you can't specialize it:

    template<typename T, typename U> auto store(Register<T>, U) -> void;
    template<typename U> auto TLCS900H::store<uint32, U>(Register<uint32>, U) -> void;

Because somehow it's 2019 and we still can't do partial template
specialization inside classes ...

So as a result, I had to make T source be type uint32 even for
Register<uint8> and Register<uint16>. Doesn't matter too much, just
annoying.
parent dbee8934
Pipeline #42630870 passed with stage
in 13 minutes and 26 seconds
......@@ -30,7 +30,7 @@ using namespace nall;
namespace Emulator {
static const string Name = "higan";
static const string Version = "106.73";
static const string Version = "106.74";
static const string Author = "byuu";
static const string License = "GPLv3";
static const string Website = "https://byuu.org/";
......
template<> auto TLCS900H::parity(Byte data) const -> bool {
template<> auto TLCS900H::parity< uint8>( uint8 data) const -> bool {
data ^= data >> 4;
data ^= data >> 2;
data ^= data >> 1;
return !(data & 1);
}
template<> auto TLCS900H::parity(Word data) const -> bool {
template<> auto TLCS900H::parity<uint16>(uint16 data) const -> bool {
data ^= data >> 8;
data ^= data >> 4;
data ^= data >> 2;
......@@ -13,65 +13,65 @@ template<> auto TLCS900H::parity(Word data) const -> bool {
return !(data & 1);
}
template<> auto TLCS900H::parity(Long data) const -> bool {
template<> auto TLCS900H::parity<uint32>(uint32 data) const -> bool {
return Undefined;
}
//
template<typename Size> auto TLCS900H::algorithmAdd(Size target, Size source, uint1 carry) -> Size {
template<typename T> auto TLCS900H::algorithmAdd(T target, T source, uint1 carry) -> T {
uint64 result = target + source + carry;
CF = result.bit(Size::bits());
NF = 0;
VF = Size(~(target ^ source) & (target ^ result)).negative();
HF = Size(target ^ source ^ result).bit(4);
if constexpr(isLong<Size>()) HF = Undefined;
ZF = Size(result).zero();
SF = result.negative();
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());
return result;
}
template<typename Size> auto TLCS900H::algorithmAnd(Size target, Size source) -> Size {
Size result = target & source;
CF = 0;
NF = 0;
VF = parity(result);
HF = 1;
ZF = result.zero();
SF = result.negative();
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());
return result;
}
template<typename Size> auto TLCS900H::algorithmOr(Size target, Size source) -> Size {
Size result = target | source;
CF = 0;
NF = 0;
VF = parity(result);
HF = 0;
ZF = result.zero();
SF = result.negative();
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());
return result;
}
template<typename Size> auto TLCS900H::algorithmSubtract(Size target, Size source, uint1 carry) -> Size {
template<typename T> auto TLCS900H::algorithmSubtract(T target, T source, uint1 carry) -> T {
uint64 result = target - source - carry;
CF = result.bit(Size::bits());
NF = 1;
VF = Size((target ^ source) & (target ^ result)).negative();
HF = Size(target ^ source ^ result).bit(4);
if constexpr(isLong<Size>()) HF = Undefined;
ZF = Size(result).zero();
SF = result.negative();
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());
return result;
}
template<typename Size> auto TLCS900H::algorithmXor(Size target, Size source) -> Size {
Size result = target ^ source;
CF = 0;
NF = 0;
VF = parity(result);
HF = 0;
ZF = result.zero();
SF = result.negative();
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());
return result;
}
auto TLCS900H::condition(uint4 code) -> bool {
switch(code) {
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)
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)
} unreachable;
}
This diff is collapsed.
#define read read<Size>
#define write write<Size>
#define push push<Size>
#define pop pop<Size>
#define algorithm(name, ...) algorithm##name<Size>(__VA_ARGS__)
template<typename Size, typename Target, typename Source>
template<typename Target, typename Source>
auto TLCS900H::instructionAdd(Target target, Source source) -> void {
write(target, algorithm(Add, read(target), read(source)));
store(target, algorithmAdd(load(target), load(source)));
}
template<typename Size, typename Target, typename Source>
template<typename Target, typename Source>
auto TLCS900H::instructionAddCarry(Target target, Source source) -> void {
write(target, algorithm(Add, read(target), read(source), CF));
store(target, algorithmAdd(load(target), load(source), carry()));
}
template<typename Size, typename Target, typename Source>
template<typename Target, typename Source>
auto TLCS900H::instructionAnd(Target target, Source source) -> void {
write(target, algorithm(And, read(target), read(source)));
store(target, algorithmAnd(load(target), load(source)));
}
template<typename Source>
auto TLCS900H::instructionCall(uint4 code, Source source) -> void {
auto address = load(source);
if(condition(code)) push(PC), store(PC, address);
}
template<typename Size, typename Target, typename Source>
template<typename Target, typename Source>
auto TLCS900H::instructionCompare(Target target, Source source) -> void {
algorithm(Subtract, read(target), read(source));
algorithmSubtract(load(target), load(source));
}
auto TLCS900H::instructionComplementCarry() -> void {
CF = !CF;
setCarry(!carry());
}
template<typename Size, typename Source> auto TLCS900H::instructionJump(Source source) -> void {
PC = read(source);
template<typename Target, typename Source>
auto TLCS900H::instructionExchange(Target target, Source source) -> void {
auto data = load(target);
store(target, load(source));
store(source, data);
}
template<typename Size, typename Source> auto TLCS900H::instructionJump(uint4 code, Source source) -> void {
auto address = read(source);
if(condition(code)) PC = address;
auto TLCS900H::instructionHalt() -> void {
setHalted(true);
}
template<typename Size> auto TLCS900H::instructionJumpRelative(uint4 code, Size displacement) -> void {
if(condition(code)) PC += displacement;
template<typename Source>
auto TLCS900H::instructionJump(uint4 code, Source source) -> void {
auto address = load(source);
if(condition(code)) store(PC, address);
}
template<typename Size, typename Target, typename Source>
template<typename Source>
auto TLCS900H::instructionJumpRelative(uint4 code, Source displacement) -> void {
if(condition(code)) store(PC, load(PC) + load(displacement));
}
template<typename Target, typename Source>
auto TLCS900H::instructionLoad(Target target, Source source) -> void {
write(target, read(source));
store(target, load(source));
}
auto TLCS900H::instructionNoOperation() -> void {
}
template<typename Size, typename Target, typename Source>
template<typename Target, typename Source>
auto TLCS900H::instructionOr(Target target, Source source) -> void {
write(target, algorithm(Or, read(target), read(source)));
store(target, algorithmOr(load(target), load(source)));
}
template<typename Size, typename Target>
template<typename Target>
auto TLCS900H::instructionPop(Target target) -> void {
write(target, pop());
pop(target);
}
template<typename Size, typename Source>
template<typename Source>
auto TLCS900H::instructionPush(Source source) -> void {
push(read(source));
push(source);
}
auto TLCS900H::instructionReturnInterrupt() -> void {
pop(SR);
pop(PC);
//TODO: decrement INTNEST here
}
template<typename Target>
auto TLCS900H::instructionSetConditionCode(uint4 code, Target target) -> void {
store(target, condition(code));
}
auto TLCS900H::instructionSetInterruptFlags(uint3 flags) -> void {
setIFF(flags);
}
auto TLCS900H::instructionSoftwareInterrupt(uint3 interrupt) -> void {
//TODO
}
template<typename Size, typename Target, typename Source>
template<typename Target, typename Source>
auto TLCS900H::instructionSubtract(Target target, Source source) -> void {
write(target, algorithm(Subtract, read(target), read(source)));
store(target, algorithmSubtract(load(target), load(source)));
}
template<typename Size, typename Target, typename Source>
template<typename Target, typename Source>
auto TLCS900H::instructionSubtractCarry(Target target, Source source) -> void {
write(target, algorithm(Subtract, read(target), read(source), CF));
store(target, algorithmSubtract(load(target), load(source), carry()));
}
template<typename Size, typename Target, typename Source>
template<typename Target, typename Source>
auto TLCS900H::instructionXor(Target target, Source source) -> void {
write(target, algorithm(Xor, read(target), read(source)));
store(target, algorithmXor(load(target), load(source)));
}
#undef read
#undef write
#undef push
#undef pop
#undef algorithm
template<> auto TLCS900H::fetch<Byte>() -> Byte {
return 0x00;
template<> auto TLCS900H::fetch< uint8>() -> uint8 {
return rand();
}
template<> auto TLCS900H::fetch<Word>() -> Word {
uint16 data = fetch<Byte>();
return data | fetch<Byte>() << 8;
template<> auto TLCS900H::fetch<uint16>() -> uint16 {
uint16 data = fetch<uint8>();
return data | fetch<uint8>() << 8;
}
template<> auto TLCS900H::fetch<uint24>() -> uint24 {
uint24 data = fetch<Byte>();
data |= fetch<Byte>() << 8;
return data |= fetch<Byte>() << 16;
uint24 data = fetch<uint8>();
data |= fetch<uint8>() << 8;
return data |= fetch<uint8>() << 16;
}
template<> auto TLCS900H::fetch<Long>() -> Long {
uint32 data = fetch<Byte>();
data |= fetch<Byte>() << 8;
data |= fetch<Byte>() << 16;
return data |= fetch<Byte>() << 24;
template<> auto TLCS900H::fetch<uint32>() -> uint32 {
uint32 data = fetch<uint8>();
data |= fetch<uint8>() << 8;
data |= fetch<uint8>() << 16;
return data |= fetch<uint8>() << 24;
}
template<> auto TLCS900H::fetch< int8>() -> int8 { return ( int8)fetch< uint8>(); }
......@@ -29,78 +29,55 @@ template<> auto TLCS900H::fetch<int32>() -> int32 { return (int32)fetch<uint32>(
#define XSP r.xsp.l.l0
template<> auto TLCS900H::push<Byte>(Byte data) -> void {
write(--XSP, data);
template<typename T> auto TLCS900H::pop(T data) -> void {
auto value = typename T::type();
if constexpr(T::bits >= 8) value.byte(0) = read(XSP++);
if constexpr(T::bits >= 16) value.byte(1) = read(XSP++);
if constexpr(T::bits >= 24) value.byte(2) = read(XSP++);
if constexpr(T::bits >= 32) value.byte(3) = read(XSP++);
store(data, value);
}
template<> auto TLCS900H::push<Word>(Word data) -> void {
write(--XSP, data >> 0);
write(--XSP, data >> 8);
}
template<> auto TLCS900H::push<Long>(Long data) -> void {
write(--XSP, data >> 0);
write(--XSP, data >> 8);
write(--XSP, data >> 16);
write(--XSP, data >> 24);
}
//
template<> auto TLCS900H::pop<Byte>() -> Byte {
return read(XSP++);
}
template<> auto TLCS900H::pop<Word>() -> Word {
uint16 data = read(XSP++) << 0;
return data | read(XSP++) << 8;
}
template<> auto TLCS900H::pop<Long>() -> Long {
uint32 data = read(XSP++) << 0;
data |= read(XSP++) << 8;
data |= read(XSP++) << 16;
return data |= read(XSP++) << 24;
template<typename T> auto TLCS900H::push(T data) -> void {
auto value = load(data);
if constexpr(T::bits >= 8) write(--XSP, value >> 0);
if constexpr(T::bits >= 16) write(--XSP, value >> 8);
if constexpr(T::bits >= 24) write(--XSP, value >> 16);
if constexpr(T::bits >= 32) write(--XSP, value >> 24);
}
#undef XSP
//
template<> auto TLCS900H::read<Byte>(Memory memory) -> uint8 {
uint32 address = memory.value;
return read(address);
template<> auto TLCS900H::load(Memory< uint8> memory) -> uint8 {
return read(memory.address);
}
template<> auto TLCS900H::read<Word>(Memory memory) -> uint16 {
uint32 address = memory.value;
uint16 data = read(address + 0) << 0;
return data | read(address + 1) << 8;
template<> auto TLCS900H::load(Memory<uint16> memory) -> uint16 {
uint16 data = read(memory.address + 0) << 0;
return data | read(memory.address + 1) << 8;
}
template<> auto TLCS900H::read<Long>(Memory memory) -> uint32 {
uint32 address = memory.value;
uint32 data = read(address + 0) << 0;
data |= read(address + 1) << 8;
data |= read(address + 2) << 16;
return data |= read(address + 3) << 24;
template<> auto TLCS900H::load(Memory<uint32> memory) -> uint32 {
uint32 data = read(memory.address + 0) << 0;
data |= read(memory.address + 1) << 8;
data |= read(memory.address + 2) << 16;
return data |= read(memory.address + 3) << 24;
}
template<> auto TLCS900H::write<Byte>(Memory memory, uint8 data) -> void {
uint32 address = memory.value;
write(address + 0, data >> 0);
template<> auto TLCS900H::store(Memory< uint8> memory, uint32 data) -> void {
write(memory.address, data);
}
template<> auto TLCS900H::write<Word>(Memory memory, uint16 data) -> void {
uint32 address = memory.value;
write(address + 0, data >> 0);
write(address + 1, data >> 8);
template<> auto TLCS900H::store(Memory<uint16> memory, uint32 data) -> void {
write(memory.address + 0, data >> 0);
write(memory.address + 1, data >> 8);
}
template<> auto TLCS900H::write<Long>(Memory memory, uint32 data) -> void {
uint32 address = memory.value;
write(address + 0, data >> 0);
write(address + 1, data >> 8);
write(address + 2, data >> 16);
write(address + 3, data >> 24);
template<> auto TLCS900H::store(Memory<uint32> memory, uint32 data) -> void {
write(memory.address + 0, data >> 0);
write(memory.address + 1, data >> 8);
write(memory.address + 2, data >> 16);
write(memory.address + 3, data >> 24);
}
#define PC r.pc.l.l0
#define a r.rfp
#define p r.rfpp
#define CF r.sr.f.c
#define NF r.sr.f.n
#define VF r.sr.f.v
#define HF r.sr.f.h
#define ZF r.sr.f.z
#define SF r.sr.f.s
#define RFP r.sr.rfp
#define RFPP r.sr.rfpp
#define IFF r.sr.iff
#define a RFP
#define p RFPP
template<> auto TLCS900H::map<Byte>(Register register) -> maybe<Byte&> {
switch(register.value) {
template<> auto TLCS900H::map(Register<uint8> register) -> maybe<uint8&> {
switch(register.id) {
#define r(id, name) case id: return r.name;
r(0x00, xwa[0].b.b0) r(0x01, xwa[0].b.b1) r(0x02, xwa[0].b.b2) r(0x03, xwa[0].b.b3)
r(0x04, xbc[0].b.b0) r(0x05, xbc[0].b.b1) r(0x06, xbc[0].b.b2) r(0x07, xbc[0].b.b3)
......@@ -50,8 +37,8 @@ template<> auto TLCS900H::map<Byte>(Register register) -> maybe<Byte&> {
return nothing;
}
template<> auto TLCS900H::map<Word>(Register register) -> maybe<Word&> {
switch(register.value & ~1) {
template<> auto TLCS900H::map(Register<uint16> register) -> maybe<uint16&> {
switch(register.id & ~1) {
#define r(id, name) case id: return r.name;
r(0x00, xwa[0].w.w0) r(0x02, xwa[0].w.w1) r(0x04, xbc[0].w.w0) r(0x06, xbc[0].w.w1)
r(0x08, xde[0].w.w0) r(0x0a, xde[0].w.w1) r(0x0c, xhl[0].w.w0) r(0x0e, xhl[0].w.w1)
......@@ -72,8 +59,8 @@ template<> auto TLCS900H::map<Word>(Register register) -> maybe<Word&> {
return nothing;
}
template<> auto TLCS900H::map<Long>(Register register) -> maybe<Long&> {
switch(register.value & ~3) {
template<> auto TLCS900H::map(Register<uint32> register) -> maybe<uint32&> {
switch(register.id & ~3) {
#define r(id, name) case id: return r.name;
r(0x00, xwa[0].l.l0) r(0x04, xbc[0].l.l0) r(0x08, xde[0].l.l0) r(0x0c, xhl[0].l.l0)
r(0x10, xwa[1].l.l0) r(0x14, xbc[1].l.l0) r(0x18, xde[1].l.l0) r(0x1c, xhl[1].l.l0)
......@@ -90,64 +77,38 @@ template<> auto TLCS900H::map<Long>(Register register) -> maybe<Long&> {
#undef a
#undef p
template<> auto TLCS900H::read<Byte>(Register register) -> Byte {
return map<Byte>(register)(0);
}
template<> auto TLCS900H::read<Word>(Register register) -> Word {
return map<Word>(register)(0);
}
template<> auto TLCS900H::read<Long>(Register register) -> Long {
return map<Long>(register)(0);
}
template<> auto TLCS900H::write<Byte>(Register register, Byte data) -> void {
if(auto r = map<Byte>(register)) r() = data;
}
template<> auto TLCS900H::load< uint8>(Register< uint8> register) -> uint8 { return map(register)(Undefined); }
template<> auto TLCS900H::load<uint16>(Register<uint16> register) -> uint16 { return map(register)(Undefined); }
template<> auto TLCS900H::load<uint32>(Register<uint32> register) -> uint32 { return map(register)(Undefined); }
template<> auto TLCS900H::write<Word>(Register register, Word data) -> void {
if(auto r = map<Word>(register)) r() = data;
}
template<> auto TLCS900H::store< uint8>(Register< uint8> register, uint32 data) -> void { if(auto r = map(register)) r() = data