qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH] Z80 emulation updated again!
@ 2009-05-31 15:20 Stuart Brady
  2010-12-20 22:45 ` Alexander Graf
  0 siblings, 1 reply; 27+ messages in thread
From: Stuart Brady @ 2009-05-31 15:20 UTC (permalink / raw)
  To: qemu-devel, Ulrich Hecht

[-- Attachment #1: Type: text/plain, Size: 2149 bytes --]

Hi!

Here's an update of the Z80 system emulator, which currently emulates
the ZX Spectrum only.  Significant changes since Ulrich Hecht's updated
version are as follows:

 * Converted CPU emulation core to TCG
 * Fixed emulation of 'bright' display attribute
 * Fixed display output for pixel formats besides 32-bit RGB
 * Improved keyboard handling, with support for cursor keys, etc.
 * Removed dead code
 * Now supports libspectrum 0.5 (for snapshot loading)
 * Numerous cleanups

The emulation has not yet been optimised, but should achieve something
between 100 and 200 times the performance of real hardware.

Thanks go to Ulrich for adding support for snapshot loading, and for
porting the 'HALT' hack from xz80, in his updated version!

BTW, I have also written some experimental 128K emulation -- however,
this only achieves ~10 times the speed of real hardware, as the 128K
makes heavy use of bank switching.

I can split the patch up if wanted, although only 44 insertions are in
existing files.

Cheers,
-- 
Stuart Brady

diffstat:
 Makefile                        |    3 
 Makefile.target                 |   13 
 configure                       |   16 
 cpu-exec.c                      |   10 
 dis-asm.h                       |    1 
 disas.c                         |    2 
 hw/pixel_ops_dup.h              |   61 +
 hw/zx_glyphs.h                  |   37 
 hw/zx_key_template.h            |   50 +
 hw/zx_spectrum.c                |  443 +++++++++
 hw/zx_video.c                   |  375 ++++++++
 hw/zx_video.h                   |    8 
 target-z80/TODO                 |   13 
 target-z80/cpu.h                |  259 +++++
 target-z80/exec.h               |  196 ++++
 target-z80/genreg_template.h    |   65 +
 target-z80/genreg_template_af.h |   83 +
 target-z80/helper.c             |  211 ++++
 target-z80/helper.h             |   89 +
 target-z80/machine.c            |   11 
 target-z80/op_helper.c          |  947 ++++++++++++++++++++
 target-z80/translate.c          | 1834 ++++++++++++++++++++++++++++++++++++++++
 z80-dis.c                       |  621 +++++++++++++
 23 files changed, 5347 insertions(+), 1 deletion(-)

[-- Attachment #2: qemu-z80.diff --]
[-- Type: text/x-diff, Size: 154072 bytes --]

diff --git a/Makefile b/Makefile
index d7b9985..3ef706b 100644
--- a/Makefile
+++ b/Makefile
@@ -271,7 +271,7 @@ ifdef INSTALL_BLOBS
 BLOBS=bios.bin vgabios.bin vgabios-cirrus.bin ppc_rom.bin \
 video.x openbios-sparc32 openbios-sparc64 openbios-ppc \
 pxe-ne2k_pci.bin pxe-rtl8139.bin pxe-pcnet.bin pxe-e1000.bin \
-bamboo.dtb
+bamboo.dtb zx-rom.bin
 else
 BLOBS=
 endif
@@ -386,6 +386,7 @@ tarbin:
 	$(bindir)/qemu-system-sh4 \
 	$(bindir)/qemu-system-sh4eb \
 	$(bindir)/qemu-system-sparc \
+	$(bindir)/qemu-system-z80 \
 	$(bindir)/qemu-i386 \
 	$(bindir)/qemu-x86_64 \
 	$(bindir)/qemu-alpha \
diff --git a/Makefile.target b/Makefile.target
index 445d55f..a97ca05 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -168,6 +168,12 @@ LIBOBJS+= mmu.o
 endif
 endif
 
+ifeq ($(TARGET_BASE_ARCH), z80)
+ifdef CONFIG_LIBSPECTRUM
+LIBS+=-lspectrum
+endif
+endif
+
 # NOTE: the disassembler code is only needed for debugging
 LIBOBJS+=disas.o
 ifeq ($(findstring i386, $(TARGET_ARCH) $(ARCH)),i386)
@@ -212,6 +218,9 @@ endif
 ifeq ($(findstring s390, $(TARGET_ARCH) $(ARCH)),s390)
 LIBOBJS+=s390-dis.o
 endif
+ifeq ($(findstring z80, $(TARGET_ARCH) $(ARCH)),z80)
+LIBOBJS+=z80-dis.o
+endif
 
 # libqemu
 
@@ -686,6 +695,10 @@ ifeq ($(TARGET_BASE_ARCH), m68k)
 OBJS+= an5206.o mcf5206.o mcf_uart.o mcf_intc.o mcf5208.o mcf_fec.o
 OBJS+= m68k-semi.o dummy_m68k.o
 endif
+ifeq ($(TARGET_BASE_ARCH), z80)
+OBJS+= zx_spectrum.o zx_video.o dma.o
+OBJS+= serial.o i8259.o
+endif
 ifdef CONFIG_COCOA
 COCOA_LIBS=-F/System/Library/Frameworks -framework Cocoa -framework IOKit
 ifdef CONFIG_COREAUDIO
diff --git a/configure b/configure
index 21c0633..fdd9493 100755
--- a/configure
+++ b/configure
@@ -163,6 +163,7 @@ bigendian="no"
 mingw32="no"
 EXESUF=""
 slirp="yes"
+libspectrum="no"
 vde="yes"
 fmod_lib=""
 fmod_inc=""
@@ -397,6 +398,8 @@ for opt do
   ;;
   --disable-sdl) sdl="no"
   ;;
+  --enable-libspectrum) libspectrum="yes"
+  ;;
   --fmod-lib=*) fmod_lib="$optarg"
   ;;
   --fmod-inc=*) fmod_inc="$optarg"
@@ -595,6 +598,7 @@ echo "  --disable-strip          disable stripping binaries"
 echo "  --disable-werror         disable compilation abort on warning"
 echo "  --disable-sdl            disable SDL"
 echo "  --enable-cocoa           enable COCOA (Mac OS X only)"
+echo "  --enable-libspectrum     enable ZX Spectrum snapshot loading"
 echo "  --audio-drv-list=LIST    set audio drivers list:"
 echo "                           Available drivers: $audio_possible_drivers"
 echo "  --audio-card-list=LIST   set list of emulated audio cards [$audio_card_list]"
@@ -694,6 +698,7 @@ ppc64-softmmu \
 sh4-softmmu \
 sh4eb-softmmu \
 sparc-softmmu \
+z80-softmmu \
 "
     fi
 # the following are Linux specific
@@ -1355,6 +1360,7 @@ fi
 if test -n "$sparc_cpu"; then
     echo "Target Sparc Arch $sparc_cpu"
 fi
+echo "libspec. support  $libspectrum"
 echo "kqemu support     $kqemu"
 echo "xen support       $xen"
 echo "brlapi support    $brlapi"
@@ -1595,6 +1601,10 @@ if test "$mixemu" = "yes" ; then
   echo "CONFIG_MIXEMU=yes" >> $config_mak
   echo "#define CONFIG_MIXEMU 1" >> $config_h
 fi
+if test "$libspectrum" = "yes" ; then
+  echo "CONFIG_LIBSPECTRUM=yes" >> $config_mak
+  echo "#define CONFIG_LIBSPECTRUM 1" >> $config_h
+fi
 if test "$vnc_tls" = "yes" ; then
   echo "CONFIG_VNC_TLS=yes" >> $config_mak
   echo "CONFIG_VNC_TLS_CFLAGS=$vnc_tls_cflags" >> $config_mak
@@ -2038,6 +2048,12 @@ case "$target_cpu" in
     echo "#define TARGET_ABI32 1" >> $config_h
     target_phys_bits=64
   ;;
+  z80)
+    echo "TARGET_ARCH=z80" >> $config_mak
+    echo "#define TARGET_ARCH \"z80\"" >> $config_h
+    echo "#define TARGET_Z80 1" >> $config_h
+    target_phys_bits=32
+  ;;
   *)
     echo "Unsupported target CPU"
     exit 1
diff --git a/cpu-exec.c b/cpu-exec.c
index 8734337..d614c42 100644
--- a/cpu-exec.c
+++ b/cpu-exec.c
@@ -250,6 +250,7 @@ int cpu_exec(CPUState *env1)
 #elif defined(TARGET_MIPS)
 #elif defined(TARGET_SH4)
 #elif defined(TARGET_CRIS)
+#elif defined(TARGET_Z80)
     /* XXXXX */
 #else
 #error unsupported target CPU
@@ -543,6 +544,12 @@ int cpu_exec(CPUState *env1)
                         do_interrupt(1);
                         next_tb = 0;
                     }
+#elif defined(TARGET_Z80)
+                    if (interrupt_request & CPU_INTERRUPT_HARD) {
+			env->interrupt_request &= ~CPU_INTERRUPT_HARD;
+                        /* TODO: Add support for NMIs */
+                        do_interrupt(env);
+                    }
 #endif
                    /* Don't use the cached interupt_request value,
                       do_interrupt may have updated the EXITTB flag. */
@@ -588,6 +595,8 @@ int cpu_exec(CPUState *env1)
                     log_cpu_state(env, 0);
 #elif defined(TARGET_CRIS)
                     log_cpu_state(env, 0);
+#elif defined(TARGET_Z80)
+                    log_cpu_state(env, 0);
 #else
 #error unsupported target CPU
 #endif
@@ -702,6 +711,7 @@ int cpu_exec(CPUState *env1)
 #elif defined(TARGET_SH4)
 #elif defined(TARGET_ALPHA)
 #elif defined(TARGET_CRIS)
+#elif defined(TARGET_Z80)
     /* XXXXX */
 #else
 #error unsupported target CPU
diff --git a/dis-asm.h b/dis-asm.h
index 251c490..0a00f7b 100644
--- a/dis-asm.h
+++ b/dis-asm.h
@@ -402,6 +402,7 @@ extern int print_insn_ppc		PARAMS ((bfd_vma, disassemble_info*));
 extern int print_insn_s390		PARAMS ((bfd_vma, disassemble_info*));
 extern int print_insn_crisv32           PARAMS ((bfd_vma, disassemble_info*));
 extern int print_insn_microblaze        PARAMS ((bfd_vma, disassemble_info*));
+extern int print_insn_z80		PARAMS ((bfd_vma, disassemble_info*));
 
 #if 0
 /* Fetch the disassembler for a given BFD, if that support is available.  */
diff --git a/disas.c b/disas.c
index af5a9ea..5ca873e 100644
--- a/disas.c
+++ b/disas.c
@@ -198,6 +198,8 @@ void target_disas(FILE *out, target_ulong code, target_ulong size, int flags)
 #elif defined(TARGET_MICROBLAZE)
     disasm_info.mach = bfd_arch_microblaze;
     print_insn = print_insn_microblaze;
+#elif defined(TARGET_Z80)
+    print_insn = print_insn_z80;
 #else
     fprintf(out, "0x" TARGET_FMT_lx
 	    ": Asm output not supported on this arch\n", code);
diff --git a/hw/pixel_ops_dup.h b/hw/pixel_ops_dup.h
new file mode 100644
index 0000000..da74d5d
--- /dev/null
+++ b/hw/pixel_ops_dup.h
@@ -0,0 +1,61 @@
+static inline unsigned int rgb_to_pixel8_dup(unsigned int r, unsigned int g,
+                                             unsigned int b)
+{
+    unsigned int col;
+    col = rgb_to_pixel8(r, g, b);
+    col |= col << 8;
+    col |= col << 16;
+    return col;
+}
+
+static inline unsigned int rgb_to_pixel15_dup(unsigned int r, unsigned int g,
+                                              unsigned int b)
+{
+    unsigned int col;
+    col = rgb_to_pixel15(r, g, b);
+    col |= col << 16;
+    return col;
+}
+
+static inline unsigned int rgb_to_pixel15bgr_dup(unsigned int r, unsigned int g,
+                                                 unsigned int b)
+{
+    unsigned int col;
+    col = rgb_to_pixel15bgr(r, g, b);
+    col |= col << 16;
+    return col;
+}
+
+static inline unsigned int rgb_to_pixel16_dup(unsigned int r, unsigned int g,
+                                              unsigned int b)
+{
+    unsigned int col;
+    col = rgb_to_pixel16(r, g, b);
+    col |= col << 16;
+    return col;
+}
+
+static inline unsigned int rgb_to_pixel16bgr_dup(unsigned int r, unsigned int g,
+                                                 unsigned int b)
+{
+    unsigned int col;
+    col = rgb_to_pixel16bgr(r, g, b);
+    col |= col << 16;
+    return col;
+}
+
+static inline unsigned int rgb_to_pixel32_dup(unsigned int r, unsigned int g,
+                                              unsigned int b)
+{
+    unsigned int col;
+    col = rgb_to_pixel32(r, g, b);
+    return col;
+}
+
+static inline unsigned int rgb_to_pixel32bgr_dup(unsigned int r, unsigned int g,
+                                                 unsigned int b)
+{
+    unsigned int col;
+    col = rgb_to_pixel32bgr(r, g, b);
+    return col;
+}
diff --git a/hw/zx_glyphs.h b/hw/zx_glyphs.h
new file mode 100644
index 0000000..dd1ade2
--- /dev/null
+++ b/hw/zx_glyphs.h
@@ -0,0 +1,37 @@
+#if DEPTH == 8
+#define BPP 1
+#elif DEPTH == 16
+#define BPP 2
+#elif DEPTH == 32
+#define BPP 4
+#else
+#error unsupport depth
+#endif
+
+static inline void glue(zx_draw_glyph_line_, DEPTH)(uint8_t *d,
+                                                    uint32_t font_data,
+                                                    uint32_t xorcol,
+                                                    uint32_t bgcol)
+{
+#if BPP == 1
+    ((uint32_t *)d)[0] = (dmask16[(font_data >> 4)] & xorcol) ^ bgcol;
+    ((uint32_t *)d)[1] = (dmask16[(font_data >> 0) & 0xf] & xorcol) ^ bgcol;
+#elif BPP == 2
+    ((uint32_t *)d)[0] = (dmask4[(font_data >> 6)] & xorcol) ^ bgcol;
+    ((uint32_t *)d)[1] = (dmask4[(font_data >> 4) & 3] & xorcol) ^ bgcol;
+    ((uint32_t *)d)[2] = (dmask4[(font_data >> 2) & 3] & xorcol) ^ bgcol;
+    ((uint32_t *)d)[3] = (dmask4[(font_data >> 0) & 3] & xorcol) ^ bgcol;
+#else
+    ((uint32_t *)d)[0] = (-((font_data >> 7)) & xorcol) ^ bgcol;
+    ((uint32_t *)d)[1] = (-((font_data >> 6) & 1) & xorcol) ^ bgcol;
+    ((uint32_t *)d)[2] = (-((font_data >> 5) & 1) & xorcol) ^ bgcol;
+    ((uint32_t *)d)[3] = (-((font_data >> 4) & 1) & xorcol) ^ bgcol;
+    ((uint32_t *)d)[4] = (-((font_data >> 3) & 1) & xorcol) ^ bgcol;
+    ((uint32_t *)d)[5] = (-((font_data >> 2) & 1) & xorcol) ^ bgcol;
+    ((uint32_t *)d)[6] = (-((font_data >> 1) & 1) & xorcol) ^ bgcol;
+    ((uint32_t *)d)[7] = (-((font_data >> 0) & 1) & xorcol) ^ bgcol;
+#endif
+}
+
+#undef DEPTH
+#undef BPP
diff --git a/hw/zx_key_template.h b/hw/zx_key_template.h
new file mode 100644
index 0000000..35305ce
--- /dev/null
+++ b/hw/zx_key_template.h
@@ -0,0 +1,50 @@
+/*
+ * ZX Spectrum Keyboard Layout
+ */
+
+        /* Name,    Row, Column */
+DEF_ZX_KEY(1,         3, 0)
+DEF_ZX_KEY(2,         3, 1)
+DEF_ZX_KEY(3,         3, 2)
+DEF_ZX_KEY(4,         3, 3)
+DEF_ZX_KEY(5,         3, 4)
+DEF_ZX_KEY(6,         4, 4)
+DEF_ZX_KEY(7,         4, 3)
+DEF_ZX_KEY(8,         4, 2)
+DEF_ZX_KEY(9,         4, 1)
+DEF_ZX_KEY(0,         4, 0)
+
+DEF_ZX_KEY(Q,         2, 0)
+DEF_ZX_KEY(W,         2, 1)
+DEF_ZX_KEY(E,         2, 2)
+DEF_ZX_KEY(R,         2, 3)
+DEF_ZX_KEY(T,         2, 4)
+DEF_ZX_KEY(Y,         5, 4)
+DEF_ZX_KEY(U,         5, 3)
+DEF_ZX_KEY(I,         5, 2)
+DEF_ZX_KEY(O,         5, 1)
+DEF_ZX_KEY(P,         5, 0)
+
+DEF_ZX_KEY(A,         1, 0)
+DEF_ZX_KEY(S,         1, 1)
+DEF_ZX_KEY(D,         1, 2)
+DEF_ZX_KEY(F,         1, 3)
+DEF_ZX_KEY(G,         1, 4)
+DEF_ZX_KEY(H,         6, 4)
+DEF_ZX_KEY(J,         6, 3)
+DEF_ZX_KEY(K,         6, 2)
+DEF_ZX_KEY(L,         6, 1)
+DEF_ZX_KEY(ENTER,     6, 0)
+
+DEF_ZX_KEY(CAPSSHIFT, 0, 0)
+DEF_ZX_KEY(Z,         0, 1)
+DEF_ZX_KEY(X,         0, 2)
+DEF_ZX_KEY(C,         0, 3)
+DEF_ZX_KEY(V,         0, 4)
+DEF_ZX_KEY(B,         7, 4)
+DEF_ZX_KEY(N,         7, 3)
+DEF_ZX_KEY(M,         7, 2)
+DEF_ZX_KEY(SYMBSHIFT, 7, 1)
+DEF_ZX_KEY(SPACE,     7, 0)
+
+#undef DEF_ZX_KEY
diff --git a/hw/zx_spectrum.c b/hw/zx_spectrum.c
new file mode 100644
index 0000000..4ccb887
--- /dev/null
+++ b/hw/zx_spectrum.c
@@ -0,0 +1,443 @@
+/*
+ * QEMU ZX Spectrum Emulator
+ *
+ * Copyright (c) 2007-2009 Stuart Brady <stuart.brady@gmail.com>
+ * Copyright (c) 2007 Ulrich Hecht
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw.h"
+#include "qemu-timer.h"
+#include "console.h"
+#include "isa.h"
+#include "sysemu.h"
+#include "zx_video.h"
+#include "boards.h"
+
+#ifdef CONFIG_LIBSPECTRUM
+#include <libspectrum.h>
+#endif
+
+#define ROM_FILENAME "zx-rom.bin"
+
+//#define DEBUG_ZX_SPECTRUM
+
+#ifdef DEBUG_ZX_SPECTRUM
+#define DPRINTF(fmt, ...) printf(fmt, ## __VA_ARGS__)
+#else
+#define DPRINTF(fmt, ...)
+#endif
+
+static int keystate[8];
+
+static uint32_t io_keyboard_read(void *opaque, uint32_t addr)
+{
+    int r = 0;
+    uint8_t colbits = 0xff;
+
+    uint32_t rowbits = ((addr >> 8) & 0xff);
+
+    for (r = 0; r < 8; r++) {
+        if (!(rowbits & (1 << r))) {
+            colbits &= keystate[r];
+        }
+    }
+    return colbits;
+}
+
+static uint32_t io_spectrum_read(void *opaque, uint32_t addr)
+{
+    if (addr & 1) {
+        return 0xff;
+    }
+
+    return io_keyboard_read(opaque, addr);
+}
+
+static void main_cpu_reset(void *opaque)
+{
+    CPUState *env = opaque;
+    cpu_reset(env);
+}
+
+static QEMUTimer *zx_ula_timer;
+
+static void zx_50hz_timer(void *opaque)
+{
+    int64_t next_time;
+
+    CPUState *env = opaque;
+    cpu_interrupt(env, CPU_INTERRUPT_HARD);
+
+    /* FIXME: not exactly 50 Hz */
+    next_time = qemu_get_clock(vm_clock) + muldiv64(1, ticks_per_sec, 50);
+    qemu_mod_timer(zx_ula_timer, next_time);
+
+    zx_video_do_retrace();
+}
+
+static CPUState *zx_env;
+
+static void zx_timer_init(void)
+{
+    int64_t t = qemu_get_clock(vm_clock);
+    zx_ula_timer = qemu_new_timer(vm_clock, zx_50hz_timer, zx_env);
+    qemu_mod_timer(zx_ula_timer, t);
+}
+
+typedef struct {
+    int row;
+    int column;
+} ZXKeypos;
+
+#define DEF_ZX_KEY(name, row, column) ZX_KEY_ ## name,
+enum zx_keys {
+#include "zx_key_template.h"
+ZX_MAX_KEYS
+};
+
+#define DEF_ZX_KEY(name, row, column) [ZX_KEY_ ## name] = {row, column},
+static const ZXKeypos keypos[ZX_MAX_KEYS] = {
+#include "zx_key_template.h"
+};
+
+static int zx_keypressed[ZX_MAX_KEYS];
+static int qemu_keypressed[0x100];
+
+static const int map[0x100][2] = {
+    [0 ... 0xff] = {-1, -1}, /* Unmapped by default */
+
+    [0x01] = {ZX_KEY_CAPSSHIFT, ZX_KEY_SPACE}, /* Escape */
+
+    [0x02] = {ZX_KEY_1, -1},
+    [0x03] = {ZX_KEY_2, -1},
+    [0x04] = {ZX_KEY_3, -1},
+    [0x05] = {ZX_KEY_4, -1},
+    [0x06] = {ZX_KEY_5, -1},
+    [0x07] = {ZX_KEY_6, -1},
+    [0x08] = {ZX_KEY_7, -1},
+    [0x09] = {ZX_KEY_8, -1},
+    [0x0a] = {ZX_KEY_9, -1},
+    [0x0b] = {ZX_KEY_0, -1},
+
+    [0x0c] = {ZX_KEY_SYMBSHIFT, ZX_KEY_J}, /* Minus */
+
+    [0x0e] = {ZX_KEY_CAPSSHIFT, ZX_KEY_0}, /* Backspace */
+
+    [0x10] = {ZX_KEY_Q, -1},
+    [0x11] = {ZX_KEY_W, -1},
+    [0x12] = {ZX_KEY_E, -1},
+    [0x13] = {ZX_KEY_R, -1},
+    [0x14] = {ZX_KEY_T, -1},
+    [0x15] = {ZX_KEY_Y, -1},
+    [0x16] = {ZX_KEY_U, -1},
+    [0x17] = {ZX_KEY_I, -1},
+    [0x18] = {ZX_KEY_O, -1},
+    [0x19] = {ZX_KEY_P, -1},
+
+    [0x0d] = {ZX_KEY_SYMBSHIFT, ZX_KEY_L}, /* Equals */
+    [0x0f] = {ZX_KEY_CAPSSHIFT, ZX_KEY_1}, /* Tab */
+
+    [0x1c] = {ZX_KEY_ENTER,     -1}, /* Enter */
+
+    [0x1d] = {ZX_KEY_SYMBSHIFT, -1}, /* Left Control */
+
+    [0x1e] = {ZX_KEY_A, -1},
+    [0x1f] = {ZX_KEY_S, -1},
+    [0x20] = {ZX_KEY_D, -1},
+    [0x21] = {ZX_KEY_F, -1},
+    [0x22] = {ZX_KEY_G, -1},
+    [0x23] = {ZX_KEY_H, -1},
+    [0x24] = {ZX_KEY_J, -1},
+    [0x25] = {ZX_KEY_K, -1},
+    [0x26] = {ZX_KEY_L, -1},
+
+    [0x27] = {ZX_KEY_SYMBSHIFT, ZX_KEY_O}, /* Semicolon */
+    [0x28] = {ZX_KEY_SYMBSHIFT, ZX_KEY_7}, /* Apostrophe */
+
+    [0x2a] = {ZX_KEY_CAPSSHIFT, -1}, /* Left Shift */
+
+    [0x2b] = {ZX_KEY_SYMBSHIFT, ZX_KEY_3}, /* Hash */
+
+    [0x2c] = {ZX_KEY_Z, -1},
+    [0x2d] = {ZX_KEY_X, -1},
+    [0x2e] = {ZX_KEY_C, -1},
+    [0x2f] = {ZX_KEY_V, -1},
+    [0x30] = {ZX_KEY_B, -1},
+    [0x31] = {ZX_KEY_N, -1},
+    [0x32] = {ZX_KEY_M, -1},
+
+    [0x33] = {ZX_KEY_SYMBSHIFT, ZX_KEY_N}, /* Period */
+    [0x34] = {ZX_KEY_SYMBSHIFT, ZX_KEY_M}, /* Comma */
+    [0x35] = {ZX_KEY_SYMBSHIFT, ZX_KEY_V}, /* Slash */
+
+    [0x36] = {ZX_KEY_CAPSSHIFT, -1}, /* Right Shift */
+    [0x37] = {ZX_KEY_SYMBSHIFT, ZX_KEY_B}, /* * (Numpad) */
+    [0x38] = {ZX_KEY_SYMBSHIFT, -1}, /* Left Alt */
+    [0x39] = {ZX_KEY_SPACE,     -1}, /* Space Bar */
+
+    [0x47] = {ZX_KEY_7,         -1}, /* 7 (Numpad) */
+    [0x48] = {ZX_KEY_8,         -1}, /* 8 (Numpad) */
+    [0x49] = {ZX_KEY_9,         -1}, /* 9 (Numpad) */
+    [0x4a] = {ZX_KEY_SYMBSHIFT, ZX_KEY_J}, /* Minus (Numpad) */
+    [0x4b] = {ZX_KEY_4,         -1}, /* 4 (Numpad) */
+    [0x4c] = {ZX_KEY_5,         -1}, /* 5 (Numpad) */
+    [0x4d] = {ZX_KEY_6,         -1}, /* 6 (Numpad) */
+    [0x4e] = {ZX_KEY_SYMBSHIFT, ZX_KEY_K}, /* Plus (Numpad) */
+    [0x4f] = {ZX_KEY_1,         -1}, /* 1 (Numpad) */
+    [0x50] = {ZX_KEY_2,         -1}, /* 2 (Numpad) */
+    [0x51] = {ZX_KEY_3,         -1}, /* 3 (Numpad) */
+    [0x52] = {ZX_KEY_0,         -1}, /* 0 (Numpad) */
+    [0x53] = {ZX_KEY_SYMBSHIFT, ZX_KEY_M}, /* Period (Numpad) */
+
+    [0x9c] = {ZX_KEY_SYMBSHIFT, -1}, /* Enter (Numpad) */
+    [0x9d] = {ZX_KEY_SYMBSHIFT, -1}, /* Right Control */
+    [0xb5] = {ZX_KEY_SYMBSHIFT, ZX_KEY_V}, /* Slash (Numpad) */
+    [0xb8] = {ZX_KEY_SYMBSHIFT, -1}, /* Right Alt */
+
+    [0xc8] = {ZX_KEY_CAPSSHIFT, ZX_KEY_7}, /* Up Arrow */
+    [0xcb] = {ZX_KEY_CAPSSHIFT, ZX_KEY_5}, /* Left Arrow */
+    [0xcd] = {ZX_KEY_CAPSSHIFT, ZX_KEY_8}, /* Right Arrow */
+    [0xd0] = {ZX_KEY_CAPSSHIFT, ZX_KEY_6}, /* Down Arrow */
+
+    [0xdb] = {ZX_KEY_CAPSSHIFT, ZX_KEY_SYMBSHIFT}, /* Left Meta */
+    [0xdc] = {ZX_KEY_CAPSSHIFT, ZX_KEY_SYMBSHIFT}, /* Menu */
+    [0xdd] = {ZX_KEY_CAPSSHIFT, ZX_KEY_SYMBSHIFT}, /* Right Meta */
+};
+
+/* FIXME:
+ *   Need to mappings from stepping on each other...
+ *   or at least make them step on one another in a consistent manner?
+ *   Could use separate state arrays for surpressing/adding keys
+ *   and allow only one change to the modifier keys at a time...
+ *
+ * Also need to implement shifted mappings.
+ */
+
+static void zx_put_keycode(void *opaque, int keycode)
+{
+    int release = keycode & 0x80;
+    int key, row, col;
+    static int ext_keycode = 0;
+    int i;
+    int valid;
+
+    if (keycode == 0xe0) {
+        ext_keycode = 1;
+    } else {
+        if (ext_keycode) {
+            keycode |= 0x80;
+        } else {
+            keycode &= 0x7f;
+        }
+        ext_keycode = 0;
+
+        DPRINTF("Keycode 0x%02x (%s)\n", keycode, release ? "release" : "press");
+
+        if (release && qemu_keypressed[keycode]) {
+            valid = 1;
+            qemu_keypressed[keycode] = 0;
+        } else if (!release && !qemu_keypressed[keycode]) {
+            valid = 1;
+            qemu_keypressed[keycode] = 1;
+        } else {
+            valid = 0;
+        }
+
+        if (valid) {
+            for (i = 0; i < 2; i++) {
+                key = map[keycode][i];
+                if (key != -1) {
+                    row = keypos[key].row;
+                    col = keypos[key].column;
+                    if (release) {
+                        if (--zx_keypressed[key] <= 0) {
+                            DPRINTF("Releasing 0x%02x\n", key);
+                            zx_keypressed[key] = 0;
+                            keystate[row] |= 1 << col;
+                        }
+                    } else {
+                        DPRINTF("Pressing 0x%02x\n", key);
+                        zx_keypressed[key]++;
+                        keystate[row] &= ~(1 << col);
+                    }
+                }
+            }
+        }
+    }
+}
+
+static void zx_keyboard_init(void)
+{
+    int i;
+    for (i=0; i<8; i++) {
+        keystate[i] = 0xff;
+    }
+    memset(zx_keypressed, 0, sizeof(zx_keypressed));
+    memset(qemu_keypressed, 0, sizeof(qemu_keypressed));
+    qemu_add_kbd_event_handler(zx_put_keycode, NULL);
+}
+
+static const uint8_t halthack_oldip[16] =
+    {253, 203, 1,110, 200, 58, 8, 92, 253, 203, 1, 174};
+static const uint8_t halthack_newip[16] =
+    {33, 59, 92, 118, 203, 110, 200, 58, 8, 92, 203, 174};
+
+/* ZX Spectrum initialisation */
+static void zx_spectrum_init(ram_addr_t ram_size,
+                             const char *boot_device,
+                             const char *kernel_filename,
+                             const char *kernel_cmdline,
+                             const char *initrd_filename,
+                             const char *cpu_model)
+{
+    char buf[1024];
+    uint8_t halthack_curip[12];
+    int ret;
+    ram_addr_t ram_offset, rom_offset;
+    int rom_size;
+    CPUState *env;
+
+    /* init CPUs */
+    if (!cpu_model) {
+        cpu_model = "z80";
+    }
+    env = cpu_init(cpu_model);
+    zx_env = env; // XXX
+    register_savevm("cpu", 0, 4, cpu_save, cpu_load, env);
+    qemu_register_reset(main_cpu_reset, 0, env);
+    main_cpu_reset(env);
+
+    /* allocate RAM */
+    ram_offset = qemu_ram_alloc(0xc000);
+    cpu_register_physical_memory(0x4000, 0xc000, ram_offset | IO_MEM_RAM);
+
+    /* ROM load */
+    snprintf(buf, sizeof(buf), "%s/%s", bios_dir, ROM_FILENAME);
+    rom_size = get_image_size(buf);
+    if (rom_size <= 0 ||
+        (rom_size % 0x4000) != 0) {
+        goto rom_error;
+    }
+    rom_offset = qemu_ram_alloc(rom_size);
+    cpu_register_physical_memory(0x0000, 0x4000, rom_offset | IO_MEM_ROM);
+    ret = load_image_targphys(buf, 0, rom_size);
+    if (ret != rom_size) {
+    rom_error:
+        fprintf(stderr, "qemu: could not load ZX Spectrum ROM '%s'\n", buf);
+        exit(1);
+    }
+
+    /* hack from xz80 adding HALT to the keyboard input loop to save CPU */
+    cpu_physical_memory_read(0x10b0, halthack_curip, 12);
+    if (!memcmp(halthack_curip, halthack_oldip, 12)) {
+        cpu_physical_memory_write_rom(0x10b0, halthack_newip, 12);
+    }
+
+    /* map entire I/O space */
+    register_ioport_read(0, 0x10000, 1, io_spectrum_read, NULL);
+
+    zx_video_init(ram_offset);
+
+    zx_keyboard_init();
+    zx_timer_init();
+
+#ifdef CONFIG_LIBSPECTRUM
+    /* load a snapshot */
+    if (kernel_filename) {
+        libspectrum_id_t type;
+        libspectrum_class_t cls;
+        libspectrum_snap* snap;
+        uint8_t* snapmem;
+        libspectrum_byte* page;
+        int length;
+        int i;
+        if (libspectrum_init() != LIBSPECTRUM_ERROR_NONE ||
+            libspectrum_identify_file(&type, kernel_filename, NULL, 0) != LIBSPECTRUM_ERROR_NONE ||
+            libspectrum_identify_class(&cls, type) != LIBSPECTRUM_ERROR_NONE) {
+            fprintf(stderr, "%s: libspectrum error\n", __FUNCTION__);
+            exit(1);
+        }
+        snap = libspectrum_snap_alloc();
+        if (cls != LIBSPECTRUM_CLASS_SNAPSHOT) {
+            fprintf(stderr, "%s: %s is not a snapshot\n", __FUNCTION__, kernel_filename);
+            exit(1);
+        }
+        snapmem = qemu_mallocz(0x10000);
+        length = load_image(kernel_filename, snapmem);
+        //printf("loaded %d bytes from %s\n",length, kernel_filename);
+        if (libspectrum_snap_read(snap, snapmem, length, type, NULL) != LIBSPECTRUM_ERROR_NONE) {
+            fprintf(stderr, "%s: failed to load snapshot %s\n", __FUNCTION__, kernel_filename);
+            exit(1);
+        }
+        //printf("snap pc = %d\n",libspectrum_snap_pc(snap));
+
+        /* fill memory */
+        page = libspectrum_snap_pages(snap, 5);
+        for (i = 0x4000; i < 0x8000; i++) {
+            //printf("storing 0x%x in 0x%x\n",page[i-0x4000],i);
+            stb_phys(i, page[i - 0x4000]);
+        }
+        page = libspectrum_snap_pages(snap, 2);
+        for (i = 0x8000; i < 0xc000; i++) {
+            stb_phys(i, page[i - 0x8000]);
+        }
+        page = libspectrum_snap_pages(snap, 0);
+        for (i = 0xc000; i < 0x10000; i++) {
+            stb_phys(i, page[i - 0xc000]);
+        }
+
+        /* restore registers */
+        env->regs[R_A] = libspectrum_snap_a(snap);
+        env->regs[R_F] = libspectrum_snap_f(snap);
+        env->regs[R_BC] = libspectrum_snap_bc(snap);
+        env->regs[R_DE] = libspectrum_snap_de(snap);
+        env->regs[R_HL] = libspectrum_snap_hl(snap);
+        env->regs[R_AX] = libspectrum_snap_a_(snap);
+        env->regs[R_FX] = libspectrum_snap_f_(snap);
+        env->regs[R_BCX] = libspectrum_snap_bc_(snap);
+        env->regs[R_DEX] = libspectrum_snap_de_(snap);
+        env->regs[R_HLX] = libspectrum_snap_hl_(snap);
+        env->regs[R_IX] = libspectrum_snap_ix(snap);
+        env->regs[R_IY] = libspectrum_snap_iy(snap);
+        env->regs[R_I] = libspectrum_snap_i(snap);
+        env->regs[R_R] = libspectrum_snap_r(snap);
+        env->regs[R_SP] = libspectrum_snap_sp(snap);
+        env->pc = libspectrum_snap_pc(snap);
+        env->iff1 = libspectrum_snap_iff1(snap);
+        env->iff2 = libspectrum_snap_iff2(snap);
+        env->imode = libspectrum_snap_im(snap);
+
+        qemu_free(snapmem);
+    }
+#endif
+}
+
+static QEMUMachine zxspec_machine = {
+    .name = "zxspec",
+    .desc = "ZX Spectrum",
+    .init = zx_spectrum_init,
+    .is_default = 1,
+};
+
+static void zxspec_machine_init(void) {
+    qemu_register_machine(&zxspec_machine);
+}
+
+machine_init(zxspec_machine_init);
diff --git a/hw/zx_video.c b/hw/zx_video.c
new file mode 100644
index 0000000..6015608
--- /dev/null
+++ b/hw/zx_video.c
@@ -0,0 +1,375 @@
+/*
+ * ZX Spectrum Video Emulation
+ *
+ * Copyright (c) 2007-2009 Stuart Brady <stuart.brady@gmail.com>
+ *
+ * Uses code from VGA emulation
+ *   Copyright (c) 2003 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "hw.h"
+#include "isa.h"
+#include "console.h"
+#include "zx_video.h"
+#include "pixel_ops.h"
+#include "pixel_ops_dup.h"
+
+typedef unsigned int rgb_to_pixel_dup_func(unsigned int r,
+                                           unsigned int g,
+                                           unsigned int b);
+
+typedef struct {
+    DisplayState *ds;
+    uint8_t *vram_ptr;
+
+    int bwidth;
+    int bheight;
+    int swidth;
+    int sheight;
+    int twidth;
+    int theight;
+
+    int border;
+    int prevborder;
+
+    int flash;
+    int flashcount;
+
+    int invalidate;
+    uint32_t palette[16];
+    rgb_to_pixel_dup_func *rgb_to_pixel;
+} ZXVState;
+
+static const uint32_t zx_cols[16] = {
+    0x00000000, /*  0: Black          */
+    0x000000c0, /*  1: Blue           */
+    0x00c00000, /*  2: Red            */
+    0x00c000c0, /*  3: Magenta        */
+    0x0000c000, /*  4: Green          */
+    0x0000c0c0, /*  5: Cyan           */
+    0x00c0c000, /*  6: Yellow         */
+    0x00c0c0c0, /*  7: Light grey     */
+
+    0x00000000, /*  8: Black          */
+    0x000000ff, /*  9: Bright blue    */
+    0x00ff0000, /* 10: Bright red     */
+    0x00ff00ff, /* 11: Bright magenta */
+    0x0000ff00, /* 12: Bright green   */
+    0x0000ffff, /* 13: Bright cyan    */
+    0x00ffff00, /* 14: Bright yellow  */
+    0x00ffffff, /* 15: White          */
+};
+
+/* copied from vga.c / vga_template.h */
+
+#define cbswap_32(__x) \
+((uint32_t)( \
+                (((uint32_t)(__x) & (uint32_t)0x000000ffUL) << 24) | \
+                (((uint32_t)(__x) & (uint32_t)0x0000ff00UL) <<  8) | \
+                (((uint32_t)(__x) & (uint32_t)0x00ff0000UL) >>  8) | \
+                (((uint32_t)(__x) & (uint32_t)0xff000000UL) >> 24) ))
+
+#ifdef WORDS_BIGENDIAN
+#define PAT(x) (x)
+#else
+#define PAT(x) cbswap_32(x)
+#endif
+
+static const uint32_t dmask16[16] = {
+    PAT(0x00000000),
+    PAT(0x000000ff),
+    PAT(0x0000ff00),
+    PAT(0x0000ffff),
+    PAT(0x00ff0000),
+    PAT(0x00ff00ff),
+    PAT(0x00ffff00),
+    PAT(0x00ffffff),
+    PAT(0xff000000),
+    PAT(0xff0000ff),
+    PAT(0xff00ff00),
+    PAT(0xff00ffff),
+    PAT(0xffff0000),
+    PAT(0xffff00ff),
+    PAT(0xffffff00),
+    PAT(0xffffffff),
+};
+
+static const uint32_t dmask4[4] = {
+    PAT(0x00000000),
+    PAT(0x0000ffff),
+    PAT(0xffff0000),
+    PAT(0xffffffff),
+};
+
+typedef void zx_draw_line_func(uint8_t *d, uint32_t font_data,
+                               uint32_t xorcol, uint32_t bgcol);
+
+#define DEPTH 8
+#include "zx_glyphs.h"
+
+#define DEPTH 16
+#include "zx_glyphs.h"
+
+#define DEPTH 32
+#include "zx_glyphs.h"
+
+enum {
+    zx_pixfmt_8 = 0,
+    zx_pixfmt_15rgb,
+    zx_pixfmt_16rgb,
+    zx_pixfmt_32rgb,
+    zx_pixfmt_32bgr,
+    NB_DEPTHS
+};
+
+static zx_draw_line_func *zx_draw_line_table[NB_DEPTHS] = {
+    zx_draw_glyph_line_8,
+    zx_draw_glyph_line_16,
+    zx_draw_glyph_line_16,
+    zx_draw_glyph_line_32,
+    zx_draw_glyph_line_32,
+};
+
+static rgb_to_pixel_dup_func *rgb_to_pixel_dup_table[NB_DEPTHS] = {
+    rgb_to_pixel8_dup,
+    rgb_to_pixel15_dup,
+    rgb_to_pixel16_dup,
+    rgb_to_pixel32_dup,
+    rgb_to_pixel32bgr_dup,
+};
+
+static inline int get_pixfmt_index(DisplayState *s)
+{
+    switch(ds_get_bits_per_pixel(s)) {
+    default:
+    case 8:
+        return zx_pixfmt_8;
+    case 15:
+        return zx_pixfmt_15rgb;
+    case 16:
+        return zx_pixfmt_16rgb;
+    case 32:
+        if (is_surface_bgr(s->surface)) {
+            return zx_pixfmt_32bgr;
+        } else {
+            return zx_pixfmt_32rgb;
+        }
+    }
+}
+
+/* end of code copied from vga.c / vga_template.h */
+
+static ZXVState *zxvstate;
+
+void zx_video_do_retrace(void)
+{
+    ZXVState *s = zxvstate;
+
+    if (++s->flashcount == 16) {
+        s->flashcount = 0;
+        s->invalidate = 1;
+        s->flash = !s->flash;
+    }
+}
+
+static void zx_draw_scanline(ZXVState *s1, uint8_t *d,
+                             const uint8_t *s, const uint8_t *as)
+{
+    int x, x_incr;
+    zx_draw_line_func *zx_draw_line;
+
+    zx_draw_line = zx_draw_line_table[get_pixfmt_index(s1->ds)];
+    x_incr = (ds_get_bits_per_pixel(s1->ds) + 7) >> 3;
+
+    for (x = 0; x < 32; x++) {
+        int attrib, fg, bg, bright, flash;
+
+        attrib = *as;
+        bright = (attrib & 0x40) >> 3;
+        flash = (attrib & 0x80) && s1->flash;
+        if (flash) {
+            fg = (attrib >> 3) & 0x07;
+            bg = attrib & 0x07;
+        } else {
+            fg = attrib & 0x07;
+            bg = (attrib >> 3) & 0x07;
+        }
+        fg |= bright;
+        bg |= bright;
+
+        zx_draw_line(d, *s, s1->palette[fg] ^ s1->palette[bg], s1->palette[bg]);
+
+        d += 8 * x_incr;
+        s++; as++;
+    }
+}
+
+static void zx_border_row(ZXVState *s, uint8_t *d)
+{
+    int x, x_incr;
+    zx_draw_line_func *zx_draw_line;
+
+    zx_draw_line = zx_draw_line_table[get_pixfmt_index(s->ds)];
+    x_incr = (ds_get_bits_per_pixel(s->ds) + 7) >> 3;
+
+    for (x = 0; x < s->twidth / 8; x++) {
+        zx_draw_line(d, 0xff, s->palette[s->border], 0);
+        d += 8 * x_incr;
+    }
+}
+
+static void zx_border_sides(ZXVState *s, uint8_t *d)
+{
+    int x, x_incr;
+    zx_draw_line_func *zx_draw_line;
+
+    zx_draw_line = zx_draw_line_table[get_pixfmt_index(s->ds)];
+    x_incr = (ds_get_bits_per_pixel(s->ds) + 7) >> 3;
+
+    for (x = 0; x < s->bwidth / 8; x++) {
+        zx_draw_line(d, 0xff, s->palette[s->border], 0);
+        d += 8 * x_incr;
+    }
+    d += s->swidth * x_incr;
+    for (x = 0; x < s->bwidth / 8; x++) {
+        zx_draw_line(d, 0xff, s->palette[s->border], 0);
+        d += 8 * x_incr;
+    }
+}
+
+static void update_palette(ZXVState *s)
+{
+    int i, r, g, b;
+    for(i = 0; i < 16; i++) {
+        r = (zx_cols[i] >> 16) & 0xff;
+        g = (zx_cols[i] >> 8) & 0xff;
+        b = zx_cols[i] & 0xff;
+        s->palette[i] = s->rgb_to_pixel(r, g, b);
+    }
+}
+
+static void zx_update_display(void *opaque)
+{
+    int y;
+    uint8_t *d;
+    ZXVState *s = (ZXVState *)opaque;
+    uint32_t addr, attrib;
+    int x_incr;
+    int dirty = s->invalidate;
+    static int inited = 0;
+
+    x_incr = (ds_get_bits_per_pixel(s->ds) + 7) >> 3;
+
+    if (unlikely(inited == 0)) {
+        s->rgb_to_pixel = rgb_to_pixel_dup_table[get_pixfmt_index(s->ds)];
+        update_palette(s);
+        inited = 1;
+    }
+
+    if (unlikely(ds_get_width(s->ds) != s->twidth ||
+                 ds_get_height(s->ds) != s->theight)) {
+        qemu_console_resize(s->ds, s->twidth, s->theight);
+        s->invalidate = 1;
+        s->prevborder = -1;
+    }
+
+    if (!dirty) {
+        for (addr = 0; addr < 0x1b00; addr += TARGET_PAGE_SIZE) {
+            if (cpu_physical_memory_get_dirty(addr, VGA_DIRTY_FLAG)) {
+                dirty = 1;
+            }
+        }
+    }
+
+    if (dirty) {
+        d = ds_get_data(s->ds);
+        d += s->bheight * ds_get_linesize(s->ds);
+        d += s->bwidth * x_incr;
+
+        for (y = 0; y < 192; y++) {
+            addr = ((y & 0x07) << 8) | ((y & 0x38) << 2) | ((y & 0xc0) << 5);
+            attrib = 0x1800 | ((y & 0xf8) << 2);
+            zx_draw_scanline(s, d, s->vram_ptr + addr, s->vram_ptr + attrib);
+            d += ds_get_linesize(s->ds);
+        }
+
+        s->invalidate = 0;
+        cpu_physical_memory_reset_dirty(0, 0x1b00, VGA_DIRTY_FLAG);
+    }
+
+    if (s->border != s->prevborder) {
+        d = ds_get_data(s->ds);
+        for (y = 0; y < s->bheight; y++) {
+            zx_border_row(s, d + (y * ds_get_linesize(s->ds)));
+        }
+        for (y = s->bheight; y < s->theight - s->bheight; y++) {
+            zx_border_sides(s, d + (y * ds_get_linesize(s->ds)));
+        }
+        for (y = s->theight - s->bheight; y < s->theight; y++) {
+            zx_border_row(s, d + (y * ds_get_linesize(s->ds)));
+        }
+        s->prevborder = s->border;
+    }
+
+    dpy_update(s->ds, 0, 0, s->twidth, s->theight);
+}
+
+static void zx_invalidate_display(void *opaque)
+{
+    ZXVState *s = (ZXVState *)opaque;
+    s->invalidate = 1;
+    s->prevborder = -1;
+}
+
+static void io_spectrum_write(void *opaque, uint32_t addr, uint32_t data)
+{
+    ZXVState *s = (ZXVState *)opaque;
+
+    /* port xxfe */
+    if (!(addr & 1)) {
+        s->border = data & 0x07;
+    }
+};
+
+void zx_video_init(ram_addr_t zx_vram_offset)
+{
+    ZXVState *s = qemu_mallocz(sizeof(ZXVState));
+    zxvstate = s;
+    s->invalidate = 1;
+    s->prevborder = -1;
+    s->flashcount = 0;
+    s->vram_ptr = qemu_get_ram_ptr(zx_vram_offset);
+
+    s->ds = graphic_console_init(zx_update_display, zx_invalidate_display,
+                                 NULL, NULL, s);
+
+    s->bwidth = 32;
+    s->bheight = 24;
+    s->swidth = 256;
+    s->sheight = 192;
+    s->twidth = s->swidth + s->bwidth * 2;
+    s->theight = s->sheight + s->bheight * 2;
+    s->border = 0;
+    s->flash = 0;
+
+    /* ZX Spectrum ULA */
+    register_ioport_write(0, 0x10000, 1, io_spectrum_write, s);
+}
diff --git a/hw/zx_video.h b/hw/zx_video.h
new file mode 100644
index 0000000..b53dc36
--- /dev/null
+++ b/hw/zx_video.h
@@ -0,0 +1,8 @@
+#ifndef HW_ZX_VIDEO_H
+#define HW_ZX_VIDEO_H
+/* ZX Spectrum Video */
+
+void zx_video_init(ram_addr_t zx_vram_offset);
+void zx_video_do_retrace(void);
+
+#endif
diff --git a/target-z80/TODO b/target-z80/TODO
new file mode 100644
index 0000000..7698cd2
--- /dev/null
+++ b/target-z80/TODO
@@ -0,0 +1,13 @@
+TODO
+----
+
+- remove unused x86 code
+- allow execution from video RAM
+- don't accept interrupts immediately after EI
+- emulate I and R registers
+- GDB support
+- CP/M emulation
+- R800 emulation:
+  - TST instruction
+  - MULUB/MULUW flags
+  - flags 3 and 5
diff --git a/target-z80/cpu.h b/target-z80/cpu.h
new file mode 100644
index 0000000..f724e16
--- /dev/null
+++ b/target-z80/cpu.h
@@ -0,0 +1,259 @@
+/*
+ * Z80 virtual CPU header
+ *
+ *  Copyright (c) 2007-2009 Stuart Brady <stuart.brady@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA  02110-1301 USA
+ */
+#ifndef CPU_Z80_H
+#define CPU_Z80_H
+
+#include "config.h"
+
+#define TARGET_LONG_BITS 32
+
+/* target supports implicit self modifying code */
+#define TARGET_HAS_SMC
+/* support for self modifying code even if the modified instruction is
+   close to the modifying instruction */
+#define TARGET_HAS_PRECISE_SMC
+
+#define TARGET_HAS_ICE 1
+
+#define ELF_MACHINE	EM_NONE
+
+#define CPUState struct CPUZ80State
+
+#include "cpu-defs.h"
+
+#include "softfloat.h"
+
+/* Z80 registers */
+
+#define R_A     0
+#define R_F     1
+
+#define R_BC    2
+#define R_DE    3
+#define R_HL    4
+#define R_IX    5
+#define R_IY    6
+#define R_SP    7
+
+#define R_I     8
+#define R_R     9
+
+#define R_AX    10
+#define R_FX    11
+#define R_BCX   12
+#define R_DEX   13
+#define R_HLX   14
+
+#define CPU_NB_REGS 15
+
+/* flags masks */
+#define CC_C   	0x0001
+#define CC_N    0x0002
+#define CC_P 	0x0004
+#define CC_X 	0x0008
+#define CC_H	0x0010
+#define CC_Y 	0x0020
+#define CC_Z	0x0040
+#define CC_S    0x0080
+
+/* hidden flags - used internally by qemu to represent additionnal cpu
+   states. Only the CPL and INHIBIT_IRQ are not redundant. We avoid
+   using the IOPL_MASK, TF_MASK and VM_MASK bit position to ease oring
+   with eflags. */
+/* current cpl */
+#define HF_CPL_SHIFT         0
+/* true if soft mmu is being used */
+#define HF_SOFTMMU_SHIFT     2
+/* true if hardware interrupts must be disabled for next instruction */
+#define HF_INHIBIT_IRQ_SHIFT 3
+/* 16 or 32 segments */
+#define HF_CS32_SHIFT        4
+#define HF_SS32_SHIFT        5
+/* zero base for DS, ES and SS : can be '0' only in 32 bit CS segment */
+#define HF_ADDSEG_SHIFT      6
+/* copy of CR0.PE (protected mode) */
+#define HF_PE_SHIFT          7
+#define HF_TF_SHIFT          8 /* must be same as eflags */
+#define HF_MP_SHIFT          9 /* the order must be MP, EM, TS */
+#define HF_EM_SHIFT         10
+#define HF_TS_SHIFT         11
+#define HF_IOPL_SHIFT       12 /* must be same as eflags */
+#define HF_LMA_SHIFT        14 /* only used on x86_64: long mode active */
+#define HF_CS64_SHIFT       15 /* only used on x86_64: 64 bit code segment  */
+#define HF_OSFXSR_SHIFT     16 /* CR4.OSFXSR */
+#define HF_VM_SHIFT         17 /* must be same as eflags */
+#define HF_SMM_SHIFT        19 /* CPU in SMM mode */
+
+#define HF_CPL_MASK          (3 << HF_CPL_SHIFT)
+#define HF_SOFTMMU_MASK      (1 << HF_SOFTMMU_SHIFT)
+#define HF_INHIBIT_IRQ_MASK  (1 << HF_INHIBIT_IRQ_SHIFT)
+#define HF_CS32_MASK         (1 << HF_CS32_SHIFT)
+#define HF_SS32_MASK         (1 << HF_SS32_SHIFT)
+#define HF_ADDSEG_MASK       (1 << HF_ADDSEG_SHIFT)
+#define HF_PE_MASK           (1 << HF_PE_SHIFT)
+#define HF_TF_MASK           (1 << HF_TF_SHIFT)
+#define HF_MP_MASK           (1 << HF_MP_SHIFT)
+#define HF_EM_MASK           (1 << HF_EM_SHIFT)
+#define HF_TS_MASK           (1 << HF_TS_SHIFT)
+#define HF_LMA_MASK          (1 << HF_LMA_SHIFT)
+#define HF_CS64_MASK         (1 << HF_CS64_SHIFT)
+#define HF_OSFXSR_MASK       (1 << HF_OSFXSR_SHIFT)
+#define HF_SMM_MASK          (1 << HF_SMM_SHIFT)
+
+#define EXCP00_DIVZ	0
+#define EXCP01_SSTP	1
+#define EXCP02_NMI	2
+#define EXCP03_INT3	3
+#define EXCP04_INTO	4
+#define EXCP05_BOUND	5
+#define EXCP06_ILLOP	6
+#define EXCP07_PREX	7
+#define EXCP08_DBLE	8
+#define EXCP09_XERR	9
+#define EXCP0A_TSS	10
+#define EXCP0B_NOSEG	11
+#define EXCP0C_STACK	12
+#define EXCP0D_GPF	13
+#define EXCP0E_PAGE	14
+#define EXCP10_COPR	16
+#define EXCP11_ALGN	17
+#define EXCP12_MCHK	18
+
+#define NB_MMU_MODES 2
+
+typedef struct CPUZ80State {
+#if TARGET_LONG_BITS > HOST_LONG_BITS
+    /* temporaries if we cannot store them in host registers */
+    target_ulong t0, t1;
+#endif
+    target_ulong a0;
+
+    /* Z80 registers */
+    uint16_t pc;
+    /* not sure if this is messy: */
+    target_ulong regs[CPU_NB_REGS];
+
+    int iff1;
+    int iff2;
+    int imode;
+
+    int ir;
+
+    /* standard registers */
+    target_ulong eflags; /* eflags register. During CPU emulation, CC
+                        flags are set to zero because they are
+                        stored elsewhere */
+
+    /* emulator internal eflags handling */
+    uint32_t hflags; /* hidden flags, see HF_xxx constants */
+
+    target_ulong cr[5]; /* NOTE: cr1 is unused */
+
+    /* sysenter registers */
+    uint64_t efer;
+    uint64_t star;
+
+    uint64_t pat;
+
+    /* exception/interrupt handling */
+    int error_code;
+    int exception_is_int;
+    target_ulong exception_next_pc;
+    target_ulong dr[8]; /* debug registers */
+    uint32_t smbase;
+
+    CPU_COMMON
+
+    int model;
+
+    /* in order to simplify APIC support, we leave this pointer to the
+       user */
+    struct APICState *apic_state;
+} CPUZ80State;
+
+CPUZ80State *cpu_z80_init(const char *cpu_model);
+void z80_translate_init(void);
+int cpu_z80_exec(CPUZ80State *s);
+void cpu_z80_close(CPUZ80State *s);
+int cpu_get_pic_interrupt(CPUZ80State *s);
+
+/* wrapper, just in case memory mappings must be changed */
+static inline void cpu_z80_set_cpl(CPUZ80State *s, int cpl)
+{
+#if HF_CPL_MASK == 3
+    s->hflags = (s->hflags & ~HF_CPL_MASK) | cpl;
+#else
+#error HF_CPL_MASK is hardcoded
+#endif
+}
+
+/* you can call this signal handler from your SIGBUS and SIGSEGV
+   signal handlers to inform the virtual CPU of exceptions. non zero
+   is returned if the signal was handled by the virtual CPU.  */
+struct siginfo;
+int cpu_z80_signal_handler(int host_signum, struct siginfo *info,
+                           void *puc);
+
+uint64_t cpu_get_tsc(CPUZ80State *env);
+
+int cpu_z80_handle_mmu_fault(CPUZ80State *env1, target_ulong address, int rw,
+                             int mmu_idx, int is_softmmu);
+
+void z80_cpu_list(FILE *f, int (*cpu_fprintf)(FILE *f, const char *fmt, ...));
+
+#define Z80_CPU_Z80  1
+#define Z80_CPU_R800 2
+
+#define TARGET_PAGE_BITS 12
+
+#define cpu_init cpu_z80_init
+#define cpu_exec cpu_z80_exec
+#define cpu_gen_code cpu_z80_gen_code
+#define cpu_signal_handler cpu_z80_signal_handler
+#define cpu_list z80_cpu_list
+
+/* MMU modes definitions */
+#define MMU_MODE0_SUFFIX _kernel
+#define MMU_MODE1_SUFFIX _user
+#define MMU_USER_IDX 1
+static inline int cpu_mmu_index (CPUState *env)
+{
+    /* return (env->hflags & HF_CPL_MASK) == 3 ? 1 : 0; */
+    return 0;
+}
+
+#include "cpu-all.h"
+#include "exec-all.h"
+
+static inline void cpu_pc_from_tb(CPUState *env, TranslationBlock *tb)
+{
+    env->pc = tb->pc;
+    env->hflags = tb->flags;
+}
+
+static inline void cpu_get_tb_cpu_state(CPUState *env, target_ulong *pc,
+                                        target_ulong *cs_base, int *flags)
+{
+    *pc = env->pc;
+    *cs_base = 0;
+    *flags = env->hflags;
+}
+
+#endif /* CPU_Z80_H */
diff --git a/target-z80/exec.h b/target-z80/exec.h
new file mode 100644
index 0000000..570afa8
--- /dev/null
+++ b/target-z80/exec.h
@@ -0,0 +1,196 @@
+/*
+ * Z80 execution defines
+ *
+ *  Copyright (c) 2007-2009 Stuart Brady <stuart.brady@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA  02110-1301 USA
+ */
+#include "config.h"
+#include "dyngen-exec.h"
+
+#define TARGET_LONG_BITS 32
+
+#include "cpu-defs.h"
+
+/* at least 4 register variables are defined */
+register struct CPUZ80State *env asm(AREG0);
+
+#if TARGET_LONG_BITS > HOST_LONG_BITS
+
+/* no registers can be used */
+#define T0 (env->t0)
+#define T1 (env->t1)
+
+#else
+
+/* XXX: use unsigned long instead of target_ulong - better code will
+   be generated for 64 bit CPUs */
+register target_ulong T0 asm(AREG1);
+register target_ulong T1 asm(AREG2);
+
+#endif /* ! (TARGET_LONG_BITS > HOST_LONG_BITS) */
+
+#define A0 (env->a0)
+
+#define A   (env->regs[R_A])
+#define F   (env->regs[R_F])
+#define BC  (env->regs[R_BC])
+#define DE  (env->regs[R_DE])
+#define HL  (env->regs[R_HL])
+#define IX  (env->regs[R_IX])
+#define IY  (env->regs[R_IY])
+#define SP  (env->regs[R_SP])
+#define I   (env->regs[R_I])
+#define R   (env->regs[R_R])
+#define AX  (env->regs[R_AX])
+#define FX  (env->regs[R_FX])
+#define BCX (env->regs[R_BCX])
+#define DEX (env->regs[R_DEX])
+#define HLX (env->regs[R_HLX])
+
+#define PC  (env->pc)
+
+#include "cpu.h"
+#include "exec-all.h"
+
+void do_interrupt(CPUZ80State *env);
+void raise_interrupt(int intno, int is_int, int error_code,
+                     int next_eip_addend);
+void raise_exception_err(int exception_index, int error_code);
+void raise_exception(int exception_index);
+
+#if !defined(CONFIG_USER_ONLY)
+
+#include "softmmu_exec.h"
+
+#endif /* !defined(CONFIG_USER_ONLY) */
+
+extern const uint8_t parity_table[256];
+
+static inline void env_to_regs(void)
+{
+#ifdef reg_A
+    A = env->regs[R_A];
+#endif
+#ifdef reg_F
+    F = env->regs[R_F];
+#endif
+#ifdef reg_BC
+    BC = env->regs[R_BC];
+#endif
+#ifdef reg_DE
+    DE = env->regs[R_DE];
+#endif
+#ifdef reg_HL
+    HL = env->regs[R_HL];
+#endif
+#ifdef reg_IX
+    IX = env->regs[R_IX];
+#endif
+#ifdef reg_IY
+    IY = env->regs[R_IY];
+#endif
+#ifdef reg_SP
+    SP = env->regs[R_SP];
+#endif
+#ifdef reg_I
+    I = env->regs[R_I];
+#endif
+#ifdef reg_R
+    R = env->regs[R_R];
+#endif
+#ifdef reg_AX
+    AX = env->regs[R_AX];
+#endif
+#ifdef reg_FX
+    FX = env->regs[R_FX];
+#endif
+#ifdef reg_BCX
+    BCX = env->regs[R_BCX];
+#endif
+#ifdef reg_DEX
+    DEX = env->regs[R_DEX];
+#endif
+#ifdef reg_HLX
+    HLX = env->regs[R_HLX];
+#endif
+}
+
+static inline void regs_to_env(void)
+{
+#ifdef reg_A
+    env->regs[R_A] = A;
+#endif
+#ifdef reg_F
+    env->regs[R_F] = F;
+#endif
+#ifdef reg_BC
+    env->regs[R_BC] = BC;
+#endif
+#ifdef reg_DE
+    env->regs[R_DE] = DE;
+#endif
+#ifdef reg_HL
+    env->regs[R_HL] = HL;
+#endif
+#ifdef reg_IX
+    env->regs[R_IX] = IX;
+#endif
+#ifdef reg_IY
+    env->regs[R_IY] = IY;
+#endif
+#ifdef reg_SP
+    env->regs[R_SP] = SP;
+#endif
+#ifdef reg_I
+    env->regs[R_I] = I;
+#endif
+#ifdef reg_R
+    env->regs[R_R] = R;
+#endif
+#ifdef reg_AX
+    env->regs[R_AX] = AX;
+#endif
+#ifdef reg_FX
+    env->regs[R_FX] = FX;
+#endif
+#ifdef reg_BCX
+    env->regs[R_BCX] = BCX;
+#endif
+#ifdef reg_DEX
+    env->regs[R_DEX] = DEX;
+#endif
+#ifdef reg_HLX
+    env->regs[R_HLX] = HLX;
+#endif
+}
+
+static inline int cpu_has_work(CPUState *env)
+{
+    return env->interrupt_request & CPU_INTERRUPT_HARD;
+}
+
+static inline int cpu_halted(CPUState *env)
+{
+    if (!env->halted) {
+        return 0;
+    }
+    //printf("%s: at PC 0x%x halted == %d, irq %d\n",__FUNCTION__, env->pc, env->halted,env->interrupt_request);
+    if (cpu_has_work(env)) {
+        env->halted = 0;
+        return 0;
+    }
+    return EXCP_HALTED;
+}
diff --git a/target-z80/genreg_template.h b/target-z80/genreg_template.h
new file mode 100644
index 0000000..6be8ac8
--- /dev/null
+++ b/target-z80/genreg_template.h
@@ -0,0 +1,65 @@
+/*
+ * Z80 translation (templates for various register related operations)
+ *
+ *  Copyright (c) 2007-2009 Stuart Brady <stuart.brady@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA  02110-1301 USA
+ */
+
+/* Loads */
+
+static inline void glue(gen_movb_v_,REGHIGH)(TCGv v)
+{
+    tcg_gen_ld8u_tl(v, cpu_env,
+                    offsetof(CPUState, regs[glue(R_,REGPAIR)]) +
+                                            BYTE_OFFSET(cpu_env->regs[], 1));
+}
+
+static inline void glue(gen_movb_v_,REGLOW)(TCGv v)
+{
+    tcg_gen_ld8u_tl(v, cpu_env,
+                    offsetof(CPUState, regs[glue(R_,REGPAIR)]) +
+                                            BYTE_OFFSET(cpu_env->regs[], 0));
+}
+
+static inline void glue(gen_movw_v_,REGPAIR)(TCGv v)
+{
+    tcg_gen_ld16u_tl(v, cpu_env,
+                     offsetof(CPUState, regs[glue(R_,REGPAIR)]) +
+                                             WORD_OFFSET(cpu_env->regs[], 0));
+}
+
+/* Stores */
+
+static inline void glue(glue(gen_movb_,REGHIGH),_v)(TCGv v)
+{
+    tcg_gen_st8_tl(v, cpu_env,
+                   offsetof(CPUState, regs[glue(R_,REGPAIR)]) +
+                                           BYTE_OFFSET(cpu_env->regs[], 1));
+}
+
+static inline void glue(glue(gen_movb_,REGLOW),_v)(TCGv v)
+{
+    tcg_gen_st8_tl(v, cpu_env,
+                   offsetof(CPUState, regs[glue(R_,REGPAIR)]) +
+                                           BYTE_OFFSET(cpu_env->regs[], 0));
+}
+
+static inline void glue(glue(gen_movw_,REGPAIR),_v)(TCGv v)
+{
+    tcg_gen_st16_tl(v, cpu_env,
+                    offsetof(CPUState, regs[glue(R_,REGPAIR)]) +
+                                            WORD_OFFSET(cpu_env->regs[], 0));
+}
diff --git a/target-z80/genreg_template_af.h b/target-z80/genreg_template_af.h
new file mode 100644
index 0000000..2bf326c
--- /dev/null
+++ b/target-z80/genreg_template_af.h
@@ -0,0 +1,83 @@
+/*
+ * Z80 translation (templates for various register related operations)
+ *
+ *  Copyright (c) 2007-2009 Stuart Brady <stuart.brady@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA  02110-1301 USA
+ */
+
+/* Loads */
+
+static inline void glue(gen_movw_v_,REGPAIR)(TCGv v)
+{
+    TCGv tmp1 = tcg_temp_new();
+
+    tcg_gen_ld8u_tl(tmp1, cpu_env,
+                    offsetof(CPUState, regs[glue(R_,REGHIGH)]) +
+                                            BYTE_OFFSET(cpu_env->regs[], 0));
+    tcg_gen_shli_tl(tmp1, tmp1, 8);
+    tcg_gen_ld8u_tl(v, cpu_env,
+                    offsetof(CPUState, regs[glue(R_,REGLOW)]) +
+                                            BYTE_OFFSET(cpu_env->regs[], 0));
+    tcg_gen_or_tl(v, tmp1, v);
+
+    tcg_temp_free(tmp1);
+}
+
+static inline void glue(gen_movb_v_,REGHIGH)(TCGv v)
+{
+    tcg_gen_ld8u_tl(v, cpu_env,
+                    offsetof(CPUState, regs[glue(R_,REGHIGH)]) +
+                                            BYTE_OFFSET(cpu_env->regs[], 0));
+}
+
+static inline void glue(gen_movb_v_,REGLOW)(TCGv v)
+{
+    tcg_gen_ld8u_tl(v, cpu_env,
+                    offsetof(CPUState, regs[glue(R_,REGLOW)]) +
+                                            BYTE_OFFSET(cpu_env->regs[], 0));
+}
+
+/* Stores */
+
+static inline void glue(glue(gen_movw_,REGPAIR),_v)(TCGv v)
+{
+    TCGv tmp1 = tcg_temp_new();
+
+    tcg_gen_shri_tl(tmp1, v, 8);
+    tcg_gen_st8_tl(tmp1, cpu_env,
+                   offsetof(CPUState, regs[glue(R_,REGHIGH)]) +
+                                           BYTE_OFFSET(cpu_env->regs[], 0));
+    tcg_gen_ext8u_tl(tmp1, v);
+    tcg_gen_st8_tl(tmp1, cpu_env,
+                   offsetof(CPUState, regs[glue(R_,REGLOW)]) +
+                                           BYTE_OFFSET(cpu_env->regs[], 0));
+
+    tcg_temp_free(tmp1);
+}
+
+static inline void glue(glue(gen_movb_,REGHIGH),_v)(TCGv v)
+{
+    tcg_gen_st8_tl(v, cpu_env,
+                   offsetof(CPUState, regs[glue(R_,REGHIGH)]) +
+                                           BYTE_OFFSET(cpu_env->regs[], 0));
+}
+
+static inline void glue(glue(gen_movb_,REGLOW),_v)(TCGv v)
+{
+    tcg_gen_st8_tl(v, cpu_env,
+                   offsetof(CPUState, regs[glue(R_,REGLOW)]) +
+                                           BYTE_OFFSET(cpu_env->regs[], 0));
+}
diff --git a/target-z80/helper.c b/target-z80/helper.c
new file mode 100644
index 0000000..4022f94
--- /dev/null
+++ b/target-z80/helper.c
@@ -0,0 +1,211 @@
+/*
+ * Z80 helpers (without register variable usage)
+ *
+ *  Copyright (c) 2007 Stuart Brady <stuart.brady@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA  02110-1301 USA
+ */
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+#include <signal.h>
+
+#include "cpu.h"
+#include "exec-all.h"
+#include "qemu-common.h"
+
+//#define DEBUG_MMU
+
+static int cpu_z80_find_by_name(const char *name);
+
+CPUZ80State *cpu_z80_init(const char *model)
+{
+    CPUZ80State *env;
+    static int inited;
+    int id;
+
+    id = cpu_z80_find_by_name(model);
+    if (id == 0) {
+        return NULL;
+    }
+    env = qemu_mallocz(sizeof(CPUZ80State));
+    cpu_exec_init(env);
+
+    /* init various static tables */
+    if (!inited) {
+        inited = 1;
+        z80_translate_init();
+    }
+    env->model = id;
+    cpu_reset(env);
+    qemu_init_vcpu(env);
+    return env;
+}
+
+typedef struct {
+    int id;
+    const char *name;
+} Z80CPUModel;
+
+static const Z80CPUModel z80_cpu_names[] = {
+    { Z80_CPU_Z80,  "z80" },
+    { Z80_CPU_R800, "r800" },
+    { 0, NULL }
+};
+
+void z80_cpu_list(FILE *f, int (*cpu_fprintf)(FILE *f, const char *fmt, ...))
+{
+    int i;
+
+    (*cpu_fprintf)(f, "Available CPUs:\n");
+    for (i = 0; z80_cpu_names[i].name; i++) {
+        (*cpu_fprintf)(f, "  %s\n", z80_cpu_names[i].name);
+    }
+}
+
+/* return 0 if not found */
+static int cpu_z80_find_by_name(const char *name)
+{
+    int i;
+    int id;
+
+    id = 0;
+    for (i = 0; z80_cpu_names[i].name; i++) {
+        if (strcmp(name, z80_cpu_names[i].name) == 0) {
+            id = z80_cpu_names[i].id;
+            break;
+        }
+    }
+    return id;
+}
+
+/* NOTE: must be called outside the CPU execute loop */
+void cpu_reset(CPUZ80State *env)
+{
+    if (qemu_loglevel_mask(CPU_LOG_RESET)) {
+        qemu_log("CPU Reset (CPU %d)\n", env->cpu_index);
+        log_cpu_state(env, 0);
+    }
+
+    memset(env, 0, offsetof(CPUZ80State, breakpoints));
+
+    tlb_flush(env, 1);
+
+    /* init to reset state */
+
+#ifdef CONFIG_SOFTMMU
+    env->hflags |= HF_SOFTMMU_MASK;
+#endif
+
+    env->pc = 0x0000;
+    env->iff1 = 0;
+    env->iff2 = 0;
+    env->imode = 0;
+    env->regs[R_A] = 0xff;
+    env->regs[R_F] = 0xff;
+    env->regs[R_SP] = 0xffff;
+}
+
+void cpu_z80_close(CPUZ80State *env)
+{
+    free(env);
+}
+
+/***********************************************************/
+/* x86 debug */
+
+void cpu_dump_state(CPUState *env, FILE *f,
+                    int (*cpu_fprintf)(FILE *f, const char *fmt, ...),
+                    int flags)
+{
+    int fl = env->regs[R_F];
+
+    cpu_fprintf(f, "AF =%04x BC =%04x DE =%04x HL =%04x IX=%04x\n"
+                   "AF'=%04x BC'=%04x DE'=%04x HL'=%04x IY=%04x\n"
+                   "PC =%04x SP =%04x F=[%c%c%c%c%c%c%c%c]\n"
+                   "IM=%i IFF1=%i IFF2=%i I=%02x R=%02x\n",
+                   (env->regs[R_A] << 8) | env->regs[R_F],
+                   env->regs[R_BC],
+                   env->regs[R_DE],
+                   env->regs[R_HL],
+                   env->regs[R_IX],
+                   (env->regs[R_AX] << 8) | env->regs[R_FX],
+                   env->regs[R_BCX],
+                   env->regs[R_DEX],
+                   env->regs[R_HLX],
+                   env->regs[R_IY],
+                   env->pc, /* pc == -1 ? env->pc : pc, */
+                   env->regs[R_SP],
+                   fl & 0x80 ? 'S' : '-',
+                   fl & 0x40 ? 'Z' : '-',
+                   fl & 0x20 ? 'Y' : '-',
+                   fl & 0x10 ? 'H' : '-',
+                   fl & 0x08 ? 'X' : '-',
+                   fl & 0x04 ? 'P' : '-',
+                   fl & 0x02 ? 'N' : '-',
+                   fl & 0x01 ? 'C' : '-',
+                   env->imode, env->iff1, env->iff2, env->regs[R_I], env->regs[R_R]);
+}
+
+/***********************************************************/
+static void cpu_z80_flush_tlb(CPUZ80State *env, target_ulong addr)
+{
+    tlb_flush_page(env, addr);
+}
+
+/* return value:
+   -1 = cannot handle fault
+   0  = nothing more to do
+   1  = generate PF fault
+   2  = soft MMU activation required for this block
+*/
+int cpu_z80_handle_mmu_fault(CPUZ80State *env, target_ulong addr,
+                             int is_write1, int is_user, int is_softmmu)
+{
+    int prot, page_size, ret, is_write;
+    unsigned long paddr, page_offset;
+    target_ulong vaddr, virt_addr;
+
+#if defined(DEBUG_MMU)
+    printf("MMU fault: addr=" TARGET_FMT_lx " w=%d u=%d pc=" TARGET_FMT_lx "\n",
+           addr, is_write1, is_user, env->pc);
+#endif
+    is_write = is_write1 & 1;
+
+    virt_addr = addr & TARGET_PAGE_MASK;
+    prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
+    page_size = TARGET_PAGE_SIZE;
+
+    page_offset = (addr & TARGET_PAGE_MASK) & (page_size - 1);
+    paddr = (addr & TARGET_PAGE_MASK) + page_offset;
+    vaddr = virt_addr + page_offset;
+
+    ret = tlb_set_page_exec(env, vaddr, paddr, prot, is_user, is_softmmu);
+    return ret;
+}
+
+target_phys_addr_t cpu_get_phys_page_debug(CPUState *env, target_ulong addr)
+{
+    uint32_t pte, paddr, page_offset, page_size;
+
+    pte = addr;
+    page_size = TARGET_PAGE_SIZE;
+
+    page_offset = (addr & TARGET_PAGE_MASK) & (page_size - 1);
+    paddr = (pte & TARGET_PAGE_MASK) + page_offset;
+    return paddr;
+}
diff --git a/target-z80/helper.h b/target-z80/helper.h
new file mode 100644
index 0000000..0784e81
--- /dev/null
+++ b/target-z80/helper.h
@@ -0,0 +1,89 @@
+#include "def-helper.h"
+
+DEF_HELPER_0(debug, void)
+DEF_HELPER_1(raise_exception, void, i32)
+DEF_HELPER_0(set_inhibit_irq, void)
+DEF_HELPER_0(reset_inhibit_irq, void)
+
+DEF_HELPER_1(movl_pc_im, void, i32)
+
+DEF_HELPER_0(halt, void)
+
+/* In / Out */
+DEF_HELPER_1(in_T0_im, void, i32)
+DEF_HELPER_0(in_T0_bc_cc, void)
+DEF_HELPER_1(out_T0_im, void, i32)
+DEF_HELPER_0(out_T0_bc, void)
+
+/* Misc */
+DEF_HELPER_1(bit_T0, void, i32)
+DEF_HELPER_0(jmp_T0, void)
+DEF_HELPER_2(djnz, void, i32, i32)
+
+/* 8-bit arithmetic */
+DEF_HELPER_0(add_cc, void)
+DEF_HELPER_0(adc_cc, void)
+DEF_HELPER_0(sub_cc, void)
+DEF_HELPER_0(sbc_cc, void)
+DEF_HELPER_0(and_cc, void)
+DEF_HELPER_0(xor_cc, void)
+DEF_HELPER_0(or_cc, void)
+DEF_HELPER_0(cp_cc, void)
+
+/* Rotation/shifts */
+DEF_HELPER_0(rlc_T0_cc, void)
+DEF_HELPER_0(rrc_T0_cc, void)
+DEF_HELPER_0(rl_T0_cc, void)
+DEF_HELPER_0(rr_T0_cc, void)
+DEF_HELPER_0(sla_T0_cc, void)
+DEF_HELPER_0(sra_T0_cc, void)
+DEF_HELPER_0(sll_T0_cc, void)
+DEF_HELPER_0(srl_T0_cc, void)
+DEF_HELPER_0(rld_cc, void)
+DEF_HELPER_0(rrd_cc, void)
+
+/* Block instructions */
+DEF_HELPER_0(bli_ld_inc_cc, void)
+DEF_HELPER_0(bli_ld_dec_cc, void)
+DEF_HELPER_1(bli_ld_rep, void, i32)
+DEF_HELPER_0(bli_cp_cc, void)
+DEF_HELPER_0(bli_cp_inc_cc, void)
+DEF_HELPER_0(bli_cp_dec_cc, void)
+DEF_HELPER_1(bli_cp_rep, void, i32)
+DEF_HELPER_0(bli_io_inc, void)
+DEF_HELPER_0(bli_io_dec, void)
+DEF_HELPER_1(bli_io_rep, void, i32)
+
+/* Misc */
+DEF_HELPER_0(rlca_cc, void)
+DEF_HELPER_0(rrca_cc, void)
+DEF_HELPER_0(rla_cc, void)
+DEF_HELPER_0(rra_cc, void)
+DEF_HELPER_0(daa_cc, void)
+DEF_HELPER_0(cpl_cc, void)
+DEF_HELPER_0(scf_cc, void)
+DEF_HELPER_0(ccf_cc, void)
+DEF_HELPER_0(neg_cc, void)
+
+/* 16-bit arithmetic */
+DEF_HELPER_0(sbcw_T0_T1_cc, void)
+DEF_HELPER_0(addw_T0_T1_cc, void)
+DEF_HELPER_0(adcw_T0_T1_cc, void)
+DEF_HELPER_0(incb_T0_cc, void)
+DEF_HELPER_0(decb_T0_cc, void)
+
+/* Interrupt handling / IR registers */
+DEF_HELPER_1(imode, void, i32)
+DEF_HELPER_0(ei, void)
+DEF_HELPER_0(di, void)
+DEF_HELPER_0(ri, void)
+DEF_HELPER_0(ld_R_A, void)
+DEF_HELPER_0(ld_I_A, void)
+DEF_HELPER_0(ld_A_R, void)
+DEF_HELPER_0(ld_A_I, void)
+
+/* R800 */
+DEF_HELPER_0(mulub_cc, void)
+DEF_HELPER_0(muluw_cc, void)
+
+#include "def-helper.h"
diff --git a/target-z80/machine.c b/target-z80/machine.c
new file mode 100644
index 0000000..1be1c35
--- /dev/null
+++ b/target-z80/machine.c
@@ -0,0 +1,11 @@
+#include "hw/hw.h"
+#include "hw/boards.h"
+
+void cpu_save(QEMUFile *f, void *opaque)
+{
+}
+
+int cpu_load(QEMUFile *f, void *opaque, int version_id)
+{
+    return 0;
+}
diff --git a/target-z80/op_helper.c b/target-z80/op_helper.c
new file mode 100644
index 0000000..899520c
--- /dev/null
+++ b/target-z80/op_helper.c
@@ -0,0 +1,947 @@
+/*
+ * Z80 helpers
+ *
+ *  Copyright (c) 2007 Stuart Brady <stuart.brady@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA  02110-1301 USA
+ */
+#include "exec.h"
+#include "helper.h"
+
+const uint8_t parity_table[256] = {
+    CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
+    0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
+    0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
+    CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
+    0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
+    CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
+    CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
+    0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
+    0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
+    CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
+    CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
+    0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
+    CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
+    0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
+    0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
+    CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
+    0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
+    CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
+    CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
+    0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
+    CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
+    0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
+    0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
+    CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
+    CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
+    0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
+    0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
+    CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
+    0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
+    CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
+    CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
+    0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
+};
+
+void do_interrupt(CPUZ80State *env)
+{
+// printf("z80: do_interrupt()\n");
+
+    if (!env->iff1) {
+        return;
+    }
+
+    env->iff1 = 0;
+    env->iff2 = 0; /* XXX: Unchanged for NMI */
+
+    {
+        target_ulong sp;
+        sp = (uint16_t)(env->regs[R_SP] - 2);
+        env->regs[R_SP] = sp;
+        stw_kernel(sp, env->pc);
+    }
+
+    /* IM0 = execute data on bus (0xff == rst $38) */
+    /* IM1 = execute rst $38 (ROM uses this)*/
+    /* IM2 = indirect jump -- address is held at (I << 8) | DATA */
+
+    /* value on data bus is 0xff for the zx spectrum */
+
+    /* when an interrupt occurs, iff1 and iff2 are reset, disabling interrupts */
+    /* when an NMI occurs, iff1 is reset. iff2 is left unchanged */
+
+    uint8_t d;
+    switch (env->imode) {
+    case 0:
+        /* XXX: assuming 0xff on data bus */
+    case 1:
+        env->pc = 0x0038;
+        break;
+    case 2:
+        /* XXX: assuming 0xff on data bus */
+        d = 0xff;
+        env->pc = lduw_kernel((env->regs[R_I] << 8) | d);
+        break;
+    }
+}
+
+/*
+ * Signal an interruption. It is executed in the main CPU loop.
+ * is_int is TRUE if coming from the int instruction. next_eip is the
+ * EIP value AFTER the interrupt instruction. It is only relevant if
+ * is_int is TRUE.
+ */
+void raise_interrupt(int intno, int is_int, int error_code,
+                     int next_eip_addend)
+{
+    env->exception_index = intno;
+    env->error_code = error_code;
+    env->exception_is_int = is_int;
+    env->exception_next_pc = env->pc + next_eip_addend;
+    cpu_loop_exit();
+}
+
+/* same as raise_exception_err, but do not restore global registers */
+static void raise_exception_err_norestore(int exception_index, int error_code)
+{
+    env->exception_index = exception_index;
+    env->error_code = error_code;
+    env->exception_is_int = 0;
+    env->exception_next_pc = 0;
+    longjmp(env->jmp_env, 1);
+}
+
+/* shortcuts to generate exceptions */
+
+void (raise_exception_err)(int exception_index, int error_code)
+{
+    raise_interrupt(exception_index, 0, error_code, 0);
+}
+
+void raise_exception(int exception_index)
+{
+    raise_interrupt(exception_index, 0, 0, 0);
+}
+
+void HELPER(debug)(void)
+{
+    env->exception_index = EXCP_DEBUG;
+    cpu_loop_exit();
+}
+
+void HELPER(raise_exception)(uint32_t exception_index)
+{
+    raise_exception(exception_index);
+}
+
+void HELPER(set_inhibit_irq)(void)
+{
+    env->hflags |= HF_INHIBIT_IRQ_MASK;
+}
+
+void HELPER(reset_inhibit_irq)(void)
+{
+    env->hflags &= ~HF_INHIBIT_IRQ_MASK;
+}
+
+void HELPER(movl_pc_im)(uint32_t new_pc)
+{
+    PC = (uint16_t)new_pc;
+}
+
+/* Z80 instruction-specific helpers */
+
+/* Halt */
+
+void HELPER(halt)(void)
+{
+    //printf("halting at PC 0x%x\n",env->pc);
+    env->halted = 1;
+    env->hflags &= ~HF_INHIBIT_IRQ_MASK; /* needed if sti is just before */
+    env->exception_index = EXCP_HLT;
+    cpu_loop_exit();
+}
+
+/* In / Out */
+
+void HELPER(in_T0_im)(uint32_t val)
+{
+    T0 = cpu_inb(env, (A << 8) | val);
+}
+
+void HELPER(in_T0_bc_cc)(void)
+{
+    int sf, zf, pf;
+
+    T0 = cpu_inb(env, BC);
+
+    sf = (T0 & 0x80) ? CC_S : 0;
+    zf = T0 ? 0 : CC_Z;
+    pf = parity_table[(uint8_t)T0];
+    F = (F & CC_C) | sf | zf | pf;
+}
+
+void HELPER(out_T0_im)(uint32_t val)
+{
+    cpu_outb(env, (A << 8) | val, T0);
+}
+
+void HELPER(out_T0_bc)(void)
+{
+    cpu_outb(env, BC, T0);
+}
+
+/* Misc */
+
+void HELPER(bit_T0)(uint32_t val)
+{
+    int sf, zf, pf;
+
+    sf = (T0 & val & 0x80) ? CC_S : 0;
+    zf = (T0 & val) ? 0 : CC_Z;
+    pf = (T0 & val) ? 0 : CC_P;
+    F = (F & CC_C) | sf | zf | CC_H | pf;
+}
+
+void HELPER(jmp_T0)(void)
+{
+    PC = T0;
+}
+
+void HELPER(djnz)(uint32_t pc1, uint32_t pc2)
+{
+    BC = (uint16_t)(BC - 0x0100);
+    if (BC & 0xff00) {
+        PC = (uint16_t)pc1;
+    } else {
+        PC = (uint16_t)pc2;
+    }
+}
+
+/* Arithmetic/logic operations */
+
+#define signed_overflow_add(op1, op2, res, size) \
+    (!!((~(op1 ^ op2) & (op1 ^ res)) >> (size - 1)))
+
+#define signed_overflow_sub(op1, op2, res, size) \
+    (!!(((op1 ^ op2) & (op1 ^ res)) >> (size - 1)))
+
+void HELPER(add_cc)(void)
+{
+    int sf, zf, hf, pf, cf;
+    int tmp = A;
+    int carry;
+
+    A = (uint8_t)(A + T0);
+    sf = (A & 0x80) ? CC_S : 0;
+    zf = A ? 0 : CC_Z;
+    carry = (tmp & T0) | ((tmp | T0) & ~A);
+    hf = (carry & 0x08) ? CC_H : 0;
+    pf = signed_overflow_add(tmp, T0, A, 8) ? CC_P : 0;
+    cf = (carry & 0x80) ? CC_C : 0;
+
+    F = sf | zf | hf | pf | cf;
+}
+
+void HELPER(adc_cc)(void)
+{
+    int sf, zf, hf, pf, cf;
+    int tmp = A;
+    int carry;
+
+    A = (uint8_t)(A + T0 + !!(F & CC_C));
+    sf = (A & 0x80) ? CC_S : 0;
+    zf = A ? 0 : CC_Z;
+    carry = (tmp & T0) | ((tmp | T0) & ~A);
+    hf = (carry & 0x08) ? CC_H : 0;
+    pf = signed_overflow_add(tmp, T0, A, 8) ? CC_P : 0;
+    cf = (carry & 0x80) ? CC_C : 0;
+
+    F = sf | zf | hf | pf | cf;
+}
+
+void HELPER(sub_cc)(void)
+{
+    int sf, zf, hf, pf, cf;
+    int tmp = A;
+    int carry;
+
+    A = (uint8_t)(A - T0);
+    sf = (A & 0x80) ? CC_S : 0;
+    zf = A ? 0 : CC_Z;
+    carry = (~tmp & T0) | (~(tmp ^ T0) & A);
+    hf = (carry & 0x08) ? CC_H : 0;
+    pf = signed_overflow_sub(tmp, T0, A, 8) ? CC_P : 0;
+    cf = (carry & 0x80) ? CC_C : 0;
+
+    F = sf | zf | hf | pf | CC_N | cf;
+}
+
+void HELPER(sbc_cc)(void)
+{
+    int sf, zf, hf, pf, cf;
+    int tmp = A;
+    int carry;
+
+    A = (uint8_t)(A - T0 - !!(F & CC_C));
+    sf = (A & 0x80) ? CC_S : 0;
+    zf = A ? 0 : CC_Z;
+    carry = (~tmp & T0) | (~(tmp ^ T0) & A);
+    hf = (carry & 0x08) ? CC_H : 0;
+    pf = signed_overflow_sub(tmp, T0, A, 8) ? CC_P : 0;
+    cf = (carry & 0x80) ? CC_C : 0;
+
+    F = sf | zf | hf | pf | CC_N | cf;
+}
+
+void HELPER(and_cc)(void)
+{
+    int sf, zf, pf;
+    A = (uint8_t)(A & T0);
+
+    sf = (A & 0x80) ? CC_S : 0;
+    zf = A ? 0 : CC_Z;
+    pf = parity_table[(uint8_t)A];
+    F = sf | zf | CC_H | pf;
+}
+
+void HELPER(xor_cc)(void)
+{
+    int sf, zf, pf;
+    A = (uint8_t)(A ^ T0);
+
+    sf = (A & 0x80) ? CC_S : 0;
+    zf = A ? 0 : CC_Z;
+    pf = parity_table[(uint8_t)A];
+    F = sf | zf | pf;
+}
+
+void HELPER(or_cc)(void)
+{
+    int sf, zf, pf;
+    A = (uint8_t)(A | T0);
+
+    sf = (A & 0x80) ? CC_S : 0;
+    zf = A ? 0 : CC_Z;
+    pf = parity_table[(uint8_t)A];
+    F = sf | zf | pf;
+}
+
+void HELPER(cp_cc)(void)
+{
+    int sf, zf, hf, pf, cf;
+    int res, carry;
+
+    res = (uint8_t)(A - T0);
+    sf = (res & 0x80) ? CC_S : 0;
+    zf = res ? 0 : CC_Z;
+    carry = (~A & T0) | (~(A ^ T0) & res);
+    hf = (carry & 0x08) ? CC_H : 0;
+    pf = signed_overflow_sub(A, T0, res, 8) ? CC_P : 0;
+    cf = (carry & 0x80) ? CC_C : 0;
+
+    F = sf | zf | hf | pf | CC_N | cf;
+//  CC_DST = (uint8_t)(A - T0);
+}
+
+/* Rotation/shift operations */
+
+void HELPER(rlc_T0_cc)(void)
+{
+    int sf, zf, pf, cf;
+    int tmp;
+
+    tmp = T0;
+    T0 = (uint8_t)((T0 << 1) | !!(T0 & 0x80));
+    sf = (T0 & 0x80) ? CC_S : 0;
+    zf = T0 ? 0 : CC_Z;
+    pf = parity_table[T0];
+    cf = (tmp & 0x80) ? CC_C : 0;
+    F = sf | zf | pf | cf;
+}
+
+void HELPER(rrc_T0_cc)(void)
+{
+    int sf, zf, pf, cf;
+    int tmp;
+
+    tmp = T0;
+    T0 = (uint8_t)((T0 >> 1) | ((tmp & 0x01) ? 0x80 : 0));
+    sf = (T0 & 0x80) ? CC_S : 0;
+    zf = T0 ? 0 : CC_Z;
+    pf = parity_table[T0];
+    cf = (tmp & 0x01) ? CC_C : 0;
+    F = sf | zf | pf | cf;
+}
+
+void HELPER(rl_T0_cc)(void)
+{
+    int sf, zf, pf, cf;
+    int tmp;
+
+    tmp = T0;
+    T0 = (uint8_t)((T0 << 1) | !!(F & CC_C));
+    sf = (T0 & 0x80) ? CC_S : 0;
+    zf = T0 ? 0 : CC_Z;
+    pf = parity_table[T0];
+    cf = (tmp & 0x80) ? CC_C : 0;
+    F = sf | zf | pf | cf;
+}
+
+void HELPER(rr_T0_cc)(void)
+{
+    int sf, zf, pf, cf;
+    int tmp;
+
+    tmp = T0;
+    T0 = (uint8_t)((T0 >> 1) | ((F & CC_C) ? 0x80 : 0));
+    sf = (T0 & 0x80) ? CC_S : 0;
+    zf = T0 ? 0 : CC_Z;
+    pf = parity_table[T0];
+    cf = (tmp & 0x01) ? CC_C : 0;
+    F = sf | zf | pf | cf;
+}
+
+void HELPER(sla_T0_cc)(void)
+{
+    int sf, zf, pf, cf;
+    int tmp;
+
+    tmp = T0;
+    T0 = (uint8_t)(T0 << 1);
+    sf = (T0 & 0x80) ? CC_S : 0;
+    zf = T0 ? 0 : CC_Z;
+    pf = parity_table[T0];
+    cf = (tmp & 0x80) ? CC_C : 0;
+    F = sf | zf | pf | cf;
+}
+
+void HELPER(sra_T0_cc)(void)
+{
+    int sf, zf, pf, cf;
+    int tmp;
+
+    tmp = T0;
+    T0 = (uint8_t)((T0 >> 1) | (T0 & 0x80));
+    sf = (T0 & 0x80) ? CC_S : 0;
+    zf = T0 ? 0 : CC_Z;
+    pf = parity_table[T0];
+    cf = (tmp & 0x01) ? CC_C : 0;
+    F = sf | zf | pf | cf;
+}
+
+/* Z80-specific: R800 has tst instruction */
+void HELPER(sll_T0_cc)(void)
+{
+    int sf, zf, pf, cf;
+    int tmp;
+
+    tmp = T0;
+    T0 = (uint8_t)((T0 << 1) | 1); /* Yes -- bit 0 is *set* */
+    sf = (T0 & 0x80) ? CC_S : 0;
+    zf = T0 ? 0 : CC_Z;
+    pf = parity_table[T0];
+    cf = (tmp & 0x80) ? CC_C : 0;
+    F = sf | zf | pf | cf;
+}
+
+void HELPER(srl_T0_cc)(void)
+{
+    int sf, zf, pf, cf;
+    int tmp;
+
+    tmp = T0;
+    T0 = (uint8_t)(T0 >> 1);
+    sf = (T0 & 0x80) ? CC_S : 0;
+    zf = T0 ? 0 : CC_Z;
+    pf = parity_table[T0];
+    cf = (tmp & 0x01) ? CC_C : 0;
+    F = sf | zf | pf | cf;
+}
+
+void HELPER(rld_cc)(void)
+{
+    int sf, zf, pf;
+    int tmp = A & 0x0f;
+    A = (A & 0xf0) | ((T0 >> 4) & 0x0f);
+    T0 = ((T0 << 4) & 0xf0) | tmp;
+
+    sf = (A & 0x80) ? CC_S : 0;
+    zf = A ? 0 : CC_Z;
+    pf = parity_table[A];
+
+    F = (F & CC_C) | sf | zf | pf;
+}
+
+void HELPER(rrd_cc)(void)
+{
+    int sf, zf, pf;
+    int tmp = A & 0x0f;
+    A = (A & 0xf0) | (T0 & 0x0f);
+    T0 = (T0 >> 4) | (tmp << 4);
+
+    sf = (A & 0x80) ? CC_S : 0;
+    zf = A ? 0 : CC_Z;
+    pf = parity_table[A];
+
+    F = (F & CC_C) | sf | zf | pf;
+}
+
+/* Block instructions */
+
+void HELPER(bli_ld_inc_cc)(void)
+{
+    int pf;
+
+    BC = (uint16_t)(BC - 1);
+    DE = (uint16_t)(DE + 1);
+    HL = (uint16_t)(HL + 1);
+
+    pf = BC ? CC_P : 0;
+    F = (F & (CC_S | CC_Z | CC_C)) | pf;
+}
+
+void HELPER(bli_ld_dec_cc)(void)
+{
+    int pf;
+
+    BC = (uint16_t)(BC - 1);
+    DE = (uint16_t)(DE - 1);
+    HL = (uint16_t)(HL - 1);
+
+    pf = BC ? CC_P : 0;
+    F = (F & (CC_S | CC_Z | CC_C)) | pf;
+}
+
+void HELPER(bli_ld_rep)(uint32_t next_pc)
+{
+    if (BC) {
+        PC = (uint16_t)(next_pc - 2);
+    } else {
+        PC = next_pc;
+    }
+}
+
+void HELPER(bli_cp_cc)(void)
+{
+    int sf, zf, hf, pf;
+    int res, carry;
+
+    res = (uint8_t)(A - T0);
+    sf = (res & 0x80) ? CC_S : 0;
+    zf = res ? 0 : CC_Z;
+    carry = (~A & T0) | (~(A ^ T0) & res);
+    hf = (carry & 0x08) ? CC_H : 0;
+    pf = BC ? CC_P : 0;
+
+    F = (F & CC_C) | sf | zf | hf | pf | CC_N;
+}
+
+void HELPER(bli_cp_inc_cc)(void)
+{
+    int pf;
+
+    BC = (uint16_t)(BC - 1);
+    HL = (uint16_t)(HL + 1);
+
+    pf = BC ? CC_P : 0;
+    F = (F & ~CC_P) | pf;
+}
+
+void HELPER(bli_cp_dec_cc)(void)
+{
+    int pf;
+
+    BC = (uint16_t)(BC - 1);
+    HL = (uint16_t)(HL - 1);
+
+    pf = BC ? CC_P : 0;
+    F = (F & ~CC_P) | pf;
+}
+
+void HELPER(bli_cp_rep)(uint32_t next_pc)
+{
+    if (BC && T0 != A) {
+        PC = (uint16_t)(next_pc - 2);
+    } else {
+        PC = next_pc;
+    }
+}
+
+void HELPER(bli_io_inc)(void)
+{
+    HL = (uint16_t)(HL + 1);
+    BC = (uint16_t)BC - 0x0100;
+}
+
+void HELPER(bli_io_dec)(void)
+{
+    HL = (uint16_t)(HL - 1);
+    BC = (uint16_t)BC - 0x0100;
+}
+
+void HELPER(bli_io_rep)(uint32_t next_pc)
+{
+    if (BC & 0xff00) {
+        PC = (uint16_t)(next_pc - 2);
+    } else {
+        PC = next_pc;
+    }
+}
+
+/* misc */
+
+void HELPER(rlca_cc)(void)
+{
+    int cf;
+    int tmp;
+
+    tmp = A;
+    A = (uint8_t)((A << 1) | !!(tmp & 0x80));
+    cf = (tmp & 0x80) ? CC_C : 0;
+    F = (F & (CC_S | CC_Z | CC_P)) | cf;
+}
+
+void HELPER(rrca_cc)(void)
+{
+    int cf;
+    int tmp;
+
+    tmp = A;
+    A = (A >> 1) | ((tmp & 0x01) ? 0x80 : 0);
+    cf = (tmp & 0x01) ? CC_C : 0;
+    F = (F & (CC_S | CC_Z | CC_P)) | cf;
+}
+
+void HELPER(rla_cc)(void)
+{
+    int cf;
+    int tmp;
+
+    tmp = A;
+    A = (uint8_t)((A << 1) | !!(F & CC_C));
+    cf = (tmp & 0x80) ? CC_C : 0;
+    F = (F & (CC_S | CC_Z | CC_P)) | cf;
+}
+
+void HELPER(rra_cc)(void)
+{
+    int cf;
+    int tmp;
+
+    tmp = A;
+    A = (A >> 1) | ((F & CC_C) ? 0x80 : 0);
+    cf = (tmp & 0x01) ? CC_C : 0;
+    F = (F & (CC_S | CC_Z | CC_P)) | cf;
+}
+
+/* TODO */
+void HELPER(daa_cc)(void)
+{
+    int sf, zf, hf, pf, cf;
+    int cor = 0;
+    int tmp = A;
+
+    if (A > 0x99 || (F & CC_C)) {
+        cor |= 0x60;
+        cf = CC_C;
+    } else {
+        cf = 0;
+    }
+
+    if ((A & 0x0f) > 0x09 || (F & CC_H)) {
+        cor |= 0x06;
+    }
+
+    if (!(F & CC_N)) {
+        A = (uint8_t)(A + cor);
+    } else {
+        A = (uint8_t)(A - cor);
+    }
+
+    sf = (A & 0x80) ? CC_S : 0;
+    zf = A ? 0 : CC_Z;
+    hf = ((tmp ^ A) & 0x10) ? CC_H : 0;
+    pf = parity_table[(uint8_t)A];
+
+    F = (F & CC_N) | sf | zf | hf | pf | cf;
+}
+
+void HELPER(cpl_cc)(void)
+{
+    A = (uint8_t)~A;
+    F |= CC_H | CC_N;
+}
+
+void HELPER(scf_cc)(void)
+{
+    F = (F & (CC_S | CC_Z | CC_P)) | CC_C;
+}
+
+void HELPER(ccf_cc)(void)
+{
+    int hf, cf;
+
+    hf = (F & CC_C) ? CC_H : 0;
+    cf = (F & CC_C) ^ CC_C;
+    F = (F & (CC_S | CC_Z | CC_P)) | hf | cf;
+}
+
+/* misc */
+
+void HELPER(neg_cc)(void)
+{
+    int sf, zf, hf, pf, cf;
+    int tmp = A;
+    int carry;
+
+    A = (uint8_t)-A;
+    sf = (A & 0x80) ? CC_S : 0;
+    zf = A ? 0 : CC_Z;
+    carry = (tmp & T0) | ((tmp | T0) & ~A);
+    hf = (carry & 0x08) ? CC_H : 0;
+    pf = signed_overflow_sub(tmp, T0, A, 8) ? CC_P : 0;
+    cf = (carry & 0x80) ? CC_C : 0;
+
+    F = sf | zf | hf | pf | CC_N | cf;
+}
+
+/* word operations -- HL only? */
+
+void HELPER(sbcw_T0_T1_cc)(void)
+{
+    int sf, zf, hf, pf, cf;
+    int tmp = T0;
+    int carry;
+
+    T0 = (uint16_t)(T0 - T1 - !!(F & CC_C));
+    sf = (T0 & 0x8000) ? CC_S : 0;
+    zf = T0 ? 0 : CC_Z;
+    carry = (~tmp & T1) | (~(tmp ^ T1) & T0);
+    hf = (carry & 0x0800) ? CC_H : 0;
+    pf = signed_overflow_sub(tmp, T1, T0, 16) ? CC_P : 0;
+    cf = (carry & 0x8000) ? CC_C : 0;
+
+    F = sf | zf | hf | pf | CC_N | cf;
+}
+
+void HELPER(addw_T0_T1_cc)(void)
+{
+    int hf, cf;
+    int tmp = T0;
+    int carry;
+
+    T0 = (uint16_t)(T0 + T1);
+    carry = (tmp & T1) | ((tmp | T1) & ~T0);
+    hf = (carry & 0x0800) ? CC_H : 0;
+    cf = (carry & 0x8000) ? CC_C : 0;
+
+    F = (F & (CC_S | CC_Z | CC_P)) | hf | cf;
+}
+
+void HELPER(adcw_T0_T1_cc)(void)
+{
+    int sf, zf, hf, pf, cf;
+    int tmp = T0;
+    int carry;
+
+    T0 = (uint16_t)(T0 + T1 + !!(F & CC_C));
+    sf = (T0 & 0x8000) ? CC_S : 0;
+    zf = T0 ? 0 : CC_Z;
+    carry = (tmp & T1) | ((tmp | T1) & ~T0);
+    hf = (carry & 0x0800) ? CC_H : 0;
+    pf = signed_overflow_add(tmp, T1, T0, 8) ? CC_P : 0;
+    cf = (carry & 0x8000) ? CC_C : 0;
+
+    F = sf | zf | hf | pf | cf;
+}
+
+/* misc */
+
+void HELPER(incb_T0_cc)(void)
+{
+    int sf, zf, hf, pf;
+    int tmp;
+    int carry;
+
+    tmp = T0;
+    T0 = (uint8_t)(T0 + 1);
+    sf = (T0 & 0x80) ? CC_S : 0;
+    zf = T0 ? 0 : CC_Z;
+
+    carry = (tmp & 1) | ((tmp | 1) & ~T0);
+    hf = (carry & 0x08) ? CC_H : 0;
+    pf = signed_overflow_add(tmp, 1, T0, 8) ? CC_P : 0;
+
+    F = (F & CC_C) | sf | zf | hf | pf;
+}
+
+void HELPER(decb_T0_cc)(void)
+{
+    int sf, zf, hf, pf;
+    int tmp;
+    int carry;
+
+    tmp = T0;
+    T0 = (uint8_t)(T0 - 1);
+    sf = (T0 & 0x80) ? CC_S : 0;
+    zf = T0 ? 0 : CC_Z;
+
+    carry = (~tmp & 1) | (~(tmp ^ 1) & T0);
+    hf = (carry & 0x08) ? CC_H : 0;
+    pf = signed_overflow_sub(tmp, 1, T0, 8) ? CC_P : 0;
+
+    F = (F & CC_C) | sf | zf | hf | CC_N | pf;
+    /* TODO: check CC_N is set */
+}
+
+/* value on data bus is 0xff for speccy */
+/* IM0 = execute data on bus (rst $38 on speccy) */
+/* IM1 = execute rst $38 (ROM uses this)*/
+/* IM2 = indirect jump -- address is held at (I << 8) | DATA */
+
+/* when an interrupt occurs, iff1 and iff2 are reset, disabling interrupts */
+/* when an NMI occurs, iff1 is reset. iff2 is left unchanged */
+
+void HELPER(imode)(uint32_t imode)
+{
+    env->imode = imode;
+}
+
+/* enable interrupts */
+void HELPER(ei)(void)
+{
+    env->iff1 = 1;
+    env->iff2 = 1;
+}
+
+/* disable interrupts */
+void HELPER(di)(void)
+{
+    env->iff1 = 0;
+    env->iff2 = 0;
+}
+
+/* reenable interrupts if enabled */
+void HELPER(ri)(void)
+{
+    env->iff1 = env->iff2;
+}
+
+void HELPER(ld_R_A)(void)
+{
+    R = A;
+}
+
+void HELPER(ld_I_A)(void)
+{
+    I = A;
+}
+
+void HELPER(ld_A_R)(void)
+{
+    int sf, zf, pf;
+
+    A = R;
+    sf = (A & 0x80) ? CC_S : 0;
+    zf = A ? 0 : CC_Z;
+    pf = env->iff2 ? CC_P : 0;
+
+    F = (F & CC_C) | sf | zf | pf;
+}
+
+void HELPER(ld_A_I)(void)
+{
+    int sf, zf, pf;
+
+    A = I;
+    sf = (A & 0x80) ? CC_S : 0;
+    zf = A ? 0 : CC_Z;
+    pf = env->iff2 ? CC_P : 0;
+
+    F = (F & CC_C) | sf | zf | pf;
+}
+
+void HELPER(mulub_cc)(void)
+{
+    /* TODO: flags */
+
+    HL = A * T0;
+}
+
+void HELPER(muluw_cc)(void)
+{
+    /* TODO: flags */
+    uint32_t tmp;
+
+    tmp = HL * T0;
+    DE = tmp >> 16;
+    HL = tmp & 0xff;
+}
+
+#if !defined(CONFIG_USER_ONLY)
+
+#define MMUSUFFIX _mmu
+
+#define SHIFT 0
+#include "softmmu_template.h"
+
+#define SHIFT 1
+#include "softmmu_template.h"
+
+#define SHIFT 2
+#include "softmmu_template.h"
+
+#define SHIFT 3
+#include "softmmu_template.h"
+
+#endif
+
+/* try to fill the TLB and return an exception if error. If retaddr is
+   NULL, it means that the function was called in C code (i.e. not
+   from generated code or from helper.c) */
+/* XXX: fix it to restore all registers */
+void tlb_fill(target_ulong addr, int is_write, int is_user, void *retaddr)
+{
+    TranslationBlock *tb;
+    int ret;
+    unsigned long pc;
+    CPUZ80State *saved_env;
+
+    /* XXX: hack to restore env in all cases, even if not called from
+       generated code */
+    saved_env = env;
+    env = cpu_single_env;
+
+    ret = cpu_z80_handle_mmu_fault(env, addr, is_write, is_user, 1);
+    if (ret) {
+        if (retaddr) {
+            /* now we have a real cpu fault */
+            pc = (unsigned long)retaddr;
+            tb = tb_find_pc(pc);
+            if (tb) {
+                /* the PC is inside the translated code. It means that we have
+                   a virtual CPU fault */
+                cpu_restore_state(tb, env, pc, NULL);
+            }
+        }
+        if (retaddr) {
+            raise_exception_err(env->exception_index, env->error_code);
+        } else {
+            raise_exception_err_norestore(env->exception_index, env->error_code);
+        }
+    }
+    env = saved_env;
+}
diff --git a/target-z80/translate.c b/target-z80/translate.c
new file mode 100644
index 0000000..846ec03
--- /dev/null
+++ b/target-z80/translate.c
@@ -0,0 +1,1834 @@
+/*
+ * Z80 translation
+ *
+ *  Copyright (c) 2007-2009 Stuart Brady <stuart.brady@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA  02110-1301 USA
+ */
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+#include <signal.h>
+
+#include "cpu.h"
+#include "exec-all.h"
+#include "disas.h"
+#include "tcg-op.h"
+
+#include "helper.h"
+#define GEN_HELPER 1
+#include "helper.h"
+
+#define PREFIX_CB  0x01
+#define PREFIX_DD  0x02
+#define PREFIX_ED  0x04
+#define PREFIX_FD  0x08
+
+#define MODE_NORMAL 0
+#define MODE_DD     1
+#define MODE_FD     2
+
+#define zprintf(...)
+//#define zprintf printf
+
+/* global register indexes */
+static TCGv cpu_env, cpu_T[3], cpu_A0;
+
+#include "gen-icount.h"
+
+#define MEM_INDEX 0
+
+typedef struct DisasContext {
+    /* current insn context */
+    int override; /* -1 if no override */
+    int prefix;
+    uint16_t pc; /* pc = pc + cs_base */
+    int is_jmp; /* 1 = means jump (stop translation), 2 means CPU
+                   static state change (stop translation) */
+    int model;
+    /* current block context */
+    target_ulong cs_base; /* base of CS segment */
+    int singlestep_enabled; /* "hardware" single step enabled */
+    int jmp_opt; /* use direct block chaining for direct jumps */
+    int flags; /* all execution flags */
+    struct TranslationBlock *tb;
+} DisasContext;
+
+static void gen_eob(DisasContext *s);
+static void gen_jmp(DisasContext *s, target_ulong pc);
+static void gen_jmp_tb(DisasContext *s, target_ulong pc, int tb_num);
+
+enum {
+    /* 8-bit registers */
+    OR_B,
+    OR_C,
+    OR_D,
+    OR_E,
+    OR_H,
+    OR_L,
+    OR_HLmem,
+    OR_A,
+
+    OR_IXh,
+    OR_IXl,
+
+    OR_IYh,
+    OR_IYl,
+
+    OR_IXmem,
+    OR_IYmem,
+};
+
+static const char *const regnames[] = {
+    [OR_B]     = "b",
+    [OR_C]     = "c",
+    [OR_D]     = "d",
+    [OR_E]     = "e",
+    [OR_H]     = "h",
+    [OR_L]     = "l",
+    [OR_HLmem] = "(hl)",
+    [OR_A]     = "a",
+
+    [OR_IXh]   = "ixh",
+    [OR_IXl]   = "ixl",
+
+    [OR_IYh]   = "iyh",
+    [OR_IYl]   = "iyl",
+
+    [OR_IXmem] = "(ix+d)",
+    [OR_IYmem] = "(iy+d)",
+};
+
+static const char *const idxnames[] = {
+    [OR_IXmem] = "ix",
+    [OR_IYmem] = "iy",
+};
+
+/* signed hex byte value for printf */
+#define shexb(val) (val < 0 ? '-' : '+'), (abs(val))
+
+/* Register accessor functions */
+
+#if defined(WORDS_BIGENDIAN)
+#define UNIT_OFFSET(type, units, num) (sizeof(type) - ((num + 1) * units))
+#else
+#define UNIT_OFFSET(type, units, num) (num * units)
+#endif
+
+#define BYTE_OFFSET(type, num) UNIT_OFFSET(type, 1, num)
+#define WORD_OFFSET(type, num) UNIT_OFFSET(type, 2, num)
+
+#define REGPAIR AF
+#define REGHIGH A
+#define REGLOW  F
+#include "genreg_template_af.h"
+#undef REGPAIR
+#undef REGHIGH
+#undef REGLOW
+
+#define REGPAIR BC
+#define REGHIGH B
+#define REGLOW  C
+#include "genreg_template.h"
+#undef REGPAIR
+#undef REGHIGH
+#undef REGLOW
+
+#define REGPAIR DE
+#define REGHIGH D
+#define REGLOW  E
+#include "genreg_template.h"
+#undef REGPAIR
+#undef REGHIGH
+#undef REGLOW
+
+#define REGPAIR HL
+#define REGHIGH H
+#define REGLOW  L
+#include "genreg_template.h"
+#undef REGPAIR
+#undef REGHIGH
+#undef REGLOW
+
+#define REGPAIR IX
+#define REGHIGH IXh
+#define REGLOW  IXl
+#include "genreg_template.h"
+#undef REGPAIR
+#undef REGHIGH
+#undef REGLOW
+
+#define REGPAIR IY
+#define REGHIGH IYh
+#define REGLOW  IYl
+#include "genreg_template.h"
+#undef REGPAIR
+#undef REGHIGH
+#undef REGLOW
+
+#define REGPAIR AFX
+#define REGHIGH AX
+#define REGLOW  FX
+#include "genreg_template_af.h"
+#undef REGPAIR
+#undef REGHIGH
+#undef REGLOW
+
+#define REGPAIR BCX
+#define REGHIGH BX
+#define REGLOW  CX
+#include "genreg_template.h"
+#undef REGPAIR
+#undef REGHIGH
+#undef REGLOW
+
+#define REGPAIR DEX
+#define REGHIGH DX
+#define REGLOW  EX
+#include "genreg_template.h"
+#undef REGPAIR
+#undef REGHIGH
+#undef REGLOW
+
+#define REGPAIR HLX
+#define REGHIGH HX
+#define REGLOW  LX
+#include "genreg_template.h"
+#undef REGPAIR
+#undef REGHIGH
+#undef REGLOW
+
+#define REGPAIR SP
+#include "genreg_template.h"
+#undef REGPAIR
+
+typedef void (gen_mov_func)(TCGv v);
+typedef void (gen_mov_func_idx)(TCGv v, uint16_t ofs);
+
+static inline void gen_movb_v_HLmem(TCGv v)
+{
+    TCGv addr = tcg_temp_new();
+    gen_movw_v_HL(addr);
+    tcg_gen_qemu_ld8u(v, addr, MEM_INDEX);
+    tcg_temp_free(addr);
+}
+
+static inline void gen_movb_HLmem_v(TCGv v)
+{
+    TCGv addr = tcg_temp_new();
+    gen_movw_v_HL(addr);
+    tcg_gen_qemu_st8(v, addr, MEM_INDEX);
+    tcg_temp_free(addr);
+}
+
+static inline void gen_movb_v_IXmem(TCGv v, uint16_t ofs)
+{
+    TCGv addr = tcg_temp_new();
+    gen_movw_v_IX(addr);
+    tcg_gen_addi_tl(addr, addr, ofs);
+    tcg_gen_ext16u_tl(addr, addr);
+    tcg_gen_qemu_ld8u(v, addr, MEM_INDEX);
+    tcg_temp_free(addr);
+}
+
+static inline void gen_movb_v_IYmem(TCGv v, uint16_t ofs)
+{
+    TCGv addr = tcg_temp_new();
+    gen_movw_v_IY(addr);
+    tcg_gen_addi_tl(addr, addr, ofs);
+    tcg_gen_ext16u_tl(addr, addr);
+    tcg_gen_qemu_ld8u(v, addr, MEM_INDEX);
+    tcg_temp_free(addr);
+}
+
+static inline void gen_movb_IXmem_v(TCGv v, uint16_t ofs)
+{
+    TCGv addr = tcg_temp_new();
+    gen_movw_v_IX(addr);
+    tcg_gen_addi_tl(addr, addr, ofs);
+    tcg_gen_ext16u_tl(addr, addr);
+    tcg_gen_qemu_st8(v, addr, MEM_INDEX);
+    tcg_temp_free(addr);
+}
+
+static inline void gen_movb_IYmem_v(TCGv v, uint16_t ofs)
+{
+    TCGv addr = tcg_temp_new();
+    gen_movw_v_IY(addr);
+    tcg_gen_addi_tl(addr, addr, ofs);
+    tcg_gen_ext16u_tl(addr, addr);
+    tcg_gen_qemu_st8(v, addr, MEM_INDEX);
+    tcg_temp_free(addr);
+}
+
+static inline void gen_pushw(TCGv v)
+{
+    TCGv addr = tcg_temp_new();
+    gen_movw_v_SP(addr);
+    tcg_gen_subi_i32(addr, addr, 2);
+    tcg_gen_ext16u_i32(addr, addr);
+    gen_movw_SP_v(addr);
+    tcg_gen_qemu_st16(v, addr, MEM_INDEX);
+    tcg_temp_free(addr);
+}
+
+static inline void gen_popw(TCGv v)
+{
+    TCGv addr = tcg_temp_new();
+    gen_movw_v_SP(addr);
+    tcg_gen_qemu_ld16u(v, addr, MEM_INDEX);
+    tcg_gen_addi_i32(addr, addr, 2);
+    tcg_gen_ext16u_i32(addr, addr);
+    gen_movw_SP_v(addr);
+    tcg_temp_free(addr);
+}
+
+static gen_mov_func *const gen_movb_v_reg_tbl[] = {
+    [OR_B]     = gen_movb_v_B,
+    [OR_C]     = gen_movb_v_C,
+    [OR_D]     = gen_movb_v_D,
+    [OR_E]     = gen_movb_v_E,
+    [OR_H]     = gen_movb_v_H,
+    [OR_L]     = gen_movb_v_L,
+    [OR_HLmem] = gen_movb_v_HLmem,
+    [OR_A]     = gen_movb_v_A,
+
+    [OR_IXh]   = gen_movb_v_IXh,
+    [OR_IXl]   = gen_movb_v_IXl,
+
+    [OR_IYh]   = gen_movb_v_IYh,
+    [OR_IYl]   = gen_movb_v_IYl,
+};
+
+static inline void gen_movb_v_reg(TCGv v, int reg)
+{
+    gen_movb_v_reg_tbl[reg](v);
+}
+
+static gen_mov_func_idx *const gen_movb_v_idx_tbl[] = {
+    [OR_IXmem] = gen_movb_v_IXmem,
+    [OR_IYmem] = gen_movb_v_IYmem,
+};
+
+static inline void gen_movb_v_idx(TCGv v, int idx, int ofs)
+{
+    gen_movb_v_idx_tbl[idx](v, ofs);
+}
+
+static gen_mov_func *const gen_movb_reg_v_tbl[] = {
+    [OR_B]     = gen_movb_B_v,
+    [OR_C]     = gen_movb_C_v,
+    [OR_D]     = gen_movb_D_v,
+    [OR_E]     = gen_movb_E_v,
+    [OR_H]     = gen_movb_H_v,
+    [OR_L]     = gen_movb_L_v,
+    [OR_HLmem] = gen_movb_HLmem_v,
+    [OR_A]     = gen_movb_A_v,
+
+    [OR_IXh]   = gen_movb_IXh_v,
+    [OR_IXl]   = gen_movb_IXl_v,
+
+    [OR_IYh]   = gen_movb_IYh_v,
+    [OR_IYl]   = gen_movb_IYl_v,
+};
+
+static inline void gen_movb_reg_v(int reg, TCGv v)
+{
+    gen_movb_reg_v_tbl[reg](v);
+}
+
+static gen_mov_func_idx *const gen_movb_idx_v_tbl[] = {
+    [OR_IXmem] = gen_movb_IXmem_v,
+    [OR_IYmem] = gen_movb_IYmem_v,
+};
+
+static inline void gen_movb_idx_v(int idx, TCGv v, int ofs)
+{
+    gen_movb_idx_v_tbl[idx](v, ofs);
+}
+
+static inline int regmap(int reg, int m)
+{
+    switch (m) {
+    case MODE_DD:
+        switch (reg) {
+        case OR_H:
+            return OR_IXh;
+        case OR_L:
+            return OR_IXl;
+        case OR_HLmem:
+            return OR_IXmem;
+        default:
+            return reg;
+        }
+    case MODE_FD:
+        switch (reg) {
+        case OR_H:
+            return OR_IYh;
+        case OR_L:
+            return OR_IYl;
+        case OR_HLmem:
+            return OR_IYmem;
+        default:
+            return reg;
+        }
+    case MODE_NORMAL:
+    default:
+        return reg;
+    }
+}
+
+static inline int is_indexed(int reg)
+{
+    if (reg == OR_IXmem || reg == OR_IYmem) {
+        return 1;
+    } else {
+        return 0;
+    }
+}
+
+static const int reg[8] = {
+    OR_B,
+    OR_C,
+    OR_D,
+    OR_E,
+    OR_H,
+    OR_L,
+    OR_HLmem,
+    OR_A,
+};
+
+enum {
+    /* 16-bit registers and register pairs */
+    OR2_AF,
+    OR2_BC,
+    OR2_DE,
+    OR2_HL,
+
+    OR2_IX,
+    OR2_IY,
+    OR2_SP,
+
+    OR2_AFX,
+    OR2_BCX,
+    OR2_DEX,
+    OR2_HLX,
+};
+
+static const char *const regpairnames[] = {
+    [OR2_AF]  = "af",
+    [OR2_BC]  = "bc",
+    [OR2_DE]  = "de",
+    [OR2_HL]  = "hl",
+
+    [OR2_IX]  = "ix",
+    [OR2_IY]  = "iy",
+    [OR2_SP]  = "sp",
+
+    [OR2_AFX] = "afx",
+    [OR2_BCX] = "bcx",
+    [OR2_DEX] = "dex",
+    [OR2_HLX] = "hlx",
+};
+
+static gen_mov_func *const gen_movw_v_reg_tbl[] = {
+    [OR2_AF]  = gen_movw_v_AF,
+    [OR2_BC]  = gen_movw_v_BC,
+    [OR2_DE]  = gen_movw_v_DE,
+    [OR2_HL]  = gen_movw_v_HL,
+
+    [OR2_IX]  = gen_movw_v_IX,
+    [OR2_IY]  = gen_movw_v_IY,
+    [OR2_SP]  = gen_movw_v_SP,
+
+    [OR2_AFX] = gen_movw_v_AFX,
+    [OR2_BCX] = gen_movw_v_BCX,
+    [OR2_DEX] = gen_movw_v_DEX,
+    [OR2_HLX] = gen_movw_v_HLX,
+};
+
+static inline void gen_movw_v_reg(TCGv v, int regpair)
+{
+    gen_movw_v_reg_tbl[regpair](v);
+}
+
+static gen_mov_func *const gen_movw_reg_v_tbl[] = {
+    [OR2_AF]  = gen_movw_AF_v,
+    [OR2_BC]  = gen_movw_BC_v,
+    [OR2_DE]  = gen_movw_DE_v,
+    [OR2_HL]  = gen_movw_HL_v,
+
+    [OR2_IX]  = gen_movw_IX_v,
+    [OR2_IY]  = gen_movw_IY_v,
+    [OR2_SP]  = gen_movw_SP_v,
+
+    [OR2_AFX] = gen_movw_AFX_v,
+    [OR2_BCX] = gen_movw_BCX_v,
+    [OR2_DEX] = gen_movw_DEX_v,
+    [OR2_HLX] = gen_movw_HLX_v,
+};
+
+static inline void gen_movw_reg_v(int regpair, TCGv v)
+{
+    gen_movw_reg_v_tbl[regpair](v);
+}
+
+static inline int regpairmap(int regpair, int m)
+{
+    switch (regpair) {
+    case OR2_HL:
+        switch (m) {
+        case MODE_DD:
+            return OR2_IX;
+        case MODE_FD:
+            return OR2_IY;
+        case MODE_NORMAL:
+        default:
+            return OR2_HL;
+        }
+    default:
+        return regpair;
+    }
+}
+
+static const int regpair[4] = {
+    OR2_BC,
+    OR2_DE,
+    OR2_HL,
+    OR2_SP,
+};
+
+static const int regpair2[4] = {
+    OR2_BC,
+    OR2_DE,
+    OR2_HL,
+    OR2_AF,
+};
+
+static inline void gen_jmp_im(target_ulong pc)
+{
+    gen_helper_movl_pc_im(tcg_const_tl(pc));
+}
+
+static void gen_debug(DisasContext *s, target_ulong cur_pc)
+{
+    gen_jmp_im(cur_pc);
+    gen_helper_debug();
+    s->is_jmp = 3;
+}
+
+static void gen_eob(DisasContext *s)
+{
+    if (s->tb->flags & HF_INHIBIT_IRQ_MASK) {
+        gen_helper_reset_inhibit_irq();
+    }
+    if (s->singlestep_enabled) {
+        gen_helper_debug();
+    } else {
+        tcg_gen_exit_tb(0);
+    }
+    s->is_jmp = 3;
+}
+
+static void gen_exception(DisasContext *s, int trapno, target_ulong cur_pc)
+{
+    gen_jmp_im(cur_pc);
+    gen_helper_raise_exception(trapno);
+    s->is_jmp = 3;
+}
+
+/* Conditions */
+
+static const char *const cc[8] = {
+    "nz",
+    "z",
+    "nc",
+    "c",
+    "po",
+    "pe",
+    "p",
+    "m",
+};
+
+enum {
+    COND_NZ = 0,
+    COND_Z,
+    COND_NC,
+    COND_C,
+    COND_PO,
+    COND_PE,
+    COND_P,
+    COND_M,
+};
+
+static const int cc_flags[4] = {
+    CC_Z,
+    CC_C,
+    CC_P,
+    CC_S,
+};
+
+/* Arithmetic/logic operations */
+
+static const char *const alu[8] = {
+    "add a,",
+    "adc a,",
+    "sub ",
+    "sbc a,",
+    "and ",
+    "xor ",
+    "or ",
+    "cp ",
+};
+
+typedef void (alu_helper_func)(void);
+
+static alu_helper_func *const gen_alu[8] = {
+    gen_helper_add_cc,
+    gen_helper_adc_cc,
+    gen_helper_sub_cc,
+    gen_helper_sbc_cc,
+    gen_helper_and_cc,
+    gen_helper_xor_cc,
+    gen_helper_or_cc,
+    gen_helper_cp_cc,
+};
+
+/* Rotation/shift operations */
+
+static const char *const rot[8] = {
+    "rlc",
+    "rrc",
+    "rl",
+    "rr",
+    "sla",
+    "sra",
+    "sll",
+    "srl",
+};
+
+typedef void (rot_helper_func)(void);
+
+static rot_helper_func *const gen_rot_T0[8] = {
+    gen_helper_rlc_T0_cc,
+    gen_helper_rrc_T0_cc,
+    gen_helper_rl_T0_cc,
+    gen_helper_rr_T0_cc,
+    gen_helper_sla_T0_cc,
+    gen_helper_sra_T0_cc,
+    gen_helper_sll_T0_cc,
+    gen_helper_srl_T0_cc,
+};
+
+/* Block instructions */
+
+static const char *const bli[4][4] = {
+    { "ldi",  "cpi",  "ini",  "outi", },
+    { "ldd",  "cpd",  "ind",  "outd", },
+    { "ldir", "cpir", "inir", "otir", },
+    { "lddr", "cpdr", "indr", "otdr", },
+};
+
+static const int imode[8] = {
+    0, 0, 1, 2, 0, 0, 1, 2,
+};
+
+static inline void gen_goto_tb(DisasContext *s, int tb_num, target_ulong pc)
+{
+    gen_jmp_im(pc);
+    gen_eob(s);
+}
+
+static inline void gen_cond_jump(int cc, int l1)
+{
+    gen_movb_v_F(cpu_T[0]);
+
+    tcg_gen_andi_tl(cpu_T[0], cpu_T[0], cc_flags[cc >> 1]);
+
+    tcg_gen_brcondi_tl((cc & 1) ? TCG_COND_NE : TCG_COND_EQ, cpu_T[0], 0, l1);
+}
+
+static inline void gen_jcc(DisasContext *s, int cc,
+                           target_ulong val, target_ulong next_pc)
+{
+    TranslationBlock *tb;
+    int l1;
+
+    tb = s->tb;
+
+    l1 = gen_new_label();
+
+    gen_cond_jump(cc, l1);
+
+    gen_goto_tb(s, 0, next_pc);
+
+    gen_set_label(l1);
+    gen_goto_tb(s, 1, val);
+
+    s->is_jmp = 3;
+}
+
+static inline void gen_callcc(DisasContext *s, int cc,
+                              target_ulong val, target_ulong next_pc)
+{
+    TranslationBlock *tb;
+    int l1;
+
+    tb = s->tb;
+
+    l1 = gen_new_label();
+
+    gen_cond_jump(cc, l1);
+
+    gen_goto_tb(s, 0, next_pc);
+
+    gen_set_label(l1);
+    tcg_gen_movi_tl(cpu_T[0], next_pc);
+    gen_pushw(cpu_T[0]);
+    gen_goto_tb(s, 1, val);
+
+    s->is_jmp = 3;
+}
+
+static inline void gen_retcc(DisasContext *s, int cc,
+                             target_ulong next_pc)
+{
+    TranslationBlock *tb;
+    int l1;
+
+    tb = s->tb;
+
+    l1 = gen_new_label();
+
+    gen_cond_jump(cc, l1);
+
+    gen_goto_tb(s, 0, next_pc);
+
+    gen_set_label(l1);
+    gen_popw(cpu_T[0]);
+    gen_helper_jmp_T0();
+    gen_eob(s);
+
+    s->is_jmp = 3;
+}
+
+static inline void gen_ex(int regpair1, int regpair2)
+{
+    TCGv tmp1 = tcg_temp_new();
+    TCGv tmp2 = tcg_temp_new();
+    gen_movw_v_reg(tmp1, regpair1);
+    gen_movw_v_reg(tmp2, regpair2);
+    gen_movw_reg_v(regpair2, tmp1);
+    gen_movw_reg_v(regpair1, tmp2);
+    tcg_temp_free(tmp1);
+    tcg_temp_free(tmp2);
+}
+
+/* TODO: condition code optimisation */
+
+/* micro-ops that modify condition codes should end in _cc */
+
+/* convert one instruction. s->is_jmp is set if the translation must
+   be stopped. Return the next pc value */
+static target_ulong disas_insn(DisasContext *s, target_ulong pc_start)
+{
+    int b, prefixes;
+    int rex_w, rex_r;
+    int m;
+
+    s->pc = pc_start;
+    prefixes = 0;
+    s->override = -1;
+    rex_w = -1;
+    rex_r = 0;
+
+    //printf("PC = %04x: ", s->pc);
+next_byte:
+    s->prefix = prefixes;
+
+/* START */
+
+    if (prefixes & PREFIX_DD) {
+        m = MODE_DD;
+    } else if (prefixes & PREFIX_FD) {
+        m = MODE_FD;
+    } else {
+        m = MODE_NORMAL;
+    }
+
+    /* unprefixed opcodes */
+
+    if ((prefixes & (PREFIX_CB | PREFIX_ED)) == 0) {
+        b = ldub_code(s->pc);
+        s->pc++;
+
+        int x, y, z, p, q;
+        int n, d;
+        int r1, r2;
+
+        x = (b >> 6) & 0x03;
+        y = (b >> 3) & 0x07;
+        z = b & 0x07;
+        p = y >> 1;
+        q = y & 0x01;
+
+        switch (x) {
+        case 0:
+            switch (z) {
+
+            case 0:
+                switch (y) {
+                case 0:
+                    zprintf("nop\n");
+                    break;
+                case 1:
+                    gen_ex(OR2_AF, OR2_AFX);
+                    zprintf("ex af,af'\n");
+                    break;
+                case 2:
+                    n = ldsb_code(s->pc);
+                    s->pc++;
+                    gen_helper_djnz(tcg_const_tl(s->pc + n), tcg_const_tl(s->pc));
+                    gen_eob(s);
+                    s->is_jmp = 3;
+                    zprintf("djnz $%02x\n", n);
+                    break;
+                case 3:
+                    n = ldsb_code(s->pc);
+                    s->pc++;
+                    gen_jmp_im(s->pc + n);
+                    gen_eob(s);
+                    s->is_jmp = 3;
+                    zprintf("jr $%02x\n", n);
+                    break;
+                case 4:
+                case 5:
+                case 6:
+                case 7:
+                    n = ldsb_code(s->pc);
+                    s->pc++;
+                    zprintf("jr %s,$%04x\n", cc[y-4], (s->pc + n) & 0xffff);
+                    gen_jcc(s, y-4, s->pc + n, s->pc);
+                    break;
+                }
+                break;
+
+            case 1:
+                switch (q) {
+                case 0:
+                    n = lduw_code(s->pc);
+                    s->pc += 2;
+                    tcg_gen_movi_tl(cpu_T[0], n);
+                    r1 = regpairmap(regpair[p], m);
+                    gen_movw_reg_v(r1, cpu_T[0]);
+                    zprintf("ld %s,$%04x\n", regpairnames[r1], n);
+                    break;
+                case 1:
+                    r1 = regpairmap(regpair[p], m);
+                    r2 = regpairmap(OR2_HL, m);
+                    gen_movw_v_reg(cpu_T[0], r1);
+                    gen_movw_v_reg(cpu_T[1], r2);
+                    gen_helper_addw_T0_T1_cc();
+                    gen_movw_reg_v(r2, cpu_T[0]);
+                    zprintf("add %s,%s\n", regpairnames[r2], regpairnames[r1]);
+                    break;
+                }
+                break;
+
+            case 2:
+                switch (q) {
+                case 0:
+                    switch (p) {
+                    case 0:
+                        gen_movb_v_A(cpu_T[0]);
+                        gen_movw_v_BC(cpu_A0);
+                        tcg_gen_qemu_st8(cpu_T[0], cpu_A0, MEM_INDEX);
+                        zprintf("ld (bc),a\n");
+                        break;
+                    case 1:
+                        gen_movb_v_A(cpu_T[0]);
+                        gen_movw_v_DE(cpu_A0);
+                        tcg_gen_qemu_st8(cpu_T[0], cpu_A0, MEM_INDEX);
+                        zprintf("ld (de),a\n");
+                        break;
+                    case 2:
+                        n = lduw_code(s->pc);
+                        s->pc += 2;
+                        r1 = regpairmap(OR2_HL, m);
+                        gen_movw_v_reg(cpu_T[0], r1);
+                        tcg_gen_movi_i32(cpu_A0, n);
+                        tcg_gen_qemu_st16(cpu_T[0], cpu_A0, MEM_INDEX);
+                        zprintf("ld ($%04x),%s\n", n, regpairnames[r1]);
+                        break;
+                    case 3:
+                        n = lduw_code(s->pc);
+                        s->pc += 2;
+                        gen_movb_v_A(cpu_T[0]);
+                        tcg_gen_movi_i32(cpu_A0, n);
+                        tcg_gen_qemu_st8(cpu_T[0], cpu_A0, MEM_INDEX);
+                        zprintf("ld ($%04x),a\n", n);
+                        break;
+                    }
+                    break;
+                case 1:
+                    switch (p) {
+                    case 0:
+                        gen_movw_v_BC(cpu_A0);
+                        tcg_gen_qemu_ld8u(cpu_T[0], cpu_A0, MEM_INDEX);
+                        gen_movb_A_v(cpu_T[0]);
+                        zprintf("ld a,(bc)\n");
+                        break;
+                    case 1:
+                        gen_movw_v_DE(cpu_A0);
+                        tcg_gen_qemu_ld8u(cpu_T[0], cpu_A0, MEM_INDEX);
+                        gen_movb_A_v(cpu_T[0]);
+                        zprintf("ld a,(de)\n");
+                        break;
+                    case 2:
+                        n = lduw_code(s->pc);
+                        s->pc += 2;
+                        r1 = regpairmap(OR2_HL, m);
+                        tcg_gen_movi_i32(cpu_A0, n);
+                        tcg_gen_qemu_ld16u(cpu_T[0], cpu_A0, MEM_INDEX);
+                        gen_movw_reg_v(r1, cpu_T[0]);
+                        zprintf("ld %s,($%04x)\n", regpairnames[r1], n);
+                        break;
+                    case 3:
+                        n = lduw_code(s->pc);
+                        s->pc += 2;
+                        tcg_gen_movi_i32(cpu_A0, n);
+                        tcg_gen_qemu_ld8u(cpu_T[0], cpu_A0, MEM_INDEX);
+                        gen_movb_A_v(cpu_T[0]);
+                        zprintf("ld a,($%04x)\n", n);
+                        break;
+                    }
+                    break;
+                }
+                break;
+
+            case 3:
+                switch (q) {
+                case 0:
+                    r1 = regpairmap(regpair[p], m);
+                    gen_movw_v_reg(cpu_T[0], r1);
+                    tcg_gen_addi_tl(cpu_T[0], cpu_T[0], 1);
+                    gen_movw_reg_v(r1, cpu_T[0]);
+                    zprintf("inc %s\n", regpairnames[r1]);
+                    break;
+                case 1:
+                    r1 = regpairmap(regpair[p], m);
+                    gen_movw_v_reg(cpu_T[0], r1);
+                    tcg_gen_subi_tl(cpu_T[0], cpu_T[0], 1);
+                    gen_movw_reg_v(r1, cpu_T[0]);
+                    zprintf("dec %s\n", regpairnames[r1]);
+                    break;
+                }
+                break;
+
+            case 4:
+                r1 = regmap(reg[y], m);
+                if (is_indexed(r1)) {
+                    d = ldsb_code(s->pc);
+                    s->pc++;
+                    gen_movb_v_idx(cpu_T[0], r1, d);
+                } else {
+                    gen_movb_v_reg(cpu_T[0], r1);
+                }
+                gen_helper_incb_T0_cc();
+                if (is_indexed(r1)) {
+                    gen_movb_idx_v(r1, cpu_T[0], d);
+                } else {
+                    gen_movb_reg_v(r1, cpu_T[0]);
+                }
+                if (is_indexed(r1)) {
+                    zprintf("inc (%s%c$%02x)\n", idxnames[r1], shexb(d));
+                } else {
+                    zprintf("inc %s\n", regnames[r1]);
+                }
+                break;
+
+            case 5:
+                r1 = regmap(reg[y], m);
+                if (is_indexed(r1)) {
+                    d = ldsb_code(s->pc);
+                    s->pc++;
+                    gen_movb_v_idx(cpu_T[0], r1, d);
+                } else {
+                    gen_movb_v_reg(cpu_T[0], r1);
+                }
+                gen_helper_decb_T0_cc();
+                if (is_indexed(r1)) {
+                    gen_movb_idx_v(r1, cpu_T[0], d);
+                } else {
+                    gen_movb_reg_v(r1, cpu_T[0]);
+                }
+                if (is_indexed(r1)) {
+                    zprintf("dec (%s%c$%02x)\n", idxnames[r1], shexb(d));
+                } else {
+                    zprintf("dec %s\n", regnames[r1]);
+                }
+                break;
+
+            case 6:
+                r1 = regmap(reg[y], m);
+                if (is_indexed(r1)) {
+                    d = ldsb_code(s->pc);
+                    s->pc++;
+                }
+                n = ldub_code(s->pc);
+                s->pc++;
+                tcg_gen_movi_tl(cpu_T[0], n);
+                if (is_indexed(r1)) {
+                    gen_movb_idx_v(r1, cpu_T[0], d);
+                } else {
+                    gen_movb_reg_v(r1, cpu_T[0]);
+                }
+                if (is_indexed(r1)) {
+                    zprintf("ld (%s%c$%02x),$%02x\n", idxnames[r1], shexb(d), n);
+                } else {
+                    zprintf("ld %s,$%02x\n", regnames[r1], n);
+                }
+                break;
+
+            case 7:
+                switch (y) {
+                case 0:
+                    gen_helper_rlca_cc();
+                    zprintf("rlca\n");
+                    break;
+                case 1:
+                    gen_helper_rrca_cc();
+                    zprintf("rrca\n");
+                    break;
+                case 2:
+                    gen_helper_rla_cc();
+                    zprintf("rla\n");
+                    break;
+                case 3:
+                    gen_helper_rra_cc();
+                    zprintf("rra\n");
+                    break;
+                case 4:
+                    gen_helper_daa_cc();
+                    zprintf("daa\n");
+                    break;
+                case 5:
+                    gen_helper_cpl_cc();
+                    zprintf("cpl\n");
+                    break;
+                case 6:
+                    gen_helper_scf_cc();
+                    zprintf("scf\n");
+                    break;
+                case 7:
+                    gen_helper_ccf_cc();
+                    zprintf("ccf\n");
+                    break;
+                }
+                break;
+            }
+            break;
+
+        case 1:
+            if (z == 6 && y == 6) {
+                gen_jmp_im(s->pc);
+                gen_helper_halt();
+                zprintf("halt\n");
+            } else {
+                if (z == 6) {
+                    r1 = regmap(reg[z], m);
+                    r2 = regmap(reg[y], 0);
+                } else if (y == 6) {
+                    r1 = regmap(reg[z], 0);
+                    r2 = regmap(reg[y], m);
+                } else {
+                    r1 = regmap(reg[z], m);
+                    r2 = regmap(reg[y], m);
+                }
+                if (is_indexed(r1) || is_indexed(r2)) {
+                    d = ldsb_code(s->pc);
+                    s->pc++;
+                }
+                if (is_indexed(r1)) {
+                    gen_movb_v_idx(cpu_T[0], r1, d);
+                } else {
+                    gen_movb_v_reg(cpu_T[0], r1);
+                }
+                if (is_indexed(r2)) {
+                    gen_movb_idx_v(r2, cpu_T[0], d);
+                } else {
+                    gen_movb_reg_v(r2, cpu_T[0]);
+                }
+                if (is_indexed(r1)) {
+                    zprintf("ld %s,(%s%c$%02x)\n", regnames[r2], idxnames[r1], shexb(d));
+                } else if (is_indexed(r2)) {
+                    zprintf("ld (%s%c$%02x),%s\n", idxnames[r2], shexb(d), regnames[r1]);
+                } else {
+                    zprintf("ld %s,%s\n", regnames[r2], regnames[r1]);
+                }
+            }
+            break;
+
+        case 2:
+            r1 = regmap(reg[z], m);
+            if (is_indexed(r1)) {
+                d = ldsb_code(s->pc);
+                s->pc++;
+                gen_movb_v_idx(cpu_T[0], r1, d);
+            } else {
+                gen_movb_v_reg(cpu_T[0], r1);
+            }
+            gen_alu[y](); /* places output in A */
+            if (is_indexed(r1)) {
+                zprintf("%s(%s%c$%02x)\n", alu[y], idxnames[r1], shexb(d));
+            } else {
+                zprintf("%s%s\n", alu[y], regnames[r1]);
+            }
+            break;
+
+        case 3:
+            switch (z) {
+            case 0:
+                gen_retcc(s, y, s->pc);
+                zprintf("ret %s\n", cc[y]);
+                break;
+
+            case 1:
+                switch (q) {
+                case 0:
+                    r1 = regpairmap(regpair2[p], m);
+                    gen_popw(cpu_T[0]);
+                    gen_movw_reg_v(r1, cpu_T[0]);
+                    zprintf("pop %s\n", regpairnames[r1]);
+                    break;
+                case 1:
+                    switch (p) {
+                    case 0:
+                        gen_popw(cpu_T[0]);
+                        gen_helper_jmp_T0();
+                        zprintf("ret\n");
+                        gen_eob(s);
+                        s->is_jmp = 3;
+//                      s->is_ei = 1;
+                        break;
+                    case 1:
+                        gen_ex(OR2_BC, OR2_BCX);
+                        gen_ex(OR2_DE, OR2_DEX);
+                        gen_ex(OR2_HL, OR2_HLX);
+                        zprintf("exx\n");
+                        break;
+                    case 2:
+                        r1 = regpairmap(OR2_HL, m);
+                        gen_movw_v_reg(cpu_T[0], r1);
+                        gen_helper_jmp_T0();
+                        zprintf("jp %s\n", regpairnames[r1]);
+                        gen_eob(s);
+                        s->is_jmp = 3;
+                        break;
+                    case 3:
+                        r1 = regpairmap(OR2_HL, m);
+                        gen_movw_v_reg(cpu_T[0], r1);
+                        gen_movw_SP_v(cpu_T[0]);
+                        zprintf("ld sp,%s\n", regpairnames[r1]);
+                        break;
+                    }
+                    break;
+                }
+                break;
+
+            case 2:
+                n = lduw_code(s->pc);
+                s->pc += 2;
+                gen_jcc(s, y, n, s->pc);
+                zprintf("jp %s,$%04x\n", cc[y], n);
+                break;
+
+            case 3:
+                switch (y) {
+                case 0:
+                    n = lduw_code(s->pc);
+                    s->pc += 2;
+                    gen_jmp_im(n);
+                    zprintf("jp $%04x\n", n);
+                    gen_eob(s);
+                    s->is_jmp = 3;
+                    break;
+                case 1:
+                    zprintf("cb prefix\n");
+                    prefixes |= PREFIX_CB;
+                    goto next_byte;
+                    break;
+                case 2:
+                    n = ldub_code(s->pc);
+                    s->pc++;
+                    gen_movb_v_A(cpu_T[0]);
+                    if (use_icount) {
+                        gen_io_start();
+                    }
+                    gen_helper_out_T0_im(tcg_const_tl(n));
+                    if (use_icount) {
+                        gen_io_end();
+                        gen_jmp_im(s->pc);
+                    }
+                    zprintf("out ($%02x),a\n", n);
+                    break;
+                case 3:
+                    n = ldub_code(s->pc);
+                    s->pc++;
+                    if (use_icount) {
+                        gen_io_start();
+                    }
+                    gen_helper_in_T0_im(tcg_const_tl(n));
+                    gen_movb_A_v(cpu_T[0]);
+                    if (use_icount) {
+                        gen_io_end();
+                        gen_jmp_im(s->pc);
+                    }
+                    zprintf("in a,($%02x)\n", n);
+                    break;
+                case 4:
+                    r1 = regpairmap(OR2_HL, m);
+                    gen_popw(cpu_T[1]);
+                    gen_movw_v_reg(cpu_T[0], r1);
+                    gen_pushw(cpu_T[0]);
+                    gen_movw_reg_v(r1, cpu_T[1]);
+                    zprintf("ex (sp),%s\n", regpairnames[r1]);
+                    break;
+                case 5:
+                    gen_ex(OR2_DE, OR2_HL);
+                    zprintf("ex de,hl\n");
+                    break;
+                case 6:
+                    gen_helper_di();
+                    zprintf("di\n");
+                    break;
+                case 7:
+                    gen_helper_ei();
+                    zprintf("ei\n");
+//                  gen_eob(s);
+//                  s->is_ei = 1;
+                    break;
+                }
+                break;
+
+            case 4:
+                n = lduw_code(s->pc);
+                s->pc += 2;
+                gen_callcc(s, y, n, s->pc);
+                zprintf("call %s,$%04x\n", cc[y], n);
+                break;
+
+            case 5:
+                switch (q) {
+                case 0:
+                    r1 = regpairmap(regpair2[p], m);
+                    gen_movw_v_reg(cpu_T[0], r1);
+                    gen_pushw(cpu_T[0]);
+                    zprintf("push %s\n", regpairnames[r1]);
+                    break;
+                case 1:
+                    switch (p) {
+                    case 0:
+                        n = lduw_code(s->pc);
+                        s->pc += 2;
+                        tcg_gen_movi_tl(cpu_T[0], s->pc);
+                        gen_pushw(cpu_T[0]);
+                        gen_jmp_im(n);
+                        zprintf("call $%04x\n", n);
+                        gen_eob(s);
+                        s->is_jmp = 3;
+                        break;
+                    case 1:
+                        zprintf("dd prefix\n");
+                        prefixes |= PREFIX_DD;
+                        goto next_byte;
+                        break;
+                    case 2:
+                        zprintf("ed prefix\n");
+                        prefixes |= PREFIX_ED;
+                        goto next_byte;
+                        break;
+                    case 3:
+                        zprintf("fd prefix\n");
+                        prefixes |= PREFIX_FD;
+                        goto next_byte;
+                        break;
+                    }
+                    break;
+                }
+                break;
+
+            case 6:
+                n = ldub_code(s->pc);
+                s->pc++;
+                tcg_gen_movi_tl(cpu_T[0], n);
+                gen_alu[y](); /* places output in A */
+                zprintf("%s$%02x\n", alu[y], n);
+                break;
+
+            case 7:
+                tcg_gen_movi_tl(cpu_T[0], s->pc);
+                gen_pushw(cpu_T[0]);
+                gen_jmp_im(y*8);
+                zprintf("rst $%02x\n", y*8);
+                gen_eob(s);
+                s->is_jmp = 3;
+                break;
+            }
+            break;
+        }
+    } else if (prefixes & PREFIX_CB) {
+        /* cb mode: */
+
+        int x, y, z, p, q;
+        int d;
+        int r1, r2;
+
+        if (m != MODE_NORMAL) {
+            d = ldsb_code(s->pc);
+            s->pc++;
+        }
+
+        b = ldub_code(s->pc);
+        s->pc++;
+
+        x = (b >> 6) & 0x03;
+        y = (b >> 3) & 0x07;
+        z = b & 0x07;
+        p = y >> 1;
+        q = y & 0x01;
+
+        if (m != MODE_NORMAL) {
+            r1 = regmap(OR_HLmem, m);
+            gen_movb_v_idx(cpu_T[0], r1, d);
+            if (z != 6) {
+                r2 = regmap(reg[z], 0);
+            }
+        } else {
+            r1 = regmap(reg[z], m);
+            gen_movb_v_reg(cpu_T[0], r1);
+        }
+
+        switch (x) {
+        case 0:
+            /* TODO: TST instead of SLL for R800 */
+            gen_rot_T0[y]();
+            if (m != MODE_NORMAL) {
+                gen_movb_idx_v(r1, cpu_T[0], d);
+                if (z != 6) {
+                    gen_movb_reg_v(r2, cpu_T[0]);
+                }
+            } else {
+                gen_movb_reg_v(r1, cpu_T[0]);
+            }
+            zprintf("%s %s\n", rot[y], regnames[r1]);
+            break;
+        case 1:
+            gen_helper_bit_T0(tcg_const_tl(1 << y));
+            zprintf("bit %i,%s\n", y, regnames[r1]);
+            break;
+        case 2:
+            tcg_gen_andi_tl(cpu_T[0], cpu_T[0], ~(1 << y));
+            if (m != MODE_NORMAL) {
+                gen_movb_idx_v(r1, cpu_T[0], d);
+                if (z != 6) {
+                    gen_movb_reg_v(r2, cpu_T[0]);
+                }
+            } else {
+                gen_movb_reg_v(r1, cpu_T[0]);
+            }
+            zprintf("res %i,%s\n", y, regnames[r1]);
+            break;
+        case 3:
+            tcg_gen_ori_tl(cpu_T[0], cpu_T[0], 1 << y);
+            if (m != MODE_NORMAL) {
+                gen_movb_idx_v(r1, cpu_T[0], d);
+                if (z != 6) {
+                    gen_movb_reg_v(r2, cpu_T[0]);
+                }
+            } else {
+                gen_movb_reg_v(r1, cpu_T[0]);
+            }
+            zprintf("set %i,%s\n", y, regnames[r1]);
+            break;
+        }
+
+    } else if (prefixes & PREFIX_ED) {
+        /* ed mode: */
+
+        b = ldub_code(s->pc);
+        s->pc++;
+
+        int x, y, z, p, q;
+        int n;
+        int r1, r2;
+
+        x = (b >> 6) & 0x03;
+        y = (b >> 3) & 0x07;
+        z = b & 0x07;
+        p = y >> 1;
+        q = y & 0x01;
+
+        switch (x) {
+        case 0:
+            zprintf("nop\n");
+            break;
+        case 3:
+            if (s->model == Z80_CPU_R800) {
+                switch (z) {
+                case 1:
+                    /* does mulub work with r1 == h, l, (hl) or a? */
+                    r1 = regmap(reg[y], m);
+                    gen_movb_v_reg(cpu_T[0], r1);
+                    gen_helper_mulub_cc();
+                    zprintf("mulub a,%s\n", regnames[r1]);
+                    break;
+                case 3:
+                    if (q == 0) {
+                        /* does muluw work with r1 == de or hl? */
+                        /* what is the effect of DD/FD prefixes here? */
+                        r1 = regpairmap(regpair[p], m);
+                        gen_movw_v_reg(cpu_T[0], r1);
+                        gen_helper_muluw_cc();
+                        zprintf("muluw hl,%s\n", regpairnames[r1]);
+                    } else {
+                        zprintf("nop\n");
+                    }
+                    break;
+                default:
+                    zprintf("nop\n");
+                    break;
+                }
+            } else {
+                zprintf("nop\n");
+            }
+            break;
+
+        case 1:
+            switch (z) {
+            case 0:
+                if (use_icount) {
+                    gen_io_start();
+                }
+                gen_helper_in_T0_bc_cc();
+                if (y != 6) {
+                    r1 = regmap(reg[y], m);
+                    gen_movb_reg_v(r1, cpu_T[0]);
+                    zprintf("in %s,(c)\n", regnames[r1]);
+                } else {
+                    zprintf("in (c)\n");
+                }
+                if (use_icount) {
+                    gen_io_end();
+                    gen_jmp_im(s->pc);
+                }
+                break;
+            case 1:
+                if (y != 6) {
+                    r1 = regmap(reg[y], m);
+                    gen_movb_v_reg(cpu_T[0], r1);
+                    zprintf("out (c),%s\n", regnames[r1]);
+                } else {
+                    tcg_gen_movi_tl(cpu_T[0], 0);
+                    zprintf("out (c),0\n");
+                }
+                if (use_icount) {
+                    gen_io_start();
+                }
+                gen_helper_out_T0_bc();
+                if (use_icount) {
+                    gen_io_end();
+                    gen_jmp_im(s->pc);
+                }
+                break;
+            case 2:
+                r1 = regpairmap(OR2_HL, m);
+                r2 = regpairmap(regpair[p], m);
+                gen_movw_v_reg(cpu_T[0], r1);
+                gen_movw_v_reg(cpu_T[1], r2);
+                if (q == 0) {
+                    zprintf("sbc %s,%s\n", regpairnames[r1], regpairnames[r2]);
+                    gen_helper_sbcw_T0_T1_cc();
+                } else {
+                    zprintf("adc %s,%s\n", regpairnames[r1], regpairnames[r2]);
+                    gen_helper_adcw_T0_T1_cc();
+                }
+                gen_movw_reg_v(r1, cpu_T[0]);
+                break;
+            case 3:
+                n = lduw_code(s->pc);
+                s->pc += 2;
+                r1 = regpairmap(regpair[p], m);
+                if (q == 0) {
+                    gen_movw_v_reg(cpu_T[0], r1);
+                    tcg_gen_movi_i32(cpu_A0, n);
+                    tcg_gen_qemu_st16(cpu_T[0], cpu_A0, MEM_INDEX);
+                    zprintf("ld ($%02x),%s\n", n, regpairnames[r1]);
+                } else {
+                    tcg_gen_movi_i32(cpu_A0, n);
+                    tcg_gen_qemu_ld16u(cpu_T[0], cpu_A0, MEM_INDEX);
+                    gen_movw_reg_v(r1, cpu_T[0]);
+                    zprintf("ld %s,($%02x)\n", regpairnames[r1], n);
+                }
+                break;
+            case 4:
+                zprintf("neg\n");
+                gen_helper_neg_cc();
+                break;
+            case 5:
+                /* FIXME */
+                gen_popw(cpu_T[0]);
+                gen_helper_jmp_T0();
+                gen_helper_ri();
+                if (q == 0) {
+                    zprintf("retn\n");
+                } else {
+                    zprintf("reti\n");
+                }
+                gen_eob(s);
+                s->is_jmp = 3;
+//              s->is_ei = 1;
+                break;
+            case 6:
+                gen_helper_imode(tcg_const_tl(imode[y]));
+                zprintf("im im[%i]\n", imode[y]);
+//              gen_eob(s);
+//              s->is_ei = 1;
+                break;
+            case 7:
+                switch (y) {
+                case 0:
+                    gen_helper_ld_I_A();
+                    zprintf("ld i,a\n");
+                    break;
+                case 1:
+                    gen_helper_ld_R_A();
+                    zprintf("ld r,a\n");
+                    break;
+                case 2:
+                    gen_helper_ld_A_I();
+                    zprintf("ld a,i\n");
+                    break;
+                case 3:
+                    gen_helper_ld_A_R();
+                    zprintf("ld a,r\n");
+                    break;
+                case 4:
+                    gen_movb_v_HLmem(cpu_T[0]);
+                    gen_helper_rrd_cc();
+                    gen_movb_HLmem_v(cpu_T[0]);
+                    zprintf("rrd\n");
+                    break;
+                case 5:
+                    gen_movb_v_HLmem(cpu_T[0]);
+                    gen_helper_rld_cc();
+                    gen_movb_HLmem_v(cpu_T[0]);
+                    zprintf("rld\n");
+                    break;
+                case 6:
+                case 7:
+                    zprintf("nop2\n");
+                    /* nop */
+                    break;
+                }
+                break;
+            }
+            break;
+
+        case 2:
+            /* FIXME */
+            if (y >= 4) {
+                switch (z) {
+                case 0: /* ldi/ldd/ldir/lddr */
+                    gen_movw_v_HL(cpu_A0);
+                    tcg_gen_qemu_ld8u(cpu_T[0], cpu_A0, MEM_INDEX);
+                    gen_movw_v_DE(cpu_A0);
+                    tcg_gen_qemu_st8(cpu_T[0], cpu_A0, MEM_INDEX);
+
+                    if (!(y & 1)) {
+                        gen_helper_bli_ld_inc_cc();
+                    } else {
+                        gen_helper_bli_ld_dec_cc();
+                    }
+                    if ((y & 2)) {
+                        gen_helper_bli_ld_rep(tcg_const_tl(s->pc));
+                        gen_eob(s);
+                        s->is_jmp = 3;
+                    }
+                    break;
+
+                case 1: /* cpi/cpd/cpir/cpdr */
+                    gen_movw_v_HL(cpu_A0);
+                    tcg_gen_qemu_ld8u(cpu_T[0], cpu_A0, MEM_INDEX);
+                    gen_helper_bli_cp_cc();
+
+                    if (!(y & 1)) {
+                        gen_helper_bli_cp_inc_cc();
+                    } else {
+                        gen_helper_bli_cp_dec_cc();
+                    }
+                    if ((y & 2)) {
+                        gen_helper_bli_cp_rep(tcg_const_tl(s->pc));
+                        gen_eob(s);
+                        s->is_jmp = 3;
+                    }
+                    break;
+
+                case 2: /* ini/ind/inir/indr */
+                    if (use_icount) {
+                        gen_io_start();
+                    }
+                    gen_helper_in_T0_bc_cc();
+                    if (use_icount) {
+                        gen_io_end();
+                    }
+                    gen_movw_v_HL(cpu_A0);
+                    tcg_gen_qemu_st8(cpu_T[0], cpu_A0, MEM_INDEX);
+                    if (!(y & 1)) {
+                        gen_helper_bli_io_inc();
+                    } else {
+                        gen_helper_bli_io_dec();
+                    }
+                    if ((y & 2)) {
+                        gen_helper_bli_io_rep(tcg_const_tl(s->pc));
+                        gen_eob(s);
+                        s->is_jmp = 3;
+                    } else if (use_icount) {
+                        gen_jmp_im(s->pc);
+                    }
+                    break;
+
+                case 3: /* outi/outd/otir/otdr */
+                    gen_movw_v_HL(cpu_A0);
+                    tcg_gen_qemu_ld8u(cpu_T[0], cpu_A0, MEM_INDEX);
+                    if (use_icount) {
+                        gen_io_start();
+                    }
+                    gen_helper_out_T0_bc();
+                    if (use_icount) {
+                        gen_io_end();
+                    }
+                    if (!(y & 1)) {
+                        gen_helper_bli_io_inc();
+                    } else {
+                        gen_helper_bli_io_dec();
+                    }
+                    if ((y & 2)) {
+                        gen_helper_bli_io_rep(tcg_const_tl(s->pc));
+                        gen_eob(s);
+                        s->is_jmp = 3;
+                    } else if (use_icount) {
+                        gen_jmp_im(s->pc);
+                    }
+                    break;
+                }
+
+                zprintf("%s\n", bli[y-4][z]);
+                break;
+            }
+        }
+    }
+
+    prefixes = 0;
+
+    /* now check op code */
+//    switch (b) {
+//    default:
+//        goto illegal_op;
+//    }
+    /* lock generation */
+    return s->pc;
+ illegal_op:
+    /* XXX: ensure that no lock was generated */
+    gen_exception(s, EXCP06_ILLOP, pc_start - s->cs_base);
+    return s->pc;
+}
+
+#define CC_SZHPNC (CC_S | CC_Z | CC_H | CC_P | CC_N | CC_C)
+#define CC_SZHPN (CC_S | CC_Z | CC_H | CC_P | CC_N)
+
+void z80_translate_init(void)
+{
+    cpu_env = tcg_global_reg_new_ptr(TCG_AREG0, "env");
+
+#if TARGET_LONG_BITS > HOST_LONG_BITS
+    cpu_T[0] = tcg_global_mem_new_i32(TCG_AREG0, offsetof(CPUState, t0), "T0");
+    cpu_T[1] = tcg_global_mem_new_i32(TCG_AREG0, offsetof(CPUState, t1), "T1");
+#else
+    cpu_T[0] = tcg_global_reg_new_i32(TCG_AREG1, "T0");
+    cpu_T[1] = tcg_global_reg_new_i32(TCG_AREG2, "T1");
+#endif
+    cpu_A0 = tcg_global_mem_new_i32(TCG_AREG0, offsetof(CPUState, a0), "A0");
+
+    /* register helpers */
+#define GEN_HELPER 2
+#include "helper.h"
+}
+
+/* generate intermediate code in gen_opc_buf and gen_opparam_buf for
+   basic block 'tb'. If search_pc is TRUE, also generate PC
+   information for each intermediate instruction. */
+static inline int gen_intermediate_code_internal(CPUState *env,
+                                                 TranslationBlock *tb,
+                                                 int search_pc)
+{
+    DisasContext dc1, *dc = &dc1;
+    target_ulong pc_ptr;
+    uint16_t *gen_opc_end;
+    CPUBreakpoint *bp;
+    int flags, j, lj, cflags;
+    target_ulong pc_start;
+    target_ulong cs_base;
+    int num_insns;
+    int max_insns;
+
+    /* generate intermediate code */
+    pc_start = tb->pc;
+    cs_base = tb->cs_base;
+    flags = tb->flags;
+    cflags = tb->cflags;
+
+    dc->singlestep_enabled = env->singlestep_enabled;
+    dc->cs_base = cs_base;
+    dc->tb = tb;
+    dc->flags = flags;
+    dc->jmp_opt = !(env->singlestep_enabled ||
+                    (flags & HF_INHIBIT_IRQ_MASK)
+#ifndef CONFIG_SOFTMMU
+                    || (flags & HF_SOFTMMU_MASK)
+#endif
+                    );
+
+    gen_opc_ptr = gen_opc_buf;
+    gen_opc_end = gen_opc_buf + OPC_MAX_SIZE;
+    gen_opparam_ptr = gen_opparam_buf;
+
+    dc->is_jmp = DISAS_NEXT;
+    pc_ptr = pc_start;
+    lj = -1;
+    dc->model = env->model;
+
+    num_insns = 0;
+    max_insns = tb->cflags & CF_COUNT_MASK;
+    if (max_insns == 0) {
+        max_insns = CF_COUNT_MASK;
+    }
+
+    gen_icount_start();
+    for (;;) {
+        if (unlikely(!TAILQ_EMPTY(&env->breakpoints))) {
+            TAILQ_FOREACH(bp, &env->breakpoints, entry) {
+                if (bp->pc == pc_ptr) {
+                    gen_debug(dc, pc_ptr - dc->cs_base);
+                    break;
+                }
+            }
+        }
+        if (search_pc) {
+            j = gen_opc_ptr - gen_opc_buf;
+            if (lj < j) {
+                lj++;
+                while (lj < j) {
+                    gen_opc_instr_start[lj++] = 0;
+                }
+            }
+            gen_opc_pc[lj] = pc_ptr;
+            gen_opc_instr_start[lj] = 1;
+            gen_opc_icount[lj] = num_insns;
+        }
+        if (num_insns + 1 == max_insns && (tb->cflags & CF_LAST_IO)) {
+            gen_io_start();
+        }
+
+        pc_ptr = disas_insn(dc, pc_ptr);
+        num_insns++;
+        /* stop translation if indicated */
+        if (dc->is_jmp) {
+            break;
+        }
+        /* if single step mode, we generate only one instruction and
+           generate an exception */
+        /* if irq were inhibited with HF_INHIBIT_IRQ_MASK, we clear
+           the flag and abort the translation to give the irqs a
+           change to be happen */
+        if (dc->singlestep_enabled ||
+            (flags & HF_INHIBIT_IRQ_MASK)) {
+            gen_jmp_im(pc_ptr - dc->cs_base);
+            gen_eob(dc);
+            break;
+        }
+        /* if too long translation, stop generation too */
+        if (gen_opc_ptr >= gen_opc_end ||
+            (pc_ptr - pc_start) >= (TARGET_PAGE_SIZE - 32) ||
+            num_insns >= max_insns) {
+            gen_jmp_im(pc_ptr - dc->cs_base);
+            gen_eob(dc);
+            break;
+        }
+        if (singlestep) {
+            gen_jmp_im(pc_ptr - dc->cs_base);
+            gen_eob(dc);
+            break;
+        }
+    }
+    if (tb->cflags & CF_LAST_IO) {
+        gen_io_end();
+    }
+    gen_icount_end(tb, num_insns);
+    *gen_opc_ptr = INDEX_op_end;
+    /* we don't forget to fill the last values */
+    if (search_pc) {
+        j = gen_opc_ptr - gen_opc_buf;
+        lj++;
+        while (lj <= j) {
+            gen_opc_instr_start[lj++] = 0;
+        }
+    }
+
+#ifdef DEBUG_DISAS
+    log_cpu_state_mask(CPU_LOG_TB_CPU, env, 0);
+    if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) {
+        qemu_log("----------------\n");
+        qemu_log("IN: %s\n", lookup_symbol(pc_start));
+        log_target_disas(pc_start, pc_ptr - pc_start, 0);
+        qemu_log("\n");
+    }
+#endif
+
+    if (!search_pc) {
+        tb->size = pc_ptr - pc_start;
+        tb->icount = num_insns;
+    }
+    return 0;
+}
+
+void gen_intermediate_code(CPUState *env, TranslationBlock *tb)
+{
+    gen_intermediate_code_internal(env, tb, 0);
+}
+
+void gen_intermediate_code_pc(CPUState *env, TranslationBlock *tb)
+{
+    gen_intermediate_code_internal(env, tb, 1);
+}
+
+void gen_pc_load(CPUState *env, TranslationBlock *tb,
+                 unsigned long searched_pc, int pc_pos, void *puc)
+{
+    env->pc = gen_opc_pc[pc_pos];
+}
diff --git a/z80-dis.c b/z80-dis.c
new file mode 100644
index 0000000..4af3c62
--- /dev/null
+++ b/z80-dis.c
@@ -0,0 +1,621 @@
+/* Print Z80 and R800 instructions
+   Copyright 2005 Free Software Foundation, Inc.
+   Contributed by Arnold Metselaar <arnold_m@operamail.com>
+
+   Taken from GDB
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+   MA 02110-1301, USA.  */
+
+#include "dis-asm.h"
+#include <stdio.h>
+
+struct buffer
+{
+  bfd_vma base;
+  int n_fetch;
+  int n_used;
+  signed char data[4];
+} ;
+
+typedef int (*func)(struct buffer *, disassemble_info *, char *);
+
+struct tab_elt
+{
+  unsigned char val;
+  unsigned char mask;
+  func          fp;
+  char *        text;
+} ;
+
+#define TXTSIZ 24
+/* Names of 16-bit registers.  */
+static char * rr_str[] = { "bc", "de", "hl", "sp" };
+/* Names of 8-bit registers.  */
+static char * r_str[]  = { "b", "c", "d", "e", "h", "l", "(hl)", "a" };
+/* Texts for condition codes.  */
+static char * cc_str[] = { "nz", "z", "nc", "c", "po", "pe", "p", "m" };
+/* Instruction names for 8-bit arithmetic, operand "a" is often implicit */
+static char * arit_str[] =
+{
+  "add a,", "adc a,", "sub ", "sbc a,", "and ", "xor ", "or ", "cp "
+} ;
+\f

+static int
+fetch_data (struct buffer *buf, disassemble_info * info, int n)
+{
+  int r;
+
+  if (buf->n_fetch + n > 4)
+    abort ();
+
+  r = info->read_memory_func (buf->base + buf->n_fetch,
+			      (unsigned char*) buf->data + buf->n_fetch,
+			      n, info);
+  if (r == 0)
+    buf->n_fetch += n;
+  return !r;
+}
+
+static int
+prt (struct buffer *buf, disassemble_info * info, char *txt)
+{
+  info->fprintf_func (info->stream, "%s", txt);
+  buf->n_used = buf->n_fetch;
+  return 1;
+}
+
+static int
+prt_e (struct buffer *buf, disassemble_info * info, char *txt)
+{
+  char e;
+  int target_addr;
+
+  if (fetch_data (buf, info, 1))
+    {
+      e = buf->data[1];
+      target_addr = (buf->base + 2 + e) & 0xffff;
+      buf->n_used = buf->n_fetch;
+      info->fprintf_func (info->stream, "%s0x%04x", txt, target_addr);
+    }
+  else
+    buf->n_used = -1;
+
+  return buf->n_used;
+}
+
+static int
+jr_cc (struct buffer *buf, disassemble_info * info, char *txt)
+{
+  char mytxt[TXTSIZ];
+
+  snprintf (mytxt, TXTSIZ, txt, cc_str[(buf->data[0] >> 3) & 3]);
+  return prt_e (buf, info, mytxt);
+}
+
+static int
+prt_nn (struct buffer *buf, disassemble_info * info, char *txt)
+{
+  int nn;
+  unsigned char *p;
+
+  p = (unsigned char*) buf->data + buf->n_fetch;
+  if (fetch_data (buf, info, 2))
+    {
+      nn = p[0] + (p[1] << 8);
+      info->fprintf_func (info->stream, txt, nn);
+      buf->n_used = buf->n_fetch;
+    }
+  else
+    buf->n_used = -1;
+  return buf->n_used;
+}
+
+static int
+prt_rr_nn (struct buffer *buf, disassemble_info * info, char *txt)
+{
+  char mytxt[TXTSIZ];
+
+  snprintf (mytxt, TXTSIZ, txt, rr_str[(buf->data[0] >> 4) & 3]);
+  return prt_nn (buf, info, mytxt);
+}
+
+static int
+prt_rr (struct buffer *buf, disassemble_info * info, char *txt)
+{
+  info->fprintf_func (info->stream, "%s%s", txt,
+		      rr_str[(buf->data[buf->n_fetch - 1] >> 4) & 3]);
+  buf->n_used = buf->n_fetch;
+  return buf->n_used;
+}
+
+static int
+prt_n (struct buffer *buf, disassemble_info * info, char *txt)
+{
+  int n;
+  unsigned char *p;
+
+  p = (unsigned char*) buf->data + buf->n_fetch;
+
+  if (fetch_data (buf, info, 1))
+    {
+      n = p[0];
+      info->fprintf_func (info->stream, txt, n);
+      buf->n_used = buf->n_fetch;
+    }
+  else
+    buf->n_used = -1;
+
+  return buf->n_used;
+}
+
+static int
+ld_r_n (struct buffer *buf, disassemble_info * info, char *txt)
+{
+  char mytxt[TXTSIZ];
+
+  snprintf (mytxt, TXTSIZ, txt, r_str[(buf->data[0] >> 3) & 7]);
+  return prt_n (buf, info, mytxt);
+}
+
+static int
+prt_r (struct buffer *buf, disassemble_info * info, char *txt)
+{
+  info->fprintf_func (info->stream, txt,
+		      r_str[(buf->data[buf->n_fetch - 1] >> 3) & 7]);
+  buf->n_used = buf->n_fetch;
+  return buf->n_used;
+}
+
+static int
+ld_r_r (struct buffer *buf, disassemble_info * info, char *txt)
+{
+  info->fprintf_func (info->stream, txt,
+		      r_str[(buf->data[buf->n_fetch - 1] >> 3) & 7],
+		      r_str[buf->data[buf->n_fetch - 1] & 7]);
+  buf->n_used = buf->n_fetch;
+  return buf->n_used;
+}
+
+static int
+arit_r (struct buffer *buf, disassemble_info * info, char *txt)
+{
+  info->fprintf_func (info->stream, txt,
+		      arit_str[(buf->data[buf->n_fetch - 1] >> 3) & 7],
+		      r_str[buf->data[buf->n_fetch - 1] & 7]);
+  buf->n_used = buf->n_fetch;
+  return buf->n_used;
+}
+
+static int
+prt_cc (struct buffer *buf, disassemble_info * info, char *txt)
+{
+  info->fprintf_func (info->stream, "%s%s", txt,
+		      cc_str[(buf->data[0] >> 3) & 7]);
+  buf->n_used = buf->n_fetch;
+  return buf->n_used;
+}
+
+static int
+pop_rr (struct buffer *buf, disassemble_info * info, char *txt)
+{
+  static char *rr_stack[] = { "bc","de","hl","af"};
+
+  info->fprintf_func (info->stream, "%s %s", txt,
+		      rr_stack[(buf->data[0] >> 4) & 3]);
+  buf->n_used = buf->n_fetch;
+  return buf->n_used;
+}
+
+
+static int
+jp_cc_nn (struct buffer *buf, disassemble_info * info, char *txt)
+{
+  char mytxt[TXTSIZ];
+
+  snprintf (mytxt,TXTSIZ,
+	    "%s%s,0x%%04x", txt, cc_str[(buf->data[0] >> 3) & 7]);
+  return prt_nn (buf, info, mytxt);
+}
+
+static int
+arit_n (struct buffer *buf, disassemble_info * info, char *txt)
+{
+  char mytxt[TXTSIZ];
+
+  snprintf (mytxt,TXTSIZ, txt, arit_str[(buf->data[0] >> 3) & 7]);
+  return prt_n (buf, info, mytxt);
+}
+
+static int
+rst (struct buffer *buf, disassemble_info * info, char *txt)
+{
+  info->fprintf_func (info->stream, txt, buf->data[0] & 0x38);
+  buf->n_used = buf->n_fetch;
+  return buf->n_used;
+}
+
+\f

+static int
+cis (struct buffer *buf, disassemble_info * info, char *txt ATTRIBUTE_UNUSED)
+{
+  static char * opar[] = { "ld", "cp", "in", "out" };
+  char * op;
+  char c;
+
+  c = buf->data[1];
+  op = ((0x13 & c) == 0x13) ? "ot" : (opar[c & 3]);
+  info->fprintf_func (info->stream,
+		      "%s%c%s", op,
+		      (c & 0x08) ? 'd' : 'i',
+		      (c & 0x10) ? "r" : "");
+  buf->n_used = 2;
+  return buf->n_used;
+}
+
+static int
+dump (struct buffer *buf, disassemble_info * info, char *txt)
+{
+  int i;
+
+  info->fprintf_func (info->stream, "defb ");
+  for (i = 0; txt[i]; ++i)
+    info->fprintf_func (info->stream, i ? ", 0x%02x" : "0x%02x",
+			(unsigned char) buf->data[i]);
+  buf->n_used = i;
+  return buf->n_used;
+}
+\f

+/* Table to disassemble machine codes with prefix 0xED.  */
+struct tab_elt opc_ed[] =
+{
+  { 0x70, 0xFF, prt, "in f,(c)" },
+  { 0x70, 0xFF, dump, "xx" },
+  { 0x40, 0xC7, prt_r, "in %s,(c)" },
+  { 0x71, 0xFF, prt, "out (c),0" },
+  { 0x70, 0xFF, dump, "xx" },
+  { 0x41, 0xC7, prt_r, "out (c),%s" },
+  { 0x42, 0xCF, prt_rr, "sbc hl," },
+  { 0x43, 0xCF, prt_rr_nn, "ld (0x%%04x),%s" },
+  { 0x44, 0xFF, prt, "neg" },
+  { 0x45, 0xFF, prt, "retn" },
+  { 0x46, 0xFF, prt, "im 0" },
+  { 0x47, 0xFF, prt, "ld i,a" },
+  { 0x4A, 0xCF, prt_rr, "adc hl," },
+  { 0x4B, 0xCF, prt_rr_nn, "ld %s,(0x%%04x)" },
+  { 0x4D, 0xFF, prt, "reti" },
+  { 0x56, 0xFF, prt, "im 1" },
+  { 0x57, 0xFF, prt, "ld a,i" },
+  { 0x5E, 0xFF, prt, "im 2" },
+  { 0x67, 0xFF, prt, "rrd" },
+  { 0x6F, 0xFF, prt, "rld" },
+  { 0xA0, 0xE4, cis, "" },
+  { 0xC3, 0xFF, prt, "muluw hl,bc" },
+  { 0xC5, 0xE7, prt_r, "mulub a,%s" },
+  { 0xF3, 0xFF, prt, "muluw hl,sp" },
+  { 0x00, 0x00, dump, "xx" }
+};
+
+static int
+pref_ed (struct buffer * buf, disassemble_info * info, 
+	 char* txt ATTRIBUTE_UNUSED)
+{
+  struct tab_elt *p;
+
+  if (fetch_data(buf, info, 1))
+    {
+      for (p = opc_ed; p->val != (buf->data[1] & p->mask); ++p)
+	;
+      p->fp (buf, info, p->text);
+    }
+  else
+    buf->n_used = -1;
+
+  return buf->n_used;
+}
+\f

+/* Instruction names for the instructions addressing single bits.  */
+static char *cb1_str[] = { "", "bit", "res", "set"};
+/* Instruction names for shifts and rotates.  */
+static char *cb2_str[] =
+{
+  "rlc", "rrc", "rl", "rr", "sla", "sra", "sli", "srl"
+};
+
+static int
+pref_cb (struct buffer * buf, disassemble_info * info,
+	 char* txt ATTRIBUTE_UNUSED)
+{
+  if (fetch_data (buf, info, 1))
+    {
+      buf->n_used = 2;
+      if ((buf->data[1] & 0xc0) == 0)
+	info->fprintf_func (info->stream, "%s %s",
+			    cb2_str[(buf->data[1] >> 3) & 7],
+			    r_str[buf->data[1] & 7]);
+      else
+	info->fprintf_func (info->stream, "%s %d,%s",
+			    cb1_str[(buf->data[1] >> 6) & 3],
+			    (buf->data[1] >> 3) & 7,
+			    r_str[buf->data[1] & 7]);
+    }
+  else
+    buf->n_used = -1;
+
+  return buf->n_used;
+}
+\f

+static int
+addvv (struct buffer * buf, disassemble_info * info, char* txt)
+{
+  info->fprintf_func (info->stream, "add %s,%s", txt, txt);
+
+  return buf->n_used = buf->n_fetch;
+}
+
+static int
+ld_v_v (struct buffer * buf, disassemble_info * info, char* txt)
+{
+  char mytxt[TXTSIZ];
+
+  snprintf (mytxt, TXTSIZ, "ld %s%%s,%s%%s", txt, txt);
+  return ld_r_r (buf, info, mytxt);
+}
+
+static int
+prt_d (struct buffer *buf, disassemble_info * info, char *txt)
+{
+  int d;
+  signed char *p;
+
+  p = buf->data + buf->n_fetch;
+
+  if (fetch_data (buf, info, 1))
+    {
+      d = p[0];
+      info->fprintf_func (info->stream, txt, d);
+      buf->n_used = buf->n_fetch;
+    }
+  else
+    buf->n_used = -1;
+
+  return buf->n_used;
+}
+
+static int
+prt_d_n (struct buffer *buf, disassemble_info * info, char *txt)
+{
+  char mytxt[TXTSIZ];
+  int d;
+  signed char *p;
+
+  p = buf->data + buf->n_fetch;
+
+  if (fetch_data (buf, info, 1))
+    {
+      d = p[0];
+      snprintf (mytxt, TXTSIZ, txt, d);
+      return prt_n (buf, info, mytxt);
+    }
+  else
+    buf->n_used = -1;
+
+  return buf->n_used;
+}
+
+static int
+arit_d (struct buffer *buf, disassemble_info * info, char *txt)
+{
+  char mytxt[TXTSIZ];
+  signed char c;
+
+  c = buf->data[buf->n_fetch - 1];
+  snprintf (mytxt, TXTSIZ, txt, arit_str[(c >> 3) & 7]);
+  return prt_d (buf, info, mytxt);
+}
+
+static int
+ld_r_d (struct buffer *buf, disassemble_info * info, char *txt)
+{
+  char mytxt[TXTSIZ];
+  signed char c;
+
+  c = buf->data[buf->n_fetch - 1];
+  snprintf (mytxt, TXTSIZ, txt, r_str[(c >> 3) & 7]);
+  return prt_d (buf, info, mytxt);
+}
+
+static int
+ld_d_r(struct buffer *buf, disassemble_info * info, char *txt)
+{
+  char mytxt[TXTSIZ];
+  signed char c;
+
+  c = buf->data[buf->n_fetch - 1];
+  snprintf (mytxt, TXTSIZ, txt, r_str[c & 7]);
+  return prt_d (buf, info, mytxt);
+}
+
+static int
+pref_xd_cb (struct buffer * buf, disassemble_info * info, char* txt)
+{
+  if (fetch_data (buf, info, 2))
+    {
+      int d;
+      char arg[TXTSIZ];
+      signed char *p;
+
+      buf->n_used = 4;
+      p = buf->data;
+      d = p[2];
+
+      if (((p[3] & 0xC0) == 0x40) || ((p[3] & 7) == 0x06))
+	snprintf (arg, TXTSIZ, "(%s%+d)", txt, d);
+      else
+	snprintf (arg, TXTSIZ, "(%s%+d),%s", txt, d, r_str[p[3] & 7]);
+
+      if ((p[3] & 0xc0) == 0)
+	info->fprintf_func (info->stream, "%s %s",
+			    cb2_str[(buf->data[3] >> 3) & 7],
+			    arg);
+      else
+	info->fprintf_func (info->stream, "%s %d,%s",
+			    cb1_str[(buf->data[3] >> 6) & 3],
+			    (buf->data[3] >> 3) & 7,
+			    arg);
+    }
+  else
+    buf->n_used = -1;
+
+  return buf->n_used;
+}
+\f

+/* Table to disassemble machine codes with prefix 0xDD or 0xFD.  */
+static struct tab_elt opc_ind[] =
+{
+  { 0x24, 0xF7, prt_r, "inc %s%%s" },
+  { 0x25, 0xF7, prt_r, "dec %s%%s" },
+  { 0x26, 0xF7, ld_r_n, "ld %s%%s,0x%%%%02x" },
+  { 0x21, 0xFF, prt_nn, "ld %s,0x%%04x" },
+  { 0x22, 0xFF, prt_nn, "ld (0x%%04x),%s" },
+  { 0x2A, 0xFF, prt_nn, "ld %s,(0x%%04x)" },
+  { 0x23, 0xFF, prt, "inc %s" },
+  { 0x2B, 0xFF, prt, "dec %s" },
+  { 0x29, 0xFF, addvv, "%s" },
+  { 0x09, 0xCF, prt_rr, "add %s," },
+  { 0x34, 0xFF, prt_d, "inc (%s%%+d)" },
+  { 0x35, 0xFF, prt_d, "dec (%s%%+d)" },
+  { 0x36, 0xFF, prt_d_n, "ld (%s%%+d),0x%%%%02x" },
+
+  { 0x76, 0xFF, dump, "h" },
+  { 0x46, 0xC7, ld_r_d, "ld %%s,(%s%%%%+d)" },
+  { 0x70, 0xF8, ld_d_r, "ld (%s%%%%+d),%%s" },
+  { 0x64, 0xF6, ld_v_v, "%s" },
+  { 0x60, 0xF0, ld_r_r, "ld %s%%s,%%s" },
+  { 0x44, 0xC6, ld_r_r, "ld %%s,%s%%s" },
+
+  { 0x86, 0xC7, arit_d, "%%s(%s%%%%+d)" },
+  { 0x84, 0xC6, arit_r, "%%s%s%%s" },
+
+  { 0xE1, 0xFF, prt, "pop %s" },
+  { 0xE5, 0xFF, prt, "push %s" },
+  { 0xCB, 0xFF, pref_xd_cb, "%s" },
+  { 0xE3, 0xFF, prt, "ex (sp),%s" },
+  { 0xE9, 0xFF, prt, "jp (%s)" },
+  { 0xF9, 0xFF, prt, "ld sp,%s" },
+  { 0x00, 0x00, dump, "?" },
+} ;
+
+static int
+pref_ind (struct buffer * buf, disassemble_info * info, char* txt)
+{
+  if (fetch_data (buf, info, 1))
+    {
+      char mytxt[TXTSIZ];
+      struct tab_elt *p;
+
+      for (p = opc_ind; p->val != (buf->data[1] & p->mask); ++p)
+	;
+      snprintf (mytxt, TXTSIZ, p->text, txt);
+      p->fp (buf, info, mytxt);
+    }
+  else
+    buf->n_used = -1;
+
+  return buf->n_used;
+}
+
+/* Table to disassemble machine codes without prefix.  */
+static struct tab_elt opc_main[] =
+{
+  { 0x00, 0xFF, prt, "nop" },
+  { 0x01, 0xCF, prt_rr_nn, "ld %s,0x%%04x" },
+  { 0x02, 0xFF, prt, "ld (bc),a" },
+  { 0x03, 0xCF, prt_rr, "inc " },
+  { 0x04, 0xC7, prt_r, "inc %s" },
+  { 0x05, 0xC7, prt_r, "dec %s" },
+  { 0x06, 0xC7, ld_r_n, "ld %s,0x%%02x" },
+  { 0x07, 0xFF, prt, "rlca" },
+  { 0x08, 0xFF, prt, "ex af,af'" },
+  { 0x09, 0xCF, prt_rr, "add hl," },
+  { 0x0A, 0xFF, prt, "ld a,(bc)" },
+  { 0x0B, 0xCF, prt_rr, "dec " },
+  { 0x0F, 0xFF, prt, "rrca" },
+  { 0x10, 0xFF, prt_e, "djnz " },
+  { 0x12, 0xFF, prt, "ld (de),a" },
+  { 0x17, 0xFF, prt, "rla" },
+  { 0x18, 0xFF, prt_e, "jr "},
+  { 0x1A, 0xFF, prt, "ld a,(de)" },
+  { 0x1F, 0xFF, prt, "rra" },
+  { 0x20, 0xE7, jr_cc, "jr %s,"},
+  { 0x22, 0xFF, prt_nn, "ld (0x%04x),hl" },
+  { 0x27, 0xFF, prt, "daa"},
+  { 0x2A, 0xFF, prt_nn, "ld hl,(0x%04x)" },
+  { 0x2F, 0xFF, prt, "cpl" },
+  { 0x32, 0xFF, prt_nn, "ld (0x%04x),a" },
+  { 0x37, 0xFF, prt, "scf" },
+  { 0x3A, 0xFF, prt_nn, "ld a,(0x%04x)" },
+  { 0x3F, 0xFF, prt, "ccf" },
+
+  { 0x76, 0xFF, prt, "halt" },
+  { 0x40, 0xC0, ld_r_r, "ld %s,%s"},
+
+  { 0x80, 0xC0, arit_r, "%s%s" },
+
+  { 0xC0, 0xC7, prt_cc, "ret " },
+  { 0xC1, 0xCF, pop_rr, "pop" },
+  { 0xC2, 0xC7, jp_cc_nn, "jp " },
+  { 0xC3, 0xFF, prt_nn, "jp 0x%04x" },
+  { 0xC4, 0xC7, jp_cc_nn, "call " },
+  { 0xC5, 0xCF, pop_rr, "push" },
+  { 0xC6, 0xC7, arit_n, "%s0x%%02x" },
+  { 0xC7, 0xC7, rst, "rst 0x%02x" },
+  { 0xC9, 0xFF, prt, "ret" },
+  { 0xCB, 0xFF, pref_cb, "" },
+  { 0xCD, 0xFF, prt_nn, "call 0x%04x" },
+  { 0xD3, 0xFF, prt_n, "out (0x%02x),a" },
+  { 0xD9, 0xFF, prt, "exx" },
+  { 0xDB, 0xFF, prt_n, "in a,(0x%02x)" },
+  { 0xDD, 0xFF, pref_ind, "ix" },
+  { 0xE3, 0xFF, prt, "ex (sp),hl" },
+  { 0xE9, 0xFF, prt, "jp (hl)" },
+  { 0xEB, 0xFF, prt, "ex de,hl" },
+  { 0xED, 0xFF, pref_ed, ""},
+  { 0xF3, 0xFF, prt, "di" },
+  { 0xF9, 0xFF, prt, "ld sp,hl" },
+  { 0xFB, 0xFF, prt, "ei" },
+  { 0xFD, 0xFF, pref_ind, "iy" },
+  { 0x00, 0x00, prt, "????" },
+} ;
+
+int
+print_insn_z80 (bfd_vma addr, disassemble_info * info)
+{
+  struct buffer buf;
+  struct tab_elt *p;
+
+  buf.base = addr;
+  buf.n_fetch = 0;
+  buf.n_used = 0;
+
+  if (! fetch_data (& buf, info, 1))
+    return -1;
+
+  for (p = opc_main; p->val != (buf.data[0] & p->mask); ++p)
+    ;
+  p->fp (& buf, info, p->text);
+
+  return buf.n_used;
+}

^ permalink raw reply related	[flat|nested] 27+ messages in thread

end of thread, other threads:[~2010-12-29 19:57 UTC | newest]

Thread overview: 27+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2009-05-31 15:20 [Qemu-devel] [PATCH] Z80 emulation updated again! Stuart Brady
2010-12-20 22:45 ` Alexander Graf
2010-12-21  0:06   ` Andreas Färber
2010-12-21  0:10     ` Alexander Graf
2010-12-21  1:24       ` Andreas Färber
2010-12-21  2:38         ` Natalia Portillo
2010-12-21  2:47           ` John Williams
2010-12-21 17:24             ` Andreas Färber
2010-12-21 17:20           ` Stefan Weil
2010-12-21 18:28             ` [Qemu-devel] QEMU forks survey Andreas Färber
2010-12-21 18:38               ` Alexander Graf
2010-12-21 19:04               ` Peter Maydell
2010-12-21 21:16                 ` Andreas Färber
2010-12-21 19:21               ` M P
2010-12-21 20:27               ` François Revol
2010-12-21 21:49               ` Stefan Weil
2010-12-22  7:47               ` Bastien ROUCARIES
2010-12-22 15:28                 ` Andreas Färber
2010-12-26 22:19                   ` Bastien ROUCARIES
2010-12-26 23:44                     ` Andreas Färber
2010-12-27  8:08                       ` Stefan Weil
2010-12-27 18:06                         ` Andreas Färber
2010-12-21  9:25         ` [Qemu-devel] [PATCH] Z80 emulation updated again! Alexander Graf
2010-12-21 11:14           ` Artyom Tarasenko
2010-12-21 11:49             ` Alexander Graf
2010-12-21  9:59     ` Artyom Tarasenko
2010-12-29 19:57   ` Stuart Brady

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).