mirror of
https://github.com/MoleskiCoder/EightBit.git
synced 2024-09-09 21:54:44 +00:00
f7da03d46b
Signed-off-by: Adrian Conlon <adrian.conlon@gmail.com>
481 lines
13 KiB
C++
481 lines
13 KiB
C++
#pragma once
|
||
|
||
#include <cstdint>
|
||
#include <cassert>
|
||
#include <stdexcept>
|
||
|
||
#include <IntelProcessor.h>
|
||
#include <EventArgs.h>
|
||
#include <Signal.h>
|
||
#include <Register.h>
|
||
#include <EightBitCompilerDefinitions.h>
|
||
|
||
namespace EightBit {
|
||
|
||
class Bus;
|
||
|
||
class Z80 final : public IntelProcessor {
|
||
public:
|
||
struct refresh_t {
|
||
|
||
bool high : 1;
|
||
uint8_t variable : 7;
|
||
|
||
refresh_t(const uint8_t value)
|
||
: high(!!(value & Bit7)),
|
||
variable(value & Mask7)
|
||
{ }
|
||
|
||
operator uint8_t() const {
|
||
return (high << 7) | variable;
|
||
}
|
||
|
||
auto& operator++() {
|
||
++variable;
|
||
return *this;
|
||
}
|
||
};
|
||
|
||
enum StatusBits {
|
||
SF = Bit7,
|
||
ZF = Bit6,
|
||
YF = Bit5,
|
||
HC = Bit4,
|
||
XF = Bit3,
|
||
PF = Bit2,
|
||
VF = Bit2,
|
||
NF = Bit1,
|
||
CF = Bit0,
|
||
};
|
||
|
||
Z80(Bus& bus);
|
||
|
||
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;
|
||
|
||
[[nodiscard]] register16_t& AF() final { return m_accumulatorFlags[m_accumulatorFlagsSet]; }
|
||
[[nodiscard]] register16_t& BC() final { return m_registers[m_registerSet][BC_IDX]; }
|
||
[[nodiscard]] register16_t& DE() final { return m_registers[m_registerSet][DE_IDX]; }
|
||
[[nodiscard]] register16_t& HL() final { return m_registers[m_registerSet][HL_IDX]; }
|
||
|
||
[[nodiscard]] auto& IX() { return m_ix; }
|
||
[[nodiscard]] auto& IXH() { return IX().high; }
|
||
[[nodiscard]] auto& IXL() { return IX().low; }
|
||
|
||
[[nodiscard]] auto& IY() { return m_iy; }
|
||
[[nodiscard]] auto& IYH() { return IY().high; }
|
||
[[nodiscard]] auto& IYL() { return IY().low; }
|
||
|
||
// ** From the Z80 CPU User Manual
|
||
// Memory Refresh(R) Register.The Z80 CPU contains a memory refresh counter,
|
||
// enabling dynamic memories to be used with the same ease as static memories.Seven bits
|
||
// of this 8-bit register are automatically incremented after each instruction fetch.The eighth
|
||
// bit remains as programmed, resulting from an LD R, A instruction. The data in the refresh
|
||
// counter is sent out on the lower portion of the address bus along with a refresh control
|
||
// signal while the CPU is decoding and executing the fetched instruction. This mode of refresh
|
||
// is transparent to the programmer and does not slow the CPU operation.The programmer
|
||
// can load the R register for testing purposes, but this register is normally not used by the
|
||
// programmer. During refresh, the contents of the I Register are placed on the upper eight
|
||
// bits of the address bus.
|
||
[[nodiscard]] auto& REFRESH() { return m_refresh; }
|
||
|
||
[[nodiscard]] auto& IV() { return iv; }
|
||
[[nodiscard]] auto& IM() { return m_interruptMode; }
|
||
[[nodiscard]] auto& IFF1() { return m_iff1; }
|
||
[[nodiscard]] auto& IFF2() { return m_iff2; }
|
||
|
||
void exx() {
|
||
m_registerSet ^= 1;
|
||
}
|
||
|
||
void exxAF() {
|
||
m_accumulatorFlagsSet ^= 1;
|
||
}
|
||
|
||
bool requestingIO() { return lowered(IORQ()); }
|
||
bool requestingMemory() { return lowered(MREQ()); }
|
||
|
||
bool requestingRead() { return lowered(RD()); }
|
||
bool requestingWrite() { return lowered(WR()); }
|
||
|
||
// ** From the Z80 CPU User Manual
|
||
// RFSH.Refresh(output, active Low). RFSH, together with MREQ, indicates that the lower
|
||
// seven bits of the system<65>s address bus can be used as a refresh address to the system<65>s
|
||
// dynamic memories.
|
||
DECLARE_PIN_OUTPUT(RFSH)
|
||
|
||
DECLARE_PIN_INPUT(NMI)
|
||
DECLARE_PIN_OUTPUT(M1)
|
||
DECLARE_PIN_OUTPUT(MREQ)
|
||
DECLARE_PIN_OUTPUT(IORQ)
|
||
DECLARE_PIN_OUTPUT(RD)
|
||
DECLARE_PIN_OUTPUT(WR)
|
||
|
||
protected:
|
||
void handleRESET() final;
|
||
void handleINT() final;
|
||
|
||
void pushWord(const register16_t destination) override {
|
||
tick();
|
||
IntelProcessor::pushWord(destination);
|
||
}
|
||
|
||
void memoryWrite() final;
|
||
uint8_t memoryRead() final;
|
||
|
||
void busWrite() final;
|
||
uint8_t busRead() final;
|
||
|
||
void jr(int8_t offset) final {
|
||
IntelProcessor::jr(offset);
|
||
tick(5);
|
||
}
|
||
|
||
int jrConditional(const int condition) final {
|
||
if (!IntelProcessor::jrConditional(condition))
|
||
tick(3);
|
||
return condition;
|
||
}
|
||
|
||
private:
|
||
|
||
DEFINE_PIN_ACTIVATOR_LOW(RFSH)
|
||
DEFINE_PIN_ACTIVATOR_LOW(M1)
|
||
DEFINE_PIN_ACTIVATOR_LOW(MREQ)
|
||
DEFINE_PIN_ACTIVATOR_LOW(IORQ)
|
||
DEFINE_PIN_ACTIVATOR_LOW(RD)
|
||
DEFINE_PIN_ACTIVATOR_LOW(WR)
|
||
|
||
auto readBusDataM1() {
|
||
_ActivateM1 m1(*this);
|
||
return BUS().DATA();
|
||
}
|
||
|
||
enum { BC_IDX, DE_IDX, HL_IDX };
|
||
|
||
std::array<std::array<register16_t, 3>, 2> m_registers;
|
||
int m_registerSet = 0;
|
||
|
||
std::array<register16_t, 2> m_accumulatorFlags;
|
||
int m_accumulatorFlagsSet = 0;
|
||
|
||
register16_t m_ix = 0xffff;
|
||
register16_t m_iy = 0xffff;
|
||
|
||
refresh_t m_refresh = 0x7f;
|
||
|
||
uint8_t iv = 0xff;
|
||
int m_interruptMode = 0;
|
||
bool m_iff1 = false;
|
||
bool m_iff2 = false;
|
||
|
||
bool m_prefixCB = false;
|
||
bool m_prefixDD = false;
|
||
bool m_prefixED = false;
|
||
bool m_prefixFD = false;
|
||
|
||
int8_t m_displacement = 0;
|
||
bool m_displaced = false;
|
||
|
||
void handleNMI();
|
||
|
||
[[nodiscard]] uint16_t displacedAddress();
|
||
void fetchDisplacement();
|
||
[[nodiscard]] uint8_t fetchOpCode();
|
||
|
||
typedef std::function<register16_t(void)> addresser_t;
|
||
typedef std::function<uint8_t(void)> reader_t;
|
||
|
||
void loadAccumulatorIndirect(addresser_t addresser) {
|
||
(MEMPTR() = BUS().ADDRESS() = addresser())++;
|
||
A() = memoryRead();
|
||
}
|
||
|
||
void storeAccumulatorIndirect(addresser_t addresser) {
|
||
(MEMPTR() = BUS().ADDRESS() = addresser())++;
|
||
MEMPTR().high = BUS().DATA() = A();
|
||
memoryWrite();
|
||
}
|
||
|
||
void readInternalRegister(reader_t reader) {
|
||
F() = adjustSZXY<Z80>(F(), A() = reader());
|
||
F() = clearBit(F(), NF | HC);
|
||
F() = setBit(F(), PF, IFF2());
|
||
tick();
|
||
}
|
||
|
||
[[nodiscard]] auto& HL2() {
|
||
if (LIKELY(!m_displaced))
|
||
return HL();
|
||
if (m_prefixDD)
|
||
return IX();
|
||
// Must be FD prefix
|
||
return IY();
|
||
}
|
||
|
||
[[nodiscard]] auto& RP(const int rp) {
|
||
ASSUME(rp >= 0);
|
||
ASSUME(rp <= 3);
|
||
switch (rp) {
|
||
case 0b00:
|
||
return BC();
|
||
case 0b01:
|
||
return DE();
|
||
case 0b10:
|
||
return HL2();
|
||
case 0b11:
|
||
return SP();
|
||
default:
|
||
UNREACHABLE;
|
||
}
|
||
}
|
||
|
||
[[nodiscard]] auto& RP2(const int rp) {
|
||
ASSUME(rp >= 0);
|
||
ASSUME(rp <= 3);
|
||
switch (rp) {
|
||
case 0b00:
|
||
return BC();
|
||
case 0b01:
|
||
return DE();
|
||
case 0b10:
|
||
return HL2();
|
||
case 0b11:
|
||
return AF();
|
||
default:
|
||
UNREACHABLE;
|
||
}
|
||
}
|
||
|
||
[[nodiscard]] auto R(const int r) {
|
||
ASSUME(r >= 0);
|
||
ASSUME(r <= 7);
|
||
switch (r) {
|
||
case 0:
|
||
return B();
|
||
case 1:
|
||
return C();
|
||
case 2:
|
||
return D();
|
||
case 3:
|
||
return E();
|
||
case 4:
|
||
return HL2().high;
|
||
case 5:
|
||
return HL2().low;
|
||
case 6:
|
||
return IntelProcessor::memoryRead(UNLIKELY(m_displaced) ? displacedAddress() : HL().word);
|
||
case 7:
|
||
return A();
|
||
default:
|
||
UNREACHABLE;
|
||
}
|
||
}
|
||
|
||
void R(const int r, const uint8_t value) {
|
||
ASSUME(r >= 0);
|
||
ASSUME(r <= 7);
|
||
switch (r) {
|
||
case 0:
|
||
B() = value;
|
||
break;
|
||
case 1:
|
||
C() = value;
|
||
break;
|
||
case 2:
|
||
D() = value;
|
||
break;
|
||
case 3:
|
||
E() = value;
|
||
break;
|
||
case 4:
|
||
HL2().high = value;
|
||
break;
|
||
case 5:
|
||
HL2().low = value;
|
||
break;
|
||
case 6:
|
||
IntelProcessor::memoryWrite(UNLIKELY(m_displaced) ? displacedAddress() : HL().word, value);
|
||
break;
|
||
case 7:
|
||
A() = value;
|
||
break;
|
||
default:
|
||
UNREACHABLE;
|
||
}
|
||
}
|
||
|
||
void R2(const int r, const uint8_t value) {
|
||
ASSUME(r >= 0);
|
||
ASSUME(r <= 7);
|
||
switch (r) {
|
||
case 0:
|
||
B() = value;
|
||
break;
|
||
case 1:
|
||
C() = value;
|
||
break;
|
||
case 2:
|
||
D() = value;
|
||
break;
|
||
case 3:
|
||
E() = value;
|
||
break;
|
||
case 4:
|
||
H() = value;
|
||
break;
|
||
case 5:
|
||
L() = value;
|
||
break;
|
||
case 6:
|
||
IntelProcessor::memoryWrite(HL(), value);
|
||
break;
|
||
case 7:
|
||
A() = value;
|
||
break;
|
||
default:
|
||
UNREACHABLE;
|
||
}
|
||
}
|
||
|
||
[[nodiscard]] static auto adjustHalfCarryAdd(uint8_t f, const uint8_t before, const uint8_t value, const int calculation) {
|
||
return setBit(f, HC, calculateHalfCarryAdd(before, value, calculation));
|
||
}
|
||
|
||
[[nodiscard]] static auto adjustHalfCarrySub(uint8_t f, const uint8_t before, const uint8_t value, const int calculation) {
|
||
return setBit(f, HC, calculateHalfCarrySub(before, value, calculation));
|
||
}
|
||
|
||
[[nodiscard]] static uint8_t adjustOverflowAdd(uint8_t f, const uint8_t before, const uint8_t value, const uint8_t calculation) {
|
||
return adjustOverflowAdd(f, before & SF, value & SF, calculation & SF);
|
||
}
|
||
|
||
[[nodiscard]] static uint8_t adjustOverflowAdd(uint8_t f, const int beforeNegative, const int valueNegative, const int afterNegative) {
|
||
const auto overflow = (beforeNegative == valueNegative) && (beforeNegative != afterNegative);
|
||
return setBit(f, VF, overflow);
|
||
}
|
||
|
||
[[nodiscard]] static uint8_t adjustOverflowSub(uint8_t f, const uint8_t before, const uint8_t value, const uint8_t calculation) {
|
||
return adjustOverflowSub(f, before & SF, value & SF, calculation & SF);
|
||
}
|
||
|
||
[[nodiscard]] static uint8_t adjustOverflowSub(uint8_t f, const int beforeNegative, const int valueNegative, const int afterNegative) {
|
||
const auto overflow = (beforeNegative != valueNegative) && (beforeNegative != afterNegative);
|
||
return setBit(f, VF, overflow);
|
||
}
|
||
|
||
[[nodiscard]] static bool convertCondition(uint8_t f, int flag);
|
||
|
||
static uint8_t subtract(uint8_t& f, uint8_t operand, uint8_t value, int carry = 0);
|
||
|
||
void executeCB(int x, int y, int z);
|
||
void executeED(int x, int y, int z, int p, int q);
|
||
void executeOther(int x, int y, int z, int p, int q);
|
||
|
||
[[nodiscard]] static uint8_t increment(uint8_t& f, uint8_t operand);
|
||
[[nodiscard]] static uint8_t decrement(uint8_t& f, uint8_t operand);
|
||
|
||
void di();
|
||
void ei();
|
||
|
||
void retn();
|
||
void reti();
|
||
|
||
void returnConditionalFlag(uint8_t f, int flag);
|
||
void jrConditionalFlag(uint8_t f, int flag);
|
||
void callConditionalFlag(uint8_t f, int flag);
|
||
void jumpConditionalFlag(uint8_t f, int flag);
|
||
|
||
[[nodiscard]] register16_t sbc(uint8_t& f, register16_t operand, register16_t value);
|
||
[[nodiscard]] register16_t adc(uint8_t& f, register16_t operand, register16_t value);
|
||
[[nodiscard]] register16_t add(uint8_t& f, register16_t operand, register16_t value, int carry = 0);
|
||
|
||
[[nodiscard]] static uint8_t add(uint8_t& f, uint8_t operand, uint8_t value, int carry = 0);
|
||
[[nodiscard]] static uint8_t adc(uint8_t& f, uint8_t operand, uint8_t value);
|
||
[[nodiscard]] static uint8_t sub(uint8_t& f, uint8_t operand, uint8_t value, int carry = 0);
|
||
[[nodiscard]] static uint8_t sbc(uint8_t& f, uint8_t operand, uint8_t value);
|
||
[[nodiscard]] static uint8_t andr(uint8_t& f, uint8_t operand, uint8_t value);
|
||
[[nodiscard]] static uint8_t xorr(uint8_t& f, uint8_t operand, uint8_t value);
|
||
[[nodiscard]] static uint8_t orr(uint8_t& f, uint8_t operand, uint8_t value);
|
||
static void compare(uint8_t& f, uint8_t operand, uint8_t value);
|
||
|
||
[[nodiscard]] static uint8_t rlc(uint8_t& f, uint8_t operand);
|
||
[[nodiscard]] static uint8_t rrc(uint8_t& f, uint8_t operand);
|
||
[[nodiscard]] static uint8_t rl(uint8_t& f, uint8_t operand);
|
||
[[nodiscard]] static uint8_t rr(uint8_t& f, uint8_t operand);
|
||
[[nodiscard]] static uint8_t sla(uint8_t& f, uint8_t operand);
|
||
[[nodiscard]] static uint8_t sra(uint8_t& f, uint8_t operand);
|
||
[[nodiscard]] static uint8_t sll(uint8_t& f, uint8_t operand);
|
||
[[nodiscard]] static uint8_t srl(uint8_t& f, uint8_t operand);
|
||
|
||
static void bit(uint8_t& f, int n, uint8_t operand);
|
||
[[nodiscard]] static uint8_t res(int n, uint8_t operand);
|
||
[[nodiscard]] static uint8_t set(int n, uint8_t operand);
|
||
|
||
[[nodiscard]] static uint8_t daa(uint8_t& f, uint8_t operand);
|
||
|
||
static void scf(uint8_t& f, uint8_t operand);
|
||
static void ccf(uint8_t& f, uint8_t operand);
|
||
static uint8_t cpl(uint8_t& f, uint8_t operand);
|
||
|
||
void xhtl(register16_t& exchange);
|
||
|
||
void blockCompare(uint8_t& f, uint8_t value, register16_t source, register16_t& counter);
|
||
|
||
void cpi(uint8_t& f, uint8_t value);
|
||
bool cpir(uint8_t& f, uint8_t value);
|
||
|
||
void cpd(uint8_t& f, uint8_t value);
|
||
bool cpdr(uint8_t& f, uint8_t value);
|
||
|
||
void blockLoad(uint8_t& f, uint8_t a, register16_t source, register16_t destination, register16_t& counter);
|
||
|
||
void ldi(uint8_t& f, uint8_t a);
|
||
bool ldir(uint8_t& f, uint8_t a);
|
||
|
||
void ldd(uint8_t& f, uint8_t a);
|
||
bool lddr(uint8_t& f, uint8_t a);
|
||
|
||
void blockIn(register16_t& source, register16_t destination);
|
||
|
||
void ini();
|
||
bool inir();
|
||
|
||
void ind();
|
||
bool indr();
|
||
|
||
void blockOut(register16_t source, register16_t& destination);
|
||
|
||
void outi();
|
||
bool otir();
|
||
|
||
void outd();
|
||
bool otdr();
|
||
|
||
[[nodiscard]] uint8_t neg(uint8_t& f, uint8_t operand);
|
||
|
||
void rrd(uint8_t& f, register16_t address, uint8_t& update);
|
||
void rld(uint8_t& f, register16_t address, uint8_t& update);
|
||
|
||
void portWrite(uint8_t port);
|
||
void portWrite();
|
||
|
||
uint8_t portRead(uint8_t port);
|
||
uint8_t portRead();
|
||
};
|
||
} |