aboutsummaryrefslogtreecommitdiff
path: root/zymosis.c
diff options
context:
space:
mode:
authorJuan J. Martinez <jjm@usebox.net>2021-04-17 22:05:24 +0100
committerJuan J. Martinez <jjm@usebox.net>2021-04-17 22:05:24 +0100
commita5745813e442b66ae6eed30bba81d1b3dd5cf634 (patch)
treeb302db1780350dc44543b81d49bfc1b2d4dff6a8 /zymosis.c
downloadbeeper-int-zx-a5745813e442b66ae6eed30bba81d1b3dd5cf634.tar.gz
beeper-int-zx-a5745813e442b66ae6eed30bba81d1b3dd5cf634.zip
Initial public release
Diffstat (limited to 'zymosis.c')
-rw-r--r--zymosis.c1985
1 files changed, 1985 insertions, 0 deletions
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 <stdlib.h>
+
+#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);
+}