2017-06-04 20:38:34 +00:00
|
|
|
#pragma once
|
|
|
|
|
2017-06-05 21:39:15 +00:00
|
|
|
#include <cstdint>
|
|
|
|
|
2017-06-11 08:45:34 +00:00
|
|
|
#include "IntelProcessor.h"
|
2017-06-05 21:39:15 +00:00
|
|
|
#include "InputOutput.h"
|
2017-06-12 13:33:00 +00:00
|
|
|
#include "Signal.h"
|
2017-06-04 20:38:34 +00:00
|
|
|
|
2017-06-05 21:39:15 +00:00
|
|
|
namespace EightBit {
|
2017-06-11 08:45:34 +00:00
|
|
|
class Z80 : public IntelProcessor {
|
2017-06-05 21:39:15 +00:00
|
|
|
public:
|
|
|
|
enum StatusBits {
|
|
|
|
SF = Bit7,
|
|
|
|
ZF = Bit6,
|
|
|
|
YF = Bit5,
|
|
|
|
HC = Bit4,
|
|
|
|
XF = Bit3,
|
|
|
|
PF = Bit2,
|
|
|
|
VF = Bit2,
|
|
|
|
NF = Bit1,
|
|
|
|
CF = Bit0,
|
|
|
|
};
|
2017-06-04 20:38:34 +00:00
|
|
|
|
2017-06-05 21:39:15 +00:00
|
|
|
Z80(Memory& memory, InputOutput& ports);
|
2017-06-04 20:38:34 +00:00
|
|
|
|
2017-06-05 21:39:15 +00:00
|
|
|
Signal<Z80> ExecutingInstruction;
|
2017-06-04 20:38:34 +00:00
|
|
|
|
2017-06-11 20:08:40 +00:00
|
|
|
void di();
|
|
|
|
void ei();
|
2017-06-04 20:38:34 +00:00
|
|
|
|
2017-06-05 21:39:15 +00:00
|
|
|
int interruptMaskable(uint8_t value) { return interrupt(true, value); }
|
|
|
|
int interruptMaskable() { return interruptMaskable(0); }
|
|
|
|
int interruptNonMaskable() { return interrupt(false, 0); }
|
2017-06-04 20:38:34 +00:00
|
|
|
|
2017-06-05 21:39:15 +00:00
|
|
|
int interrupt(bool maskable, uint8_t value);
|
2017-06-04 20:38:34 +00:00
|
|
|
|
2017-06-05 21:39:15 +00:00
|
|
|
int execute(uint8_t opcode);
|
|
|
|
int step();
|
2017-06-04 20:38:34 +00:00
|
|
|
|
2017-06-05 21:39:15 +00:00
|
|
|
bool getM1() const { return m1; }
|
2017-06-04 20:38:34 +00:00
|
|
|
|
2017-06-05 21:39:15 +00:00
|
|
|
// Mutable access to processor!!
|
2017-06-04 20:38:34 +00:00
|
|
|
|
2017-06-05 21:39:15 +00:00
|
|
|
register16_t& AF() {
|
|
|
|
return m_accumulatorFlags[m_accumulatorFlagsSet];
|
|
|
|
}
|
2017-06-04 20:38:34 +00:00
|
|
|
|
2017-06-05 21:39:15 +00:00
|
|
|
uint8_t& A() { return AF().high; }
|
|
|
|
uint8_t& F() { return AF().low; }
|
|
|
|
|
|
|
|
register16_t& BC() {
|
|
|
|
return m_registers[m_registerSet][BC_IDX];
|
|
|
|
}
|
2017-06-04 20:38:34 +00:00
|
|
|
|
2017-06-05 21:39:15 +00:00
|
|
|
uint8_t& B() { return BC().high; }
|
|
|
|
uint8_t& C() { return BC().low; }
|
2017-06-04 20:38:34 +00:00
|
|
|
|
2017-06-05 21:39:15 +00:00
|
|
|
register16_t& DE() {
|
|
|
|
return m_registers[m_registerSet][DE_IDX];
|
|
|
|
}
|
2017-06-04 20:38:34 +00:00
|
|
|
|
2017-06-05 21:39:15 +00:00
|
|
|
uint8_t& D() { return DE().high; }
|
|
|
|
uint8_t& E() { return DE().low; }
|
2017-06-04 20:38:34 +00:00
|
|
|
|
2017-06-05 21:39:15 +00:00
|
|
|
register16_t& HL() {
|
|
|
|
return m_registers[m_registerSet][HL_IDX];
|
|
|
|
}
|
2017-06-04 20:38:34 +00:00
|
|
|
|
2017-06-05 21:39:15 +00:00
|
|
|
uint8_t& H() { return HL().high; }
|
|
|
|
uint8_t& L() { return HL().low; }
|
2017-06-04 20:38:34 +00:00
|
|
|
|
2017-06-05 21:39:15 +00:00
|
|
|
register16_t& IX() { return m_ix; }
|
|
|
|
uint8_t& IXH() { return IX().high; }
|
|
|
|
uint8_t& IXL() { return IX().low; }
|
2017-06-04 20:38:34 +00:00
|
|
|
|
2017-06-05 21:39:15 +00:00
|
|
|
register16_t& IY() { return m_iy; }
|
|
|
|
uint8_t& IYH() { return IY().high; }
|
|
|
|
uint8_t& IYL() { return IY().low; }
|
2017-06-04 20:38:34 +00:00
|
|
|
|
2017-06-05 21:39:15 +00:00
|
|
|
uint8_t& REFRESH() { return m_refresh; }
|
|
|
|
uint8_t& IV() { return iv; }
|
|
|
|
int& IM() { return m_interruptMode; }
|
|
|
|
bool& IFF1() { return m_iff1; }
|
|
|
|
bool& IFF2() { return m_iff2; }
|
2017-06-04 20:38:34 +00:00
|
|
|
|
2017-06-05 21:39:15 +00:00
|
|
|
bool& M1() { return m1; }
|
2017-06-04 20:38:34 +00:00
|
|
|
|
2017-06-05 21:39:15 +00:00
|
|
|
void exx() {
|
|
|
|
m_registerSet ^= 1;
|
|
|
|
}
|
2017-06-04 20:38:34 +00:00
|
|
|
|
2017-06-05 21:39:15 +00:00
|
|
|
void exxAF() {
|
|
|
|
m_accumulatorFlagsSet = !m_accumulatorFlagsSet;
|
|
|
|
}
|
2017-06-04 20:38:34 +00:00
|
|
|
|
2017-06-05 21:39:15 +00:00
|
|
|
virtual void reset();
|
|
|
|
virtual void initialise();
|
2017-06-04 20:38:34 +00:00
|
|
|
|
2017-06-11 08:45:34 +00:00
|
|
|
protected:
|
2017-06-05 21:39:15 +00:00
|
|
|
InputOutput& m_ports;
|
2017-06-04 20:38:34 +00:00
|
|
|
|
2017-06-05 21:39:15 +00:00
|
|
|
enum { BC_IDX, DE_IDX, HL_IDX };
|
2017-06-04 20:38:34 +00:00
|
|
|
|
2017-06-05 21:39:15 +00:00
|
|
|
std::array<std::array<register16_t, 3>, 2> m_registers;
|
|
|
|
int m_registerSet;
|
2017-06-04 20:38:34 +00:00
|
|
|
|
2017-06-05 21:39:15 +00:00
|
|
|
std::array<register16_t, 2> m_accumulatorFlags;
|
|
|
|
int m_accumulatorFlagsSet;
|
2017-06-04 20:38:34 +00:00
|
|
|
|
2017-06-05 21:39:15 +00:00
|
|
|
register16_t m_ix;
|
|
|
|
register16_t m_iy;
|
2017-06-04 20:38:34 +00:00
|
|
|
|
2017-06-05 21:39:15 +00:00
|
|
|
uint8_t m_refresh;
|
|
|
|
uint8_t iv;
|
|
|
|
int m_interruptMode;
|
|
|
|
bool m_iff1;
|
|
|
|
bool m_iff2;
|
2017-06-04 20:38:34 +00:00
|
|
|
|
2017-06-05 21:39:15 +00:00
|
|
|
bool m1;
|
2017-06-04 20:38:34 +00:00
|
|
|
|
2017-06-05 21:39:15 +00:00
|
|
|
bool m_prefixCB;
|
|
|
|
bool m_prefixDD;
|
|
|
|
bool m_prefixED;
|
|
|
|
bool m_prefixFD;
|
2017-06-04 20:38:34 +00:00
|
|
|
|
2017-06-05 21:39:15 +00:00
|
|
|
int8_t m_displacement;
|
2017-06-04 20:38:34 +00:00
|
|
|
|
2017-06-05 21:39:15 +00:00
|
|
|
int fetchExecute() {
|
|
|
|
M1() = true;
|
2017-06-11 20:08:40 +00:00
|
|
|
return execute(fetchByte());
|
2017-06-05 21:39:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void incrementRefresh() {
|
|
|
|
auto incremented = ((REFRESH() & Mask7) + 1) & Mask7;
|
|
|
|
REFRESH() = (REFRESH() & Bit7) | incremented;
|
|
|
|
}
|
|
|
|
|
|
|
|
void clearFlag(int flag) { F() &= ~flag; }
|
|
|
|
void setFlag(int flag) { F() |= flag; }
|
|
|
|
|
|
|
|
void setFlag(int flag, int condition) { setFlag(flag, condition != 0); }
|
|
|
|
void setFlag(int flag, uint32_t condition) { setFlag(flag, condition != 0); }
|
|
|
|
void setFlag(int flag, bool condition) { condition ? setFlag(flag) : clearFlag(flag); }
|
|
|
|
|
|
|
|
void clearFlag(int flag, int condition) { clearFlag(flag, condition != 0); }
|
|
|
|
void clearFlag(int flag, uint32_t condition) { clearFlag(flag, condition != 0); }
|
|
|
|
void clearFlag(int flag, bool condition) { condition ? clearFlag(flag) : setFlag(flag); }
|
|
|
|
|
|
|
|
uint8_t& DISPLACED() {
|
|
|
|
if (!(m_prefixDD || m_prefixFD))
|
|
|
|
throw std::logic_error("Unprefixed indexed displacement requested");
|
|
|
|
m_memory.ADDRESS().word = MEMPTR().word = (m_prefixDD ? IX() : IY()).word + m_displacement;
|
2017-06-04 20:38:34 +00:00
|
|
|
return m_memory.reference();
|
2017-06-05 21:39:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t& R(int r) {
|
|
|
|
switch (r) {
|
|
|
|
case 0:
|
|
|
|
return B();
|
|
|
|
case 1:
|
|
|
|
return C();
|
|
|
|
case 2:
|
|
|
|
return D();
|
|
|
|
case 3:
|
|
|
|
return E();
|
|
|
|
case 4:
|
|
|
|
return ALT_HL().high;
|
|
|
|
case 5:
|
|
|
|
return ALT_HL().low;
|
|
|
|
case 6:
|
|
|
|
if (m_prefixDD || m_prefixFD) {
|
2017-06-11 20:08:40 +00:00
|
|
|
m_displacement = fetchByte();
|
2017-06-05 21:39:15 +00:00
|
|
|
return DISPLACED();
|
|
|
|
}
|
|
|
|
m_memory.ADDRESS() = HL();
|
|
|
|
return m_memory.reference();
|
|
|
|
case 7:
|
|
|
|
return A();
|
|
|
|
}
|
|
|
|
throw std::logic_error("Unhandled registry mechanism");
|
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t& R2(int r) {
|
|
|
|
switch (r) {
|
|
|
|
case 0:
|
|
|
|
return B();
|
|
|
|
case 1:
|
|
|
|
return C();
|
|
|
|
case 2:
|
|
|
|
return D();
|
|
|
|
case 3:
|
|
|
|
return E();
|
|
|
|
case 4:
|
|
|
|
return H();
|
|
|
|
case 5:
|
|
|
|
return L();
|
|
|
|
case 6:
|
|
|
|
m_memory.ADDRESS() = HL();
|
|
|
|
return m_memory.reference();
|
|
|
|
case 7:
|
|
|
|
return A();
|
|
|
|
}
|
|
|
|
throw std::logic_error("Unhandled registry mechanism");
|
|
|
|
}
|
|
|
|
|
|
|
|
register16_t& RP(int rp) {
|
|
|
|
switch (rp) {
|
|
|
|
case 3:
|
|
|
|
return sp;
|
|
|
|
case HL_IDX:
|
|
|
|
return ALT_HL();
|
|
|
|
default:
|
|
|
|
return m_registers[m_registerSet][rp];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
register16_t& ALT_HL() {
|
|
|
|
if (m_prefixDD)
|
|
|
|
return IX();
|
|
|
|
else if (m_prefixFD)
|
|
|
|
return IY();
|
|
|
|
return HL();
|
|
|
|
}
|
|
|
|
|
|
|
|
register16_t& RP2(int rp) {
|
|
|
|
switch (rp) {
|
|
|
|
case 3:
|
|
|
|
return AF();
|
|
|
|
case HL_IDX:
|
|
|
|
return ALT_HL();
|
|
|
|
default:
|
|
|
|
return m_registers[m_registerSet][rp];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void addViaMemptr(register16_t& hl, register16_t operand) {
|
|
|
|
MEMPTR().word = hl.word + 1;
|
|
|
|
add(hl, operand);
|
|
|
|
}
|
|
|
|
|
|
|
|
void sbcViaMemptr(register16_t& hl, register16_t operand) {
|
|
|
|
MEMPTR().word = hl.word + 1;
|
|
|
|
sbc(hl, operand);
|
|
|
|
}
|
|
|
|
|
|
|
|
void adcViaMemptr(register16_t& hl, register16_t operand) {
|
|
|
|
MEMPTR().word = hl.word + 1;
|
|
|
|
adc(hl, operand);
|
|
|
|
}
|
|
|
|
|
|
|
|
void adjustHalfCarryAdd(uint8_t before, uint8_t value, int calculation) {
|
2017-06-11 08:45:34 +00:00
|
|
|
setFlag(HC, calculateHalfCarryAdd(before, value, calculation));
|
2017-06-05 21:39:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void adjustHalfCarrySub(uint8_t before, uint8_t value, int calculation) {
|
2017-06-11 08:45:34 +00:00
|
|
|
setFlag(HC, calculateHalfCarrySub(before, value, calculation));
|
2017-06-05 21:39:15 +00:00
|
|
|
}
|
2017-06-04 20:38:34 +00:00
|
|
|
|
2017-06-05 21:39:15 +00:00
|
|
|
void adjustOverflowAdd(uint8_t before, uint8_t value, uint8_t calculation) {
|
|
|
|
adjustOverflowAdd(before & SF, value & SF, calculation & SF);
|
|
|
|
}
|
2017-06-04 20:38:34 +00:00
|
|
|
|
2017-06-05 21:39:15 +00:00
|
|
|
void adjustOverflowAdd(int beforeNegative, int valueNegative, int afterNegative) {
|
|
|
|
auto overflow = (beforeNegative == valueNegative) && (beforeNegative != afterNegative);
|
|
|
|
setFlag(VF, overflow);
|
|
|
|
}
|
2017-06-04 20:38:34 +00:00
|
|
|
|
2017-06-05 21:39:15 +00:00
|
|
|
void adjustOverflowSub(uint8_t before, uint8_t value, uint8_t calculation) {
|
|
|
|
adjustOverflowSub(before & SF, value & SF, calculation & SF);
|
|
|
|
}
|
|
|
|
|
|
|
|
void adjustOverflowSub(int beforeNegative, int valueNegative, int afterNegative) {
|
|
|
|
auto overflow = (beforeNegative != valueNegative) && (beforeNegative != afterNegative);
|
|
|
|
setFlag(VF, overflow);
|
|
|
|
}
|
2017-06-04 20:38:34 +00:00
|
|
|
|
2017-06-05 21:39:15 +00:00
|
|
|
void executeCB(int x, int y, int z, int p, int q);
|
|
|
|
void executeED(int x, int y, int z, int p, int q);
|
|
|
|
void executeOther(int x, int y, int z, int p, int q);
|
2017-06-04 20:38:34 +00:00
|
|
|
|
2017-06-05 21:39:15 +00:00
|
|
|
void adjustSign(uint8_t value);
|
|
|
|
void adjustZero(uint8_t value);
|
|
|
|
void adjustParity(uint8_t value);
|
|
|
|
void adjustSZ(uint8_t value);
|
|
|
|
void adjustSZP(uint8_t value);
|
|
|
|
void adjustXY(uint8_t value);
|
|
|
|
void adjustSZPXY(uint8_t value);
|
|
|
|
void adjustSZXY(uint8_t value);
|
2017-06-04 20:38:34 +00:00
|
|
|
|
2017-06-05 21:39:15 +00:00
|
|
|
void postIncrement(uint8_t value);
|
|
|
|
void postDecrement(uint8_t value);
|
2017-06-04 20:38:34 +00:00
|
|
|
|
2017-06-05 21:39:15 +00:00
|
|
|
void retn();
|
|
|
|
void reti();
|
2017-06-04 20:38:34 +00:00
|
|
|
|
2017-06-11 20:08:40 +00:00
|
|
|
bool jrConditionalFlag(int flag);
|
|
|
|
bool returnConditionalFlag(int flag);
|
|
|
|
bool jumpConditionalFlag(int flag);
|
|
|
|
bool callConditionalFlag(int flag);
|
2017-06-04 20:38:34 +00:00
|
|
|
|
2017-06-05 21:39:15 +00:00
|
|
|
void sbc(register16_t& operand, register16_t value);
|
|
|
|
void adc(register16_t& operand, register16_t value);
|
2017-06-04 20:38:34 +00:00
|
|
|
|
2017-06-05 21:39:15 +00:00
|
|
|
void add(register16_t& operand, register16_t value);
|
2017-06-04 20:38:34 +00:00
|
|
|
|
2017-06-05 21:39:15 +00:00
|
|
|
void add(uint8_t& operand, uint8_t value, int carry = 0);
|
|
|
|
void adc(uint8_t& operand, uint8_t value);
|
|
|
|
void sub(uint8_t& operand, uint8_t value, int carry = 0);
|
|
|
|
void sbc(uint8_t& operand, uint8_t value);
|
|
|
|
void andr(uint8_t& operand, uint8_t value);
|
|
|
|
void xorr(uint8_t& operand, uint8_t value);
|
|
|
|
void orr(uint8_t& operand, uint8_t value);
|
|
|
|
void compare(uint8_t value);
|
2017-06-04 20:38:34 +00:00
|
|
|
|
2017-06-05 21:39:15 +00:00
|
|
|
void rlca();
|
|
|
|
void rrca();
|
|
|
|
void rla();
|
|
|
|
void rra();
|
2017-06-04 20:38:34 +00:00
|
|
|
|
2017-06-05 21:39:15 +00:00
|
|
|
void rlc(uint8_t& operand);
|
|
|
|
void rrc(uint8_t& operand);
|
|
|
|
void rl(uint8_t& operand);
|
|
|
|
void rr(uint8_t& operand);
|
|
|
|
void sla(uint8_t& operand);
|
|
|
|
void sra(uint8_t& operand);
|
|
|
|
void sll(uint8_t& operand);
|
|
|
|
void srl(uint8_t& operand);
|
2017-06-04 20:38:34 +00:00
|
|
|
|
2017-06-05 21:39:15 +00:00
|
|
|
void bit(int n, uint8_t& operand);
|
|
|
|
void res(int n, uint8_t& operand);
|
|
|
|
void set(int nit, uint8_t& operand);
|
2017-06-04 20:38:34 +00:00
|
|
|
|
2017-06-05 21:39:15 +00:00
|
|
|
void daa();
|
2017-06-04 20:38:34 +00:00
|
|
|
|
2017-06-05 21:39:15 +00:00
|
|
|
void scf();
|
|
|
|
void ccf();
|
|
|
|
void cpl();
|
|
|
|
|
|
|
|
void xhtl(register16_t& operand);
|
|
|
|
void xhtl();
|
|
|
|
|
|
|
|
void blockCompare();
|
|
|
|
|
|
|
|
void cpi();
|
2017-06-12 13:33:00 +00:00
|
|
|
bool cpir();
|
2017-06-05 21:39:15 +00:00
|
|
|
|
|
|
|
void cpd();
|
2017-06-12 13:33:00 +00:00
|
|
|
bool cpdr();
|
2017-06-05 21:39:15 +00:00
|
|
|
|
|
|
|
void blockLoad(register16_t source, register16_t destination);
|
|
|
|
|
|
|
|
void ldi();
|
2017-06-12 13:33:00 +00:00
|
|
|
bool ldir();
|
2017-06-05 21:39:15 +00:00
|
|
|
|
|
|
|
void ldd();
|
2017-06-12 13:33:00 +00:00
|
|
|
bool lddr();
|
2017-06-05 21:39:15 +00:00
|
|
|
|
|
|
|
void ini();
|
2017-06-12 13:33:00 +00:00
|
|
|
bool inir();
|
2017-06-05 21:39:15 +00:00
|
|
|
|
|
|
|
void ind();
|
2017-06-12 13:33:00 +00:00
|
|
|
bool indr();
|
2017-06-05 21:39:15 +00:00
|
|
|
|
|
|
|
void blockOut();
|
|
|
|
|
|
|
|
void outi();
|
2017-06-12 13:33:00 +00:00
|
|
|
bool otir();
|
2017-06-05 21:39:15 +00:00
|
|
|
|
|
|
|
void outd();
|
2017-06-12 13:33:00 +00:00
|
|
|
bool otdr();
|
2017-06-05 21:39:15 +00:00
|
|
|
|
|
|
|
void neg();
|
|
|
|
|
|
|
|
void rrd();
|
|
|
|
void rld();
|
|
|
|
|
|
|
|
void writePort() {
|
|
|
|
m_ports.write(m_memory.ADDRESS().low, m_memory.DATA());
|
|
|
|
}
|
|
|
|
|
|
|
|
void readPort() {
|
|
|
|
m_memory.placeDATA(m_ports.read(m_memory.ADDRESS().low));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|