/* * Z80 CPU emulation engine v0.0.3b * coded by Ketmar // Vampire Avalon * * This program is free software. It comes without any warranty, to * the extent permitted by applicable law. You can redistribute it * and/or modify it under the terms of the Do What The Fuck You Want * To Public License, Version 2, as published by Sam Hocevar. See * http://sam.zoy.org/wtfpl/COPYING for more details. */ #include #include "zymosis.h" /******************************************************************************/ /* some funny tables */ static int tablesInitialized = 0; static uint8_t parityTable[256]; static uint8_t sz53Table[256]; /* bits 3, 5 and 7 of result, Z flag */ static uint8_t sz53pTable[256]; /* bits 3, 5 and 7 of result, Z and P flags */ /******************************************************************************/ void Z80_InitTables (void) { if (!tablesInitialized) { int f; /***/ for (f = 0; f <= 255; ++f) { int n, p; /***/ sz53Table[f] = (f & Z80_FLAG_S35); for (n = f, p = 0; n != 0; n >>= 1) p ^= n & 0x01; parityTable[f] = (p ? 0 : Z80_FLAG_PV); sz53pTable[f] = (sz53Table[f] | parityTable[f]); } sz53Table[0] |= Z80_FLAG_Z; sz53pTable[0] |= Z80_FLAG_Z; /***/ tablesInitialized = 1; } } void Z80_ResetCallbacks (Z80Info *z80) { if (!tablesInitialized) Z80_InitTables(); z80->memReadFn = NULL; z80->memWriteFn = NULL; z80->contentionFn = NULL; z80->portInFn = NULL; z80->portOutFn = NULL; z80->portContentionFn = NULL; z80->retiFn = NULL; z80->retnFn = NULL; z80->trapEDFn = NULL; z80->pagerFn = NULL; z80->checkBPFn = NULL; } /* seems that all regs (and memptr) should be set to 'all 1' here, but i don't care */ void Z80_Reset (Z80Info *z80) { if (!tablesInitialized) Z80_InitTables(); z80->bc.w = z80->de.w = z80->hl.w = z80->af.w = z80->sp.w = z80->ix.w = z80->iy.w = 0; z80->bcx.w = z80->dex.w = z80->hlx.w = z80->afx.w = 0; z80->pc = z80->prev_pc = z80->org_pc = 0; z80->memptr.w = 0; z80->regI = z80->regR = 0; z80->iff1 = z80->iff2 = 0; z80->im = 0; z80->halted = 0; z80->prev_was_EIDDR = 0; z80->tstates = 0; z80->dd = &z80->hl; } /******************************************************************************/ #define Z80_EXX(_z80) do { \ uint16_t t = (_z80)->bc.w; (_z80)->bc.w = (_z80)->bcx.w; (_z80)->bcx.w = t; \ t = (_z80)->de.w; (_z80)->de.w = (_z80)->dex.w; (_z80)->dex.w = t; \ t = (_z80)->hl.w; (_z80)->hl.w = (_z80)->hlx.w; (_z80)->hlx.w = t; \ } while (0) #define Z80_EXAFAF(_z80) do { \ uint16_t t = (_z80)->af.w; (_z80)->af.w = (_z80)->afx.w; (_z80)->afx.w = t; \ } while (0) /******************************************************************************/ /* simulate contented memory access */ /* (tstates = tstates+contention+1)*cnt */ /* (Z80Info *z80, uint16_t addr, int tstates, Z80MemIOType mio) */ #define Z80_Contention(_z80,_addr,_tstates,_mio) do { \ if ((_z80)->contentionFn != NULL) (_z80)->contentionFn((_z80), (_addr), (_tstates), (_mio)); else (_z80)->tstates += (_tstates); \ } while (0) #define Z80_ContentionBy1(_z80,_addr,_cnt) do { \ if ((z80)->contentionFn != NULL) { \ int _f; \ for (_f = (_cnt); _f-- > 0; (_z80)->contentionFn((_z80), (_addr), 1, Z80_MREQ_NONE|Z80_MEMIO_OTHER)) ; \ } else { \ (_z80)->tstates += (_cnt); \ } \ } while (0) #define Z80_ContentionIRBy1(_z80,_cnt) Z80_ContentionBy1((_z80), (((uint16_t)(_z80)->regI)<<8)|((_z80)->regR), (_cnt)) #define Z80_ContentionPCBy1(_z80,_cnt) Z80_ContentionBy1((_z80), (_z80)->pc, (_cnt)) /******************************************************************************/ static ZYMOSIS_INLINE uint8_t Z80_PortIn (Z80Info *z80, uint16_t port) { uint8_t value; /***/ if (z80->portContentionFn != NULL) { z80->portContentionFn(z80, port, 1, Z80_PIOFLAG_IN | Z80_PIOFLAG_EARLY); z80->portContentionFn(z80, port, 2, Z80_PIOFLAG_IN); } else { z80->tstates += 3; } value = z80->portInFn(z80, port, Z80_PIO_NORMAL); ++z80->tstates; return value; } static ZYMOSIS_INLINE void Z80_PortOut (Z80Info *z80, uint16_t port, uint8_t value) { if (z80->portContentionFn != NULL) { z80->portContentionFn(z80, port, 1, Z80_PIOFLAG_EARLY); } else { ++z80->tstates; } z80->portOutFn(z80, port, value, Z80_PIO_NORMAL); if (z80->portContentionFn != NULL) { z80->portContentionFn(z80, port, 2, 0); ++z80->tstates; } else { z80->tstates += 3; } } /******************************************************************************/ #define Z80_PeekBI(_z80,_addr) (_z80)->memReadFn((_z80), (_addr), Z80_MEMIO_OTHER) #define Z80_PeekB(_z80,_addr) (_z80)->memReadFn((_z80), (_addr), Z80_MEMIO_DATA) /*#define Z80_PeekWI(_z80,_addr) (((uint16_t)Z80_PeekBI((_z80), (_addr)))|(((uint16_t)Z80_PeekBI((_z80), ((_addr)+1)&0xffff))<<8)) */ #define Z80_PokeBI(_z80,_addr,_byte) (_z80)->memWriteFn((_z80), (_addr), (_byte), Z80_MEMIO_OTHER) #define Z80_PokeB(_z80,_addr,_byte) (_z80)->memWriteFn((_z80), (_addr), (_byte), Z80_MEMIO_DATA) /* t1: setting /MREQ & /RD */ /* t2: memory read */ /*#define Z80_PeekB3T(_z80,_addr) (Z80_Contention(_z80, (_addr), 3, Z80_MREQ_READ|Z80_MEMIO_DATA), Z80_PeekB(_z80, (_addr))) */ static ZYMOSIS_INLINE uint8_t Z80_PeekB3T (Z80Info *z80, uint16_t addr) { Z80_Contention(z80, addr, 3, Z80_MREQ_READ | Z80_MEMIO_DATA); return Z80_PeekB(z80, addr); } static ZYMOSIS_INLINE uint8_t Z80_PeekB3TA (Z80Info *z80, uint16_t addr) { Z80_Contention(z80, addr, 3, Z80_MREQ_READ | Z80_MEMIO_OPCARG); return Z80_PeekB(z80, addr); } /* t1: setting /MREQ & /WR */ /* t2: memory write */ #define Z80_PokeB3T(_z80,_addr,_byte) do { \ Z80_Contention((_z80), (_addr), 3, Z80_MREQ_WRITE|Z80_MEMIO_DATA); \ Z80_PokeB((_z80), (_addr), (_byte)); \ } while (0) static ZYMOSIS_INLINE uint16_t Z80_PeekW6T (Z80Info *z80, uint16_t addr) { uint16_t res = Z80_PeekB3T(z80, addr); return res | (((uint16_t)Z80_PeekB3T(z80, (addr + 1) & 0xffff)) << 8); } static ZYMOSIS_INLINE void Z80_PokeW6T (Z80Info *z80, uint16_t addr, uint16_t value) { Z80_PokeB3T(z80, addr, value & 0xff); Z80_PokeB3T(z80, (addr + 1) & 0xffff, (value >> 8) & 0xff); } static ZYMOSIS_INLINE void Z80_PokeW6TInv (Z80Info *z80, uint16_t addr, uint16_t value) { Z80_PokeB3T(z80, (addr + 1) & 0xffff, (value >> 8) & 0xff); Z80_PokeB3T(z80, addr, value & 0xff); } static ZYMOSIS_INLINE uint16_t Z80_GetWordPC (Z80Info *z80, int wait1) { uint16_t res = Z80_PeekB3TA(z80, z80->pc); /***/ z80->pc = (z80->pc + 1) & 0xffff; res |= ((uint16_t)Z80_PeekB3TA(z80, z80->pc)) << 8; if (wait1) Z80_ContentionPCBy1(z80, wait1); z80->pc = (z80->pc + 1) & 0xffff; return res; } static ZYMOSIS_INLINE uint16_t Z80_Pop6T (Z80Info *z80) { uint16_t res = Z80_PeekB3T(z80, z80->sp.w); /***/ z80->sp.w = (z80->sp.w + 1) & 0xffff; res |= ((uint16_t)Z80_PeekB3T(z80, z80->sp.w)) << 8; z80->sp.w = (z80->sp.w + 1) & 0xffff; return res; } /* 3 T states write high byte of PC to the stack and decrement SP */ /* 3 T states write the low byte of PC and jump to #0066 */ static ZYMOSIS_INLINE void Z80_Push6T (Z80Info *z80, uint16_t value) { z80->sp.w = (((int32_t)z80->sp.w) - 1) & 0xffff; Z80_PokeB3T(z80, z80->sp.w, (value >> 8) & 0xff); z80->sp.w = (((int32_t)z80->sp.w) - 1) & 0xffff; Z80_PokeB3T(z80, z80->sp.w, value & 0xff); } /******************************************************************************/ static ZYMOSIS_INLINE void Z80_ADC_A (Z80Info *z80, uint8_t b) { uint16_t new, o = z80->af.a; /***/ z80->af.a = (new = o + b + (z80->af.f & Z80_FLAG_C)) & 0xff; /* Z80_FLAG_C is 0x01, so it's safe */ z80->af.f = sz53Table[new & 0xff] | (new > 0xff ? Z80_FLAG_C : 0) | ((o ^ (~b)) & (o ^ new) & 0x80 ? Z80_FLAG_PV : 0) | ((o & 0x0f) + (b & 0x0f) + (z80->af.f & Z80_FLAG_C) >= 0x10 ? Z80_FLAG_H : 0); } static ZYMOSIS_INLINE void Z80_SBC_A (Z80Info *z80, uint8_t b) { uint16_t new, o = z80->af.a; /***/ z80->af.a = (new = ((int32_t)o - (int32_t)b - (int32_t)(z80->af.f & Z80_FLAG_C)) & 0xffff) & 0xff; /* Z80_FLAG_C is 0x01, so it's safe */ z80->af.f = Z80_FLAG_N | sz53Table[new & 0xff] | (new > 0xff ? Z80_FLAG_C : 0) | ((o ^ b) & (o ^ new) & 0x80 ? Z80_FLAG_PV : 0) | ((int32_t)(o & 0x0f) - (int32_t)(b & 0x0f) - (int32_t)(z80->af.f & Z80_FLAG_C) < 0 ? Z80_FLAG_H : 0); } static ZYMOSIS_INLINE void Z80_ADD_A (Z80Info *z80, uint8_t b) { z80->af.f &= ~Z80_FLAG_C; Z80_ADC_A(z80, b); } static ZYMOSIS_INLINE void Z80_SUB_A (Z80Info *z80, uint8_t b) { z80->af.f &= ~Z80_FLAG_C; Z80_SBC_A(z80, b); } static ZYMOSIS_INLINE void Z80_CP_A (Z80Info *z80, uint8_t b) { uint8_t o = z80->af.a, new = ((int32_t)o - (int32_t)b) & 0xff; /***/ z80->af.f = Z80_FLAG_N | (new & Z80_FLAG_S) | (b & Z80_FLAG_35) | (new == 0 ? Z80_FLAG_Z : 0) | (o < b ? Z80_FLAG_C : 0) | ((o ^ b) & (o ^ new) & 0x80 ? Z80_FLAG_PV : 0) | ((int32_t)(o & 0x0f) - (int32_t)(b & 0x0f) < 0 ? Z80_FLAG_H : 0); } #define Z80_AND_A(_z80,_b) ((_z80)->af.f = sz53pTable[(_z80)->af.a&=(_b)]|Z80_FLAG_H) #define Z80_OR_A(_z80,_b) ((_z80)->af.f = sz53pTable[(_z80)->af.a|=(_b)]) #define Z80_XOR_A(_z80,_b) ((_z80)->af.f = sz53pTable[(_z80)->af.a^=(_b)]) /* carry unchanged */ static ZYMOSIS_INLINE uint8_t Z80_DEC8 (Z80Info *z80, uint8_t b) { z80->af.f &= Z80_FLAG_C; z80->af.f |= Z80_FLAG_N | (b == 0x80 ? Z80_FLAG_PV : 0) | (b & 0x0f ? 0 : Z80_FLAG_H) | sz53Table[(((int)b) - 1) & 0xff]; return (((int)b) - 1) & 0xff; } /* carry unchanged */ static ZYMOSIS_INLINE uint8_t Z80_INC8 (Z80Info *z80, uint8_t b) { z80->af.f &= Z80_FLAG_C; z80->af.f |= (b == 0x7f ? Z80_FLAG_PV : 0) | ((b + 1) & 0x0f ? 0 : Z80_FLAG_H ) | sz53Table[(b + 1) & 0xff]; return ((b + 1) & 0xff); } /* cyclic, carry reflects shifted bit */ static ZYMOSIS_INLINE void Z80_RLCA (Z80Info *z80) { uint8_t c = ((z80->af.a >> 7) & 0x01); /***/ z80->af.a = (z80->af.a << 1) | c; z80->af.f = c | (z80->af.a & Z80_FLAG_35) | (z80->af.f & (Z80_FLAG_PV | Z80_FLAG_Z | Z80_FLAG_S)); } /* cyclic, carry reflects shifted bit */ static ZYMOSIS_INLINE void Z80_RRCA (Z80Info *z80) { uint8_t c = (z80->af.a & 0x01); /***/ z80->af.a = (z80->af.a >> 1) | (c << 7); z80->af.f = c | (z80->af.a & Z80_FLAG_35) | (z80->af.f & (Z80_FLAG_PV | Z80_FLAG_Z | Z80_FLAG_S)); } /* cyclic thru carry */ static ZYMOSIS_INLINE void Z80_RLA (Z80Info *z80) { uint8_t c = ((z80->af.a >> 7) & 0x01); /***/ z80->af.a = (z80->af.a << 1) | (z80->af.f & Z80_FLAG_C); z80->af.f = c | (z80->af.a & Z80_FLAG_35) | (z80->af.f & (Z80_FLAG_PV | Z80_FLAG_Z | Z80_FLAG_S)); } /* cyclic thru carry */ static ZYMOSIS_INLINE void Z80_RRA (Z80Info *z80) { uint8_t c = (z80->af.a & 0x01); /***/ z80->af.a = (z80->af.a >> 1) | ((z80->af.f & Z80_FLAG_C) << 7); z80->af.f = c | (z80->af.a & Z80_FLAG_35) | (z80->af.f & (Z80_FLAG_PV | Z80_FLAG_Z | Z80_FLAG_S)); } /* cyclic thru carry */ static ZYMOSIS_INLINE uint8_t Z80_RL (Z80Info *z80, uint8_t b) { uint8_t c = (b >> 7)&Z80_FLAG_C; /***/ z80->af.f = sz53pTable[(b = ((b << 1) & 0xff) | (z80->af.f & Z80_FLAG_C))] | c; return b; } static ZYMOSIS_INLINE uint8_t Z80_RR (Z80Info *z80, uint8_t b) { uint8_t c = (b & 0x01); /***/ z80->af.f = sz53pTable[(b = (b >> 1) | ((z80->af.f & Z80_FLAG_C) << 7))] | c; return b; } /* cyclic, carry reflects shifted bit */ static ZYMOSIS_INLINE uint8_t Z80_RLC (Z80Info *z80, uint8_t b) { uint8_t c = ((b >> 7)&Z80_FLAG_C); /***/ z80->af.f = sz53pTable[(b = ((b << 1) & 0xff) | c)] | c; return b; } /* cyclic, carry reflects shifted bit */ static ZYMOSIS_INLINE uint8_t Z80_RRC (Z80Info *z80, uint8_t b) { uint8_t c = (b & 0x01); /***/ z80->af.f = sz53pTable[(b = (b >> 1) | (c << 7))] | c; return b; } static ZYMOSIS_INLINE uint8_t Z80_SLA (Z80Info *z80, uint8_t b) { uint8_t c = ((b >> 7) & 0x01); /***/ z80->af.f = sz53pTable[(b <<= 1)] | c; return b; } static ZYMOSIS_INLINE uint8_t Z80_SRA (Z80Info *z80, uint8_t b) { uint8_t c = (b & 0x01); /***/ z80->af.f = sz53pTable[(b = (b >> 1) | (b & 0x80))] | c; return b; } static ZYMOSIS_INLINE uint8_t Z80_SLL (Z80Info *z80, uint8_t b) { uint8_t c = ((b >> 7) & 0x01); /***/ z80->af.f = sz53pTable[(b = (b << 1) | 0x01)] | c; return b; } static ZYMOSIS_INLINE uint8_t Z80_SLR (Z80Info *z80, uint8_t b) { uint8_t c = (b & 0x01); /***/ z80->af.f = sz53pTable[(b >>= 1)] | c; return b; } /* ddvalue+value */ static ZYMOSIS_INLINE uint16_t Z80_ADD_DD (Z80Info *z80, uint16_t value, uint16_t ddvalue) { static const uint8_t hct[8] = { 0, Z80_FLAG_H, Z80_FLAG_H, Z80_FLAG_H, 0, 0, 0, Z80_FLAG_H }; uint32_t res = (uint32_t)value + (uint32_t)ddvalue; uint8_t b = ((value & 0x0800) >> 11) | ((ddvalue & 0x0800) >> 10) | ((res & 0x0800) >> 9); /***/ z80->memptr.w = (ddvalue + 1) & 0xffff; z80->af.f = (z80->af.f & (Z80_FLAG_PV | Z80_FLAG_Z | Z80_FLAG_S)) | (res > 0xffff ? Z80_FLAG_C : 0) | ((res >> 8)&Z80_FLAG_35) | hct[b]; return res; } /* ddvalue+value */ static ZYMOSIS_INLINE uint16_t Z80_ADC_DD (Z80Info *z80, uint16_t value, uint16_t ddvalue) { uint8_t c = (z80->af.f & Z80_FLAG_C); uint32_t new = (uint32_t)value + (uint32_t)ddvalue + (uint32_t)c; uint16_t res = (new & 0xffff); /***/ z80->memptr.w = (ddvalue + 1) & 0xffff; z80->af.f = ((res >> 8)&Z80_FLAG_S35) | (res == 0 ? Z80_FLAG_Z : 0) | (new > 0xffff ? Z80_FLAG_C : 0) | ((value ^ ((~ddvalue) & 0xffff)) & (value ^ new) & 0x8000 ? Z80_FLAG_PV : 0) | ((value & 0x0fff) + (ddvalue & 0x0fff) + c >= 0x1000 ? Z80_FLAG_H : 0); return res; } /* ddvalue-value */ static ZYMOSIS_INLINE uint16_t Z80_SBC_DD (Z80Info *z80, uint16_t value, uint16_t ddvalue) { uint16_t res; uint8_t tmpB = z80->af.a; /***/ z80->memptr.w = (ddvalue + 1) & 0xffff; z80->af.a = ddvalue & 0xff; Z80_SBC_A(z80, value & 0xff); res = z80->af.a; z80->af.a = (ddvalue >> 8) & 0xff; Z80_SBC_A(z80, (value >> 8) & 0xff); res |= (z80->af.a << 8); z80->af.a = tmpB; z80->af.f = (res ? z80->af.f & (~Z80_FLAG_Z) : z80->af.f | Z80_FLAG_Z); return res; } static ZYMOSIS_INLINE void Z80_BIT (Z80Info *z80, uint8_t bit, uint8_t num, int mptr) { z80->af.f = Z80_FLAG_H | (z80->af.f & Z80_FLAG_C) | (num & Z80_FLAG_35) | (num & (1 << bit) ? 0 : Z80_FLAG_PV | Z80_FLAG_Z) | (bit == 7 ? num&Z80_FLAG_S : 0); if (mptr) z80->af.f = (z80->af.f & ~Z80_FLAG_35) | (z80->memptr.h & Z80_FLAG_35); } static ZYMOSIS_INLINE void Z80_DAA (Z80Info *z80) { uint8_t tmpI = 0, tmpC = (z80->af.f & Z80_FLAG_C), tmpA = z80->af.a; /***/ if ((z80->af.f & Z80_FLAG_H) || (tmpA & 0x0f) > 9) tmpI = 6; if (tmpC != 0 || tmpA > 0x99) tmpI |= 0x60; if (tmpA > 0x99) tmpC = Z80_FLAG_C; if (z80->af.f & Z80_FLAG_N) Z80_SUB_A(z80, tmpI); else Z80_ADD_A(z80, tmpI); z80->af.f = (z80->af.f & ~(Z80_FLAG_C | Z80_FLAG_PV)) | tmpC | parityTable[z80->af.a]; } static ZYMOSIS_INLINE void Z80_RRD_A (Z80Info *z80) { uint8_t tmpB = Z80_PeekB3T(z80, z80->hl.w); /*IOP(4)*/ z80->memptr.w = (z80->hl.w + 1) & 0xffff; Z80_ContentionBy1(z80, z80->hl.w, 4); Z80_PokeB3T(z80, z80->hl.w, (z80->af.a << 4) | (tmpB >> 4)); z80->af.a = (z80->af.a & 0xf0) | (tmpB & 0x0f); z80->af.f = (z80->af.f & Z80_FLAG_C) | sz53pTable[z80->af.a]; } static ZYMOSIS_INLINE void Z80_RLD_A (Z80Info *z80) { uint8_t tmpB = Z80_PeekB3T(z80, z80->hl.w); /*IOP(4)*/ z80->memptr.w = (z80->hl.w + 1) & 0xffff; Z80_ContentionBy1(z80, z80->hl.w, 4); Z80_PokeB3T(z80, z80->hl.w, (tmpB << 4) | (z80->af.a & 0x0f)); z80->af.a = (z80->af.a & 0xf0) | (tmpB >> 4); z80->af.f = (z80->af.f & Z80_FLAG_C) | sz53pTable[z80->af.a]; } static ZYMOSIS_INLINE void Z80_LD_A_IR (Z80Info *z80, uint8_t ir) { z80->af.a = ir; z80->prev_was_EIDDR = -1; Z80_ContentionIRBy1(z80, 1); z80->af.f = sz53Table[z80->af.a] | (z80->af.f & Z80_FLAG_C) | (z80->iff2 ? Z80_FLAG_PV : 0); } /******************************************************************************/ #define INC_R (z80->regR = ((z80->regR+1)&0x7f)|(z80->regR&0x80)) #define SET_TRUE_CC \ switch ((opcode>>3)&0x07) { \ case 0: trueCC = (z80->af.f&Z80_FLAG_Z) == 0; break; \ case 1: trueCC = (z80->af.f&Z80_FLAG_Z) != 0; break; \ case 2: trueCC = (z80->af.f&Z80_FLAG_C) == 0; break; \ case 3: trueCC = (z80->af.f&Z80_FLAG_C) != 0; break; \ case 4: trueCC = (z80->af.f&Z80_FLAG_PV) == 0; break; \ case 5: trueCC = (z80->af.f&Z80_FLAG_PV) != 0; break; \ case 6: trueCC = (z80->af.f&Z80_FLAG_S) == 0; break; \ case 7: trueCC = (z80->af.f&Z80_FLAG_S) != 0; break; \ } #define INC_PC (z80->pc = (z80->pc+1)&0xffff) #define DEC_PC (z80->pc = ((int32_t)(z80->pc)-1)&0xffff) #define INC_W(n) ((n) = ((n)+1)&0xffff) #define DEC_W(n) ((n) = ((int32_t)(n)-1)&0xffff) #define XADD_W(n,v) ((n) = ((n)+v)&0xffff) #define XSUB_W(n,v) ((n) = ((int32_t)(n)-v)&0xffff) #define ZADD_W(n,v) ((n) = ((int32_t)(n)+v)&0xffff) #define ZADD_WX(n,v) (((int32_t)(n)+v)&0xffff) #define INC_B(n) ((n) = ((n)+1)&0xff) #define DEC_B(n) ((n) = ((int32_t)(n)-1)&0xff) /* t1: setting /MREQ & /RD */ /* t2: memory read */ /* t3, t4: decode command, increment R */ #define GET_OPCODE(_opc) do { \ Z80_Contention(z80, z80->pc, 4, Z80_MREQ_READ|Z80_MEMIO_OPCODE); \ if (z80->evenM1 && (z80->tstates&0x01)) ++z80->tstates; \ (_opc) = z80->memReadFn(z80, z80->pc, Z80_MEMIO_OPCODE); \ z80->pc = (z80->pc+1)&0xffff; \ z80->regR = ((z80->regR+1)&0x7f)|(z80->regR&0x80); \ } while (0) #define GET_OPCODE_EXT(_opc) do { \ Z80_Contention(z80, z80->pc, 4, Z80_MREQ_READ|Z80_MEMIO_OPCEXT); \ (_opc) = z80->memReadFn(z80, z80->pc, Z80_MEMIO_OPCEXT); \ z80->pc = (z80->pc+1)&0xffff; \ z80->regR = ((z80->regR+1)&0x7f)|(z80->regR&0x80); \ } while (0) #define CBX_REPEATED (opcode&0x10) #define CBX_BACKWARD (opcode&0x08) void Z80_Execute (Z80Info *z80) { uint8_t opcode; int gotDD, trueCC; /* booleans */ int disp; uint8_t tmpB, tmpC, rsrc, rdst; uint16_t tmpW = 0; /* shut up the compiler; it's wrong but stubborn */ /***/ while (z80->tstates < z80->next_event_tstate) { if (z80->pagerFn != NULL) z80->pagerFn(z80); if (z80->checkBPFn != NULL && z80->checkBPFn(z80)) return; z80->prev_pc = z80->org_pc; z80->org_pc = z80->pc; /* read opcode -- OCR(4) */ GET_OPCODE(opcode); z80->prev_was_EIDDR = 0; disp = gotDD = 0; z80->dd = &z80->hl; if (z80->halted) { DEC_W(z80->pc); continue; } /***/ if (opcode == 0xdd || opcode == 0xfd) { static const uint32_t withIndexBmp[8] = {0x00, 0x700000, 0x40404040, 0x40bf4040, 0x40404040, 0x40404040, 0x0800, 0x00}; /* IX/IY prefix */ z80->dd = (opcode == 0xdd ? &z80->ix : &z80->iy); /* read opcode -- OCR(4) */ GET_OPCODE_EXT(opcode); /* test if this instruction have (HL) */ if (withIndexBmp[opcode >> 5] & (1 << (opcode & 0x1f))) { /* 3rd byte is always DISP here */ disp = Z80_PeekB3TA(z80, z80->pc); if (disp > 127) disp -= 256; INC_PC; z80->memptr.w = ZADD_WX(z80->dd->w, disp); } else if (opcode == 0xdd && opcode == 0xfd) { /* double prefix; restart main loop */ z80->prev_was_EIDDR = 1; continue; } gotDD = 1; } /* instructions */ if (opcode == 0xed) { z80->dd = &z80->hl; /* а нас -- рать! */ /* read opcode -- OCR(4) */ GET_OPCODE_EXT(opcode); switch (opcode) { /* LDI, LDIR, LDD, LDDR */ case 0xa0: case 0xb0: case 0xa8: case 0xb8: tmpB = Z80_PeekB3T(z80, z80->hl.w); Z80_PokeB3T(z80, z80->de.w, tmpB); /*MWR(5)*/ Z80_ContentionBy1(z80, z80->de.w, 2); DEC_W(z80->bc.w); tmpB = (tmpB + z80->af.a) & 0xff; /***/ z80->af.f = (tmpB & Z80_FLAG_3) | (z80->af.f & (Z80_FLAG_C | Z80_FLAG_Z | Z80_FLAG_S)) | (z80->bc.w != 0 ? Z80_FLAG_PV : 0) | (tmpB & 0x02 ? Z80_FLAG_5 : 0); /***/ if (CBX_REPEATED) { if (z80->bc.w != 0) { /*IOP(5)*/ Z80_ContentionBy1(z80, z80->de.w, 5); /* do it again */ XSUB_W(z80->pc, 2); z80->memptr.w = (z80->pc + 1) & 0xffff; } } if (!CBX_BACKWARD) { INC_W(z80->hl.w); INC_W(z80->de.w); } else { DEC_W(z80->hl.w); DEC_W(z80->de.w); } break; /* CPI, CPIR, CPD, CPDR */ case 0xa1: case 0xb1: case 0xa9: case 0xb9: /* MEMPTR */ if (CBX_REPEATED && (!(z80->bc.w == 1 || Z80_PeekBI(z80, z80->hl.w) == z80->af.a))) { z80->memptr.w = ZADD_WX(z80->org_pc, 1); } else { z80->memptr.w = ZADD_WX(z80->memptr.w, (CBX_BACKWARD ? -1 : 1)); } /***/ tmpB = Z80_PeekB3T(z80, z80->hl.w); /*IOP(5)*/ Z80_ContentionBy1(z80, z80->hl.w, 5); DEC_W(z80->bc.w); /***/ z80->af.f = Z80_FLAG_N | (z80->af.f & Z80_FLAG_C) | (z80->bc.w != 0 ? Z80_FLAG_PV : 0) | ((int32_t)(z80->af.a & 0x0f) - (int32_t)(tmpB & 0x0f) < 0 ? Z80_FLAG_H : 0); /***/ tmpB = ((int32_t)z80->af.a - (int32_t)tmpB) & 0xff; /***/ z80->af.f |= (tmpB == 0 ? Z80_FLAG_Z : 0) | (tmpB & Z80_FLAG_S); /***/ if (z80->af.f & Z80_FLAG_H) tmpB = ((uint16_t)tmpB - 1) & 0xff; z80->af.f |= (tmpB & Z80_FLAG_3) | (tmpB & 0x02 ? Z80_FLAG_5 : 0); /***/ if (CBX_REPEATED) { /* repeated */ if ((z80->af.f & (Z80_FLAG_Z | Z80_FLAG_PV)) == Z80_FLAG_PV) { /*IOP(5)*/ Z80_ContentionBy1(z80, z80->hl.w, 5); /* do it again */ XSUB_W(z80->pc, 2); } } if (CBX_BACKWARD) DEC_W(z80->hl.w); else INC_W(z80->hl.w); break; /* OUTI, OTIR, OUTD, OTDR */ case 0xa3: case 0xb3: case 0xab: case 0xbb: DEC_B(z80->bc.b); /* fallthru */ /* INI, INIR, IND, INDR */ case 0xa2: case 0xb2: case 0xaa: case 0xba: z80->memptr.w = ZADD_WX(z80->bc.w, (CBX_BACKWARD ? -1 : 1)); /*OCR(5)*/ Z80_ContentionIRBy1(z80, 1); if (opcode & 0x01) { /* OUT* */ tmpB = Z80_PeekB3T(z80, z80->hl.w);/*MRD(3)*/ Z80_PortOut(z80, z80->bc.w, tmpB); tmpW = ZADD_WX(z80->hl.w, (CBX_BACKWARD ? -1 : 1)); tmpC = (tmpB + tmpW) & 0xff; } else { /* IN* */ tmpB = Z80_PortIn(z80, z80->bc.w); Z80_PokeB3T(z80, z80->hl.w, tmpB);/*MWR(3)*/ DEC_B(z80->bc.b); if (CBX_BACKWARD) tmpC = ((int32_t)tmpB + (int32_t)z80->bc.c - 1) & 0xff; else tmpC = (tmpB + z80->bc.c + 1) & 0xff; } /***/ z80->af.f = (tmpB & 0x80 ? Z80_FLAG_N : 0) | (tmpC < tmpB ? Z80_FLAG_H | Z80_FLAG_C : 0) | parityTable[(tmpC & 0x07)^z80->bc.b] | sz53Table[z80->bc.b]; /***/ if (CBX_REPEATED) { /* repeating commands */ if (z80->bc.b != 0) { uint16_t a = (opcode & 0x01 ? z80->bc.w : z80->hl.w); /***/ /*IOP(5)*/ Z80_ContentionBy1(z80, a, 5); /* do it again */ XSUB_W(z80->pc, 2); } } if (CBX_BACKWARD) DEC_W(z80->hl.w); else INC_W(z80->hl.w); break; /* not strings, but some good instructions anyway */ default: if ((opcode & 0xc0) == 0x40) { /* 0x40...0x7f */ switch (opcode & 0x07) { /* IN r8,(C) */ case 0: z80->memptr.w = ZADD_WX(z80->bc.w, 1); tmpB = Z80_PortIn(z80, z80->bc.w); z80->af.f = sz53pTable[tmpB] | (z80->af.f & Z80_FLAG_C); switch ((opcode >> 3) & 0x07) { case 0: z80->bc.b = tmpB; break; case 1: z80->bc.c = tmpB; break; case 2: z80->de.d = tmpB; break; case 3: z80->de.e = tmpB; break; case 4: z80->hl.h = tmpB; break; case 5: z80->hl.l = tmpB; break; case 7: z80->af.a = tmpB; break; /* 6 affects only flags */ } break; /* OUT (C),r8 */ case 1: z80->memptr.w = ZADD_WX(z80->bc.w, 1); switch ((opcode >> 3) & 0x07) { case 0: tmpB = z80->bc.b; break; case 1: tmpB = z80->bc.c; break; case 2: tmpB = z80->de.d; break; case 3: tmpB = z80->de.e; break; case 4: tmpB = z80->hl.h; break; case 5: tmpB = z80->hl.l; break; case 7: tmpB = z80->af.a; break; default: tmpB = 0; break; /*6*/ } Z80_PortOut(z80, z80->bc.w, tmpB); break; /* SBC HL,rr/ADC HL,rr */ case 2: /*IOP(4),IOP(3)*/ Z80_ContentionIRBy1(z80, 7); switch ((opcode >> 4) & 0x03) { case 0: tmpW = z80->bc.w; break; case 1: tmpW = z80->de.w; break; case 2: tmpW = z80->hl.w; break; default: tmpW = z80->sp.w; break; } z80->hl.w = (opcode & 0x08 ? Z80_ADC_DD(z80, tmpW, z80->hl.w) : Z80_SBC_DD(z80, tmpW, z80->hl.w)); break; /* LD (nn),rr/LD rr,(nn) */ case 3: tmpW = Z80_GetWordPC(z80, 0); z80->memptr.w = (tmpW + 1) & 0xffff; if (opcode & 0x08) { /* LD rr,(nn) */ switch ((opcode >> 4) & 0x03) { case 0: z80->bc.w = Z80_PeekW6T(z80, tmpW); break; case 1: z80->de.w = Z80_PeekW6T(z80, tmpW); break; case 2: z80->hl.w = Z80_PeekW6T(z80, tmpW); break; case 3: z80->sp.w = Z80_PeekW6T(z80, tmpW); break; } } else { /* LD (nn),rr */ switch ((opcode >> 4) & 0x03) { case 0: Z80_PokeW6T(z80, tmpW, z80->bc.w); break; case 1: Z80_PokeW6T(z80, tmpW, z80->de.w); break; case 2: Z80_PokeW6T(z80, tmpW, z80->hl.w); break; case 3: Z80_PokeW6T(z80, tmpW, z80->sp.w); break; } } break; /* NEG */ case 4: tmpB = z80->af.a; z80->af.a = 0; Z80_SUB_A(z80, tmpB); break; /* RETI/RETN */ case 5: /*RETI: 0x4d, 0x5d, 0x6d, 0x7d*/ /*RETN: 0x45, 0x55, 0x65, 0x75*/ z80->iff1 = z80->iff2; z80->memptr.w = z80->pc = Z80_Pop6T(z80); if (opcode & 0x08) { /* RETI */ if (z80->retiFn != NULL && z80->retiFn(z80, opcode)) return; } else { /* RETN */ if (z80->retnFn != NULL && z80->retnFn(z80, opcode)) return; } break; /* IM n */ case 6: switch (opcode) { case 0x56: case 0x76: z80->im = 1; break; case 0x5e: case 0x7e: z80->im = 2; break; default: z80->im = 0; break; } break; /* specials */ case 7: switch (opcode) { /* LD I,A */ case 0x47: /*OCR(5)*/ Z80_ContentionIRBy1(z80, 1); z80->regI = z80->af.a; break; /* LD R,A */ case 0x4f: /*OCR(5)*/ Z80_ContentionIRBy1(z80, 1); z80->regR = z80->af.a; break; /* LD A,I */ case 0x57: Z80_LD_A_IR(z80, z80->regI); break; /* LD A,R */ case 0x5f: Z80_LD_A_IR(z80, z80->regR); break; /* RRD */ case 0x67: Z80_RRD_A(z80); break; /* RLD */ case 0x6F: Z80_RLD_A(z80); break; } } } else { /* slt and other traps */ if (z80->trapEDFn != NULL && z80->trapEDFn(z80, opcode)) return; } break; } continue; } /* 0xed done */ /***/ if (opcode == 0xcb) { /* shifts and bit operations */ /* read opcode -- OCR(4) */ if (!gotDD) { GET_OPCODE_EXT(opcode); } else { Z80_Contention(z80, z80->pc, 3, Z80_MREQ_READ | Z80_MEMIO_OPCEXT); opcode = z80->memReadFn(z80, z80->pc, Z80_MEMIO_OPCEXT); Z80_ContentionPCBy1(z80, 2); INC_PC; } if (gotDD) { tmpW = ZADD_WX(z80->dd->w, disp); tmpB = Z80_PeekB3T(z80, tmpW); Z80_ContentionBy1(z80, tmpW, 1); } else { switch (opcode & 0x07) { case 0: tmpB = z80->bc.b; break; case 1: tmpB = z80->bc.c; break; case 2: tmpB = z80->de.d; break; case 3: tmpB = z80->de.e; break; case 4: tmpB = z80->hl.h; break; case 5: tmpB = z80->hl.l; break; case 6: tmpB = Z80_PeekB3T(z80, z80->hl.w); Z80_Contention(z80, z80->hl.w, 1, Z80_MREQ_READ | Z80_MEMIO_DATA); break; case 7: tmpB = z80->af.a; break; } } switch ((opcode >> 3) & 0x1f) { case 0: tmpB = Z80_RLC(z80, tmpB); break; case 1: tmpB = Z80_RRC(z80, tmpB); break; case 2: tmpB = Z80_RL(z80, tmpB); break; case 3: tmpB = Z80_RR(z80, tmpB); break; case 4: tmpB = Z80_SLA(z80, tmpB); break; case 5: tmpB = Z80_SRA(z80, tmpB); break; case 6: tmpB = Z80_SLL(z80, tmpB); break; case 7: tmpB = Z80_SLR(z80, tmpB); break; default: switch ((opcode >> 6) & 0x03) { case 1: Z80_BIT(z80, (opcode >> 3) & 0x07, tmpB, (gotDD || (opcode & 0x07) == 6)); break; case 2: tmpB &= ~(1 << ((opcode >> 3) & 0x07)); break; /* RES */ case 3: tmpB |= (1 << ((opcode >> 3) & 0x07)); break; /* SET */ } break; } /***/ if ((opcode & 0xc0) != 0x40) { /* BITs are not welcome here */ if (gotDD) { /* tmpW was set earlier */ if ((opcode & 0x07) != 6) Z80_PokeB3T(z80, tmpW, tmpB); } switch (opcode & 0x07) { case 0: z80->bc.b = tmpB; break; case 1: z80->bc.c = tmpB; break; case 2: z80->de.d = tmpB; break; case 3: z80->de.e = tmpB; break; case 4: z80->hl.h = tmpB; break; case 5: z80->hl.l = tmpB; break; case 6: Z80_PokeB3T(z80, ZADD_WX(z80->dd->w, disp), tmpB); break; case 7: z80->af.a = tmpB; break; } } continue; } /* 0xcb done */ /* normal things */ switch (opcode & 0xc0) { /* 0x00..0x3F */ case 0x00: switch (opcode & 0x07) { /* misc,DJNZ,JR,JR cc */ case 0: if (opcode & 0x30) { /* branches */ if (opcode & 0x20) { /* JR cc */ switch ((opcode >> 3) & 0x03) { case 0: trueCC = (z80->af.f & Z80_FLAG_Z) == 0; break; case 1: trueCC = (z80->af.f & Z80_FLAG_Z) != 0; break; case 2: trueCC = (z80->af.f & Z80_FLAG_C) == 0; break; case 3: trueCC = (z80->af.f & Z80_FLAG_C) != 0; break; default: trueCC = 0; break; } } else { /* DJNZ/JR */ if ((opcode & 0x08) == 0) { /* DJNZ */ /*OCR(5)*/ Z80_ContentionIRBy1(z80, 1); DEC_B(z80->bc.b); trueCC = (z80->bc.b != 0); } else { /* JR */ trueCC = 1; } } /***/ disp = Z80_PeekB3TA(z80, z80->pc); if (trueCC) { /* execute branch (relative) */ /*IOP(5)*/ if (disp > 127) disp -= 256; Z80_ContentionPCBy1(z80, 5); INC_PC; ZADD_W(z80->pc, disp); z80->memptr.w = z80->pc; } else { INC_PC; } } else { /* EX AF,AF' or NOP */ if (opcode != 0) Z80_EXAFAF(z80); } break; /* LD rr,nn/ADD HL,rr */ case 1: if (opcode & 0x08) { /* ADD HL,rr */ /*IOP(4),IOP(3)*/ Z80_ContentionIRBy1(z80, 7); switch ((opcode >> 4) & 0x03) { case 0: z80->dd->w = Z80_ADD_DD(z80, z80->bc.w, z80->dd->w); break; case 1: z80->dd->w = Z80_ADD_DD(z80, z80->de.w, z80->dd->w); break; case 2: z80->dd->w = Z80_ADD_DD(z80, z80->dd->w, z80->dd->w); break; case 3: z80->dd->w = Z80_ADD_DD(z80, z80->sp.w, z80->dd->w); break; } } else { /* LD rr,nn */ tmpW = Z80_GetWordPC(z80, 0); switch ((opcode >> 4) & 0x03) { case 0: z80->bc.w = tmpW; break; case 1: z80->de.w = tmpW; break; case 2: z80->dd->w = tmpW; break; case 3: z80->sp.w = tmpW; break; } } break; /* LD xxx,xxx */ case 2: switch ((opcode >> 3) & 0x07) { /* LD (BC),A */ case 0: Z80_PokeB3T(z80, z80->bc.w, z80->af.a); z80->memptr.l = (z80->bc.c + 1) & 0xff; z80->memptr.h = z80->af.a; break; /* LD A,(BC) */ case 1: z80->af.a = Z80_PeekB3T(z80, z80->bc.w); z80->memptr.w = (z80->bc.w + 1) & 0xffff; break; /* LD (DE),A */ case 2: Z80_PokeB3T(z80, z80->de.w, z80->af.a); z80->memptr.l = (z80->de.e+1) & 0xff; z80->memptr.h = z80->af.a; break; /* LD A,(DE) */ case 3: z80->af.a = Z80_PeekB3T(z80, z80->de.w); z80->memptr.w = (z80->de.w + 1) & 0xffff; break; /* LD (nn),HL */ case 4: tmpW = Z80_GetWordPC(z80, 0); z80->memptr.w = (tmpW + 1) & 0xffff; Z80_PokeW6T(z80, tmpW, z80->dd->w); break; /* LD HL,(nn) */ case 5: tmpW = Z80_GetWordPC(z80, 0); z80->memptr.w = (tmpW + 1) & 0xffff; z80->dd->w = Z80_PeekW6T(z80, tmpW); break; /* LD (nn),A */ case 6: tmpW = Z80_GetWordPC(z80, 0); z80->memptr.l = (tmpW + 1) & 0xff; z80->memptr.h = z80->af.a; Z80_PokeB3T(z80, tmpW, z80->af.a); break; /* LD A,(nn) */ case 7: tmpW = Z80_GetWordPC(z80, 0); z80->memptr.w = (tmpW + 1) & 0xffff; z80->af.a = Z80_PeekB3T(z80, tmpW); break; } break; /* INC rr/DEC rr */ case 3: /*OCR(6)*/ Z80_ContentionIRBy1(z80, 2); if (opcode & 0x08) { /*DEC*/ switch ((opcode >> 4) & 0x03) { case 0: DEC_W(z80->bc.w); break; case 1: DEC_W(z80->de.w); break; case 2: DEC_W(z80->dd->w); break; case 3: DEC_W(z80->sp.w); break; } } else { /*INC*/ switch ((opcode >> 4) & 0x03) { case 0: INC_W(z80->bc.w); break; case 1: INC_W(z80->de.w); break; case 2: INC_W(z80->dd->w); break; case 3: INC_W(z80->sp.w); break; } } break; /* INC r8 */ case 4: switch ((opcode >> 3) & 0x07) { case 0: z80->bc.b = Z80_INC8(z80, z80->bc.b); break; case 1: z80->bc.c = Z80_INC8(z80, z80->bc.c); break; case 2: z80->de.d = Z80_INC8(z80, z80->de.d); break; case 3: z80->de.e = Z80_INC8(z80, z80->de.e); break; case 4: z80->dd->h = Z80_INC8(z80, z80->dd->h); break; case 5: z80->dd->l = Z80_INC8(z80, z80->dd->l); break; case 6: if (gotDD) { DEC_PC; Z80_ContentionPCBy1(z80, 5); INC_PC; } tmpW = ZADD_WX(z80->dd->w, disp); tmpB = Z80_PeekB3T(z80, tmpW); Z80_ContentionBy1(z80, tmpW, 1); tmpB = Z80_INC8(z80, tmpB); Z80_PokeB3T(z80, tmpW, tmpB); break; case 7: z80->af.a = Z80_INC8(z80, z80->af.a); break; } break; /* DEC r8 */ case 5: switch ((opcode >> 3) & 0x07) { case 0: z80->bc.b = Z80_DEC8(z80, z80->bc.b); break; case 1: z80->bc.c = Z80_DEC8(z80, z80->bc.c); break; case 2: z80->de.d = Z80_DEC8(z80, z80->de.d); break; case 3: z80->de.e = Z80_DEC8(z80, z80->de.e); break; case 4: z80->dd->h = Z80_DEC8(z80, z80->dd->h); break; case 5: z80->dd->l = Z80_DEC8(z80, z80->dd->l); break; case 6: if (gotDD) { DEC_PC; Z80_ContentionPCBy1(z80, 5); INC_PC; } tmpW = ZADD_WX(z80->dd->w, disp); tmpB = Z80_PeekB3T(z80, tmpW); Z80_ContentionBy1(z80, tmpW, 1); tmpB = Z80_DEC8(z80, tmpB); Z80_PokeB3T(z80, tmpW, tmpB); break; case 7: z80->af.a = Z80_DEC8(z80, z80->af.a); break; } break; /* LD r8,n */ case 6: tmpB = Z80_PeekB3TA(z80, z80->pc); INC_PC; switch ((opcode >> 3) & 0x07) { case 0: z80->bc.b = tmpB; break; case 1: z80->bc.c = tmpB; break; case 2: z80->de.d = tmpB; break; case 3: z80->de.e = tmpB; break; case 4: z80->dd->h = tmpB; break; case 5: z80->dd->l = tmpB; break; case 6: if (gotDD) { DEC_PC; Z80_ContentionPCBy1(z80, 2); INC_PC; } tmpW = ZADD_WX(z80->dd->w, disp); Z80_PokeB3T(z80, tmpW, tmpB); break; case 7: z80->af.a = tmpB; break; } break; /* swim-swim-hungry */ case 7: switch ((opcode >> 3) & 0x07) { case 0: Z80_RLCA(z80); break; case 1: Z80_RRCA(z80); break; case 2: Z80_RLA(z80); break; case 3: Z80_RRA(z80); break; case 4: Z80_DAA(z80); break; case 5: /* CPL */ z80->af.a ^= 0xff; z80->af.f = (z80->af.a & Z80_FLAG_35) | (Z80_FLAG_N | Z80_FLAG_H) | (z80->af.f & (Z80_FLAG_C | Z80_FLAG_PV | Z80_FLAG_Z | Z80_FLAG_S)); break; case 6: /* SCF */ z80->af.f = (z80->af.f & (Z80_FLAG_PV | Z80_FLAG_Z | Z80_FLAG_S)) | (z80->af.a & Z80_FLAG_35) | Z80_FLAG_C; break; case 7: /* CCF */ tmpB = z80->af.f & Z80_FLAG_C; z80->af.f = (z80->af.f & (Z80_FLAG_PV | Z80_FLAG_Z | Z80_FLAG_S)) | (z80->af.a & Z80_FLAG_35); z80->af.f |= tmpB ? Z80_FLAG_H : Z80_FLAG_C; break; } break; } break; /* 0x40..0x7F (LD r8,r8) */ case 0x40: if (opcode == 0x76) { z80->halted = 1; /* HALT */ DEC_W(z80->pc); continue; } rsrc = (opcode & 0x07); rdst = ((opcode >> 3) & 0x07); switch (rsrc) { case 0: tmpB = z80->bc.b; break; case 1: tmpB = z80->bc.c; break; case 2: tmpB = z80->de.d; break; case 3: tmpB = z80->de.e; break; case 4: tmpB = (gotDD && rdst == 6 ? z80->hl.h : z80->dd->h); break; case 5: tmpB = (gotDD && rdst == 6 ? z80->hl.l : z80->dd->l); break; case 6: if (gotDD) { DEC_PC; Z80_ContentionPCBy1(z80, 5); INC_PC; } tmpW = ZADD_WX(z80->dd->w, disp); tmpB = Z80_PeekB3T(z80, tmpW); break; case 7: tmpB = z80->af.a; break; } switch (rdst) { case 0: z80->bc.b = tmpB; break; case 1: z80->bc.c = tmpB; break; case 2: z80->de.d = tmpB; break; case 3: z80->de.e = tmpB; break; case 4: if (gotDD && rsrc == 6) z80->hl.h = tmpB; else z80->dd->h = tmpB; break; case 5: if (gotDD && rsrc == 6) z80->hl.l = tmpB; else z80->dd->l = tmpB; break; case 6: if (gotDD) { DEC_PC; Z80_ContentionPCBy1(z80, 5); INC_PC; } tmpW = ZADD_WX(z80->dd->w, disp); Z80_PokeB3T(z80, tmpW, tmpB); break; case 7: z80->af.a = tmpB; break; } break; /* 0x80..0xBF (ALU A,r8) */ case 0x80: switch (opcode & 0x07) { case 0: tmpB = z80->bc.b; break; case 1: tmpB = z80->bc.c; break; case 2: tmpB = z80->de.d; break; case 3: tmpB = z80->de.e; break; case 4: tmpB = z80->dd->h; break; case 5: tmpB = z80->dd->l; break; case 6: if (gotDD) { DEC_PC; Z80_ContentionPCBy1(z80, 5); INC_PC; } tmpW = ZADD_WX(z80->dd->w, disp); tmpB = Z80_PeekB3T(z80, tmpW); break; case 7: tmpB = z80->af.a; break; } switch ((opcode >> 3) & 0x07) { case 0: Z80_ADD_A(z80, tmpB); break; case 1: Z80_ADC_A(z80, tmpB); break; case 2: Z80_SUB_A(z80, tmpB); break; case 3: Z80_SBC_A(z80, tmpB); break; case 4: Z80_AND_A(z80, tmpB); break; case 5: Z80_XOR_A(z80, tmpB); break; case 6: Z80_OR_A(z80, tmpB); break; case 7: Z80_CP_A(z80, tmpB); break; } break; /* 0xC0..0xFF */ case 0xC0: switch (opcode & 0x07) { /* RET cc */ case 0: Z80_ContentionIRBy1(z80, 1); SET_TRUE_CC if (trueCC) z80->memptr.w = z80->pc = Z80_Pop6T(z80); break; /* POP rr/special0 */ case 1: if (opcode & 0x08) { /* special 0 */ switch ((opcode >> 4) & 0x03) { /* RET */ case 0: z80->memptr.w = z80->pc = Z80_Pop6T(z80); break; /* EXX */ case 1: Z80_EXX(z80); break; /* JP (HL) */ case 2: z80->pc = z80->dd->w; break; /* LD SP,HL */ case 3: /*OCR(6)*/ Z80_ContentionIRBy1(z80, 2); z80->sp.w = z80->dd->w; break; } } else { /* POP rr */ tmpW = Z80_Pop6T(z80); switch ((opcode >> 4) & 0x03) { case 0: z80->bc.w = tmpW; break; case 1: z80->de.w = tmpW; break; case 2: z80->dd->w = tmpW; break; case 3: z80->af.w = tmpW; break; } } break; /* JP cc,nn */ case 2: SET_TRUE_CC z80->memptr.w = Z80_GetWordPC(z80, 0); if (trueCC) z80->pc = z80->memptr.w; break; /* special1/special3 */ case 3: switch ((opcode >> 3) & 0x07) { /* JP nn */ case 0: z80->memptr.w = z80->pc = Z80_GetWordPC(z80, 0); break; /* OUT (n),A */ case 2: tmpW = Z80_PeekB3TA(z80, z80->pc); INC_PC; z80->memptr.l = (tmpW + 1) & 0xff; z80->memptr.h = z80->af.a; tmpW |= (((uint16_t)(z80->af.a)) << 8); Z80_PortOut(z80, tmpW, z80->af.a); break; /* IN A,(n) */ case 3: tmpW = (((uint16_t)(z80->af.a)) << 8) | Z80_PeekB3TA(z80, z80->pc); INC_PC; z80->memptr.w = (tmpW + 1) & 0xffff; z80->af.a = Z80_PortIn(z80, tmpW); break; /* EX (SP),HL */ case 4: /*SRL(3),SRH(4)*/ tmpW = Z80_PeekW6T(z80, z80->sp.w); Z80_ContentionBy1(z80, (z80->sp.w + 1) & 0xffff, 1); /*SWL(3),SWH(5)*/ Z80_PokeW6TInv(z80, z80->sp.w, z80->dd->w); Z80_ContentionBy1(z80, z80->sp.w, 2); z80->memptr.w = z80->dd->w = tmpW; break; /* EX DE,HL */ case 5: tmpW = z80->de.w; z80->de.w = z80->hl.w; z80->hl.w = tmpW; break; /* DI */ case 6: z80->iff1 = z80->iff2 = 0; break; /* EI */ case 7: z80->iff1 = z80->iff2 = 1; z80->prev_was_EIDDR = 1; break; } break; /* CALL cc,nn */ case 4: SET_TRUE_CC z80->memptr.w = Z80_GetWordPC(z80, trueCC); if (trueCC) { Z80_Push6T(z80, z80->pc); z80->pc = z80->memptr.w; } break; /* PUSH rr/special2 */ case 5: if (opcode & 0x08) { if (((opcode >> 4) & 0x03) == 0) { /* CALL */ z80->memptr.w = tmpW = Z80_GetWordPC(z80, 1); Z80_Push6T(z80, z80->pc); z80->pc = tmpW; } } else { /* PUSH rr */ /*OCR(5)*/ Z80_ContentionIRBy1(z80, 1); switch ((opcode >> 4) & 0x03) { case 0: tmpW = z80->bc.w; break; case 1: tmpW = z80->de.w; break; case 2: tmpW = z80->dd->w; break; default: tmpW = z80->af.w; break; } Z80_Push6T(z80, tmpW); } break; /* ALU A,n */ case 6: tmpB = Z80_PeekB3TA(z80, z80->pc); INC_PC; switch ((opcode >> 3) & 0x07) { case 0: Z80_ADD_A(z80, tmpB); break; case 1: Z80_ADC_A(z80, tmpB); break; case 2: Z80_SUB_A(z80, tmpB); break; case 3: Z80_SBC_A(z80, tmpB); break; case 4: Z80_AND_A(z80, tmpB); break; case 5: Z80_XOR_A(z80, tmpB); break; case 6: Z80_OR_A(z80, tmpB); break; case 7: Z80_CP_A(z80, tmpB); break; } break; /* RST nnn */ case 7: /*OCR(5)*/ Z80_ContentionIRBy1(z80, 1); Z80_Push6T(z80, z80->pc); z80->memptr.w = z80->pc = opcode & 0x38; break; } break; } /* end switch */ } } int32_t Z80_ExecuteStep (Z80Info *z80) { int32_t one = z80->next_event_tstate, ots = z80->tstates; /***/ z80->next_event_tstate = ots + 1; Z80_Execute(z80); z80->next_event_tstate = one; return z80->tstates - ots; } int32_t Z80_ExecuteTS (Z80Info *z80, int32_t tstates) { if (tstates > 0) { z80->tstates = 0; z80->next_event_tstate = tstates; Z80_Execute(z80); return z80->tstates; } return 0; } /******************************************************************************/ /* changes z80->tstates if interrupt occurs */ int Z80_Interrupt (Z80Info *z80) { uint16_t a; int ots = z80->tstates; /***/ if (z80->prev_was_EIDDR < 0) { z80->prev_was_EIDDR = 0; /* Z80 bug */ z80->af.f &= ~Z80_FLAG_PV; } if (z80->prev_was_EIDDR || !z80->iff1) return 0; /* not accepted */ if (z80->halted) { z80->halted = 0; INC_PC; } z80->iff1 = z80->iff2 = 0; /* disable interrupts */ /***/ switch ((z80->im &= 0x03)) { case 3: /* ??? */ z80->im = 0; case 0: /* take instruction from the bus (for now we assume that reading from bus always returns 0xff) */ /* with a CALL nnnn on the data bus, it takes 19 cycles: */ /* M1 cycle: 7 T to acknowledge interrupt (where exactly data bus reading occures?) */ /* M2 cycle: 3 T to read low byte of 'nnnn' from data bus */ /* M3 cycle: 3 T to read high byte of 'nnnn' and decrement SP */ /* M4 cycle: 3 T to write high byte of PC to the stack and decrement SP */ /* M5 cycle: 3 T to write low byte of PC and jump to 'nnnn' */ z80->tstates += 6; case 1: /* just do RST #38 */ INC_R; z80->tstates += 7; /* M1 cycle: 7 T to acknowledge interrupt and decrement SP */ /* M2 cycle: 3 T states write high byte of PC to the stack and decrement SP */ /* M3 cycle: 3 T states write the low byte of PC and jump to #0038 */ Z80_Push6T(z80, z80->pc); z80->memptr.w = z80->pc = 0x38; break; case 2: INC_R; z80->tstates += 7; /* M1 cycle: 7 T to acknowledge interrupt and decrement SP */ /* M2 cycle: 3 T states write high byte of PC to the stack and decrement SP */ /* M3 cycle: 3 T states write the low byte of PC */ Z80_Push6T(z80, z80->pc); /* M4 cycle: 3 T to read high byte from the interrupt vector */ /* M5 cycle: 3 T to read low byte from bus and jump to interrupt routine */ a = (((uint16_t)z80->regI) << 8) | 0xff; z80->memptr.w = z80->pc = Z80_PeekW6T(z80, a); break; } return z80->tstates - ots; /* accepted */ } int Z80_NMI (Z80Info *z80) { int ots = z80->tstates; /***/ /* emulate Z80 bug with interrupted LD A,I/R */ /*if (z80->prev_was_EIDDR < 0) { z80->prev_was_EIDDR = 0; z80->af.f &= ~Z80_FLAG_PV; }*/ /*if (z80->prev_was_EIDDR) return 0;*/ z80->prev_was_EIDDR = 0; /* don't care */ if (z80->halted) { z80->halted = 0; INC_PC; } INC_R; z80->iff1 = 0; /* IFF2 is not changed */ z80->tstates += 5; /* M1 cycle: 5 T states to do an opcode read and decrement SP */ /* M2 cycle: 3 T states write high byte of PC to the stack and decrement SP */ /* M3 cycle: 3 T states write the low byte of PC and jump to #0066 */ Z80_Push6T(z80, z80->pc); z80->memptr.w = z80->pc = 0x66; return z80->tstates - ots; } /******************************************************************************/ uint16_t Z80_Pop (Z80Info *z80) { uint16_t res = Z80_PeekBI(z80, z80->sp.w); /***/ z80->sp.w = (z80->sp.w + 1) & 0xffff; res |= ((uint16_t)Z80_PeekBI(z80, z80->sp.w)) << 8; z80->sp.w = (z80->sp.w + 1) & 0xffff; return res; } void Z80_Push (Z80Info *z80, uint16_t value) { z80->sp.w = (((int32_t)z80->sp.w) - 1) & 0xffff; Z80_PokeBI(z80, z80->sp.w, (value >> 8) & 0xff); z80->sp.w = (((int32_t)z80->sp.w) - 1) & 0xffff; Z80_PokeBI(z80, z80->sp.w, value & 0xff); }