M-Cycle accurate Z80 modifications.

Signed-off-by: Adrian Conlon <Adrian.conlon@gmail.com>
This commit is contained in:
Adrian Conlon 2019-12-29 01:18:54 +00:00
parent feee7ec2e6
commit d9466082ec
14 changed files with 283 additions and 238 deletions

View File

@ -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);

View File

@ -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));

View File

@ -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);

View File

@ -61,7 +61,7 @@ void EightBit::GameBoy::LR35902::decrement(uint8_t& operand) {
F() = adjustZero<LR35902>(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));

View File

@ -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 };
}
////

View File

@ -2,10 +2,48 @@
#include "FuseTestRunner.h"
#include "Disassembler.h"
Fuse::TestRunner::TestRunner(const Test& test, const ExpectedTestResult& expected)
#include <boost/algorithm/string/predicate.hpp>
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<TestEvent>& 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();

View File

@ -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<TestEvent>& 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 };

View File

@ -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);

View File

@ -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<Z80>(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<Z80>(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<Z80>(F(), operand);
tick(4);
break;
} case 1: // BIT y, r[z]
tick(4);
bit(F(), y, operand);
if (indirect) {
F() = adjustXY<Z80>(F(), MEMPTR().high);
tick(4);
} else {
F() = adjustXY<Z80>(F(), operand);
}
F() = adjustXY<Z80>(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<Z80>(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<Z80>(F(), A() = IV());
F() = clearBit(F(), NF | HC);
F() = setBit(F(), PF, IFF2());
tick();
break;
case 3: // LD A,R
F() = adjustSZXY<Z80>(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;

View File

@ -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;

View File

@ -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);
}

View File

@ -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) {

View File

@ -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);
}

View File

@ -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) {