mirror of
https://github.com/MoleskiCoder/EightBit.git
synced 2025-08-08 09:25:00 +00:00
Tidy up some C++ code (concentrating on the Z80 at the moment).
Signed-off-by: Adrian Conlon <adrian.conlon@gmail.com>
This commit is contained in:
242
Z80/inc/Z80.h
242
Z80/inc/Z80.h
@@ -1,14 +1,13 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <cassert>
|
#include <array>
|
||||||
#include <stdexcept>
|
#include <functional>
|
||||||
|
|
||||||
#include <IntelProcessor.h>
|
#include <IntelProcessor.h>
|
||||||
#include <EventArgs.h>
|
#include <EventArgs.h>
|
||||||
#include <Signal.h>
|
#include <Signal.h>
|
||||||
#include <Register.h>
|
#include <Register.h>
|
||||||
#include <EightBitCompilerDefinitions.h>
|
|
||||||
|
|
||||||
namespace EightBit {
|
namespace EightBit {
|
||||||
|
|
||||||
@@ -99,19 +98,14 @@ namespace EightBit {
|
|||||||
[[nodiscard]] auto& IFF1() { return m_iff1; }
|
[[nodiscard]] auto& IFF1() { return m_iff1; }
|
||||||
[[nodiscard]] auto& IFF2() { return m_iff2; }
|
[[nodiscard]] auto& IFF2() { return m_iff2; }
|
||||||
|
|
||||||
void exx() {
|
void exx() { m_registerSet ^= 1; }
|
||||||
m_registerSet ^= 1;
|
void exxAF() { m_accumulatorFlagsSet ^= 1; }
|
||||||
}
|
|
||||||
|
|
||||||
void exxAF() {
|
[[nodiscard]] auto requestingIO() const { return lowered(IORQ()); }
|
||||||
m_accumulatorFlagsSet ^= 1;
|
[[nodiscard]] auto requestingMemory() const { return lowered(MREQ()); }
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] bool requestingIO() const { return lowered(IORQ()); }
|
[[nodiscard]] auto requestingRead() const { return lowered(RD()); }
|
||||||
[[nodiscard]] bool requestingMemory() const { return lowered(MREQ()); }
|
[[nodiscard]] auto requestingWrite() const { return lowered(WR()); }
|
||||||
|
|
||||||
[[nodiscard]] bool requestingRead() const { return lowered(RD()); }
|
|
||||||
[[nodiscard]] bool requestingWrite() const { return lowered(WR()); }
|
|
||||||
|
|
||||||
// ** From the Z80 CPU User Manual
|
// ** From the Z80 CPU User Manual
|
||||||
// RFSH.Refresh(output, active Low). RFSH, together with MREQ, indicates that the lower
|
// RFSH.Refresh(output, active Low). RFSH, together with MREQ, indicates that the lower
|
||||||
@@ -150,11 +144,6 @@ namespace EightBit {
|
|||||||
DEFINE_PIN_ACTIVATOR_LOW(RD)
|
DEFINE_PIN_ACTIVATOR_LOW(RD)
|
||||||
DEFINE_PIN_ACTIVATOR_LOW(WR)
|
DEFINE_PIN_ACTIVATOR_LOW(WR)
|
||||||
|
|
||||||
auto readBusDataM1() {
|
|
||||||
_ActivateM1 m1(*this);
|
|
||||||
return BUS().DATA();
|
|
||||||
}
|
|
||||||
|
|
||||||
enum { BC_IDX, DE_IDX, HL_IDX };
|
enum { BC_IDX, DE_IDX, HL_IDX };
|
||||||
|
|
||||||
std::array<std::array<register16_t, 3>, 2> m_registers;
|
std::array<std::array<register16_t, 3>, 2> m_registers;
|
||||||
@@ -179,194 +168,39 @@ namespace EightBit {
|
|||||||
bool m_prefixFD = false;
|
bool m_prefixFD = false;
|
||||||
|
|
||||||
int8_t m_displacement = 0;
|
int8_t m_displacement = 0;
|
||||||
bool m_displaced = false;
|
|
||||||
|
|
||||||
void handleNMI();
|
void handleNMI();
|
||||||
|
|
||||||
|
void resetPrefixes();
|
||||||
|
|
||||||
|
[[nodiscard]] auto displaced() const { return m_prefixDD || m_prefixFD; }
|
||||||
[[nodiscard]] uint16_t displacedAddress();
|
[[nodiscard]] uint16_t displacedAddress();
|
||||||
void fetchDisplacement();
|
void fetchDisplacement();
|
||||||
[[nodiscard]] uint8_t fetchOpCode();
|
[[nodiscard]] uint8_t fetchOpCode();
|
||||||
|
|
||||||
|
uint8_t readBusDataM1();
|
||||||
|
|
||||||
typedef std::function<register16_t(void)> addresser_t;
|
typedef std::function<register16_t(void)> addresser_t;
|
||||||
|
void loadAccumulatorIndirect(addresser_t addresser);
|
||||||
|
void storeAccumulatorIndirect(addresser_t addresser);
|
||||||
|
|
||||||
typedef std::function<uint8_t(void)> reader_t;
|
typedef std::function<uint8_t(void)> reader_t;
|
||||||
|
void readInternalRegister(reader_t reader);
|
||||||
|
|
||||||
void loadAccumulatorIndirect(addresser_t addresser) {
|
[[nodiscard]] register16_t& HL2();
|
||||||
(MEMPTR() = BUS().ADDRESS() = addresser())++;
|
[[nodiscard]] register16_t& RP(int rp);
|
||||||
A() = memoryRead();
|
[[nodiscard]] register16_t& RP2(int rp);
|
||||||
}
|
|
||||||
|
|
||||||
void storeAccumulatorIndirect(addresser_t addresser) {
|
[[nodiscard]] uint8_t R(int r);
|
||||||
(MEMPTR() = BUS().ADDRESS() = addresser())++;
|
void R(int r, uint8_t value);
|
||||||
MEMPTR().high = BUS().DATA() = A();
|
void R2(int r, uint8_t value);
|
||||||
memoryWrite();
|
|
||||||
}
|
|
||||||
|
|
||||||
void readInternalRegister(reader_t reader) {
|
[[nodiscard]] static uint8_t adjustHalfCarryAdd(uint8_t f, uint8_t before, uint8_t value, int calculation);
|
||||||
F() = adjustSZXY<Z80>(F(), A() = reader());
|
[[nodiscard]] static uint8_t adjustHalfCarrySub(uint8_t f, uint8_t before, uint8_t value, int calculation);
|
||||||
F() = clearBit(F(), NF | HC);
|
[[nodiscard]] static uint8_t adjustOverflowAdd(uint8_t f, uint8_t before, uint8_t value, uint8_t calculation);
|
||||||
F() = setBit(F(), PF, IFF2());
|
[[nodiscard]] static uint8_t adjustOverflowAdd(uint8_t f, int beforeNegative, int valueNegative, int afterNegative);
|
||||||
tick();
|
[[nodiscard]] static uint8_t adjustOverflowSub(uint8_t f, uint8_t before, uint8_t value, uint8_t calculation);
|
||||||
}
|
[[nodiscard]] static uint8_t adjustOverflowSub(uint8_t f, int beforeNegative, int valueNegative, int afterNegative);
|
||||||
|
|
||||||
[[nodiscard]] auto& HL2() {
|
|
||||||
if (LIKELY(!m_displaced))
|
|
||||||
return HL();
|
|
||||||
if (m_prefixDD)
|
|
||||||
return IX();
|
|
||||||
// Must be FD prefix
|
|
||||||
return IY();
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] auto& RP(const int rp) {
|
|
||||||
ASSUME(rp >= 0);
|
|
||||||
ASSUME(rp <= 3);
|
|
||||||
switch (rp) {
|
|
||||||
case 0b00:
|
|
||||||
return BC();
|
|
||||||
case 0b01:
|
|
||||||
return DE();
|
|
||||||
case 0b10:
|
|
||||||
return HL2();
|
|
||||||
case 0b11:
|
|
||||||
return SP();
|
|
||||||
default:
|
|
||||||
UNREACHABLE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] auto& RP2(const int rp) {
|
|
||||||
ASSUME(rp >= 0);
|
|
||||||
ASSUME(rp <= 3);
|
|
||||||
switch (rp) {
|
|
||||||
case 0b00:
|
|
||||||
return BC();
|
|
||||||
case 0b01:
|
|
||||||
return DE();
|
|
||||||
case 0b10:
|
|
||||||
return HL2();
|
|
||||||
case 0b11:
|
|
||||||
return AF();
|
|
||||||
default:
|
|
||||||
UNREACHABLE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] auto R(const int r) {
|
|
||||||
ASSUME(r >= 0);
|
|
||||||
ASSUME(r <= 7);
|
|
||||||
switch (r) {
|
|
||||||
case 0:
|
|
||||||
return B();
|
|
||||||
case 1:
|
|
||||||
return C();
|
|
||||||
case 2:
|
|
||||||
return D();
|
|
||||||
case 3:
|
|
||||||
return E();
|
|
||||||
case 4:
|
|
||||||
return HL2().high;
|
|
||||||
case 5:
|
|
||||||
return HL2().low;
|
|
||||||
case 6:
|
|
||||||
return IntelProcessor::memoryRead(UNLIKELY(m_displaced) ? displacedAddress() : HL().word);
|
|
||||||
case 7:
|
|
||||||
return A();
|
|
||||||
default:
|
|
||||||
UNREACHABLE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void R(const int r, const uint8_t value) {
|
|
||||||
ASSUME(r >= 0);
|
|
||||||
ASSUME(r <= 7);
|
|
||||||
switch (r) {
|
|
||||||
case 0:
|
|
||||||
B() = value;
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
C() = value;
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
D() = value;
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
E() = value;
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
HL2().high = value;
|
|
||||||
break;
|
|
||||||
case 5:
|
|
||||||
HL2().low = value;
|
|
||||||
break;
|
|
||||||
case 6:
|
|
||||||
IntelProcessor::memoryWrite(UNLIKELY(m_displaced) ? displacedAddress() : HL().word, value);
|
|
||||||
break;
|
|
||||||
case 7:
|
|
||||||
A() = value;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
UNREACHABLE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void R2(const int r, const uint8_t value) {
|
|
||||||
ASSUME(r >= 0);
|
|
||||||
ASSUME(r <= 7);
|
|
||||||
switch (r) {
|
|
||||||
case 0:
|
|
||||||
B() = value;
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
C() = value;
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
D() = value;
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
E() = value;
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
H() = value;
|
|
||||||
break;
|
|
||||||
case 5:
|
|
||||||
L() = value;
|
|
||||||
break;
|
|
||||||
case 6:
|
|
||||||
IntelProcessor::memoryWrite(HL(), value);
|
|
||||||
break;
|
|
||||||
case 7:
|
|
||||||
A() = value;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
UNREACHABLE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] static auto adjustHalfCarryAdd(uint8_t f, const uint8_t before, const uint8_t value, const int calculation) {
|
|
||||||
return setBit(f, HC, calculateHalfCarryAdd(before, value, calculation));
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] static auto adjustHalfCarrySub(uint8_t f, const uint8_t before, const uint8_t value, const int calculation) {
|
|
||||||
return setBit(f, HC, calculateHalfCarrySub(before, value, calculation));
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] static uint8_t adjustOverflowAdd(uint8_t f, const uint8_t before, const uint8_t value, const uint8_t calculation) {
|
|
||||||
return adjustOverflowAdd(f, before & SF, value & SF, calculation & SF);
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] static uint8_t adjustOverflowAdd(uint8_t f, const int beforeNegative, const int valueNegative, const int afterNegative) {
|
|
||||||
const auto overflow = (beforeNegative == valueNegative) && (beforeNegative != afterNegative);
|
|
||||||
return setBit(f, VF, overflow);
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] static uint8_t adjustOverflowSub(uint8_t f, const uint8_t before, const uint8_t value, const uint8_t calculation) {
|
|
||||||
return adjustOverflowSub(f, before & SF, value & SF, calculation & SF);
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] static uint8_t adjustOverflowSub(uint8_t f, const int beforeNegative, const int valueNegative, const int afterNegative) {
|
|
||||||
const auto overflow = (beforeNegative != valueNegative) && (beforeNegative != afterNegative);
|
|
||||||
return setBit(f, VF, overflow);
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] static bool convertCondition(uint8_t f, int flag);
|
[[nodiscard]] static bool convertCondition(uint8_t f, int flag);
|
||||||
|
|
||||||
@@ -420,41 +254,41 @@ namespace EightBit {
|
|||||||
|
|
||||||
static void scf(uint8_t& f, uint8_t operand);
|
static void scf(uint8_t& f, uint8_t operand);
|
||||||
static void ccf(uint8_t& f, uint8_t operand);
|
static void ccf(uint8_t& f, uint8_t operand);
|
||||||
static uint8_t cpl(uint8_t& f, uint8_t operand);
|
[[nodiscard]] static uint8_t cpl(uint8_t& f, uint8_t operand);
|
||||||
|
|
||||||
void xhtl(register16_t& exchange);
|
void xhtl(register16_t& exchange);
|
||||||
|
|
||||||
void blockCompare(uint8_t& f, uint8_t value, register16_t source, register16_t& counter);
|
void blockCompare(uint8_t& f, uint8_t value, register16_t source, register16_t& counter);
|
||||||
|
|
||||||
void cpi(uint8_t& f, uint8_t value);
|
void cpi(uint8_t& f, uint8_t value);
|
||||||
bool cpir(uint8_t& f, uint8_t value);
|
[[nodiscard]] bool cpir(uint8_t& f, uint8_t value);
|
||||||
|
|
||||||
void cpd(uint8_t& f, uint8_t value);
|
void cpd(uint8_t& f, uint8_t value);
|
||||||
bool cpdr(uint8_t& f, uint8_t value);
|
[[nodiscard]] bool cpdr(uint8_t& f, uint8_t value);
|
||||||
|
|
||||||
void blockLoad(uint8_t& f, uint8_t a, register16_t source, register16_t destination, register16_t& counter);
|
void blockLoad(uint8_t& f, uint8_t a, register16_t source, register16_t destination, register16_t& counter);
|
||||||
|
|
||||||
void ldi(uint8_t& f, uint8_t a);
|
void ldi(uint8_t& f, uint8_t a);
|
||||||
bool ldir(uint8_t& f, uint8_t a);
|
[[nodiscard]] bool ldir(uint8_t& f, uint8_t a);
|
||||||
|
|
||||||
void ldd(uint8_t& f, uint8_t a);
|
void ldd(uint8_t& f, uint8_t a);
|
||||||
bool lddr(uint8_t& f, uint8_t a);
|
[[nodiscard]] bool lddr(uint8_t& f, uint8_t a);
|
||||||
|
|
||||||
void blockIn(register16_t& source, register16_t destination);
|
void blockIn(register16_t& source, register16_t destination);
|
||||||
|
|
||||||
void ini();
|
void ini();
|
||||||
bool inir();
|
[[nodiscard]] bool inir();
|
||||||
|
|
||||||
void ind();
|
void ind();
|
||||||
bool indr();
|
[[nodiscard]] bool indr();
|
||||||
|
|
||||||
void blockOut(register16_t source, register16_t& destination);
|
void blockOut(register16_t source, register16_t& destination);
|
||||||
|
|
||||||
void outi();
|
void outi();
|
||||||
bool otir();
|
[[nodiscard]] bool otir();
|
||||||
|
|
||||||
void outd();
|
void outd();
|
||||||
bool otdr();
|
[[nodiscard]] bool otdr();
|
||||||
|
|
||||||
[[nodiscard]] uint8_t neg(uint8_t& f, uint8_t operand);
|
[[nodiscard]] uint8_t neg(uint8_t& f, uint8_t operand);
|
||||||
|
|
||||||
|
229
Z80/src/Z80.cpp
229
Z80/src/Z80.cpp
@@ -25,7 +25,7 @@ EightBit::Z80::Z80(Bus& bus)
|
|||||||
|
|
||||||
AF() = IX() = IY() = BC() = DE() = HL() = Mask16;
|
AF() = IX() = IY() = BC() = DE() = HL() = Mask16;
|
||||||
|
|
||||||
m_prefixCB = m_prefixDD = m_prefixED = m_prefixFD = false;
|
resetPrefixes();
|
||||||
});
|
});
|
||||||
|
|
||||||
RaisedM1.connect([this](EventArgs) {
|
RaisedM1.connect([this](EventArgs) {
|
||||||
@@ -186,9 +186,33 @@ uint8_t EightBit::Z80::decrement(uint8_t& f, const uint8_t operand) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint8_t EightBit::Z80::adjustHalfCarryAdd(const uint8_t f, const uint8_t before, const uint8_t value, const int calculation) {
|
||||||
|
return setBit(f, HC, calculateHalfCarryAdd(before, value, calculation));
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t EightBit::Z80::adjustHalfCarrySub(const uint8_t f, const uint8_t before, const uint8_t value, const int calculation) {
|
||||||
|
return setBit(f, HC, calculateHalfCarrySub(before, value, calculation));
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t EightBit::Z80::adjustOverflowAdd(const uint8_t f, const uint8_t before, const uint8_t value, const uint8_t calculation) {
|
||||||
|
return adjustOverflowAdd(f, before & SF, value & SF, calculation & SF);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t EightBit::Z80::adjustOverflowAdd(const uint8_t f, const int beforeNegative, const int valueNegative, const int afterNegative) {
|
||||||
|
const auto overflow = (beforeNegative == valueNegative) && (beforeNegative != afterNegative);
|
||||||
|
return setBit(f, VF, overflow);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t EightBit::Z80::adjustOverflowSub(const uint8_t f, const uint8_t before, const uint8_t value, const uint8_t calculation) {
|
||||||
|
return adjustOverflowSub(f, before & SF, value & SF, calculation & SF);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t EightBit::Z80::adjustOverflowSub(const uint8_t f, const int beforeNegative, const int valueNegative, const int afterNegative) {
|
||||||
|
const auto overflow = (beforeNegative != valueNegative) && (beforeNegative != afterNegative);
|
||||||
|
return setBit(f, VF, overflow);
|
||||||
|
}
|
||||||
|
|
||||||
bool EightBit::Z80::convertCondition(const uint8_t f, int flag) {
|
bool EightBit::Z80::convertCondition(const uint8_t f, int flag) {
|
||||||
ASSUME(flag >= 0);
|
|
||||||
ASSUME(flag <= 7);
|
|
||||||
switch (flag) {
|
switch (flag) {
|
||||||
case 0:
|
case 0:
|
||||||
return !(f & ZF);
|
return !(f & ZF);
|
||||||
@@ -447,8 +471,6 @@ uint8_t EightBit::Z80::srl(uint8_t& f, const uint8_t operand) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void EightBit::Z80::bit(uint8_t& f, const int n, const uint8_t operand) {
|
void EightBit::Z80::bit(uint8_t& f, const int n, const uint8_t operand) {
|
||||||
ASSUME(n >= 0);
|
|
||||||
ASSUME(n <= 7);
|
|
||||||
f = setBit(f, HC);
|
f = setBit(f, HC);
|
||||||
f = clearBit(f, NF);
|
f = clearBit(f, NF);
|
||||||
const auto discarded = operand & Chip::bit(n);
|
const auto discarded = operand & Chip::bit(n);
|
||||||
@@ -457,14 +479,10 @@ void EightBit::Z80::bit(uint8_t& f, const int n, const uint8_t operand) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
uint8_t EightBit::Z80::res(const int n, const uint8_t operand) {
|
uint8_t EightBit::Z80::res(const int n, const uint8_t operand) {
|
||||||
ASSUME(n >= 0);
|
|
||||||
ASSUME(n <= 7);
|
|
||||||
return clearBit(operand, Chip::bit(n));
|
return clearBit(operand, Chip::bit(n));
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t EightBit::Z80::set(const int n, const uint8_t operand) {
|
uint8_t EightBit::Z80::set(const int n, const uint8_t operand) {
|
||||||
ASSUME(n >= 0);
|
|
||||||
ASSUME(n <= 7);
|
|
||||||
return setBit(operand, Chip::bit(n));
|
return setBit(operand, Chip::bit(n));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -584,7 +602,7 @@ bool EightBit::Z80::cpdr(uint8_t& f, uint8_t value) {
|
|||||||
|
|
||||||
void EightBit::Z80::blockLoad(uint8_t& f, const uint8_t a, const register16_t source, const register16_t destination, register16_t& counter) {
|
void EightBit::Z80::blockLoad(uint8_t& f, const uint8_t a, const register16_t source, const register16_t destination, register16_t& counter) {
|
||||||
const auto value = IntelProcessor::memoryRead(source);
|
const auto value = IntelProcessor::memoryRead(source);
|
||||||
IntelProcessor::memoryWrite(destination, value);
|
IntelProcessor::memoryWrite(destination);
|
||||||
const auto xy = a + value;
|
const auto xy = a + value;
|
||||||
f = setBit(f, XF, xy & Bit3);
|
f = setBit(f, XF, xy & Bit3);
|
||||||
f = setBit(f, YF, xy & Bit1);
|
f = setBit(f, YF, xy & Bit1);
|
||||||
@@ -748,8 +766,13 @@ uint8_t EightBit::Z80::portRead() {
|
|||||||
|
|
||||||
//
|
//
|
||||||
|
|
||||||
|
void EightBit::Z80::resetPrefixes() {
|
||||||
|
m_prefixCB = m_prefixDD = m_prefixED = m_prefixFD = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
uint16_t EightBit::Z80::displacedAddress() {
|
uint16_t EightBit::Z80::displacedAddress() {
|
||||||
assert(m_displaced);
|
|
||||||
return MEMPTR().word = (m_prefixDD ? IX() : IY()).word + m_displacement;
|
return MEMPTR().word = (m_prefixDD ? IX() : IY()).word + m_displacement;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -757,6 +780,13 @@ void EightBit::Z80::fetchDisplacement() {
|
|||||||
m_displacement = fetchByte();
|
m_displacement = fetchByte();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
uint8_t EightBit::Z80::readBusDataM1() {
|
||||||
|
_ActivateM1 m1(*this);
|
||||||
|
return BUS().DATA();
|
||||||
|
}
|
||||||
|
|
||||||
// ** From the Z80 CPU User Manual
|
// ** From the Z80 CPU User Manual
|
||||||
|
|
||||||
// Figure 5 depicts the timing during an M1 (op code fetch) cycle. The Program Counter is
|
// Figure 5 depicts the timing during an M1 (op code fetch) cycle. The Program Counter is
|
||||||
@@ -803,11 +833,152 @@ uint8_t EightBit::Z80::fetchOpCode() {
|
|||||||
return returned;
|
return returned;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EightBit::Z80::loadAccumulatorIndirect(addresser_t addresser) {
|
||||||
|
(MEMPTR() = BUS().ADDRESS() = addresser())++;
|
||||||
|
A() = memoryRead();
|
||||||
|
}
|
||||||
|
|
||||||
|
void EightBit::Z80::storeAccumulatorIndirect(addresser_t addresser) {
|
||||||
|
(MEMPTR() = BUS().ADDRESS() = addresser())++;
|
||||||
|
MEMPTR().high = BUS().DATA() = A();
|
||||||
|
memoryWrite();
|
||||||
|
}
|
||||||
|
|
||||||
|
void EightBit::Z80::readInternalRegister(reader_t reader) {
|
||||||
|
F() = adjustSZXY<Z80>(F(), A() = reader());
|
||||||
|
F() = clearBit(F(), NF | HC);
|
||||||
|
F() = setBit(F(), PF, IFF2());
|
||||||
|
tick();
|
||||||
|
}
|
||||||
|
|
||||||
|
EightBit::register16_t& EightBit::Z80::HL2() {
|
||||||
|
if (UNLIKELY(m_prefixDD))
|
||||||
|
return IX();
|
||||||
|
if (UNLIKELY(m_prefixFD))
|
||||||
|
return IY();
|
||||||
|
return HL();
|
||||||
|
}
|
||||||
|
|
||||||
|
EightBit::register16_t& EightBit::Z80::RP(const int rp) {
|
||||||
|
switch (rp) {
|
||||||
|
case 0b00:
|
||||||
|
return BC();
|
||||||
|
case 0b01:
|
||||||
|
return DE();
|
||||||
|
case 0b10:
|
||||||
|
return HL2();
|
||||||
|
case 0b11:
|
||||||
|
return SP();
|
||||||
|
default:
|
||||||
|
UNREACHABLE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EightBit::register16_t& EightBit::Z80::RP2(const int rp) {
|
||||||
|
switch (rp) {
|
||||||
|
case 0b00:
|
||||||
|
return BC();
|
||||||
|
case 0b01:
|
||||||
|
return DE();
|
||||||
|
case 0b10:
|
||||||
|
return HL2();
|
||||||
|
case 0b11:
|
||||||
|
return AF();
|
||||||
|
default:
|
||||||
|
UNREACHABLE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t EightBit::Z80::R(const int r) {
|
||||||
|
switch (r) {
|
||||||
|
case 0:
|
||||||
|
return B();
|
||||||
|
case 1:
|
||||||
|
return C();
|
||||||
|
case 2:
|
||||||
|
return D();
|
||||||
|
case 3:
|
||||||
|
return E();
|
||||||
|
case 4:
|
||||||
|
return HL2().high;
|
||||||
|
case 5:
|
||||||
|
return HL2().low;
|
||||||
|
case 6:
|
||||||
|
return IntelProcessor::memoryRead(UNLIKELY(displaced()) ? displacedAddress() : HL().word);
|
||||||
|
case 7:
|
||||||
|
return A();
|
||||||
|
default:
|
||||||
|
UNREACHABLE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EightBit::Z80::R(const int r, const uint8_t value) {
|
||||||
|
switch (r) {
|
||||||
|
case 0:
|
||||||
|
B() = value;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
C() = value;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
D() = value;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
E() = value;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
HL2().high = value;
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
HL2().low = value;
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
IntelProcessor::memoryWrite(UNLIKELY(displaced()) ? displacedAddress() : HL().word, value);
|
||||||
|
break;
|
||||||
|
case 7:
|
||||||
|
A() = value;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
UNREACHABLE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EightBit::Z80::R2(const int r, const uint8_t value) {
|
||||||
|
switch (r) {
|
||||||
|
case 0:
|
||||||
|
B() = value;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
C() = value;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
D() = value;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
E() = value;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
H() = value;
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
L() = value;
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
IntelProcessor::memoryWrite(HL(), value);
|
||||||
|
break;
|
||||||
|
case 7:
|
||||||
|
A() = value;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
UNREACHABLE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int EightBit::Z80::step() {
|
int EightBit::Z80::step() {
|
||||||
resetCycles();
|
resetCycles();
|
||||||
ExecutingInstruction.fire(*this);
|
ExecutingInstruction.fire(*this);
|
||||||
if (LIKELY(powered())) {
|
if (LIKELY(powered())) {
|
||||||
m_displaced = m_prefixCB = m_prefixDD = m_prefixED = m_prefixFD = false;
|
resetPrefixes();
|
||||||
bool handled = false;
|
bool handled = false;
|
||||||
if (lowered(RESET())) {
|
if (lowered(RESET())) {
|
||||||
handleRESET();
|
handleRESET();
|
||||||
@@ -855,10 +1026,10 @@ int EightBit::Z80::execute() {
|
|||||||
void EightBit::Z80::executeCB(const int x, const int y, const int z) {
|
void EightBit::Z80::executeCB(const int x, const int y, const int z) {
|
||||||
|
|
||||||
const bool memoryZ = z == 6;
|
const bool memoryZ = z == 6;
|
||||||
const bool indirect = (!m_displaced && memoryZ) || m_displaced;
|
const bool indirect = (!displaced() && memoryZ) || displaced();
|
||||||
|
|
||||||
uint8_t operand;
|
uint8_t operand;
|
||||||
if (m_displaced) {
|
if (displaced()) {
|
||||||
tick(2);
|
tick(2);
|
||||||
operand = IntelProcessor::memoryRead(displacedAddress());
|
operand = IntelProcessor::memoryRead(displacedAddress());
|
||||||
} else {
|
} else {
|
||||||
@@ -915,7 +1086,7 @@ void EightBit::Z80::executeCB(const int x, const int y, const int z) {
|
|||||||
}
|
}
|
||||||
if (update) {
|
if (update) {
|
||||||
tick();
|
tick();
|
||||||
if (m_displaced) {
|
if (displaced()) {
|
||||||
IntelProcessor::memoryWrite(operand);
|
IntelProcessor::memoryWrite(operand);
|
||||||
if (!memoryZ)
|
if (!memoryZ)
|
||||||
R2(z, operand);
|
R2(z, operand);
|
||||||
@@ -1240,7 +1411,7 @@ void EightBit::Z80::executeOther(const int x, const int y, const int z, const in
|
|||||||
tick(2);
|
tick(2);
|
||||||
break;
|
break;
|
||||||
case 4: { // 8-bit INC
|
case 4: { // 8-bit INC
|
||||||
if (memoryY && m_displaced) {
|
if (memoryY && displaced()) {
|
||||||
fetchDisplacement();
|
fetchDisplacement();
|
||||||
tick(5);
|
tick(5);
|
||||||
}
|
}
|
||||||
@@ -1251,7 +1422,7 @@ void EightBit::Z80::executeOther(const int x, const int y, const int z, const in
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 5: { // 8-bit DEC
|
case 5: { // 8-bit DEC
|
||||||
if (memoryY && m_displaced) {
|
if (memoryY && displaced()) {
|
||||||
fetchDisplacement();
|
fetchDisplacement();
|
||||||
tick(5);
|
tick(5);
|
||||||
}
|
}
|
||||||
@@ -1262,10 +1433,10 @@ void EightBit::Z80::executeOther(const int x, const int y, const int z, const in
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 6: { // 8-bit load immediate
|
case 6: { // 8-bit load immediate
|
||||||
if (memoryY && m_displaced)
|
if (memoryY && displaced())
|
||||||
fetchDisplacement();
|
fetchDisplacement();
|
||||||
const auto value = fetchByte();
|
const auto value = fetchByte();
|
||||||
if (m_displaced)
|
if (displaced())
|
||||||
tick(2);
|
tick(2);
|
||||||
R(y, value); // LD r,n
|
R(y, value); // LD r,n
|
||||||
break;
|
break;
|
||||||
@@ -1309,19 +1480,19 @@ void EightBit::Z80::executeOther(const int x, const int y, const int z, const in
|
|||||||
lowerHALT();
|
lowerHALT();
|
||||||
} else {
|
} else {
|
||||||
bool normal = true;
|
bool normal = true;
|
||||||
if (m_displaced) {
|
if (displaced()) {
|
||||||
if (memoryZ || memoryY)
|
if (memoryZ || memoryY)
|
||||||
fetchDisplacement();
|
fetchDisplacement();
|
||||||
if (memoryZ) {
|
if (memoryZ) {
|
||||||
switch (y) {
|
switch (y) {
|
||||||
case 4:
|
case 4:
|
||||||
if (m_displaced)
|
if (displaced())
|
||||||
tick(5);
|
tick(5);
|
||||||
H() = R(z);
|
H() = R(z);
|
||||||
normal = false;
|
normal = false;
|
||||||
break;
|
break;
|
||||||
case 5:
|
case 5:
|
||||||
if (m_displaced)
|
if (displaced())
|
||||||
tick(5);
|
tick(5);
|
||||||
L() = R(z);
|
L() = R(z);
|
||||||
normal = false;
|
normal = false;
|
||||||
@@ -1331,13 +1502,13 @@ void EightBit::Z80::executeOther(const int x, const int y, const int z, const in
|
|||||||
if (memoryY) {
|
if (memoryY) {
|
||||||
switch (z) {
|
switch (z) {
|
||||||
case 4:
|
case 4:
|
||||||
if (m_displaced)
|
if (displaced())
|
||||||
tick(5);
|
tick(5);
|
||||||
R(y, H());
|
R(y, H());
|
||||||
normal = false;
|
normal = false;
|
||||||
break;
|
break;
|
||||||
case 5:
|
case 5:
|
||||||
if (m_displaced)
|
if (displaced())
|
||||||
tick(5);
|
tick(5);
|
||||||
R(y, L());
|
R(y, L());
|
||||||
normal = false;
|
normal = false;
|
||||||
@@ -1346,14 +1517,14 @@ void EightBit::Z80::executeOther(const int x, const int y, const int z, const in
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (normal) {
|
if (normal) {
|
||||||
if (m_displaced)
|
if (displaced())
|
||||||
tick(5);
|
tick(5);
|
||||||
R(y, R(z));
|
R(y, R(z));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 2: { // Operate on accumulator and register/memory location
|
case 2: { // Operate on accumulator and register/memory location
|
||||||
if (memoryZ && m_displaced) {
|
if (memoryZ && displaced()) {
|
||||||
fetchDisplacement();
|
fetchDisplacement();
|
||||||
tick(5);
|
tick(5);
|
||||||
}
|
}
|
||||||
@@ -1431,7 +1602,7 @@ void EightBit::Z80::executeOther(const int x, const int y, const int z, const in
|
|||||||
break;
|
break;
|
||||||
case 1: // CB prefix
|
case 1: // CB prefix
|
||||||
m_prefixCB = true;
|
m_prefixCB = true;
|
||||||
if (m_displaced) {
|
if (displaced()) {
|
||||||
fetchDisplacement();
|
fetchDisplacement();
|
||||||
IntelProcessor::execute(fetchByte());
|
IntelProcessor::execute(fetchByte());
|
||||||
} else {
|
} else {
|
||||||
@@ -1474,7 +1645,7 @@ void EightBit::Z80::executeOther(const int x, const int y, const int z, const in
|
|||||||
call(MEMPTR() = fetchWord());
|
call(MEMPTR() = fetchWord());
|
||||||
break;
|
break;
|
||||||
case 1: // DD prefix
|
case 1: // DD prefix
|
||||||
m_displaced = m_prefixDD = true;
|
m_prefixDD = true;
|
||||||
IntelProcessor::execute(fetchOpCode());
|
IntelProcessor::execute(fetchOpCode());
|
||||||
break;
|
break;
|
||||||
case 2: // ED prefix
|
case 2: // ED prefix
|
||||||
@@ -1482,7 +1653,7 @@ void EightBit::Z80::executeOther(const int x, const int y, const int z, const in
|
|||||||
IntelProcessor::execute(fetchOpCode());
|
IntelProcessor::execute(fetchOpCode());
|
||||||
break;
|
break;
|
||||||
case 3: // FD prefix
|
case 3: // FD prefix
|
||||||
m_displaced = m_prefixFD = true;
|
m_prefixFD = true;
|
||||||
IntelProcessor::execute(fetchOpCode());
|
IntelProcessor::execute(fetchOpCode());
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@@ -11,12 +11,13 @@ namespace EightBit {
|
|||||||
|
|
||||||
Signal<EventArgs> Ticked;
|
Signal<EventArgs> Ticked;
|
||||||
|
|
||||||
void tick(const int extra) { for (int i = 0; i < extra; ++i) tick(); }
|
|
||||||
void tick() { ++m_cycles; Ticked.fire(); }
|
|
||||||
[[nodiscard]] auto cycles() const noexcept { return m_cycles; }
|
[[nodiscard]] auto cycles() const noexcept { return m_cycles; }
|
||||||
|
|
||||||
|
void tick(int extra);
|
||||||
|
void tick();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void resetCycles() noexcept { m_cycles = 0; }
|
void resetCycles() noexcept;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int m_cycles = 0;
|
int m_cycles = 0;
|
||||||
|
@@ -40,6 +40,7 @@ namespace EightBit {
|
|||||||
virtual void handleINT();
|
virtual void handleINT();
|
||||||
|
|
||||||
void memoryWrite(register16_t address, uint8_t data);
|
void memoryWrite(register16_t address, uint8_t data);
|
||||||
|
void memoryWrite(register16_t address);
|
||||||
void memoryWrite(uint8_t data);
|
void memoryWrite(uint8_t data);
|
||||||
virtual void memoryWrite();
|
virtual void memoryWrite();
|
||||||
virtual void busWrite();
|
virtual void busWrite();
|
||||||
@@ -48,17 +49,10 @@ namespace EightBit {
|
|||||||
virtual uint8_t memoryRead();
|
virtual uint8_t memoryRead();
|
||||||
virtual uint8_t busRead();
|
virtual uint8_t busRead();
|
||||||
|
|
||||||
auto getBytePaged(const uint8_t page, const uint8_t offset) {
|
uint8_t getBytePaged(uint8_t page, uint8_t offset);
|
||||||
return memoryRead(register16_t(offset, page));
|
void setBytePaged(uint8_t page, uint8_t offset, uint8_t value);
|
||||||
}
|
|
||||||
|
|
||||||
void setBytePaged(const uint8_t page, const uint8_t offset, const uint8_t value) {
|
uint8_t fetchByte();
|
||||||
memoryWrite(register16_t(offset, page), value);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto fetchByte() {
|
|
||||||
return memoryRead(PC()++);
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] virtual register16_t getWord() = 0;
|
[[nodiscard]] virtual register16_t getWord() = 0;
|
||||||
virtual void setWord(register16_t value) = 0;
|
virtual void setWord(register16_t value) = 0;
|
||||||
@@ -74,20 +68,10 @@ namespace EightBit {
|
|||||||
virtual void pushWord(register16_t value) = 0;
|
virtual void pushWord(register16_t value) = 0;
|
||||||
[[nodiscard]] virtual register16_t popWord() = 0;
|
[[nodiscard]] virtual register16_t popWord() = 0;
|
||||||
|
|
||||||
[[nodiscard]] auto getWord(const register16_t address) {
|
register16_t getWord(register16_t address);
|
||||||
BUS().ADDRESS() = address;
|
void setWord(register16_t address, register16_t value);
|
||||||
return getWord();
|
|
||||||
}
|
|
||||||
|
|
||||||
void setWord(const register16_t address, const register16_t value) {
|
|
||||||
BUS().ADDRESS() = address;
|
|
||||||
setWord(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
void jump(const register16_t destination) noexcept {
|
|
||||||
PC() = destination;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
void jump(const register16_t destination) noexcept;
|
||||||
virtual void call(register16_t destination);
|
virtual void call(register16_t destination);
|
||||||
virtual void ret();
|
virtual void ret();
|
||||||
|
|
||||||
|
16
src/ClockedChip.cpp
Normal file
16
src/ClockedChip.cpp
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
#include "stdafx.h"
|
||||||
|
#include "ClockedChip.h"
|
||||||
|
|
||||||
|
void EightBit::ClockedChip::tick(const int extra) {
|
||||||
|
for (int i = 0; i < extra; ++i)
|
||||||
|
tick();
|
||||||
|
}
|
||||||
|
|
||||||
|
void EightBit::ClockedChip::tick() {
|
||||||
|
++m_cycles;
|
||||||
|
Ticked.fire();
|
||||||
|
}
|
||||||
|
|
||||||
|
void EightBit::ClockedChip::resetCycles() noexcept {
|
||||||
|
m_cycles = 0;
|
||||||
|
}
|
@@ -178,6 +178,7 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="BigEndianProcessor.cpp" />
|
<ClCompile Include="BigEndianProcessor.cpp" />
|
||||||
<ClCompile Include="Bus.cpp" />
|
<ClCompile Include="Bus.cpp" />
|
||||||
|
<ClCompile Include="ClockedChip.cpp" />
|
||||||
<ClCompile Include="Device.cpp" />
|
<ClCompile Include="Device.cpp" />
|
||||||
<ClCompile Include="EventArgs.cpp" />
|
<ClCompile Include="EventArgs.cpp" />
|
||||||
<ClCompile Include="InputOutput.cpp" />
|
<ClCompile Include="InputOutput.cpp" />
|
||||||
|
@@ -121,5 +121,8 @@
|
|||||||
<ClCompile Include="IntelHexFile.cpp">
|
<ClCompile Include="IntelHexFile.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="ClockedChip.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
@@ -2,7 +2,7 @@ LIB = libeightbit.a
|
|||||||
|
|
||||||
CXXFLAGS = -I ../inc
|
CXXFLAGS = -I ../inc
|
||||||
|
|
||||||
CXXFILES = BigEndianProcessor.cpp Bus.cpp Device.cpp EventArgs.cpp InputOutput.cpp IntelHexFile.cpp IntelProcessor.cpp LittleEndianProcessor.cpp Memory.cpp Processor.cpp Ram.cpp Rom.cpp UnusedMemory.cpp
|
CXXFILES = BigEndianProcessor.cpp Bus.cpp ClockedChip.cpp Device.cpp EventArgs.cpp InputOutput.cpp IntelHexFile.cpp IntelProcessor.cpp LittleEndianProcessor.cpp Memory.cpp Processor.cpp Ram.cpp Rom.cpp UnusedMemory.cpp
|
||||||
|
|
||||||
include ../compile.mk
|
include ../compile.mk
|
||||||
include ../lib_build.mk
|
include ../lib_build.mk
|
||||||
|
@@ -21,6 +21,11 @@ void EightBit::Processor::memoryWrite(const register16_t address, const uint8_t
|
|||||||
memoryWrite(data);
|
memoryWrite(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EightBit::Processor::memoryWrite(const register16_t address) {
|
||||||
|
BUS().ADDRESS() = address;
|
||||||
|
memoryWrite();
|
||||||
|
}
|
||||||
|
|
||||||
void EightBit::Processor::memoryWrite(const uint8_t data) {
|
void EightBit::Processor::memoryWrite(const uint8_t data) {
|
||||||
BUS().DATA() = data;
|
BUS().DATA() = data;
|
||||||
memoryWrite();
|
memoryWrite();
|
||||||
@@ -47,6 +52,28 @@ uint8_t EightBit::Processor::busRead() {
|
|||||||
return BUS().read();
|
return BUS().read();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint8_t EightBit::Processor::getBytePaged(const uint8_t page, const uint8_t offset) {
|
||||||
|
return memoryRead(register16_t(offset, page));
|
||||||
|
}
|
||||||
|
|
||||||
|
void EightBit::Processor::setBytePaged(const uint8_t page, const uint8_t offset, const uint8_t value) {
|
||||||
|
memoryWrite(register16_t(offset, page), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t EightBit::Processor::fetchByte() {
|
||||||
|
return memoryRead(PC()++);
|
||||||
|
}
|
||||||
|
|
||||||
|
EightBit::register16_t EightBit::Processor::getWord(const register16_t address) {
|
||||||
|
BUS().ADDRESS() = address;
|
||||||
|
return getWord();
|
||||||
|
}
|
||||||
|
|
||||||
|
void EightBit::Processor::setWord(const register16_t address, const register16_t value) {
|
||||||
|
BUS().ADDRESS() = address;
|
||||||
|
setWord(value);
|
||||||
|
}
|
||||||
|
|
||||||
int EightBit::Processor::run(const int limit) {
|
int EightBit::Processor::run(const int limit) {
|
||||||
int current = 0;
|
int current = 0;
|
||||||
while (LIKELY(powered() && (current < limit)))
|
while (LIKELY(powered() && (current < limit)))
|
||||||
@@ -67,6 +94,10 @@ int8_t EightBit::Processor::signExtend(const int b, uint8_t x) noexcept {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EightBit::Processor::jump(const register16_t destination) noexcept {
|
||||||
|
PC() = destination;
|
||||||
|
}
|
||||||
|
|
||||||
void EightBit::Processor::call(const register16_t destination) {
|
void EightBit::Processor::call(const register16_t destination) {
|
||||||
pushWord(PC());
|
pushWord(PC());
|
||||||
jump(destination);
|
jump(destination);
|
||||||
|
Reference in New Issue
Block a user