mirror of
https://github.com/MoleskiCoder/EightBit.git
synced 2025-01-03 09:29:50 +00:00
Improve the flexibility of the BUS mapping/read/write architecture.
Signed-off-by: Adrian Conlon <Adrian.conlon@gmail.com>
This commit is contained in:
parent
722888ae66
commit
f38d326ca7
@ -27,7 +27,7 @@ namespace EightBit {
|
||||
Signal<Intel8080> ExecutingInstruction;
|
||||
Signal<Intel8080> ExecutedInstruction;
|
||||
|
||||
virtual int execute(uint8_t opcode) final;
|
||||
virtual int execute() final;
|
||||
virtual int step() final;
|
||||
|
||||
virtual register16_t& AF() final;
|
||||
|
@ -35,7 +35,7 @@ void EightBit::Intel8080::handleINT() {
|
||||
raise(HALT());
|
||||
if (m_interruptEnable) {
|
||||
di();
|
||||
execute(BUS().DATA());
|
||||
Processor::execute(BUS().DATA());
|
||||
}
|
||||
addCycles(3);
|
||||
}
|
||||
@ -275,18 +275,18 @@ int EightBit::Intel8080::step() {
|
||||
} else if (UNLIKELY(lowered(INT()))) {
|
||||
handleINT();
|
||||
} else if (UNLIKELY(lowered(HALT()))) {
|
||||
execute(0); // NOP
|
||||
Processor::execute(0); // NOP
|
||||
} else {
|
||||
execute(fetchByte());
|
||||
Processor::execute(fetchByte());
|
||||
}
|
||||
}
|
||||
ExecutedInstruction.fire(*this);
|
||||
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 y = decoded.y;
|
||||
|
@ -36,7 +36,7 @@ namespace EightBit {
|
||||
virtual register16_t& HL() final;
|
||||
|
||||
protected:
|
||||
virtual int execute(uint8_t opcode) final;
|
||||
virtual int execute() final;
|
||||
virtual int step() final;
|
||||
|
||||
virtual void handleRESET() final;
|
||||
|
@ -326,9 +326,9 @@ int EightBit::GameBoy::LR35902::step() {
|
||||
} else if (UNLIKELY(lowered(INT()))) {
|
||||
handleINT();
|
||||
} else if (UNLIKELY(lowered(HALT()))) {
|
||||
execute(0); // NOP
|
||||
Processor::execute(0); // NOP
|
||||
} else {
|
||||
execute(fetchByte());
|
||||
Processor::execute(fetchByte());
|
||||
}
|
||||
|
||||
m_bus.IO().checkTimers(clockCycles());
|
||||
@ -339,9 +339,9 @@ int EightBit::GameBoy::LR35902::step() {
|
||||
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 y = decoded.y;
|
||||
@ -750,7 +750,7 @@ void EightBit::GameBoy::LR35902::executeOther(const int x, const int y, const in
|
||||
break;
|
||||
case 1: // CB prefix
|
||||
m_prefixCB = true;
|
||||
execute(fetchByte());
|
||||
Processor::execute(fetchByte());
|
||||
break;
|
||||
case 6: // DI
|
||||
di();
|
||||
|
@ -57,7 +57,7 @@ namespace EightBit {
|
||||
Signal<mc6809> ExecutingInstruction;
|
||||
Signal<mc6809> ExecutedInstruction;
|
||||
|
||||
virtual int execute(uint8_t opcode) final;
|
||||
virtual int execute() final;
|
||||
virtual int step() final;
|
||||
|
||||
virtual void powerOn() final;
|
||||
@ -148,9 +148,9 @@ namespace EightBit {
|
||||
|
||||
// Execution helpers
|
||||
|
||||
void executeUnprefixed(uint8_t opcode);
|
||||
void execute10(uint8_t opcode);
|
||||
void execute11(uint8_t opcode);
|
||||
void executeUnprefixed();
|
||||
void execute10();
|
||||
void execute11();
|
||||
|
||||
// Register selection for "indexed"
|
||||
register16_t& RR(int which);
|
||||
|
@ -29,7 +29,7 @@ int EightBit::mc6809::step() {
|
||||
else if (UNLIKELY(lowered(IRQ()) && !interruptMasked()))
|
||||
handleIRQ();
|
||||
else
|
||||
execute(fetchByte());
|
||||
Processor::execute(fetchByte());
|
||||
}
|
||||
ExecutedInstruction.fire(*this);
|
||||
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(BS());
|
||||
const bool prefixed = m_prefix10 || m_prefix11;
|
||||
const bool unprefixed = !prefixed;
|
||||
if (unprefixed) {
|
||||
executeUnprefixed(opcode);
|
||||
executeUnprefixed();
|
||||
} else {
|
||||
if (m_prefix10)
|
||||
execute10(opcode);
|
||||
execute10();
|
||||
else
|
||||
execute11(opcode);
|
||||
execute11();
|
||||
}
|
||||
assert(cycles() > 0);
|
||||
return cycles();
|
||||
}
|
||||
|
||||
void EightBit::mc6809::executeUnprefixed(const uint8_t opcode) {
|
||||
void EightBit::mc6809::executeUnprefixed() {
|
||||
|
||||
assert(!(m_prefix10 || m_prefix11));
|
||||
assert(cycles() == 0);
|
||||
|
||||
switch (opcode) {
|
||||
switch (opcode()) {
|
||||
|
||||
case 0x10: m_prefix10 = true; execute(fetchByte()); break;
|
||||
case 0x11: m_prefix11 = true; execute(fetchByte()); break;
|
||||
case 0x10: m_prefix10 = true; Processor::execute(fetchByte()); break;
|
||||
case 0x11: m_prefix11 = true; Processor::execute(fetchByte()); break;
|
||||
|
||||
// ABX
|
||||
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(cycles() == 0);
|
||||
|
||||
switch (opcode) {
|
||||
switch (opcode()) {
|
||||
|
||||
// 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(cycles() == 0);
|
||||
|
||||
switch (opcode) {
|
||||
switch (opcode()) {
|
||||
|
||||
// CMP
|
||||
|
||||
|
@ -54,7 +54,7 @@ namespace EightBit {
|
||||
[[nodiscard]] auto& NMI() { return m_nmiLine; } // In
|
||||
[[nodiscard]] auto& M1() { return m_m1Line; } // Out
|
||||
|
||||
int execute(uint8_t opcode) final;
|
||||
int execute() final;
|
||||
int step() final;
|
||||
void powerOn() final;
|
||||
|
||||
|
@ -67,7 +67,7 @@ void EightBit::Z80::handleINT() {
|
||||
di();
|
||||
switch (IM()) {
|
||||
case 0: // i8080 equivalent
|
||||
execute(BUS().DATA());
|
||||
Processor::execute(BUS().DATA());
|
||||
break;
|
||||
case 1:
|
||||
restart(7 << 3);
|
||||
@ -671,16 +671,16 @@ int EightBit::Z80::step() {
|
||||
} else if (UNLIKELY(lowered(INT()))) {
|
||||
handleINT();
|
||||
} else if (UNLIKELY(lowered(HALT()))) {
|
||||
execute(0); // NOP
|
||||
Processor::execute(0); // NOP
|
||||
} else {
|
||||
execute(fetchByte());
|
||||
Processor::execute(fetchByte());
|
||||
}
|
||||
}
|
||||
ExecutedInstruction.fire(*this);
|
||||
return cycles();
|
||||
}
|
||||
|
||||
int EightBit::Z80::execute(const uint8_t opcode) {
|
||||
int EightBit::Z80::execute() {
|
||||
|
||||
ASSUME(lowered(M1()));
|
||||
|
||||
@ -689,7 +689,7 @@ int EightBit::Z80::execute(const uint8_t opcode) {
|
||||
raise(M1());
|
||||
}
|
||||
|
||||
const auto& decoded = getDecodedOpcode(opcode);
|
||||
const auto& decoded = getDecodedOpcode(opcode());
|
||||
|
||||
const auto x = decoded.x;
|
||||
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))
|
||||
fetchDisplacement();
|
||||
lower(M1());
|
||||
execute(fetchByte());
|
||||
Processor::execute(fetchByte());
|
||||
break;
|
||||
case 2: // OUT (n),A
|
||||
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
|
||||
m_displaced = m_prefixDD = true;
|
||||
lower(M1());
|
||||
execute(fetchByte());
|
||||
Processor::execute(fetchByte());
|
||||
break;
|
||||
case 2: // ED prefix
|
||||
m_prefixED = true;
|
||||
lower(M1());
|
||||
execute(fetchByte());
|
||||
Processor::execute(fetchByte());
|
||||
break;
|
||||
case 3: // FD prefix
|
||||
m_displaced = m_prefixFD = true;
|
||||
lower(M1());
|
||||
execute(fetchByte());
|
||||
Processor::execute(fetchByte());
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE;
|
||||
|
@ -9,10 +9,10 @@
|
||||
#include "Signal.h"
|
||||
#include "Register.h"
|
||||
#include "EventArgs.h"
|
||||
#include "MemoryMapping.h"
|
||||
#include "Mapper.h"
|
||||
|
||||
namespace EightBit {
|
||||
class Bus {
|
||||
class Bus : public Mapper {
|
||||
public:
|
||||
virtual ~Bus() = default;
|
||||
|
||||
@ -54,7 +54,6 @@ namespace EightBit {
|
||||
protected:
|
||||
virtual void initialise() = 0;
|
||||
|
||||
[[nodiscard]] virtual MemoryMapping mapping(uint16_t address) = 0;
|
||||
[[nodiscard]] uint8_t& reference(uint16_t address);
|
||||
[[nodiscard]] auto& reference(const register16_t address) { return reference(address.word); }
|
||||
[[nodiscard]] uint8_t& reference() { return reference(ADDRESS()); }
|
||||
|
14
inc/Mapper.h
Normal file
14
inc/Mapper.h
Normal 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;
|
||||
};
|
||||
}
|
@ -9,7 +9,7 @@ namespace EightBit {
|
||||
|
||||
struct MemoryMapping {
|
||||
|
||||
enum class AccessLevel { Unknown, ReadOnly, ReadWrite, };
|
||||
enum class AccessLevel { Unknown, ReadOnly, WriteOnly, ReadWrite };
|
||||
|
||||
Memory& memory;
|
||||
uint16_t begin = Chip::Mask16;
|
||||
|
@ -29,7 +29,8 @@ namespace EightBit {
|
||||
|
||||
int run(int limit);
|
||||
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; }
|
||||
|
||||
@ -39,6 +40,7 @@ namespace EightBit {
|
||||
protected:
|
||||
Processor(Bus& memory);
|
||||
|
||||
[[nodiscard]] auto& opcode() noexcept { return m_opcode; }
|
||||
[[nodiscard]] auto& BUS() noexcept { return m_bus; }
|
||||
|
||||
[[nodiscard]] auto halted() noexcept { return lowered(HALT()); }
|
||||
@ -49,16 +51,23 @@ namespace EightBit {
|
||||
virtual void handleINT();
|
||||
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) {
|
||||
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) {
|
||||
BUS().write(register16_t(offset, page), value);
|
||||
busWrite(register16_t(offset, page), value);
|
||||
}
|
||||
|
||||
[[nodiscard]] auto fetchByte() {
|
||||
return BUS().read(PC()++);
|
||||
return busRead(PC()++);
|
||||
}
|
||||
|
||||
[[nodiscard]] virtual register16_t getWord() = 0;
|
||||
@ -102,6 +111,7 @@ namespace EightBit {
|
||||
|
||||
private:
|
||||
Bus& m_bus;
|
||||
uint8_t m_opcode = Mask8;
|
||||
int m_cycles = 0;
|
||||
register16_t m_pc;
|
||||
|
||||
|
@ -5,29 +5,29 @@ EightBit::BigEndianProcessor::BigEndianProcessor(Bus& memory)
|
||||
: Processor(memory) {}
|
||||
|
||||
EightBit::register16_t EightBit::BigEndianProcessor::getWord() {
|
||||
const auto high = BUS().read();
|
||||
const auto high = busRead();
|
||||
++BUS().ADDRESS();
|
||||
const auto low = BUS().read();
|
||||
const auto low = busRead();
|
||||
return { low, high };
|
||||
}
|
||||
|
||||
void EightBit::BigEndianProcessor::setWord(const register16_t value) {
|
||||
BUS().write(value.high);
|
||||
busWrite(value.high);
|
||||
++BUS().ADDRESS();
|
||||
BUS().write(value.low);
|
||||
busWrite(value.low);
|
||||
}
|
||||
|
||||
EightBit::register16_t EightBit::BigEndianProcessor::getWordPaged(const uint8_t page, const uint8_t offset) {
|
||||
const auto high = getBytePaged(page, offset);
|
||||
++BUS().ADDRESS().low;
|
||||
const auto low = BUS().read();
|
||||
const auto low = busRead();
|
||||
return { low, high };
|
||||
}
|
||||
|
||||
void EightBit::BigEndianProcessor::setWordPaged(const uint8_t page, const uint8_t offset, const register16_t value) {
|
||||
setBytePaged(page, offset, value.high);
|
||||
++BUS().ADDRESS().low;
|
||||
BUS().write(value.low);
|
||||
busWrite(value.low);
|
||||
}
|
||||
|
||||
EightBit::register16_t EightBit::BigEndianProcessor::fetchWord() {
|
||||
|
@ -150,6 +150,7 @@
|
||||
<ClInclude Include="..\inc\InputOutput.h" />
|
||||
<ClInclude Include="..\inc\IntelProcessor.h" />
|
||||
<ClInclude Include="..\inc\LittleEndianProcessor.h" />
|
||||
<ClInclude Include="..\inc\Mapper.h" />
|
||||
<ClInclude Include="..\inc\Rom.h" />
|
||||
<ClInclude Include="..\inc\Memory.h" />
|
||||
<ClInclude Include="..\inc\MemoryMapping.h" />
|
||||
|
@ -65,6 +65,9 @@
|
||||
<ClInclude Include="..\inc\Memory.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\inc\Mapper.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="stdafx.cpp">
|
||||
|
@ -5,29 +5,29 @@ EightBit::LittleEndianProcessor::LittleEndianProcessor(Bus& memory)
|
||||
: Processor(memory) {}
|
||||
|
||||
EightBit::register16_t EightBit::LittleEndianProcessor::getWord() {
|
||||
const auto low = BUS().read();
|
||||
const auto low = busRead();
|
||||
++BUS().ADDRESS();
|
||||
const auto high = BUS().read();
|
||||
const auto high = busRead();
|
||||
return { low, high };
|
||||
}
|
||||
|
||||
void EightBit::LittleEndianProcessor::setWord(const register16_t value) {
|
||||
BUS().write(value.low);
|
||||
busWrite(value.low);
|
||||
++BUS().ADDRESS();
|
||||
BUS().write(value.high);
|
||||
busWrite(value.high);
|
||||
}
|
||||
|
||||
EightBit::register16_t EightBit::LittleEndianProcessor::getWordPaged(const uint8_t page, const uint8_t offset) {
|
||||
const auto low = getBytePaged(page, offset);
|
||||
++BUS().ADDRESS().low;
|
||||
const auto high = BUS().read();
|
||||
const auto high = busRead();
|
||||
return { low, high };
|
||||
}
|
||||
|
||||
void EightBit::LittleEndianProcessor::setWordPaged(const uint8_t page, const uint8_t offset, const register16_t value) {
|
||||
setBytePaged(page, offset, value.low);
|
||||
++BUS().ADDRESS().low;
|
||||
BUS().write(value.high);
|
||||
busWrite(value.high);
|
||||
}
|
||||
|
||||
EightBit::register16_t EightBit::LittleEndianProcessor::fetchWord() {
|
||||
|
@ -25,6 +25,29 @@ void EightBit::Processor::handleIRQ() {
|
||||
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 current = 0;
|
||||
while (LIKELY(powered() && (current < limit)))
|
||||
@ -32,6 +55,11 @@ int EightBit::Processor::run(const int limit) {
|
||||
return current;
|
||||
}
|
||||
|
||||
int EightBit::Processor::execute(const uint8_t value) {
|
||||
opcode() = value;
|
||||
return execute();
|
||||
}
|
||||
|
||||
// http://graphics.stanford.edu/~seander/bithacks.html#FixedSignExtend
|
||||
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
|
||||
|
Loading…
Reference in New Issue
Block a user