diff --git a/Z80/HarteTest_Z80/checker_t.cpp b/Z80/HarteTest_Z80/checker_t.cpp index db12263..34e4b2b 100644 --- a/Z80/HarteTest_Z80/checker_t.cpp +++ b/Z80/HarteTest_Z80/checker_t.cpp @@ -7,7 +7,7 @@ void checker_t::addActualCycle(const uint16_t address, const uint8_t value, cons } void checker_t::addActualCycle(const EightBit::register16_t address, const uint8_t value, const std::string& action) { - addActualCycle(address.word, value, action); + addActualCycle(address.joined, value, action); } void checker_t::dumpCycles(const std::string which, const actual_cycles_t& events) { @@ -226,8 +226,8 @@ bool checker_t::checkState(test_t test) { const auto final = test.final(); - const auto pc_good = check("PC", final.pc(), cpu.PC().word); - const auto sp_good = check("SP", final.sp(), cpu.SP().word); + const auto pc_good = check("PC", final.pc(), cpu.PC().joined); + const auto sp_good = check("SP", final.sp(), cpu.SP().joined); const auto a_good = check("A", final.a(), cpu.A()); const auto f_good = check("F", final.f(), cpu.F()); @@ -248,26 +248,29 @@ bool checker_t::checkState(test_t test) { cpu.exxAF(); cpu.exx(); - const auto af_alt_good = check("AF'", final.af_(), cpu.AF().word); - const auto bc_alt_good = check("BC'", final.bc_(), cpu.BC().word); - const auto de_alt_good = check("DE'", final.de_(), cpu.DE().word); - const auto hl_alt_good = check("DE'", final.de_(), cpu.DE().word); + const auto af_alt_good = check("AF'", final.af_(), cpu.AF().joined); + const auto bc_alt_good = check("BC'", final.bc_(), cpu.BC().joined); + const auto de_alt_good = check("DE'", final.de_(), cpu.DE().joined); + const auto hl_alt_good = check("HL'", final.hl_(), cpu.HL().joined); + + cpu.exx(); + cpu.exxAF(); const auto i_good = check("I", final.i(), cpu.IV()); const auto r_good = check("R", final.r(), (uint8_t)cpu.REFRESH()); const auto im_good = check("IM", final.im(), (uint8_t)cpu.IM()); - const auto q_good = check("Q", final.im(), (uint8_t)cpu.IM()); + const auto q_good = check("Q", final.q(), (uint8_t)cpu.Q()); const auto iff1_good = check("IFF1", final.iff1(), (uint8_t)cpu.IFF1()); const auto iff2_good = check("IFF2", final.iff2(), (uint8_t)cpu.IFF2()); const auto iff_good = iff1_good && iff2_good; - const auto memptr_good = check("MEMPTR", final.wz(), cpu.MEMPTR().word); + const auto memptr_good = check("MEMPTR", final.wz(), cpu.MEMPTR().joined); - const auto ix_good = check("IX", final.ix(), cpu.IX().word); - const auto iy_good = check("IY", final.iy(), cpu.IY().word); + const auto ix_good = check("IX", final.ix(), cpu.IX().joined); + const auto iy_good = check("IY", final.iy(), cpu.IY().joined); bool ram_problem = false; for (const auto entry : final.ram()) { @@ -319,7 +322,7 @@ void checker_t::check(test_t test) { runner().raisePOWER(); initialiseState(test); - const auto pc = cpu.PC().word; + const auto pc = cpu.PC().joined; m_cycles = cpu.step(); runner().lowerPOWER(); @@ -336,11 +339,11 @@ void checker_t::check(test_t test) { add_disassembly(pc); const auto final = test.final(); - raise("PC", final.pc(), cpu.PC().word); - raise("SP", final.sp(), cpu.SP().word); + raise("PC", final.pc(), cpu.PC().joined); + raise("SP", final.sp(), cpu.SP().joined); raise("A", final.a(), cpu.A()); - raiseFlags("F", final.a(), cpu.A()); + raiseFlags("F", final.f(), cpu.F()); raise("B", final.b(), cpu.B()); raise("C", final.c(), cpu.C()); raise("D", final.d(), cpu.D()); @@ -351,10 +354,10 @@ void checker_t::check(test_t test) { cpu.exx(); cpu.exxAF(); - raise("'AF", final.af_(), cpu.AF().word); - raise("'BC", final.bc_(), cpu.BC().word); - raise("'DE", final.de_(), cpu.DE().word); - raise("'HL", final.hl_(), cpu.HL().word); + raise("'AF", final.af_(), cpu.AF().joined); + raise("'BC", final.bc_(), cpu.BC().joined); + raise("'DE", final.de_(), cpu.DE().joined); + raise("'HL", final.hl_(), cpu.HL().joined); raise("I", final.i(), cpu.IV()); raise("R", final.r(), cpu.REFRESH()); @@ -366,10 +369,10 @@ void checker_t::check(test_t test) { raise("IFF1", final.iff1(), (uint8_t)cpu.IFF1()); raise("IFF2", final.iff2(), (uint8_t)cpu.IFF2()); - raise("WZ", final.wz(), cpu.MEMPTR().word); + raise("WZ", final.wz(), cpu.MEMPTR().joined); - raise("IX", final.ix(), cpu.IX().word); - raise("IY", final.iy(), cpu.IY().word); + raise("IX", final.ix(), cpu.IX().joined); + raise("IY", final.iy(), cpu.IY().joined); os() << std::dec << std::setfill(' ') diff --git a/Z80/inc/Z80.h b/Z80/inc/Z80.h index 246e85c..7700143 100644 --- a/Z80/inc/Z80.h +++ b/Z80/inc/Z80.h @@ -18,7 +18,7 @@ namespace EightBit { public: // ** From the Z80 CPU User Manual // RFSH.Refresh(output, active Low). RFSH, together with MREQ, indicates that the lower - // seven bits of the system’s address bus can be used as a refresh address to the system’s + // seven bits of the system’s address bus can be used as a refresh address to the system’s // dynamic memories. DECLARE_PIN_OUTPUT(RFSH) @@ -208,8 +208,7 @@ namespace EightBit { [[nodiscard]] constexpr auto subtracting() noexcept { return subtractingTest(F()); } void adjustStatusFlags(uint8_t value) noexcept { - m_modifiedF = F(); - F() = value; + F() = m_modifiedF = value; } void setBit(StatusBits flag) noexcept { return adjustStatusFlags(setBit(F(), flag)); } @@ -386,6 +385,7 @@ namespace EightBit { void lddr() noexcept; void repeatBlockInstruction() noexcept; + void loadRepeat() noexcept; void adjustBlockRepeatFlagsIO() noexcept; void adjustBlockInputOutputFlags(int basis) noexcept; void adjustBlockInFlagsIncrement() noexcept; @@ -410,8 +410,8 @@ namespace EightBit { void neg() noexcept; - void rrd(register16_t address, uint8_t& update) noexcept; - void rld(register16_t address, uint8_t& update) noexcept; + void rrd() noexcept; + void rld() noexcept; void writePort(register16_t port) noexcept; void writePort(uint8_t port) noexcept; @@ -426,5 +426,8 @@ namespace EightBit { void pushRegisterPair(int p); void prefixCB(); + void prefixDD(); + void prefixED(); + void prefixFD(); }; } \ No newline at end of file diff --git a/Z80/src/Z80.cpp b/Z80/src/Z80.cpp index 787a061..170198e 100644 --- a/Z80/src/Z80.cpp +++ b/Z80/src/Z80.cpp @@ -21,15 +21,12 @@ EightBit::Z80::Z80(Bus& bus, InputOutput& ports) noexcept REFRESH() = 0; IV() = Mask8; - exxAF(); - exx(); - IX() = IY() = Mask16; - resetRegisterSet(); - }); - RaisedM1.connect([this](EventArgs) { - ++REFRESH(); + exx(); + exxAF(); + + resetRegisterSet(); }); LoweredRESET.connect([this](EventArgs) { @@ -114,7 +111,15 @@ bool EightBit::Z80::operator==(const EightBit::Z80& rhs) const noexcept { DEFINE_PIN_LEVEL_CHANGERS(NMI, Z80); DEFINE_PIN_LEVEL_CHANGERS(M1, Z80); -DEFINE_PIN_LEVEL_CHANGERS(RFSH, Z80); +void EightBit::Z80::raiseRFSH() noexcept { + if (lowered(RFSH())) { + RaisingRFSH.fire(); + raise(RFSH()); + ++REFRESH(); + RaisedRFSH.fire(); + } +} +DEFINE_PIN_LEVEL_LOWER(RFSH, Z80); DEFINE_PIN_LEVEL_CHANGERS(MREQ, Z80); DEFINE_PIN_LEVEL_CHANGERS(IORQ, Z80); DEFINE_PIN_LEVEL_CHANGERS(RD, Z80); @@ -195,23 +200,23 @@ void EightBit::Z80::handleNMI() noexcept { IFF2() = IFF1(); IFF1() = false; lowerM1(); + auto _ = BUS().DATA(); raiseM1(); restart(0x66); } uint8_t EightBit::Z80::readDataUnderInterrupt() noexcept { lowerM1(); - tick(2); + tick(3); lowerIORQ(); tick(); - const auto data = BUS().DATA(); + auto _ = BUS().DATA(); raiseIORQ(); assert(cycles() == 4); refreshMemory(); assert(cycles() == 5); raiseM1(); - tick(); - return data; + return BUS().DATA(); } void EightBit::Z80::handleINT() noexcept { @@ -272,10 +277,8 @@ bool EightBit::Z80::convertCondition(int flag) noexcept { } void EightBit::Z80::returnConditionalFlag(const int flag) noexcept { - const auto condition = convertCondition(flag); tick(); - if (condition) - ret(); + IntelProcessor::returnConditionalFlag(flag); } void EightBit::Z80::retn() noexcept { @@ -473,19 +476,19 @@ uint8_t EightBit::Z80::rrc(uint8_t operand) noexcept { } uint8_t EightBit::Z80::rl(uint8_t operand) noexcept { - clearBit(NF | HC); const auto carrying = carry(); setBit(CF, operand & Bit7); const uint8_t result = (operand << 1) | carrying; + clearBit(NF | HC); adjustXY(result); return result; } uint8_t EightBit::Z80::rr(uint8_t operand) noexcept { - clearBit(NF | HC); const auto carrying = carry(); setBit(CF, operand & Bit0); const uint8_t result = (operand >> 1) | (carrying << 7); + clearBit(NF | HC); adjustXY(result); return result; } @@ -569,8 +572,8 @@ void EightBit::Z80::ccf() noexcept { } void EightBit::Z80::cpl() noexcept { - setBit(HC | NF); IntelProcessor::cpl(); + setBit(HC | NF); adjustXY(A()); } @@ -624,19 +627,18 @@ void EightBit::Z80::cpd() noexcept { void EightBit::Z80::cpir() noexcept { cpi(); - if (parity() != 0 && zero() == 0) { + if (parity() != 0 && zero() == 0) repeatBlockInstruction(); - tick(5); - } } void EightBit::Z80::cpdr() noexcept { cpd(); if (parity() != 0 && zero() == 0) { repeatBlockInstruction(); - tick(5); } else { - MEMPTR() = PC().joined - 2; + MEMPTR() = PC(); + --MEMPTR(); + --MEMPTR(); tick(2); } } @@ -664,36 +666,39 @@ void EightBit::Z80::ldi() noexcept { ++DE(); } -void EightBit::Z80::ldir() noexcept { - ldi(); +void EightBit::Z80::loadRepeat() noexcept { if (parity() != 0) repeatBlockInstruction(); - tick(5); + else + tick(5); +} + +void EightBit::Z80::ldir() noexcept { + ldi(); + loadRepeat(); } void EightBit::Z80::lddr() noexcept { ldd(); - if (parity() != 0) - repeatBlockInstruction(); - tick(5); + loadRepeat(); } void EightBit::Z80::repeatBlockInstruction() noexcept { MEMPTR() = --PC(); --PC(); adjustXY(PC().high); + tick(5); } void EightBit::Z80::adjustBlockRepeatFlagsIO() noexcept { + auto direction = B(); if (carry() != 0) { const auto negative = signTest(BUS().DATA()) != 0; - const uint8_t direction = B() + (negative ? -1 : +1); - const auto calculatedParity = (parity() >> 2) ^ (evenParity(direction & Mask::Mask3) ? 1 : 0) ^ 1; - setBit(PF, calculatedParity != 0); + direction += (negative ? -1 : +1); setBit(HC, (B() & Mask::Mask4) == (negative ? 0 : Mask::Mask4)); - } else { - adjustParity((B() & Mask::Mask3)); } + const auto calculatedParity = (parity() >> 2) ^ (evenParity(direction & Mask::Mask3) ? 1 : 0) ^ 1; + setBit(PF, calculatedParity != 0); } void EightBit::Z80::adjustBlockInputOutputFlags(int basis) noexcept { @@ -743,7 +748,6 @@ void EightBit::Z80::inir() noexcept { if (zero() == 0) { repeatBlockInstruction(); adjustBlockRepeatFlagsIO(); - tick(5); } } @@ -752,7 +756,6 @@ void EightBit::Z80::indr() noexcept { if (zero() == 0) { repeatBlockInstruction(); adjustBlockRepeatFlagsIO(); - tick(5); } } @@ -782,7 +785,6 @@ void EightBit::Z80::otir() noexcept { if (zero() == 0) { repeatBlockInstruction(); adjustBlockRepeatFlagsIO(); - tick(5); } } @@ -791,29 +793,26 @@ void EightBit::Z80::otdr() noexcept { if (zero() == 0) { repeatBlockInstruction(); adjustBlockRepeatFlagsIO(); - tick(5); } } -void EightBit::Z80::rrd(register16_t address, uint8_t& update) noexcept { - MEMPTR() = BUS().ADDRESS() = address; - ++MEMPTR(); - const auto memory = memoryRead(); - tick(4); - IntelProcessor::memoryWrite(promoteNibble(update) | highNibble(memory)); - update = higherNibble(update) | lowerNibble(memory); - adjustSZPXY(update); +void EightBit::Z80::rrd() noexcept { + readMemoryIndirect(HL()); + const auto memory = BUS().DATA(); + BUS().DATA() = promoteNibble(A()) | highNibble(memory); + memoryUpdate(5); + A() = higherNibble(A()) | lowerNibble(memory); + adjustSZPXY(A()); clearBit(NF | HC); } -void EightBit::Z80::rld(register16_t address, uint8_t& update) noexcept { - MEMPTR() = BUS().ADDRESS() = address; - ++MEMPTR(); - const auto memory = memoryRead(); - tick(4); - IntelProcessor::memoryWrite(promoteNibble(memory) | lowNibble(update)); - update = higherNibble(update) | highNibble(memory); - adjustSZPXY(update); +void EightBit::Z80::rld() noexcept { + readMemoryIndirect(HL()); + const auto memory = BUS().DATA(); + BUS().DATA() = promoteNibble(memory) | lowNibble(A()); + memoryUpdate(5); + A() = higherNibble(A()) | highNibble(memory); + adjustSZPXY(A()); clearBit(NF | HC); } @@ -1255,10 +1254,10 @@ void EightBit::Z80::executeED(const int x, const int y, const int z, const int p readInternalRegister([this]() { return REFRESH(); }); break; case 4: // RRD - rrd(HL(), A()); + rrd(); break; case 5: // RLD - rld(HL(), A()); + rld(); break; case 6: // NOP case 7: // NOP @@ -1665,16 +1664,13 @@ void EightBit::Z80::executeOther(const int x, const int y, const int z, const in callIndirect(); break; case 1: // DD prefix - m_prefixDD = true; - IntelProcessor::execute(fetchInstruction()); + prefixDD(); break; case 2: // ED prefix - m_prefixED = true; - IntelProcessor::execute(fetchInstruction()); + prefixED(); break; case 3: // FD prefix - m_prefixFD = true; - IntelProcessor::execute(fetchInstruction()); + prefixFD(); break; default: UNREACHABLE; @@ -1746,6 +1742,21 @@ void EightBit::Z80::pushRegisterPair(int p) { pushShort(RP2(p)); } +void EightBit::Z80::prefixDD() { + m_prefixDD = true; + IntelProcessor::execute(fetchInstruction()); +} + +void EightBit::Z80::prefixED() { + m_prefixED = true; + IntelProcessor::execute(fetchInstruction()); +} + +void EightBit::Z80::prefixFD() { + m_prefixFD = true; + IntelProcessor::execute(fetchInstruction()); +} + void EightBit::Z80::prefixCB() { m_prefixCB = true; if (displaced()) { diff --git a/inc/IntelProcessor.h b/inc/IntelProcessor.h index bb924af..62b48ad 100644 --- a/inc/IntelProcessor.h +++ b/inc/IntelProcessor.h @@ -154,6 +154,7 @@ namespace EightBit { virtual void jumpIndirect() noexcept; void jump() noexcept; void callIndirect() noexcept; + using Processor::call; void call() noexcept; virtual void jumpRelative(int8_t offset) noexcept; void jumpRelative(uint8_t offset) noexcept { jumpRelative(static_cast(offset)); } diff --git a/inc/Processor.h b/inc/Processor.h index d284c11..5b479c7 100644 --- a/inc/Processor.h +++ b/inc/Processor.h @@ -64,11 +64,11 @@ namespace EightBit { void memoryWrite(register16_t address) noexcept; void memoryWrite(uint8_t data) noexcept; virtual void memoryWrite() noexcept; - virtual void busWrite() noexcept; + void busWrite() noexcept; uint8_t memoryRead(register16_t address) noexcept; virtual uint8_t memoryRead() noexcept; - virtual uint8_t busRead() noexcept; + uint8_t busRead() noexcept; virtual register16_t& incrementPC() noexcept; diff --git a/inc/Signal.h b/inc/Signal.h index de612b5..3f38029 100644 --- a/inc/Signal.h +++ b/inc/Signal.h @@ -28,103 +28,3 @@ namespace EightBit { } }; } - -// -// -//#pragma once -// -//#include -//#include -//#include -//#include "EventArgs.h" -// -//namespace EightBit { -// template class Signal final { -// private: -// using fn_t = void(*)(void*, T&); -// using dtor_t = void(*)(void*); -// -// struct Entry { -// void* ctx; -// fn_t fn; -// dtor_t dtor; // null for inline-stored (trivially-destructible) callables -// }; -// -// int m_count = 0; -// std::vector m_delegates; -// -// void destroy_entries() noexcept { -// for (auto& e : m_delegates) -// if (e.dtor) e.dtor(e.ctx); -// m_delegates.clear(); -// m_count = 0; -// } -// -// public: -// Signal() = default; -// -// // Copies are connection-less: a copied chip instance starts with no listeners -// Signal(const Signal&) noexcept {} -// Signal& operator=(const Signal&) noexcept { destroy_entries(); return *this; } -// -// ~Signal() { destroy_entries(); } -// -// template -// void connect(Callable&& callable) { -// using Decay = std::decay_t; -// -// // [this] lambdas on x64: sizeof == 8 == sizeof(void*), trivially copyable/destructible. -// // Pack the captured pointer directly into the void* slot — zero heap overhead, -// // and the dispatch function knows the concrete type so c(e) can be inlined. -// constexpr bool fits_inline = -// sizeof(Decay) <= sizeof(void*) && -// alignof(Decay) <= alignof(void*) && -// std::is_trivially_copyable_v && -// std::is_trivially_destructible_v; -// -// if constexpr (fits_inline) { -// void* storage = nullptr; -// std::memcpy(&storage, &callable, sizeof(Decay)); -// m_delegates.push_back({ -// storage, -// [](void* raw, T& e) noexcept { -// // Reconstruct the callable from the packed pointer bits. -// // Trivially-copyable, so memcpy into aligned storage is valid. -// alignas(Decay) char buf[sizeof(Decay)]; -// std::memcpy(buf, &raw, sizeof(Decay)); -// (*reinterpret_cast(buf))(e); -// }, -// nullptr -// }); -// } -// else { -// auto* heap = new Decay(std::forward(callable)); -// m_delegates.push_back({ -// static_cast(heap), -// [](void* raw, T& e) noexcept { -// (*static_cast(raw))(e); -// }, -// [](void* raw) noexcept { -// delete static_cast(raw); -// } -// }); -// } -// ++m_count; -// } -// -// [[nodiscard]] constexpr auto count() const noexcept { return m_count; } -// [[nodiscard]] constexpr auto inactive() const noexcept { return count() == 0; } -// [[nodiscard]] constexpr auto singular() const noexcept { return count() == 1; } -// [[nodiscard]] constexpr auto active() const noexcept { return count() != 0; } -// -// void fire(T& e = EventArgs::empty()) const noexcept { -// //if (inactive()) return; -// //if (singular()) { -// // m_delegates[0].fn(m_delegates[0].ctx, e); -// // return; -// //} -// for (const auto& entry : m_delegates) -// entry.fn(entry.ctx, e); -// } -// }; -//} diff --git a/src/IntelProcessor.cpp b/src/IntelProcessor.cpp index d07dff3..78a03ad 100644 --- a/src/IntelProcessor.cpp +++ b/src/IntelProcessor.cpp @@ -123,7 +123,7 @@ void EightBit::IntelProcessor::callIndirect() noexcept { } void EightBit::IntelProcessor::call() noexcept { - LittleEndianProcessor::call(MEMPTR()); + call(MEMPTR()); } void EightBit::IntelProcessor::jumpConditionalFlag(int flag) noexcept {