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 + * 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 +#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 + * + * 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 + * + * 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 + * + * 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 + * + * 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 + * + * 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 + * + * 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 +#include +#include +#include +#include +#include + +#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 + * + * 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 + * + * 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 +#include +#include +#include +#include +#include + +#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 + + 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 + +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 " +} ; + +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; +} + + +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; +} + +/* 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; +} + +/* 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; +} + +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; +} + +/* 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; +}