mirror of
https://github.com/MoleskiCoder/EightBit.git
synced 2026-03-15 16:16:36 +00:00
Moving towards Z80 cycle accuracy
This commit is contained in:
@@ -168,9 +168,6 @@
|
||||
<ClCompile Include="tests.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\M6502\src\M6502.vcxproj">
|
||||
<Project>{d8726a1b-bbfe-47ef-9860-26b90140ba66}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\src\EightBit.vcxproj">
|
||||
<Project>{a9c24bd9-0cb4-4c84-b09b-46b815f9da47}</Project>
|
||||
</ProjectReference>
|
||||
|
||||
@@ -135,16 +135,14 @@ namespace EightBit {
|
||||
void handleRESET() noexcept final;
|
||||
void handleINT() noexcept final;
|
||||
|
||||
void pushWord(register16_t destination) noexcept final;
|
||||
|
||||
void memoryUpdate(int ticks) noexcept;
|
||||
void memoryWrite() noexcept final;
|
||||
uint8_t memoryRead() noexcept final;
|
||||
void refreshMemory() noexcept;
|
||||
|
||||
void busWrite() noexcept final;
|
||||
uint8_t busRead() noexcept final;
|
||||
void jumpRelative(int8_t offset) noexcept final;
|
||||
|
||||
void jr(int8_t offset) noexcept final;
|
||||
int jrConditional(int condition) noexcept final;
|
||||
void call(register16_t destination) override;
|
||||
|
||||
private:
|
||||
bool m_interruptPending = false;
|
||||
@@ -189,17 +187,17 @@ namespace EightBit {
|
||||
|
||||
[[nodiscard]] constexpr auto displaced() const noexcept { return m_prefixDD || m_prefixFD; }
|
||||
|
||||
[[nodiscard]] constexpr const register16_t& displacedAddress() noexcept {
|
||||
[[nodiscard]] constexpr void displaceAddress() noexcept {
|
||||
const auto& index_register = m_prefixDD ? IX() : IY();
|
||||
const auto address = index_register.word + m_displacement;
|
||||
MEMPTR().word = address;
|
||||
return MEMPTR();
|
||||
BUS().ADDRESS() = MEMPTR();
|
||||
}
|
||||
|
||||
void fetchDisplacement() noexcept;
|
||||
[[nodiscard]] uint8_t fetchInstruction() noexcept;
|
||||
[[nodiscard]] uint8_t fetchInstruction() noexcept final;
|
||||
|
||||
uint8_t readBusDataM1() noexcept;
|
||||
uint8_t readDataUnderInterrupt();
|
||||
|
||||
typedef std::function<register16_t(void)> addresser_t;
|
||||
void loadAccumulatorIndirect(addresser_t addresser) noexcept;
|
||||
@@ -212,8 +210,8 @@ namespace EightBit {
|
||||
[[nodiscard]] register16_t& RP(int rp) noexcept;
|
||||
[[nodiscard]] register16_t& RP2(int rp) noexcept;
|
||||
|
||||
[[nodiscard]] uint8_t R(int r) noexcept;
|
||||
void R(int r, uint8_t value) noexcept;
|
||||
[[nodiscard]] uint8_t& R(int r, MemoryMapping::AccessLevel access = MemoryMapping::AccessLevel::ReadOnly) noexcept;
|
||||
void R(int r, uint8_t value, int ticks = 1) noexcept;
|
||||
void R2(int r, uint8_t value) noexcept;
|
||||
|
||||
[[nodiscard]] static constexpr auto zeroTest(uint8_t data) noexcept { return data & ZF; }
|
||||
@@ -341,7 +339,7 @@ namespace EightBit {
|
||||
return adjustOverflowSub(input, signTest(before), signTest(value), signTest(calculation));
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr bool convertCondition(int flag) noexcept {
|
||||
[[nodiscard]] bool convertCondition(int flag) noexcept override {
|
||||
switch (flag) {
|
||||
case 0:
|
||||
return zero() == 0;
|
||||
@@ -407,10 +405,7 @@ namespace EightBit {
|
||||
void retn() noexcept;
|
||||
void reti() noexcept;
|
||||
|
||||
void returnConditionalFlag(int flag) noexcept;
|
||||
void jrConditionalFlag(int flag) noexcept;
|
||||
void callConditionalFlag(int flag) noexcept;
|
||||
void jumpConditionalFlag(int flag) noexcept;
|
||||
void returnConditionalFlag(int flag) noexcept final;
|
||||
|
||||
[[nodiscard]] register16_t sbc(register16_t operand, register16_t value) noexcept;
|
||||
[[nodiscard]] register16_t adc(register16_t operand, register16_t value) noexcept;
|
||||
@@ -583,9 +578,10 @@ namespace EightBit {
|
||||
adjustXY(((Q() ^ F()) | A()));
|
||||
}
|
||||
|
||||
void cpl() noexcept {
|
||||
void cpl() noexcept final {
|
||||
setBit(HC | NF);
|
||||
adjustXY(A() = ~A());
|
||||
IntelProcessor::cpl();
|
||||
adjustXY(A());
|
||||
}
|
||||
|
||||
void xhtl(register16_t& exchange) noexcept;
|
||||
|
||||
290
Z80/src/Z80.cpp
290
Z80/src/Z80.cpp
@@ -141,76 +141,57 @@ const EightBit::register16_t& EightBit::Z80::HL() const noexcept {
|
||||
return m_registers[m_registerSet][HL_IDX];
|
||||
}
|
||||
|
||||
void EightBit::Z80::pushWord(const register16_t destination) noexcept {
|
||||
void EightBit::Z80::memoryUpdate(int ticks) noexcept {
|
||||
assert(ticks > 0 && "Ticks must be greater than zero");
|
||||
WritingMemory.fire();
|
||||
tick(ticks);
|
||||
lowerMREQ();
|
||||
lowerWR();
|
||||
tick();
|
||||
IntelProcessor::memoryWrite();
|
||||
raiseWR();
|
||||
raiseMREQ();
|
||||
tick();
|
||||
IntelProcessor::pushWord(destination);
|
||||
WrittenMemory.fire();
|
||||
}
|
||||
|
||||
void EightBit::Z80::memoryWrite() noexcept {
|
||||
memoryUpdate(1);
|
||||
}
|
||||
|
||||
class _Writer final {
|
||||
Z80& m_parent;
|
||||
public:
|
||||
_Writer(Z80& parent) noexcept
|
||||
: m_parent(parent) {
|
||||
m_parent.WritingMemory.fire();
|
||||
m_parent.tick(2);
|
||||
m_parent.lowerMREQ();
|
||||
}
|
||||
|
||||
~_Writer() noexcept {
|
||||
m_parent.raiseMREQ();
|
||||
m_parent.WrittenMemory.fire();
|
||||
}
|
||||
};
|
||||
|
||||
_Writer writer(*this);
|
||||
IntelProcessor::memoryWrite();
|
||||
void EightBit::Z80::refreshMemory() noexcept {
|
||||
assert(lowered(M1()) && "M1 must be lowered to refresh memory");
|
||||
BUS().ADDRESS() = { REFRESH(), IV() };
|
||||
lowerRFSH();
|
||||
tick();
|
||||
lowerMREQ();
|
||||
raiseMREQ();
|
||||
raiseRFSH();
|
||||
}
|
||||
|
||||
uint8_t EightBit::Z80::memoryRead() noexcept {
|
||||
|
||||
class _Reader final {
|
||||
Z80& m_parent;
|
||||
public:
|
||||
_Reader(Z80& parent) noexcept
|
||||
: m_parent(parent) {
|
||||
m_parent.ReadingMemory.fire();
|
||||
if (lowered(m_parent.M1()))
|
||||
m_parent.tick();
|
||||
m_parent.tick(2);
|
||||
m_parent.lowerMREQ();
|
||||
}
|
||||
|
||||
~_Reader() noexcept {
|
||||
m_parent.raiseMREQ();
|
||||
m_parent.ReadMemory.fire();
|
||||
}
|
||||
};
|
||||
|
||||
_Reader reader(*this);
|
||||
return IntelProcessor::memoryRead();
|
||||
}
|
||||
|
||||
void EightBit::Z80::busWrite() noexcept {
|
||||
ReadingMemory.fire();
|
||||
tick();
|
||||
_ActivateWR writer(*this);
|
||||
IntelProcessor::busWrite();
|
||||
}
|
||||
|
||||
uint8_t EightBit::Z80::busRead() noexcept {
|
||||
lowerMREQ();
|
||||
lowerRD();
|
||||
tick();
|
||||
IntelProcessor::memoryRead();
|
||||
raiseRD();
|
||||
raiseMREQ();
|
||||
if (lowered(M1()))
|
||||
refreshMemory();
|
||||
tick();
|
||||
_ActivateRD reader(*this);
|
||||
return IntelProcessor::busRead();
|
||||
ReadMemory.fire();
|
||||
return BUS().DATA();
|
||||
}
|
||||
|
||||
void EightBit::Z80::handleRESET() noexcept {
|
||||
IntelProcessor::handleRESET();
|
||||
disableInterrupts();
|
||||
IM() = 0;
|
||||
IV() = 0;
|
||||
REFRESH() = 0;
|
||||
SP().word = AF().word = Mask16;
|
||||
tick(3);
|
||||
}
|
||||
|
||||
void EightBit::Z80::handleNMI() noexcept {
|
||||
@@ -218,18 +199,32 @@ void EightBit::Z80::handleNMI() noexcept {
|
||||
raiseHALT();
|
||||
IFF2() = IFF1();
|
||||
IFF1() = false;
|
||||
readBusDataM1();
|
||||
lowerM1();
|
||||
raiseM1();
|
||||
restart(0x66);
|
||||
}
|
||||
|
||||
uint8_t EightBit::Z80::readDataUnderInterrupt() {
|
||||
lowerM1();
|
||||
tick(3);
|
||||
lowerIORQ();
|
||||
tick();
|
||||
const auto data = BUS().DATA();
|
||||
raiseIORQ();
|
||||
assert(cycles() == 4);
|
||||
refreshMemory();
|
||||
assert(cycles() == 5);
|
||||
raiseM1();
|
||||
return data;
|
||||
}
|
||||
|
||||
void EightBit::Z80::handleINT() noexcept {
|
||||
|
||||
IntelProcessor::handleINT();
|
||||
tick(2); // 2 extra clock cycles introduced to allow the bus to settle
|
||||
uint8_t data;
|
||||
{
|
||||
_ActivateIORQ iorq(*this);
|
||||
data = readBusDataM1();
|
||||
}
|
||||
|
||||
const auto data = readDataUnderInterrupt();
|
||||
tick();
|
||||
assert(cycles() == 6);
|
||||
|
||||
switch (IM()) {
|
||||
case 0: // i8080 equivalent
|
||||
@@ -237,11 +232,13 @@ void EightBit::Z80::handleINT() noexcept {
|
||||
break;
|
||||
case 1:
|
||||
restart(7 << 3);
|
||||
assert(cycles() == 13);
|
||||
break;
|
||||
case 2:
|
||||
tick(7); // How long to allow fetching data from the device...
|
||||
tick();
|
||||
MEMPTR() = Processor::getWordPaged(IV(), data);
|
||||
call(MEMPTR());
|
||||
assert(cycles() == 13);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE;
|
||||
@@ -257,23 +254,12 @@ void EightBit::Z80::enableInterrupts() {
|
||||
}
|
||||
|
||||
void EightBit::Z80::returnConditionalFlag(const int flag) noexcept {
|
||||
const auto condition = convertCondition(flag);
|
||||
tick();
|
||||
if (convertCondition(flag))
|
||||
if (condition)
|
||||
ret();
|
||||
}
|
||||
|
||||
void EightBit::Z80::jrConditionalFlag(const int flag) noexcept {
|
||||
jrConditional(convertCondition(flag));
|
||||
}
|
||||
|
||||
void EightBit::Z80::jumpConditionalFlag(const int flag) noexcept {
|
||||
jumpConditional(convertCondition(flag));
|
||||
}
|
||||
|
||||
void EightBit::Z80::callConditionalFlag(const int flag) noexcept {
|
||||
callConditional(convertCondition(flag));
|
||||
}
|
||||
|
||||
void EightBit::Z80::retn() noexcept {
|
||||
ret();
|
||||
IFF1() = IFF2();
|
||||
@@ -283,15 +269,15 @@ void EightBit::Z80::reti() noexcept {
|
||||
retn();
|
||||
}
|
||||
|
||||
void EightBit::Z80::jr(int8_t offset) noexcept {
|
||||
IntelProcessor::jr(offset);
|
||||
tick(5);
|
||||
|
||||
void EightBit::Z80::call(register16_t destination) {
|
||||
tick();
|
||||
Processor::call(destination);
|
||||
}
|
||||
|
||||
int EightBit::Z80::jrConditional(const int condition) noexcept {
|
||||
if (!IntelProcessor::jrConditional(condition))
|
||||
tick(3);
|
||||
return condition;
|
||||
void EightBit::Z80::jumpRelative(int8_t offset) noexcept {
|
||||
IntelProcessor::jumpRelative(offset);
|
||||
tick(5);
|
||||
}
|
||||
|
||||
EightBit::register16_t EightBit::Z80::sbc(const register16_t operand, const register16_t value) noexcept {
|
||||
@@ -345,7 +331,6 @@ EightBit::register16_t EightBit::Z80::add(const register16_t operand, const regi
|
||||
|
||||
MEMPTR() = operand + 1;
|
||||
|
||||
tick(7);
|
||||
return intermediate();
|
||||
}
|
||||
|
||||
@@ -353,12 +338,13 @@ void EightBit::Z80::xhtl(register16_t& exchange) noexcept {
|
||||
MEMPTR().low = IntelProcessor::memoryRead(SP());
|
||||
++BUS().ADDRESS();
|
||||
MEMPTR().high = memoryRead();
|
||||
tick();
|
||||
IntelProcessor::memoryWrite(exchange.high);
|
||||
BUS().DATA() = exchange.high;
|
||||
exchange.high = MEMPTR().high;
|
||||
memoryUpdate(2);
|
||||
--BUS().ADDRESS();
|
||||
IntelProcessor::memoryWrite(exchange.low);
|
||||
BUS().DATA() = exchange.low;
|
||||
exchange.low = MEMPTR().low;
|
||||
memoryUpdate(1);
|
||||
tick(2);
|
||||
}
|
||||
|
||||
@@ -597,7 +583,7 @@ void EightBit::Z80::writePort(const uint8_t port) noexcept {
|
||||
|
||||
void EightBit::Z80::writePort() noexcept {
|
||||
MEMPTR() = BUS().ADDRESS();
|
||||
//tick(2);
|
||||
tick(2);
|
||||
lowerIORQ();
|
||||
lowerWR();
|
||||
tick();
|
||||
@@ -620,11 +606,12 @@ void EightBit::Z80::readPort(const uint8_t port) noexcept {
|
||||
|
||||
void EightBit::Z80::readPort() noexcept {
|
||||
MEMPTR() = BUS().ADDRESS();
|
||||
//tick(2);
|
||||
tick(2);
|
||||
tick();
|
||||
lowerIORQ();
|
||||
lowerRD();
|
||||
BUS().DATA() = m_ports.read(BUS().ADDRESS());
|
||||
tick();
|
||||
raiseRD();
|
||||
raiseIORQ();
|
||||
tick();
|
||||
@@ -638,11 +625,6 @@ void EightBit::Z80::fetchDisplacement() noexcept {
|
||||
|
||||
//
|
||||
|
||||
uint8_t EightBit::Z80::readBusDataM1() noexcept {
|
||||
_ActivateM1 m1(*this);
|
||||
return BUS().DATA();
|
||||
}
|
||||
|
||||
// ** From the Z80 CPU User Manual
|
||||
|
||||
// Figure 5 depicts the timing during an M1 (op code fetch) cycle. The Program Counter is
|
||||
@@ -671,22 +653,10 @@ uint8_t EightBit::Z80::readBusDataM1() noexcept {
|
||||
// CPU.The HALT acknowledge signal is active during this time indicating that the processor
|
||||
// is in the HALT state
|
||||
uint8_t EightBit::Z80::fetchInstruction() noexcept {
|
||||
uint8_t returned;
|
||||
{
|
||||
_ActivateM1 m1(*this);
|
||||
const auto halted = lowered(HALT());
|
||||
returned = IntelProcessor::memoryRead(PC());
|
||||
if (UNLIKELY(halted))
|
||||
returned = 0; // NOP
|
||||
else
|
||||
PC()++;
|
||||
}
|
||||
BUS().ADDRESS() = { REFRESH(), IV() };
|
||||
{
|
||||
_ActivateRFSH rfsh(*this);
|
||||
_ActivateMREQ mreq(*this);
|
||||
}
|
||||
return returned;
|
||||
lowerM1();
|
||||
IntelProcessor::fetchInstruction();
|
||||
raiseM1();
|
||||
return BUS().DATA();
|
||||
}
|
||||
|
||||
void EightBit::Z80::loadAccumulatorIndirect(addresser_t addresser) noexcept {
|
||||
@@ -745,7 +715,7 @@ EightBit::register16_t& EightBit::Z80::RP2(const int rp) noexcept {
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t EightBit::Z80::R(const int r) noexcept {
|
||||
uint8_t& EightBit::Z80::R(const int r, MemoryMapping::AccessLevel access) noexcept {
|
||||
switch (r) {
|
||||
case 0:
|
||||
return B();
|
||||
@@ -760,7 +730,21 @@ uint8_t EightBit::Z80::R(const int r) noexcept {
|
||||
case 5:
|
||||
return HL2().low;
|
||||
case 6:
|
||||
return IntelProcessor::memoryRead(UNLIKELY(displaced()) ? displacedAddress() : HL());
|
||||
if (displaced())
|
||||
displaceAddress();
|
||||
else
|
||||
BUS().ADDRESS() = HL();
|
||||
switch (access) {
|
||||
case MemoryMapping::AccessLevel::ReadOnly:
|
||||
memoryRead();
|
||||
break;
|
||||
case MemoryMapping::AccessLevel::WriteOnly:
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE;
|
||||
break;
|
||||
}
|
||||
return BUS().DATA();
|
||||
case 7:
|
||||
return A();
|
||||
default:
|
||||
@@ -768,35 +752,11 @@ uint8_t EightBit::Z80::R(const int r) noexcept {
|
||||
}
|
||||
}
|
||||
|
||||
void EightBit::Z80::R(const int r, const uint8_t value) noexcept {
|
||||
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(), value);
|
||||
break;
|
||||
case 7:
|
||||
A() = value;
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE;
|
||||
}
|
||||
void EightBit::Z80::R(const int r, const uint8_t value, const int ticks) noexcept {
|
||||
|
||||
R(r, MemoryMapping::AccessLevel::WriteOnly) = value;
|
||||
if (r == 6)
|
||||
memoryUpdate(ticks);
|
||||
}
|
||||
|
||||
void EightBit::Z80::R2(const int r, const uint8_t value) noexcept {
|
||||
@@ -879,11 +839,13 @@ void EightBit::Z80::executeCB(const int x, const int y, const int z) noexcept {
|
||||
|
||||
const bool memoryZ = z == 6;
|
||||
const bool indirect = (!displaced() && memoryZ) || displaced();
|
||||
const auto direct = !indirect;
|
||||
|
||||
uint8_t operand;
|
||||
if (displaced()) {
|
||||
tick(2);
|
||||
operand = IntelProcessor::memoryRead(displacedAddress());
|
||||
displaceAddress();
|
||||
operand = memoryRead();
|
||||
} else {
|
||||
operand = R(z);
|
||||
}
|
||||
@@ -923,8 +885,8 @@ void EightBit::Z80::executeCB(const int x, const int y, const int z) noexcept {
|
||||
break;
|
||||
} case 1: // BIT y, r[z]
|
||||
bit(y, operand);
|
||||
adjustXY(indirect ? MEMPTR().high : operand);
|
||||
if (memoryZ)
|
||||
adjustXY(direct ? operand : MEMPTR().high);
|
||||
if (indirect)
|
||||
tick();
|
||||
break;
|
||||
case 2: // RES y, r[z]
|
||||
@@ -937,8 +899,8 @@ void EightBit::Z80::executeCB(const int x, const int y, const int z) noexcept {
|
||||
UNREACHABLE;
|
||||
}
|
||||
if (update) {
|
||||
tick();
|
||||
if (displaced()) {
|
||||
tick();
|
||||
IntelProcessor::memoryWrite(operand);
|
||||
if (!memoryZ)
|
||||
R2(z, operand);
|
||||
@@ -980,6 +942,7 @@ void EightBit::Z80::executeED(const int x, const int y, const int z, const int p
|
||||
default:
|
||||
UNREACHABLE;
|
||||
}
|
||||
tick(7);
|
||||
break;
|
||||
case 3: // Retrieve/store register pair from/to immediate address
|
||||
BUS().ADDRESS() = fetchWord();
|
||||
@@ -1147,16 +1110,16 @@ void EightBit::Z80::executeOther(const int x, const int y, const int z, const in
|
||||
break;
|
||||
case 2: // DJNZ d
|
||||
tick();
|
||||
jrConditional(--B());
|
||||
jumpRelativeConditional(--B());
|
||||
break;
|
||||
case 3: // JR d
|
||||
jr(fetchByte());
|
||||
jumpRelative(fetchByte());
|
||||
break;
|
||||
case 4: // JR cc,d
|
||||
case 5:
|
||||
case 6:
|
||||
case 7:
|
||||
jrConditionalFlag(y - 4);
|
||||
jumpRelativeConditionalFlag(y - 4);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE;
|
||||
@@ -1169,6 +1132,7 @@ void EightBit::Z80::executeOther(const int x, const int y, const int z, const in
|
||||
break;
|
||||
case 1: // ADD HL,rp
|
||||
HL2() = add(HL2(), RP(p));
|
||||
tick(7);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE;
|
||||
@@ -1237,9 +1201,7 @@ void EightBit::Z80::executeOther(const int x, const int y, const int z, const in
|
||||
tick(5);
|
||||
}
|
||||
const auto original = R(y);
|
||||
if (memoryY)
|
||||
tick();
|
||||
R(y, increment(original));
|
||||
R(y, increment(original), 2);
|
||||
break;
|
||||
}
|
||||
case 5: { // 8-bit DEC
|
||||
@@ -1248,16 +1210,14 @@ void EightBit::Z80::executeOther(const int x, const int y, const int z, const in
|
||||
tick(5);
|
||||
}
|
||||
const auto original = R(y);
|
||||
if (memoryY)
|
||||
tick();
|
||||
R(y, decrement(original));
|
||||
R(y, decrement(original), 2);
|
||||
break;
|
||||
}
|
||||
case 6: { // 8-bit load immediate
|
||||
if (memoryY && displaced())
|
||||
fetchDisplacement();
|
||||
const auto value = fetchByte();
|
||||
if (displaced())
|
||||
if (memoryY)
|
||||
tick(2);
|
||||
R(y, value); // LD r,n
|
||||
break;
|
||||
@@ -1297,24 +1257,20 @@ void EightBit::Z80::executeOther(const int x, const int y, const int z, const in
|
||||
}
|
||||
break;
|
||||
case 1: // 8-bit loading
|
||||
if ((memoryZ && memoryY)) { // Exception (replaces LD (HL), (HL))
|
||||
lowerHALT();
|
||||
} else {
|
||||
if (!(memoryZ && memoryY)) {
|
||||
bool normal = true;
|
||||
if (displaced()) {
|
||||
if (memoryZ || memoryY)
|
||||
if (memoryZ || memoryY) {
|
||||
fetchDisplacement();
|
||||
tick(5);
|
||||
}
|
||||
if (memoryZ) {
|
||||
switch (y) {
|
||||
case 4:
|
||||
if (displaced())
|
||||
tick(5);
|
||||
H() = R(z);
|
||||
normal = false;
|
||||
break;
|
||||
case 5:
|
||||
if (displaced())
|
||||
tick(5);
|
||||
L() = R(z);
|
||||
normal = false;
|
||||
break;
|
||||
@@ -1323,25 +1279,20 @@ void EightBit::Z80::executeOther(const int x, const int y, const int z, const in
|
||||
if (memoryY) {
|
||||
switch (z) {
|
||||
case 4:
|
||||
if (displaced())
|
||||
tick(5);
|
||||
R(y, H());
|
||||
normal = false;
|
||||
break;
|
||||
case 5:
|
||||
if (displaced())
|
||||
tick(5);
|
||||
R(y, L());
|
||||
normal = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (normal) {
|
||||
if (displaced())
|
||||
tick(5);
|
||||
if (normal)
|
||||
R(y, R(z));
|
||||
}
|
||||
} else {
|
||||
lowerHALT(); // Exception (replaces LD (HL), (HL))
|
||||
}
|
||||
break;
|
||||
case 2: { // Operate on accumulator and register/memory location
|
||||
@@ -1399,7 +1350,7 @@ void EightBit::Z80::executeOther(const int x, const int y, const int z, const in
|
||||
exx();
|
||||
break;
|
||||
case 2: // JP (HL)
|
||||
jump(HL2());
|
||||
Processor::jump(HL2());
|
||||
break;
|
||||
case 3: // LD SP,HL
|
||||
SP() = HL2();
|
||||
@@ -1459,6 +1410,7 @@ void EightBit::Z80::executeOther(const int x, const int y, const int z, const in
|
||||
case 5: // PUSH & various ops
|
||||
switch (q) {
|
||||
case 0: // PUSH rp2[p]
|
||||
tick();
|
||||
pushWord(RP2(p));
|
||||
break;
|
||||
case 1:
|
||||
|
||||
@@ -150,26 +150,55 @@ namespace EightBit {
|
||||
|
||||
//
|
||||
|
||||
register16_t& incrementPC() override;
|
||||
uint8_t fetchInstruction() override;
|
||||
|
||||
//
|
||||
|
||||
[[nodiscard]] register16_t getWord() final;
|
||||
void setWord(register16_t value) final;
|
||||
|
||||
//
|
||||
|
||||
virtual void restart(uint8_t address);
|
||||
virtual int callConditional(int condition);
|
||||
virtual int jumpConditional(int condition);
|
||||
virtual int returnConditional(int condition);
|
||||
virtual void jr(int8_t offset) noexcept;
|
||||
virtual int jrConditional(int condition);
|
||||
virtual void callConditional(bool condition);
|
||||
virtual void jumpConditional(bool condition);
|
||||
virtual void jumpRelativeConditional(bool condition);
|
||||
virtual void returnConditional(bool condition);
|
||||
virtual void jumpIndirect();
|
||||
virtual void jump();
|
||||
void callIndirect();
|
||||
void call();
|
||||
virtual void jumpRelative(int8_t offset) noexcept;
|
||||
void jumpRelative(uint8_t offset) noexcept { jumpRelative((int8_t)offset); }
|
||||
void ret() override;
|
||||
|
||||
virtual void fetchWordMEMPTR();
|
||||
|
||||
void jumpIndirect();
|
||||
void callIndirect();
|
||||
|
||||
void resetWorkingRegisters() noexcept;
|
||||
|
||||
[[nodiscard]] virtual bool convertCondition(int flag) noexcept = 0;
|
||||
|
||||
virtual void jumpConditionalFlag(int flag) {
|
||||
jumpConditional(convertCondition(flag));
|
||||
}
|
||||
|
||||
virtual void jumpRelativeConditionalFlag(int flag) {
|
||||
jumpRelativeConditional(convertCondition(flag));
|
||||
}
|
||||
|
||||
virtual void returnConditionalFlag(int flag) {
|
||||
returnConditional(convertCondition(flag));
|
||||
}
|
||||
|
||||
virtual void callConditionalFlag(int flag) {
|
||||
callConditional(convertCondition(flag));
|
||||
}
|
||||
|
||||
virtual void cpl() noexcept {
|
||||
A() = ~A();
|
||||
}
|
||||
|
||||
private:
|
||||
static std::array<int, 8> m_halfCarryTableAdd;
|
||||
static std::array<int, 8> m_halfCarryTableSub;
|
||||
|
||||
@@ -70,7 +70,13 @@ namespace EightBit {
|
||||
virtual uint8_t memoryRead();
|
||||
virtual uint8_t busRead();
|
||||
|
||||
virtual register16_t& incrementPC();
|
||||
virtual register16_t& decrementPC();
|
||||
|
||||
virtual void immediateAddress();
|
||||
|
||||
uint8_t fetchByte();
|
||||
virtual uint8_t fetchInstruction();
|
||||
|
||||
[[nodiscard]] virtual register16_t getWord() = 0;
|
||||
virtual void setWord(register16_t value) = 0;
|
||||
|
||||
@@ -9,9 +9,6 @@ EightBit::IntelProcessor::IntelProcessor(Bus& bus)
|
||||
for (int i = 0; i < 0x100; ++i)
|
||||
m_decodedOpcodes.at(i) = i;
|
||||
|
||||
LoweredHALT.connect([this](EventArgs) noexcept { --PC(); });
|
||||
RaisedHALT.connect([this](EventArgs) noexcept { ++PC(); });
|
||||
|
||||
RaisedPOWER.connect([this](EventArgs) {
|
||||
PC() = SP() = Mask16;
|
||||
resetWorkingRegisters();
|
||||
@@ -37,7 +34,7 @@ DEFINE_PIN_LEVEL_CHANGERS(HALT, IntelProcessor);
|
||||
void EightBit::IntelProcessor::handleRESET() {
|
||||
Processor::handleRESET();
|
||||
disableInterrupts();
|
||||
jump(0);
|
||||
Processor::jump(0);
|
||||
}
|
||||
|
||||
void EightBit::IntelProcessor::handleINT() {
|
||||
@@ -54,6 +51,18 @@ uint8_t EightBit::IntelProcessor::pop() {
|
||||
return memoryRead(SP()++);
|
||||
}
|
||||
|
||||
|
||||
EightBit::register16_t& EightBit::IntelProcessor::incrementPC() {
|
||||
if (raised(HALT()))
|
||||
Processor::incrementPC();
|
||||
return PC();
|
||||
}
|
||||
|
||||
uint8_t EightBit::IntelProcessor::fetchInstruction() {
|
||||
fetchByte();
|
||||
return lowered(HALT()) ? (uint8_t)0 : BUS().DATA();
|
||||
}
|
||||
|
||||
EightBit::register16_t EightBit::IntelProcessor::getWord() {
|
||||
const auto returned = LittleEndianProcessor::getWord();
|
||||
MEMPTR() = BUS().ADDRESS();
|
||||
@@ -66,41 +75,38 @@ void EightBit::IntelProcessor::setWord(const register16_t value) {
|
||||
}
|
||||
|
||||
void EightBit::IntelProcessor::restart(const uint8_t address) {
|
||||
call(MEMPTR() = { address, 0 });
|
||||
MEMPTR() = { address, 0 };
|
||||
call();
|
||||
}
|
||||
|
||||
int EightBit::IntelProcessor::callConditional(const int condition) {
|
||||
void EightBit::IntelProcessor::callConditional(bool condition) {
|
||||
fetchWordMEMPTR();
|
||||
if (condition)
|
||||
call(MEMPTR());
|
||||
return condition;
|
||||
call();
|
||||
}
|
||||
|
||||
int EightBit::IntelProcessor::jumpConditional(const int condition) {
|
||||
void EightBit::IntelProcessor::jumpConditional(bool condition) {
|
||||
fetchWordMEMPTR();
|
||||
if (condition)
|
||||
jump(MEMPTR());
|
||||
return condition;
|
||||
jump();
|
||||
}
|
||||
|
||||
int EightBit::IntelProcessor::returnConditional(const int condition) {
|
||||
void EightBit::IntelProcessor::jumpRelativeConditional(bool condition) {
|
||||
const auto offset = fetchByte();
|
||||
if (condition)
|
||||
jumpRelative(offset);
|
||||
}
|
||||
|
||||
void EightBit::IntelProcessor::returnConditional(bool condition) {
|
||||
if (condition)
|
||||
ret();
|
||||
return condition;
|
||||
}
|
||||
|
||||
void EightBit::IntelProcessor::jr(const int8_t offset) noexcept {
|
||||
jump(MEMPTR() = PC() + offset);
|
||||
void EightBit::IntelProcessor::jumpRelative(const int8_t offset) noexcept {
|
||||
MEMPTR() = PC() + offset;
|
||||
jump();
|
||||
}
|
||||
|
||||
int EightBit::IntelProcessor::jrConditional(const int condition) {
|
||||
const auto offsetAddress = PC()++;
|
||||
if (condition) {
|
||||
const auto offset = memoryRead(offsetAddress);
|
||||
jr(offset);
|
||||
}
|
||||
return condition;
|
||||
}
|
||||
|
||||
void EightBit::IntelProcessor::ret() {
|
||||
LittleEndianProcessor::ret();
|
||||
@@ -114,12 +120,20 @@ void EightBit::IntelProcessor::fetchWordMEMPTR() {
|
||||
|
||||
void EightBit::IntelProcessor::jumpIndirect() {
|
||||
fetchWordMEMPTR();
|
||||
jump(MEMPTR());
|
||||
jump();
|
||||
}
|
||||
|
||||
void EightBit::IntelProcessor::jump() {
|
||||
Processor::jump(MEMPTR());
|
||||
}
|
||||
|
||||
void EightBit::IntelProcessor::callIndirect() {
|
||||
fetchWordMEMPTR();
|
||||
call(MEMPTR());
|
||||
call();
|
||||
}
|
||||
|
||||
void EightBit::IntelProcessor::call() {
|
||||
Processor::call(MEMPTR());
|
||||
}
|
||||
|
||||
bool EightBit::IntelProcessor::operator==(const EightBit::IntelProcessor& rhs) const noexcept {
|
||||
|
||||
@@ -80,8 +80,28 @@ void EightBit::Processor::setWordPaged(const uint8_t page, const uint8_t offset,
|
||||
setWordPaged(value);
|
||||
}
|
||||
|
||||
EightBit::register16_t& EightBit::Processor::incrementPC() {
|
||||
PC()++;
|
||||
return PC();
|
||||
}
|
||||
|
||||
EightBit::register16_t& EightBit::Processor::decrementPC() {
|
||||
PC()--;
|
||||
return PC();
|
||||
}
|
||||
|
||||
void EightBit::Processor::immediateAddress() {
|
||||
BUS().ADDRESS() = PC();
|
||||
incrementPC();
|
||||
}
|
||||
|
||||
uint8_t EightBit::Processor::fetchByte() {
|
||||
return memoryRead(PC()++);
|
||||
immediateAddress();
|
||||
return memoryRead();
|
||||
}
|
||||
|
||||
uint8_t EightBit::Processor::fetchInstruction() {
|
||||
return fetchByte();
|
||||
}
|
||||
|
||||
EightBit::register16_t EightBit::Processor::getWord(const register16_t address) {
|
||||
|
||||
Reference in New Issue
Block a user