diff --git a/Intel8080/inc/Intel8080.h b/Intel8080/inc/Intel8080.h index 3beb3ba..c3253e3 100644 --- a/Intel8080/inc/Intel8080.h +++ b/Intel8080/inc/Intel8080.h @@ -152,9 +152,9 @@ namespace EightBit { void di(); void ei(); - bool returnConditionalFlag(int flag); - bool jumpConditionalFlag(int flag); - bool callConditionalFlag(int flag); + int returnConditionalFlag(int flag); + int jumpConditionalFlag(int flag); + int callConditionalFlag(int flag); void add(register16_t value); diff --git a/Intel8080/src/Intel8080.cpp b/Intel8080/src/Intel8080.cpp index 858d026..f6c6c66 100644 --- a/Intel8080/src/Intel8080.cpp +++ b/Intel8080/src/Intel8080.cpp @@ -56,7 +56,7 @@ void EightBit::Intel8080::decrement(uint8_t& operand) { F() = setBit(F(), AC, lowNibble(operand) != Mask4); } -bool EightBit::Intel8080::jumpConditionalFlag(const int flag) { +int EightBit::Intel8080::jumpConditionalFlag(const int flag) { switch (flag) { case 0: // NZ return jumpConditional(!(F() & ZF)); @@ -79,7 +79,7 @@ bool EightBit::Intel8080::jumpConditionalFlag(const int flag) { } } -bool EightBit::Intel8080::returnConditionalFlag(const int flag) { +int EightBit::Intel8080::returnConditionalFlag(const int flag) { switch (flag) { case 0: // NZ return returnConditional(!(F() & ZF)); @@ -102,7 +102,7 @@ bool EightBit::Intel8080::returnConditionalFlag(const int flag) { } } -bool EightBit::Intel8080::callConditionalFlag(const int flag) { +int EightBit::Intel8080::callConditionalFlag(const int flag) { switch (flag) { case 0: // NZ return callConditional(!(F() & ZF)); diff --git a/LR35902/inc/LR35902.h b/LR35902/inc/LR35902.h index 9835566..2e4f48f 100644 --- a/LR35902/inc/LR35902.h +++ b/LR35902/inc/LR35902.h @@ -174,10 +174,10 @@ namespace EightBit { void reti(); - bool jrConditionalFlag(int flag); - bool returnConditionalFlag(int flag); - bool jumpConditionalFlag(int flag); - bool callConditionalFlag(int flag); + int jrConditionalFlag(int flag); + int returnConditionalFlag(int flag); + int jumpConditionalFlag(int flag); + int callConditionalFlag(int flag); void add(register16_t& operand, register16_t value); diff --git a/LR35902/src/LR35902.cpp b/LR35902/src/LR35902.cpp index c61d762..b0527be 100644 --- a/LR35902/src/LR35902.cpp +++ b/LR35902/src/LR35902.cpp @@ -61,7 +61,7 @@ void EightBit::GameBoy::LR35902::decrement(uint8_t& operand) { F() = adjustZero(F(), --operand); } -bool EightBit::GameBoy::LR35902::jrConditionalFlag(const int flag) { +int EightBit::GameBoy::LR35902::jrConditionalFlag(const int flag) { switch (flag) { case 0: // NZ return jrConditional(!(F() & ZF)); @@ -76,7 +76,7 @@ bool EightBit::GameBoy::LR35902::jrConditionalFlag(const int flag) { } } -bool EightBit::GameBoy::LR35902::jumpConditionalFlag(const int flag) { +int EightBit::GameBoy::LR35902::jumpConditionalFlag(const int flag) { switch (flag) { case 0: // NZ return jumpConditional(!(F() & ZF)); @@ -96,7 +96,7 @@ void EightBit::GameBoy::LR35902::reti() { ei(); } -bool EightBit::GameBoy::LR35902::returnConditionalFlag(const int flag) { +int EightBit::GameBoy::LR35902::returnConditionalFlag(const int flag) { switch (flag) { case 0: // NZ return returnConditional(!(F() & ZF)); @@ -111,7 +111,7 @@ bool EightBit::GameBoy::LR35902::returnConditionalFlag(const int flag) { } } -bool EightBit::GameBoy::LR35902::callConditionalFlag(const int flag) { +int EightBit::GameBoy::LR35902::callConditionalFlag(const int flag) { switch (flag) { case 0: // NZ return callConditional(!(F() & ZF)); diff --git a/M6502/src/mos6502.cpp b/M6502/src/mos6502.cpp index 0e1d30e..0dcb70b 100644 --- a/M6502/src/mos6502.cpp +++ b/M6502/src/mos6502.cpp @@ -402,7 +402,7 @@ uint8_t EightBit::MOS6502::pop() { void EightBit::MOS6502::dummyPush(const uint8_t value) { tick(); BUS().DATA() = value; - BUS().ADDRESS() = register16_t(S()--, 1); + BUS().ADDRESS() = { S()--, 1 }; } //// diff --git a/Z80/fusetest_Z80/FuseTestRunner.cpp b/Z80/fusetest_Z80/FuseTestRunner.cpp index 1dd2464..16d8783 100644 --- a/Z80/fusetest_Z80/FuseTestRunner.cpp +++ b/Z80/fusetest_Z80/FuseTestRunner.cpp @@ -2,10 +2,48 @@ #include "FuseTestRunner.h" #include "Disassembler.h" -Fuse::TestRunner::TestRunner(const Test& test, const ExpectedTestResult& expected) +#include + +Fuse::TestRunner::TestRunner(const Test& test, const ExpectedTestResult& result) : m_test(test), - m_expected(expected), - m_cpu(*this, m_ports) { + m_result(result), + m_cpu(*this, m_ports), + m_totalCycles(0) { + + for (const auto& event : m_result.events.events) { + if (!boost::algorithm::ends_with(event.specifier, "C")) + m_expectedEvents.events.push_back(event); + } + + m_cpu.ExecutedInstruction.connect([this](EightBit::Z80& cpu) { + m_totalCycles += cpu.cycles(); + }); + + ReadByte.connect([this](EightBit::EventArgs&) { + addActualEvent("MR"); + }); + + WrittenByte.connect([this](EightBit::EventArgs&) { + addActualEvent("MW"); + }); + + m_ports.ReadPort.connect([this](uint8_t port) { + addActualEvent("PR"); + }); + + m_ports.WrittenPort.connect([this](uint8_t port) { + addActualEvent("PW"); + }); +} + +void Fuse::TestRunner::addActualEvent(const std::string& specifier) { + TestEvent actual; + actual.address = ADDRESS().word; + actual.cycles = m_totalCycles + m_cpu.cycles(); + actual.specifier = specifier; + actual.value = DATA(); + actual.valid = true; + m_actualEvents.events.push_back(actual); } // @@ -71,8 +109,9 @@ void Fuse::TestRunner::initialiseMemory() { // void Fuse::TestRunner::check() { - checkregisters(); + checkRegisters(); checkMemory(); + checkEvents(); } void Fuse::TestRunner::dumpDifference(const std::string& description, uint8_t actual, uint8_t expected) const { @@ -102,9 +141,9 @@ void Fuse::TestRunner::dumpDifference( dumpDifference(lowDescription, actualLow, expectedLow); } -void Fuse::TestRunner::checkregisters() { +void Fuse::TestRunner::checkRegisters() { - const auto& expectedState = m_expected.registerState; + const auto& expectedState = m_result.registerState; const auto& expectedRegisters = expectedState.registers; auto af = m_cpu.AF() == expectedRegisters[Fuse::RegisterState::AF]; @@ -306,7 +345,7 @@ void Fuse::TestRunner::checkMemory() { bool first = true; - for (auto memoryDatum : m_expected.memoryData) { + for (auto memoryDatum : m_result.memoryData) { auto bytes = memoryDatum.bytes; for (int i = 0; i < bytes.size(); ++i) { auto expected = bytes[i]; @@ -329,6 +368,61 @@ void Fuse::TestRunner::checkMemory() { } } +void Fuse::TestRunner::checkEvents() { + + const auto& expectations = m_expectedEvents.events; + const auto& actuals = m_actualEvents.events; + + auto eventFailure = expectations.size() != actuals.size(); + for (auto i = 0; !eventFailure && (i < expectations.size()); ++i) { + + const auto& expectation = expectations[i]; + const auto& actual = actuals[i]; + + const auto equalCycles = expectation.cycles == actual.cycles; + const auto equalSpecifier = expectation.specifier == actual.specifier; + const auto equalAddress = expectation.address == actual.address; + const auto equalValue = expectation.value == actual.value; + + const auto equal = equalCycles && equalSpecifier && equalAddress && equalValue; + eventFailure = !equal; + } + + if (eventFailure) { + dumpExpectedEvents(); + dumpActualEvents(); + } + + if (!m_failed) + m_failed = eventFailure; +} + +void Fuse::TestRunner::dumpExpectedEvents() const { + std::cerr << "++++ Dumping expected events:" << std::endl; + dumpEvents(m_expectedEvents.events); +} + +void Fuse::TestRunner::dumpActualEvents() const { + std::cerr << "++++ Dumping actual events:" << std::endl; + dumpEvents(m_actualEvents.events); +} + +void Fuse::TestRunner::dumpEvents(const std::vector& events) { + for (const auto& event : events) { + dumpEvent(event); + } +} + +void Fuse::TestRunner::dumpEvent(const TestEvent& event) { + std::cerr << " Event issue " << + "Cycles = " << event.cycles << + ", Specifier = " << event.specifier << + ", Address = " << EightBit::Disassembler::hex((uint16_t)event.address); + if (!boost::algorithm::ends_with(event.specifier, "C")) + std::cerr << ", Value=" << EightBit::Disassembler::hex((uint8_t)event.value); + std::cerr << std::endl; +} + void Fuse::TestRunner::run() { raisePOWER(); initialise(); diff --git a/Z80/fusetest_Z80/FuseTestRunner.h b/Z80/fusetest_Z80/FuseTestRunner.h index cb0b982..34511a5 100644 --- a/Z80/fusetest_Z80/FuseTestRunner.h +++ b/Z80/fusetest_Z80/FuseTestRunner.h @@ -12,7 +12,10 @@ namespace Fuse { class TestRunner : public EightBit::Bus { private: const Test& m_test; - const ExpectedTestResult& m_expected; + const ExpectedTestResult& m_result; + + TestEvents m_expectedEvents; + TestEvents m_actualEvents; bool m_failed = false; bool m_unimplemented = false; @@ -21,12 +24,15 @@ namespace Fuse { EightBit::InputOutput m_ports; EightBit::Z80 m_cpu; + int m_totalCycles; + void initialiseRegisters(); void initialiseMemory(); void check(); - void checkregisters(); + void checkRegisters(); void checkMemory(); + void checkEvents(); void dumpDifference(const std::string& description, uint8_t high, uint8_t low) const; void dumpDifference( @@ -34,6 +40,13 @@ namespace Fuse { const std::string& lowDescription, EightBit::register16_t actual, EightBit::register16_t expected) const; + void addActualEvent(const std::string& specifier); + void dumpExpectedEvents() const; + void dumpActualEvents() const; + + static void dumpEvents(const std::vector& events); + static void dumpEvent(const TestEvent& event); + protected: virtual EightBit::MemoryMapping mapping(uint16_t address) final { return { m_ram, 0x0000, 0xffff, EightBit::MemoryMapping::AccessLevel::ReadWrite }; diff --git a/Z80/inc/Z80.h b/Z80/inc/Z80.h index d30dda2..d6b22d9 100644 --- a/Z80/inc/Z80.h +++ b/Z80/inc/Z80.h @@ -113,6 +113,11 @@ namespace EightBit { void handleRESET() final; void handleINT() final; + void call(const register16_t destination) override { + tick(); + IntelProcessor::call(destination); + } + void busWrite() final; uint8_t busRead() final; @@ -169,18 +174,16 @@ namespace EightBit { // refresh dynamic memories. The CPU uses this time to decode and execute the fetched // instruction so that no other concurrent operation can be performed. uint8_t readInitialOpCode() { + tick(); lowerM1(); const auto returned = IntelProcessor::busRead(PC()); raiseM1(); - tick(2); BUS().ADDRESS().low = REFRESH(); BUS().ADDRESS().high = IV(); lowerRFSH(); lowerMREQ(); - tick(); raiseMREQ(); raiseRFSH(); - tick(); return returned; } @@ -350,6 +353,8 @@ namespace EightBit { return setBit(f, VF, overflow); } + [[nodiscard]] static bool convertCondition(uint8_t f, int flag); + static uint8_t subtract(uint8_t& f, uint8_t operand, uint8_t value, int carry = 0); void executeCB(int x, int y, int z); @@ -365,9 +370,9 @@ namespace EightBit { void retn(); void reti(); - [[nodiscard]] bool jrConditionalFlag(uint8_t f, int flag); - [[nodiscard]] bool returnConditionalFlag(uint8_t f, int flag); - [[nodiscard]] bool callConditionalFlag(uint8_t f, int flag); + void returnConditionalFlag(uint8_t f, int flag); + void jrConditionalFlag(uint8_t f, int flag); + void callConditionalFlag(uint8_t f, int flag); void jumpConditionalFlag(uint8_t f, int flag); [[nodiscard]] register16_t sbc(uint8_t& f, register16_t operand, register16_t value); diff --git a/Z80/src/Z80.cpp b/Z80/src/Z80.cpp index cef23b3..b4c964a 100644 --- a/Z80/src/Z80.cpp +++ b/Z80/src/Z80.cpp @@ -59,6 +59,7 @@ EightBit::register16_t& EightBit::Z80::HL() { } void EightBit::Z80::busWrite() { + tick(3); lowerMREQ(); lowerWR(); IntelProcessor::busWrite(); @@ -67,6 +68,7 @@ void EightBit::Z80::busWrite() { } uint8_t EightBit::Z80::busRead() { + tick(3); lowerMREQ(); lowerRD(); const auto returned = IntelProcessor::busRead(); @@ -78,18 +80,20 @@ uint8_t EightBit::Z80::busRead() { void EightBit::Z80::handleRESET() { IntelProcessor::handleRESET(); di(); + IV() = REFRESH() = 0; + SP().word = AF().word = Mask16; tick(3); } void EightBit::Z80::handleNMI() { raiseNMI(); raiseHALT(); + IFF2() = IFF1(); IFF1() = false; lowerM1(); const auto discarded = BUS().DATA(); raiseM1(); restart(0x66); - tick(13); } void EightBit::Z80::handleINT() { @@ -100,17 +104,18 @@ void EightBit::Z80::handleINT() { raiseIORQ(); raiseM1(); di(); + tick(5); switch (IM()) { case 0: // i8080 equivalent execute(data); break; case 1: + tick(); restart(7 << 3); - tick(13); break; case 2: - call(MEMPTR() = register16_t(data, IV())); - tick(19); + tick(7); + call(MEMPTR() = { data, IV() }); break; default: UNREACHABLE; @@ -143,56 +148,50 @@ uint8_t EightBit::Z80::decrement(uint8_t& f, const uint8_t operand) { return result; } -bool EightBit::Z80::jrConditionalFlag(const uint8_t f, const int flag) { +bool EightBit::Z80::convertCondition(const uint8_t f, int flag) { ASSUME(flag >= 0); - ASSUME(flag <= 3); + ASSUME(flag <= 7); switch (flag) { - case 0: // NZ - return jrConditional(!(f & ZF)); - case 1: // Z - return jrConditional(f & ZF); - case 2: // NC - return jrConditional(!(f & CF)); - case 3: // C - return jrConditional(f & CF); + case 0: + return !(f & ZF); + case 1: + return f & ZF; + case 2: + return !(f & CF); + case 3: + return f & CF; + case 4: + return !(f & PF); + case 5: + return f & PF; + case 6: + return !(f & SF); + case 7: + return f & SF; default: UNREACHABLE; } } -void EightBit::Z80::jumpConditionalFlag(const uint8_t f, const int flag) { - ASSUME(flag >= 0); - ASSUME(flag <= 7); - switch (flag) { - case 0: // NZ - jumpConditional(!(f & ZF)); - break; - case 1: // Z - jumpConditional(f & ZF); - break; - case 2: // NC - jumpConditional(!(f & CF)); - break; - case 3: // C - jumpConditional(f & CF); - break; - case 4: // PO - jumpConditional(!(f & PF)); - break; - case 5: // PE - jumpConditional(f & PF); - break; - case 6: // P - jumpConditional(!(f & SF)); - break; - case 7: // M - jumpConditional(f & SF); - break; - default: - UNREACHABLE; +void EightBit::Z80::returnConditionalFlag(const uint8_t f, const int flag) { + if (convertCondition(f, flag)) { + tick(); + ret(); } } +void EightBit::Z80::jrConditionalFlag(const uint8_t f, const int flag) { + jrConditional(convertCondition(f, flag)); +} + +void EightBit::Z80::jumpConditionalFlag(const uint8_t f, const int flag) { + jumpConditional(convertCondition(f, flag)); +} + +void EightBit::Z80::callConditionalFlag(const uint8_t f, const int flag) { + callConditional(convertCondition(f, flag)); +} + void EightBit::Z80::retn() { ret(); IFF1() = IFF2(); @@ -202,56 +201,6 @@ void EightBit::Z80::reti() { retn(); } -bool EightBit::Z80::returnConditionalFlag(const uint8_t f, const int flag) { - ASSUME(flag >= 0); - ASSUME(flag <= 7); - switch (flag) { - case 0: // NZ - return returnConditional(!(f & ZF)); - case 1: // Z - return returnConditional(f & ZF); - case 2: // NC - return returnConditional(!(f & CF)); - case 3: // C - return returnConditional(f & CF); - case 4: // PO - return returnConditional(!(f & PF)); - case 5: // PE - return returnConditional(f & PF); - case 6: // P - return returnConditional(!(f & SF)); - case 7: // M - return returnConditional(f & SF); - default: - UNREACHABLE; - } -} - -bool EightBit::Z80::callConditionalFlag(const uint8_t f, const int flag) { - ASSUME(flag >= 0); - ASSUME(flag <= 7); - switch (flag) { - case 0: // NZ - return callConditional(!(f & ZF)); - case 1: // Z - return callConditional(f & ZF); - case 2: // NC - return callConditional(!(f & CF)); - case 3: // C - return callConditional(f & CF); - case 4: // PO - return callConditional(!(f & PF)); - case 5: // PE - return callConditional(f & PF); - case 6: // P - return callConditional(!(f & SF)); - case 7: // M - return callConditional(f & SF); - default: - UNREACHABLE; - } -} - EightBit::register16_t EightBit::Z80::sbc(uint8_t& f, const register16_t operand, const register16_t value) { const auto subtraction = operand.word - value.word - (f & CF); @@ -534,7 +483,8 @@ void EightBit::Z80::ccf(uint8_t& f, const uint8_t operand) { void EightBit::Z80::xhtl(register16_t& exchange) { MEMPTR().low = IntelProcessor::busRead(SP()); ++BUS().ADDRESS(); - MEMPTR().high = IntelProcessor::busRead(); + MEMPTR().high = busRead(); + tick(); IntelProcessor::busWrite(exchange.high); exchange.high = MEMPTR().high; --BUS().ADDRESS(); @@ -609,7 +559,9 @@ bool EightBit::Z80::lddr(uint8_t& f, const uint8_t a) { void EightBit::Z80::blockIn(register16_t& source, const register16_t destination) { MEMPTR() = BUS().ADDRESS() = source; + tick(); const auto value = readPort(); + tick(3); IntelProcessor::busWrite(destination, value); source.high = decrement(F(), source.high); F() = setBit(F(), NF); @@ -636,6 +588,7 @@ bool EightBit::Z80::indr() { } void EightBit::Z80::blockOut(const register16_t source, register16_t& destination) { + tick(); const auto value = IntelProcessor::busRead(source); destination.high = decrement(F(), destination.high); BUS().ADDRESS() = destination; @@ -669,6 +622,7 @@ bool EightBit::Z80::otdr() { void EightBit::Z80::rrd(uint8_t& f, register16_t address, uint8_t& update) { (MEMPTR() = BUS().ADDRESS() = address)++; const auto memory = busRead(); + tick(4); IntelProcessor::busWrite(promoteNibble(update) | highNibble(memory)); update = higherNibble(update) | lowerNibble(memory); f = adjustSZPXY(f, update); @@ -678,6 +632,7 @@ void EightBit::Z80::rrd(uint8_t& f, register16_t address, uint8_t& update) { void EightBit::Z80::rld(uint8_t& f, register16_t address, uint8_t& update) { (MEMPTR() = BUS().ADDRESS() = address)++; const auto memory = busRead(); + tick(4); IntelProcessor::busWrite(promoteNibble(memory) | lowNibble(update)); update = higherNibble(update) | highNibble(memory); f = adjustSZPXY(f, update); @@ -685,13 +640,14 @@ void EightBit::Z80::rld(uint8_t& f, register16_t address, uint8_t& update) { } void EightBit::Z80::writePort(const uint8_t port) { - MEMPTR() = BUS().ADDRESS() = register16_t(port, A()); + MEMPTR() = BUS().ADDRESS() = { port, A() }; BUS().DATA() = A(); writePort(); ++MEMPTR().low; } void EightBit::Z80::writePort() { + tick(); lowerIORQ(); lowerWR(); m_ports.write(BUS().ADDRESS().low, BUS().DATA()); @@ -700,12 +656,13 @@ void EightBit::Z80::writePort() { } uint8_t EightBit::Z80::readPort(const uint8_t port) { - MEMPTR() = BUS().ADDRESS() = register16_t(port, A()); + MEMPTR() = BUS().ADDRESS() = { port, A() }; ++MEMPTR().low; return readPort(); } uint8_t EightBit::Z80::readPort() { + tick(); lowerIORQ(); lowerRD(); const auto returned = BUS().DATA() = m_ports.read(BUS().ADDRESS().low); @@ -782,10 +739,19 @@ int EightBit::Z80::execute() { } void EightBit::Z80::executeCB(const int x, const int y, const int z) { - const bool memoryY = y == 6; + const bool memoryZ = z == 6; const bool indirect = (!m_displaced && memoryZ) || m_displaced; - auto operand = m_displaced ? IntelProcessor::busRead(displacedAddress()) : R(z); + const bool direct = !indirect; + + uint8_t operand; + if (m_displaced) { + tick(2); + operand = IntelProcessor::busRead(displacedAddress()); + } else { + operand = R(z); + } + const bool update = x != 1; // BIT does not update switch (x) { case 0: { // rot[y] r[z] @@ -818,46 +784,34 @@ void EightBit::Z80::executeCB(const int x, const int y, const int z) { UNREACHABLE; } F() = adjustSZP(F(), operand); - tick(4); break; } case 1: // BIT y, r[z] - tick(4); bit(F(), y, operand); - if (indirect) { - F() = adjustXY(F(), MEMPTR().high); - tick(4); - } else { - F() = adjustXY(F(), operand); - } + F() = adjustXY(F(), direct ? operand : MEMPTR().high); break; case 2: // RES y, r[z] - tick(4); operand = res(y, operand); break; case 3: // SET y, r[z] - tick(4); operand = set(y, operand); break; default: UNREACHABLE; } if (update) { + tick(); if (m_displaced) { IntelProcessor::busWrite(operand); if (!memoryZ) R2(z, operand); - tick(15); } else { R(z, operand); - if (memoryZ) - tick(7); } } } void EightBit::Z80::executeED(const int x, const int y, const int z, const int p, const int q) { - const bool memoryY = y == 6; - const bool memoryZ = z == 6; + switch (x) { case 0: case 3: // Invalid instruction, equivalent to NONI followed by NOP @@ -871,16 +825,11 @@ void EightBit::Z80::executeED(const int x, const int y, const int z, const int p R(y, BUS().DATA()); F() = adjustSZPXY(F(), BUS().DATA()); F() = clearBit(F(), NF | HC); - tick(4); break; case 1: // Output to port with 16-bit address (MEMPTR() = BUS().ADDRESS() = BC())++; - if (y == 6) // OUT (C),0 - BUS().DATA() = 0; - else // OUT (C),r[y] - BUS().DATA() = R(y); + BUS().DATA() = y == 6 ? 0 : R(y); writePort(); - tick(4); break; case 2: // 16-bit add/subtract with carry switch (q) { @@ -893,7 +842,6 @@ 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(); @@ -907,7 +855,6 @@ void EightBit::Z80::executeED(const int x, const int y, const int z, const int p default: UNREACHABLE; } - tick(12); break; case 4: // Negate accumulator A() = neg(F(), A()); @@ -921,7 +868,6 @@ void EightBit::Z80::executeED(const int x, const int y, const int z, const int p retn(); // RETN break; } - tick(6); break; case 6: // Set interrupt mode switch (y) { @@ -947,31 +893,25 @@ void EightBit::Z80::executeED(const int x, const int y, const int z, const int p switch (y) { case 0: // LD I,A IV() = A(); - tick(); break; case 1: // LD R,A REFRESH() = A(); - tick(); break; case 2: // LD A,I F() = adjustSZXY(F(), A() = IV()); F() = clearBit(F(), NF | HC); F() = setBit(F(), PF, IFF2()); - tick(); break; case 3: // LD A,R F() = adjustSZXY(F(), A() = REFRESH()); F() = clearBit(F(), NF | HC); F() = setBit(F(), PF, IFF2()); - tick(); break; case 4: // RRD rrd(F(), HL(), A()); - tick(10); break; case 5: // RLD rld(F(), HL(), A()); - tick(10); break; case 6: // NOP case 7: // NOP @@ -998,15 +938,15 @@ void EightBit::Z80::executeED(const int x, const int y, const int z, const int p if (ldir(F(), A())) { MEMPTR() = --PC(); --PC(); - tick(5); } + tick(7); break; case 7: // LDDR if (lddr(F(), A())) { MEMPTR() = --PC(); --PC(); - tick(5); } + tick(7); break; } break; @@ -1024,15 +964,17 @@ void EightBit::Z80::executeED(const int x, const int y, const int z, const int p --PC(); tick(5); } + tick(5); break; case 7: // CPDR if (cpdr(F(), A())) { MEMPTR() = --PC(); --PC(); - tick(5); + tick(3); } else { MEMPTR() = PC() - 2; } + tick(7); break; } break; @@ -1071,17 +1013,18 @@ void EightBit::Z80::executeED(const int x, const int y, const int z, const int p PC() -= 2; tick(5); } + tick(3); break; case 7: // OTDR if (otdr()) { PC() -= 2; tick(5); } + tick(3); break; } break; } - tick(8); break; } } @@ -1100,21 +1043,19 @@ void EightBit::Z80::executeOther(const int x, const int y, const int z, const in exxAF(); break; case 2: // DJNZ d + tick(); if (jrConditional(--B())) - tick(5); - tick(4); + tick(2); + tick(3); break; case 3: // JR d jr(fetchByte()); - tick(8); break; case 4: // JR cc,d case 5: case 6: case 7: - if (jrConditionalFlag(F(), y - 4)) - tick(5); - tick(3); + jrConditionalFlag(F(), y - 4); break; default: UNREACHABLE; @@ -1124,11 +1065,9 @@ void EightBit::Z80::executeOther(const int x, const int y, const int z, const in switch (q) { case 0: // LD rp,nn RP(p) = fetchWord(); - tick(6); break; case 1: // ADD HL,rp HL2() = add(F(), HL2(), RP(p)); - tick(7); break; default: UNREACHABLE; @@ -1142,24 +1081,20 @@ void EightBit::Z80::executeOther(const int x, const int y, const int z, const in (MEMPTR() = BUS().ADDRESS() = BC())++; MEMPTR().high = BUS().DATA() = A(); busWrite(); - tick(3); break; case 1: // LD (DE),A (MEMPTR() = BUS().ADDRESS() = DE())++; MEMPTR().high = BUS().DATA() = A(); busWrite(); - tick(3); break; case 2: // LD (nn),HL BUS().ADDRESS() = fetchWord(); setWord(HL2()); - tick(12); break; case 3: // LD (nn),A (MEMPTR() = BUS().ADDRESS() = fetchWord())++; MEMPTR().high = BUS().DATA() = A(); busWrite(); - tick(9); break; default: UNREACHABLE; @@ -1170,22 +1105,18 @@ void EightBit::Z80::executeOther(const int x, const int y, const int z, const in case 0: // LD A,(BC) (MEMPTR() = BUS().ADDRESS() = BC())++; A() = busRead(); - tick(3); break; case 1: // LD A,(DE) (MEMPTR() = BUS().ADDRESS() = DE())++; A() = busRead(); - tick(3); break; case 2: // LD HL,(nn) BUS().ADDRESS() = fetchWord(); HL2() = getWord(); - tick(12); break; case 3: // LD A,(nn) (MEMPTR() = BUS().ADDRESS() = fetchWord())++; A() = busRead(); - tick(9); break; default: UNREACHABLE; @@ -1206,30 +1137,36 @@ void EightBit::Z80::executeOther(const int x, const int y, const int z, const in default: UNREACHABLE; } - tick(2); break; - case 4: // 8-bit INC - if (m_displaced && memoryY) + case 4: { // 8-bit INC + if (memoryY && m_displaced) { fetchDisplacement(); - R(y, increment(F(), R(y))); - break; - case 5: // 8-bit DEC - if (memoryY) { - tick(7); - if (m_displaced) - fetchDisplacement(); + tick(5); } - R(y, decrement(F(), R(y))); + const auto original = R(y); + tick(); + R(y, increment(F(), original)); break; - case 6: // 8-bit load immediate - if (memoryY) { - tick(3); - if (m_displaced) - fetchDisplacement(); + } + case 5: { // 8-bit DEC + if (memoryY && m_displaced) { + fetchDisplacement(); + tick(5); } - R(y, fetchByte()); // LD r,n - tick(3); + const auto original = R(y); + tick(); + R(y, decrement(F(), original)); break; + } + case 6: { // 8-bit load immediate + if (memoryY && m_displaced) + fetchDisplacement(); + const auto value = fetchByte(); + if (m_displaced) + tick(2); + R(y, value); // LD r,n + break; + } case 7: // Assorted operations on accumulator/flags switch (y) { case 0: @@ -1275,10 +1212,14 @@ void EightBit::Z80::executeOther(const int x, const int y, const int z, const in if (memoryZ) { switch (y) { case 4: + if (m_displaced) + tick(5); H() = R(z); normal = false; break; case 5: + if (m_displaced) + tick(5); L() = R(z); normal = false; break; @@ -1287,27 +1228,31 @@ void EightBit::Z80::executeOther(const int x, const int y, const int z, const in if (memoryY) { switch (z) { case 4: + if (m_displaced) + tick(5); R(y, H()); normal = false; break; case 5: + if (m_displaced) + tick(5); R(y, L()); normal = false; break; } } } - if (normal) + if (normal) { + if (m_displaced) + tick(5); R(y, R(z)); - if (memoryY || memoryZ) // M operations - tick(3); + } } break; case 2: { // Operate on accumulator and register/memory location - if (memoryZ) { - tick(3); - if (m_displaced) - fetchDisplacement(); + if (memoryZ && m_displaced) { + fetchDisplacement(); + tick(5); } const auto value = R(z); switch (y) { @@ -1343,21 +1288,17 @@ void EightBit::Z80::executeOther(const int x, const int y, const int z, const in case 3: switch (z) { case 0: // Conditional return - if (returnConditionalFlag(F(), y)) - tick(6); - tick(1); + returnConditionalFlag(F(), y); break; case 1: // POP & various ops switch (q) { case 0: // POP rp2[p] RP2(p) = popWord(); - tick(6); break; case 1: switch (p) { case 0: // RET ret(); - tick(6); break; case 1: // EXX exx(); @@ -1378,13 +1319,11 @@ void EightBit::Z80::executeOther(const int x, const int y, const int z, const in break; case 2: // Conditional jump jumpConditionalFlag(F(), y); - tick(6); break; case 3: // Assorted operations switch (y) { case 0: // JP nn jump(MEMPTR() = fetchWord()); - tick(6); break; case 1: // CB prefix m_prefixCB = true; @@ -1397,15 +1336,12 @@ void EightBit::Z80::executeOther(const int x, const int y, const int z, const in break; case 2: // OUT (n),A writePort(fetchByte()); - tick(7); break; case 3: // IN A,(n) A() = readPort(fetchByte()); - tick(7); break; case 4: // EX (SP),HL xhtl(HL2()); - tick(15); break; case 5: // EX DE,HL std::swap(DE(), HL()); @@ -1421,21 +1357,18 @@ void EightBit::Z80::executeOther(const int x, const int y, const int z, const in } break; case 4: // Conditional call: CALL cc[y], nn - if (callConditionalFlag(F(), y)) - tick(7); - tick(6); + callConditionalFlag(F(), y); break; case 5: // PUSH & various ops switch (q) { case 0: // PUSH rp2[p] + tick(); pushWord(RP2(p)); - tick(7); break; case 1: switch (p) { case 0: // CALL nn call(MEMPTR() = fetchWord()); - tick(13); break; case 1: // DD prefix m_displaced = m_prefixDD = true; @@ -1487,12 +1420,10 @@ void EightBit::Z80::executeOther(const int x, const int y, const int z, const in default: UNREACHABLE; } - tick(3); break; } case 7: // Restart: RST y * 8 restart(y << 3); - tick(7); break; default: UNREACHABLE; diff --git a/inc/IntelProcessor.h b/inc/IntelProcessor.h index abc7368..be39d7b 100644 --- a/inc/IntelProcessor.h +++ b/inc/IntelProcessor.h @@ -138,20 +138,20 @@ namespace EightBit { MEMPTR() = fetchWord(); if (condition) call(MEMPTR()); - return !!condition; + return condition; } auto jumpConditional(const int condition) { MEMPTR() = fetchWord(); if (condition) jump(MEMPTR()); - return !!condition; + return condition; } auto returnConditional(const int condition) { if (condition) ret(); - return !!condition; + return condition; } void jr(const int8_t offset) noexcept { @@ -164,7 +164,7 @@ namespace EightBit { const auto offset = busRead(offsetAddress); jr(offset); } - return !!condition; + return condition; } void ret() final; diff --git a/inc/Processor.h b/inc/Processor.h index 858595f..4303e8a 100644 --- a/inc/Processor.h +++ b/inc/Processor.h @@ -88,7 +88,7 @@ namespace EightBit { PC() = destination; } - void call(const register16_t destination) { + virtual void call(const register16_t destination) { pushWord(PC()); jump(destination); } diff --git a/src/BigEndianProcessor.cpp b/src/BigEndianProcessor.cpp index b5b86d3..b972b89 100644 --- a/src/BigEndianProcessor.cpp +++ b/src/BigEndianProcessor.cpp @@ -8,7 +8,7 @@ EightBit::register16_t EightBit::BigEndianProcessor::getWord() { const auto high = busRead(); ++BUS().ADDRESS(); const auto low = busRead(); - return register16_t(low, high); + return { low, high }; } void EightBit::BigEndianProcessor::setWord(const register16_t value) { @@ -21,7 +21,7 @@ EightBit::register16_t EightBit::BigEndianProcessor::getWordPaged(const uint8_t const auto high = getBytePaged(page, offset); ++BUS().ADDRESS().low; const auto low = busRead(); - return register16_t(low, high); + return { low, high }; } void EightBit::BigEndianProcessor::setWordPaged(const uint8_t page, const uint8_t offset, const register16_t value) { @@ -33,7 +33,7 @@ void EightBit::BigEndianProcessor::setWordPaged(const uint8_t page, const uint8_ EightBit::register16_t EightBit::BigEndianProcessor::fetchWord() { const auto high = fetchByte(); const auto low = fetchByte(); - return register16_t(low, high); + return { low, high }; } void EightBit::BigEndianProcessor::pushWord(const register16_t value) { @@ -44,13 +44,13 @@ void EightBit::BigEndianProcessor::pushWord(const register16_t value) { EightBit::register16_t EightBit::BigEndianProcessor::popWord() { const auto high = pop(); const auto low = pop(); - return register16_t(low, high); + return { low, high }; } EightBit::register16_t EightBit::BigEndianProcessor::peekWord(const register16_t address) { const auto high = BUS().peek(address); const auto low = BUS().peek(address + 1); - return register16_t(low, high); + return { low, high }; } void EightBit::BigEndianProcessor::pokeWord(const register16_t address, const register16_t value) { diff --git a/src/Bus.cpp b/src/Bus.cpp index a9c801b..cfb7507 100644 --- a/src/Bus.cpp +++ b/src/Bus.cpp @@ -41,7 +41,9 @@ void EightBit::Bus::loadHexFile(const std::string path) { uint8_t& EightBit::Bus::reference(const uint16_t address) { const auto mapped = mapping(address); const uint16_t offset = (address - mapped.begin) & mapped.mask; - if (mapped.access == MemoryMapping::AccessLevel::ReadOnly) - return DATA() = mapped.memory.peek(offset); + if (mapped.access == MemoryMapping::AccessLevel::ReadOnly) { + DATA() = mapped.memory.peek(offset); + return DATA(); + } return mapped.memory.reference(offset); } diff --git a/src/LittleEndianProcessor.cpp b/src/LittleEndianProcessor.cpp index 843f350..3b78fa5 100644 --- a/src/LittleEndianProcessor.cpp +++ b/src/LittleEndianProcessor.cpp @@ -8,7 +8,7 @@ EightBit::register16_t EightBit::LittleEndianProcessor::getWord() { const auto low = busRead(); ++BUS().ADDRESS(); const auto high = busRead(); - return register16_t(low, high); + return { low, high }; } void EightBit::LittleEndianProcessor::setWord(const register16_t value) { @@ -21,7 +21,7 @@ EightBit::register16_t EightBit::LittleEndianProcessor::getWordPaged(const uint8 const auto low = getBytePaged(page, offset); ++BUS().ADDRESS().low; const auto high = busRead(); - return register16_t(low, high); + return { low, high }; } void EightBit::LittleEndianProcessor::setWordPaged(const uint8_t page, const uint8_t offset, const register16_t value) { @@ -33,7 +33,7 @@ void EightBit::LittleEndianProcessor::setWordPaged(const uint8_t page, const uin EightBit::register16_t EightBit::LittleEndianProcessor::fetchWord() { const auto low = fetchByte(); const auto high = fetchByte(); - return register16_t(low, high); + return { low, high }; } void EightBit::LittleEndianProcessor::pushWord(const register16_t value) { @@ -44,13 +44,13 @@ void EightBit::LittleEndianProcessor::pushWord(const register16_t value) { EightBit::register16_t EightBit::LittleEndianProcessor::popWord() { const auto low = pop(); const auto high = pop(); - return register16_t(low, high); + return { low, high }; } EightBit::register16_t EightBit::LittleEndianProcessor::peekWord(const register16_t address) { const auto low = BUS().peek(address); const auto high = BUS().peek(address + 1); - return register16_t(low, high); + return { low, high }; } void EightBit::LittleEndianProcessor::pokeWord(const register16_t address, const register16_t value) {