#include "stdafx.h" #include "Z80.h" // based on http://www.z80.info/decoding.htm // Half carry flag help from https://github.com/oubiwann/z80 Z80::Z80(Memory& memory, InputOutput& ports) : Processor(memory, ports), m_registerSet(0), m_accumulatorFlagsSet(0), m_refresh(0x7f), iv(0xff), m_interruptMode(0), m_iff1(false), m_iff2(false), m1(false), m_prefixCB(false), m_prefixDD(false), m_prefixED(false), m_prefixFD(false) { IX().word = 0xffff; IY().word = 0xffff; MEMPTR().word = 0; } void Z80::reset() { Processor::reset(); IFF1() = IFF2() = false; } void Z80::initialise() { Processor::initialise(); IM() = 0; AF().word = 0xffff; BC().word = 0xffff; DE().word = 0xffff; HL().word = 0xffff; exxAF(); exx(); AF().word = 0xffff; BC().word = 0xffff; DE().word = 0xffff; HL().word = 0xffff; IX().word = 0xffff; IY().word = 0xffff; REFRESH() = 0x7f; IV() = 0xff; MEMPTR().word = 0; m_prefixCB = false; m_prefixDD = false; m_prefixED = false; m_prefixFD = false; } #pragma region Interrupt routines void Z80::disableInterrupts() { IFF1() = IFF2() = false; } void Z80::enableInterrupts() { IFF1() = IFF2() = true; } int Z80::interrupt(bool maskable, uint8_t value) { cycles = 0; if (!maskable || (maskable && IFF1())) { if (maskable) { disableInterrupts(); switch (IM()) { case 0: M1() = true; cycles += execute(value); break; case 1: restart(7 << 3); cycles += 13; break; case 2: pushWord(pc); pc.low = value; pc.high = IV(); cycles += 19; break; } } else { IFF1() = 0; restart(0x66); cycles += 13; } } // Could be zero for a masked interrupt... return cycles; } #pragma endregion Interrupt routines #pragma region Flag manipulation helpers void Z80::adjustSign(uint8_t value) { setFlag(SF, value & SF); } void Z80::adjustZero(uint8_t value) { clearFlag(ZF, value); } void Z80::adjustParity(uint8_t value) { static const uint8_t lookup[0x10] = { 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4 }; auto set = lookup[highNibble(value)] + lookup[lowNibble(value)]; clearFlag(PF, set % 2); } void Z80::adjustSZ(uint8_t value) { adjustSign(value); adjustZero(value); } void Z80::adjustSZP(uint8_t value) { adjustSZ(value); adjustParity(value); } void Z80::adjustXY(uint8_t value) { setFlag(XF, value & XF); setFlag(YF, value & YF); } void Z80::adjustSZPXY(uint8_t value) { adjustSZP(value); adjustXY(value); } void Z80::adjustSZXY(uint8_t value) { adjustSZ(value); adjustXY(value); } void Z80::postIncrement(uint8_t value) { adjustSZXY(value); clearFlag(NF); setFlag(VF, value == Bit7); clearFlag(HC, lowNibble(value)); } void Z80::postDecrement(uint8_t value) { adjustSZXY(value); setFlag(NF); setFlag(VF, value == Mask7); clearFlag(HC, lowNibble(value + 1)); } #pragma endregion Flag manipulation helpers #pragma region PC manipulation: call/ret/jp/jr void Z80::restart(uint8_t address) { pushWord(pc); register16_t destination; destination.word = address; setPcViaMemptr(destination); } void Z80::jrConditional(int conditional) { auto offset = (int8_t)fetchByteData(); if (conditional) { register16_t destination; destination.word = pc.word + offset; setPcViaMemptr(destination); cycles += 5; } } void Z80::jrConditionalFlag(int flag) { switch (flag) { case 0: // NZ jrConditional(!(F() & ZF)); break; case 1: // Z jrConditional(F() & ZF); break; case 2: // NC jrConditional(!(F() & CF)); break; case 3: // C jrConditional(F() & CF); break; case 4: // PO jrConditional(!(F() & PF)); break; case 5: // PE jrConditional(F() & PF); break; case 6: // P jrConditional(!(F() & SF)); break; case 7: // M jrConditional(F() & SF); break; } } void Z80::jumpConditional(int conditional) { auto address = fetchWord(); if (conditional) pc = address; MEMPTR() = address; } void Z80::jumpConditionalFlag(int flag) { switch (flag) { case 0: // NZ jumpConditional(!(F() & ZF)); break; case 1: // Z jumpConditional(F() & ZF); break; case 2: // NC jumpConditional(!(F() & CF)); break; case 3: // C jumpConditional(F() & CF); break; case 4: // PO jumpConditional(!(F() & PF)); break; case 5: // PE jumpConditional(F() & PF); break; case 6: // P jumpConditional(!(F() & SF)); break; case 7: // M jumpConditional(F() & SF); break; } } void Z80::ret() { setPcViaMemptr(popWord()); } void Z80::retn() { ret(); IFF1() = IFF2(); } void Z80::reti() { retn(); } void Z80::returnConditional(int condition) { if (condition) { ret(); cycles += 6; } } void Z80::returnConditionalFlag(int flag) { switch (flag) { case 0: // NZ returnConditional(!(F() & ZF)); break; case 1: // Z returnConditional(F() & ZF); break; case 2: // NC returnConditional(!(F() & CF)); break; case 3: // C returnConditional(F() & CF); break; case 4: // PO returnConditional(!(F() & PF)); break; case 5: // PE returnConditional(F() & PF); break; case 6: // P returnConditional(!(F() & SF)); break; case 7: // M returnConditional(F() & SF); break; } } void Z80::call(register16_t address) { pushWord(pc); pc = address; } void Z80::callConditional(register16_t address, int condition) { if (condition) { call(address); cycles += 7; } MEMPTR() = address; } void Z80::callConditionalFlag(register16_t address, int flag) { switch (flag) { case 0: // NZ callConditional(address, !(F() & ZF)); break; case 1: // Z callConditional(address, F() & ZF); break; case 2: // NC callConditional(address, !(F() & CF)); break; case 3: // C callConditional(address, F() & CF); break; case 4: // PO callConditional(address, !(F() & PF)); break; case 5: // PE callConditional(address, F() & PF); break; case 6: // P callConditional(address, !(F() & SF)); break; case 7: // M callConditional(address, F() & SF); break; } } #pragma endregion PC manipulation: call/ret/jp/jr #pragma region 16-bit arithmetic void Z80::sbc(register16_t& operand, register16_t value) { auto before = operand; auto beforeNegative = before.high & SF; auto valueNegative = value.high & SF; auto result = before.word - value.word - (F() & CF); operand.word = result; auto afterNegative = operand.high & SF; setFlag(SF, afterNegative); clearFlag(ZF, operand.word); adjustHalfCarrySub(before.high, value.high, operand.high); adjustOverflowSub(beforeNegative, valueNegative, afterNegative); setFlag(NF); setFlag(CF, result & Bit16); adjustXY(operand.high); } void Z80::adc(register16_t& operand, register16_t value) { auto before = operand; auto beforeNegative = before.high & SF; auto valueNegative = value.high & SF; auto result = before.word + value.word + (F() & CF); operand.word = result; auto afterNegative = operand.high & SF; setFlag(SF, afterNegative); clearFlag(ZF, result); adjustHalfCarryAdd(before.high, value.high, operand.high); adjustOverflowAdd(beforeNegative, valueNegative, afterNegative); clearFlag(NF); setFlag(CF, result & Bit16); adjustXY(operand.high); } void Z80::add(register16_t& operand, register16_t value) { auto before = operand; auto result = before.word + value.word; operand.word = result; clearFlag(NF); setFlag(CF, result & Bit16); adjustHalfCarryAdd(before.high, value.high, operand.high); adjustXY(operand.high); } #pragma endregion 16-bit arithmetic #pragma region ALU void Z80::add(uint8_t& operand, uint8_t value, int carry) { register16_t result; result.word = operand + value + carry; adjustHalfCarryAdd(operand, value, result.low); adjustOverflowAdd(operand, value, result.low); operand = result.low; clearFlag(NF); setFlag(CF, result.word & Bit8); adjustSZXY(operand); } void Z80::adc(uint8_t& operand, uint8_t value) { add(operand, value, F() & CF); } void Z80::sub(uint8_t& operand, uint8_t value, int carry) { register16_t result; result.word = operand - value - carry; adjustHalfCarrySub(operand, value, result.low); adjustOverflowSub(operand, value, result.low); operand = result.low; setFlag(NF); setFlag(CF, result.word & Bit8); adjustSZXY(operand); } void Z80::sbc(uint8_t& operand, uint8_t value) { sub(operand, value, F() & CF); } void Z80::andr(uint8_t& operand, uint8_t value) { operand &= value; setFlag(HC); clearFlag(CF | NF); adjustSZPXY(operand); } void Z80::xorr(uint8_t& operand, uint8_t value) { operand ^= value; clearFlag(HC | CF | NF); adjustSZPXY(operand); } void Z80::orr(uint8_t& operand, uint8_t value) { operand |= value; clearFlag(HC | CF | NF); adjustSZPXY(operand); } void Z80::compare(uint8_t value) { auto check = A(); sub(check, value); adjustXY(value); } #pragma endregion ALU #pragma region Shift and rotate void Z80::rlc(uint8_t& operand) { auto carry = operand & Bit7; operand <<= 1; setFlag(CF, carry); carry ? operand |= Bit0 : operand &= ~Bit0; clearFlag(NF | HC); adjustXY(operand); } void Z80::rrc(uint8_t& operand) { auto carry = operand & Bit0; operand >>= 1; carry ? operand |= Bit7 : operand &= ~Bit7; setFlag(CF, carry); clearFlag(NF | HC); adjustXY(operand); } void Z80::rl(uint8_t& operand) { auto oldCarry = F() & CF; auto newCarry = operand & Bit7; operand <<= 1; oldCarry ? operand |= Bit0 : operand &= ~Bit0; setFlag(CF, newCarry); clearFlag(NF | HC); adjustXY(operand); } void Z80::rr(uint8_t& operand) { auto oldCarry = F() & CF; auto newCarry = operand & Bit0; operand >>= 1; operand |= oldCarry << 7; setFlag(CF, newCarry); clearFlag(NF | HC); adjustXY(operand); } // void Z80::sla(uint8_t& operand) { auto newCarry = operand & Bit7; operand <<= 1; setFlag(CF, newCarry); clearFlag(NF | HC); adjustXY(operand); } void Z80::sra(uint8_t& operand) { auto new7 = operand & Bit7; auto newCarry = operand & Bit0; operand >>= 1; operand |= new7; setFlag(CF, newCarry); clearFlag(NF | HC); adjustXY(operand); } void Z80::sll(uint8_t& operand) { auto newCarry = operand & Bit7; operand <<= 1; operand |= 1; setFlag(CF, newCarry); clearFlag(NF | HC); adjustXY(operand); } void Z80::srl(uint8_t& operand) { auto newCarry = operand & Bit0; operand >>= 1; operand &= ~Bit7; // clear bit 7 setFlag(CF, newCarry); clearFlag(NF | HC); adjustXY(operand); setFlag(ZF, operand); } // void Z80::rlca() { rlc(A()); } void Z80::rrca() { rrc(A()); } void Z80::rla() { rl(A()); } void Z80::rra() { rr(A()); } #pragma endregion Shift and rotate #pragma region BIT/SET/RES void Z80::bit(int n, uint8_t& operand) { auto carry = F() & CF; uint8_t discarded = operand; andr(discarded, 1 << n); clearFlag(PF, discarded); setFlag(CF, carry); } void Z80::res(int n, uint8_t& operand) { auto bit = 1 << n; operand &= ~bit; } void Z80::set(int n, uint8_t& operand) { auto bit = 1 << n; operand |= bit; } #pragma endregion BIT/SET/RES #pragma region Miscellaneous instructions void Z80::neg() { auto original = A(); A() = 0; sub(A(), original); setFlag(PF, original == 0x80); setFlag(CF, original); } void Z80::daa() { uint8_t a = A(); auto lowAdjust = (F() & HC) | ((A() & 0xf) > 9); auto highAdjust = (F() & CF) | (A() > 0x99); if (F() & NF) { if (lowAdjust) a -= 6; if (highAdjust) a -= 0x60; } else { if (lowAdjust) a += 6; if (highAdjust) a += 0x60; } F() = (F() & (CF | NF)) | (A() > 0x99) | ((A() ^ a) & HC); adjustSZPXY(a); A() = a; } void Z80::cpl() { A() = ~A(); adjustXY(A()); setFlag(HC | NF); } void Z80::scf() { setFlag(CF); adjustXY(A()); clearFlag(HC | NF); } void Z80::ccf() { auto carry = F() & CF; setFlag(HC, carry); clearFlag(CF, carry); clearFlag(NF); adjustXY(A()); } void Z80::xhtl(register16_t& operand) { m_memory.ADDRESS() = sp; MEMPTR().low = m_memory.reference(); m_memory.reference() = operand.low; operand.low = MEMPTR().low; m_memory.ADDRESS().word++; MEMPTR().high = m_memory.reference(); m_memory.reference() = operand.high; operand.high = MEMPTR().high; } void Z80::xhtl() { if (m_prefixDD) xhtl(IX()); else if (m_prefixFD) xhtl(IY()); else xhtl(HL()); } #pragma endregion Miscellaneous instructions #pragma region Block instructions #pragma region Block compare instructions void Z80::blockCompare() { m_memory.ADDRESS() = HL(); auto value = m_memory.reference(); uint8_t result = A() - value; setFlag(PF, --BC().word); adjustSZ(result); adjustHalfCarrySub(A(), value, result); setFlag(NF); if (F() & HC) result -= 1; setFlag(YF, result & Bit1); setFlag(XF, result & Bit3); } void Z80::cpi() { blockCompare(); HL().word++; MEMPTR().word++; } void Z80::cpd() { blockCompare(); HL().word--; MEMPTR().word--; } void Z80::cpir() { cpi(); if ((F() & PF) && !(F() & ZF)) { // See CPI cycles += 5; pc.word -= 2; MEMPTR().word = pc.word + 1; } else { MEMPTR().word = pc.word; } } void Z80::cpdr() { cpd(); if ((F() & PF) && !(F() & ZF)) { // See CPD cycles += 5; pc.word -= 2; MEMPTR().word = pc.word + 1; } else { MEMPTR().word = pc.word - 2; } } #pragma endregion Block compare instructions #pragma region Block load instructions void Z80::blockLoad(register16_t source, register16_t destination) { m_memory.ADDRESS() = source; auto value = m_memory.reference(); m_memory.ADDRESS() = destination; m_memory.reference() = value; auto xy = A() + value; setFlag(XF, xy & 8); setFlag(YF, xy & 2); clearFlag(NF | HC); setFlag(PF, --BC().word); } void Z80::ldd() { blockLoad(HL(), DE()); HL().word--; DE().word--; } void Z80::ldi() { blockLoad(HL(), DE()); HL().word++; DE().word++; } void Z80::ldir() { ldi(); if (F() & PF) { // See LDI cycles += 5; pc.word -= 2; MEMPTR().word = pc.word + 1; } } void Z80::lddr() { ldd(); if (F() & PF) { // See LDR cycles += 5; pc.word -= 2; MEMPTR().word = pc.word + 1; } } #pragma endregion Block load instructions #pragma region Block input instructions void Z80::ini() { auto bc = BC().word; m_memory.ADDRESS().word = bc; readPort(); auto value = m_memory.DATA(); m_memory.ADDRESS().word = HL().word++; m_memory.reference() = value; postDecrement(--B()); setFlag(NF); MEMPTR().word = ++bc; } void Z80::ind() { auto bc = BC().word; m_memory.ADDRESS().word = bc; readPort(); auto value = m_memory.DATA(); m_memory.ADDRESS().word = HL().word--; m_memory.reference() = value; postDecrement(--B()); setFlag(NF); MEMPTR().word = --bc; } void Z80::inir() { ini(); if (!(F() & ZF)) { // See INI cycles += 5; pc.word -= 2; } } void Z80::indr() { ind(); if (!(F() & ZF)) { // See IND cycles += 5; pc.word -= 2; } } #pragma endregion Block input instructions #pragma region Block output instructions void Z80::blockOut() { auto value = m_memory.reference(); m_memory.ADDRESS().word = BC().word; writePort(); postDecrement(--B()); setFlag(NF, value & Bit7); setFlag(HC | CF, (L() + value) > 0xff); adjustParity(((value + L()) & 7) ^ B()); } void Z80::outi() { m_memory.ADDRESS().word = HL().word++; blockOut(); MEMPTR().word = BC().word + 1; } void Z80::outd() { m_memory.ADDRESS().word = HL().word--; blockOut(); MEMPTR().word = BC().word - 1; } void Z80::otir() { outi(); if (!(F() & ZF)) { // See OUTI cycles += 5; pc.word -= 2; } } void Z80::otdr() { outd(); if (!(F() & ZF)) { // See OUTD cycles += 5; pc.word -= 2; } } #pragma endregion Block output instructions #pragma endregion Block instructions #pragma region Nibble rotation void Z80::rrd() { auto accumulator = A(); m_memory.ADDRESS() = HL(); auto memory = m_memory.reference(); A() = (accumulator & 0xf0) | lowNibble(memory); uint8_t updated = promoteNibble(lowNibble(accumulator)) | highNibble(memory); m_memory.reference() = updated; adjustSZPXY(A()); clearFlag(NF | HC); MEMPTR().word = HL().word + 1; } void Z80::rld() { auto accumulator = A(); m_memory.ADDRESS() = HL(); auto memory = m_memory.reference(); uint8_t updated = lowNibble(accumulator) | promoteNibble(memory); A() = (accumulator & 0xf0) | highNibble(memory); m_memory.reference() = updated; adjustSZPXY(A()); clearFlag(NF | HC); MEMPTR().word = HL().word + 1; } #pragma endregion Nibble rotation int Z80::step() { ExecutingInstruction.fire(*this); m_prefixCB = m_prefixDD = m_prefixED = m_prefixFD = false; cycles = 0; return fetchExecute(); } int Z80::execute(uint8_t opcode) { if (!getM1()) throw std::logic_error("M1 cannot be high"); auto x = (opcode & 0b11000000) >> 6; auto y = (opcode & 0b111000) >> 3; auto z = (opcode & 0b111); auto p = (y & 0b110) >> 1; auto q = (y & 1); if (!(m_prefixCB && (m_prefixDD || m_prefixFD))) { incrementRefresh(); M1() = false; } if (m_prefixCB) executeCB(x, y, z, p, q); else if (m_prefixED) executeED(x, y, z, p, q); else executeOther(x, y, z, p, q); if (cycles == 0) throw std::logic_error("Unhandled opcode"); return cycles; } void Z80::executeCB(int x, int y, int z, int p, int q) { switch (x) { case 0: // rot[y] r[z] switch (y) { case 0: if (m_prefixDD || m_prefixFD) { rlc(DISPLACED()); R2(z) = DISPLACED(); } else { rlc(R(z)); } break; case 1: if (m_prefixDD || m_prefixFD) { rrc(DISPLACED()); R2(z) = DISPLACED(); } else { rrc(R(z)); } break; case 2: if (m_prefixDD || m_prefixFD) { rl(DISPLACED()); R2(z) = DISPLACED(); } else { rl(R(z)); } break; case 3: if (m_prefixDD || m_prefixFD) { rr(DISPLACED()); R2(z) = DISPLACED(); } else { rr(R(z)); } break; case 4: if (m_prefixDD || m_prefixFD) { sla(DISPLACED()); R2(z) = DISPLACED(); } else { sla(R(z)); } break; case 5: if (m_prefixDD || m_prefixFD) { sra(DISPLACED()); R2(z) = DISPLACED(); } else { sra(R(z)); } break; case 6: if (m_prefixDD || m_prefixFD) { sll(DISPLACED()); R2(z) = DISPLACED(); } else { sll(R(z)); } break; case 7: if (m_prefixDD || m_prefixFD) { srl(DISPLACED()); R2(z) = DISPLACED(); } else { srl(R(z)); } break; } if (m_prefixDD || m_prefixFD) adjustSZP(DISPLACED()); else adjustSZP(R(z)); if (m_prefixDD || m_prefixFD) { cycles += 23; } else { cycles += 8; if (z == 6) cycles += 7; } break; case 1: // BIT y, r[z] if (m_prefixDD || m_prefixFD) { bit(y, DISPLACED()); adjustXY(MEMPTR().high); cycles += 20; } else { auto operand = R(z); bit(y, operand); cycles += 8; if (z == 6) { adjustXY(MEMPTR().high); cycles += 4; } else { adjustXY(operand); } } break; case 2: // RES y, r[z] if (m_prefixDD || m_prefixFD) { res(y, DISPLACED()); R2(z) = DISPLACED(); cycles += 23; } else { res(y, R(z)); cycles += 8; if (z == 6) cycles += 7; } break; case 3: // SET y, r[z] if (m_prefixDD || m_prefixFD) { set(y, DISPLACED()); R2(z) = DISPLACED(); cycles += 23; } else { set(y, R(z)); cycles += 8; if (z == 6) cycles += 7; } break; } } void Z80::executeED(int x, int y, int z, int p, int q) { switch (x) { case 0: case 3: // Invalid instruction, equivalent to NONI followed by NOP cycles += 8; break; case 1: switch (z) { case 0: // Input from port with 16-bit address MEMPTR() = m_memory.ADDRESS() = BC(); readPort(); if (y != 6) // IN r[y],(C) R(y) = m_memory.DATA(); adjustSZPXY(m_memory.DATA()); clearFlag(HC | NF); MEMPTR().word++; cycles += 12; break; case 1: // Output to port with 16-bit address MEMPTR() = m_memory.ADDRESS() = BC(); if (y == 6) // OUT (C),0 m_memory.placeDATA(0); else // OUT (C),r[y] m_memory.placeDATA(R(y)); writePort(); MEMPTR().word++; cycles += 12; break; case 2: // 16-bit add/subtract with carry switch (q) { case 0: // SBC HL, rp[p] sbcViaMemptr(ALT_HL(), RP(p)); break; case 1: // ADC HL, rp[p] adcViaMemptr(ALT_HL(), RP(p)); break; } cycles += 15; break; case 3: // Retrieve/store register pair from/to immediate address switch (q) { case 0: // LD (nn), rp[p] setWordViaMemptr(fetchWord(), RP(p)); break; case 1: // LD rp[p], (nn) RP(p) = getWordViaMemptr(fetchWord()); break; } cycles += 20; break; case 4: // Negate accumulator neg(); cycles += 8; break; case 5: // Return from interrupt switch (y) { case 1: reti(); // RETI break; default: retn(); // RETN break; } cycles += 14; break; case 6: // Set interrupt mode switch (y) { case 0: case 4: IM() = 0; break; case 2: case 6: IM() = 1; break; case 3: case 7: IM() = 2; break; case 1: case 5: IM() = 0; } cycles += 8; break; case 7: // Assorted ops switch (y) { case 0: // LD I,A IV() = A(); cycles += 9; break; case 1: // LD R,A REFRESH() = A(); cycles += 9; break; case 2: // LD A,I A() = IV(); adjustSZXY(A()); setFlag(PF, IFF2()); clearFlag(NF | HC); cycles += 9; break; case 3: // LD A,R A() = REFRESH(); adjustSZXY(A()); clearFlag(HC | NF); setFlag(PF, IFF2()); cycles += 9; break; case 4: // RRD rrd(); cycles += 18; break; case 5: // RLD rld(); cycles += 18; break; case 6: // NOP case 7: // NOP cycles += 4; break; } break; } break; case 2: switch (z) { case 0: // LD switch (y) { case 4: // LDI ldi(); break; case 5: // LDD ldd(); break; case 6: // LDIR ldir(); break; case 7: // LDDR lddr(); break; } break; case 1: // CP switch (y) { case 4: // CPI cpi(); break; case 5: // CPD cpd(); break; case 6: // CPIR cpir(); break; case 7: // CPDR cpdr(); break; } break; case 2: // IN switch (y) { case 4: // INI ini(); break; case 5: // IND ind(); break; case 6: // INIR inir(); break; case 7: // INDR indr(); break; } break; case 3: // OUT switch (y) { case 4: // OUTI outi(); break; case 5: // OUTD outd(); break; case 6: // OTIR otir(); break; case 7: // OTDR otdr(); break; } break; } cycles += 16; break; } } void Z80::executeOther(int x, int y, int z, int p, int q) { switch (x) { case 0: switch (z) { case 0: // Relative jumps and assorted ops switch (y) { case 0: // NOP cycles += 4; break; case 1: // EX AF AF' exxAF(); cycles += 4; break; case 2: // DJNZ d jrConditional(--B()); cycles += 8; break; case 3: // JR d jrConditional(true); cycles += 7; break; default: // JR cc,d jrConditionalFlag(y - 4); cycles += 5; break; } break; case 1: // 16-bit load immediate/add switch (q) { case 0: // LD rp,nn RP(p) = fetchWord(); cycles += 10; break; case 1: // ADD HL,rp addViaMemptr(ALT_HL(), RP(p)); cycles += 11; break; } break; case 2: // Indirect loading switch (q) { case 0: switch (p) { case 0: // LD (BC),A setViaMemptr(BC(), A()); cycles += 7; break; case 1: // LD (DE),A setViaMemptr(DE(), A()); cycles += 7; break; case 2: // LD (nn),HL setWordViaMemptr(fetchWord(), ALT_HL()); cycles += 16; break; case 3: // LD (nn),A setViaMemptr(fetchWord(), A()); cycles += 13; break; } break; case 1: switch (p) { case 0: // LD A,(BC) A() = getViaMemptr(BC()); cycles += 7; break; case 1: // LD A,(DE) A() = getViaMemptr(DE()); cycles += 7; break; case 2: // LD HL,(nn) ALT_HL() = getWordViaMemptr(fetchWord()); cycles += 16; break; case 3: // LD A,(nn) A() = getViaMemptr(fetchWord()); cycles += 13; break; } break; } break; case 3: // 16-bit INC/DEC switch (q) { case 0: // INC rp ++RP(p).word; break; case 1: // DEC rp --RP(p).word; break; } cycles += 6; break; case 4: // 8-bit INC postIncrement(++R(y)); // INC r cycles += 4; break; case 5: // 8-bit DEC postDecrement(--R(y)); // DEC r cycles += 4; if (y == 6) cycles += 7; break; case 6: { // 8-bit load immediate R(y) = fetchByteData(); // LD r,n cycles += 7; if (y == 6) cycles += 3; break; } case 7: // Assorted operations on accumulator/flags switch (y) { case 0: rlca(); break; case 1: rrca(); break; case 2: rla(); break; case 3: rra(); break; case 4: daa(); break; case 5: cpl(); break; case 6: scf(); break; case 7: ccf(); break; } cycles += 4; break; } break; case 1: // 8-bit loading if (z == 6 && y == 6) { // Exception (replaces LD (HL), (HL)) halt(); } else { bool normal = true; if (m_prefixDD || m_prefixFD) { if (z == 6) { switch (y) { case 4: H() = R(z); normal = false; break; case 5: L() = R(z); normal = false; break; } } if (y == 6) { switch (z) { case 4: R(y) = H(); normal = false; break; case 5: R(y) = L(); normal = false; break; } } } if (normal) R(y) = R(z); if ((y == 6) || (z == 6)) // M operations cycles += 3; } cycles += 4; break; case 2: // Operate on accumulator and register/memory location switch (y) { case 0: // ADD A,r add(A(), R(z)); break; case 1: // ADC A,r adc(A(), R(z)); break; case 2: // SUB r sub(A(), R(z)); break; case 3: // SBC A,r sbc(A(), R(z)); break; case 4: // AND r andr(A(), R(z)); break; case 5: // XOR r xorr(A(), R(z)); break; case 6: // OR r orr(A(), R(z)); break; case 7: // CP r compare(R(z)); break; } cycles += 4; if (z == 6) cycles += 3; break; case 3: switch (z) { case 0: // Conditional return returnConditionalFlag(y); cycles += 5; break; case 1: // POP & various ops switch (q) { case 0: // POP rp2[p] RP2(p) = popWord(); cycles += 10; break; case 1: switch (p) { case 0: // RET ret(); cycles += 10; break; case 1: // EXX exx(); cycles += 4; break; case 2: // JP HL pc = ALT_HL(); cycles += 4; break; case 3: // LD SP,HL sp = ALT_HL(); cycles += 4; break; } } break; case 2: // Conditional jump jumpConditionalFlag(y); cycles += 10; break; case 3: // Assorted operations switch (y) { case 0: // JP nn setPcViaMemptr(fetchWord()); cycles += 10; break; case 1: // CB prefix m_prefixCB = true; if (m_prefixDD || m_prefixFD) m_displacement = fetchByteData(); fetchExecute(); break; case 2: // OUT (n),A m_memory.ADDRESS().low = fetchByteData(); m_memory.ADDRESS().high = A(); MEMPTR() = m_memory.ADDRESS(); m_memory.placeDATA(A()); writePort(); MEMPTR().low++; cycles += 11; break; case 3: // IN A,(n) m_memory.ADDRESS().low = fetchByteData(); m_memory.ADDRESS().high = A(); MEMPTR() = m_memory.ADDRESS(); readPort(); A() = m_memory.DATA(); MEMPTR().low++; cycles += 11; break; case 4: // EX (SP),HL xhtl(); cycles += 19; break; case 5: // EX DE,HL std::swap(DE(), HL()); cycles += 4; break; case 6: // DI disableInterrupts(); cycles += 4; break; case 7: // EI enableInterrupts(); cycles += 4; break; } break; case 4: // Conditional call: CALL cc[y], nn callConditionalFlag(fetchWord(), y); cycles += 10; break; case 5: // PUSH & various ops switch (q) { case 0: // PUSH rp2[p] pushWord(RP2(p)); cycles += 11; break; case 1: switch (p) { case 0: // CALL nn callConditional(fetchWord(), true); cycles += 17; break; case 1: // DD prefix m_prefixDD = true; fetchExecute(); break; case 2: // ED prefix m_prefixED = true; fetchExecute(); break; case 3: // FD prefix m_prefixFD = true; fetchExecute(); break; } } break; case 6: // Operate on accumulator and immediate operand: alu[y] n switch (y) { case 0: // ADD A,n add(A(), fetchByteData()); break; case 1: // ADC A,n adc(A(), fetchByteData()); break; case 2: // SUB n sub(A(), fetchByteData()); break; case 3: // SBC A,n sbc(A(), fetchByteData()); break; case 4: // AND n andr(A(), fetchByteData()); break; case 5: // XOR n xorr(A(), fetchByteData()); break; case 6: // OR n orr(A(), fetchByteData()); break; case 7: // CP n compare(fetchByteData()); break; } cycles += 7; break; case 7: // Restart: RST y * 8 restart(y << 3); cycles += 11; break; } break; } }