mirror of
https://github.com/MoleskiCoder/EightBit.git
synced 2025-01-10 10:29:43 +00:00
Add finer control of memory and IO events in the Z80 implementation. Allows small tidy of the halt condition.
Signed-off-by: Adrian Conlon <adrian.conlon@gmail.com>
This commit is contained in:
parent
99f0712801
commit
dcb809d8f9
@ -21,28 +21,21 @@ Fuse::TestRunner::TestRunner(const Test& test, const ExpectedTestResult& result)
|
||||
std::cout << "**** Cycle count: " << cpu.cycles() << std::endl;
|
||||
});
|
||||
|
||||
ReadByte.connect([this](EightBit::EventArgs&) {
|
||||
addActualEvent(currentBusAccessType() + "R");
|
||||
m_cpu.ReadMemory.connect([this](EightBit::EventArgs&) {
|
||||
addActualEvent("MR");
|
||||
});
|
||||
|
||||
WrittenByte.connect([this](EightBit::EventArgs&) {
|
||||
addActualEvent(currentBusAccessType() + "W");
|
||||
m_cpu.WrittenMemory.connect([this](EightBit::EventArgs&) {
|
||||
addActualEvent("MW");
|
||||
});
|
||||
}
|
||||
|
||||
std::string Fuse::TestRunner::currentBusAccessType() {
|
||||
m_cpu.ReadIO.connect([this](EightBit::EventArgs&) {
|
||||
addActualEvent("PR");
|
||||
});
|
||||
|
||||
const bool ioRequest = m_cpu.requestingIO();
|
||||
const bool memoryRequest = m_cpu.requestingMemory();
|
||||
if (ioRequest && memoryRequest)
|
||||
throw std::logic_error("Invalid bus state (both IORQ and MREQ lowered");
|
||||
|
||||
if (ioRequest)
|
||||
return "P";
|
||||
if (memoryRequest)
|
||||
return "M";
|
||||
|
||||
throw std::logic_error("Invalid bus state (neither IORQ and MREQ lowered");
|
||||
m_cpu.WrittenIO.connect([this](EightBit::EventArgs&) {
|
||||
addActualEvent("PW");
|
||||
});
|
||||
}
|
||||
|
||||
void Fuse::TestRunner::addActualEvent(const std::string& specifier) {
|
||||
@ -50,7 +43,8 @@ void Fuse::TestRunner::addActualEvent(const std::string& specifier) {
|
||||
actual.address = ADDRESS().word;
|
||||
actual.cycles = m_totalCycles + m_cpu.cycles();
|
||||
actual.specifier = specifier;
|
||||
actual.value = DATA();
|
||||
if (!boost::algorithm::ends_with(specifier, "C"))
|
||||
actual.value = DATA();
|
||||
actual.valid = true;
|
||||
m_actualEvents.events.push_back(actual);
|
||||
}
|
||||
|
@ -40,7 +40,6 @@ namespace Fuse {
|
||||
const std::string& lowDescription,
|
||||
EightBit::register16_t actual, EightBit::register16_t expected) const;
|
||||
|
||||
std::string currentBusAccessType();
|
||||
void addActualEvent(const std::string& specifier);
|
||||
void dumpExpectedEvents() const;
|
||||
void dumpActualEvents() const;
|
||||
|
@ -51,6 +51,18 @@ namespace EightBit {
|
||||
Signal<Z80> ExecutingInstruction;
|
||||
Signal<Z80> ExecutedInstruction;
|
||||
|
||||
Signal<EventArgs> ReadingMemory;
|
||||
Signal<EventArgs> ReadMemory;
|
||||
|
||||
Signal<EventArgs> WritingMemory;
|
||||
Signal<EventArgs> WrittenMemory;
|
||||
|
||||
Signal<EventArgs> ReadingIO;
|
||||
Signal<EventArgs> ReadIO;
|
||||
|
||||
Signal<EventArgs> WritingIO;
|
||||
Signal<EventArgs> WrittenIO;
|
||||
|
||||
int execute() final;
|
||||
int step() final;
|
||||
|
||||
@ -189,10 +201,28 @@ namespace EightBit {
|
||||
// before the RD signal becomes inactive. Clock states T3 and T4 of a fetch cycle are used to
|
||||
// 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() {
|
||||
//
|
||||
// When a software HALT instruction is executed, the CPU executes NOPs until an interrupt
|
||||
// is received(either a nonmaskable or a maskable interrupt while the interrupt flip-flop is
|
||||
// enabled). The two interrupt lines are sampled with the rising clock edge during each T4
|
||||
// state as depicted in Figure 11.If a nonmaskable interrupt is received or a maskable interrupt
|
||||
// is received and the interrupt enable flip-flop is set, then the HALT state is exited on
|
||||
// the next rising clock edge.The following cycle is an interrupt acknowledge cycle corresponding
|
||||
// to the type of interrupt that was received.If both are received at this time, then
|
||||
// the nonmaskable interrupt is acknowledged because it is the highest priority.The purpose
|
||||
// of executing NOP instructions while in the HALT state is to keep the memory refresh signals
|
||||
// active.Each cycle in the HALT state is a normal M1(fetch) cycle except that the data
|
||||
// received from the memory is ignored and an NOP instruction is forced internally to the
|
||||
// CPU.The HALT acknowledge signal is active during this time indicating that the processor
|
||||
// is in the HALT state
|
||||
uint8_t fetchInitialOpCode() {
|
||||
tick();
|
||||
lowerM1();
|
||||
const auto returned = IntelProcessor::memoryRead(PC());
|
||||
auto returned = IntelProcessor::memoryRead(PC());
|
||||
if (UNLIKELY(lowered(HALT())))
|
||||
returned = 0; // NOP
|
||||
else
|
||||
PC()++;
|
||||
raiseM1();
|
||||
BUS().ADDRESS() = { REFRESH(), IV() };
|
||||
lowerRFSH();
|
||||
@ -202,12 +232,6 @@ namespace EightBit {
|
||||
return returned;
|
||||
}
|
||||
|
||||
uint8_t fetchInitialOpCode() {
|
||||
const auto returned = readInitialOpCode();
|
||||
++PC();
|
||||
return returned;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto& HL2() {
|
||||
if (LIKELY(!m_displaced))
|
||||
return HL();
|
||||
|
@ -58,17 +58,21 @@ EightBit::register16_t& EightBit::Z80::HL() {
|
||||
}
|
||||
|
||||
void EightBit::Z80::memoryWrite() {
|
||||
WritingMemory.fire(EventArgs::empty());
|
||||
tick(2);
|
||||
lowerMREQ();
|
||||
IntelProcessor::memoryWrite();
|
||||
raiseMREQ();
|
||||
WrittenMemory.fire(EventArgs::empty());
|
||||
}
|
||||
|
||||
uint8_t EightBit::Z80::memoryRead() {
|
||||
ReadingMemory.fire(EventArgs::empty());
|
||||
tick(2);
|
||||
lowerMREQ();
|
||||
const auto returned = IntelProcessor::memoryRead();
|
||||
raiseMREQ();
|
||||
ReadMemory.fire(EventArgs::empty());
|
||||
return returned;
|
||||
}
|
||||
|
||||
@ -661,10 +665,15 @@ void EightBit::Z80::portWrite(const uint8_t port) {
|
||||
}
|
||||
|
||||
void EightBit::Z80::portWrite() {
|
||||
WritingIO.fire(EventArgs::empty());
|
||||
lowerIORQ();
|
||||
busWrite();
|
||||
raiseIORQ();
|
||||
tick(3);
|
||||
WrittenIO.fire(EventArgs::empty());
|
||||
tick(3); // I guess this means the extra three ticks on
|
||||
// port writing are just a quirk of the
|
||||
// individual instructions, rather than a
|
||||
// fundamental aspect of port IO. Hmmm.
|
||||
}
|
||||
|
||||
uint8_t EightBit::Z80::portRead(const uint8_t port) {
|
||||
@ -674,9 +683,11 @@ uint8_t EightBit::Z80::portRead(const uint8_t port) {
|
||||
}
|
||||
|
||||
uint8_t EightBit::Z80::portRead() {
|
||||
ReadingIO.fire(EventArgs::empty());
|
||||
lowerIORQ();
|
||||
const auto returned = busRead();
|
||||
raiseIORQ();
|
||||
ReadIO.fire(EventArgs::empty());
|
||||
tick(3);
|
||||
return returned;
|
||||
}
|
||||
@ -700,24 +711,6 @@ int EightBit::Z80::step() {
|
||||
handleINT();
|
||||
handled = true;
|
||||
}
|
||||
} else if (lowered(HALT())) {
|
||||
// ** From the Z80 CPU User Manual
|
||||
// When a software HALT instruction is executed, the CPU executes NOPs until an interrupt
|
||||
// is received(either a nonmaskable or a maskable interrupt while the interrupt flip-flop is
|
||||
// enabled). The two interrupt lines are sampled with the rising clock edge during each T4
|
||||
// state as depicted in Figure 11.If a nonmaskable interrupt is received or a maskable interrupt
|
||||
// is received and the interrupt enable flip-flop is set, then the HALT state is exited on
|
||||
// the next rising clock edge.The following cycle is an interrupt acknowledge cycle corresponding
|
||||
// to the type of interrupt that was received.If both are received at this time, then
|
||||
// the nonmaskable interrupt is acknowledged because it is the highest priority.The purpose
|
||||
// of executing NOP instructions while in the HALT state is to keep the memory refresh signals
|
||||
// active.Each cycle in the HALT state is a normal M1(fetch) cycle except that the data
|
||||
// received from the memory is ignored and an NOP instruction is forced internally to the
|
||||
// CPU.The HALT acknowledge signal is active during this time indicating that the processor
|
||||
// is in the HALT state.
|
||||
const auto discarded = readInitialOpCode();
|
||||
IntelProcessor::execute(0); // NOP
|
||||
handled = true;
|
||||
}
|
||||
if (!handled)
|
||||
IntelProcessor::execute(fetchInitialOpCode());
|
||||
|
Loading…
x
Reference in New Issue
Block a user