mirror of
https://github.com/MoleskiCoder/EightBit.git
synced 2024-08-31 09:29:27 +00:00
M-Cycle accurate Z80 modifications.
Signed-off-by: Adrian Conlon <Adrian.conlon@gmail.com>
This commit is contained in:
parent
feee7ec2e6
commit
d9466082ec
@ -152,9 +152,9 @@ namespace EightBit {
|
|||||||
void di();
|
void di();
|
||||||
void ei();
|
void ei();
|
||||||
|
|
||||||
bool returnConditionalFlag(int flag);
|
int returnConditionalFlag(int flag);
|
||||||
bool jumpConditionalFlag(int flag);
|
int jumpConditionalFlag(int flag);
|
||||||
bool callConditionalFlag(int flag);
|
int callConditionalFlag(int flag);
|
||||||
|
|
||||||
void add(register16_t value);
|
void add(register16_t value);
|
||||||
|
|
||||||
|
@ -56,7 +56,7 @@ void EightBit::Intel8080::decrement(uint8_t& operand) {
|
|||||||
F() = setBit(F(), AC, lowNibble(operand) != Mask4);
|
F() = setBit(F(), AC, lowNibble(operand) != Mask4);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EightBit::Intel8080::jumpConditionalFlag(const int flag) {
|
int EightBit::Intel8080::jumpConditionalFlag(const int flag) {
|
||||||
switch (flag) {
|
switch (flag) {
|
||||||
case 0: // NZ
|
case 0: // NZ
|
||||||
return jumpConditional(!(F() & ZF));
|
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) {
|
switch (flag) {
|
||||||
case 0: // NZ
|
case 0: // NZ
|
||||||
return returnConditional(!(F() & ZF));
|
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) {
|
switch (flag) {
|
||||||
case 0: // NZ
|
case 0: // NZ
|
||||||
return callConditional(!(F() & ZF));
|
return callConditional(!(F() & ZF));
|
||||||
|
@ -174,10 +174,10 @@ namespace EightBit {
|
|||||||
|
|
||||||
void reti();
|
void reti();
|
||||||
|
|
||||||
bool jrConditionalFlag(int flag);
|
int jrConditionalFlag(int flag);
|
||||||
bool returnConditionalFlag(int flag);
|
int returnConditionalFlag(int flag);
|
||||||
bool jumpConditionalFlag(int flag);
|
int jumpConditionalFlag(int flag);
|
||||||
bool callConditionalFlag(int flag);
|
int callConditionalFlag(int flag);
|
||||||
|
|
||||||
void add(register16_t& operand, register16_t value);
|
void add(register16_t& operand, register16_t value);
|
||||||
|
|
||||||
|
@ -61,7 +61,7 @@ void EightBit::GameBoy::LR35902::decrement(uint8_t& operand) {
|
|||||||
F() = adjustZero<LR35902>(F(), --operand);
|
F() = adjustZero<LR35902>(F(), --operand);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EightBit::GameBoy::LR35902::jrConditionalFlag(const int flag) {
|
int EightBit::GameBoy::LR35902::jrConditionalFlag(const int flag) {
|
||||||
switch (flag) {
|
switch (flag) {
|
||||||
case 0: // NZ
|
case 0: // NZ
|
||||||
return jrConditional(!(F() & ZF));
|
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) {
|
switch (flag) {
|
||||||
case 0: // NZ
|
case 0: // NZ
|
||||||
return jumpConditional(!(F() & ZF));
|
return jumpConditional(!(F() & ZF));
|
||||||
@ -96,7 +96,7 @@ void EightBit::GameBoy::LR35902::reti() {
|
|||||||
ei();
|
ei();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EightBit::GameBoy::LR35902::returnConditionalFlag(const int flag) {
|
int EightBit::GameBoy::LR35902::returnConditionalFlag(const int flag) {
|
||||||
switch (flag) {
|
switch (flag) {
|
||||||
case 0: // NZ
|
case 0: // NZ
|
||||||
return returnConditional(!(F() & ZF));
|
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) {
|
switch (flag) {
|
||||||
case 0: // NZ
|
case 0: // NZ
|
||||||
return callConditional(!(F() & ZF));
|
return callConditional(!(F() & ZF));
|
||||||
|
@ -402,7 +402,7 @@ uint8_t EightBit::MOS6502::pop() {
|
|||||||
void EightBit::MOS6502::dummyPush(const uint8_t value) {
|
void EightBit::MOS6502::dummyPush(const uint8_t value) {
|
||||||
tick();
|
tick();
|
||||||
BUS().DATA() = value;
|
BUS().DATA() = value;
|
||||||
BUS().ADDRESS() = register16_t(S()--, 1);
|
BUS().ADDRESS() = { S()--, 1 };
|
||||||
}
|
}
|
||||||
|
|
||||||
////
|
////
|
||||||
|
@ -2,10 +2,48 @@
|
|||||||
#include "FuseTestRunner.h"
|
#include "FuseTestRunner.h"
|
||||||
#include "Disassembler.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_test(test),
|
||||||
m_expected(expected),
|
m_result(result),
|
||||||
m_cpu(*this, m_ports) {
|
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() {
|
void Fuse::TestRunner::check() {
|
||||||
checkregisters();
|
checkRegisters();
|
||||||
checkMemory();
|
checkMemory();
|
||||||
|
checkEvents();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Fuse::TestRunner::dumpDifference(const std::string& description, uint8_t actual, uint8_t expected) const {
|
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);
|
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;
|
const auto& expectedRegisters = expectedState.registers;
|
||||||
|
|
||||||
auto af = m_cpu.AF() == expectedRegisters[Fuse::RegisterState::AF];
|
auto af = m_cpu.AF() == expectedRegisters[Fuse::RegisterState::AF];
|
||||||
@ -306,7 +345,7 @@ void Fuse::TestRunner::checkMemory() {
|
|||||||
|
|
||||||
bool first = true;
|
bool first = true;
|
||||||
|
|
||||||
for (auto memoryDatum : m_expected.memoryData) {
|
for (auto memoryDatum : m_result.memoryData) {
|
||||||
auto bytes = memoryDatum.bytes;
|
auto bytes = memoryDatum.bytes;
|
||||||
for (int i = 0; i < bytes.size(); ++i) {
|
for (int i = 0; i < bytes.size(); ++i) {
|
||||||
auto expected = bytes[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() {
|
void Fuse::TestRunner::run() {
|
||||||
raisePOWER();
|
raisePOWER();
|
||||||
initialise();
|
initialise();
|
||||||
|
@ -12,7 +12,10 @@ namespace Fuse {
|
|||||||
class TestRunner : public EightBit::Bus {
|
class TestRunner : public EightBit::Bus {
|
||||||
private:
|
private:
|
||||||
const Test& m_test;
|
const Test& m_test;
|
||||||
const ExpectedTestResult& m_expected;
|
const ExpectedTestResult& m_result;
|
||||||
|
|
||||||
|
TestEvents m_expectedEvents;
|
||||||
|
TestEvents m_actualEvents;
|
||||||
|
|
||||||
bool m_failed = false;
|
bool m_failed = false;
|
||||||
bool m_unimplemented = false;
|
bool m_unimplemented = false;
|
||||||
@ -21,12 +24,15 @@ namespace Fuse {
|
|||||||
EightBit::InputOutput m_ports;
|
EightBit::InputOutput m_ports;
|
||||||
EightBit::Z80 m_cpu;
|
EightBit::Z80 m_cpu;
|
||||||
|
|
||||||
|
int m_totalCycles;
|
||||||
|
|
||||||
void initialiseRegisters();
|
void initialiseRegisters();
|
||||||
void initialiseMemory();
|
void initialiseMemory();
|
||||||
|
|
||||||
void check();
|
void check();
|
||||||
void checkregisters();
|
void checkRegisters();
|
||||||
void checkMemory();
|
void checkMemory();
|
||||||
|
void checkEvents();
|
||||||
|
|
||||||
void dumpDifference(const std::string& description, uint8_t high, uint8_t low) const;
|
void dumpDifference(const std::string& description, uint8_t high, uint8_t low) const;
|
||||||
void dumpDifference(
|
void dumpDifference(
|
||||||
@ -34,6 +40,13 @@ namespace Fuse {
|
|||||||
const std::string& lowDescription,
|
const std::string& lowDescription,
|
||||||
EightBit::register16_t actual, EightBit::register16_t expected) const;
|
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:
|
protected:
|
||||||
virtual EightBit::MemoryMapping mapping(uint16_t address) final {
|
virtual EightBit::MemoryMapping mapping(uint16_t address) final {
|
||||||
return { m_ram, 0x0000, 0xffff, EightBit::MemoryMapping::AccessLevel::ReadWrite };
|
return { m_ram, 0x0000, 0xffff, EightBit::MemoryMapping::AccessLevel::ReadWrite };
|
||||||
|
@ -113,6 +113,11 @@ namespace EightBit {
|
|||||||
void handleRESET() final;
|
void handleRESET() final;
|
||||||
void handleINT() final;
|
void handleINT() final;
|
||||||
|
|
||||||
|
void call(const register16_t destination) override {
|
||||||
|
tick();
|
||||||
|
IntelProcessor::call(destination);
|
||||||
|
}
|
||||||
|
|
||||||
void busWrite() final;
|
void busWrite() final;
|
||||||
uint8_t busRead() 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
|
// refresh dynamic memories. The CPU uses this time to decode and execute the fetched
|
||||||
// instruction so that no other concurrent operation can be performed.
|
// instruction so that no other concurrent operation can be performed.
|
||||||
uint8_t readInitialOpCode() {
|
uint8_t readInitialOpCode() {
|
||||||
|
tick();
|
||||||
lowerM1();
|
lowerM1();
|
||||||
const auto returned = IntelProcessor::busRead(PC());
|
const auto returned = IntelProcessor::busRead(PC());
|
||||||
raiseM1();
|
raiseM1();
|
||||||
tick(2);
|
|
||||||
BUS().ADDRESS().low = REFRESH();
|
BUS().ADDRESS().low = REFRESH();
|
||||||
BUS().ADDRESS().high = IV();
|
BUS().ADDRESS().high = IV();
|
||||||
lowerRFSH();
|
lowerRFSH();
|
||||||
lowerMREQ();
|
lowerMREQ();
|
||||||
tick();
|
|
||||||
raiseMREQ();
|
raiseMREQ();
|
||||||
raiseRFSH();
|
raiseRFSH();
|
||||||
tick();
|
|
||||||
return returned;
|
return returned;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -350,6 +353,8 @@ namespace EightBit {
|
|||||||
return setBit(f, VF, overflow);
|
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);
|
static uint8_t subtract(uint8_t& f, uint8_t operand, uint8_t value, int carry = 0);
|
||||||
|
|
||||||
void executeCB(int x, int y, int z);
|
void executeCB(int x, int y, int z);
|
||||||
@ -365,9 +370,9 @@ namespace EightBit {
|
|||||||
void retn();
|
void retn();
|
||||||
void reti();
|
void reti();
|
||||||
|
|
||||||
[[nodiscard]] bool jrConditionalFlag(uint8_t f, int flag);
|
void returnConditionalFlag(uint8_t f, int flag);
|
||||||
[[nodiscard]] bool returnConditionalFlag(uint8_t f, int flag);
|
void jrConditionalFlag(uint8_t f, int flag);
|
||||||
[[nodiscard]] bool callConditionalFlag(uint8_t f, int flag);
|
void callConditionalFlag(uint8_t f, int flag);
|
||||||
void jumpConditionalFlag(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);
|
[[nodiscard]] register16_t sbc(uint8_t& f, register16_t operand, register16_t value);
|
||||||
|
313
Z80/src/Z80.cpp
313
Z80/src/Z80.cpp
@ -59,6 +59,7 @@ EightBit::register16_t& EightBit::Z80::HL() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void EightBit::Z80::busWrite() {
|
void EightBit::Z80::busWrite() {
|
||||||
|
tick(3);
|
||||||
lowerMREQ();
|
lowerMREQ();
|
||||||
lowerWR();
|
lowerWR();
|
||||||
IntelProcessor::busWrite();
|
IntelProcessor::busWrite();
|
||||||
@ -67,6 +68,7 @@ void EightBit::Z80::busWrite() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
uint8_t EightBit::Z80::busRead() {
|
uint8_t EightBit::Z80::busRead() {
|
||||||
|
tick(3);
|
||||||
lowerMREQ();
|
lowerMREQ();
|
||||||
lowerRD();
|
lowerRD();
|
||||||
const auto returned = IntelProcessor::busRead();
|
const auto returned = IntelProcessor::busRead();
|
||||||
@ -78,18 +80,20 @@ uint8_t EightBit::Z80::busRead() {
|
|||||||
void EightBit::Z80::handleRESET() {
|
void EightBit::Z80::handleRESET() {
|
||||||
IntelProcessor::handleRESET();
|
IntelProcessor::handleRESET();
|
||||||
di();
|
di();
|
||||||
|
IV() = REFRESH() = 0;
|
||||||
|
SP().word = AF().word = Mask16;
|
||||||
tick(3);
|
tick(3);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EightBit::Z80::handleNMI() {
|
void EightBit::Z80::handleNMI() {
|
||||||
raiseNMI();
|
raiseNMI();
|
||||||
raiseHALT();
|
raiseHALT();
|
||||||
|
IFF2() = IFF1();
|
||||||
IFF1() = false;
|
IFF1() = false;
|
||||||
lowerM1();
|
lowerM1();
|
||||||
const auto discarded = BUS().DATA();
|
const auto discarded = BUS().DATA();
|
||||||
raiseM1();
|
raiseM1();
|
||||||
restart(0x66);
|
restart(0x66);
|
||||||
tick(13);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void EightBit::Z80::handleINT() {
|
void EightBit::Z80::handleINT() {
|
||||||
@ -100,17 +104,18 @@ void EightBit::Z80::handleINT() {
|
|||||||
raiseIORQ();
|
raiseIORQ();
|
||||||
raiseM1();
|
raiseM1();
|
||||||
di();
|
di();
|
||||||
|
tick(5);
|
||||||
switch (IM()) {
|
switch (IM()) {
|
||||||
case 0: // i8080 equivalent
|
case 0: // i8080 equivalent
|
||||||
execute(data);
|
execute(data);
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
|
tick();
|
||||||
restart(7 << 3);
|
restart(7 << 3);
|
||||||
tick(13);
|
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
call(MEMPTR() = register16_t(data, IV()));
|
tick(7);
|
||||||
tick(19);
|
call(MEMPTR() = { data, IV() });
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
UNREACHABLE;
|
UNREACHABLE;
|
||||||
@ -143,56 +148,50 @@ uint8_t EightBit::Z80::decrement(uint8_t& f, const uint8_t operand) {
|
|||||||
return result;
|
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 >= 0);
|
||||||
ASSUME(flag <= 3);
|
ASSUME(flag <= 7);
|
||||||
switch (flag) {
|
switch (flag) {
|
||||||
case 0: // NZ
|
case 0:
|
||||||
return jrConditional(!(f & ZF));
|
return !(f & ZF);
|
||||||
case 1: // Z
|
case 1:
|
||||||
return jrConditional(f & ZF);
|
return f & ZF;
|
||||||
case 2: // NC
|
case 2:
|
||||||
return jrConditional(!(f & CF));
|
return !(f & CF);
|
||||||
case 3: // C
|
case 3:
|
||||||
return jrConditional(f & CF);
|
return f & CF;
|
||||||
|
case 4:
|
||||||
|
return !(f & PF);
|
||||||
|
case 5:
|
||||||
|
return f & PF;
|
||||||
|
case 6:
|
||||||
|
return !(f & SF);
|
||||||
|
case 7:
|
||||||
|
return f & SF;
|
||||||
default:
|
default:
|
||||||
UNREACHABLE;
|
UNREACHABLE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void EightBit::Z80::jumpConditionalFlag(const uint8_t f, const int flag) {
|
void EightBit::Z80::returnConditionalFlag(const uint8_t f, const int flag) {
|
||||||
ASSUME(flag >= 0);
|
if (convertCondition(f, flag)) {
|
||||||
ASSUME(flag <= 7);
|
tick();
|
||||||
switch (flag) {
|
ret();
|
||||||
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::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() {
|
void EightBit::Z80::retn() {
|
||||||
ret();
|
ret();
|
||||||
IFF1() = IFF2();
|
IFF1() = IFF2();
|
||||||
@ -202,56 +201,6 @@ void EightBit::Z80::reti() {
|
|||||||
retn();
|
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) {
|
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);
|
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) {
|
void EightBit::Z80::xhtl(register16_t& exchange) {
|
||||||
MEMPTR().low = IntelProcessor::busRead(SP());
|
MEMPTR().low = IntelProcessor::busRead(SP());
|
||||||
++BUS().ADDRESS();
|
++BUS().ADDRESS();
|
||||||
MEMPTR().high = IntelProcessor::busRead();
|
MEMPTR().high = busRead();
|
||||||
|
tick();
|
||||||
IntelProcessor::busWrite(exchange.high);
|
IntelProcessor::busWrite(exchange.high);
|
||||||
exchange.high = MEMPTR().high;
|
exchange.high = MEMPTR().high;
|
||||||
--BUS().ADDRESS();
|
--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) {
|
void EightBit::Z80::blockIn(register16_t& source, const register16_t destination) {
|
||||||
MEMPTR() = BUS().ADDRESS() = source;
|
MEMPTR() = BUS().ADDRESS() = source;
|
||||||
|
tick();
|
||||||
const auto value = readPort();
|
const auto value = readPort();
|
||||||
|
tick(3);
|
||||||
IntelProcessor::busWrite(destination, value);
|
IntelProcessor::busWrite(destination, value);
|
||||||
source.high = decrement(F(), source.high);
|
source.high = decrement(F(), source.high);
|
||||||
F() = setBit(F(), NF);
|
F() = setBit(F(), NF);
|
||||||
@ -636,6 +588,7 @@ bool EightBit::Z80::indr() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void EightBit::Z80::blockOut(const register16_t source, register16_t& destination) {
|
void EightBit::Z80::blockOut(const register16_t source, register16_t& destination) {
|
||||||
|
tick();
|
||||||
const auto value = IntelProcessor::busRead(source);
|
const auto value = IntelProcessor::busRead(source);
|
||||||
destination.high = decrement(F(), destination.high);
|
destination.high = decrement(F(), destination.high);
|
||||||
BUS().ADDRESS() = destination;
|
BUS().ADDRESS() = destination;
|
||||||
@ -669,6 +622,7 @@ bool EightBit::Z80::otdr() {
|
|||||||
void EightBit::Z80::rrd(uint8_t& f, register16_t address, uint8_t& update) {
|
void EightBit::Z80::rrd(uint8_t& f, register16_t address, uint8_t& update) {
|
||||||
(MEMPTR() = BUS().ADDRESS() = address)++;
|
(MEMPTR() = BUS().ADDRESS() = address)++;
|
||||||
const auto memory = busRead();
|
const auto memory = busRead();
|
||||||
|
tick(4);
|
||||||
IntelProcessor::busWrite(promoteNibble(update) | highNibble(memory));
|
IntelProcessor::busWrite(promoteNibble(update) | highNibble(memory));
|
||||||
update = higherNibble(update) | lowerNibble(memory);
|
update = higherNibble(update) | lowerNibble(memory);
|
||||||
f = adjustSZPXY<Z80>(f, update);
|
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) {
|
void EightBit::Z80::rld(uint8_t& f, register16_t address, uint8_t& update) {
|
||||||
(MEMPTR() = BUS().ADDRESS() = address)++;
|
(MEMPTR() = BUS().ADDRESS() = address)++;
|
||||||
const auto memory = busRead();
|
const auto memory = busRead();
|
||||||
|
tick(4);
|
||||||
IntelProcessor::busWrite(promoteNibble(memory) | lowNibble(update));
|
IntelProcessor::busWrite(promoteNibble(memory) | lowNibble(update));
|
||||||
update = higherNibble(update) | highNibble(memory);
|
update = higherNibble(update) | highNibble(memory);
|
||||||
f = adjustSZPXY<Z80>(f, update);
|
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) {
|
void EightBit::Z80::writePort(const uint8_t port) {
|
||||||
MEMPTR() = BUS().ADDRESS() = register16_t(port, A());
|
MEMPTR() = BUS().ADDRESS() = { port, A() };
|
||||||
BUS().DATA() = A();
|
BUS().DATA() = A();
|
||||||
writePort();
|
writePort();
|
||||||
++MEMPTR().low;
|
++MEMPTR().low;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EightBit::Z80::writePort() {
|
void EightBit::Z80::writePort() {
|
||||||
|
tick();
|
||||||
lowerIORQ();
|
lowerIORQ();
|
||||||
lowerWR();
|
lowerWR();
|
||||||
m_ports.write(BUS().ADDRESS().low, BUS().DATA());
|
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) {
|
uint8_t EightBit::Z80::readPort(const uint8_t port) {
|
||||||
MEMPTR() = BUS().ADDRESS() = register16_t(port, A());
|
MEMPTR() = BUS().ADDRESS() = { port, A() };
|
||||||
++MEMPTR().low;
|
++MEMPTR().low;
|
||||||
return readPort();
|
return readPort();
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t EightBit::Z80::readPort() {
|
uint8_t EightBit::Z80::readPort() {
|
||||||
|
tick();
|
||||||
lowerIORQ();
|
lowerIORQ();
|
||||||
lowerRD();
|
lowerRD();
|
||||||
const auto returned = BUS().DATA() = m_ports.read(BUS().ADDRESS().low);
|
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) {
|
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 memoryZ = z == 6;
|
||||||
const bool indirect = (!m_displaced && memoryZ) || m_displaced;
|
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
|
const bool update = x != 1; // BIT does not update
|
||||||
switch (x) {
|
switch (x) {
|
||||||
case 0: { // rot[y] r[z]
|
case 0: { // rot[y] r[z]
|
||||||
@ -818,46 +784,34 @@ void EightBit::Z80::executeCB(const int x, const int y, const int z) {
|
|||||||
UNREACHABLE;
|
UNREACHABLE;
|
||||||
}
|
}
|
||||||
F() = adjustSZP<Z80>(F(), operand);
|
F() = adjustSZP<Z80>(F(), operand);
|
||||||
tick(4);
|
|
||||||
break;
|
break;
|
||||||
} case 1: // BIT y, r[z]
|
} case 1: // BIT y, r[z]
|
||||||
tick(4);
|
|
||||||
bit(F(), y, operand);
|
bit(F(), y, operand);
|
||||||
if (indirect) {
|
F() = adjustXY<Z80>(F(), direct ? operand : MEMPTR().high);
|
||||||
F() = adjustXY<Z80>(F(), MEMPTR().high);
|
|
||||||
tick(4);
|
|
||||||
} else {
|
|
||||||
F() = adjustXY<Z80>(F(), operand);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case 2: // RES y, r[z]
|
case 2: // RES y, r[z]
|
||||||
tick(4);
|
|
||||||
operand = res(y, operand);
|
operand = res(y, operand);
|
||||||
break;
|
break;
|
||||||
case 3: // SET y, r[z]
|
case 3: // SET y, r[z]
|
||||||
tick(4);
|
|
||||||
operand = set(y, operand);
|
operand = set(y, operand);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
UNREACHABLE;
|
UNREACHABLE;
|
||||||
}
|
}
|
||||||
if (update) {
|
if (update) {
|
||||||
|
tick();
|
||||||
if (m_displaced) {
|
if (m_displaced) {
|
||||||
IntelProcessor::busWrite(operand);
|
IntelProcessor::busWrite(operand);
|
||||||
if (!memoryZ)
|
if (!memoryZ)
|
||||||
R2(z, operand);
|
R2(z, operand);
|
||||||
tick(15);
|
|
||||||
} else {
|
} else {
|
||||||
R(z, operand);
|
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) {
|
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) {
|
switch (x) {
|
||||||
case 0:
|
case 0:
|
||||||
case 3: // Invalid instruction, equivalent to NONI followed by NOP
|
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());
|
R(y, BUS().DATA());
|
||||||
F() = adjustSZPXY<Z80>(F(), BUS().DATA());
|
F() = adjustSZPXY<Z80>(F(), BUS().DATA());
|
||||||
F() = clearBit(F(), NF | HC);
|
F() = clearBit(F(), NF | HC);
|
||||||
tick(4);
|
|
||||||
break;
|
break;
|
||||||
case 1: // Output to port with 16-bit address
|
case 1: // Output to port with 16-bit address
|
||||||
(MEMPTR() = BUS().ADDRESS() = BC())++;
|
(MEMPTR() = BUS().ADDRESS() = BC())++;
|
||||||
if (y == 6) // OUT (C),0
|
BUS().DATA() = y == 6 ? 0 : R(y);
|
||||||
BUS().DATA() = 0;
|
|
||||||
else // OUT (C),r[y]
|
|
||||||
BUS().DATA() = R(y);
|
|
||||||
writePort();
|
writePort();
|
||||||
tick(4);
|
|
||||||
break;
|
break;
|
||||||
case 2: // 16-bit add/subtract with carry
|
case 2: // 16-bit add/subtract with carry
|
||||||
switch (q) {
|
switch (q) {
|
||||||
@ -893,7 +842,6 @@ void EightBit::Z80::executeED(const int x, const int y, const int z, const int p
|
|||||||
default:
|
default:
|
||||||
UNREACHABLE;
|
UNREACHABLE;
|
||||||
}
|
}
|
||||||
tick(7);
|
|
||||||
break;
|
break;
|
||||||
case 3: // Retrieve/store register pair from/to immediate address
|
case 3: // Retrieve/store register pair from/to immediate address
|
||||||
BUS().ADDRESS() = fetchWord();
|
BUS().ADDRESS() = fetchWord();
|
||||||
@ -907,7 +855,6 @@ void EightBit::Z80::executeED(const int x, const int y, const int z, const int p
|
|||||||
default:
|
default:
|
||||||
UNREACHABLE;
|
UNREACHABLE;
|
||||||
}
|
}
|
||||||
tick(12);
|
|
||||||
break;
|
break;
|
||||||
case 4: // Negate accumulator
|
case 4: // Negate accumulator
|
||||||
A() = neg(F(), A());
|
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
|
retn(); // RETN
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
tick(6);
|
|
||||||
break;
|
break;
|
||||||
case 6: // Set interrupt mode
|
case 6: // Set interrupt mode
|
||||||
switch (y) {
|
switch (y) {
|
||||||
@ -947,31 +893,25 @@ void EightBit::Z80::executeED(const int x, const int y, const int z, const int p
|
|||||||
switch (y) {
|
switch (y) {
|
||||||
case 0: // LD I,A
|
case 0: // LD I,A
|
||||||
IV() = A();
|
IV() = A();
|
||||||
tick();
|
|
||||||
break;
|
break;
|
||||||
case 1: // LD R,A
|
case 1: // LD R,A
|
||||||
REFRESH() = A();
|
REFRESH() = A();
|
||||||
tick();
|
|
||||||
break;
|
break;
|
||||||
case 2: // LD A,I
|
case 2: // LD A,I
|
||||||
F() = adjustSZXY<Z80>(F(), A() = IV());
|
F() = adjustSZXY<Z80>(F(), A() = IV());
|
||||||
F() = clearBit(F(), NF | HC);
|
F() = clearBit(F(), NF | HC);
|
||||||
F() = setBit(F(), PF, IFF2());
|
F() = setBit(F(), PF, IFF2());
|
||||||
tick();
|
|
||||||
break;
|
break;
|
||||||
case 3: // LD A,R
|
case 3: // LD A,R
|
||||||
F() = adjustSZXY<Z80>(F(), A() = REFRESH());
|
F() = adjustSZXY<Z80>(F(), A() = REFRESH());
|
||||||
F() = clearBit(F(), NF | HC);
|
F() = clearBit(F(), NF | HC);
|
||||||
F() = setBit(F(), PF, IFF2());
|
F() = setBit(F(), PF, IFF2());
|
||||||
tick();
|
|
||||||
break;
|
break;
|
||||||
case 4: // RRD
|
case 4: // RRD
|
||||||
rrd(F(), HL(), A());
|
rrd(F(), HL(), A());
|
||||||
tick(10);
|
|
||||||
break;
|
break;
|
||||||
case 5: // RLD
|
case 5: // RLD
|
||||||
rld(F(), HL(), A());
|
rld(F(), HL(), A());
|
||||||
tick(10);
|
|
||||||
break;
|
break;
|
||||||
case 6: // NOP
|
case 6: // NOP
|
||||||
case 7: // 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())) {
|
if (ldir(F(), A())) {
|
||||||
MEMPTR() = --PC();
|
MEMPTR() = --PC();
|
||||||
--PC();
|
--PC();
|
||||||
tick(5);
|
|
||||||
}
|
}
|
||||||
|
tick(7);
|
||||||
break;
|
break;
|
||||||
case 7: // LDDR
|
case 7: // LDDR
|
||||||
if (lddr(F(), A())) {
|
if (lddr(F(), A())) {
|
||||||
MEMPTR() = --PC();
|
MEMPTR() = --PC();
|
||||||
--PC();
|
--PC();
|
||||||
tick(5);
|
|
||||||
}
|
}
|
||||||
|
tick(7);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -1024,15 +964,17 @@ void EightBit::Z80::executeED(const int x, const int y, const int z, const int p
|
|||||||
--PC();
|
--PC();
|
||||||
tick(5);
|
tick(5);
|
||||||
}
|
}
|
||||||
|
tick(5);
|
||||||
break;
|
break;
|
||||||
case 7: // CPDR
|
case 7: // CPDR
|
||||||
if (cpdr(F(), A())) {
|
if (cpdr(F(), A())) {
|
||||||
MEMPTR() = --PC();
|
MEMPTR() = --PC();
|
||||||
--PC();
|
--PC();
|
||||||
tick(5);
|
tick(3);
|
||||||
} else {
|
} else {
|
||||||
MEMPTR() = PC() - 2;
|
MEMPTR() = PC() - 2;
|
||||||
}
|
}
|
||||||
|
tick(7);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -1071,17 +1013,18 @@ void EightBit::Z80::executeED(const int x, const int y, const int z, const int p
|
|||||||
PC() -= 2;
|
PC() -= 2;
|
||||||
tick(5);
|
tick(5);
|
||||||
}
|
}
|
||||||
|
tick(3);
|
||||||
break;
|
break;
|
||||||
case 7: // OTDR
|
case 7: // OTDR
|
||||||
if (otdr()) {
|
if (otdr()) {
|
||||||
PC() -= 2;
|
PC() -= 2;
|
||||||
tick(5);
|
tick(5);
|
||||||
}
|
}
|
||||||
|
tick(3);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
tick(8);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1100,21 +1043,19 @@ void EightBit::Z80::executeOther(const int x, const int y, const int z, const in
|
|||||||
exxAF();
|
exxAF();
|
||||||
break;
|
break;
|
||||||
case 2: // DJNZ d
|
case 2: // DJNZ d
|
||||||
|
tick();
|
||||||
if (jrConditional(--B()))
|
if (jrConditional(--B()))
|
||||||
tick(5);
|
tick(2);
|
||||||
tick(4);
|
tick(3);
|
||||||
break;
|
break;
|
||||||
case 3: // JR d
|
case 3: // JR d
|
||||||
jr(fetchByte());
|
jr(fetchByte());
|
||||||
tick(8);
|
|
||||||
break;
|
break;
|
||||||
case 4: // JR cc,d
|
case 4: // JR cc,d
|
||||||
case 5:
|
case 5:
|
||||||
case 6:
|
case 6:
|
||||||
case 7:
|
case 7:
|
||||||
if (jrConditionalFlag(F(), y - 4))
|
jrConditionalFlag(F(), y - 4);
|
||||||
tick(5);
|
|
||||||
tick(3);
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
UNREACHABLE;
|
UNREACHABLE;
|
||||||
@ -1124,11 +1065,9 @@ void EightBit::Z80::executeOther(const int x, const int y, const int z, const in
|
|||||||
switch (q) {
|
switch (q) {
|
||||||
case 0: // LD rp,nn
|
case 0: // LD rp,nn
|
||||||
RP(p) = fetchWord();
|
RP(p) = fetchWord();
|
||||||
tick(6);
|
|
||||||
break;
|
break;
|
||||||
case 1: // ADD HL,rp
|
case 1: // ADD HL,rp
|
||||||
HL2() = add(F(), HL2(), RP(p));
|
HL2() = add(F(), HL2(), RP(p));
|
||||||
tick(7);
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
UNREACHABLE;
|
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() = BUS().ADDRESS() = BC())++;
|
||||||
MEMPTR().high = BUS().DATA() = A();
|
MEMPTR().high = BUS().DATA() = A();
|
||||||
busWrite();
|
busWrite();
|
||||||
tick(3);
|
|
||||||
break;
|
break;
|
||||||
case 1: // LD (DE),A
|
case 1: // LD (DE),A
|
||||||
(MEMPTR() = BUS().ADDRESS() = DE())++;
|
(MEMPTR() = BUS().ADDRESS() = DE())++;
|
||||||
MEMPTR().high = BUS().DATA() = A();
|
MEMPTR().high = BUS().DATA() = A();
|
||||||
busWrite();
|
busWrite();
|
||||||
tick(3);
|
|
||||||
break;
|
break;
|
||||||
case 2: // LD (nn),HL
|
case 2: // LD (nn),HL
|
||||||
BUS().ADDRESS() = fetchWord();
|
BUS().ADDRESS() = fetchWord();
|
||||||
setWord(HL2());
|
setWord(HL2());
|
||||||
tick(12);
|
|
||||||
break;
|
break;
|
||||||
case 3: // LD (nn),A
|
case 3: // LD (nn),A
|
||||||
(MEMPTR() = BUS().ADDRESS() = fetchWord())++;
|
(MEMPTR() = BUS().ADDRESS() = fetchWord())++;
|
||||||
MEMPTR().high = BUS().DATA() = A();
|
MEMPTR().high = BUS().DATA() = A();
|
||||||
busWrite();
|
busWrite();
|
||||||
tick(9);
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
UNREACHABLE;
|
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)
|
case 0: // LD A,(BC)
|
||||||
(MEMPTR() = BUS().ADDRESS() = BC())++;
|
(MEMPTR() = BUS().ADDRESS() = BC())++;
|
||||||
A() = busRead();
|
A() = busRead();
|
||||||
tick(3);
|
|
||||||
break;
|
break;
|
||||||
case 1: // LD A,(DE)
|
case 1: // LD A,(DE)
|
||||||
(MEMPTR() = BUS().ADDRESS() = DE())++;
|
(MEMPTR() = BUS().ADDRESS() = DE())++;
|
||||||
A() = busRead();
|
A() = busRead();
|
||||||
tick(3);
|
|
||||||
break;
|
break;
|
||||||
case 2: // LD HL,(nn)
|
case 2: // LD HL,(nn)
|
||||||
BUS().ADDRESS() = fetchWord();
|
BUS().ADDRESS() = fetchWord();
|
||||||
HL2() = getWord();
|
HL2() = getWord();
|
||||||
tick(12);
|
|
||||||
break;
|
break;
|
||||||
case 3: // LD A,(nn)
|
case 3: // LD A,(nn)
|
||||||
(MEMPTR() = BUS().ADDRESS() = fetchWord())++;
|
(MEMPTR() = BUS().ADDRESS() = fetchWord())++;
|
||||||
A() = busRead();
|
A() = busRead();
|
||||||
tick(9);
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
UNREACHABLE;
|
UNREACHABLE;
|
||||||
@ -1206,30 +1137,36 @@ void EightBit::Z80::executeOther(const int x, const int y, const int z, const in
|
|||||||
default:
|
default:
|
||||||
UNREACHABLE;
|
UNREACHABLE;
|
||||||
}
|
}
|
||||||
tick(2);
|
|
||||||
break;
|
break;
|
||||||
case 4: // 8-bit INC
|
case 4: { // 8-bit INC
|
||||||
if (m_displaced && memoryY)
|
if (memoryY && m_displaced) {
|
||||||
fetchDisplacement();
|
fetchDisplacement();
|
||||||
R(y, increment(F(), R(y)));
|
tick(5);
|
||||||
break;
|
|
||||||
case 5: // 8-bit DEC
|
|
||||||
if (memoryY) {
|
|
||||||
tick(7);
|
|
||||||
if (m_displaced)
|
|
||||||
fetchDisplacement();
|
|
||||||
}
|
}
|
||||||
R(y, decrement(F(), R(y)));
|
const auto original = R(y);
|
||||||
|
tick();
|
||||||
|
R(y, increment(F(), original));
|
||||||
break;
|
break;
|
||||||
case 6: // 8-bit load immediate
|
}
|
||||||
if (memoryY) {
|
case 5: { // 8-bit DEC
|
||||||
tick(3);
|
if (memoryY && m_displaced) {
|
||||||
if (m_displaced)
|
fetchDisplacement();
|
||||||
fetchDisplacement();
|
tick(5);
|
||||||
}
|
}
|
||||||
R(y, fetchByte()); // LD r,n
|
const auto original = R(y);
|
||||||
tick(3);
|
tick();
|
||||||
|
R(y, decrement(F(), original));
|
||||||
break;
|
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
|
case 7: // Assorted operations on accumulator/flags
|
||||||
switch (y) {
|
switch (y) {
|
||||||
case 0:
|
case 0:
|
||||||
@ -1275,10 +1212,14 @@ void EightBit::Z80::executeOther(const int x, const int y, const int z, const in
|
|||||||
if (memoryZ) {
|
if (memoryZ) {
|
||||||
switch (y) {
|
switch (y) {
|
||||||
case 4:
|
case 4:
|
||||||
|
if (m_displaced)
|
||||||
|
tick(5);
|
||||||
H() = R(z);
|
H() = R(z);
|
||||||
normal = false;
|
normal = false;
|
||||||
break;
|
break;
|
||||||
case 5:
|
case 5:
|
||||||
|
if (m_displaced)
|
||||||
|
tick(5);
|
||||||
L() = R(z);
|
L() = R(z);
|
||||||
normal = false;
|
normal = false;
|
||||||
break;
|
break;
|
||||||
@ -1287,27 +1228,31 @@ void EightBit::Z80::executeOther(const int x, const int y, const int z, const in
|
|||||||
if (memoryY) {
|
if (memoryY) {
|
||||||
switch (z) {
|
switch (z) {
|
||||||
case 4:
|
case 4:
|
||||||
|
if (m_displaced)
|
||||||
|
tick(5);
|
||||||
R(y, H());
|
R(y, H());
|
||||||
normal = false;
|
normal = false;
|
||||||
break;
|
break;
|
||||||
case 5:
|
case 5:
|
||||||
|
if (m_displaced)
|
||||||
|
tick(5);
|
||||||
R(y, L());
|
R(y, L());
|
||||||
normal = false;
|
normal = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (normal)
|
if (normal) {
|
||||||
|
if (m_displaced)
|
||||||
|
tick(5);
|
||||||
R(y, R(z));
|
R(y, R(z));
|
||||||
if (memoryY || memoryZ) // M operations
|
}
|
||||||
tick(3);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 2: { // Operate on accumulator and register/memory location
|
case 2: { // Operate on accumulator and register/memory location
|
||||||
if (memoryZ) {
|
if (memoryZ && m_displaced) {
|
||||||
tick(3);
|
fetchDisplacement();
|
||||||
if (m_displaced)
|
tick(5);
|
||||||
fetchDisplacement();
|
|
||||||
}
|
}
|
||||||
const auto value = R(z);
|
const auto value = R(z);
|
||||||
switch (y) {
|
switch (y) {
|
||||||
@ -1343,21 +1288,17 @@ void EightBit::Z80::executeOther(const int x, const int y, const int z, const in
|
|||||||
case 3:
|
case 3:
|
||||||
switch (z) {
|
switch (z) {
|
||||||
case 0: // Conditional return
|
case 0: // Conditional return
|
||||||
if (returnConditionalFlag(F(), y))
|
returnConditionalFlag(F(), y);
|
||||||
tick(6);
|
|
||||||
tick(1);
|
|
||||||
break;
|
break;
|
||||||
case 1: // POP & various ops
|
case 1: // POP & various ops
|
||||||
switch (q) {
|
switch (q) {
|
||||||
case 0: // POP rp2[p]
|
case 0: // POP rp2[p]
|
||||||
RP2(p) = popWord();
|
RP2(p) = popWord();
|
||||||
tick(6);
|
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
switch (p) {
|
switch (p) {
|
||||||
case 0: // RET
|
case 0: // RET
|
||||||
ret();
|
ret();
|
||||||
tick(6);
|
|
||||||
break;
|
break;
|
||||||
case 1: // EXX
|
case 1: // EXX
|
||||||
exx();
|
exx();
|
||||||
@ -1378,13 +1319,11 @@ void EightBit::Z80::executeOther(const int x, const int y, const int z, const in
|
|||||||
break;
|
break;
|
||||||
case 2: // Conditional jump
|
case 2: // Conditional jump
|
||||||
jumpConditionalFlag(F(), y);
|
jumpConditionalFlag(F(), y);
|
||||||
tick(6);
|
|
||||||
break;
|
break;
|
||||||
case 3: // Assorted operations
|
case 3: // Assorted operations
|
||||||
switch (y) {
|
switch (y) {
|
||||||
case 0: // JP nn
|
case 0: // JP nn
|
||||||
jump(MEMPTR() = fetchWord());
|
jump(MEMPTR() = fetchWord());
|
||||||
tick(6);
|
|
||||||
break;
|
break;
|
||||||
case 1: // CB prefix
|
case 1: // CB prefix
|
||||||
m_prefixCB = true;
|
m_prefixCB = true;
|
||||||
@ -1397,15 +1336,12 @@ void EightBit::Z80::executeOther(const int x, const int y, const int z, const in
|
|||||||
break;
|
break;
|
||||||
case 2: // OUT (n),A
|
case 2: // OUT (n),A
|
||||||
writePort(fetchByte());
|
writePort(fetchByte());
|
||||||
tick(7);
|
|
||||||
break;
|
break;
|
||||||
case 3: // IN A,(n)
|
case 3: // IN A,(n)
|
||||||
A() = readPort(fetchByte());
|
A() = readPort(fetchByte());
|
||||||
tick(7);
|
|
||||||
break;
|
break;
|
||||||
case 4: // EX (SP),HL
|
case 4: // EX (SP),HL
|
||||||
xhtl(HL2());
|
xhtl(HL2());
|
||||||
tick(15);
|
|
||||||
break;
|
break;
|
||||||
case 5: // EX DE,HL
|
case 5: // EX DE,HL
|
||||||
std::swap(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;
|
break;
|
||||||
case 4: // Conditional call: CALL cc[y], nn
|
case 4: // Conditional call: CALL cc[y], nn
|
||||||
if (callConditionalFlag(F(), y))
|
callConditionalFlag(F(), y);
|
||||||
tick(7);
|
|
||||||
tick(6);
|
|
||||||
break;
|
break;
|
||||||
case 5: // PUSH & various ops
|
case 5: // PUSH & various ops
|
||||||
switch (q) {
|
switch (q) {
|
||||||
case 0: // PUSH rp2[p]
|
case 0: // PUSH rp2[p]
|
||||||
|
tick();
|
||||||
pushWord(RP2(p));
|
pushWord(RP2(p));
|
||||||
tick(7);
|
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
switch (p) {
|
switch (p) {
|
||||||
case 0: // CALL nn
|
case 0: // CALL nn
|
||||||
call(MEMPTR() = fetchWord());
|
call(MEMPTR() = fetchWord());
|
||||||
tick(13);
|
|
||||||
break;
|
break;
|
||||||
case 1: // DD prefix
|
case 1: // DD prefix
|
||||||
m_displaced = m_prefixDD = true;
|
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:
|
default:
|
||||||
UNREACHABLE;
|
UNREACHABLE;
|
||||||
}
|
}
|
||||||
tick(3);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 7: // Restart: RST y * 8
|
case 7: // Restart: RST y * 8
|
||||||
restart(y << 3);
|
restart(y << 3);
|
||||||
tick(7);
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
UNREACHABLE;
|
UNREACHABLE;
|
||||||
|
@ -138,20 +138,20 @@ namespace EightBit {
|
|||||||
MEMPTR() = fetchWord();
|
MEMPTR() = fetchWord();
|
||||||
if (condition)
|
if (condition)
|
||||||
call(MEMPTR());
|
call(MEMPTR());
|
||||||
return !!condition;
|
return condition;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto jumpConditional(const int condition) {
|
auto jumpConditional(const int condition) {
|
||||||
MEMPTR() = fetchWord();
|
MEMPTR() = fetchWord();
|
||||||
if (condition)
|
if (condition)
|
||||||
jump(MEMPTR());
|
jump(MEMPTR());
|
||||||
return !!condition;
|
return condition;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto returnConditional(const int condition) {
|
auto returnConditional(const int condition) {
|
||||||
if (condition)
|
if (condition)
|
||||||
ret();
|
ret();
|
||||||
return !!condition;
|
return condition;
|
||||||
}
|
}
|
||||||
|
|
||||||
void jr(const int8_t offset) noexcept {
|
void jr(const int8_t offset) noexcept {
|
||||||
@ -164,7 +164,7 @@ namespace EightBit {
|
|||||||
const auto offset = busRead(offsetAddress);
|
const auto offset = busRead(offsetAddress);
|
||||||
jr(offset);
|
jr(offset);
|
||||||
}
|
}
|
||||||
return !!condition;
|
return condition;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ret() final;
|
void ret() final;
|
||||||
|
@ -88,7 +88,7 @@ namespace EightBit {
|
|||||||
PC() = destination;
|
PC() = destination;
|
||||||
}
|
}
|
||||||
|
|
||||||
void call(const register16_t destination) {
|
virtual void call(const register16_t destination) {
|
||||||
pushWord(PC());
|
pushWord(PC());
|
||||||
jump(destination);
|
jump(destination);
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ EightBit::register16_t EightBit::BigEndianProcessor::getWord() {
|
|||||||
const auto high = busRead();
|
const auto high = busRead();
|
||||||
++BUS().ADDRESS();
|
++BUS().ADDRESS();
|
||||||
const auto low = busRead();
|
const auto low = busRead();
|
||||||
return register16_t(low, high);
|
return { low, high };
|
||||||
}
|
}
|
||||||
|
|
||||||
void EightBit::BigEndianProcessor::setWord(const register16_t value) {
|
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);
|
const auto high = getBytePaged(page, offset);
|
||||||
++BUS().ADDRESS().low;
|
++BUS().ADDRESS().low;
|
||||||
const auto low = busRead();
|
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) {
|
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() {
|
EightBit::register16_t EightBit::BigEndianProcessor::fetchWord() {
|
||||||
const auto high = fetchByte();
|
const auto high = fetchByte();
|
||||||
const auto low = fetchByte();
|
const auto low = fetchByte();
|
||||||
return register16_t(low, high);
|
return { low, high };
|
||||||
}
|
}
|
||||||
|
|
||||||
void EightBit::BigEndianProcessor::pushWord(const register16_t value) {
|
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() {
|
EightBit::register16_t EightBit::BigEndianProcessor::popWord() {
|
||||||
const auto high = pop();
|
const auto high = pop();
|
||||||
const auto low = pop();
|
const auto low = pop();
|
||||||
return register16_t(low, high);
|
return { low, high };
|
||||||
}
|
}
|
||||||
|
|
||||||
EightBit::register16_t EightBit::BigEndianProcessor::peekWord(const register16_t address) {
|
EightBit::register16_t EightBit::BigEndianProcessor::peekWord(const register16_t address) {
|
||||||
const auto high = BUS().peek(address);
|
const auto high = BUS().peek(address);
|
||||||
const auto low = BUS().peek(address + 1);
|
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) {
|
void EightBit::BigEndianProcessor::pokeWord(const register16_t address, const register16_t value) {
|
||||||
|
@ -41,7 +41,9 @@ void EightBit::Bus::loadHexFile(const std::string path) {
|
|||||||
uint8_t& EightBit::Bus::reference(const uint16_t address) {
|
uint8_t& EightBit::Bus::reference(const uint16_t address) {
|
||||||
const auto mapped = mapping(address);
|
const auto mapped = mapping(address);
|
||||||
const uint16_t offset = (address - mapped.begin) & mapped.mask;
|
const uint16_t offset = (address - mapped.begin) & mapped.mask;
|
||||||
if (mapped.access == MemoryMapping::AccessLevel::ReadOnly)
|
if (mapped.access == MemoryMapping::AccessLevel::ReadOnly) {
|
||||||
return DATA() = mapped.memory.peek(offset);
|
DATA() = mapped.memory.peek(offset);
|
||||||
|
return DATA();
|
||||||
|
}
|
||||||
return mapped.memory.reference(offset);
|
return mapped.memory.reference(offset);
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ EightBit::register16_t EightBit::LittleEndianProcessor::getWord() {
|
|||||||
const auto low = busRead();
|
const auto low = busRead();
|
||||||
++BUS().ADDRESS();
|
++BUS().ADDRESS();
|
||||||
const auto high = busRead();
|
const auto high = busRead();
|
||||||
return register16_t(low, high);
|
return { low, high };
|
||||||
}
|
}
|
||||||
|
|
||||||
void EightBit::LittleEndianProcessor::setWord(const register16_t value) {
|
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);
|
const auto low = getBytePaged(page, offset);
|
||||||
++BUS().ADDRESS().low;
|
++BUS().ADDRESS().low;
|
||||||
const auto high = busRead();
|
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) {
|
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() {
|
EightBit::register16_t EightBit::LittleEndianProcessor::fetchWord() {
|
||||||
const auto low = fetchByte();
|
const auto low = fetchByte();
|
||||||
const auto high = fetchByte();
|
const auto high = fetchByte();
|
||||||
return register16_t(low, high);
|
return { low, high };
|
||||||
}
|
}
|
||||||
|
|
||||||
void EightBit::LittleEndianProcessor::pushWord(const register16_t value) {
|
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() {
|
EightBit::register16_t EightBit::LittleEndianProcessor::popWord() {
|
||||||
const auto low = pop();
|
const auto low = pop();
|
||||||
const auto high = pop();
|
const auto high = pop();
|
||||||
return register16_t(low, high);
|
return { low, high };
|
||||||
}
|
}
|
||||||
|
|
||||||
EightBit::register16_t EightBit::LittleEndianProcessor::peekWord(const register16_t address) {
|
EightBit::register16_t EightBit::LittleEndianProcessor::peekWord(const register16_t address) {
|
||||||
const auto low = BUS().peek(address);
|
const auto low = BUS().peek(address);
|
||||||
const auto high = BUS().peek(address + 1);
|
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) {
|
void EightBit::LittleEndianProcessor::pokeWord(const register16_t address, const register16_t value) {
|
||||||
|
Loading…
Reference in New Issue
Block a user