ch81/z80emu/z80emu.cc

2239 lines
42 KiB
C++
Raw Permalink Normal View History

2025-09-20 14:12:11 +02:00
/* z80emu.c
* Z80 processor emulator.
*
* Copyright (c) 2012-2017 Lin Ke-Fong
*
* This code is free, do whatever you want with it.
*/
#include "z80emu.h"
#include "instructions.h"
#include "macros.h"
#include "tables.h"
namespace z80emu {
namespace {
2026-03-13 17:52:31 +01:00
/* The main registers are stored inside Z80_STATE as an union of arrays named
* registers. They are referenced using indexes. Words are stored in the
* endianness of the host processor. The alternate set of word registers AF',
* BC', DE', and HL' is stored in the alternates member of Z80_STATE, as an
* array using the same ordering.
*/
#ifdef Z80_BIG_ENDIAN
#define Z80_B 0
#define Z80_C 1
#define Z80_D 2
#define Z80_E 3
#define Z80_H 4
#define Z80_L 5
#define Z80_A 6
#define Z80_F 7
#define Z80_IXH 8
#define Z80_IXL 9
#define Z80_IYH 10
#define Z80_IYL 11
#else
#define Z80_B 1
#define Z80_C 0
#define Z80_D 3
#define Z80_E 2
#define Z80_H 5
#define Z80_L 4
#define Z80_A 7
#define Z80_F 6
#define Z80_IXH 9
#define Z80_IXL 8
#define Z80_IYH 11
#define Z80_IYL 10
#endif
#define Z80_BC 0
#define Z80_DE 1
#define Z80_HL 2
#define Z80_AF 3
#define Z80_IX 4
#define Z80_IY 5
#define Z80_SP 6
/* Z80's flags. */
#define Z80_S_FLAG_SHIFT 7
#define Z80_Z_FLAG_SHIFT 6
#define Z80_Y_FLAG_SHIFT 5
#define Z80_H_FLAG_SHIFT 4
#define Z80_X_FLAG_SHIFT 3
#define Z80_PV_FLAG_SHIFT 2
#define Z80_N_FLAG_SHIFT 1
#define Z80_C_FLAG_SHIFT 0
#define Z80_S_FLAG (1 << Z80_S_FLAG_SHIFT)
#define Z80_Z_FLAG (1 << Z80_Z_FLAG_SHIFT)
#define Z80_Y_FLAG (1 << Z80_Y_FLAG_SHIFT)
#define Z80_H_FLAG (1 << Z80_H_FLAG_SHIFT)
#define Z80_X_FLAG (1 << Z80_X_FLAG_SHIFT)
#define Z80_PV_FLAG (1 << Z80_PV_FLAG_SHIFT)
#define Z80_N_FLAG (1 << Z80_N_FLAG_SHIFT)
#define Z80_C_FLAG (1 << Z80_C_FLAG_SHIFT)
#define Z80_P_FLAG_SHIFT Z80_PV_FLAG_SHIFT
#define Z80_V_FLAG_SHIFT Z80_PV_FLAG_SHIFT
#define Z80_P_FLAG Z80_PV_FLAG
#define Z80_V_FLAG Z80_PV_FLAG
/* Z80's three interrupt modes. */
enum {
Z80_INTERRUPT_MODE_0,
Z80_INTERRUPT_MODE_1,
Z80_INTERRUPT_MODE_2
};
#define Z80_READ_BYTE(address, x) x = board_.ReadByte(address)
#define Z80_FETCH_BYTE(address, x) x = board_.FetchByte(address)
#define Z80_READ_WORD(address, x) x = board_.ReadWord(address)
#define Z80_FETCH_WORD(address, x) x = board_.FetchWord(address)
#define Z80_WRITE_BYTE(address, x) board_.WriteByte(address, x)
#define Z80_WRITE_WORD(address, x) board_.WriteWord(address, x)
#define Z80_READ_WORD_INTERRUPT(address, x) \
x = board_.ReadWordInterrupt(address)
#define Z80_WRITE_WORD_INTERRUPT(address, x) \
board_.WriteWordInterrupt(address, x)
2025-09-21 15:30:27 +02:00
#define Z80_INPUT_BYTE(port, x) x = board_.InputByte(*this, port)
#define Z80_OUTPUT_BYTE(port, x) board_.OutputByte(port, x)
2025-09-20 14:12:11 +02:00
#define Z80_CATCH_HALT
#define Z80_CATCH_EI
2025-09-20 14:12:11 +02:00
/* Indirect (HL) or prefixed indexed (IX + d) and (IY + d) memory operands are
* encoded using the 3 bits "110" (0x06).
*/
#define INDIRECT_HL 0x06
/* Condition codes are encoded using 2 or 3 bits. The xor table is needed for
* negated conditions, it is used along with the and table.
*/
constexpr uint8_t XOR_CONDITION_TABLE[8] = {
Z80_Z_FLAG, 0, Z80_C_FLAG, 0, Z80_P_FLAG, 0, Z80_S_FLAG, 0,
};
constexpr uint8_t AND_CONDITION_TABLE[8] = {
Z80_Z_FLAG, Z80_Z_FLAG, Z80_C_FLAG, Z80_C_FLAG,
Z80_P_FLAG, Z80_P_FLAG, Z80_S_FLAG, Z80_S_FLAG,
};
/* RST instruction restart addresses, encoded by Y() bits of the opcode. */
constexpr uint8_t RST_TABLE[8] = {
0x00, 0x08, 0x10, 0x18, 0x20, 0x28, 0x30, 0x38,
};
/* There is an overflow if the xor of the carry out and the carry of the most
* significant bit is not zero.
*/
constexpr uint8_t OVERFLOW_TABLE[4] = {
0,
Z80_V_FLAG,
Z80_V_FLAG,
0,
};
} // namespace
template <typename Board>
void Z80Emu<Board>::Reset(uint16_t pc) {
2025-09-20 14:12:11 +02:00
int i;
status_ = Status::UNDEFINED;
2025-09-20 14:12:11 +02:00
AF = 0xffff;
SP = 0xffff;
i_ = iff1_ = iff2_ = 0;
im_ = Z80_INTERRUPT_MODE_0;
pc_ = pc;
2025-09-20 14:12:11 +02:00
/* Build register decoding tables for both 3-bit encoded 8-bit
* registers and 2-bit encoded 16-bit registers. When an opcode is
* prefixed by 0xdd, HL is replaced by IX. When 0xfd prefixed, HL is
* replaced by IY.
*/
/* 8-bit "R" registers. */
register_table_[0] = &registers_.byte[Z80_B];
register_table_[1] = &registers_.byte[Z80_C];
register_table_[2] = &registers_.byte[Z80_D];
register_table_[3] = &registers_.byte[Z80_E];
register_table_[4] = &registers_.byte[Z80_H];
register_table_[5] = &registers_.byte[Z80_L];
2025-09-20 14:12:11 +02:00
/* Encoding 0x06 is used for indexed memory operands and direct HL or
* IX/IY register access.
*/
register_table_[6] = &registers_.word[Z80_HL];
register_table_[7] = &registers_.byte[Z80_A];
2025-09-20 14:12:11 +02:00
/* "Regular" 16-bit "RR" registers. */
register_table_[8] = &registers_.word[Z80_BC];
register_table_[9] = &registers_.word[Z80_DE];
register_table_[10] = &registers_.word[Z80_HL];
register_table_[11] = &registers_.word[Z80_SP];
2025-09-20 14:12:11 +02:00
/* 16-bit "SS" registers for PUSH and POP instructions (note that SP is
* replaced by AF).
*/
register_table_[12] = &registers_.word[Z80_BC];
register_table_[13] = &registers_.word[Z80_DE];
register_table_[14] = &registers_.word[Z80_HL];
register_table_[15] = &registers_.word[Z80_AF];
2025-09-20 14:12:11 +02:00
/* 0xdd and 0xfd prefixed register decoding tables. */
for (i = 0; i < 16; i++)
dd_register_table_[i] = fd_register_table_[i] = register_table_[i];
2025-09-20 14:12:11 +02:00
dd_register_table_[4] = &registers_.byte[Z80_IXH];
dd_register_table_[5] = &registers_.byte[Z80_IXL];
dd_register_table_[6] = &registers_.word[Z80_IX];
dd_register_table_[10] = &registers_.word[Z80_IX];
dd_register_table_[14] = &registers_.word[Z80_IX];
2025-09-20 14:12:11 +02:00
fd_register_table_[4] = &registers_.byte[Z80_IYH];
fd_register_table_[5] = &registers_.byte[Z80_IYL];
fd_register_table_[6] = &registers_.word[Z80_IY];
fd_register_table_[10] = &registers_.word[Z80_IY];
fd_register_table_[14] = &registers_.word[Z80_IY];
2025-09-20 14:12:11 +02:00
}
template <typename Board>
int Z80Emu<Board>::Interrupt(int data_on_bus) {
status_ = Status::UNDEFINED;
if (iff1_) {
iff1_ = iff2_ = 0;
r_ = (r_ & 0x80) | ((r_ + 1) & 0x7f);
switch (im_) {
2025-09-20 14:12:11 +02:00
case Z80_INTERRUPT_MODE_0: {
/* Assuming the opcode in data_on_bus is an
* RST instruction, accepting the interrupt
* should take 2 + 11 = 13 cycles.
*/
return Emulate(data_on_bus, 2, 4);
}
case Z80_INTERRUPT_MODE_1: {
int elapsed_cycles;
elapsed_cycles = 0;
SP -= 2;
Z80_WRITE_WORD_INTERRUPT(SP, pc_);
pc_ = 0x0038;
2025-09-20 14:12:11 +02:00
return elapsed_cycles + 13;
}
case Z80_INTERRUPT_MODE_2:
default: {
int elapsed_cycles, vector;
elapsed_cycles = 0;
SP -= 2;
Z80_WRITE_WORD_INTERRUPT(SP, pc_);
vector = i_ << 8 | data_on_bus;
2025-09-20 14:12:11 +02:00
#ifdef Z80_MASK_IM2_VECTOR_ADDRESS
vector &= 0xfffe;
#endif
Z80_READ_WORD_INTERRUPT(vector, pc_);
2025-09-20 14:12:11 +02:00
return elapsed_cycles + 19;
}
}
} else
return 0;
}
template <typename Board>
int Z80Emu<Board>::NonMaskableInterrupt() {
2025-09-20 14:12:11 +02:00
int elapsed_cycles;
status_ = Status::UNDEFINED;
2025-09-20 14:12:11 +02:00
iff2_ = iff1_;
iff1_ = 0;
r_ = (r_ & 0x80) | ((r_ + 1) & 0x7f);
2025-09-20 14:12:11 +02:00
elapsed_cycles = 0;
SP -= 2;
Z80_WRITE_WORD_INTERRUPT(SP, pc_);
pc_ = 0x0066;
2025-09-20 14:12:11 +02:00
return elapsed_cycles + 11;
}
template <typename Board>
int Z80Emu<Board>::Emulate(int number_cycles) {
2025-09-20 14:12:11 +02:00
int elapsed_cycles, pc, opcode;
status_ = Status::UNDEFINED;
2025-09-20 14:12:11 +02:00
elapsed_cycles = 0;
pc = pc_;
2025-09-20 14:12:11 +02:00
Z80_FETCH_BYTE(pc, opcode);
pc_ = pc + 1;
2025-09-20 14:12:11 +02:00
return Emulate(opcode, elapsed_cycles, number_cycles);
}
/* Actual emulation function. opcode is the first opcode to emulate, this is
* needed by Z80Interrupt() for interrupt mode 0.
*/
template <typename Board>
int Z80Emu<Board>::Emulate(int opcode, int elapsed_cycles, int number_cycles) {
2025-09-20 14:12:11 +02:00
int pc, r;
pc = pc_;
r = r_ & 0x7f;
2025-09-20 14:12:11 +02:00
goto start_emulation;
for (;;) {
void **registers;
Instruction instruction;
Z80_FETCH_BYTE(pc, opcode);
pc++;
start_emulation:
registers = register_table_;
2025-09-20 14:12:11 +02:00
emulate_next_opcode:
instruction = (Instruction)INSTRUCTION_TABLE[opcode];
// ++counts_[instruction];
emulate_next_instruction:
elapsed_cycles += 4;
r++;
switch (instruction) {
/* 8-bit load group. */
case Instruction::LD_R_R: {
R(Y(opcode)) = R(Z(opcode));
break;
}
case Instruction::LD_R_N: {
READ_N(R(Y(opcode)));
break;
}
case Instruction::LD_R_INDIRECT_HL: {
if (registers == register_table_) {
2025-09-20 14:12:11 +02:00
READ_BYTE(HL, R(Y(opcode)));
} else {
int d;
READ_D(d);
d += HL_IX_IY;
READ_BYTE(d, S(Y(opcode)));
elapsed_cycles += 5;
}
break;
}
case Instruction::LD_INDIRECT_HL_R: {
if (registers == register_table_) {
2025-09-20 14:12:11 +02:00
WRITE_BYTE(HL, R(Z(opcode)));
} else {
int d;
READ_D(d);
d += HL_IX_IY;
WRITE_BYTE(d, S(Z(opcode)));
elapsed_cycles += 5;
}
break;
}
case Instruction::LD_INDIRECT_HL_N: {
int n;
if (registers == register_table_) {
2025-09-20 14:12:11 +02:00
READ_N(n);
WRITE_BYTE(HL, n);
} else {
int d;
READ_D(d);
d += HL_IX_IY;
READ_N(n);
WRITE_BYTE(d, n);
elapsed_cycles += 2;
}
break;
}
case Instruction::LD_A_INDIRECT_BC: {
READ_BYTE(BC, A);
break;
}
case Instruction::LD_A_INDIRECT_DE: {
READ_BYTE(DE, A);
break;
}
case Instruction::LD_A_INDIRECT_NN: {
int nn;
READ_NN(nn);
READ_BYTE(nn, A);
break;
}
case Instruction::LD_INDIRECT_BC_A: {
WRITE_BYTE(BC, A);
break;
}
case Instruction::LD_INDIRECT_DE_A: {
WRITE_BYTE(DE, A);
break;
}
case Instruction::LD_INDIRECT_NN_A: {
int nn;
READ_NN(nn);
WRITE_BYTE(nn, A);
break;
}
case Instruction::LD_A_I_LD_A_R: {
int a, f;
a = opcode == OPCODE_LD_A_I ? i_ : (r_ & 0x80) | (r & 0x7f);
2025-09-20 14:12:11 +02:00
f = SZYX_FLAGS_TABLE[a];
/* Note: On a real processor, if an interrupt
* occurs during the execution of either
* "LD A, I" or "LD A, R", the parity flag is
* reset. That can never happen here.
*/
f |= iff2_ << Z80_P_FLAG_SHIFT;
2025-09-20 14:12:11 +02:00
f |= F & Z80_C_FLAG;
AF = (a << 8) | f;
elapsed_cycles++;
break;
}
case Instruction::LD_I_A_LD_R_A: {
if (opcode == OPCODE_LD_I_A)
i_ = A;
2025-09-20 14:12:11 +02:00
else {
r_ = A;
2025-09-20 14:12:11 +02:00
r = A & 0x7f;
}
elapsed_cycles++;
break;
}
/* 16-bit load group. */
case Instruction::LD_RR_NN: {
READ_NN(RR(P(opcode)));
break;
}
case Instruction::LD_HL_INDIRECT_NN: {
int nn;
READ_NN(nn);
READ_WORD(nn, HL_IX_IY);
break;
}
case Instruction::LD_RR_INDIRECT_NN: {
int nn;
READ_NN(nn);
READ_WORD(nn, RR(P(opcode)));
break;
}
case Instruction::LD_INDIRECT_NN_HL: {
int nn;
READ_NN(nn);
WRITE_WORD(nn, HL_IX_IY);
break;
}
case Instruction::LD_INDIRECT_NN_RR: {
int nn;
READ_NN(nn);
WRITE_WORD(nn, RR(P(opcode)));
break;
}
case Instruction::LD_SP_HL: {
SP = HL_IX_IY;
elapsed_cycles += 2;
break;
}
case Instruction::PUSH_SS: {
PUSH(SS(P(opcode)));
elapsed_cycles++;
break;
}
case Instruction::POP_SS: {
POP(SS(P(opcode)));
break;
}
/* Exchange, block transfer and search group. */
case Instruction::EX_DE_HL: {
EXCHANGE(DE, HL);
break;
}
case Instruction::EX_AF_AF_PRIME: {
EXCHANGE(AF, alternates_[Z80_AF]);
2025-09-20 14:12:11 +02:00
break;
}
case Instruction::EXX: {
EXCHANGE(BC, alternates_[Z80_BC]);
EXCHANGE(DE, alternates_[Z80_DE]);
EXCHANGE(HL, alternates_[Z80_HL]);
2025-09-20 14:12:11 +02:00
break;
}
case Instruction::EX_INDIRECT_SP_HL: {
int t;
READ_WORD(SP, t);
WRITE_WORD(SP, HL_IX_IY);
HL_IX_IY = t;
elapsed_cycles += 3;
break;
}
case Instruction::LDI_LDD: {
int n, f, d;
READ_BYTE(HL, n);
WRITE_BYTE(DE, n);
f = F & SZC_FLAGS;
f |= --BC ? Z80_P_FLAG : 0;
if constexpr (!Board::kDocumentedFlagsOnly) {
n += A;
f |= n & Z80_X_FLAG;
f |= (n << (Z80_Y_FLAG_SHIFT - 1)) & Z80_Y_FLAG;
}
2025-09-20 14:12:11 +02:00
F = f;
d = opcode == OPCODE_LDI ? +1 : -1;
DE += d;
HL += d;
elapsed_cycles += 2;
break;
}
case Instruction::LDIR_LDDR: {
int d, f, bc, de, hl, n;
#ifdef Z80_HANDLE_SELF_MODIFYING_CODE
int p, q;
p = (pc - 2) & 0xffff;
q = (pc - 1) & 0xffff;
#endif
d = opcode == OPCODE_LDIR ? +1 : -1;
f = F & SZC_FLAGS;
bc = BC;
de = DE;
hl = HL;
r -= 2;
elapsed_cycles -= 8;
for (;;) {
r += 2;
Z80_READ_BYTE(hl, n);
Z80_WRITE_BYTE(de, n);
hl += d;
de += d;
if (--bc)
elapsed_cycles += 21;
else {
elapsed_cycles += 16;
break;
}
#ifdef Z80_HANDLE_SELF_MODIFYING_CODE
if (((de - d) & 0xffff) == p || ((de - d) & 0xffff) == q) {
f |= Z80_P_FLAG;
pc -= 2;
break;
}
#endif
if (elapsed_cycles < number_cycles)
continue;
else {
f |= Z80_P_FLAG;
pc -= 2;
break;
}
}
HL = hl;
DE = de;
BC = bc;
if constexpr (!Board::kDocumentedFlagsOnly) {
n += A;
f |= n & Z80_X_FLAG;
f |= (n << (Z80_Y_FLAG_SHIFT - 1)) & Z80_Y_FLAG;
}
2025-09-20 14:12:11 +02:00
F = f;
break;
}
case Instruction::CPI_CPD: {
int a, n, z, f;
a = A;
READ_BYTE(HL, n);
z = a - n;
HL += opcode == OPCODE_CPI ? +1 : -1;
f = (a ^ n ^ z) & Z80_H_FLAG;
if constexpr (!Board::kDocumentedFlagsOnly) {
n = z - (f >> Z80_H_FLAG_SHIFT);
f |= (n << (Z80_Y_FLAG_SHIFT - 1)) & Z80_Y_FLAG;
f |= n & Z80_X_FLAG;
}
2025-09-20 14:12:11 +02:00
f |= SZYX_FLAGS_TABLE[z & 0xff] & SZ_FLAGS;
f |= --BC ? Z80_P_FLAG : 0;
F = f | Z80_N_FLAG | (F & Z80_C_FLAG);
elapsed_cycles += 5;
break;
}
case Instruction::CPIR_CPDR: {
int d, a, bc, hl, n, z, f;
d = opcode == OPCODE_CPIR ? +1 : -1;
a = A;
bc = BC;
hl = HL;
r -= 2;
elapsed_cycles -= 8;
for (;;) {
r += 2;
Z80_READ_BYTE(hl, n);
z = a - n;
hl += d;
if (--bc && z)
elapsed_cycles += 21;
else {
elapsed_cycles += 16;
break;
}
if (elapsed_cycles < number_cycles)
continue;
else {
pc -= 2;
break;
}
}
HL = hl;
BC = bc;
f = (a ^ n ^ z) & Z80_H_FLAG;
if constexpr (!Board::kDocumentedFlagsOnly) {
n = z - (f >> Z80_H_FLAG_SHIFT);
f |= (n << (Z80_Y_FLAG_SHIFT - 1)) & Z80_Y_FLAG;
f |= n & Z80_X_FLAG;
}
2025-09-20 14:12:11 +02:00
f |= SZYX_FLAGS_TABLE[z & 0xff] & SZ_FLAGS;
f |= bc ? Z80_P_FLAG : 0;
F = f | Z80_N_FLAG | (F & Z80_C_FLAG);
break;
}
/* 8-bit arithmetic and logical group. */
case Instruction::ADD_R: {
ADD(R(Z(opcode)));
break;
}
case Instruction::ADD_N: {
int n;
READ_N(n);
ADD(n);
break;
}
case Instruction::ADD_INDIRECT_HL: {
int x;
READ_INDIRECT_HL(x);
ADD(x);
break;
}
case Instruction::ADC_R: {
ADC(R(Z(opcode)));
break;
}
case Instruction::ADC_N: {
int n;
READ_N(n);
ADC(n);
break;
}
case Instruction::ADC_INDIRECT_HL: {
int x;
READ_INDIRECT_HL(x);
ADC(x);
break;
}
case Instruction::SUB_R: {
SUB(R(Z(opcode)));
break;
}
case Instruction::SUB_N: {
int n;
READ_N(n);
SUB(n);
break;
}
case Instruction::SUB_INDIRECT_HL: {
int x;
READ_INDIRECT_HL(x);
SUB(x);
break;
}
case Instruction::SBC_R: {
SBC(R(Z(opcode)));
break;
}
case Instruction::SBC_N: {
int n;
READ_N(n);
SBC(n);
break;
}
case Instruction::SBC_INDIRECT_HL: {
int x;
READ_INDIRECT_HL(x);
SBC(x);
break;
}
case Instruction::AND_R: {
AND(R(Z(opcode)));
break;
}
case Instruction::AND_N: {
int n;
READ_N(n);
AND(n);
break;
}
case Instruction::AND_INDIRECT_HL: {
int x;
READ_INDIRECT_HL(x);
AND(x);
break;
}
case Instruction::OR_R: {
OR(R(Z(opcode)));
break;
}
case Instruction::OR_N: {
int n;
READ_N(n);
OR(n);
break;
}
case Instruction::OR_INDIRECT_HL: {
int x;
READ_INDIRECT_HL(x);
OR(x);
break;
}
case Instruction::XOR_R: {
XOR(R(Z(opcode)));
break;
}
case Instruction::XOR_N: {
int n;
READ_N(n);
XOR(n);
break;
}
case Instruction::XOR_INDIRECT_HL: {
int x;
READ_INDIRECT_HL(x);
XOR(x);
break;
}
case Instruction::CP_R: {
CP(R(Z(opcode)));
break;
}
case Instruction::CP_N: {
int n;
READ_N(n);
CP(n);
break;
}
case Instruction::CP_INDIRECT_HL: {
int x;
READ_INDIRECT_HL(x);
CP(x);
break;
}
case Instruction::INC_R: {
INC(R(Y(opcode)));
break;
}
case Instruction::INC_INDIRECT_HL: {
int x;
if (registers == register_table_) {
2025-09-20 14:12:11 +02:00
READ_BYTE(HL, x);
INC(x);
WRITE_BYTE(HL, x);
elapsed_cycles++;
} else {
int d;
READ_D(d);
d += HL_IX_IY;
READ_BYTE(d, x);
INC(x);
WRITE_BYTE(d, x);
elapsed_cycles += 6;
}
break;
}
case Instruction::DEC_R: {
DEC(R(Y(opcode)));
break;
}
case Instruction::DEC_INDIRECT_HL: {
int x;
if (registers == register_table_) {
2025-09-20 14:12:11 +02:00
READ_BYTE(HL, x);
DEC(x);
WRITE_BYTE(HL, x);
elapsed_cycles++;
} else {
int d;
READ_D(d);
d += HL_IX_IY;
READ_BYTE(d, x);
DEC(x);
WRITE_BYTE(d, x);
elapsed_cycles += 6;
}
break;
}
/* General-purpose arithmetic and CPU control group. */
case Instruction::DAA: {
int a, c, d;
/* The following algorithm is from
* comp.sys.sinclair's FAQ.
*/
a = A;
if (a > 0x99 || (F & Z80_C_FLAG)) {
c = Z80_C_FLAG;
d = 0x60;
} else
c = d = 0;
if ((a & 0x0f) > 0x09 || (F & Z80_H_FLAG)) d += 0x06;
A += F & Z80_N_FLAG ? -d : +d;
F = SZYXP_FLAGS_TABLE[A] | ((A ^ a) & Z80_H_FLAG) | (F & Z80_N_FLAG) |
c;
break;
}
case Instruction::CPL: {
A = ~A;
F = (F & (SZPV_FLAGS | Z80_C_FLAG)) | Z80_H_FLAG | Z80_N_FLAG;
2025-09-20 14:12:11 +02:00
if constexpr (!Board::kDocumentedFlagsOnly) {
F |= (A & YX_FLAGS);
}
2025-09-20 14:12:11 +02:00
break;
}
case Instruction::NEG: {
int a, f, z, c;
a = A;
z = -a;
c = a ^ z;
f = Z80_N_FLAG | (c & Z80_H_FLAG);
f |= SZYX_FLAGS_TABLE[z &= 0xff];
c &= 0x0180;
f |= OVERFLOW_TABLE[c >> 7];
f |= c >> (8 - Z80_C_FLAG_SHIFT);
A = z;
F = f;
break;
}
case Instruction::CCF: {
int c;
c = F & Z80_C_FLAG;
F = (F & SZPV_FLAGS) | (c << Z80_H_FLAG_SHIFT) | (c ^ Z80_C_FLAG);
2025-09-20 14:12:11 +02:00
if constexpr (!Board::kDocumentedFlagsOnly) {
F |= (A & YX_FLAGS);
}
2025-09-20 14:12:11 +02:00
break;
}
case Instruction::SCF: {
F = (F & SZPV_FLAGS) | Z80_C_FLAG;
2025-09-20 14:12:11 +02:00
if constexpr (!Board::kDocumentedFlagsOnly) {
F |= (A & YX_FLAGS);
}
2025-09-20 14:12:11 +02:00
break;
}
case Instruction::NOP: {
break;
}
case Instruction::HALT: {
2025-09-21 19:30:34 +02:00
board_.Halt();
2025-09-20 14:12:11 +02:00
#ifdef Z80_CATCH_HALT
status_ = Status::HALT;
2025-09-20 14:12:11 +02:00
#else
/* If an HALT instruction is executed, the Z80
* keeps executing NOPs until an interrupt is
* generated. Basically nothing happens for the
* remaining number of cycles.
*/
if (elapsed_cycles < number_cycles) elapsed_cycles = number_cycles;
#endif
goto stop_emulation;
}
case Instruction::DI: {
iff1_ = iff2_ = 0;
2025-09-20 14:12:11 +02:00
#ifdef Z80_CATCH_DI
status_ = Z80_STATUS_FLAG_DI;
2025-09-20 14:12:11 +02:00
goto stop_emulation;
#else
/* No interrupt can be accepted right after
* a DI or EI instruction on an actual Z80
* processor. By adding 4 cycles to
* number_cycles, at least one more
* instruction will be executed. However, this
* will fail if the next instruction has
* multiple 0xdd or 0xfd prefixes and
* Z80_PREFIX_FAILSAFE is defined, but that
* is an unlikely pathological case.
*/
number_cycles += 4;
break;
#endif
}
case Instruction::EI: {
iff1_ = iff2_ = 1;
2025-09-20 14:12:11 +02:00
#ifdef Z80_CATCH_EI
status_ = Status::EI;
2025-09-20 14:12:11 +02:00
goto stop_emulation;
#else
/* See comment for DI. */
number_cycles += 4;
break;
#endif
}
case Instruction::IM_N: {
/* "IM 0/1" (0xed prefixed opcodes 0x4e and
* 0x6e) is treated like a "IM 0".
*/
if ((Y(opcode) & 0x03) <= 0x01)
im_ = Z80_INTERRUPT_MODE_0;
2025-09-20 14:12:11 +02:00
else if (!(Y(opcode) & 1))
im_ = Z80_INTERRUPT_MODE_1;
2025-09-20 14:12:11 +02:00
else
im_ = Z80_INTERRUPT_MODE_2;
2025-09-20 14:12:11 +02:00
break;
}
/* 16-bit arithmetic group. */
case Instruction::ADD_HL_RR: {
int x, y, z, f, c;
x = HL_IX_IY;
y = RR(P(opcode));
z = x + y;
c = x ^ y ^ z;
f = F & SZPV_FLAGS;
if constexpr (!Board::kDocumentedFlagsOnly) {
f |= (z >> 8) & YX_FLAGS;
f |= (c >> 8) & Z80_H_FLAG;
}
2025-09-20 14:12:11 +02:00
f |= c >> (16 - Z80_C_FLAG_SHIFT);
HL_IX_IY = z;
F = f;
elapsed_cycles += 7;
break;
}
case Instruction::ADC_HL_RR: {
int x, y, z, f, c;
x = HL;
y = RR(P(opcode));
z = x + y + (F & Z80_C_FLAG);
c = x ^ y ^ z;
f = z & 0xffff ? (z >> 8) & SYX_FLAGS : Z80_Z_FLAG;
if constexpr (!Board::kDocumentedFlagsOnly) {
f |= (c >> 8) & Z80_H_FLAG;
}
2025-09-20 14:12:11 +02:00
f |= OVERFLOW_TABLE[c >> 15];
f |= z >> (16 - Z80_C_FLAG_SHIFT);
HL = z;
F = f;
elapsed_cycles += 7;
break;
}
case Instruction::SBC_HL_RR: {
int x, y, z, f, c;
x = HL;
y = RR(P(opcode));
z = x - y - (F & Z80_C_FLAG);
c = x ^ y ^ z;
f = Z80_N_FLAG;
f |= z & 0xffff ? (z >> 8) & SYX_FLAGS : Z80_Z_FLAG;
if constexpr (!Board::kDocumentedFlagsOnly) {
f |= (c >> 8) & Z80_H_FLAG;
}
2025-09-20 14:12:11 +02:00
c &= 0x018000;
f |= OVERFLOW_TABLE[c >> 15];
f |= c >> (16 - Z80_C_FLAG_SHIFT);
HL = z;
F = f;
elapsed_cycles += 7;
break;
}
case Instruction::INC_RR: {
int x;
x = RR(P(opcode));
x++;
RR(P(opcode)) = x;
elapsed_cycles += 2;
break;
}
case Instruction::DEC_RR: {
int x;
x = RR(P(opcode));
x--;
RR(P(opcode)) = x;
elapsed_cycles += 2;
break;
}
/* Rotate and shift group. */
case Instruction::RLCA: {
A = (A << 1) | (A >> 7);
F = (F & SZPV_FLAGS) | (A & (YX_FLAGS | Z80_C_FLAG));
break;
}
case Instruction::RLA: {
int a, f;
a = A << 1;
f = (F & SZPV_FLAGS) | (A >> 7);
2025-09-20 14:12:11 +02:00
if constexpr (!Board::kDocumentedFlagsOnly) {
f |= (a & YX_FLAGS);
}
2025-09-20 14:12:11 +02:00
A = a | (F & Z80_C_FLAG);
F = f;
break;
}
case Instruction::RRCA: {
int c;
c = A & 0x01;
A = (A >> 1) | (A << 7);
F = (F & SZPV_FLAGS) | c;
2025-09-20 14:12:11 +02:00
if constexpr (!Board::kDocumentedFlagsOnly) {
F |= (A & YX_FLAGS);
}
2025-09-20 14:12:11 +02:00
break;
}
case Instruction::RRA: {
int c;
c = A & 0x01;
A = (A >> 1) | ((F & Z80_C_FLAG) << 7);
F = (F & SZPV_FLAGS) | c;
2025-09-20 14:12:11 +02:00
if constexpr (!Board::kDocumentedFlagsOnly) {
F |= (A & YX_FLAGS);
}
2025-09-20 14:12:11 +02:00
break;
}
case Instruction::RLC_R: {
RLC(R(Z(opcode)));
break;
}
case Instruction::RLC_INDIRECT_HL: {
int x;
if (registers == register_table_) {
2025-09-20 14:12:11 +02:00
READ_BYTE(HL, x);
RLC(x);
WRITE_BYTE(HL, x);
elapsed_cycles++;
} else {
int d;
Z80_FETCH_BYTE(pc, d);
d = ((signed char)d) + HL_IX_IY;
READ_BYTE(d, x);
RLC(x);
WRITE_BYTE(d, x);
if (Z(opcode) != INDIRECT_HL) R(Z(opcode)) = x;
pc += 2;
elapsed_cycles += 5;
}
break;
}
case Instruction::RL_R: {
RL(R(Z(opcode)));
break;
}
case Instruction::RL_INDIRECT_HL: {
int x;
if (registers == register_table_) {
2025-09-20 14:12:11 +02:00
READ_BYTE(HL, x);
RL(x);
WRITE_BYTE(HL, x);
elapsed_cycles++;
} else {
int d;
Z80_FETCH_BYTE(pc, d);
d = ((signed char)d) + HL_IX_IY;
READ_BYTE(d, x);
RL(x);
WRITE_BYTE(d, x);
if (Z(opcode) != INDIRECT_HL) R(Z(opcode)) = x;
pc += 2;
elapsed_cycles += 5;
}
break;
}
case Instruction::RRC_R: {
RRC(R(Z(opcode)));
break;
}
case Instruction::RRC_INDIRECT_HL: {
int x;
if (registers == register_table_) {
2025-09-20 14:12:11 +02:00
READ_BYTE(HL, x);
RRC(x);
WRITE_BYTE(HL, x);
elapsed_cycles++;
} else {
int d;
Z80_FETCH_BYTE(pc, d);
d = ((signed char)d) + HL_IX_IY;
READ_BYTE(d, x);
RRC(x);
WRITE_BYTE(d, x);
if (Z(opcode) != INDIRECT_HL) R(Z(opcode)) = x;
pc += 2;
elapsed_cycles += 5;
}
break;
}
case Instruction::RR_R: {
RR_INSTRUCTION(R(Z(opcode)));
break;
}
case Instruction::RR_INDIRECT_HL: {
int x;
if (registers == register_table_) {
2025-09-20 14:12:11 +02:00
READ_BYTE(HL, x);
RR_INSTRUCTION(x);
WRITE_BYTE(HL, x);
elapsed_cycles++;
} else {
int d;
Z80_FETCH_BYTE(pc, d);
d = ((signed char)d) + HL_IX_IY;
READ_BYTE(d, x);
RR_INSTRUCTION(x);
WRITE_BYTE(d, x);
if (Z(opcode) != INDIRECT_HL) R(Z(opcode)) = x;
pc += 2;
elapsed_cycles += 5;
}
break;
}
case Instruction::SLA_R: {
SLA(R(Z(opcode)));
break;
}
case Instruction::SLA_INDIRECT_HL: {
int x;
if (registers == register_table_) {
2025-09-20 14:12:11 +02:00
READ_BYTE(HL, x);
SLA(x);
WRITE_BYTE(HL, x);
elapsed_cycles++;
} else {
int d;
Z80_FETCH_BYTE(pc, d);
d = ((signed char)d) + HL_IX_IY;
READ_BYTE(d, x);
SLA(x);
WRITE_BYTE(d, x);
if (Z(opcode) != INDIRECT_HL) R(Z(opcode)) = x;
pc += 2;
elapsed_cycles += 5;
}
break;
}
case Instruction::SLL_R: {
SLL(R(Z(opcode)));
break;
}
case Instruction::SLL_INDIRECT_HL: {
int x;
if (registers == register_table_) {
2025-09-20 14:12:11 +02:00
READ_BYTE(HL, x);
SLL(x);
WRITE_BYTE(HL, x);
elapsed_cycles++;
} else {
int d;
Z80_FETCH_BYTE(pc, d);
d = ((signed char)d) + HL_IX_IY;
READ_BYTE(d, x);
SLL(x);
WRITE_BYTE(d, x);
if (Z(opcode) != INDIRECT_HL) R(Z(opcode)) = x;
pc += 2;
elapsed_cycles += 5;
}
break;
}
case Instruction::SRA_R: {
SRA(R(Z(opcode)));
break;
}
case Instruction::SRA_INDIRECT_HL: {
int x;
if (registers == register_table_) {
2025-09-20 14:12:11 +02:00
READ_BYTE(HL, x);
SRA(x);
WRITE_BYTE(HL, x);
elapsed_cycles++;
} else {
int d;
Z80_FETCH_BYTE(pc, d);
d = ((signed char)d) + HL_IX_IY;
READ_BYTE(d, x);
SRA(x);
WRITE_BYTE(d, x);
if (Z(opcode) != INDIRECT_HL) R(Z(opcode)) = x;
pc += 2;
elapsed_cycles += 5;
}
break;
}
case Instruction::SRL_R: {
SRL(R(Z(opcode)));
break;
}
case Instruction::SRL_INDIRECT_HL: {
int x;
if (registers == register_table_) {
2025-09-20 14:12:11 +02:00
READ_BYTE(HL, x);
SRL(x);
WRITE_BYTE(HL, x);
elapsed_cycles++;
} else {
int d;
Z80_FETCH_BYTE(pc, d);
d = ((signed char)d) + HL_IX_IY;
READ_BYTE(d, x);
SRL(x);
WRITE_BYTE(d, x);
if (Z(opcode) != INDIRECT_HL) R(Z(opcode)) = x;
pc += 2;
elapsed_cycles += 5;
}
break;
}
case Instruction::RLD_RRD: {
int x, y;
READ_BYTE(HL, x);
y = (A & 0xf0) << 8;
y |= opcode == OPCODE_RLD
? (x << 4) | (A & 0x0f)
: ((x & 0x0f) << 8) | ((A & 0x0f) << 4) | (x >> 4);
WRITE_BYTE(HL, y);
y >>= 8;
A = y;
F = SZYXP_FLAGS_TABLE[y] | (F & Z80_C_FLAG);
elapsed_cycles += 4;
break;
}
/* Bit set, reset, and test group. */
case Instruction::BIT_B_R: {
int x;
x = R(Z(opcode)) & (1 << Y(opcode));
F = (x ? 0 : Z80_Z_FLAG | Z80_P_FLAG) | Z80_H_FLAG | (F & Z80_C_FLAG);
2025-09-20 14:12:11 +02:00
if constexpr (!Board::kDocumentedFlagsOnly) {
F |= (x & Z80_S_FLAG) | (R(Z(opcode)) & YX_FLAGS);
}
2025-09-20 14:12:11 +02:00
break;
}
case Instruction::BIT_B_INDIRECT_HL: {
int d, x;
if (registers == register_table_) {
2025-09-20 14:12:11 +02:00
d = HL;
elapsed_cycles++;
} else {
Z80_FETCH_BYTE(pc, d);
d = ((signed char)d) + HL_IX_IY;
pc += 2;
elapsed_cycles += 5;
}
READ_BYTE(d, x);
x &= 1 << Y(opcode);
F = (x ? 0 : Z80_Z_FLAG | Z80_P_FLAG) | Z80_H_FLAG | (F & Z80_C_FLAG);
2025-09-20 14:12:11 +02:00
if constexpr (!Board::kDocumentedFlagsOnly) {
F |= (x & Z80_S_FLAG) | (d & YX_FLAGS);
}
2025-09-20 14:12:11 +02:00
break;
}
case Instruction::SET_B_R: {
R(Z(opcode)) |= 1 << Y(opcode);
break;
}
case Instruction::SET_B_INDIRECT_HL: {
int x;
if (registers == register_table_) {
2025-09-20 14:12:11 +02:00
READ_BYTE(HL, x);
x |= 1 << Y(opcode);
WRITE_BYTE(HL, x);
elapsed_cycles++;
} else {
int d;
Z80_FETCH_BYTE(pc, d);
d = ((signed char)d) + HL_IX_IY;
READ_BYTE(d, x);
x |= 1 << Y(opcode);
WRITE_BYTE(d, x);
if (Z(opcode) != INDIRECT_HL) R(Z(opcode)) = x;
pc += 2;
elapsed_cycles += 5;
}
break;
}
case Instruction::RES_B_R: {
R(Z(opcode)) &= ~(1 << Y(opcode));
break;
}
case Instruction::RES_B_INDIRECT_HL: {
int x;
if (registers == register_table_) {
2025-09-20 14:12:11 +02:00
READ_BYTE(HL, x);
x &= ~(1 << Y(opcode));
WRITE_BYTE(HL, x);
elapsed_cycles++;
} else {
int d;
Z80_FETCH_BYTE(pc, d);
d = ((signed char)d) + HL_IX_IY;
READ_BYTE(d, x);
x &= ~(1 << Y(opcode));
WRITE_BYTE(d, x);
if (Z(opcode) != INDIRECT_HL) R(Z(opcode)) = x;
pc += 2;
elapsed_cycles += 5;
}
break;
}
/* Jump group. */
case Instruction::JP_NN: {
int nn;
Z80_FETCH_WORD(pc, nn);
pc = nn;
elapsed_cycles += 6;
break;
}
case Instruction::JP_CC_NN: {
int nn;
if (CC(Y(opcode))) {
Z80_FETCH_WORD(pc, nn);
pc = nn;
} else {
#ifdef Z80_FALSE_CONDITION_FETCH
Z80_FETCH_WORD(pc, nn);
#endif
pc += 2;
}
elapsed_cycles += 6;
break;
}
case Instruction::JR_E: {
int e;
Z80_FETCH_BYTE(pc, e);
pc += ((signed char)e) + 1;
elapsed_cycles += 8;
break;
}
case Instruction::JR_DD_E: {
int e;
if (DD(Q(opcode))) {
Z80_FETCH_BYTE(pc, e);
pc += ((signed char)e) + 1;
elapsed_cycles += 8;
} else {
#ifdef Z80_FALSE_CONDITION_FETCH
Z80_FETCH_BYTE(pc, e);
#endif
pc++;
elapsed_cycles += 3;
}
break;
}
case Instruction::JP_HL: {
pc = HL_IX_IY;
break;
}
case Instruction::DJNZ_E: {
int e;
if (--B) {
Z80_FETCH_BYTE(pc, e);
pc += ((signed char)e) + 1;
elapsed_cycles += 9;
} else {
#ifdef Z80_FALSE_CONDITION_FETCH
Z80_FETCH_BYTE(pc, e);
#endif
pc++;
elapsed_cycles += 4;
}
break;
}
/* Call and return group. */
case Instruction::CALL_NN: {
int nn;
READ_NN(nn);
PUSH(pc);
pc = nn;
elapsed_cycles++;
break;
}
case Instruction::CALL_CC_NN: {
int nn;
if (CC(Y(opcode))) {
READ_NN(nn);
PUSH(pc);
pc = nn;
elapsed_cycles++;
} else {
#ifdef Z80_FALSE_CONDITION_FETCH
Z80_FETCH_WORD(pc, nn);
#endif
pc += 2;
elapsed_cycles += 6;
}
break;
}
case Instruction::RET: {
POP(pc);
if (elapsed_cycles >= number_cycles) goto stop_emulation;
break;
}
case Instruction::RET_CC: {
if (CC(Y(opcode))) {
POP(pc);
}
elapsed_cycles++;
if (elapsed_cycles >= number_cycles) goto stop_emulation;
break;
}
case Instruction::RETI_RETN: {
iff1_ = iff2_;
2025-09-20 14:12:11 +02:00
POP(pc);
#if defined(Z80_CATCH_RETI) && defined(Z80_CATCH_RETN)
status_ =
2025-09-20 14:12:11 +02:00
opcode == OPCODE_RETI ? Z80_STATUS_FLAG_RETI : Z80_STATUS_FLAG_RETN;
goto stop_emulation;
#elif defined(Z80_CATCH_RETI)
status_ = Z80_STATUS_FLAG_RETI;
2025-09-20 14:12:11 +02:00
goto stop_emulation;
#elif defined(Z80_CATCH_RETN)
status_ = Z80_STATUS_FLAG_RETN;
2025-09-20 14:12:11 +02:00
goto stop_emulation;
#else
break;
#endif
}
case Instruction::RST_P: {
PUSH(pc);
pc = RST_TABLE[Y(opcode)];
elapsed_cycles++;
break;
}
/* Input and output group. */
case Instruction::IN_A_N: {
int n;
READ_N(n);
Z80_INPUT_BYTE(n, A);
elapsed_cycles += 4;
break;
}
case Instruction::IN_R_C: {
int x;
Z80_INPUT_BYTE(BC, x);
2025-09-20 14:12:11 +02:00
if (Y(opcode) != INDIRECT_HL) R(Y(opcode)) = x;
F = SZYXP_FLAGS_TABLE[x] | (F & Z80_C_FLAG);
elapsed_cycles += 4;
break;
}
/* Some of the undocumented flags for "INI", "IND",
* "INIR", "INDR", "OUTI", "OUTD", "OTIR", and
* "OTDR" are really really strange. The emulator
* implements the specifications described in "The
* Undocumented Z80 Documented Version 0.91".
*/
case Instruction::INI_IND: {
int x, f;
Z80_INPUT_BYTE(C, x);
WRITE_BYTE(HL, x);
f = SZYX_FLAGS_TABLE[--B & 0xff] | (x >> (7 - Z80_N_FLAG_SHIFT));
if (opcode == OPCODE_INI) {
HL++;
x += (C + 1) & 0xff;
} else {
HL--;
x += (C - 1) & 0xff;
}
f |= x & 0x0100 ? HC_FLAGS : 0;
f |= SZYXP_FLAGS_TABLE[(x & 0x07) ^ B] & Z80_P_FLAG;
F = f;
elapsed_cycles += 5;
break;
}
case Instruction::INIR_INDR: {
int d, b, hl, x, f;
#ifdef Z80_HANDLE_SELF_MODIFYING_CODE
int p, q;
p = (pc - 2) & 0xffff;
q = (pc - 1) & 0xffff;
#endif
d = opcode == OPCODE_INIR ? +1 : -1;
b = B;
hl = HL;
r -= 2;
elapsed_cycles -= 8;
for (;;) {
r += 2;
Z80_INPUT_BYTE(C, x);
Z80_WRITE_BYTE(hl, x);
hl += d;
if (--b)
elapsed_cycles += 21;
else {
f = Z80_Z_FLAG;
elapsed_cycles += 16;
break;
}
#ifdef Z80_HANDLE_SELF_MODIFYING_CODE
if (((hl - d) & 0xffff) == p || ((hl - d) & 0xffff) == q) {
f = SZYX_FLAGS_TABLE[b];
pc -= 2;
break;
}
#endif
if (elapsed_cycles < number_cycles)
continue;
else {
f = SZYX_FLAGS_TABLE[b];
pc -= 2;
break;
}
}
HL = hl;
B = b;
f |= x >> (7 - Z80_N_FLAG_SHIFT);
x += (C + d) & 0xff;
f |= x & 0x0100 ? HC_FLAGS : 0;
f |= SZYXP_FLAGS_TABLE[(x & 0x07) ^ b] & Z80_P_FLAG;
F = f;
break;
}
case Instruction::OUT_N_A: {
int n;
elapsed_cycles += 4;
2025-09-21 15:30:27 +02:00
READ_N(n);
if (Z80_OUTPUT_BYTE(n, A)) {
goto stop_emulation;
}
2025-09-20 14:12:11 +02:00
break;
}
case Instruction::OUT_C_R: {
int x;
elapsed_cycles += 4;
2025-09-21 15:30:27 +02:00
x = Y(opcode) != INDIRECT_HL ? R(Y(opcode)) : 0;
if (Z80_OUTPUT_BYTE(C, x)) {
goto stop_emulation;
}
2025-09-20 14:12:11 +02:00
break;
}
case Instruction::OUTI_OUTD: {
int x, f;
READ_BYTE(HL, x);
Z80_OUTPUT_BYTE(C, x);
HL += opcode == OPCODE_OUTI ? +1 : -1;
f = SZYX_FLAGS_TABLE[--B & 0xff] | (x >> (7 - Z80_N_FLAG_SHIFT));
x += HL & 0xff;
f |= x & 0x0100 ? HC_FLAGS : 0;
f |= SZYXP_FLAGS_TABLE[(x & 0x07) ^ B] & Z80_P_FLAG;
F = f;
break;
}
case Instruction::OTIR_OTDR: {
int d, b, hl, x, f;
d = opcode == OPCODE_OTIR ? +1 : -1;
b = B;
hl = HL;
r -= 2;
elapsed_cycles -= 8;
for (;;) {
r += 2;
Z80_READ_BYTE(hl, x);
Z80_OUTPUT_BYTE(C, x);
hl += d;
if (--b)
elapsed_cycles += 21;
else {
f = Z80_Z_FLAG;
elapsed_cycles += 16;
break;
}
if (elapsed_cycles < number_cycles)
continue;
else {
f = SZYX_FLAGS_TABLE[b];
pc -= 2;
break;
}
}
HL = hl;
B = b;
f |= x >> (7 - Z80_N_FLAG_SHIFT);
x += hl & 0xff;
f |= x & 0x0100 ? HC_FLAGS : 0;
f |= SZYXP_FLAGS_TABLE[(x & 0x07) ^ b] & Z80_P_FLAG;
F = f;
break;
}
/* Prefix group. */
case Instruction::CB_PREFIX: {
/* Special handling if the 0xcb prefix is
* prefixed by a 0xdd or 0xfd prefix.
*/
if (registers != register_table_) {
2025-09-20 14:12:11 +02:00
r--;
/* Indexed memory access routine will
* correctly update pc.
*/
Z80_FETCH_BYTE(pc + 1, opcode);
} else {
Z80_FETCH_BYTE(pc, opcode);
pc++;
}
instruction = (Instruction)CB_INSTRUCTION_TABLE[opcode];
goto emulate_next_instruction;
}
case Instruction::DD_PREFIX: {
registers = dd_register_table_;
2025-09-20 14:12:11 +02:00
#ifdef Z80_PREFIX_FAILSAFE
/* Ensure that at least number_cycles cycles
* are executed.
*/
if (elapsed_cycles < number_cycles) {
Z80_FETCH_BYTE(pc, opcode);
pc++;
goto emulate_next_opcode;
} else {
status_ = Z80_STATUS_PREFIX;
2025-09-20 14:12:11 +02:00
pc--;
elapsed_cycles -= 4;
goto stop_emulation;
}
#else
Z80_FETCH_BYTE(pc, opcode);
pc++;
goto emulate_next_opcode;
#endif
}
case Instruction::FD_PREFIX: {
registers = fd_register_table_;
2025-09-20 14:12:11 +02:00
#ifdef Z80_PREFIX_FAILSAFE
if (elapsed_cycles < number_cycles) {
Z80_FETCH_BYTE(pc, opcode);
pc++;
goto emulate_next_opcode;
} else {
status_ = Z80_STATUS_PREFIX;
2025-09-20 14:12:11 +02:00
pc--;
elapsed_cycles -= 4;
goto stop_emulation;
}
#else
Z80_FETCH_BYTE(pc, opcode);
pc++;
goto emulate_next_opcode;
#endif
}
case Instruction::ED_PREFIX: {
registers = register_table_;
2025-09-20 14:12:11 +02:00
Z80_FETCH_BYTE(pc, opcode);
pc++;
instruction = (Instruction)ED_INSTRUCTION_TABLE[opcode];
goto emulate_next_instruction;
}
/* Special/pseudo instruction group. */
case Instruction::ED_UNDEFINED: {
#ifdef Z80_CATCH_ED_UNDEFINED
status_ = Z80_STATUS_FLAG_ED_UNDEFINED;
2025-09-20 14:12:11 +02:00
pc -= 2;
goto stop_emulation;
#else
break;
#endif
}
}
// if (elapsed_cycles >= number_cycles) goto stop_emulation;
}
stop_emulation:
r_ = (r_ & 0x80) | (r & 0x7f);
pc_ = pc & 0xffff;
2025-09-20 14:12:11 +02:00
return elapsed_cycles;
}
template <typename Board>
uint8_t Z80Emu<Board>::ReadReg(int reg) {
return registers_.byte[reg];
}
2025-09-20 14:12:11 +02:00
template <typename Board>
uint16_t Z80Emu<Board>::ReadReg16(int reg) {
return registers_.word[reg];
}
2025-09-20 14:12:11 +02:00
} // namespace z80emu