2017-06-04 20:38:34 +00:00
|
|
|
|
#pragma once
|
|
|
|
|
|
2017-06-05 21:39:15 +00:00
|
|
|
|
#include <cstdint>
|
2021-01-09 08:41:48 +00:00
|
|
|
|
#include <array>
|
|
|
|
|
#include <functional>
|
2017-06-05 21:39:15 +00:00
|
|
|
|
|
2017-11-03 22:05:01 +00:00
|
|
|
|
#include <IntelProcessor.h>
|
2019-01-14 02:10:17 +00:00
|
|
|
|
#include <EventArgs.h>
|
2017-11-03 22:05:01 +00:00
|
|
|
|
#include <Signal.h>
|
|
|
|
|
#include <Register.h>
|
2017-06-04 20:38:34 +00:00
|
|
|
|
|
2017-06-05 21:39:15 +00:00
|
|
|
|
namespace EightBit {
|
2020-11-09 11:48:59 +00:00
|
|
|
|
|
|
|
|
|
class Bus;
|
|
|
|
|
|
2017-12-10 21:41:48 +00:00
|
|
|
|
class Z80 final : public IntelProcessor {
|
2021-07-18 13:28:40 +00:00
|
|
|
|
public:
|
|
|
|
|
// ** 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)
|
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
|
2017-06-05 21:39:15 +00:00
|
|
|
|
public:
|
2017-06-26 22:22:32 +00:00
|
|
|
|
struct refresh_t {
|
|
|
|
|
|
|
|
|
|
bool high : 1;
|
|
|
|
|
uint8_t variable : 7;
|
|
|
|
|
|
2021-10-24 10:12:23 +00:00
|
|
|
|
constexpr refresh_t(const uint8_t value) noexcept
|
2018-08-17 12:59:59 +00:00
|
|
|
|
: high(!!(value & Bit7)),
|
2017-06-27 13:02:29 +00:00
|
|
|
|
variable(value & Mask7)
|
|
|
|
|
{ }
|
|
|
|
|
|
2021-07-18 13:28:40 +00:00
|
|
|
|
constexpr operator uint8_t() const noexcept {
|
2017-06-26 22:22:32 +00:00
|
|
|
|
return (high << 7) | variable;
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-18 13:28:40 +00:00
|
|
|
|
constexpr auto& operator++() noexcept {
|
2017-06-27 13:02:29 +00:00
|
|
|
|
++variable;
|
|
|
|
|
return *this;
|
2017-06-26 22:22:32 +00:00
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2017-06-05 21:39:15 +00:00
|
|
|
|
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
|
|
|
|
|
2020-02-09 11:51:58 +00:00
|
|
|
|
Z80(Bus& bus);
|
2017-06-04 20:38:34 +00:00
|
|
|
|
|
2021-12-27 22:07:30 +00:00
|
|
|
|
Z80(const Z80& rhs);
|
2021-12-27 14:24:38 +00:00
|
|
|
|
bool operator==(const Z80& rhs) const;
|
|
|
|
|
|
2017-06-05 21:39:15 +00:00
|
|
|
|
Signal<Z80> ExecutingInstruction;
|
2018-08-25 00:34:30 +00:00
|
|
|
|
Signal<Z80> ExecutedInstruction;
|
2017-06-04 20:38:34 +00:00
|
|
|
|
|
2020-03-23 21:56:23 +00:00
|
|
|
|
Signal<EventArgs> ReadingMemory;
|
|
|
|
|
Signal<EventArgs> ReadMemory;
|
|
|
|
|
|
|
|
|
|
Signal<EventArgs> WritingMemory;
|
|
|
|
|
Signal<EventArgs> WrittenMemory;
|
|
|
|
|
|
|
|
|
|
Signal<EventArgs> ReadingIO;
|
|
|
|
|
Signal<EventArgs> ReadIO;
|
|
|
|
|
|
|
|
|
|
Signal<EventArgs> WritingIO;
|
|
|
|
|
Signal<EventArgs> WrittenIO;
|
|
|
|
|
|
2024-03-14 14:54:52 +00:00
|
|
|
|
void execute() noexcept final;
|
2022-01-17 19:10:15 +00:00
|
|
|
|
int step() noexcept final;
|
2019-01-14 02:10:17 +00:00
|
|
|
|
|
2021-12-27 14:24:38 +00:00
|
|
|
|
[[nodiscard]] const register16_t& AF() const noexcept final;
|
|
|
|
|
[[nodiscard]] auto& AF() noexcept { return IntelProcessor::AF(); }
|
|
|
|
|
[[nodiscard]] const register16_t& BC() const noexcept final;
|
|
|
|
|
[[nodiscard]] auto& BC() noexcept { return IntelProcessor::BC(); }
|
|
|
|
|
[[nodiscard]] const register16_t& DE() const noexcept final;
|
|
|
|
|
[[nodiscard]] auto& DE() noexcept { return IntelProcessor::DE(); }
|
|
|
|
|
[[nodiscard]] const register16_t& HL() const noexcept final;
|
|
|
|
|
[[nodiscard]] auto& HL() noexcept { return IntelProcessor::HL(); }
|
|
|
|
|
|
2022-01-17 19:10:15 +00:00
|
|
|
|
[[nodiscard]] constexpr const auto& IX() const noexcept { return m_ix; }
|
2021-12-27 14:24:38 +00:00
|
|
|
|
NON_CONST_REGISTOR_ACCESSOR(IX);
|
2022-01-17 19:10:15 +00:00
|
|
|
|
[[nodiscard]] constexpr auto& IXH() noexcept { return IX().high; }
|
|
|
|
|
[[nodiscard]] constexpr auto& IXL() noexcept { return IX().low; }
|
2017-06-04 20:38:34 +00:00
|
|
|
|
|
2022-01-17 19:10:15 +00:00
|
|
|
|
[[nodiscard]] constexpr const auto& IY() const noexcept { return m_iy; }
|
2021-12-27 14:24:38 +00:00
|
|
|
|
NON_CONST_REGISTOR_ACCESSOR(IY);
|
2022-01-17 19:10:15 +00:00
|
|
|
|
[[nodiscard]] constexpr auto& IYH() noexcept { return IY().high; }
|
|
|
|
|
[[nodiscard]] constexpr auto& IYL() noexcept { return IY().low; }
|
2017-06-04 20:38:34 +00:00
|
|
|
|
|
2019-11-16 23:37:57 +00:00
|
|
|
|
// ** 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.
|
2021-07-18 13:28:40 +00:00
|
|
|
|
[[nodiscard]] constexpr auto& REFRESH() noexcept { return m_refresh; }
|
2021-12-27 14:24:38 +00:00
|
|
|
|
[[nodiscard]] constexpr auto REFRESH() const noexcept { return m_refresh; }
|
2019-11-16 23:37:57 +00:00
|
|
|
|
|
2021-07-18 13:28:40 +00:00
|
|
|
|
[[nodiscard]] constexpr auto& IV() noexcept { return iv; }
|
2021-12-27 14:24:38 +00:00
|
|
|
|
[[nodiscard]] constexpr auto IV() const noexcept { return iv; }
|
2021-07-18 13:28:40 +00:00
|
|
|
|
[[nodiscard]] constexpr auto& IM() noexcept { return m_interruptMode; }
|
2021-12-27 14:24:38 +00:00
|
|
|
|
[[nodiscard]] constexpr auto IM() const noexcept { return m_interruptMode; }
|
2021-07-18 13:28:40 +00:00
|
|
|
|
[[nodiscard]] constexpr auto& IFF1() noexcept { return m_iff1; }
|
2021-12-27 14:24:38 +00:00
|
|
|
|
[[nodiscard]] constexpr auto IFF1() const noexcept { return m_iff1; }
|
2021-07-18 13:28:40 +00:00
|
|
|
|
[[nodiscard]] constexpr auto& IFF2() noexcept { return m_iff2; }
|
2021-12-27 14:24:38 +00:00
|
|
|
|
[[nodiscard]] constexpr auto IFF2() const noexcept { return m_iff2; }
|
2017-06-04 20:38:34 +00:00
|
|
|
|
|
2021-07-18 13:28:40 +00:00
|
|
|
|
constexpr void exx() noexcept { m_registerSet ^= 1; }
|
|
|
|
|
constexpr void exxAF() noexcept { m_accumulatorFlagsSet ^= 1; }
|
2017-06-04 20:38:34 +00:00
|
|
|
|
|
2021-07-18 13:28:40 +00:00
|
|
|
|
[[nodiscard]] constexpr auto requestingIO() const noexcept { return lowered(IORQ()); }
|
|
|
|
|
[[nodiscard]] constexpr auto requestingMemory() const noexcept { return lowered(MREQ()); }
|
2017-06-04 20:38:34 +00:00
|
|
|
|
|
2021-07-18 13:28:40 +00:00
|
|
|
|
[[nodiscard]] constexpr auto requestingRead() const noexcept { return lowered(RD()); }
|
|
|
|
|
[[nodiscard]] constexpr auto requestingWrite() const noexcept { return lowered(WR()); }
|
2019-01-14 23:17:54 +00:00
|
|
|
|
|
2017-12-10 21:41:48 +00:00
|
|
|
|
protected:
|
2022-01-17 19:10:15 +00:00
|
|
|
|
void handleRESET() noexcept final;
|
|
|
|
|
void handleINT() noexcept final;
|
2017-06-04 20:38:34 +00:00
|
|
|
|
|
2022-01-17 19:10:15 +00:00
|
|
|
|
void pushWord(register16_t destination) noexcept final;
|
2019-12-29 01:18:54 +00:00
|
|
|
|
|
2022-01-17 19:10:15 +00:00
|
|
|
|
void memoryWrite() noexcept final;
|
|
|
|
|
uint8_t memoryRead() noexcept final;
|
2020-02-09 11:51:58 +00:00
|
|
|
|
|
2022-01-17 19:10:15 +00:00
|
|
|
|
void busWrite() noexcept final;
|
|
|
|
|
uint8_t busRead() noexcept final;
|
2019-09-15 00:35:57 +00:00
|
|
|
|
|
2022-01-17 19:10:15 +00:00
|
|
|
|
void jr(int8_t offset) noexcept final;
|
|
|
|
|
int jrConditional(int condition) noexcept final;
|
2020-02-16 09:18:34 +00:00
|
|
|
|
|
2017-08-30 22:17:34 +00:00
|
|
|
|
private:
|
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;
|
2017-11-10 22:41:50 +00:00
|
|
|
|
int m_registerSet = 0;
|
2017-06-04 20:38:34 +00:00
|
|
|
|
|
2017-06-05 21:39:15 +00:00
|
|
|
|
std::array<register16_t, 2> m_accumulatorFlags;
|
2017-11-10 22:41:50 +00:00
|
|
|
|
int m_accumulatorFlagsSet = 0;
|
2017-06-04 20:38:34 +00:00
|
|
|
|
|
2018-06-15 23:55:32 +00:00
|
|
|
|
register16_t m_ix = 0xffff;
|
|
|
|
|
register16_t m_iy = 0xffff;
|
2017-06-04 20:38:34 +00:00
|
|
|
|
|
2017-11-10 22:41:50 +00:00
|
|
|
|
refresh_t m_refresh = 0x7f;
|
2017-06-26 22:22:32 +00:00
|
|
|
|
|
2017-11-10 22:41:50 +00:00
|
|
|
|
uint8_t iv = 0xff;
|
|
|
|
|
int m_interruptMode = 0;
|
|
|
|
|
bool m_iff1 = false;
|
|
|
|
|
bool m_iff2 = false;
|
2017-06-04 20:38:34 +00:00
|
|
|
|
|
2017-11-10 22:41:50 +00:00
|
|
|
|
bool m_prefixCB = false;
|
|
|
|
|
bool m_prefixDD = false;
|
|
|
|
|
bool m_prefixED = false;
|
|
|
|
|
bool m_prefixFD = false;
|
2017-06-04 20:38:34 +00:00
|
|
|
|
|
2017-11-10 22:41:50 +00:00
|
|
|
|
int8_t m_displacement = 0;
|
2017-06-04 20:38:34 +00:00
|
|
|
|
|
2022-01-17 19:10:15 +00:00
|
|
|
|
void handleNMI() noexcept;
|
2018-08-25 11:09:26 +00:00
|
|
|
|
|
2022-01-17 19:10:15 +00:00
|
|
|
|
constexpr void resetPrefixes() noexcept {
|
|
|
|
|
m_prefixCB = m_prefixDD = m_prefixED = m_prefixFD = false;
|
|
|
|
|
}
|
2021-01-09 08:41:48 +00:00
|
|
|
|
|
2021-07-18 13:28:40 +00:00
|
|
|
|
[[nodiscard]] constexpr auto displaced() const noexcept { return m_prefixDD || m_prefixFD; }
|
2020-05-03 19:45:01 +00:00
|
|
|
|
|
2022-01-17 19:10:15 +00:00
|
|
|
|
[[nodiscard]] constexpr uint16_t displacedAddress() noexcept {
|
|
|
|
|
const auto address = (m_prefixDD ? IX() : IY()).word + m_displacement;
|
|
|
|
|
MEMPTR().word = address;
|
|
|
|
|
return address;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void fetchDisplacement() noexcept;
|
|
|
|
|
[[nodiscard]] uint8_t fetchOpCode() noexcept;
|
|
|
|
|
|
|
|
|
|
uint8_t readBusDataM1() noexcept;
|
2017-06-05 21:39:15 +00:00
|
|
|
|
|
2021-01-09 08:41:48 +00:00
|
|
|
|
typedef std::function<register16_t(void)> addresser_t;
|
2022-01-17 19:10:15 +00:00
|
|
|
|
void loadAccumulatorIndirect(addresser_t addresser) noexcept;
|
|
|
|
|
void storeAccumulatorIndirect(addresser_t addresser) noexcept;
|
2017-06-04 20:38:34 +00:00
|
|
|
|
|
2021-01-09 08:41:48 +00:00
|
|
|
|
typedef std::function<uint8_t(void)> reader_t;
|
2022-01-17 19:10:15 +00:00
|
|
|
|
void readInternalRegister(reader_t reader) noexcept;
|
2017-06-04 20:38:34 +00:00
|
|
|
|
|
2021-07-18 13:28:40 +00:00
|
|
|
|
[[nodiscard]] register16_t& HL2() noexcept;
|
|
|
|
|
[[nodiscard]] register16_t& RP(int rp) noexcept;
|
|
|
|
|
[[nodiscard]] register16_t& RP2(int rp) noexcept;
|
2017-06-04 20:38:34 +00:00
|
|
|
|
|
2022-01-17 19:10:15 +00:00
|
|
|
|
[[nodiscard]] uint8_t R(int r) noexcept;
|
|
|
|
|
void R(int r, uint8_t value) noexcept;
|
|
|
|
|
void R2(int r, uint8_t value) noexcept;
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] static constexpr auto adjustHalfCarryAdd(uint8_t f, uint8_t before, uint8_t value, int calculation) noexcept {
|
|
|
|
|
return setBit(f, HC, calculateHalfCarryAdd(before, value, calculation));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] static constexpr auto adjustHalfCarrySub(uint8_t f, uint8_t before, uint8_t value, int calculation) noexcept {
|
|
|
|
|
return setBit(f, HC, calculateHalfCarrySub(before, value, calculation));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] static constexpr auto adjustOverflowAdd(uint8_t f, int beforeNegative, int valueNegative, int afterNegative) noexcept {
|
|
|
|
|
const auto overflow = (beforeNegative == valueNegative) && (beforeNegative != afterNegative);
|
|
|
|
|
return setBit(f, VF, overflow);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] static constexpr auto adjustOverflowAdd(uint8_t f, uint8_t before, uint8_t value, uint8_t calculation) noexcept {
|
|
|
|
|
return adjustOverflowAdd(f, before & SF, value & SF, calculation & SF);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] static constexpr auto adjustOverflowSub(uint8_t f, int beforeNegative, int valueNegative, int afterNegative) noexcept {
|
|
|
|
|
const auto overflow = (beforeNegative != valueNegative) && (beforeNegative != afterNegative);
|
|
|
|
|
return setBit(f, VF, overflow);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] static constexpr auto adjustOverflowSub(uint8_t f, uint8_t before, uint8_t value, uint8_t calculation) noexcept {
|
|
|
|
|
return adjustOverflowSub(f, before & SF, value & SF, calculation & SF);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] static constexpr bool convertCondition(uint8_t f, int flag) noexcept {
|
|
|
|
|
switch (flag) {
|
|
|
|
|
case 0:
|
|
|
|
|
return !(f & ZF);
|
|
|
|
|
case 1:
|
|
|
|
|
return f & ZF;
|
|
|
|
|
case 2:
|
|
|
|
|
return !(f & CF);
|
|
|
|
|
case 3:
|
|
|
|
|
return f & CF;
|
|
|
|
|
case 4:
|
|
|
|
|
return !(f & PF);
|
|
|
|
|
case 5:
|
|
|
|
|
return f & PF;
|
|
|
|
|
case 6:
|
|
|
|
|
return !(f & SF);
|
|
|
|
|
case 7:
|
|
|
|
|
return f & SF;
|
|
|
|
|
default:
|
|
|
|
|
UNREACHABLE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static constexpr auto subtract(uint8_t& f, uint8_t operand, uint8_t value, int carry = 0) noexcept {
|
|
|
|
|
const register16_t subtraction = operand - value - carry;
|
|
|
|
|
const auto result = subtraction.low;
|
|
|
|
|
|
|
|
|
|
f = adjustHalfCarrySub(f, operand, value, result);
|
|
|
|
|
f = adjustOverflowSub(f, operand, value, result);
|
|
|
|
|
|
|
|
|
|
f = setBit(f, NF);
|
|
|
|
|
f = setBit(f, CF, subtraction.high & CF);
|
|
|
|
|
f = adjustSZ<Z80>(f, result);
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void executeCB(int x, int y, int z) noexcept;
|
|
|
|
|
void executeED(int x, int y, int z, int p, int q) noexcept;
|
|
|
|
|
void executeOther(int x, int y, int z, int p, int q) noexcept;
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] static constexpr auto increment(uint8_t& f, uint8_t operand) noexcept {
|
|
|
|
|
f = clearBit(f, NF);
|
|
|
|
|
const uint8_t result = operand + 1;
|
|
|
|
|
f = adjustSZXY<Z80>(f, result);
|
|
|
|
|
f = setBit(f, VF, result == Bit7);
|
|
|
|
|
f = clearBit(f, HC, lowNibble(result));
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] static constexpr auto decrement(uint8_t& f, uint8_t operand) noexcept {
|
|
|
|
|
f = setBit(f, NF);
|
|
|
|
|
f = clearBit(f, HC, lowNibble(operand));
|
|
|
|
|
const uint8_t result = operand - 1;
|
|
|
|
|
f = adjustSZXY<Z80>(f, result);
|
|
|
|
|
f = setBit(f, VF, result == Mask7);
|
|
|
|
|
return result;
|
|
|
|
|
}
|
2019-12-29 01:18:54 +00:00
|
|
|
|
|
2022-01-17 19:10:15 +00:00
|
|
|
|
void di() noexcept;
|
|
|
|
|
void ei() noexcept;
|
2017-06-26 22:22:32 +00:00
|
|
|
|
|
2022-01-17 19:10:15 +00:00
|
|
|
|
void retn() noexcept;
|
|
|
|
|
void reti() noexcept;
|
|
|
|
|
|
|
|
|
|
void returnConditionalFlag(uint8_t f, int flag) noexcept;
|
|
|
|
|
void jrConditionalFlag(uint8_t f, int flag) noexcept;
|
|
|
|
|
void callConditionalFlag(uint8_t f, int flag) noexcept;
|
|
|
|
|
void jumpConditionalFlag(uint8_t f, int flag) noexcept;
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] register16_t sbc(uint8_t& f, register16_t operand, register16_t value) noexcept;
|
|
|
|
|
[[nodiscard]] register16_t adc(uint8_t& f, register16_t operand, register16_t value) noexcept;
|
|
|
|
|
[[nodiscard]] register16_t add(uint8_t& f, register16_t operand, register16_t value, int carry = 0) noexcept;
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] static constexpr auto add(uint8_t& f, uint8_t operand, uint8_t value, int carry = 0) noexcept {
|
|
|
|
|
const register16_t addition = operand + value + carry;
|
|
|
|
|
const auto result = addition.low;
|
|
|
|
|
|
|
|
|
|
f = adjustHalfCarryAdd(f, operand, value, result);
|
|
|
|
|
f = adjustOverflowAdd(f, operand, value, result);
|
|
|
|
|
|
|
|
|
|
f = clearBit(f, NF);
|
|
|
|
|
f = setBit(f, CF, addition.high & CF);
|
|
|
|
|
f = adjustSZXY<Z80>(f, result);
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] static constexpr auto adc(uint8_t& f, uint8_t operand, uint8_t value) noexcept {
|
|
|
|
|
return add(f, operand, value, f & CF);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] static constexpr auto sub(uint8_t& f, uint8_t operand, uint8_t value, int carry = 0) noexcept {
|
|
|
|
|
const auto subtraction = subtract(f, operand, value, carry);
|
|
|
|
|
f = adjustSZXY<Z80>(f, subtraction);
|
|
|
|
|
return subtraction;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] static constexpr auto sbc(uint8_t& f, uint8_t operand, uint8_t value) noexcept {
|
|
|
|
|
return sub(f, operand, value, f & CF);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] static constexpr auto andr(uint8_t& f, uint8_t operand, uint8_t value) noexcept {
|
|
|
|
|
f = setBit(f, HC);
|
|
|
|
|
f = clearBit(f, CF | NF);
|
|
|
|
|
const uint8_t result = operand & value;
|
|
|
|
|
f = adjustSZPXY<Z80>(f, result);
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] static constexpr auto xorr(uint8_t& f, uint8_t operand, uint8_t value) noexcept {
|
|
|
|
|
f = clearBit(f, HC | CF | NF);
|
|
|
|
|
const uint8_t result = operand ^ value;
|
|
|
|
|
f = adjustSZPXY<Z80>(f, result);
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] static constexpr auto orr(uint8_t& f, uint8_t operand, uint8_t value) noexcept {
|
|
|
|
|
f = clearBit(f, HC | CF | NF);
|
|
|
|
|
const uint8_t result = operand | value;
|
|
|
|
|
f = adjustSZPXY<Z80>(f, result);
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void compare(uint8_t& f, uint8_t operand, uint8_t value) noexcept {
|
|
|
|
|
subtract(f, operand, value);
|
|
|
|
|
f = adjustXY<Z80>(f, value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] static constexpr auto rlc(uint8_t& f, uint8_t operand) noexcept {
|
|
|
|
|
f = clearBit(f, NF | HC);
|
|
|
|
|
const auto carry = operand & Bit7;
|
|
|
|
|
f = setBit(f, CF, carry);
|
|
|
|
|
const uint8_t result = (operand << 1) | (carry >> 7);
|
|
|
|
|
f = adjustXY<Z80>(f, result);
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] static constexpr auto rrc(uint8_t& f, uint8_t operand) noexcept {
|
|
|
|
|
f = clearBit(f, NF | HC);
|
|
|
|
|
const auto carry = operand & Bit0;
|
|
|
|
|
f = setBit(f, CF, carry);
|
|
|
|
|
const uint8_t result = (operand >> 1) | (carry << 7);
|
|
|
|
|
f = adjustXY<Z80>(f, result);
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] static constexpr auto rl(uint8_t& f, uint8_t operand) noexcept {
|
|
|
|
|
f = clearBit(f, NF | HC);
|
|
|
|
|
const auto carry = f & CF;
|
|
|
|
|
f = setBit(f, CF, operand & Bit7);
|
|
|
|
|
const uint8_t result = (operand << 1) | carry;
|
|
|
|
|
f = adjustXY<Z80>(f, result);
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] static constexpr auto rr(uint8_t& f, uint8_t operand) noexcept {
|
|
|
|
|
f = clearBit(f, NF | HC);
|
|
|
|
|
const auto carry = f & CF;
|
|
|
|
|
f = setBit(f, CF, operand & Bit0);
|
|
|
|
|
const uint8_t result = (operand >> 1) | (carry << 7);
|
|
|
|
|
f = adjustXY<Z80>(f, result);
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] static constexpr auto sla(uint8_t& f, uint8_t operand) noexcept {
|
|
|
|
|
f = clearBit(f, NF | HC);
|
|
|
|
|
f = setBit(f, CF, operand & Bit7);
|
|
|
|
|
const uint8_t result = operand << 1;
|
|
|
|
|
f = adjustXY<Z80>(f, result);
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] static constexpr auto sra(uint8_t& f, uint8_t operand) noexcept {
|
|
|
|
|
f = clearBit(f, NF | HC);
|
|
|
|
|
f = setBit(f, CF, operand & Bit0);
|
|
|
|
|
const uint8_t result = (operand >> 1) | (operand & Bit7);
|
|
|
|
|
f = adjustXY<Z80>(f, result);
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] static constexpr auto sll(uint8_t& f, uint8_t operand) noexcept {
|
|
|
|
|
f = clearBit(f, NF | HC);
|
|
|
|
|
f = setBit(f, CF, operand & Bit7);
|
|
|
|
|
const uint8_t result = (operand << 1) | Bit0;
|
|
|
|
|
f = adjustXY<Z80>(f, result);
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] static constexpr auto srl(uint8_t& f, uint8_t operand) noexcept {
|
|
|
|
|
f = clearBit(f, NF | HC);
|
|
|
|
|
f = setBit(f, CF, operand & Bit0);
|
|
|
|
|
const uint8_t result = (operand >> 1) & ~Bit7;
|
|
|
|
|
f = adjustXY<Z80>(f, result);
|
|
|
|
|
f = setBit(f, ZF, result);
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static constexpr void bit(uint8_t& f, int n, uint8_t operand) noexcept {
|
|
|
|
|
f = setBit(f, HC);
|
|
|
|
|
f = clearBit(f, NF);
|
|
|
|
|
const auto discarded = operand & Chip::bit(n);
|
|
|
|
|
f = adjustSZ<Z80>(f, discarded);
|
|
|
|
|
f = clearBit(f, PF, discarded);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] static constexpr auto res(int n, uint8_t operand) noexcept { return clearBit(operand, Chip::bit(n)); }
|
|
|
|
|
[[nodiscard]] static constexpr auto set(int n, uint8_t operand) noexcept { return setBit(operand, Chip::bit(n)); }
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] static constexpr auto daa(uint8_t& f, uint8_t operand) noexcept {
|
|
|
|
|
const auto lowAdjust = (f & HC) || (lowNibble(operand) > 9);
|
|
|
|
|
const auto highAdjust = (f & CF) || (operand > 0x99);
|
|
|
|
|
|
|
|
|
|
auto updated = operand;
|
|
|
|
|
if (f & NF) {
|
|
|
|
|
if (lowAdjust)
|
|
|
|
|
updated -= 6;
|
|
|
|
|
if (highAdjust)
|
|
|
|
|
updated -= 0x60;
|
|
|
|
|
} else {
|
|
|
|
|
if (lowAdjust)
|
|
|
|
|
updated += 6;
|
|
|
|
|
if (highAdjust)
|
|
|
|
|
updated += 0x60;
|
|
|
|
|
}
|
2017-06-04 20:38:34 +00:00
|
|
|
|
|
2022-01-17 19:10:15 +00:00
|
|
|
|
f = (f & (CF | NF)) | (operand > 0x99 ? CF : 0) | ((operand ^ updated) & HC);
|
2017-06-04 20:38:34 +00:00
|
|
|
|
|
2022-01-17 19:10:15 +00:00
|
|
|
|
f = adjustSZPXY<Z80>(f, updated);
|
2017-08-30 22:17:34 +00:00
|
|
|
|
|
2022-01-17 19:10:15 +00:00
|
|
|
|
return updated;
|
|
|
|
|
}
|
2017-06-04 20:38:34 +00:00
|
|
|
|
|
2022-01-17 19:10:15 +00:00
|
|
|
|
static constexpr void scf(uint8_t& f, uint8_t operand) noexcept {
|
|
|
|
|
f = setBit(f, CF);
|
|
|
|
|
f = clearBit(f, HC | NF);
|
|
|
|
|
f = adjustXY<Z80>(f, operand);
|
|
|
|
|
}
|
2019-11-09 18:58:23 +00:00
|
|
|
|
|
2022-01-17 19:10:15 +00:00
|
|
|
|
static constexpr void ccf(uint8_t& f, uint8_t operand) noexcept {
|
|
|
|
|
f = clearBit(f, NF);
|
|
|
|
|
const auto carry = f & CF;
|
|
|
|
|
f = setBit(f, HC, carry);
|
|
|
|
|
f = clearBit(f, CF, carry);
|
|
|
|
|
f = adjustXY<Z80>(f, operand);
|
|
|
|
|
}
|
2019-11-09 18:58:23 +00:00
|
|
|
|
|
2022-01-17 19:10:15 +00:00
|
|
|
|
[[nodiscard]] static constexpr auto cpl(uint8_t& f, uint8_t operand) noexcept {
|
|
|
|
|
f = setBit(f, HC | NF);
|
|
|
|
|
const uint8_t result = ~operand;
|
|
|
|
|
f = adjustXY<Z80>(f, result);
|
|
|
|
|
return result;
|
|
|
|
|
}
|
2021-07-18 13:28:40 +00:00
|
|
|
|
|
2022-01-17 19:10:15 +00:00
|
|
|
|
void xhtl(register16_t& exchange) noexcept;
|
2021-07-18 13:28:40 +00:00
|
|
|
|
|
2022-01-17 19:10:15 +00:00
|
|
|
|
void blockCompare(uint8_t& f, uint8_t value, register16_t source, register16_t& counter) noexcept;
|
2021-07-18 13:28:40 +00:00
|
|
|
|
|
2022-01-17 19:10:15 +00:00
|
|
|
|
void cpi(uint8_t& f, uint8_t value) noexcept;
|
|
|
|
|
[[nodiscard]] bool cpir(uint8_t& f, uint8_t value) noexcept;
|
2021-07-18 13:28:40 +00:00
|
|
|
|
|
2022-01-17 19:10:15 +00:00
|
|
|
|
void cpd(uint8_t& f, uint8_t value) noexcept;
|
|
|
|
|
[[nodiscard]] bool cpdr(uint8_t& f, uint8_t value) noexcept;
|
2017-06-05 21:39:15 +00:00
|
|
|
|
|
2022-01-17 19:10:15 +00:00
|
|
|
|
void blockLoad(uint8_t& f, uint8_t a, register16_t source, register16_t destination, register16_t& counter) noexcept;
|
2017-06-05 21:39:15 +00:00
|
|
|
|
|
2022-01-17 19:10:15 +00:00
|
|
|
|
void ldi(uint8_t& f, uint8_t a) noexcept;
|
|
|
|
|
[[nodiscard]] bool ldir(uint8_t& f, uint8_t a) noexcept;
|
2017-06-05 21:39:15 +00:00
|
|
|
|
|
2022-01-17 19:10:15 +00:00
|
|
|
|
void ldd(uint8_t& f, uint8_t a) noexcept;
|
|
|
|
|
[[nodiscard]] bool lddr(uint8_t& f, uint8_t a) noexcept;
|
2017-06-05 21:39:15 +00:00
|
|
|
|
|
2022-01-17 19:10:15 +00:00
|
|
|
|
void blockIn(register16_t& source, register16_t destination) noexcept;
|
2017-06-05 21:39:15 +00:00
|
|
|
|
|
2022-01-17 19:10:15 +00:00
|
|
|
|
void ini() noexcept;
|
|
|
|
|
[[nodiscard]] bool inir() noexcept;
|
2017-06-05 21:39:15 +00:00
|
|
|
|
|
2022-01-17 19:10:15 +00:00
|
|
|
|
void ind() noexcept;
|
|
|
|
|
[[nodiscard]] bool indr() noexcept;
|
2017-06-05 21:39:15 +00:00
|
|
|
|
|
2022-01-17 19:10:15 +00:00
|
|
|
|
void blockOut(register16_t source, register16_t& destination) noexcept;
|
2017-06-05 21:39:15 +00:00
|
|
|
|
|
2022-01-17 19:10:15 +00:00
|
|
|
|
void outi() noexcept;
|
|
|
|
|
[[nodiscard]] bool otir() noexcept;
|
2018-08-12 15:25:30 +00:00
|
|
|
|
|
2022-01-17 19:10:15 +00:00
|
|
|
|
void outd() noexcept;
|
|
|
|
|
[[nodiscard]] bool otdr() noexcept;
|
2017-06-05 21:39:15 +00:00
|
|
|
|
|
2022-01-17 19:10:15 +00:00
|
|
|
|
[[nodiscard]] static constexpr auto neg(uint8_t& f, uint8_t operand) noexcept {
|
|
|
|
|
f = setBit(f, PF, operand == Bit7);
|
|
|
|
|
f = setBit(f, CF, operand);
|
|
|
|
|
f = setBit(f, NF);
|
2017-06-05 21:39:15 +00:00
|
|
|
|
|
2022-01-17 19:10:15 +00:00
|
|
|
|
const uint8_t result = (~operand + 1); // two's complement
|
2017-06-05 21:39:15 +00:00
|
|
|
|
|
2022-01-17 19:10:15 +00:00
|
|
|
|
f = adjustHalfCarrySub(f, 0U, operand, result);
|
|
|
|
|
f = adjustOverflowSub(f, 0U, operand, result);
|
2017-06-05 21:39:15 +00:00
|
|
|
|
|
2022-01-17 19:10:15 +00:00
|
|
|
|
f = adjustSZXY<Z80>(f, result);
|
2017-06-05 21:39:15 +00:00
|
|
|
|
|
2022-01-17 19:10:15 +00:00
|
|
|
|
return result;
|
|
|
|
|
}
|
2017-06-05 21:39:15 +00:00
|
|
|
|
|
2022-01-17 19:10:15 +00:00
|
|
|
|
void rrd(uint8_t& f, register16_t address, uint8_t& update) noexcept;
|
|
|
|
|
void rld(uint8_t& f, register16_t address, uint8_t& update) noexcept;
|
2017-06-05 21:39:15 +00:00
|
|
|
|
|
2022-01-17 19:10:15 +00:00
|
|
|
|
void portWrite(uint8_t port) noexcept;
|
|
|
|
|
void portWrite() noexcept;
|
2017-06-05 21:39:15 +00:00
|
|
|
|
|
2022-01-17 19:10:15 +00:00
|
|
|
|
uint8_t portRead(uint8_t port) noexcept;
|
|
|
|
|
uint8_t portRead() noexcept;
|
2017-06-05 21:39:15 +00:00
|
|
|
|
};
|
|
|
|
|
}
|