Improve the flexibility of the BUS mapping/read/write architecture.

Signed-off-by: Adrian Conlon <Adrian.conlon@gmail.com>
This commit is contained in:
Adrian Conlon
2018-12-29 19:17:36 +00:00
parent 722888ae66
commit f38d326ca7
17 changed files with 114 additions and 59 deletions

View File

@@ -27,7 +27,7 @@ namespace EightBit {
Signal<Intel8080> ExecutingInstruction; Signal<Intel8080> ExecutingInstruction;
Signal<Intel8080> ExecutedInstruction; Signal<Intel8080> ExecutedInstruction;
virtual int execute(uint8_t opcode) final; virtual int execute() final;
virtual int step() final; virtual int step() final;
virtual register16_t& AF() final; virtual register16_t& AF() final;

View File

@@ -35,7 +35,7 @@ void EightBit::Intel8080::handleINT() {
raise(HALT()); raise(HALT());
if (m_interruptEnable) { if (m_interruptEnable) {
di(); di();
execute(BUS().DATA()); Processor::execute(BUS().DATA());
} }
addCycles(3); addCycles(3);
} }
@@ -275,18 +275,18 @@ int EightBit::Intel8080::step() {
} else if (UNLIKELY(lowered(INT()))) { } else if (UNLIKELY(lowered(INT()))) {
handleINT(); handleINT();
} else if (UNLIKELY(lowered(HALT()))) { } else if (UNLIKELY(lowered(HALT()))) {
execute(0); // NOP Processor::execute(0); // NOP
} else { } else {
execute(fetchByte()); Processor::execute(fetchByte());
} }
} }
ExecutedInstruction.fire(*this); ExecutedInstruction.fire(*this);
return cycles(); return cycles();
} }
int EightBit::Intel8080::execute(const uint8_t opcode) { int EightBit::Intel8080::execute() {
const auto& decoded = getDecodedOpcode(opcode); const auto& decoded = getDecodedOpcode(opcode());
const auto x = decoded.x; const auto x = decoded.x;
const auto y = decoded.y; const auto y = decoded.y;

View File

@@ -36,7 +36,7 @@ namespace EightBit {
virtual register16_t& HL() final; virtual register16_t& HL() final;
protected: protected:
virtual int execute(uint8_t opcode) final; virtual int execute() final;
virtual int step() final; virtual int step() final;
virtual void handleRESET() final; virtual void handleRESET() final;

View File

@@ -326,9 +326,9 @@ int EightBit::GameBoy::LR35902::step() {
} else if (UNLIKELY(lowered(INT()))) { } else if (UNLIKELY(lowered(INT()))) {
handleINT(); handleINT();
} else if (UNLIKELY(lowered(HALT()))) { } else if (UNLIKELY(lowered(HALT()))) {
execute(0); // NOP Processor::execute(0); // NOP
} else { } else {
execute(fetchByte()); Processor::execute(fetchByte());
} }
m_bus.IO().checkTimers(clockCycles()); m_bus.IO().checkTimers(clockCycles());
@@ -339,9 +339,9 @@ int EightBit::GameBoy::LR35902::step() {
return clockCycles(); return clockCycles();
} }
int EightBit::GameBoy::LR35902::execute(const uint8_t opcode) { int EightBit::GameBoy::LR35902::execute() {
const auto& decoded = getDecodedOpcode(opcode); const auto& decoded = getDecodedOpcode(opcode());
const auto x = decoded.x; const auto x = decoded.x;
const auto y = decoded.y; const auto y = decoded.y;
@@ -750,7 +750,7 @@ void EightBit::GameBoy::LR35902::executeOther(const int x, const int y, const in
break; break;
case 1: // CB prefix case 1: // CB prefix
m_prefixCB = true; m_prefixCB = true;
execute(fetchByte()); Processor::execute(fetchByte());
break; break;
case 6: // DI case 6: // DI
di(); di();

View File

@@ -57,7 +57,7 @@ namespace EightBit {
Signal<mc6809> ExecutingInstruction; Signal<mc6809> ExecutingInstruction;
Signal<mc6809> ExecutedInstruction; Signal<mc6809> ExecutedInstruction;
virtual int execute(uint8_t opcode) final; virtual int execute() final;
virtual int step() final; virtual int step() final;
virtual void powerOn() final; virtual void powerOn() final;
@@ -148,9 +148,9 @@ namespace EightBit {
// Execution helpers // Execution helpers
void executeUnprefixed(uint8_t opcode); void executeUnprefixed();
void execute10(uint8_t opcode); void execute10();
void execute11(uint8_t opcode); void execute11();
// Register selection for "indexed" // Register selection for "indexed"
register16_t& RR(int which); register16_t& RR(int which);

View File

@@ -29,7 +29,7 @@ int EightBit::mc6809::step() {
else if (UNLIKELY(lowered(IRQ()) && !interruptMasked())) else if (UNLIKELY(lowered(IRQ()) && !interruptMasked()))
handleIRQ(); handleIRQ();
else else
execute(fetchByte()); Processor::execute(fetchByte());
} }
ExecutedInstruction.fire(*this); ExecutedInstruction.fire(*this);
return cycles(); return cycles();
@@ -88,32 +88,32 @@ void EightBit::mc6809::handleFIRQ() {
// //
int EightBit::mc6809::execute(const uint8_t opcode) { int EightBit::mc6809::execute() {
lower(BA()); lower(BA());
lower(BS()); lower(BS());
const bool prefixed = m_prefix10 || m_prefix11; const bool prefixed = m_prefix10 || m_prefix11;
const bool unprefixed = !prefixed; const bool unprefixed = !prefixed;
if (unprefixed) { if (unprefixed) {
executeUnprefixed(opcode); executeUnprefixed();
} else { } else {
if (m_prefix10) if (m_prefix10)
execute10(opcode); execute10();
else else
execute11(opcode); execute11();
} }
assert(cycles() > 0); assert(cycles() > 0);
return cycles(); return cycles();
} }
void EightBit::mc6809::executeUnprefixed(const uint8_t opcode) { void EightBit::mc6809::executeUnprefixed() {
assert(!(m_prefix10 || m_prefix11)); assert(!(m_prefix10 || m_prefix11));
assert(cycles() == 0); assert(cycles() == 0);
switch (opcode) { switch (opcode()) {
case 0x10: m_prefix10 = true; execute(fetchByte()); break; case 0x10: m_prefix10 = true; Processor::execute(fetchByte()); break;
case 0x11: m_prefix11 = true; execute(fetchByte()); break; case 0x11: m_prefix11 = true; Processor::execute(fetchByte()); break;
// ABX // ABX
case 0x3a: addCycles(3); X() += B(); break; // ABX (inherent) case 0x3a: addCycles(3); X() += B(); break; // ABX (inherent)
@@ -475,12 +475,12 @@ void EightBit::mc6809::executeUnprefixed(const uint8_t opcode) {
} }
} }
void EightBit::mc6809::execute10(const uint8_t opcode) { void EightBit::mc6809::execute10() {
assert(m_prefix10 && !m_prefix11); assert(m_prefix10 && !m_prefix11);
assert(cycles() == 0); assert(cycles() == 0);
switch (opcode) { switch (opcode()) {
// CMP // CMP
@@ -546,12 +546,12 @@ void EightBit::mc6809::execute10(const uint8_t opcode) {
} }
} }
void EightBit::mc6809::execute11(const uint8_t opcode) { void EightBit::mc6809::execute11() {
assert(!m_prefix10 && m_prefix11); assert(!m_prefix10 && m_prefix11);
assert(cycles() == 0); assert(cycles() == 0);
switch (opcode) { switch (opcode()) {
// CMP // CMP

View File

@@ -54,7 +54,7 @@ namespace EightBit {
[[nodiscard]] auto& NMI() { return m_nmiLine; } // In [[nodiscard]] auto& NMI() { return m_nmiLine; } // In
[[nodiscard]] auto& M1() { return m_m1Line; } // Out [[nodiscard]] auto& M1() { return m_m1Line; } // Out
int execute(uint8_t opcode) final; int execute() final;
int step() final; int step() final;
void powerOn() final; void powerOn() final;

View File

@@ -67,7 +67,7 @@ void EightBit::Z80::handleINT() {
di(); di();
switch (IM()) { switch (IM()) {
case 0: // i8080 equivalent case 0: // i8080 equivalent
execute(BUS().DATA()); Processor::execute(BUS().DATA());
break; break;
case 1: case 1:
restart(7 << 3); restart(7 << 3);
@@ -671,16 +671,16 @@ int EightBit::Z80::step() {
} else if (UNLIKELY(lowered(INT()))) { } else if (UNLIKELY(lowered(INT()))) {
handleINT(); handleINT();
} else if (UNLIKELY(lowered(HALT()))) { } else if (UNLIKELY(lowered(HALT()))) {
execute(0); // NOP Processor::execute(0); // NOP
} else { } else {
execute(fetchByte()); Processor::execute(fetchByte());
} }
} }
ExecutedInstruction.fire(*this); ExecutedInstruction.fire(*this);
return cycles(); return cycles();
} }
int EightBit::Z80::execute(const uint8_t opcode) { int EightBit::Z80::execute() {
ASSUME(lowered(M1())); ASSUME(lowered(M1()));
@@ -689,7 +689,7 @@ int EightBit::Z80::execute(const uint8_t opcode) {
raise(M1()); raise(M1());
} }
const auto& decoded = getDecodedOpcode(opcode); const auto& decoded = getDecodedOpcode(opcode());
const auto x = decoded.x; const auto x = decoded.x;
const auto y = decoded.y; const auto y = decoded.y;
@@ -1364,7 +1364,7 @@ void EightBit::Z80::executeOther(const int x, const int y, const int z, const in
if (UNLIKELY(m_displaced)) if (UNLIKELY(m_displaced))
fetchDisplacement(); fetchDisplacement();
lower(M1()); lower(M1());
execute(fetchByte()); Processor::execute(fetchByte());
break; break;
case 2: // OUT (n),A case 2: // OUT (n),A
writePort(fetchByte()); writePort(fetchByte());
@@ -1414,17 +1414,17 @@ void EightBit::Z80::executeOther(const int x, const int y, const int z, const in
case 1: // DD prefix case 1: // DD prefix
m_displaced = m_prefixDD = true; m_displaced = m_prefixDD = true;
lower(M1()); lower(M1());
execute(fetchByte()); Processor::execute(fetchByte());
break; break;
case 2: // ED prefix case 2: // ED prefix
m_prefixED = true; m_prefixED = true;
lower(M1()); lower(M1());
execute(fetchByte()); Processor::execute(fetchByte());
break; break;
case 3: // FD prefix case 3: // FD prefix
m_displaced = m_prefixFD = true; m_displaced = m_prefixFD = true;
lower(M1()); lower(M1());
execute(fetchByte()); Processor::execute(fetchByte());
break; break;
default: default:
UNREACHABLE; UNREACHABLE;

View File

@@ -9,10 +9,10 @@
#include "Signal.h" #include "Signal.h"
#include "Register.h" #include "Register.h"
#include "EventArgs.h" #include "EventArgs.h"
#include "MemoryMapping.h" #include "Mapper.h"
namespace EightBit { namespace EightBit {
class Bus { class Bus : public Mapper {
public: public:
virtual ~Bus() = default; virtual ~Bus() = default;
@@ -54,7 +54,6 @@ namespace EightBit {
protected: protected:
virtual void initialise() = 0; virtual void initialise() = 0;
[[nodiscard]] virtual MemoryMapping mapping(uint16_t address) = 0;
[[nodiscard]] uint8_t& reference(uint16_t address); [[nodiscard]] uint8_t& reference(uint16_t address);
[[nodiscard]] auto& reference(const register16_t address) { return reference(address.word); } [[nodiscard]] auto& reference(const register16_t address) { return reference(address.word); }
[[nodiscard]] uint8_t& reference() { return reference(ADDRESS()); } [[nodiscard]] uint8_t& reference() { return reference(ADDRESS()); }

14
inc/Mapper.h Normal file
View File

@@ -0,0 +1,14 @@
#pragma once
#include <cstdint>
#include "MemoryMapping.h"
namespace EightBit {
class Mapper {
public:
virtual ~Mapper() = default;
[[nodiscard]] virtual MemoryMapping mapping(uint16_t address) = 0;
};
}

View File

@@ -9,7 +9,7 @@ namespace EightBit {
struct MemoryMapping { struct MemoryMapping {
enum class AccessLevel { Unknown, ReadOnly, ReadWrite, }; enum class AccessLevel { Unknown, ReadOnly, WriteOnly, ReadWrite };
Memory& memory; Memory& memory;
uint16_t begin = Chip::Mask16; uint16_t begin = Chip::Mask16;

View File

@@ -29,7 +29,8 @@ namespace EightBit {
int run(int limit); int run(int limit);
virtual int step() = 0; virtual int step() = 0;
virtual int execute(uint8_t opcode) = 0; virtual int execute() = 0;
int execute(uint8_t value);
[[nodiscard]] auto cycles() const noexcept { return m_cycles; } [[nodiscard]] auto cycles() const noexcept { return m_cycles; }
@@ -39,6 +40,7 @@ namespace EightBit {
protected: protected:
Processor(Bus& memory); Processor(Bus& memory);
[[nodiscard]] auto& opcode() noexcept { return m_opcode; }
[[nodiscard]] auto& BUS() noexcept { return m_bus; } [[nodiscard]] auto& BUS() noexcept { return m_bus; }
[[nodiscard]] auto halted() noexcept { return lowered(HALT()); } [[nodiscard]] auto halted() noexcept { return lowered(HALT()); }
@@ -49,16 +51,23 @@ namespace EightBit {
virtual void handleINT(); virtual void handleINT();
virtual void handleIRQ(); virtual void handleIRQ();
void busWrite(register16_t address, uint8_t data);
void busWrite(uint8_t data);
virtual void busWrite();
[[nodiscard]] uint8_t busRead(register16_t address);
[[nodiscard]] virtual uint8_t busRead();
[[nodiscard]] auto getBytePaged(const uint8_t page, const uint8_t offset) { [[nodiscard]] auto getBytePaged(const uint8_t page, const uint8_t offset) {
return BUS().read(register16_t(offset, page)); return busRead(register16_t(offset, page));
} }
void setBytePaged(const uint8_t page, const uint8_t offset, const uint8_t value) { void setBytePaged(const uint8_t page, const uint8_t offset, const uint8_t value) {
BUS().write(register16_t(offset, page), value); busWrite(register16_t(offset, page), value);
} }
[[nodiscard]] auto fetchByte() { [[nodiscard]] auto fetchByte() {
return BUS().read(PC()++); return busRead(PC()++);
} }
[[nodiscard]] virtual register16_t getWord() = 0; [[nodiscard]] virtual register16_t getWord() = 0;
@@ -102,6 +111,7 @@ namespace EightBit {
private: private:
Bus& m_bus; Bus& m_bus;
uint8_t m_opcode = Mask8;
int m_cycles = 0; int m_cycles = 0;
register16_t m_pc; register16_t m_pc;

View File

@@ -5,29 +5,29 @@ EightBit::BigEndianProcessor::BigEndianProcessor(Bus& memory)
: Processor(memory) {} : Processor(memory) {}
EightBit::register16_t EightBit::BigEndianProcessor::getWord() { EightBit::register16_t EightBit::BigEndianProcessor::getWord() {
const auto high = BUS().read(); const auto high = busRead();
++BUS().ADDRESS(); ++BUS().ADDRESS();
const auto low = BUS().read(); const auto low = busRead();
return { low, high }; return { low, high };
} }
void EightBit::BigEndianProcessor::setWord(const register16_t value) { void EightBit::BigEndianProcessor::setWord(const register16_t value) {
BUS().write(value.high); busWrite(value.high);
++BUS().ADDRESS(); ++BUS().ADDRESS();
BUS().write(value.low); busWrite(value.low);
} }
EightBit::register16_t EightBit::BigEndianProcessor::getWordPaged(const uint8_t page, const uint8_t offset) { EightBit::register16_t EightBit::BigEndianProcessor::getWordPaged(const uint8_t page, const uint8_t offset) {
const auto high = getBytePaged(page, offset); const auto high = getBytePaged(page, offset);
++BUS().ADDRESS().low; ++BUS().ADDRESS().low;
const auto low = BUS().read(); const auto low = busRead();
return { 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) {
setBytePaged(page, offset, value.high); setBytePaged(page, offset, value.high);
++BUS().ADDRESS().low; ++BUS().ADDRESS().low;
BUS().write(value.low); busWrite(value.low);
} }
EightBit::register16_t EightBit::BigEndianProcessor::fetchWord() { EightBit::register16_t EightBit::BigEndianProcessor::fetchWord() {

View File

@@ -150,6 +150,7 @@
<ClInclude Include="..\inc\InputOutput.h" /> <ClInclude Include="..\inc\InputOutput.h" />
<ClInclude Include="..\inc\IntelProcessor.h" /> <ClInclude Include="..\inc\IntelProcessor.h" />
<ClInclude Include="..\inc\LittleEndianProcessor.h" /> <ClInclude Include="..\inc\LittleEndianProcessor.h" />
<ClInclude Include="..\inc\Mapper.h" />
<ClInclude Include="..\inc\Rom.h" /> <ClInclude Include="..\inc\Rom.h" />
<ClInclude Include="..\inc\Memory.h" /> <ClInclude Include="..\inc\Memory.h" />
<ClInclude Include="..\inc\MemoryMapping.h" /> <ClInclude Include="..\inc\MemoryMapping.h" />

View File

@@ -65,6 +65,9 @@
<ClInclude Include="..\inc\Memory.h"> <ClInclude Include="..\inc\Memory.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="..\inc\Mapper.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="stdafx.cpp"> <ClCompile Include="stdafx.cpp">

View File

@@ -5,29 +5,29 @@ EightBit::LittleEndianProcessor::LittleEndianProcessor(Bus& memory)
: Processor(memory) {} : Processor(memory) {}
EightBit::register16_t EightBit::LittleEndianProcessor::getWord() { EightBit::register16_t EightBit::LittleEndianProcessor::getWord() {
const auto low = BUS().read(); const auto low = busRead();
++BUS().ADDRESS(); ++BUS().ADDRESS();
const auto high = BUS().read(); const auto high = busRead();
return { low, high }; return { low, high };
} }
void EightBit::LittleEndianProcessor::setWord(const register16_t value) { void EightBit::LittleEndianProcessor::setWord(const register16_t value) {
BUS().write(value.low); busWrite(value.low);
++BUS().ADDRESS(); ++BUS().ADDRESS();
BUS().write(value.high); busWrite(value.high);
} }
EightBit::register16_t EightBit::LittleEndianProcessor::getWordPaged(const uint8_t page, const uint8_t offset) { EightBit::register16_t EightBit::LittleEndianProcessor::getWordPaged(const uint8_t page, const uint8_t offset) {
const auto low = getBytePaged(page, offset); const auto low = getBytePaged(page, offset);
++BUS().ADDRESS().low; ++BUS().ADDRESS().low;
const auto high = BUS().read(); const auto high = busRead();
return { 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) {
setBytePaged(page, offset, value.low); setBytePaged(page, offset, value.low);
++BUS().ADDRESS().low; ++BUS().ADDRESS().low;
BUS().write(value.high); busWrite(value.high);
} }
EightBit::register16_t EightBit::LittleEndianProcessor::fetchWord() { EightBit::register16_t EightBit::LittleEndianProcessor::fetchWord() {

View File

@@ -25,6 +25,29 @@ void EightBit::Processor::handleIRQ() {
raise(IRQ()); raise(IRQ());
} }
void EightBit::Processor::busWrite(const register16_t address, const uint8_t data) {
BUS().ADDRESS() = address;
busWrite(data);
}
void EightBit::Processor::busWrite(const uint8_t data) {
BUS().DATA() = data;
busWrite();
}
void EightBit::Processor::busWrite() {
BUS().write();
}
uint8_t EightBit::Processor::busRead(const register16_t address) {
BUS().ADDRESS() = address;
return busRead();
}
uint8_t EightBit::Processor::busRead() {
return BUS().read();
}
int EightBit::Processor::run(const int limit) { int EightBit::Processor::run(const int limit) {
int current = 0; int current = 0;
while (LIKELY(powered() && (current < limit))) while (LIKELY(powered() && (current < limit)))
@@ -32,6 +55,11 @@ int EightBit::Processor::run(const int limit) {
return current; return current;
} }
int EightBit::Processor::execute(const uint8_t value) {
opcode() = value;
return execute();
}
// http://graphics.stanford.edu/~seander/bithacks.html#FixedSignExtend // http://graphics.stanford.edu/~seander/bithacks.html#FixedSignExtend
int8_t EightBit::Processor::signExtend(const int b, uint8_t x) { int8_t EightBit::Processor::signExtend(const int b, uint8_t x) {
const uint8_t m = 1 << (b - 1); // mask can be pre-computed if b is fixed const uint8_t m = 1 << (b - 1); // mask can be pre-computed if b is fixed