From a5745813e442b66ae6eed30bba81d1b3dd5cf634 Mon Sep 17 00:00:00 2001 From: "Juan J. Martinez" Date: Sat, 17 Apr 2021 22:05:24 +0100 Subject: Initial public release --- zymosis.c | 1985 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1985 insertions(+) create mode 100644 zymosis.c (limited to 'zymosis.c') diff --git a/zymosis.c b/zymosis.c new file mode 100644 index 0000000..83ca583 --- /dev/null +++ b/zymosis.c @@ -0,0 +1,1985 @@ +/* + * 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); +} -- cgit v1.2.3