/* 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 { /* 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) #define Z80_INPUT_BYTE(port, x) x = board_.InputByte(*this, port) #define Z80_OUTPUT_BYTE(port, x) board_.OutputByte(port, x) #define Z80_CATCH_HALT #define Z80_CATCH_EI /* 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 void Z80Emu::Reset(uint16_t pc) { int i; status_ = Status::UNDEFINED; AF = 0xffff; SP = 0xffff; i_ = iff1_ = iff2_ = 0; im_ = Z80_INTERRUPT_MODE_0; pc_ = pc; /* 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] = ®isters_.byte[Z80_B]; register_table_[1] = ®isters_.byte[Z80_C]; register_table_[2] = ®isters_.byte[Z80_D]; register_table_[3] = ®isters_.byte[Z80_E]; register_table_[4] = ®isters_.byte[Z80_H]; register_table_[5] = ®isters_.byte[Z80_L]; /* Encoding 0x06 is used for indexed memory operands and direct HL or * IX/IY register access. */ register_table_[6] = ®isters_.word[Z80_HL]; register_table_[7] = ®isters_.byte[Z80_A]; /* "Regular" 16-bit "RR" registers. */ register_table_[8] = ®isters_.word[Z80_BC]; register_table_[9] = ®isters_.word[Z80_DE]; register_table_[10] = ®isters_.word[Z80_HL]; register_table_[11] = ®isters_.word[Z80_SP]; /* 16-bit "SS" registers for PUSH and POP instructions (note that SP is * replaced by AF). */ register_table_[12] = ®isters_.word[Z80_BC]; register_table_[13] = ®isters_.word[Z80_DE]; register_table_[14] = ®isters_.word[Z80_HL]; register_table_[15] = ®isters_.word[Z80_AF]; /* 0xdd and 0xfd prefixed register decoding tables. */ for (i = 0; i < 16; i++) dd_register_table_[i] = fd_register_table_[i] = register_table_[i]; dd_register_table_[4] = ®isters_.byte[Z80_IXH]; dd_register_table_[5] = ®isters_.byte[Z80_IXL]; dd_register_table_[6] = ®isters_.word[Z80_IX]; dd_register_table_[10] = ®isters_.word[Z80_IX]; dd_register_table_[14] = ®isters_.word[Z80_IX]; fd_register_table_[4] = ®isters_.byte[Z80_IYH]; fd_register_table_[5] = ®isters_.byte[Z80_IYL]; fd_register_table_[6] = ®isters_.word[Z80_IY]; fd_register_table_[10] = ®isters_.word[Z80_IY]; fd_register_table_[14] = ®isters_.word[Z80_IY]; } template int Z80Emu::Interrupt(int data_on_bus) { status_ = Status::UNDEFINED; if (iff1_) { iff1_ = iff2_ = 0; r_ = (r_ & 0x80) | ((r_ + 1) & 0x7f); switch (im_) { 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; 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; #ifdef Z80_MASK_IM2_VECTOR_ADDRESS vector &= 0xfffe; #endif Z80_READ_WORD_INTERRUPT(vector, pc_); return elapsed_cycles + 19; } } } else return 0; } template int Z80Emu::NonMaskableInterrupt() { int elapsed_cycles; status_ = Status::UNDEFINED; iff2_ = iff1_; iff1_ = 0; r_ = (r_ & 0x80) | ((r_ + 1) & 0x7f); elapsed_cycles = 0; SP -= 2; Z80_WRITE_WORD_INTERRUPT(SP, pc_); pc_ = 0x0066; return elapsed_cycles + 11; } template int Z80Emu::Emulate(int number_cycles) { int elapsed_cycles, pc, opcode; status_ = Status::UNDEFINED; elapsed_cycles = 0; pc = pc_; Z80_FETCH_BYTE(pc, opcode); pc_ = pc + 1; 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 int Z80Emu::Emulate(int opcode, int elapsed_cycles, int number_cycles) { int pc, r; pc = pc_; r = r_ & 0x7f; goto start_emulation; for (;;) { void **registers; Instruction instruction; Z80_FETCH_BYTE(pc, opcode); pc++; start_emulation: registers = register_table_; 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_) { 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_) { 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_) { 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); 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; 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; else { r_ = A; 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]); break; } case Instruction::EXX: { EXCHANGE(BC, alternates_[Z80_BC]); EXCHANGE(DE, alternates_[Z80_DE]); EXCHANGE(HL, alternates_[Z80_HL]); 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; } 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; } 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; } 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; } 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_) { 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_) { 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; if constexpr (!Board::kDocumentedFlagsOnly) { F |= (A & YX_FLAGS); } 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); if constexpr (!Board::kDocumentedFlagsOnly) { F |= (A & YX_FLAGS); } break; } case Instruction::SCF: { F = (F & SZPV_FLAGS) | Z80_C_FLAG; if constexpr (!Board::kDocumentedFlagsOnly) { F |= (A & YX_FLAGS); } break; } case Instruction::NOP: { break; } case Instruction::HALT: { board_.Halt(); #ifdef Z80_CATCH_HALT status_ = Status::HALT; #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; #ifdef Z80_CATCH_DI status_ = Z80_STATUS_FLAG_DI; 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; #ifdef Z80_CATCH_EI status_ = Status::EI; 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; else if (!(Y(opcode) & 1)) im_ = Z80_INTERRUPT_MODE_1; else im_ = Z80_INTERRUPT_MODE_2; 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; } 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; } 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; } 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); if constexpr (!Board::kDocumentedFlagsOnly) { f |= (a & YX_FLAGS); } 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; if constexpr (!Board::kDocumentedFlagsOnly) { F |= (A & YX_FLAGS); } break; } case Instruction::RRA: { int c; c = A & 0x01; A = (A >> 1) | ((F & Z80_C_FLAG) << 7); F = (F & SZPV_FLAGS) | c; if constexpr (!Board::kDocumentedFlagsOnly) { F |= (A & YX_FLAGS); } break; } case Instruction::RLC_R: { RLC(R(Z(opcode))); break; } case Instruction::RLC_INDIRECT_HL: { int x; if (registers == register_table_) { 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_) { 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_) { 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_) { 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_) { 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_) { 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_) { 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_) { 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); if constexpr (!Board::kDocumentedFlagsOnly) { F |= (x & Z80_S_FLAG) | (R(Z(opcode)) & YX_FLAGS); } break; } case Instruction::BIT_B_INDIRECT_HL: { int d, x; if (registers == register_table_) { 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); if constexpr (!Board::kDocumentedFlagsOnly) { F |= (x & Z80_S_FLAG) | (d & YX_FLAGS); } 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_) { 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_) { 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_; POP(pc); #if defined(Z80_CATCH_RETI) && defined(Z80_CATCH_RETN) status_ = opcode == OPCODE_RETI ? Z80_STATUS_FLAG_RETI : Z80_STATUS_FLAG_RETN; goto stop_emulation; #elif defined(Z80_CATCH_RETI) status_ = Z80_STATUS_FLAG_RETI; goto stop_emulation; #elif defined(Z80_CATCH_RETN) status_ = Z80_STATUS_FLAG_RETN; 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); 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; READ_N(n); if (Z80_OUTPUT_BYTE(n, A)) { goto stop_emulation; } break; } case Instruction::OUT_C_R: { int x; elapsed_cycles += 4; x = Y(opcode) != INDIRECT_HL ? R(Y(opcode)) : 0; if (Z80_OUTPUT_BYTE(C, x)) { goto stop_emulation; } 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_) { 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_; #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; 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_; #ifdef Z80_PREFIX_FAILSAFE if (elapsed_cycles < number_cycles) { Z80_FETCH_BYTE(pc, opcode); pc++; goto emulate_next_opcode; } else { status_ = Z80_STATUS_PREFIX; 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_; 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; 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; return elapsed_cycles; } template uint8_t Z80Emu::ReadReg(int reg) { return registers_.byte[reg]; } template uint16_t Z80Emu::ReadReg16(int reg) { return registers_.word[reg]; } } // namespace z80emu